diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f3f2a..8bf47ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v0.1.12 +- #33 decode_barcodes csv error + # v0.1.11 - #31 Universal wheel diff --git a/README.md b/README.md index 8ad1582..dc39a6a 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,17 @@ A python package for decoding barcodes, possibly more than one, in complex images such as scans of museum specimens. Gouda supports Python 2.7, 3.4 and 3.5. A universal wheel build is available -on the Releases page. +on the [releases](releases) pages. A command-line program `decode_barcodes` is available for Windows 64-bit and Mac OS X. It reads barcode values in individual images and in batches of images -files in a directory. It can print values to CSV and can renaming files with -the value(s) or barcode(s). Download from the Releases tab. For information run +files in a directory. It can print values to CSV and can rename files with +the value(s) or barcode(s). Download from the [releases](releases) page. +For help run decode_barcodes --help -See also the [`decode_barcodes` examples](#decode_barcodes-examples) below. +See also the [examples](#decode_barcodes-script) below. ## `gouda/engines` An engine is an interface to a barcode reading library. diff --git a/gouda/__init__.py b/gouda/__init__.py index 13b7089..e6d0c4f 100644 --- a/gouda/__init__.py +++ b/gouda/__init__.py @@ -1 +1 @@ -__version__ = '0.1.11' +__version__ = '0.1.12' diff --git a/gouda/scripts/decode_barcodes.py b/gouda/scripts/decode_barcodes.py index 6188932..0549574 100755 --- a/gouda/scripts/decode_barcodes.py +++ b/gouda/scripts/decode_barcodes.py @@ -79,8 +79,8 @@ def result(self, path, result): class CSVReportVisitor(object): """Writes a CSV report """ - def __init__(self, engine, greyscale, file=sys.stdout): - self.w = csv.writer(file, lineterminator='\n') + def __init__(self, engine, greyscale, file=None): + self.w = csv.writer(file if file else sys.stdout, lineterminator='\n') self.w.writerow([ 'OS', 'Engine', 'Directory', 'File', 'Image.conversion', 'Elapsed', 'N.found', 'Types', 'Values', 'Strategy' @@ -92,7 +92,12 @@ def __init__(self, engine, greyscale, file=sys.stdout): def result(self, path, result): strategy, barcodes = result types = '|'.join(b.type for b in barcodes) - values = '|'.join(b.data for b in barcodes) + # data could be either str or bytes + values = '|'.join( + b.data.decode() if hasattr(b.data, 'decode') else b.data + for b in barcodes + ) + self.w.writerow([sys.platform, self.engine, path.parent.name, diff --git a/gouda/tests/test_decode_barcodes.py b/gouda/tests/test_decode_barcodes.py index f74d487..54c8747 100644 --- a/gouda/tests/test_decode_barcodes.py +++ b/gouda/tests/test_decode_barcodes.py @@ -1,7 +1,16 @@ +import os import unittest import shutil +import sys from pathlib import Path +from contextlib import contextmanager + +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + from gouda.engines import ZbarEngine from gouda.scripts.decode_barcodes import main @@ -9,11 +18,49 @@ from .utils import temp_directory_with_files + +@contextmanager +def capture_stdout(): + sys.stdout, old_stdout = StringIO(), sys.stdout + try: + yield sys.stdout + finally: + sys.stdout = old_stdout + + TESTDATA = Path(__file__).parent.joinpath('test_data') @unittest.skipUnless(ZbarEngine.available(), 'ZbarEngine unavailable') class TestRename(unittest.TestCase): + def test_csv(self): + "CSV report is printed" + with capture_stdout() as stdout: + main([ + 'zbar', + '--action=csv', + str(TESTDATA.joinpath('code128.png')), + str(TESTDATA.joinpath('BM001128287.jpg')) + ]) + from pprint import pprint + + lines = stdout.getvalue().strip().split(os.linesep) + self.assertEqual(3, len(lines)) + + header = ( + 'OS,Engine,Directory,File,Image.conversion,Elapsed,N.found,Types,' + 'Values,Strategy' + ) + self.assertEqual(header, lines[0]) + + self.assertIn('BM001128287.jpg', lines[1]) + self.assertIn('CODE128|CODE128|CODE128', lines[1]) + self.assertIn('BM001128287|BM001128286|BM001128288', lines[1]) + + self.assertIn('code128.png', lines[2]) + self.assertIn('CODE128', lines[2]) + self.assertIn('Stegosaurus', lines[2]) + def test_rename(self): "File is renamed with value of barcode" with temp_directory_with_files(TESTDATA.joinpath('code128.png')) as tempdir: