Esempio n. 1
0
    def __init__(self, path, config=None, step=None, step_done=None, id=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))
        self.step = step
        self.step_done = step_done
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        if not self.path.exists():
            self.path.mkdir()
        self.id = id
        if self.images:
            self.pages_shot = len(self.images)
        else:
            self.pages_shot = 0
        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)
        self._capture_lock = threading.RLock()
        self.active = False
        self._devices = None
        self._pluginmanager = None

        # Instantiate plugins
        self.plugins = [cls(self.config) for cls in
                        plugin.get_plugins(*self.config["plugins"].get())
                        .values()]
Esempio n. 2
0
    def __init__(self, path, config=None, step=None, step_done=None, id=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))
        self.step = step
        self.step_done = step_done
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        if not self.path.exists():
            self.path.mkdir()
        self.id = id
        if self.images:
            self.pages_shot = len(self.images)
        else:
            self.pages_shot = 0
        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)
        self._capture_lock = threading.RLock()
        self.active = False
        self._devices = None
        self._pluginmanager = None

        # Instantiate plugins
        self.plugins = [
            cls(self.config) for cls in plugin.get_plugins(
                *self.config["plugins"].get()).values()
        ]
Esempio n. 3
0
    def on_update_plugin_selection(self, event):
        selection = self.plugin_select.selection()
        self.selected_plugins = list(selection)
        try:
            exts = [name for name, cls in plugin.get_plugins(*selection)
                    .iteritems() if issubclass(cls, plugin.ProcessHookMixin)]
        except plugin.ExtensionException as e:
            exts = []
            failed_ext = e.extension
            messagebox.showerror(message=e.message)
            #self.plugin_select.selection_remove(failed_ext)
            ext_id = self.plugin_select.index(failed_ext)
            self.plugin_select.delete(failed_ext)
            self.plugin_select.insert('', ext_id, failed_ext, text=failed_ext,
                                      tags=["missingdep"])

            selection = tuple(x for x in selection if x != failed_ext)
        for item in selection:
            if item in exts:
                if not self.processorder_tree.exists(item):
                    self.processorder_tree.insert('', 'end', item, text=item)
            else:
                continue
        for item in self.processorder_tree.get_children():
            if item not in selection:
                self.processorder_tree.delete(item)
        self.update_plugin_config(selection)
Esempio n. 4
0
def _setup_processing_pipeline(config):
    """ Display dialog to configure order of postprocessing plugins and update
    the configuration accordingly.

    :param config:      Currently active global configuration
    :type config:       :py:class:`spreads.config.Configuration`
    """
    # Only get names of postprocessing plugins. For this we have to load all
    # enabled plugins first and check if they implement the correct hook.
    exts = [name for name, cls in plugin.get_plugins(*config["plugins"].get())
            .iteritems() if issubclass(cls, plugin.ProcessHooksMixin)]
    if not exts:
        return
    print("The following postprocessing plugins were detected:")
    print("\n".join(" - {0}".format(ext) for ext in exts))
    while True:
        answer = raw_input("Please enter the extensions in the order that they"
                           " should be invoked, separated by commas or hit"
                           " enter to keep the current order:\n")
        if not answer:
            plugins = exts
        else:
            plugins = [x.strip() for x in answer.split(',')]
        if any(x not in exts for x in plugins):
            print(colorize("At least one of the entered extensions was not"
                           "found, please try again!", colorama.Fore.RED))
        else:
            break
    # Append other plugins after the postprocessing plugins
    config["plugins"] = plugins + [x for x in config["plugins"].get()
                                   if x not in plugins]
Esempio n. 5
0
def _setup_processing_pipeline(config):
    exts = [
        name for name, cls in plugin.get_plugins(
            *config["plugins"].get()).iteritems()
        if issubclass(cls, plugin.ProcessHookMixin)
    ]
    if not exts:
        return
    print("The following postprocessing plugins were detected:")
    print("\n".join(" - {0}".format(ext) for ext in exts))
    while True:
        answer = raw_input("Please enter the extensions in the order that they"
                           " should be invoked, separated by commas or hit"
                           " enter to keep the current order:\n")
        if not answer:
            plugins = exts
        else:
            plugins = [x.strip() for x in answer.split(',')]
        if any(x not in exts for x in plugins):
            print(
                colorize(
                    "At least one of the entered extensions was not"
                    "found, please try again!", colorama.Fore.RED))
        else:
            break
    config["plugins"] = plugins + [
        x for x in config["plugins"].get() if x not in plugins
    ]
Esempio n. 6
0
def _list_plugins():
    """ Get a the names of all activated plugins grouped by type.

    :rtype: dict (key: unicode, value: list of unicode)
    """
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    return {
        'capture': [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.CaptureHooksMixin)
        ],
        'trigger': [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.TriggerHooksMixin)
        ],
        'postprocessing': [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.ProcessHooksMixin)
        ],
        'output': [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.OutputHooksMixin)
        ]
    }
Esempio n. 7
0
    def __init__(self, path, config=None, metadata=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))
        self.status = {
            'step': None,
            'step_progress': None,
            'prepared': False
        }
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        is_new = not self.path.exists()
        try:
            self.bag = bagit.Bag(unicode(self.path))
        except bagit.BagError:
            # Convert non-bagit directories from older versions
            self.bag = bagit.Bag.convert_directory(unicode(self.path))
        if not self.slug:
            self.slug = util.slugify(unicode(self.path.name))
        if not self.id:
            self.id = unicode(uuid.uuid4())
        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)
        self._metadata = Metadata(self.path)

        if metadata:
            self.metadata = metadata

        self._capture_lock = threading.RLock()
        self._devices = None
        self._pluginmanager = None
        self._threadpool = concfut.ThreadPoolExecutor(max_workers=1)
        self._pending_tasks = []

        # Filter out subcommand plugins, since these are not workflow-specific
        plugin_classes = [
            (name, cls)
            for name, cls in plugin.get_plugins(*self.config["plugins"]
                                                .get()).iteritems()
            if not cls.__bases__ == (plugin.SubcommandHookMixin,)]
        self.plugins = [cls(self.config) for name, cls in plugin_classes]
        self.config['plugins'] = [name for name, cls in plugin_classes]

        # Save configuration
        self._save_config()

        self.pages = self._load_pages()
        self.table_of_contents = self.load_toc()

        if is_new:
            on_created.send(self, workflow=self)
Esempio n. 8
0
def list_plugins():
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    return {
        'capture': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.CaptureHooksMixin)],
        'trigger': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.TriggerHooksMixin)],
        'postprocessing': [name for name, cls in plugins.iteritems()
                           if issubclass(cls, plugin.ProcessHookMixin)],
        'output': [name for name, cls in plugins.iteritems()
                   if issubclass(cls, plugin.OutputHookMixin)]
    }
Esempio n. 9
0
def list_plugins():
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    return {
        'capture': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.CaptureHooksMixin)],
        'trigger': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.TriggerHooksMixin)],
        'postprocessing': [name for name, cls in plugins.iteritems()
                           if issubclass(cls, plugin.ProcessHookMixin)],
        'output': [name for name, cls in plugins.iteritems()
                   if issubclass(cls, plugin.OutputHookMixin)]
    }
Esempio n. 10
0
def get_plugin_templates():
    """ Return the names of all globally activated plugins and their
        configuration templates.
    """
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    scanner_exts = [
        name for name, cls in plugins.iteritems() if any(
            issubclass(cls, mixin)
            for mixin in (plugin.CaptureHooksMixin, plugin.TriggerHooksMixin))
    ]
    processor_exts = [
        name for name, cls in plugins.iteritems() if any(
            issubclass(cls, mixin)
            for mixin in (plugin.ProcessHookMixin, plugin.OutputHookMixin))
    ]
    if app.config['mode'] == 'scanner':
        templates = {
            section: config.templates[section]
            for section in config.templates
            if section in scanner_exts or section == 'device'
        }
    elif app.config['mode'] == 'processor':
        templates = {
            section: config.templates[section]
            for section in config.templates if section in processor_exts
        }
    elif app.config['mode'] == 'full':
        templates = {
            section: config.templates[section]
            for section in config.templates if section != 'core'
        }
    rv = dict()
    for plugname, options in templates.iteritems():
        if options is None:
            continue
        for key, option in options.iteritems():
            if option.selectable:
                value = [config[plugname][key].get()]
                value += [x for x in option.value if x not in value]
            else:
                value = config[plugname][key].get()
            if not plugname in rv:
                rv[plugname] = dict()
            rv[plugname][key] = dict(value=value,
                                     docstring=option.docstring,
                                     selectable=option.selectable)
    return rv
Esempio n. 11
0
    def __init__(self, path, config=None, metadata=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))
        self.status = {'step': None, 'step_progress': None, 'prepared': False}
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        is_new = not self.path.exists()
        try:
            self.bag = bagit.Bag(unicode(self.path))
        except bagit.BagError:
            # Convert non-bagit directories from older versions
            self.bag = bagit.Bag.convert_directory(unicode(self.path))
        if not self.slug:
            self.slug = util.slugify(unicode(self.path.name))
        if not self.id:
            self.id = unicode(uuid.uuid4())
        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)
        if metadata:
            self.metadata = metadata
        self._capture_lock = threading.RLock()
        self._devices = None
        self._pluginmanager = None
        self._pool_executor = None

        # Filter out subcommand plugins, since these are not workflow-specific
        plugin_classes = [
            (name, cls) for name, cls in plugin.get_plugins(
                *self.config["plugins"].get()).iteritems()
            if not cls.__bases__ == (plugin.SubcommandHookMixin, )
        ]
        self.plugins = [cls(self.config) for name, cls in plugin_classes]
        self.config['plugins'] = [name for name, cls in plugin_classes]

        # Save configuration
        self._save_config()

        self.pages = self._load_pages()
        self.table_of_contents = self.load_toc()

        if is_new:
            on_created.send(self, workflow=self)
Esempio n. 12
0
def _list_plugins():
    """ Get a the names of all activated plugins grouped by type.

    :rtype: dict (key: unicode, value: list of unicode)
    """
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    return {
        'capture': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.CaptureHooksMixin)],
        'trigger': [name for name, cls in plugins.iteritems()
                    if issubclass(cls, plugin.TriggerHooksMixin)],
        'postprocessing': [name for name, cls in plugins.iteritems()
                           if issubclass(cls, plugin.ProcessHooksMixin)],
        'output': [name for name, cls in plugins.iteritems()
                   if issubclass(cls, plugin.OutputHooksMixin)]
    }
Esempio n. 13
0
def _setup_processing_pipeline(config):
    exts = [name for name, cls in plugin.get_plugins(*config["plugins"].get())
            .iteritems() if issubclass(cls, plugin.ProcessHookMixin)]
    if not exts:
        return
    print("The following postprocessing plugins were detected:")
    print("\n".join(" - {0}".format(ext) for ext in exts))
    while True:
        answer = raw_input("Please enter the extensions in the order that they"
                           " should be invoked, separated by commas:\n")
        plugins = [x.strip() for x in answer.split(',')]
        if any(x not in exts for x in plugins):
            print(colorize("At least one of the entered extensions was not"
                           "found, please try again!", colorama.Fore.RED))
        else:
            break
    config["plugins"] = plugins + [x for x in config["plugins"].get()
                                   if x not in plugins]
Esempio n. 14
0
    def on_update_plugin_selection(self, event):
        """ Callback for when the user toggles a plugin.

        Tries to load the newly selected plugins. If loading fails, a dialog
        with the cause of failure will be displayed and the plugin will be
        highlighted in the list and made inactive. If successful, the plugin
        will be added to the 'postprocessing order' widget (if it implements
        :py:class:`spreads.plugin.ProcessHooksMixin`) and the configuration
        will be updated.

        :param event:   Event from Tkinter
        :type event:    :py:class:`Tkinter.Event`
        """
        selection = self.plugin_select.selection()
        self.selected_plugins = list(selection)
        try:
            exts = [
                name
                for name, cls in plugin.get_plugins(*selection).iteritems()
                if issubclass(cls, plugin.ProcessHooksMixin)
            ]
        except plugin.ExtensionException as e:
            exts = []
            failed_ext = e.extension
            messagebox.showerror(message=e.message)
            ext_id = self.plugin_select.index(failed_ext)
            self.plugin_select.delete(failed_ext)
            self.plugin_select.insert('',
                                      ext_id,
                                      failed_ext,
                                      text=failed_ext,
                                      tags=["missingdep"])

            selection = tuple(x for x in selection if x != failed_ext)
        for item in selection:
            if item in exts:
                if not self.processorder_tree.exists(item):
                    self.processorder_tree.insert('', 'end', item, text=item)
            else:
                continue
        for item in self.processorder_tree.get_children():
            if item not in selection:
                self.processorder_tree.delete(item)
        self.update_plugin_config(selection)
Esempio n. 15
0
def get_plugin_templates():
    """ Return the names of all globally activated plugins and their
        configuration templates.
    """
    config = app.config['default_config']
    plugins = plugin.get_plugins(*config['plugins'].get())
    scanner_exts = [name for name, cls in plugins.iteritems()
                    if any(issubclass(cls, mixin) for mixin in
                           (plugin.CaptureHooksMixin,
                            plugin.TriggerHooksMixin))]
    processor_exts = [name for name, cls in plugins.iteritems()
                      if any(issubclass(cls, mixin) for mixin in
                             (plugin.ProcessHookMixin,
                              plugin.OutputHookMixin))]
    if app.config['mode'] == 'scanner':
        templates = {section: config.templates[section]
                     for section in config.templates
                     if section in scanner_exts or section == 'device'}
    elif app.config['mode'] == 'processor':
        templates = {section: config.templates[section]
                     for section in config.templates
                     if section in processor_exts}
    elif app.config['mode'] == 'full':
        templates = {section: config.templates[section]
                     for section in config.templates
                     if section != 'core'}
    rv = dict()
    for plugname, options in templates.iteritems():
        if options is None:
            continue
        for key, option in options.iteritems():
            if option.selectable:
                value = [config[plugname][key].get()]
                value += [x for x in option.value if x not in value]
            else:
                value = config[plugname][key].get()
            if not plugname in rv:
                rv[plugname] = dict()
            rv[plugname][key] = dict(value=value,
                                     docstring=option.docstring,
                                     selectable=option.selectable)
    return rv
Esempio n. 16
0
    def on_update_plugin_selection(self, event):
        """ Callback for when the user toggles a plugin.

        Tries to load the newly selected plugins. If loading fails, a dialog
        with the cause of failure will be displayed and the plugin will be
        highlighted in the list and made inactive. If successful, the plugin
        will be added to the 'postprocessing order' widget (if it implements
        :py:class:`spreads.plugin.ProcessHooksMixin`) and the configuration
        will be updated.

        :param event:   Event from Tkinter
        :type event:    :py:class:`Tkinter.Event`
        """
        selection = self.plugin_select.selection()
        self.selected_plugins = list(selection)
        try:
            exts = [name for name, cls in plugin.get_plugins(*selection)
                    .iteritems() if issubclass(cls, plugin.ProcessHooksMixin)]
        except plugin.ExtensionException as e:
            exts = []
            failed_ext = e.extension
            messagebox.showerror(message=e.message)
            ext_id = self.plugin_select.index(failed_ext)
            self.plugin_select.delete(failed_ext)
            self.plugin_select.insert('', ext_id, failed_ext, text=failed_ext,
                                      tags=["missingdep"])

            selection = tuple(x for x in selection if x != failed_ext)
        for item in selection:
            if item in exts:
                if not self.processorder_tree.exists(item):
                    self.processorder_tree.insert('', 'end', item, text=item)
            else:
                continue
        for item in self.processorder_tree.get_children():
            if item not in selection:
                self.processorder_tree.delete(item)
        self.update_plugin_config(selection)
Esempio n. 17
0
def _setup_processing_pipeline(config):
    """ Display dialog to configure order of postprocessing plugins and update
    the configuration accordingly.

    :param config:      Currently active global configuration
    :type config:       :py:class:`spreads.config.Configuration`
    """
    # Only get names of postprocessing plugins. For this we have to load all
    # enabled plugins first and check if they implement the correct hook.
    exts = [
        name for name, cls in plugin.get_plugins(
            *config["plugins"].get()).iteritems()
        if issubclass(cls, plugin.ProcessHooksMixin)
    ]
    if not exts:
        return
    print("The following postprocessing plugins were detected:")
    print("\n".join(" - {0}".format(ext) for ext in exts))
    while True:
        answer = raw_input("Please enter the extensions in the order that they"
                           " should be invoked, separated by commas or hit"
                           " enter to keep the current order:\n")
        if not answer:
            plugins = exts
        else:
            plugins = [x.strip() for x in answer.split(',')]
        if any(x not in exts for x in plugins):
            print(
                colorize(
                    "At least one of the entered extensions was not"
                    "found, please try again!", colorama.Fore.RED))
        else:
            break
    # Append other plugins after the postprocessing plugins
    config["plugins"] = plugins + [
        x for x in config["plugins"].get() if x not in plugins
    ]
Esempio n. 18
0
def setup_parser(config):
    plugins = plugin.get_plugins(*config["plugins"].get())
    rootparser = argparse.ArgumentParser(
        description="Scanning Tool for  DIY Book Scanner",
        formatter_class=argparse.RawDescriptionHelpFormatter)

    rootparser.add_argument(
        '-V', '--version', action='version',
        version=(
            "spreads {0}\n\n"
            "Licensed under the terms of the GNU Affero General Public "
            "License 3.0.\n"
            "(C) 2013-2014 Johannes Baiter <*****@*****.**>\n"
            "For a complete list of contributors see:\n"
            "https://github.com/DIYBookScanner/spreads/graphs/contributors\n\n"
            .format(util.get_version())))

    for key, option in config.templates['core'].iteritems():
        try:
            add_argument_from_template('core', key, option, rootparser,
                                       config['core'][key].get())
        except TypeError:
            continue

    subparsers = rootparser.add_subparsers()

    wizard_parser = subparsers.add_parser(
        'wizard', help="Interactive mode")
    wizard_parser.add_argument(
        "path", type=unicode, help="Project path")
    wizard_parser.set_defaults(subcommand=cli.wizard)

    config_parser = subparsers.add_parser(
        'configure', help="Perform initial configuration")
    config_parser.set_defaults(subcommand=cli.configure)

    try:
        import spreads.tkconfigure as tkconfigure
        guiconfig_parser = subparsers.add_parser(
            'guiconfigure', help="Perform initial configuration with a GUI")
        guiconfig_parser.set_defaults(subcommand=tkconfigure.configure)
    except ImportError:
        print "Could not load _tkinter module, disabling guiconfigure command"

    capture_parser = subparsers.add_parser(
        'capture', help="Start the capturing workflow")
    capture_parser.add_argument(
        "path", type=unicode, help="Project path")
    capture_parser.set_defaults(subcommand=cli.capture)
    # Add arguments from plugins
    for parser in (capture_parser, wizard_parser):
        ext_names = [
            name for name, cls in plugins.iteritems()
            if any(issubclass(cls, mixin) for mixin in
                   (plugin.CaptureHooksMixin, plugin.TriggerHooksMixin))]
        ext_names.append('device')
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    postprocess_parser = subparsers.add_parser(
        'postprocess',
        help="Postprocess scanned images.")
    postprocess_parser.add_argument(
        "path", type=unicode, help="Project path")
    postprocess_parser.add_argument(
        "--jobs", "-j", dest="jobs", type=int, default=None,
        metavar="<int>", help="Number of concurrent processes")
    postprocess_parser.set_defaults(subcommand=cli.postprocess)
    # Add arguments from plugins
    for parser in (postprocess_parser, wizard_parser):
        ext_names = [name for name, cls in plugins.iteritems()
                     if issubclass(cls, plugin.ProcessHookMixin)]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    output_parser = subparsers.add_parser(
        'output',
        help="Generate output files.")
    output_parser.add_argument(
        "path", type=unicode, help="Project path")
    output_parser.set_defaults(subcommand=cli.output)
    # Add arguments from plugins
    for parser in (output_parser, wizard_parser):
        ext_names = [name for name, cls in plugins.iteritems()
                     if issubclass(cls, plugin.OutputHookMixin)]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    # Add custom subcommands from plugins
    if config["plugins"].get():
        classes = (cls for cls in plugins.values()
                   if issubclass(cls, plugin.SubcommandHookMixin))
        for cls in classes:
            cls.add_command_parser(subparsers, config)
    return rootparser
Esempio n. 19
0
def setup_parser(config):
    plugins = plugin.get_plugins(*config["plugins"].get())
    rootparser = argparse.ArgumentParser(
        description="Scanning Tool for  DIY Book Scanner")
    subparsers = rootparser.add_subparsers()
    for key, option in config.templates['core'].iteritems():
        try:
            add_argument_from_template('core', key, option, rootparser,
                                       config['core'][key].get())
        except TypeError:
            continue

    wizard_parser = subparsers.add_parser(
        'wizard', help="Interactive mode")
    wizard_parser.add_argument(
        "path", type=unicode, help="Project path")
    wizard_parser.set_defaults(subcommand=cli.wizard)

    config_parser = subparsers.add_parser(
        'configure', help="Perform initial configuration")
    config_parser.set_defaults(subcommand=cli.configure)

    try:
        import spreads.tkconfigure as tkconfigure
        guiconfig_parser = subparsers.add_parser(
            'guiconfigure', help="Perform initial configuration with a GUI")
        guiconfig_parser.set_defaults(subcommand=tkconfigure.configure)
    except ImportError:
        print "Could not load _tkinter module, disabling guiconfigure command"

    capture_parser = subparsers.add_parser(
        'capture', help="Start the capturing workflow")
    capture_parser.add_argument(
        "path", type=unicode, help="Project path")
    capture_parser.set_defaults(subcommand=cli.capture)
    # Add arguments from plugins
    for parser in (capture_parser, wizard_parser):
        ext_names = [
            name for name, cls in plugins.iteritems()
            if any(issubclass(cls, mixin) for mixin in
                   (plugin.CaptureHooksMixin, plugin.TriggerHooksMixin))]
        ext_names.append('driver')
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    postprocess_parser = subparsers.add_parser(
        'postprocess',
        help="Postprocess scanned images.")
    postprocess_parser.add_argument(
        "path", type=unicode, help="Project path")
    postprocess_parser.add_argument(
        "--jobs", "-j", dest="jobs", type=int, default=None,
        metavar="<int>", help="Number of concurrent processes")
    postprocess_parser.set_defaults(subcommand=cli.postprocess)
    # Add arguments from plugins
    for parser in (postprocess_parser, wizard_parser):
        ext_names = [name for name, cls in plugins.iteritems()
                     if issubclass(cls, plugin.ProcessHookMixin)]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    output_parser = subparsers.add_parser(
        'output',
        help="Generate output files.")
    output_parser.add_argument(
        "path", type=unicode, help="Project path")
    output_parser.set_defaults(subcommand=cli.output)
    # Add arguments from plugins
    for parser in (output_parser, wizard_parser):
        ext_names = [name for name, cls in plugins.iteritems()
                     if issubclass(cls, plugin.OutputHookMixin)]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser,
                                               config[ext][key].get())
                except TypeError:
                    continue

    # Add custom subcommands from plugins
    if config["plugins"].get():
        classes = (cls for cls in plugins.values()
                   if issubclass(cls, plugin.SubcommandHookMixin))
        for cls in classes:
            cls.add_command_parser(subparsers, config)
    return rootparser
Esempio n. 20
0
def setup_parser(config):
    """ Sets up an :py:class:`argparse.ArgumentParser` instance with all
    options and subcommands that are available in the core and activated
    plugins.

    :param config:  Current application configuration
    :type config:   :py:class:`spreads.config.Configuration`
    :returns:       Fully initialized argument parser
    :rtype:         :py:class:`argparse.ArgumentParser`
    """
    plugins = plugin.get_plugins(*config["plugins"].get())

    def _add_arguments(parsers, mixins, extra_names=None):
        if extra_names is None:
            extra_names = []
        for parser in parsers:
            # Only plugins that implement the capture or trigger hook mixins
            # and the currently active device configuration are relevant for
            # this subcommand.
            ext_names = [name for name, cls in plugins.iteritems()
                         if any(issubclass(cls, mixin) for mixin in mixins)]
            ext_names.extend(extra_names)
            for ext in ext_names:
                for key, tmpl in config.templates.get(ext, {}).iteritems():
                    if not should_show_argument(option,
                                                config['plugins'].get()):
                        continue
                    try:
                        add_argument_from_template(ext, key, tmpl, parser,
                                                   config[ext][key].get())
                    except TypeError:
                        continue

    rootparser = argparse.ArgumentParser(
        description="Scanning Tool for  DIY Book Scanner",
        formatter_class=argparse.RawDescriptionHelpFormatter)

    rootparser.add_argument(
        '-V', '--version', action='version',
        version=(
            "spreads {0}\n\n"
            "Licensed under the terms of the GNU Affero General Public "
            "License 3.0.\n"
            "(C) 2013-2014 Johannes Baiter <*****@*****.**>\n"
            "For a complete list of contributors see:\n"
            "https://github.com/DIYBookScanner/spreads/graphs/contributors\n\n"
            .format(util.get_version())))

    for key, option in config.templates['core'].iteritems():
        if not should_show_argument(option, config['plugins'].get()):
            continue
        try:
            add_argument_from_template('core', key, option, rootparser,
                                       config['core'][key].get())
        except TypeError:
            continue

    subparsers = rootparser.add_subparsers()

    wizard_parser = subparsers.add_parser(
        'wizard', help="Interactive mode")
    wizard_parser.add_argument(
        "path", type=unicode, help="Project path")
    wizard_parser.set_defaults(subcommand=cli.wizard)

    config_parser = subparsers.add_parser(
        'configure', help="Perform initial configuration")
    config_parser.set_defaults(subcommand=cli.configure)

    try:
        import spreads.tkconfigure as tkconfigure
        guiconfig_parser = subparsers.add_parser(
            'guiconfigure', help="Perform initial configuration with a GUI")
        guiconfig_parser.set_defaults(subcommand=tkconfigure.configure)
    except ImportError:
        pass

    capture_parser = subparsers.add_parser(
        'capture', help="Start the capturing workflow")
    capture_parser.add_argument(
        "path", type=unicode, help="Project path")
    capture_parser.set_defaults(subcommand=cli.capture)
    # Add arguments from plugins
    _add_arguments(parsers=(capture_parser, wizard_parser),
                   mixins=(plugin.CaptureHooksMixin, plugin.TriggerHooksMixin),
                   extra_names=('device',))

    postprocess_parser = subparsers.add_parser(
        'postprocess',
        help="Postprocess scanned images.")
    postprocess_parser.add_argument(
        "path", type=unicode, help="Project path")
    postprocess_parser.add_argument(
        "--jobs", "-j", dest="jobs", type=int, default=None,
        metavar="<int>", help="Number of concurrent processes")
    postprocess_parser.set_defaults(subcommand=cli.postprocess)
    _add_arguments(parsers=(postprocess_parser, wizard_parser),
                   mixins=(plugin.ProcessHooksMixin,))

    output_parser = subparsers.add_parser(
        'output',
        help="Generate output files.")
    output_parser.add_argument(
        "path", type=unicode, help="Project path")
    output_parser.set_defaults(subcommand=cli.output)
    _add_arguments(parsers=(output_parser, wizard_parser),
                   mixins=(plugin.OutputHooksMixin,))

    # Add custom subcommands from plugins
    if config["plugins"].get():
        classes = (cls for cls in plugins.values()
                   if issubclass(cls, plugin.SubcommandHooksMixin))
        for cls in classes:
            cls.add_command_parser(subparsers, config)
    return rootparser
Esempio n. 21
0
    def __init__(self, path, config=None, metadata=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))

        self.status = {
            'step': None,
            'step_progress': None,
            'prepared': False
        }
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        is_new = not self.path.exists()
        try:
            self.bag = bagit.Bag(unicode(self.path))
        except bagit.BagError:
            # Convert non-bagit directories from older versions
            self.bag = bagit.Bag.convert_directory(unicode(self.path))
        if not self.slug:
            self.slug = util.slugify(unicode(self.path.name))
        if not self.id:
            self.id = unicode(uuid.uuid4())
        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)
        #: :py:class:`spreads.metadata.Metadata` instance that backs the
        #: corresponding getter and setter
        self._metadata = Metadata(self.path)

        # This will invoke the setter
        if metadata:
            self.metadata = metadata

        #: Lock that is held when a shot is being executed during the capture
        #: phase
        self._capture_lock = threading.RLock()
        #: List of :py:class:`spreads.plugin.DeviceDriver` instances that
        #: backs the corresponding getters and setters
        self._devices = None
        # Thread pool for background tasks
        self._threadpool = concfut.ThreadPoolExecutor(max_workers=1)
        # List of unfinished :py:class:`concurrent.futures.Future` instances
        self._pending_tasks = []

        # Filter out subcommand plugins, since these are not workflow-specific
        plugin_classes = [
            (name, cls)
            for name, cls in plugin.get_plugins(*self.config["plugins"]
                                                .get()).iteritems()
            if not cls.__bases__ == (plugin.SubcommandHooksMixin,)]
        self._plugins = [cls(self.config) for name, cls in plugin_classes]
        self.config['plugins'] = [name for name, cls in plugin_classes]
        self._save_config()

        self.pages = self._load_pages()
        self.table_of_contents = self._load_toc()

        if is_new:
            on_created.send(self, workflow=self)
Esempio n. 22
0
def setup_parser(config):
    plugins = plugin.get_plugins(*config["plugins"].get())
    rootparser = argparse.ArgumentParser(
        description="Scanning Tool for  DIY Book Scanner")
    subparsers = rootparser.add_subparsers()
    for key, option in config.templates['core'].iteritems():
        try:
            add_argument_from_template('core', key, option, rootparser)
        except TypeError:
            continue

    wizard_parser = subparsers.add_parser('wizard', help="Interactive mode")
    wizard_parser.add_argument("path", type=unicode, help="Project path")
    wizard_parser.set_defaults(subcommand=wizard)

    config_parser = subparsers.add_parser('configure',
                                          help="Perform initial configuration")
    config_parser.set_defaults(subcommand=configure)

    capture_parser = subparsers.add_parser('capture',
                                           help="Start the capturing workflow")
    capture_parser.add_argument("path", type=unicode, help="Project path")
    capture_parser.set_defaults(subcommand=capture)
    # Add arguments from plugins
    for parser in (capture_parser, wizard_parser):
        ext_names = [
            name for name, cls in plugins.iteritems() if any(
                issubclass(cls, mixin) for mixin in (plugin.CaptureHooksMixin,
                                                     plugin.TriggerHooksMixin))
        ]
        ext_names.append('driver')
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser)
                except TypeError:
                    continue

    postprocess_parser = subparsers.add_parser(
        'postprocess', help="Postprocess scanned images.")
    postprocess_parser.add_argument("path", type=unicode, help="Project path")
    postprocess_parser.add_argument("--jobs",
                                    "-j",
                                    dest="jobs",
                                    type=int,
                                    default=None,
                                    metavar="<int>",
                                    help="Number of concurrent processes")
    postprocess_parser.set_defaults(subcommand=postprocess)
    # Add arguments from plugins
    for parser in (postprocess_parser, wizard_parser):
        ext_names = [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.ProcessHookMixin)
        ]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser)
                except TypeError:
                    continue

    output_parser = subparsers.add_parser('output',
                                          help="Generate output files.")
    output_parser.add_argument("path", type=unicode, help="Project path")
    output_parser.set_defaults(subcommand=output)
    # Add arguments from plugins
    for parser in (output_parser, wizard_parser):
        ext_names = [
            name for name, cls in plugins.iteritems()
            if issubclass(cls, plugin.OutputHookMixin)
        ]
        for ext in ext_names:
            for key, tmpl in config.templates.get(ext, {}).iteritems():
                try:
                    add_argument_from_template(ext, key, tmpl, parser)
                except TypeError:
                    continue

    # Add custom subcommands from plugins
    if config["plugins"].get():
        classes = (cls for cls in plugins.values()
                   if issubclass(cls, plugin.SubcommandHookMixin))
        for cls in classes:
            cls.add_command_parser(subparsers)
    return rootparser
Esempio n. 23
0
    def __init__(self, path, config=None, metadata=None):
        self._logger = logging.getLogger('Workflow')
        self._logger.debug("Initializing workflow {0}".format(path))

        self.status = {
            'step': None,
            'step_progress': None,
            'prepared': False
        }
        if not isinstance(path, Path):
            path = Path(path)
        self.path = path
        is_new = not self.path.exists()

        # See if supplied `config` is already a valid ConfigView object
        if isinstance(config, confit.ConfigView):
            self.config = config
        elif isinstance(config, Configuration):
            self.config = config.as_view()
        else:
            self.config = self._load_config(config)

        try:
            self.bag = bagit.Bag(unicode(self.path))
        except bagit.BagError:
            if self.config['core']['convert_old'].get(bool):
                # Convert non-bagit directories from older versions
                self.bag = bagit.Bag.convert_directory(unicode(self.path))
                self.pages = [Page(img)
                              for img in (self.path/'data'/'raw').iterdir()]
                self._save_pages()
            else:
                raise bagit.BagError(
                    "Specified workflow directory is not structured according "
                    "to BagIt convertions and automatic conversion has been "
                    "disabled (check `convert_old` setting)")
        if not self.slug:
            self.slug = util.slugify(unicode(self.path.name))
        if not self.id:
            self.id = unicode(uuid.uuid4())
        #: :py:class:`spreads.metadata.Metadata` instance that backs the
        #: corresponding getter and setter
        self._metadata = Metadata(self.path)

        # This will invoke the setter
        if metadata:
            self.metadata = metadata

        #: Lock that is held when a shot is being executed during the capture
        #: phase
        self._capture_lock = threading.RLock()
        #: List of :py:class:`spreads.plugin.DeviceDriver` instances that
        #: backs the corresponding getters and setters
        self._devices = None
        # Thread pool for background tasks
        self._threadpool = concfut.ThreadPoolExecutor(max_workers=1)
        # List of unfinished :py:class:`concurrent.futures.Future` instances
        self._pending_tasks = []

        # Filter out subcommand plugins, since these are not workflow-specific
        plugin_classes = [
            (name, cls)
            for name, cls in plugin.get_plugins(*self.config["plugins"]
                                                .get()).iteritems()
            if not cls.__bases__ == (plugin.SubcommandHooksMixin,)]
        self._plugins = [cls(self.config) for name, cls in plugin_classes]
        self.config['plugins'] = [name for name, cls in plugin_classes]
        self._save_config()

        self.pages = self._load_pages()
        self.table_of_contents = self._load_toc()

        if is_new:
            on_created.send(self, workflow=self)
Esempio n. 24
0
    def initializePage(self):  # noqa
        wizard = self.wizard()
        self.setTitle("Welcome!")

        intro_label = QtGui.QLabel(
            "This wizard will guide you through the digitization workflow. "
        )
        intro_label.setWordWrap(True)

        dirpick_layout = QtGui.QHBoxLayout()
        self.line_edit = QtGui.QLineEdit()
        self.line_edit.textChanged.connect(self.completeChanged)
        browse_btn = QtGui.QPushButton("Browse")
        dirpick_layout.addWidget(self.line_edit)
        dirpick_layout.addWidget(browse_btn)
        browse_btn.clicked.connect(self.show_filepicker)
        self.tab_widget = QtGui.QTabWidget()

        # Add configuration widgets from plugins
        self.plugin_widgets = {}

        # Filter out subcommand plugins
        available = [
            name for name, cls
            in plugin.get_plugins(*wizard.config['plugins'].get()).items()
            if not issubclass(cls, plugin.SubcommandHooksMixin)
        ]
        available.append('device')

        for name, tmpl in wizard.config.templates.iteritems():
            if not tmpl or name not in available:
                continue
            page = QtGui.QGroupBox()
            layout = QtGui.QFormLayout()
            widgets = self._get_plugin_config_widgets(tmpl, name)
            self.plugin_widgets[name] = widgets
            for label, widget in widgets.values():
                # We don't need a label for QCheckBoxes
                if isinstance(widget, QtGui.QCheckBox):
                    layout.addRow(widget)
                else:
                    layout.addRow(label, widget)
            page.setLayout(layout)
            self.tab_widget.addTab(page, name.title())

        self.save_btn = QtGui.QPushButton("&Save as defaults")
        self.save_btn.clicked.connect(self.saveSettings)
        save_layout = QtGui.QHBoxLayout()
        save_layout.addStretch(1)
        save_layout.addWidget(self.save_btn)

        main_layout = QtGui.QVBoxLayout()
        main_layout.addWidget(intro_label)
        main_layout.addSpacing(30)
        main_layout.addWidget(
            QtGui.QLabel("Please select a project directory.")
        )
        main_layout.addLayout(dirpick_layout)
        main_layout.addSpacing(30)
        main_layout.addWidget(self.tab_widget)
        main_layout.addLayout(save_layout)
        main_layout.setSizeConstraint(QtGui.QLayout.SetNoConstraint)

        self.setLayout(main_layout)
        self.adjustSize()
Esempio n. 25
0
def setup_parser(config):
    """ Sets up an :py:class:`argparse.ArgumentParser` instance with all
    options and subcommands that are available in the core and activated
    plugins.

    :param config:  Current application configuration
    :type config:   :py:class:`spreads.config.Configuration`
    :returns:       Fully initialized argument parser
    :rtype:         :py:class:`argparse.ArgumentParser`
    """
    plugins = plugin.get_plugins(*config["plugins"].get())

    def _add_arguments(parsers, mixins, extra_names=None):
        if extra_names is None:
            extra_names = []
        for parser in parsers:
            # Only plugins that implement the capture or trigger hook mixins
            # and the currently active device configuration are relevant for
            # this subcommand.
            ext_names = [
                name for name, cls in plugins.iteritems() if any(
                    issubclass(cls, mixin) for mixin in mixins)
            ]
            ext_names.extend(extra_names)
            for ext in ext_names:
                for key, tmpl in config.templates.get(ext, {}).iteritems():
                    if not should_show_argument(option,
                                                config['plugins'].get()):
                        continue
                    try:
                        add_argument_from_template(ext, key, tmpl, parser,
                                                   config[ext][key].get())
                    except TypeError:
                        continue

    rootparser = argparse.ArgumentParser(
        description="Scanning Tool for  DIY Book Scanner",
        formatter_class=argparse.RawDescriptionHelpFormatter)

    rootparser.add_argument(
        '-V',
        '--version',
        action='version',
        version=(
            "spreads {0}\n\n"
            "Licensed under the terms of the GNU Affero General Public "
            "License 3.0.\n"
            "(C) 2013-2014 Johannes Baiter <*****@*****.**>\n"
            "For a complete list of contributors see:\n"
            "https://github.com/DIYBookScanner/spreads/graphs/contributors\n\n"
            .format(util.get_version())))

    for key, option in config.templates['core'].iteritems():
        if not should_show_argument(option, config['plugins'].get()):
            continue
        try:
            add_argument_from_template('core', key, option, rootparser,
                                       config['core'][key].get())
        except TypeError:
            continue

    subparsers = rootparser.add_subparsers()

    wizard_parser = subparsers.add_parser('wizard', help="Interactive mode")
    wizard_parser.add_argument("path", type=unicode, help="Project path")
    wizard_parser.set_defaults(subcommand=cli.wizard)

    config_parser = subparsers.add_parser('configure',
                                          help="Perform initial configuration")
    config_parser.set_defaults(subcommand=cli.configure)

    try:
        import spreads.tkconfigure as tkconfigure
        guiconfig_parser = subparsers.add_parser(
            'guiconfigure', help="Perform initial configuration with a GUI")
        guiconfig_parser.set_defaults(subcommand=tkconfigure.configure)
    except ImportError:
        pass

    capture_parser = subparsers.add_parser('capture',
                                           help="Start the capturing workflow")
    capture_parser.add_argument("path", type=unicode, help="Project path")
    capture_parser.set_defaults(subcommand=cli.capture)
    # Add arguments from plugins
    _add_arguments(parsers=(capture_parser, wizard_parser),
                   mixins=(plugin.CaptureHooksMixin, plugin.TriggerHooksMixin),
                   extra_names=('device', ))

    postprocess_parser = subparsers.add_parser(
        'postprocess', help="Postprocess scanned images.")
    postprocess_parser.add_argument("path", type=unicode, help="Project path")
    postprocess_parser.add_argument("--jobs",
                                    "-j",
                                    dest="jobs",
                                    type=int,
                                    default=None,
                                    metavar="<int>",
                                    help="Number of concurrent processes")
    postprocess_parser.set_defaults(subcommand=cli.postprocess)
    _add_arguments(parsers=(postprocess_parser, wizard_parser),
                   mixins=(plugin.ProcessHooksMixin, ))

    output_parser = subparsers.add_parser('output',
                                          help="Generate output files.")
    output_parser.add_argument("path", type=unicode, help="Project path")
    output_parser.set_defaults(subcommand=cli.output)
    _add_arguments(parsers=(output_parser, wizard_parser),
                   mixins=(plugin.OutputHooksMixin, ))

    # Add custom subcommands from plugins
    if config["plugins"].get():
        classes = (cls for cls in plugins.values()
                   if issubclass(cls, plugin.SubcommandHooksMixin))
        for cls in classes:
            cls.add_command_parser(subparsers, config)
    return rootparser
Esempio n. 26
0
    def initializePage(self):
        wizard = self.wizard()
        self.setTitle("Welcome!")

        intro_label = QtGui.QLabel(
            "This wizard will guide you through the digitization workflow. "
        )
        intro_label.setWordWrap(True)

        dirpick_layout = QtGui.QHBoxLayout()
        self.line_edit = QtGui.QLineEdit()
        self.line_edit.textChanged.connect(self.completeChanged)
        browse_btn = QtGui.QPushButton("Browse")
        dirpick_layout.addWidget(self.line_edit)
        dirpick_layout.addWidget(browse_btn)
        browse_btn.clicked.connect(self.show_filepicker)
        self.tab_widget = QtGui.QTabWidget()

        # Add configuration widgets from plugins
        self.plugin_widgets = {}

        # Filter out subcommand plugins
        available = [
            name for name, cls
            in plugin.get_plugins(*wizard.config['plugins'].get()).items()
            if not issubclass(cls, plugin.SubcommandHookMixin)
        ]
        available.append('device')

        for name, tmpl in wizard.config.templates.iteritems():
            if not tmpl or name not in available:
                continue
            page = QtGui.QGroupBox()
            layout = QtGui.QFormLayout()
            widgets = self._get_plugin_config_widgets(tmpl, name)
            self.plugin_widgets[name] = widgets
            for label, widget in widgets.values():
                # We don't need a label for QCheckBoxes
                if isinstance(widget, QtGui.QCheckBox):
                    layout.addRow(widget)
                else:
                    layout.addRow(label, widget)
            page.setLayout(layout)
            self.tab_widget.addTab(page, name.title())

        self.save_btn = QtGui.QPushButton("&Save as defaults")
        self.save_btn.clicked.connect(self.saveSettings)
        save_layout = QtGui.QHBoxLayout()
        save_layout.addStretch(1)
        save_layout.addWidget(self.save_btn)

        main_layout = QtGui.QVBoxLayout()
        main_layout.addWidget(intro_label)
        main_layout.addSpacing(30)
        main_layout.addWidget(
            QtGui.QLabel("Please select a project directory.")
        )
        main_layout.addLayout(dirpick_layout)
        main_layout.addSpacing(30)
        main_layout.addWidget(self.tab_widget)
        main_layout.addLayout(save_layout)
        main_layout.setSizeConstraint(QtGui.QLayout.SetNoConstraint)

        self.setLayout(main_layout)
        self.adjustSize()