From 12d4fc714b5a334a21c53d3d6b39125649e8e29e 2016-05-14 01:02:47 From: Lance Edgar Date: 2016-05-14 01:02:47 Subject: [PATCH] Give teeth to `ImportHandler.process_changes()` in new framework I.e. make it send the diff emails by default. --- diff --git a/rattail/importing/handlers.py b/rattail/importing/handlers.py index eed9b8247d41ab251a43e542416e657e384b07c1..311c34ee62c913530db41a98142d14f944f60605 100644 --- a/rattail/importing/handlers.py +++ b/rattail/importing/handlers.py @@ -26,11 +26,18 @@ Import Handlers from __future__ import unicode_literals, absolute_import +import sys import datetime import logging +import humanize + from rattail.time import make_utc from rattail.util import OrderedDict +from rattail.mail import send_email + +# TODO +from rattail.db.newimporting.handlers import RecordRenderer log = logging.getLogger(__name__) @@ -180,6 +187,38 @@ class ImportHandler(object): import is running in "warnings" mode. Default implementation does nothing; override as needed. """ + # TODO: This whole thing needs a re-write...but for now, waiting until + # the old importer has really gone away, so we can share its email + # template instead of bothering with something more complicated. + + if not self.warnings: + return + + now = make_utc(datetime.datetime.utcnow(), tzinfo=True) + data = { + 'local_title': self.local_title, + 'host_title': self.host_title, + 'argv': sys.argv, + 'runtime': humanize.naturaldelta(now - self.import_began), + 'changes': changes, + 'dry_run': self.dry_run, + 'render_record': RecordRenderer(self.config), + 'max_display': 15, + } + + command = getattr(self, 'command', None) + if command: + data['command'] = '{} {}'.format(command.parent.name, command.name) + else: + data['command'] = None + + if command: + key = '{}_{}_updates'.format(command.parent.name, command.name) + key = key.replace('-', '_') + else: + key = 'rattail_import_updates' + + send_email(self.config, key, fallback_key='rattail_import_updates', data=data) class FromSQLAlchemyHandler(ImportHandler): diff --git a/rattail/tests/importing/test_handlers.py b/rattail/tests/importing/test_handlers.py index 663cbdfbcc425ed7ed337a491406e9214da11363..96b62318ac543df4ef0dd31f31e074c78d373224 100644 --- a/rattail/tests/importing/test_handlers.py +++ b/rattail/tests/importing/test_handlers.py @@ -157,8 +157,9 @@ class TestImportHandlerImportData(ImporterTester, unittest.TestCase): } def setUp(self): - self.handler = MockImportHandler() - self.importer = MockImporter() + self.config = RattailConfig() + self.handler = MockImportHandler(config=self.config) + self.importer = MockImporter(config=self.config) def import_data(self, **kwargs): # must modify our importer in-place since we need the handler to return @@ -298,6 +299,32 @@ class TestImportHandlerImportData(ImporterTester, unittest.TestCase): self.assert_import_updated('16oz') self.assert_import_deleted('bogus') + def test_warnings_run(self): + local = self.copy_data() + del local['32oz'] + local['16oz']['description'] = "wrong description" + local['bogus'] = {'upc': '00000000000000', 'description': "Delete Me"} + with self.host_data(self.sample_data): + with self.local_data(local): + with patch('rattail.importing.handlers.send_email') as send_email: + self.assertEqual(send_email.call_count, 0) + self.import_data(warnings=True, dry_run=True) + self.assertEqual(send_email.call_count, 1) + # second time is just for more coverage... + with self.host_data(self.sample_data): + with self.local_data(local): + with patch('rattail.importing.handlers.send_email') as send_email: + self.handler.command = Mock() + self.assertEqual(send_email.call_count, 0) + self.import_data(warnings=True) + self.assertEqual(send_email.call_count, 1) + # TODO: maybe need a way to confirm no changes actually made due to dry + # run; currently results still reflect "proposed" changes. this rather + # bogus test is here just for coverage sake + self.assert_import_created('32oz') + self.assert_import_updated('16oz') + self.assert_import_deleted('bogus') + Session = orm.sessionmaker() @@ -409,6 +436,8 @@ class MockBulkImportHandler(handlers.BulkToPostgreSQLHandler): class TestBulkImportHandler(RattailTestCase, ImporterTester): + importer_class = MockBulkImporter + sample_data = { 'grocery': {'number': 1, 'name': "Grocery", 'uuid': 'decd909a194011e688093ca9f40bc550'}, 'bulk': {'number': 2, 'name': "Bulk", 'uuid': 'e633d54c194011e687e33ca9f40bc550'}, @@ -420,23 +449,17 @@ class TestBulkImportHandler(RattailTestCase, ImporterTester): self.tempio = TempIO() self.config.set('rattail', 'workdir', self.tempio.realpath()) self.handler = MockBulkImportHandler(config=self.config) - self.importer = MockBulkImporter(config=self.config) def tearDown(self): self.teardown_rattail() self.tempio = None - def postgresql(self): - return self.config.rattail_engine.url.get_dialect().name == 'postgresql' - - def import_data(self, **kwargs): - # must modify our importer in-place since we need the handler to return - # that specific instance, below (because the host/local data context - # managers reference that instance directly) - self.importer._setup(**kwargs) - self.importer.session = self.session - with patch.object(self.handler, 'get_importer', Mock(return_value=self.importer)): - result = self.handler.import_data('Department', **kwargs) + def import_data(self, host_data=None, **kwargs): + if host_data is None: + host_data = list(self.copy_data().itervalues()) + with patch.object(self.importer_class, 'normalize_host_data', Mock(return_value=host_data)): + with patch.object(self.handler, 'make_session', Mock(return_value=self.session)): + return self.handler.import_data('Department', **kwargs) def test_invalid_importer_key_is_ignored(self): handler = MockBulkImportHandler() @@ -454,12 +477,8 @@ class TestBulkImportHandler(RattailTestCase, ImporterTester): def test_normal_run(self): if self.postgresql(): - with self.host_data(self.sample_data): - with self.local_data({}): - self.import_data() + self.import_data() def test_dry_run(self): if self.postgresql(): - with self.host_data(self.sample_data): - with self.local_data({}): - self.import_data(dry_run=True) + self.import_data(dry_run=True)