Tag mapper: A new rule type "split" allows you to easily split tags on a character

This commit is contained in:
Kovid Goyal 2016-04-14 08:01:40 +05:30
parent 33e23e50ab
commit ebf91eab18
4 changed files with 64 additions and 7 deletions

View File

@ -31,6 +31,10 @@ def matcher(rule):
pat = compile_pat(rule['query'])
return lambda x: pat.match(x) is None
if mt == 'has':
s = rule['query']
return lambda x: s in x
return lambda x: False
@ -83,6 +87,14 @@ def apply_rules(tag, rules):
if ac == 'upper':
ans.append(icu_upper(tag))
break
if ac == 'split':
stags = filter(None, [x.strip() for x in tag.split(rule['replace'])])
if stags:
if stags[0] == tag:
ans.append(tag)
else:
tags.extendleft(reversed(stags))
break
else: # no rule matched, default keep
ans.append(tag)
@ -141,6 +153,10 @@ def test():
run([rule('replace', 't1', 't2'), rule('replace', 't2', 't1')], 't1,t2', 't1,t2')
run(rule('replace', 'a', 'A'), 'a,b', 'A,b')
run(rule('replace', 'a,b', 'A,B'), 'a,b', 'A,B')
run(rule('split', '/', '/', 'has'), 'a/b/c,d', 'a,b,c,d')
run(rule('split', '/', '/', 'has'), '/,d', 'd')
run(rule('split', '/', '/', 'has'), '/a/', 'a')
run(rule('split', 'a,b', '/'), 'a,b', 'a,b')
if __name__ == '__main__':
test()

View File

@ -50,6 +50,7 @@ class RuleEdit(RuleEditBase):
tt = _('A case-insensitive filename pattern, for example: {0} or {1}').format('*.pdf', 'number-?.epub')
else:
tt = _('A regular expression')
self.regex_help.setVisible('matches' in q)
self.query.setToolTip(tt)
@property

View File

@ -14,10 +14,11 @@ from calibre.ebooks.css_transform_rules import (
validate_rule, safe_parser, compile_rules, transform_sheet, ACTION_MAP, MATCH_TYPE_MAP, export_rules, import_rules)
from calibre.gui2 import error_dialog, elided_text, choose_save_file, choose_files
from calibre.gui2.tag_mapper import (
RuleEditDialog as RuleEditDialogBase, Rules as RulesBase, RulesDialog as
RulesDialogBase, RuleItem as RuleItemBase, SaveLoadMixin)
RuleEdit as RE, RuleEditDialog as RuleEditDialogBase, Rules as RulesBase,
RulesDialog as RulesDialogBase, RuleItem as RuleItemBase, SaveLoadMixin)
from calibre.gui2.widgets2 import Dialog
from calibre.utils.config import JSONConfig
from calibre.utils.localization import localize_user_manual_link
class RuleEdit(QWidget): # {{{
@ -76,6 +77,13 @@ class RuleEdit(QWidget): # {{{
if clause is not parts[-1]:
h.addWidget(QLabel('\xa0'))
self.regex_help = la = QLabel('<p>' + RE.REGEXP_HELP_TEXT % localize_user_manual_link(
'http://manual.calibre-ebook.com/regexp.html'))
la.setOpenExternalLinks(True)
la.setWordWrap(True)
l.addWidget(la)
l.addStretch(10)
self.update_state()
def sizeHint(self):
@ -108,6 +116,7 @@ class RuleEdit(QWidget): # {{{
elif ac in '+=*/':
tt = _('A number')
self.action_data.setToolTip(tt)
self.regex_help.setVisible('matches' in mt)
@property
def rule(self):

View File

@ -7,6 +7,7 @@ from __future__ import (unicode_literals, division, absolute_import,
from collections import OrderedDict
from functools import partial
import textwrap
from PyQt5.Qt import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QListWidget, QIcon,
@ -19,6 +20,7 @@ from calibre.gui2 import error_dialog, elided_text, Application, question_dialog
from calibre.gui2.ui import get_gui
from calibre.gui2.widgets2 import Dialog
from calibre.utils.config import JSONConfig
from calibre.utils.localization import localize_user_manual_link
tag_maps = JSONConfig('tag-map-rules')
@ -38,18 +40,30 @@ class RuleEdit(QWidget):
('capitalize', _('Capitalize')),
('lower', _('Lower-case')),
('upper', _('Upper-case')),
('split', _('Split')),
))
MATCH_TYPE_MAP = OrderedDict((
('one_of', _('is one of')),
('not_one_of', _('is not one of')),
('matches', _('matches pattern')),
('not_matches', _('does not match pattern'))
('not_matches', _('does not match pattern')),
('has', _('contains')),
))
MSG = _('Create the rule below, the rule can be used to remove or replace tags')
SUBJECT = _('the tag, if it')
VALUE_ERROR = _('You must provide a value for the tag to match')
REPLACE_TEXT = _('with the tag:')
SPLIT_TEXT = _('on the character:')
SPLIT_TOOLTIP = _(
'The character on which to split tags. Note that technically you can specify'
' a sub-string, not just a single character. Then splitting will happen on the sub-string.')
REPLACE_TOOLTIP = _(
'What to replace the tag with. Note that if you use a pattern to match'
' tags, you can replace with parts of the matched pattern. See '
' the User Manual on how to use regular expressions for details.')
REGEXP_HELP_TEXT = _('For help with regex pattern matching, see the <a href="%s">User Manual</a>')
def __init__(self, parent=None):
QWidget.__init__(self, parent)
@ -83,10 +97,16 @@ class RuleEdit(QWidget):
b.setVisible(self.can_use_tag_editor)
self.h2 = h = QHBoxLayout()
l.addLayout(h)
self.la3 = la = QLabel(_('with the tag:') + '\xa0')
self.la3 = la = QLabel(self.REPLACE_TEXT + '\xa0')
h.addWidget(la)
self.replace = r = QLineEdit(self)
h.addWidget(r)
self.regex_help = la = QLabel('<p>' + self.REGEXP_HELP_TEXT % localize_user_manual_link(
'http://manual.calibre-ebook.com/regexp.html'))
la.setOpenExternalLinks(True)
la.setWordWrap(True)
l.addWidget(la)
la.setVisible(False)
l.addStretch(10)
self.la3.setVisible(False), self.replace.setVisible(False)
self.update_state()
@ -102,14 +122,22 @@ class RuleEdit(QWidget):
return self.SUBJECT is RuleEdit.SUBJECT and 'matches' not in self.match_type.currentData() and get_gui() is not None
def update_state(self):
replace = self.action.currentData() == 'replace'
self.la3.setVisible(replace), self.replace.setVisible(replace)
a = self.action.currentData()
replace = a == 'replace'
split = a == 'split'
self.la3.setVisible(replace or split), self.replace.setVisible(replace or split)
tt = _('A comma separated list of tags')
is_match = 'matches' in self.match_type.currentData()
m = self.match_type.currentData()
is_match = 'matches' in m
self.tag_editor_button.setVisible(self.can_use_tag_editor)
if is_match:
tt = _('A regular expression')
elif m == 'has':
tt = _('Tags that contain this string will match')
self.regex_help.setVisible(is_match)
self.la3.setText((self.SPLIT_TEXT if split else self.REPLACE_TEXT) + '\xa0')
self.query.setToolTip(tt)
self.replace.setToolTip(textwrap.fill(self.SPLIT_TOOLTIP if split else self.REPLACE_TOOLTIP))
def specialise_context_menu(self, menu):
if self.can_use_tag_editor:
@ -188,6 +216,8 @@ class RuleItem(QListWidgetItem):
action=RuleEdit.ACTION_MAP[rule['action']], match_type=RuleEdit.MATCH_TYPE_MAP[rule['match_type']], query=query)
if rule['action'] == 'replace':
text += '<br>' + _('with the tag:') + ' <b>%s</b>' % rule['replace']
if rule['action'] == 'split':
text += '<br>' + _('on the character:') + ' <b>%s</b>' % rule['replace']
return text
def __init__(self, rule, parent):
@ -467,6 +497,7 @@ if __name__ == '__main__':
d.rules = [
{'action':'remove', 'query':'moose', 'match_type':'one_of', 'replace':''},
{'action':'replace', 'query':'moose', 'match_type':'one_of', 'replace':'xxxx'},
{'action':'split', 'query':'/', 'match_type':'has', 'replace':'/'},
]
d.exec_()
from pprint import pprint