View Single Post
Old 02-18-2021, 10:17 AM   #326
capink
Wizard
capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.
 
Posts: 1,207
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
The idea of template modifying values that are remembered between invocations is interesting.

I think the action and the display should be separated. We run the action that iterates over selected books, and modify the global dict. After that the display can be handled by the formulas action, which already has access to the global dict. This way, any other action that uses templates, can be preceded by this new action (now that the display is optional and separate), and access whatever values it writes to the global dict.

All this action would need to do is loop over all selected books, and run the template for each one. Here is what I think can be a preliminary implementation for this action:
Code:
# python3 compatibility
from six import text_type as unicode

from calibre import prints
from calibre.constants import DEBUG
from calibre.gui2 import error_dialog
from calibre.ebooks.metadata.book.formatter import SafeFormat

from calibre_plugins.action_chains.actions.base import ChainAction
from calibre_plugins.action_chains.templates import TemplateBox, check_template, get_metadata_object, TEMPLATE_ERROR

class TemplateIteratorDialog(TemplateBox):
    def __init__(self, parent, plugin_action, action, name, title):
        self.plugin_action = plugin_action
        self.action = action
        self.gui = plugin_action.gui
        self.db = self.gui.current_db
        mi = get_metadata_object(self.gui)
        TemplateBox.__init__(
                self,
                parent,
                plugin_action,
                template_text='',
                placeholder_text=_('Enter a template here. It will be iterated over all selected books'),
                mi=mi
            )
        self.setWindowTitle(title)

    def load_settings(self, settings):
        if settings:
            template = settings['template']
            self.textbox.insertPlainText(template)

    def save_settings(self):
        settings = {}
        settings['template'] = unicode(self.textbox.toPlainText()).rstrip()
        return settings

    def accept(self):
        self.settings = self.save_settings()
        # validate settings
        is_valid = self.action.validate(self.settings)
        if is_valid is not True:
            msg, details = is_valid
            error_dialog(
                self,
                msg,
                details,
                show=True
            )
            return
        TemplateBox.accept(self)

class TemplateIteratorAction(ChainAction):

    name = 'Template Iterator'

    def run_template_for_book(self, db, template, book_id, chain_loop):
        mi = db.new_api.get_proxy_metadata(book_id)

        template_functions = self.plugin_action.template_functions
        template_output = SafeFormat().safe_format(template, mi, TEMPLATE_ERROR, mi,
                                                   global_vars=chain_loop.chain_vars,
                                                   template_functions=template_functions)

        return template_output

    def run(self, gui, settings, chain_loop):
        db = gui.current_db
        
        rows = gui.current_view().selectionModel().selectedRows()
        book_ids = [ gui.library_view.model().db.id(row.row()) for row in rows ]
                    
        template = settings['template']
        for book_id in book_ids:
            template_output = self.run_template_for_book(db, template, book_id, chain_loop)

    def config_widget(self):
        return TemplateIteratorDialog
This action can be tested now as a custom action, before it is included in the plugin.

I still have to ponder on this for sometime, before jumping into final implementation, to get it right first. I am also struggling with name of this new action. Changing it later would break compatibility. Maybe someone can help.

Edit: Maybe make the display part of the same action, and make it optional instead. Not yet sure about this.

Last edited by capink; 02-18-2021 at 10:19 AM.
capink is offline   Reply With Quote