Changeset - 12d4fc714b5a
[Not reviewed]
0 2 0
Lance Edgar - 8 years ago 2016-05-14 01:02:47
ledgar@sacfoodcoop.com
Give teeth to `ImportHandler.process_changes()` in new framework

I.e. make it send the diff emails by default.
2 files changed with 78 insertions and 20 deletions:
0 comments (0 inline, 0 general)
rattail/importing/handlers.py
Show inline comments
 
@@ -23,17 +23,24 @@
 
"""
 
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__)
 

	
 

	
 
class ImportHandler(object):
 
@@ -177,12 +184,44 @@ class ImportHandler(object):
 
    def process_changes(self, changes):
 
        """
 
        This method is called any time changes occur, regardless of whether the
 
        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):
 
    """
 
    Handler for imports for which the host data source is represented by a
 
    SQLAlchemy engine and ORM.
rattail/tests/importing/test_handlers.py
Show inline comments
 
@@ -154,14 +154,15 @@ class TestImportHandlerImportData(ImporterTester, unittest.TestCase):
 
        '16oz': {'upc': '00074305001161', 'description': "Apple Cider Vinegar 16oz"},
 
        '32oz': {'upc': '00074305001321', 'description': "Apple Cider Vinegar 32oz"},
 
        '1gal': {'upc': '00074305011283', 'description': "Apple Cider Vinegar 1gal"},
 
    }
 

	
 
    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
 
        # that specific instance, below (because the host/local data context
 
        # managers reference that instance directly)
 
        self.importer._setup(**kwargs)
 
@@ -295,12 +296,38 @@ class TestImportHandlerImportData(ImporterTester, unittest.TestCase):
 
        # 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')
 

	
 
    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()
 

	
 

	
 
class MockFromSQLAlchemyHandler(handlers.FromSQLAlchemyHandler):
 

	
 
@@ -406,40 +433,36 @@ class MockBulkImportHandler(handlers.BulkToPostgreSQLHandler):
 
    def make_session(self):
 
        return Session()
 

	
 

	
 
class TestBulkImportHandler(RattailTestCase, ImporterTester):
 

	
 
    importer_class = MockBulkImporter
 

	
 
    sample_data = {
 
        'grocery': {'number': 1, 'name': "Grocery", 'uuid': 'decd909a194011e688093ca9f40bc550'},
 
        'bulk': {'number': 2, 'name': "Bulk", 'uuid': 'e633d54c194011e687e33ca9f40bc550'},
 
        'hba': {'number': 3, 'name': "HBA", 'uuid': 'e2bad79e194011e6a4783ca9f40bc550'},
 
    }
 

	
 
    def setUp(self):
 
        self.setup_rattail()
 
        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()
 
        self.assertNotIn('InvalidKey', handler.importers)
 
        self.assertEqual(handler.import_data('InvalidKey'), {})
 

	
 
@@ -451,15 +474,11 @@ class TestBulkImportHandler(RattailTestCase, ImporterTester):
 

	
 
    def assert_import_deleted(self, *keys):
 
        pass
 

	
 
    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)
0 comments (0 inline, 0 general)