diff --git a/.hgignore b/.hgignore index 1b85cd9..913ee1b 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,6 @@ syntax: glob __pycache__/* locale/convert_de2en.py +build/* +mds_cashbook_media.egg-info/* +dist/* diff --git a/README.rst b/README.rst index ab62e82..c16afe5 100644 --- a/README.rst +++ b/README.rst @@ -9,11 +9,11 @@ pip install mds-cashbook-media Requires ======== -- Tryton 6.0 +- Tryton 7.0 Changes ======= -*6.0.0 - 14.10.2022* +*7.0.0 - 01.12.2023* -- init +- ported to Tryton 7.0 diff --git a/__init__.py b/__init__.py index 8681953..4c95efa 100644 --- a/__init__.py +++ b/__init__.py @@ -6,6 +6,7 @@ from trytond.pool import Pool from .line import Line + def register(): Pool.register( Line, diff --git a/line.py b/line.py index a7a482d..a0ecf3e 100644 --- a/line.py +++ b/line.py @@ -3,13 +3,13 @@ # The COPYRIGHT file at the top level of this repository contains the # full copyright notices and license terms. -import mimetypes, magic +import mimetypes +import magic from io import BytesIO -from PIL import Image +from PIL import Image, UnidentifiedImageError from trytond.model import fields -from trytond.pool import Pool, PoolMeta +from trytond.pool import PoolMeta from trytond.config import config -from trytond.transaction import Transaction from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.pyson import Eval, Bool @@ -18,30 +18,50 @@ from trytond.modules.cashbook.line import STATES, DEPENDS store_prefix = config.get('cashbook', 'store_prefix', default='cashbook') image_limit = config.get('cashbook', 'image_max_pixel', default='2000') -try : +try: image_limit = int(image_limit) if image_limit < 100: image_limit = 100 if image_limit > 10000: image_limit = 10000 -except : +except Exception: image_limit = 2000 +STATES2 = {} +STATES2.update(STATES) +DEPENDS2 = [] +DEPENDS2.extend(DEPENDS) + + class Line(metaclass=PoolMeta): __name__ = 'cashbook.line' - media = fields.Binary(string='Image of PDF', filename='media_name', - file_id='media_id', store_prefix=store_prefix, - states=STATES, depends=DEPENDS) - media_name = fields.Char(string='File name', + media = fields.Binary( + string='Image of PDF', filename='media_name', file_id='media_id', + store_prefix=store_prefix, states=STATES2, depends=DEPENDS2) + media_name = fields.Char( + string='File name', states={ 'required': Bool(Eval('media')), - 'readonly': STATES['readonly'], - }, depends=DEPENDS) + 'readonly': STATES2['readonly'], + }, depends=DEPENDS2) media_id = fields.Char(string='File ID', readonly=True) media_mime = fields.Char(string='MIME', readonly=True) media_size = fields.Integer(string='File size', readonly=True) + media_image = fields.Function(fields.Binary( + string='Image', readonly=True, + states={ + 'invisible': ~Eval('media_mime', '').in_([ + 'image/png', 'image/jpg', 'image/jpeg']), + }, depends=['media_mime']), 'on_change_with_media_image') + + @fields.depends('media', 'media_mime') + def on_change_with_media_image(self, name=True): + """ return binary if its a image + """ + if (self.media_mime or '-').startswith('image/'): + return self.media @classmethod def _identify_file(cls, data, mime=True): @@ -65,14 +85,17 @@ class Line(metaclass=PoolMeta): """ image_data2 = None with BytesIO(image_data) as fhdl: - image = Image.open(fhdl, 'r') + try: + image = Image.open(fhdl, 'r') + except UnidentifiedImageError: + raise UserError(gettext('cashbook_media.msg_file_unknown_type')) (width, height) = image.size if (width > image_limit) or (height > image_limit): if width > height: new_size = (image_limit, int(height * image_limit / width)) - else : + else: new_size = (int(width * image_limit / height), image_limit) # resize - fit in (image_limit x image_limit) @@ -95,15 +118,17 @@ class Line(metaclass=PoolMeta): values['media_mime'] = None values['media_size'] = None values['media_name'] = None - else : + else: values['media_mime'] = cls._identify_file(values['media'][:1024]) - # if its a image, resize it to fit in (image_limit x image_limit) pixel + # if its a image, resize it to fit + # in (image_limit x image_limit) pixel if values['media_mime'].startswith('image'): new_image = cls.resize_image_file(values['media']) if new_image is not None: values['media'] = new_image - values['media_mime'] = cls._identify_file(values['media'][:1024]) + values['media_mime'] = cls._identify_file( + values['media'][:1024]) values['media_size'] = len(values['media']) file_ext = mimetypes.guess_extension(values['media_mime']) @@ -126,15 +151,14 @@ class Line(metaclass=PoolMeta): if line.media_size > 1024*1024*5: raise UserError(gettext( 'cashbook_media.msg_file_too_big', - recname = line.rec_name, - )) - if not line.media_mime in ['application/pdf', - 'image/png', 'image/jpg', 'image/jpeg']: + recname=line.rec_name)) + if line.media_mime not in [ + 'application/pdf', + 'image/png', 'image/jpg', 'image/jpeg']: raise UserError(gettext( 'cashbook_media.msg_file_invalid_mime', - recname = line.rec_name, - fmime = line.media_mime, - )) + recname=line.rec_name, + fmime=line.media_mime)) @classmethod def create(cls, vlist): diff --git a/locale/de.po b/locale/de.po index 69df498..7618e5c 100644 --- a/locale/de.po +++ b/locale/de.po @@ -14,6 +14,10 @@ msgctxt "model:ir.message,text:msg_file_invalid_mime" msgid "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)" msgstr "Der Dateityp '%(fmime)s' des Datensatzes '%(recname)s' ist nicht zugelassen. (erlaubt: PNG, JPG, PDF)" +msgctxt "model:ir.message,text:msg_file_unknown_type" +msgid "cannot identify image file" +msgstr "Bilddatei kann nicht identifiziert werden" + ################# # cashbook.line # @@ -42,3 +46,6 @@ msgctxt "field:cashbook.line,media_size:" msgid "File size" msgstr "Dateigröße" +msgctxt "field:cashbook.line,media_image:" +msgid "Image" +msgstr "Bild" diff --git a/locale/en.po b/locale/en.po index 956e614..5ff9df6 100644 --- a/locale/en.po +++ b/locale/en.po @@ -10,6 +10,10 @@ msgctxt "model:ir.message,text:msg_file_invalid_mime" msgid "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)" msgstr "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)" +msgctxt "model:ir.message,text:msg_file_unknown_type" +msgid "cannot identify image file" +msgstr "cannot identify image file" + msgctxt "view:cashbook.line:" msgid "Image/PDF" msgstr "Image/PDF" diff --git a/message.xml b/message.xml index d88ff9a..3582d1e 100644 --- a/message.xml +++ b/message.xml @@ -11,6 +11,9 @@ full copyright notices and license terms. --> The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF) + + cannot identify image file + diff --git a/setup.py b/setup.py index aa97035..c3531db 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """ # Always prefer setuptools over distutils -from setuptools import setup, find_packages +from setuptools import setup # To use a consistent encoding from codecs import open from os import path @@ -36,10 +36,10 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f: l2 = i.strip().split(';') if len(l2) < 4: continue - modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]} + modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]} # tryton-version -major_version = 6 +major_version = 7 minor_version = 0 requires = ['python-magic>=0.4.24', 'Pillow'] @@ -51,42 +51,52 @@ for dep in info.get('depends', []): prefix = modversion[dep]['prefix'] if len(modversion[dep]['max']) > 0: - requires.append('%s_%s >= %s, <= %s' % - (prefix, dep, modversion[dep]['min'], modversion[dep]['max'])) - else : - requires.append('%s_%s >= %s' % + requires.append( + '%s_%s >= %s, <= %s' % + (prefix, dep, + modversion[dep]['min'], + modversion[dep]['max'])) + else: + requires.append( + '%s_%s >= %s' % (prefix, dep, modversion[dep]['min'])) - else : - requires.append('%s_%s >= %s.%s, < %s.%s' % - ('trytond', dep, major_version, minor_version, - major_version, minor_version + 1)) -requires.append('trytond >= %s.%s, < %s.%s' % - (major_version, minor_version, major_version, minor_version + 1)) + else: + requires.append( + '%s_%s >= %s.%s, < %s.%s' % ( + 'trytond', dep, major_version, minor_version, + major_version, minor_version + 1)) +requires.append( + 'trytond >= %s.%s, < %s.%s' % ( + major_version, minor_version, major_version, minor_version + 1)) -setup(name='%s_%s' % (PREFIX, MODULE), +setup( + name='%s_%s' % (PREFIX, MODULE), version=info.get('version', '0.0.1'), description='Tryton module to add a file-field to cashbook.', long_description=long_description, + long_description_content_type='text/x-rst', url='https://www.m-ds.de/', + download_url='https://scmdev.m-ds.de/Tryton/Extra/cashbook_media', author='martin-data services', author_email='service@m-ds.de', license='GPL-3', classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Plugins', - 'Framework :: Tryton', - 'Intended Audience :: Developers', - 'Intended Audience :: Customer Service', - 'Intended Audience :: Information Technology', - 'Intended Audience :: Financial and Insurance Industry', - 'Topic :: Office/Business', - 'Topic :: Office/Business :: Financial :: Accounting', - 'Natural Language :: German', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Plugins', + 'Framework :: Tryton', + 'Intended Audience :: Developers', + 'Intended Audience :: Customer Service', + 'Intended Audience :: Information Technology', + 'Intended Audience :: Financial and Insurance Industry', + 'Topic :: Office/Business', + 'Topic :: Office/Business :: Financial :: Accounting', + 'Natural Language :: German', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], keywords='tryton cashbook pdf png image file', @@ -95,7 +105,8 @@ setup(name='%s_%s' % (PREFIX, MODULE), 'trytond.modules.%s' % MODULE, ], package_data={ - 'trytond.modules.%s' % MODULE: (info.get('xml', []) + 'trytond.modules.%s' % MODULE: ( + info.get('xml', []) + ['tryton.cfg', 'locale/*.po', 'tests/*.py', 'view/*.xml', 'versiondep.txt', 'README.rst']), }, diff --git a/tests/__init__.py b/tests/__init__.py index fa351bf..4effdfa 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,24 +1,2 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. - -import trytond.tests.test_tryton -import unittest - -from trytond.modules.cashbook_media.tests.test_line import LineTestCase - - -__all__ = ['suite'] - - -class CashbookTestCase(\ - LineTestCase, - ): - 'Test cashbook module' - module = 'cashbook_media' - -# end CashbookTestCase - -def suite(): - suite = trytond.tests.test_tryton.suite() - suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CashbookTestCase)) - return suite diff --git a/tests/test_line.py b/tests/test_line.py index 28fadd9..a6fbe1d 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -5,11 +5,10 @@ from io import BytesIO from PIL import Image -from trytond.tests.test_tryton import ModuleTestCase, with_transaction +from trytond.tests.test_tryton import with_transaction from trytond.pool import Pool -from trytond.transaction import Transaction from trytond.exceptions import UserError -from trytond.modules.cashbook.tests import CashbookTestCase +from trytond.modules.cashbook.tests.test_module import CashbookTestCase from datetime import date from decimal import Decimal from .img_data import img_data_png, dok_data_pdf, text_data @@ -126,8 +125,11 @@ class LineTestCase(CashbookTestCase): self.assertEqual(book.state, 'open') # add invalid file - self.assertRaisesRegex(UserError, - "The file type 'text/plain' of the record '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. (allowed: PNG, JPG, PDF)", + self.assertRaisesRegex( + UserError, + "The file type 'text/plain' of the record " + + "'05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. " + + "(allowed: PNG, JPG, PDF)", Book.write, *[ [book], @@ -146,8 +148,11 @@ class LineTestCase(CashbookTestCase): ]) # replace image at line-1 by invalid file - self.assertRaisesRegex(UserError, - "The file type 'text/plain' of the record '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. (allowed: PNG, JPG, PDF)", + self.assertRaisesRegex( + UserError, + "The file type 'text/plain' of the record " + + "'05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. " + + "(allowed: PNG, JPG, PDF)", Lines.write, *[ [book.lines[0]], @@ -163,7 +168,6 @@ class LineTestCase(CashbookTestCase): """ pool = Pool() Book = pool.get('cashbook.book') - Lines = pool.get('cashbook.line') types = self.prep_type() category = self.prep_category(cattype='in') @@ -226,3 +230,6 @@ class LineTestCase(CashbookTestCase): self.assertEqual(img2.size, (2000, 837)) # end LineTestCase + + +del CashbookTestCase diff --git a/tryton.cfg b/tryton.cfg index f603792..e0bd666 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,5 +1,5 @@ [tryton] -version=6.0.0 +version=7.0.0 depends: cashbook xml: diff --git a/versiondep.txt b/versiondep.txt index 7a98af6..bae9199 100644 --- a/versiondep.txt +++ b/versiondep.txt @@ -1 +1 @@ -cashbook;6.0.18;6.0.999;mds +cashbook;7.0.31;7.0.999;mds diff --git a/view/line_form.xml b/view/line_form.xml index 6785333..0d80ebc 100644 --- a/view/line_form.xml +++ b/view/line_form.xml @@ -13,10 +13,10 @@ full copyright notices and license terms. -->