Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
quicklizard99 committed Aug 19, 2016
2 parents 221beb4 + d61f2bc commit f8c1498
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 181 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# v0.1.7
- #15 Mac OS X build of decode_barcodes
- #15 Mac OS X build of decode_barcodes
- #14 Suffixes to resolve filename collisions

# v0.1.6
- #12 bin to scripts
Expand Down
173 changes: 0 additions & 173 deletions gouda/scripts/decode_barcode.py

This file was deleted.

49 changes: 42 additions & 7 deletions gouda/scripts/decode_barcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import time
import traceback

import cv2
from collections import defaultdict
from itertools import count
from functools import partial

import gouda
import gouda.util
Expand All @@ -22,7 +24,7 @@


def decode(paths, strategies, engine, visitors, read_greyscale):
"""Finds and decodes barcodes in images given in paths
"""Finds and decodes barcodes in images given in pathss
"""
for p in paths:
if p.is_dir():
Expand Down Expand Up @@ -103,9 +105,32 @@ def result(self, path, result):
strategy])


class RenameReporter(object):
class RenameVisitor(object):
"""Renames files based on their barcodes
"""
def __init__(self, avoid_collisions):
self.avoid_collisions = avoid_collisions
# Mapping from path to iterator of integer suffixes, used to avoid
# collisions - see self._destination
self.suffix = defaultdict(partial(count, start=1))

def _destination(self, path):
"""Returns path possibly with a suffix appended to the name to avoid
collisions with existing files, iff self.avoid_collisions is True,
otherwise path is returned unaltered.
"""
destination = path
if self.avoid_collisions:
while destination.is_file():
fname = u'{0}-{1}{2}'.format(
path.stem,
next(self.suffix[path.name]),
path.suffix
)
destination = path.with_name(fname)

return destination

def result(self, path, result):
strategy, barcodes = result
print(path)
Expand All @@ -122,6 +147,7 @@ def result(self, path, result):
first_destination = None
for value in values:
dest = path.with_name(u'{0}{1}'.format(value, path.suffix))
dest = self._destination(dest)
source = first_destination if first_destination else path
rename = not bool(first_destination)
if source == dest:
Expand All @@ -139,8 +165,8 @@ def result(self, path, result):
first_destination = dest


if __name__ == '__main__':
# TODO ROI candidate area max and/or min?
def main(args):
# TODO LH ROI candidate area max and/or min?
# TODO Give area min and max as percentage of total image area?
# TODO Report barcode regions - both normalised and absolute coords?
# TODO Swallow zbar warnings?
Expand All @@ -154,6 +180,11 @@ def result(self, path, result):
choices=['basic', 'terse', 'csv', 'rename'], default='basic'
)
parser.add_argument('--greyscale', '-g', action='store_true')
parser.add_argument(
'--avoid-collisions', action='store_true',
help=('If the action is "rename", appends a suffix to renamed files to '
'prevent collisions')
)

options = engine_options()
if not options:
Expand All @@ -164,7 +195,7 @@ def result(self, path, result):
parser.add_argument('-v', '--version', action='version',
version='%(prog)s ' + gouda.__version__)

args = parser.parse_args()
args = parser.parse_args(args)

gouda.util.DEBUG_PRINT = args.debug

Expand All @@ -175,10 +206,14 @@ def result(self, path, result):
elif 'terse' == args.action:
visitor = TerseReportVisitor()
elif 'rename' == args.action:
visitor = RenameReporter()
visitor = RenameVisitor(args.avoid_collisions)
else:
visitor = BasicReportVisitor()

strategies = [resize, roi]
decode(expand_wildcard(args.image), strategies, engine, [visitor],
args.greyscale)


if __name__ == '__main__':
main(sys.argv[1:])
76 changes: 76 additions & 0 deletions gouda/tests/test_decode_barcodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import unittest
import shutil

from pathlib import Path

from gouda.engines import ZbarEngine
from gouda.scripts.decode_barcodes import main

from utils import temp_directory_with_files


TESTDATA = Path(__file__).parent.joinpath('test_data')


@unittest.skipUnless(ZbarEngine.available(), 'ZbarEngine unavailable')
class TestRename(unittest.TestCase):
def test_rename(self):
"File is renamed with value of barcode"
with temp_directory_with_files(TESTDATA.joinpath('code128.png')) as tempdir:
main(['zbar', '--action=rename', unicode(tempdir)])
self.assertEqual(
['Stegosaurus.png'],
[path.name for path in sorted(tempdir.iterdir())]
)

def test_rename_multiple(self):
"File with multiple barcodes results in renamed / copied to three files"
with temp_directory_with_files(TESTDATA.joinpath('BM001128287.jpg')) as tempdir:
main(['zbar', '--action=rename', unicode(tempdir)])
self.assertEqual(
['BM001128286.jpg', 'BM001128287.jpg', 'BM001128288.jpg'],
[path.name for path in sorted(tempdir.iterdir())]
)

def test_rename_with_collisions(self):
"Files with same barcode values results in just a single rename"
with temp_directory_with_files(TESTDATA.joinpath('code128.png')) as tempdir:
shutil.copy(
unicode(TESTDATA.joinpath('code128.png')),
unicode(tempdir.joinpath('first copy.png'))
)

shutil.copy(
unicode(TESTDATA.joinpath('code128.png')),
unicode(tempdir.joinpath('second copy.png'))
)

main(['zbar', '--action=rename', unicode(tempdir)])
self.assertEqual(
['Stegosaurus.png', 'first copy.png', 'second copy.png'],
[path.name for path in sorted(tempdir.iterdir())]
)

def test_rename_avoid_collisions(self):
"Files with same barcode values results in new files with suffixes"
with temp_directory_with_files(TESTDATA.joinpath('code128.png')) as tempdir:
shutil.copy(
unicode(TESTDATA.joinpath('code128.png')),
unicode(tempdir.joinpath('first copy.png'))
)

shutil.copy(
unicode(TESTDATA.joinpath('code128.png')),
unicode(tempdir.joinpath('second copy.png'))
)

main(['zbar', '--action=rename', unicode(tempdir), '--avoid-collisions'])
print([path.name for path in sorted(tempdir.iterdir())])
self.assertEqual(
['Stegosaurus-1.png', 'Stegosaurus-2.png', 'Stegosaurus.png'],
[path.name for path in sorted(tempdir.iterdir())]
)


if __name__ == '__main__':
unittest.main()
20 changes: 20 additions & 0 deletions gouda/tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import shutil
import tempfile

from contextlib import contextmanager
from pathlib import Path


@contextmanager
def temp_directory_with_files(*paths):
"""A context manager that creates a temporary directory and copies all
paths to it. The temporary directory is unlinked when the context is exited.
"""
temp = tempfile.mkdtemp()
try:
temp = Path(temp)
for p in paths:
shutil.copy(str(p), str(temp / Path(p).name))
yield temp
finally:
shutil.rmtree(str(temp))

0 comments on commit f8c1498

Please sign in to comment.