Changeset - 369451c7c4f8
[Not reviewed]
0 11 1
Lance Edgar (lance) - 3 years ago 2021-12-06 19:59:31
lance@edbob.org
OMG a ridiculous commit to overhaul import handler config etc.

- add `AppHandler.get_designated_import_handlers()` and friends
- exit w/ code 1 when command line makes no sense and must print help
- register all core import/export handlers via setup.py and config
- use "handler key" lookup for all core import/export commands
- stop displaying model list in help for import/export command
- migrate datasync "change" permissions, per tailbone changes
- log warning w/ traceback if loading entry point fails
12 files changed with 386 insertions and 112 deletions:
0 comments (0 inline, 0 general)
rattail/app.py
Show inline comments
 
@@ -30,9 +30,13 @@ import os
 
# import re
 
import tempfile
 

	
 
from rattail.util import load_object, pretty_quantity, progress_loop
 
import six
 

	
 
from rattail.util import (load_object, load_entry_points,
 
                          OrderedDict, progress_loop, pretty_quantity)
 
from rattail.files import temp_path
 
from rattail.mail import send_email
 
from rattail.config import parse_list
 

	
 

	
 
class AppHandler(object):
 
@@ -65,6 +69,9 @@ class AppHandler(object):
 
    def get_title(self, default=None):
 
        return self.config.app_title(default=default)
 

	
 
    def load_entry_points(self, group, **kwargs):
 
        return load_entry_points(group, **kwargs)
 

	
 
    def get_autocompleter(self, key, **kwargs):
 
        """
 
        Returns a new :class:`~rattail.autocomplete.base.Autocompleter`
 
@@ -157,11 +164,166 @@ class AppHandler(object):
 
    # TODO: is it helpful to expose this? or more confusing?
 
    get_mail_handler = get_email_handler
 

	
 
    def all_import_handlers(self, **kwargs):
 
        from rattail.util import load_entry_points
 
    def get_all_import_handlers(self, ignore_errors=True, sort=False,
 
                                **kwargs):
 
        """
 
        Returns *all* Import/Export Handler classes which are known to
 
        exist, i.e.  all which are registered via ``setup.py`` for the
 
        various packages installed.
 

	
 
        This means it will include both "designated" handlers as well
 
        as non-designated.  See
 
        :meth:`get_designated_import_handlers()` if you only want the
 
        designated ones.
 

	
 
        Note that this will return the *Handler classes* and not
 
        *handler instances*.
 

	
 
        :param ignore_errors: Normally any errors which come up during
 
           the loading process are ignored.  Pass ``False`` here to
 
           force errors to raise, e.g. if you are not seeing the
 
           results you expect.
 

	
 
        :param sort: If you like the results can be sorted with a
 
           simple key based on "Source -> Target" labels.
 

	
 
        :returns: List of all registered Import/Export Handler classes.
 
        """
 
        handlers = self.load_entry_points('rattail.importing',
 
                                          ignore_errors=ignore_errors)
 
        handlers = list(handlers.values())
 

	
 
        if sort:
 
            handlers.sort(key=lambda h: (h.get_generic_host_title(),
 
                                         h.get_generic_local_title()))
 

	
 
        return handlers
 

	
 
    def get_designated_import_handlers(self, with_alternates=False, **kwargs):
 
        """
 
        Returns all "designated" import/export handler instances.
 

	
 
        Each "handler type key" can have at most one Handler class
 
        which is "designated" in the config.  This method collects all
 
        registered handlers and then sorts out which one is
 
        designated, for each type key, ultimately returning only the
 
        designated ones.
 

	
 
        Note that this will return the *handler instances* and not
 
        *Handler classes*.
 

	
 
        If you have a type key and just need its designated handler,
 
        see :meth:`get_designated_import_handler()`.
 

	
 
        See also :meth:`get_all_import_handlers()` if you need all
 
        registered Handler classes.
 

	
 
        :param with_alternates: If you specify ``True`` here then each
 
           designated handler returned will have an extra attribute
 
           named ``alternate_handlers``, which will be a list of the
 
           other "available" (registered) handlers which match the
 
           designated handler's type key.
 

	
 
           This is probably most / only useful for the Configuration
 
           UI, to allow admin to change which is designated.
 

	
 
        :returns: List of all designated import/export handler instances.
 
        """
 
        grouped = OrderedDict()
 
        for Handler in self.get_all_import_handlers(**kwargs):
 
            key = Handler.get_key()
 
            grouped.setdefault(key, []).append(Handler)
 

	
 
        def find_designated(key, group):
 
            spec = self.get_designated_import_handler_spec(key)
 
            if spec:
 
                for Handler in group:
 
                    if Handler.get_spec() == spec:
 
                        return Handler
 

	
 
            if len(group) == 1:
 
                return group[0]
 

	
 
            raise RuntimeError("multiple ({}) handlers available but none designated for: {}".format(
 
                len(group), group[0].get_key()))
 

	
 
        designated = []
 
        for key, group in six.iteritems(grouped):
 
            Handler = find_designated(key, group)
 
            if Handler:
 
                # nb. we must instantiate here b/c otherwise if we
 
                # assign the `alternate_handlers` attr onto the class,
 
                # it can affect subclasses as well.  not so with
 
                # instances though
 
                handler = Handler(self.config)
 
                if with_alternates:
 
                    handler.alternate_handlers = [H for H in group
 
                                                  if H is not Handler]
 
                designated.append(handler)
 

	
 
        return designated
 

	
 
    def get_designated_import_handler(self, key, require=False, **kwargs):
 
        """
 
        Return the designated import/export handler instance, per the
 
        given handler type key.
 

	
 
        See also :meth:`get_designated_import_handlers()` if you want
 
        the full set of designated handlers.
 

	
 
        :param key: A "handler type key", e.g.
 
           ``'to_rattail.from_rattail.import'``.
 

	
 
        :param require: Specify ``True`` here if you want an error to
 
           be raised should no handler be found.
 

	
 
        :returns: The import/export handler instance corresponding to
 
           the given key.  If no handler can be found, then ``None``
 
           is returned, unless ``require`` param is true, in which
 
           case error is raised.
 
        """
 
        # first try to fetch the handler per designated spec
 
        spec = self.get_designated_import_handler_spec(key, require=require,
 
                                                       **kwargs)
 
        if spec:
 
            Handler = load_object(spec)
 
            return Handler(self.config)
 

	
 
        # nothing was designated, so leverage logic which already
 
        # sorts out which handler is "designated" for given key
 
        designated = self.get_designated_import_handlers(
 
            ignore_errors=not require)
 
        for handler in designated:
 
            if handler.get_key() == key:
 
                return handler
 

	
 
        if require:
 
            raise RuntimeError("Cannot locate designated handler for key: {}".format(
 
                key))
 

	
 
    def get_designated_import_handler_spec(self, key, require=False, **kwargs):
 
        spec = self.config.get('rattail.importing',
 
                               '{}.handler'.format(key))
 
        if spec:
 
            return spec
 

	
 
        legacy_setting = self.config.get('rattail.importing',
 
                                         '{}.legacy_handler_setting'.format(key))
 
        if legacy_setting:
 
            legacy_setting = parse_list(legacy_setting)
 
            if len(legacy_setting) == 2:
 
                section, option = legacy_setting
 
                spec = self.config.get(section, option)
 
                if spec:
 
                    return spec
 

	
 
        spec = self.config.get('rattail.importing',
 
                               '{}.default_handler'.format(key))
 
        if spec:
 
            return spec
 

	
 
        handlers = load_entry_points('rattail.importing')
 
        return list(handlers.values())
 
        if require:
 
            raise RuntimeError("Cannot locate handler spec for key: {}".format(key))
 

	
 
    def get_membership_handler(self, **kwargs):
 
        """
rattail/commands/core.py
Show inline comments
 
@@ -262,7 +262,7 @@ Commands:\n""".format(self))
 
        args = parser.parse_args(list(args))
 
        if not args or not args.command:
 
            self.print_help()
 
            return
 
            sys.exit(1)
 

	
 
        # TODO: can we make better args so this is handled by argparse somehow?
 
        if args.versioning and args.no_versioning:
 
@@ -289,7 +289,7 @@ Commands:\n""".format(self))
 
                return
 
        else:
 
            self.print_help()
 
            return
 
            sys.exit(1)
 

	
 
        # Okay, we should be done needing to print help messages.  Now it's
 
        # safe to redirect STDOUT/STDERR, if necessary.
rattail/commands/importing.py
Show inline comments
 
@@ -44,25 +44,45 @@ class ImportSubcommand(Subcommand):
 
    """
 
    Base class for subcommands which use the (new) data importing system.
 
    """
 
    handler_key = None
 

	
 
    # TODO: should remove this? or perhaps is still useful for simple
 
    # custom commands to use, which need not worry about inheritance
 
    # and alternate handlers available etc.
 
    handler_spec = None
 

	
 
    # TODO: move this into Subcommand or something..
 
    parent_name = None
 
    def __init__(self, *args, **kwargs):
 

	
 
        # TODO: should remove this? or perhaps is still useful for
 
        # simple custom commands to use, which need not worry about
 
        # inheritance and alternate handlers available etc.
 
        if 'handler_spec' in kwargs:
 
            self.handler_spec = kwargs.pop('handler_spec')
 

	
 
        super(ImportSubcommand, self).__init__(*args, **kwargs)
 
        if self.parent:
 
            self.parent_name = self.parent.name
 

	
 
    def get_handler_factory(self, **kwargs):
 
        """
 
        Subclasses must override this, and return a callable that creates an
 
        import handler instance which the command should use.
 
        Should return a Handler factory (e.g. class) which will later
 
        be called to create a handler instance.
 
        """
 
        # TODO: should remove this? or perhaps is still useful for
 
        # simple custom commands to use, which need not worry about
 
        # inheritance and alternate handlers available etc.
 
        if self.handler_spec:
 
            return load_object(self.handler_spec)
 
        raise NotImplementedError
 

	
 
        if self.handler_key:
 
            handler = self.app.get_designated_import_handler(self.handler_key)
 
            return type(handler)
 

	
 
        raise RuntimeError("Cannot locate a handler to use!  You "
 
                           "should probably define `{}.handler_key`"
 
                           "".format(self.__class__.__name__))
 

	
 
    def get_handler(self, **kwargs):
 
        """
 
@@ -103,12 +123,6 @@ class ImportSubcommand(Subcommand):
 
        doc = ("Which data models to import.  If you specify any, then only "
 
               "data for those models will be imported.  If you do not specify "
 
               "any, then all *default* models will be imported.")
 
        try:
 
            handler = self.get_handler()
 
        except NotImplementedError:
 
            pass
 
        else:
 
            doc += "  Supported models are: ({})".format(', '.join(handler.get_importer_keys()))
 
        parser.add_argument('models', nargs='*', metavar='MODEL', help=doc)
 

	
 
        # make batches
 
@@ -299,16 +313,7 @@ class ImportCSV(ImportFileSubcommand):
 
    """
 
    name = 'import-csv'
 
    description = __doc__.strip()
 
    default_handler_spec = 'rattail.importing.csv:FromCSVToRattail'
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('rattail.importing', 'csv.handler',
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 
    handler_key = 'to_rattail.from_csv.import'
 

	
 

	
 
class ImportIFPS(ImportFileSubcommand):
 
@@ -317,16 +322,7 @@ class ImportIFPS(ImportFileSubcommand):
 
    """
 
    name = 'import-ifps'
 
    description = __doc__.strip()
 
    default_handler_spec = 'rattail.importing.ifps:FromIFPSToRattail'
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('rattail.importing', 'ifps.handler',
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 
    handler_key = 'to_rattail.from_ifps.import'
 

	
 

	
 
class ExportFileSubcommand(ImportSubcommand):
 
@@ -357,16 +353,7 @@ class ExportCSV(ExportFileSubcommand):
 
    """
 
    name = 'export-csv'
 
    description = __doc__.strip()
 
    default_handler_spec = 'rattail.importing.exporters:FromRattailToCSV'
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('rattail.exporting', 'csv.handler',
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 
    handler_key = 'to_csv.from_rattail.export'
 

	
 

	
 
class ExportRattail(ImportSubcommand):
 
@@ -375,18 +362,9 @@ class ExportRattail(ImportSubcommand):
 
    """
 
    name = 'export-rattail'
 
    description = __doc__.strip()
 
    default_handler_spec = 'rattail.importing.rattail:FromRattailToRattailExport'
 
    handler_key = 'to_rattail.from_rattail.export'
 
    default_dbkey = 'host'
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('rattail.exporting', 'rattail.handler',
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 

	
 
    def add_parser_args(self, parser):
 
        super(ExportRattail, self).add_parser_args(parser)
 
        parser.add_argument('--dbkey', metavar='KEY', default=self.default_dbkey,
 
@@ -404,18 +382,6 @@ class ImportToRattail(ImportSubcommand):
 
    """
 
    Generic base class for commands which import *to* a Rattail system.
 
    """
 
    # subclass must set these!
 
    handler_key = None
 
    default_handler_spec = None
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('rattail.importing', '{}.handler'.format(self.handler_key),
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 

	
 

	
 
class ImportRattail(ImportToRattail):
 
@@ -424,8 +390,7 @@ class ImportRattail(ImportToRattail):
 
    """
 
    name = 'import-rattail'
 
    description = __doc__.strip()
 
    handler_key = 'rattail'
 
    default_handler_spec = 'rattail.importing.rattail:FromRattailToRattailImport'
 
    handler_key = 'to_rattail.from_rattail.import'
 
    accepts_dbkey_param = True
 

	
 
    def add_parser_args(self, parser):
 
@@ -450,8 +415,7 @@ class ImportRattailBulk(ImportRattail):
 
    """
 
    name = 'import-rattail-bulk'
 
    description = __doc__.strip()
 
    handler_key = 'rattail_bulk'
 
    default_handler_spec = 'rattail.importing.rattail_bulk:BulkFromRattailToRattail'
 
    handler_key = 'to_rattail.from_rattail_bulk.import'
 

	
 

	
 
class ImportSampleData(ImportToRattail):
 
@@ -460,8 +424,7 @@ class ImportSampleData(ImportToRattail):
 
    """
 
    name = 'import-sample'
 
    description = __doc__.strip()
 
    handler_key = 'sample'
 
    default_handler_spec = 'rattail.importing.sample:FromSampleToRattail'
 
    handler_key = 'to_rattail.from_sample.import'
 

	
 

	
 
class ImportVersions(ImportRattail):
 
@@ -470,8 +433,7 @@ class ImportVersions(ImportRattail):
 
    """
 
    name = 'import-versions'
 
    description = __doc__.strip()
 
    handler_key = 'versions'
 
    default_handler_spec = 'rattail.importing.versions:FromRattailToRattailVersions'
 
    handler_key = 'to_rattail_versions.from_rattail.import'
 
    accepts_dbkey_param = False
 
    default_comment = "import catch-up versions"
 

	
rattail/db/__init__.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2018 Lance Edgar
 
#  Copyright © 2010-2021 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -139,3 +139,83 @@ class ConfigExtension(BaseExtension):
 

	
 
            # TODO: This should be removed, it sets 'record changes' globally.
 
            configure_session(config, Session)
 

	
 
            # rattail export-csv
 
            config.setdefault('rattail.importing', 'to_csv.from_rattail.export.default_handler',
 
                              'rattail.importing.exporters:FromRattailToCSV')
 
            config.setdefault('rattail.importing', 'to_csv.from_rattail.export.default_cmd',
 
                              'rattail export-csv')
 
            config.setdefault('rattail.importing', 'to_csv.from_rattail.export.legacy_handler_setting',
 
                              'rattail.exporting, csv.handler')
 

	
 
            # rattail export-rattail
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.export.default_handler',
 
                              'rattail.importing.rattail:FromRattailToRattailExport')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.export.default_cmd',
 
                              'rattail export-rattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.export.legacy_handler_setting',
 
                              'rattail.exporting, rattail.handler')
 

	
 
            # rattail import-csv
 
            config.setdefault('rattail.importing', 'to_rattail.from_csv.import.default_handler',
 
                              'rattail.importing.csv:FromCSVToRattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_csv.import.default_cmd',
 
                              'rattail import-csv')
 
            config.setdefault('rattail.importing', 'to_rattail.from_csv.import.legacy_handler_setting',
 
                              'rattail.importing, csv.handler')
 

	
 
            # rattail import-ifps
 
            config.setdefault('rattail.importing', 'to_rattail.from_ifps.import.default_handler',
 
                              'rattail.importing.ifps:FromIFPSToRattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_ifps.import.default_cmd',
 
                              'rattail import-ifps')
 
            config.setdefault('rattail.importing', 'to_rattail.from_ifps.import.legacy_handler_setting',
 
                              'rattail.importing, ifps.handler')
 

	
 
            # rattail import-rattail
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.import.default_handler',
 
                              'rattail.importing.rattail:FromRattailToRattailImport')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.import.default_cmd',
 
                              'rattail import-rattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail.import.legacy_handler_setting',
 
                              'rattail.importing, rattail.handler')
 

	
 
            # rattail import-rattail-bulk
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail_bulk.import.default_handler',
 
                              'rattail.importing.rattail_bulk:BulkFromRattailToRattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail_bulk.import.default_cmd',
 
                              'rattail import-rattail-bulk')
 
            config.setdefault('rattail.importing', 'to_rattail.from_rattail_bulk.import.legacy_handler_setting',
 
                              'rattail.importing, rattail_bulk.handler')
 

	
 
            # rattail import-sample
 
            config.setdefault('rattail.importing', 'to_rattail.from_sample.import.default_handler',
 
                              'rattail.importing.sample:FromSampleToRattail')
 
            config.setdefault('rattail.importing', 'to_rattail.from_sample.import.default_cmd',
 
                              'rattail import-sample')
 
            config.setdefault('rattail.importing', 'to_rattail.from_sample.import.legacy_handler_setting',
 
                              'rattail.importing, sample.handler')
 

	
 
            # rattail import-versions
 
            config.setdefault('rattail.importing', 'to_rattail_versions.from_rattail.import.default_handler',
 
                              'rattail.importing.versions:FromRattailToRattailVersions')
 
            config.setdefault('rattail.importing', 'to_rattail_versions.from_rattail.import.default_cmd',
 
                              'rattail import-versions')
 
            config.setdefault('rattail.importing', 'to_rattail_versions.from_rattail.import.legacy_handler_setting',
 
                              'rattail.importing, versions.handler')
 

	
 
            # trainwreck export-trainwreck
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.export.default_handler',
 
                              'rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckExport')
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.export.default_cmd',
 
                              'trainwreck export-trainwreck')
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.export.legacy_handler_setting',
 
                              'trainwreck.exporting, trainwreck.handler')
 

	
 
            # trainwreck import-trainwreck
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.import.default_handler',
 
                              'rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckImport')
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.import.default_cmd',
 
                              'trainwreck import-trainwreck')
 
            config.setdefault('rattail.importing', 'to_trainwreck.from_trainwreck.import.legacy_handler_setting',
 
                              'trainwreck.importing, trainwreck.handler')
rattail/db/alembic/versions/24577b3bda93_migrate_datasync_perms.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8; -*-
 
"""migrate datasync perms
 

	
 
Revision ID: 24577b3bda93
 
Revises: 678a32b6cb19
 
Create Date: 2021-12-06 10:17:33.048549
 

	
 
"""
 

	
 
from __future__ import unicode_literals
 

	
 
# revision identifiers, used by Alembic.
 
revision = '24577b3bda93'
 
down_revision = '678a32b6cb19'
 
branch_labels = None
 
depends_on = None
 

	
 
from alembic import op
 
import sqlalchemy as sa
 
import rattail.db.types
 

	
 

	
 
permission = sa.sql.table('permission', sa.sql.column('permission'))
 

	
 

	
 
def rename_perm(oldname, newname):
 
    op.execute(permission.update()\
 
               .where(permission.c.permission == oldname)\
 
               .values({'permission': newname}))
 

	
 

	
 
def upgrade():
 
    rename_perm('datasync.bulk_delete', 'datasync_changes.bulk_delete')
 
    rename_perm('datasync.delete', 'datasync_changes.delete')
 
    rename_perm('datasync.list', 'datasync_changes.list')
 
    rename_perm('datasync.view', 'datasync_changes.view')
 

	
 

	
 
def downgrade():
 
    rename_perm('datasync_changes.bulk_delete', 'datasync.bulk_delete')
 
    rename_perm('datasync_changes.delete', 'datasync.delete')
 
    rename_perm('datasync_changes.list', 'datasync.list')
 
    rename_perm('datasync_changes.view', 'datasync.view')
rattail/importing/csv.py
Show inline comments
 
@@ -45,6 +45,9 @@ from rattail.config import parse_bool
 

	
 
class FromCSVToSQLAlchemyMixin(object):
 

	
 
    host_key = 'csv'
 
    generic_host_title = "CSV"
 

	
 
    # subclass must define this
 
    ToParent = None
 

	
rattail/importing/handlers.py
Show inline comments
 
@@ -101,7 +101,7 @@ class ImportHandler(object):
 

	
 
    @classmethod
 
    def get_key(cls):
 
        return 'to_{}.from_{}'.format(cls.local_key, cls.host_key)
 
        return 'to_{}.from_{}.{}'.format(cls.local_key, cls.host_key, cls.direction)
 

	
 
    @classmethod
 
    def get_spec(cls):
 
@@ -712,6 +712,8 @@ class ToCSVHandler(ToFileHandler):
 
    """
 
    Handler for imports which target CSV file(s) on the local side.
 
    """
 
    local_key = 'csv'
 
    generic_local_title = "CSV"
 

	
 

	
 
# TODO: this is still pretty hacky, but needed to get it in here for now
rattail/importing/ifps.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2020 Lance Edgar
 
#  Copyright © 2010-2021 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -46,6 +46,8 @@ class FromIFPSToRattail(FromFileHandler, importing.ToRattailHandler):
 
    """
 
    Handler for IFPS -> Rattail data import.
 
    """
 
    host_key = 'ifps'
 
    generic_host_title = "IFPS"
 
    host_title = "IFPS"
 
    local_title = "Rattail"
 

	
rattail/trainwreck/commands.py
Show inline comments
 
@@ -69,27 +69,35 @@ class ImportToTrainwreck(commands.ImportSubcommand):
 
    """
 
    Generic base class for commands which import *to* a Trainwreck DB.
 
    """
 
    # subclass must set these!
 
    handler_key = None
 
    # TODO: deprecate / remove this
 
    default_handler_spec = None
 

	
 
    # TODO: deprecate / remove this
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('trainwreck.importing', '{}.handler'.format(self.handler_key))
 
            if not spec:
 
                spec = self.config.get('rattail.trainwreck.importing', '{}.handler'.format(self.handler_key))
 
                if spec:
 
                    warnings.warn(("Handler override uses deprecated config.  "
 
                                   "Please change the setting name used, from "
 
                                   "`[rattail.trainwreck.importing] {0}.handler` "
 
                                   "to `[trainwreck.importing] {0}.handler`")\
 
                                  .format(self.handler_key), DeprecationWarning)
 
                else:
 
                    spec = self.default_handler_spec
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 

	
 
        # only do this nonsense if we still have old-style default
 
        if self.default_handler_spec:
 

	
 
            if self.config:
 
                # TODO: this one needs some work
 
                spec = self.config.get('trainwreck.importing', '{}.handler'.format(self.handler_key))
 
                if not spec:
 
                    spec = self.config.get('rattail.trainwreck.importing', '{}.handler'.format(self.handler_key))
 
                    if spec:
 
                        warnings.warn(("Handler override uses deprecated config.  "
 
                                       "Please change the setting name used, from "
 
                                       "`[rattail.trainwreck.importing] {0}.handler` "
 
                                       "to `[trainwreck.importing] {0}.handler`")\
 
                                      .format(self.handler_key), DeprecationWarning)
 
                    else:
 
                        spec = self.default_handler_spec
 
            else:
 
                # just use default, for sake of cmd line help
 
                spec = self.default_handler_spec
 
            return load_object(spec)
 

	
 
        # otherwise just do normal logic
 
        return super(ImportToTrainwreck, self).get_handler_factory(**kwargs)
 

	
 

	
 
class ExportTrainwreck(commands.ImportSubcommand):
 
@@ -98,16 +106,7 @@ class ExportTrainwreck(commands.ImportSubcommand):
 
    """
 
    name = 'export-trainwreck'
 
    description = __doc__.strip()
 
    default_handler_spec = 'rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckExport'
 

	
 
    def get_handler_factory(self, **kwargs):
 
        if self.config:
 
            spec = self.config.get('trainwreck.exporting', 'trainwreck.handler',
 
                                   default=self.default_handler_spec)
 
        else:
 
            # just use default, for sake of cmd line help
 
            spec = self.default_handler_spec
 
        return load_object(spec)
 
    handler_key = 'to_trainwreck.from_trainwreck.export'
 

	
 
    def add_parser_args(self, parser):
 
        super(ExportTrainwreck, self).add_parser_args(parser)
 
@@ -128,8 +127,7 @@ class ImportTrainwreck(ImportToTrainwreck):
 
    """
 
    name = 'import-trainwreck'
 
    description = __doc__.strip()
 
    handler_key = 'trainwreck'
 
    default_handler_spec = 'rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckImport'
 
    handler_key = 'to_trainwreck.from_trainwreck.import'
 
    accepts_dbkey_param = True
 

	
 
    def add_parser_args(self, parser):
rattail/trainwreck/importing/trainwreck.py
Show inline comments
 
@@ -41,6 +41,8 @@ class FromTrainwreckHandler(FromSQLAlchemyHandler):
 
    """
 
    Base class for import handlers which have a Trainwreck DB as data source on the host side.
 
    """
 
    host_key = 'trainwreck'
 
    generic_host_title = "Trainwreck"
 
    host_title = "Trainwreck"
 

	
 
    def make_host_session(self):
 
@@ -51,6 +53,8 @@ class ToTrainwreckHandler(ToSQLAlchemyHandler):
 
    """
 
    Base class for import handlers which target a Trainwreck DB on the local side.
 
    """
 
    local_key = 'trainwreck'
 
    generic_local_title = "Trainwreck"
 
    local_title = "Trainwreck"
 

	
 
    def make_session(self):
rattail/util.py
Show inline comments
 
@@ -31,6 +31,7 @@ import sys
 
import datetime
 
import decimal
 
import subprocess
 
import logging
 

	
 
import six
 
from pkg_resources import iter_entry_points
 
@@ -41,6 +42,8 @@ except ImportError: # pragma no cover
 
    from ordereddict import OrderedDict
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
# generic singleton to indicate an arg which isn't set etc.
 
NOTSET = object()
 

	
 
@@ -144,7 +147,7 @@ def load_object(specifier):
 
    return getattr(module, name)
 

	
 

	
 
def load_entry_points(group):
 
def load_entry_points(group, ignore_errors=False):
 
    """
 
    Load a set of ``setuptools``-style entry points.
 

	
 
@@ -157,7 +160,15 @@ def load_entry_points(group):
 
    """
 
    entry_points = {}
 
    for entry_point in iter_entry_points(group):
 
        entry_points[entry_point.name] = entry_point.load()
 
        try:
 
            ep = entry_point.load()
 
        except:
 
            if not ignore_errors:
 
                raise
 
            log.warning("failed to load entry point: %s", entry_point,
 
                        exc_info=True)
 
        else:
 
            entry_points[entry_point.name] = ep
 
    return entry_points
 

	
 

	
setup.py
Show inline comments
 
@@ -240,7 +240,14 @@ new-report = rattail.features.newreport:NewReportFeature
 
new-table = rattail.features.newtable:NewTableFeature
 

	
 
[rattail.importing]
 
to_rattail.from_versions = rattail.importing.versions:FromRattailToRattailVersions
 
to_csv.from_rattail.export = rattail.importing.exporters:FromRattailToCSV
 
to_rattail.from_csv.import = rattail.importing.csv:FromCSVToRattail
 
to_rattail.from_ifps.import = rattail.importing.ifps:FromIFPSToRattail
 
to_rattail.from_rattail.export = rattail.importing.rattail:FromRattailToRattailExport
 
to_rattail.from_rattail.import = rattail.importing.rattail:FromRattailToRattailImport
 
to_rattail.from_versions.import = rattail.importing.versions:FromRattailToRattailVersions
 
to_trainwreck.from_trainwreck.export = rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckExport
 
to_trainwreck.from_trainwreck.import = rattail.trainwreck.importing.trainwreck:FromTrainwreckToTrainwreckImport
 

	
 
[rattail.reports]
 
customer_mailing = rattail.reporting.customer_mailing:CustomerMailing
0 comments (0 inline, 0 general)