EDIT: A modified version of this action is available as a builtin action in the action chains plugin starting from version 1.4.0. The builtin action is called "Search Using Templates".
This is a custom action that allows constructing a calibre search using calibre template editor. The search can be a simple text search or a search constructed from a template.
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.database import is_search_valid
from calibre_plugins.action_chains.templates import check_template, get_metadata_object
from calibre_plugins.action_chains.templates.dialogs import TemplateBox
try:
load_translations()
except NameError:
prints("ActionsChain::action/search.py - exception when loading translations")
class TemplateSearchWidget(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 search expression here. It can be either '
'an ordinary text string or a template.'),
mi=mi
)
self.setWindowTitle(title)
def textbox_changed(self):
TemplateBox.textbox_changed(self)
template = self.template_value.text()
text = SafeFormat().safe_format(template, self.mi, 'Exception: ', self.mi, template_functions=self.plugin_action.template_functions)
if text:
ok = is_search_valid(self.db, text) and not text.lower().startswith('exception:')
ss = 'QLineEdit { border: 2px solid %s; border-radius: 3px }' % (
'#50c878' if ok else '#FF2400')
self.template_value.setStyleSheet(ss)
else:
self.template_value.setStyleSheet('')
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 TemplateSearchAction(ChainAction):
name = 'Template Search'
def run(self, gui, settings, chain):
template = settings['template']
template_output = chain.evaluate_template(template, book_id=None)
gui.search.clear()
gui.search.set_search_string(template_output)
def validate(self, settings):
gui = self.plugin_action.gui
db = gui.current_db
if not settings:
return (_('Settings Error'), _('You must configure this action before running it'))
mi = get_metadata_object(gui)
is_template_valid = check_template(settings['template'], self.plugin_action, print_error=False)
if is_template_valid is not True:
return is_template_valid
search_text = SafeFormat().safe_format(settings['template'], mi, 'Exception: ', mi, template_functions=self.plugin_action.template_functions)
if not is_search_valid(db, search_text):
return (_('Settings Error'), _('Search ({}) is not valid'.format(search_text)))
return True
def config_widget(self):
return TemplateSearchWidget
N.B This custom action will not work with Action Chains versions < 1.2.0