mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Metadata download: Allow specifying rules to transform publisher names in addition to author and tag names. Fixes #2012304 [[Enhancement] in "Metadata Download," add "rules to filter/transform publisher"](https://bugs.launchpad.net/calibre/+bug/2012304)
This commit is contained in:
parent
8a2b4ac5da
commit
b906380184
@ -501,7 +501,8 @@ def identify(log, abort, # {{{
|
||||
log('We have %d merged results, merging took: %.2f seconds' %
|
||||
(len(results), time.time() - start_time))
|
||||
tm_rules = msprefs['tag_map_rules']
|
||||
if tm_rules:
|
||||
pm_rules = msprefs['publisher_map_rules']
|
||||
if tm_rules or pm_rules:
|
||||
from calibre.ebooks.metadata.tag_mapper import map_tags
|
||||
am_rules = msprefs['author_map_rules']
|
||||
if am_rules:
|
||||
@ -531,6 +532,9 @@ def identify(log, abort, # {{{
|
||||
r.tags = r.tags[:max_tags]
|
||||
if getattr(r.pubdate, 'year', 2000) <= UNDEFINED_DATE.year:
|
||||
r.pubdate = None
|
||||
if pm_rules and r.publisher:
|
||||
pubs = map_tags([r.publisher], pm_rules)
|
||||
r.publisher = pubs[0] if pubs else ''
|
||||
|
||||
if msprefs['swap_author_names']:
|
||||
for r in results:
|
||||
|
@ -18,8 +18,9 @@ msprefs.defaults['swap_author_names'] = False
|
||||
msprefs.defaults['fewer_tags'] = True
|
||||
msprefs.defaults['find_first_edition_date'] = False
|
||||
msprefs.defaults['append_comments'] = False
|
||||
msprefs.defaults['tag_map_rules'] = []
|
||||
msprefs.defaults['author_map_rules'] = []
|
||||
msprefs.defaults['tag_map_rules'] = ()
|
||||
msprefs.defaults['author_map_rules'] = ()
|
||||
msprefs.defaults['publisher_map_rules'] = ()
|
||||
msprefs.defaults['id_link_rules'] = {}
|
||||
msprefs.defaults['keep_dups'] = False
|
||||
|
||||
|
@ -34,7 +34,7 @@ class RuleEdit(RuleEditBase):
|
||||
('not_matches', _('does not match regex pattern')),
|
||||
))
|
||||
|
||||
MSG = _('Create the rule below, the rule can be used to add or ignore files')
|
||||
MSG = _('Create the rule below, the rule can be used to add or ignore authors')
|
||||
SUBJECT = _('the author, if the author name')
|
||||
VALUE_ERROR = _('You must provide a value for the author name to match')
|
||||
REPLACE_TEXT = _('with the name:')
|
||||
|
@ -332,10 +332,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
self.select_default_button.clicked.connect(self.fields_model.select_user_defaults)
|
||||
self.select_default_button.clicked.connect(self.changed_signal)
|
||||
self.set_as_default_button.clicked.connect(self.fields_model.commit_user_defaults)
|
||||
self.tag_map_rules = self.author_map_rules = None
|
||||
self.tag_map_rules = self.author_map_rules = self.publisher_map_rules = None
|
||||
m = QMenu(self)
|
||||
m.addAction(_('Tags')).triggered.connect(self.change_tag_map_rules)
|
||||
m.addAction(_('Authors')).triggered.connect(self.change_author_map_rules)
|
||||
m.addAction(_('Publisher')).triggered.connect(self.change_publisher_map_rules)
|
||||
self.map_rules_button.setMenu(m)
|
||||
l = self.page.layout()
|
||||
l.setStretch(0, 1)
|
||||
@ -376,16 +377,25 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
from calibre.gui2.tag_mapper import RulesDialog
|
||||
d = RulesDialog(self)
|
||||
if msprefs.get('tag_map_rules'):
|
||||
d.rules = msprefs['tag_map_rules']
|
||||
d.rules = list(msprefs['tag_map_rules'])
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
self.tag_map_rules = d.rules
|
||||
self.changed_signal.emit()
|
||||
|
||||
def change_publisher_map_rules(self):
|
||||
from calibre.gui2.publisher_mapper import RulesDialog
|
||||
d = RulesDialog(self)
|
||||
if msprefs.get('publisher_map_rules'):
|
||||
d.rules = list(msprefs['publisher_map_rules'])
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
self.publisher_map_rules = d.rules
|
||||
self.changed_signal.emit()
|
||||
|
||||
def change_author_map_rules(self):
|
||||
from calibre.gui2.author_mapper import RulesDialog
|
||||
d = RulesDialog(self)
|
||||
if msprefs.get('author_map_rules'):
|
||||
d.rules = msprefs['author_map_rules']
|
||||
d.rules = list(msprefs['author_map_rules'])
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
self.author_map_rules = d.rules
|
||||
self.changed_signal.emit()
|
||||
@ -395,7 +405,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
self.sources_model.initialize()
|
||||
self.sources_view.resizeColumnsToContents()
|
||||
self.fields_model.initialize()
|
||||
self.tag_map_rules = self.author_map_rules = None
|
||||
self.tag_map_rules = self.author_map_rules = self.publisher_map_rules = None
|
||||
|
||||
def restore_defaults(self):
|
||||
ConfigWidgetBase.restore_defaults(self)
|
||||
@ -410,6 +420,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
msprefs['tag_map_rules'] = self.tag_map_rules or []
|
||||
if self.author_map_rules is not None:
|
||||
msprefs['author_map_rules'] = self.author_map_rules or []
|
||||
if self.publisher_map_rules is not None:
|
||||
msprefs['publisher_map_rules'] = self.publisher_map_rules or []
|
||||
return ConfigWidgetBase.commit(self)
|
||||
|
||||
|
||||
|
@ -234,7 +234,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create &rules to transform tags/authors</string>
|
||||
<string>Create &rules to transform tags/authors/publishers</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
|
137
src/calibre/gui2/publisher_mapper.py
Normal file
137
src/calibre/gui2/publisher_mapper.py
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from calibre.ebooks.metadata.tag_mapper import map_tags
|
||||
from calibre.gui2 import Application, elided_text
|
||||
from calibre.gui2.tag_mapper import (
|
||||
RuleEdit as RuleEditBase, RuleEditDialog as RuleEditDialogBase,
|
||||
RuleItem as RuleItemBase, Rules as RulesBase, RulesDialog as RulesDialogBase,
|
||||
Tester as TesterBase,
|
||||
)
|
||||
from calibre.utils.config import JSONConfig
|
||||
|
||||
publisher_maps = JSONConfig('publisher-mapping-rules')
|
||||
|
||||
|
||||
class RuleEdit(RuleEditBase):
|
||||
|
||||
ACTION_MAP = OrderedDict((
|
||||
('replace', _('Change')),
|
||||
('capitalize', _('Capitalize')),
|
||||
('titlecase', _('Title-case')),
|
||||
('lower', _('Lower-case')),
|
||||
('upper', _('Upper-case')),
|
||||
))
|
||||
|
||||
MATCH_TYPE_MAP = OrderedDict((
|
||||
('one_of', _('is one of')),
|
||||
('not_one_of', _('is not one of')),
|
||||
('has', _('contains')),
|
||||
('matches', _('matches regex pattern')),
|
||||
('not_matches', _('does not match regex pattern')),
|
||||
))
|
||||
|
||||
MSG = _('Create the rule below, the rule can be used to modify publishers')
|
||||
SUBJECT = _('the publisher, if the publisher name')
|
||||
VALUE_ERROR = _('You must provide a value for the publisher name to match')
|
||||
REPLACE_TEXT = _('with the name:')
|
||||
SINGLE_EDIT_FIELD_NAME = 'publisher'
|
||||
|
||||
@property
|
||||
def can_use_tag_editor(self):
|
||||
return False
|
||||
|
||||
def update_state(self):
|
||||
a = self.action.currentData()
|
||||
replace = a == 'replace'
|
||||
self.la3.setVisible(replace), self.replace.setVisible(replace)
|
||||
m = self.match_type.currentData()
|
||||
is_match = 'matches' in m
|
||||
self.regex_help.setVisible(is_match)
|
||||
|
||||
@property
|
||||
def rule(self):
|
||||
return {
|
||||
'action': self.action.currentData(),
|
||||
'match_type': self.match_type.currentData(),
|
||||
'query': self.query.text().strip(),
|
||||
'replace': self.replace.text().strip(),
|
||||
}
|
||||
|
||||
@rule.setter
|
||||
def rule(self, rule):
|
||||
def sc(name):
|
||||
c = getattr(self, name)
|
||||
idx = c.findData(str(rule.get(name, '')))
|
||||
if idx < 0:
|
||||
idx = 0
|
||||
c.setCurrentIndex(idx)
|
||||
sc('match_type'), sc('action')
|
||||
self.query.setText(str(rule.get('query', '')).strip())
|
||||
self.replace.setText(str(rule.get('replace', '')).strip())
|
||||
|
||||
|
||||
class RuleEditDialog(RuleEditDialogBase):
|
||||
|
||||
PREFS_NAME = 'edit-publisher-mapping-rule'
|
||||
RuleEditClass = RuleEdit
|
||||
|
||||
|
||||
class RuleItem(RuleItemBase):
|
||||
|
||||
@staticmethod
|
||||
def text_from_rule(rule, parent):
|
||||
query = elided_text(rule['query'], font=parent.font(), width=200, pos='right')
|
||||
text = _(
|
||||
'<b>{action}</b> the publisher name, if it <i>{match_type}</i>: <b>{query}</b>').format(
|
||||
action=RuleEdit.ACTION_MAP[rule['action']], match_type=RuleEdit.MATCH_TYPE_MAP[rule['match_type']], query=query)
|
||||
if rule['action'] == 'replace':
|
||||
text += '<br>' + _('to the name') + ' <b>%s</b>' % rule['replace']
|
||||
return '<div style="white-space: nowrap">' + text + '</div>'
|
||||
|
||||
|
||||
class Rules(RulesBase):
|
||||
|
||||
RuleItemClass = RuleItem
|
||||
RuleEditDialogClass = RuleEditDialog
|
||||
MSG = _('You can specify rules to manipulate publisher names here.'
|
||||
' Click the "Add Rule" button'
|
||||
' below to get started. The rules will be processed in order for every publisher.')
|
||||
|
||||
|
||||
class Tester(TesterBase):
|
||||
|
||||
DIALOG_TITLE = _('Test publisher mapping rules')
|
||||
PREFS_NAME = 'test-publisher-mapping-rules'
|
||||
LABEL = _('Enter an publisher name to test:')
|
||||
PLACEHOLDER = _('Enter publisher and click the "Test" button')
|
||||
EMPTY_RESULT = '<p> </p>'
|
||||
|
||||
def do_test(self):
|
||||
publisher = self.value.strip()
|
||||
ans = map_tags([publisher], self.rules)
|
||||
self.result.setText((ans or ('',))[0])
|
||||
|
||||
|
||||
class RulesDialog(RulesDialogBase):
|
||||
|
||||
DIALOG_TITLE = _('Edit publisher mapping rules')
|
||||
PREFS_NAME = 'edit-publisher-mapping-rules'
|
||||
RulesClass = Rules
|
||||
TesterClass = Tester
|
||||
PREFS_OBJECT = publisher_maps
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application([])
|
||||
d = RulesDialog()
|
||||
d.rules = [
|
||||
{'action':'replace', 'query':'alice Bob', 'match_type':'one_of', 'replace':'Alice Bob'},
|
||||
]
|
||||
d.exec()
|
||||
from pprint import pprint
|
||||
pprint(d.rules)
|
||||
del d, app
|
Loading…
x
Reference in New Issue
Block a user