mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
UI for creating add filter rules
This commit is contained in:
parent
913252ad02
commit
cc4a0b4e4d
147
src/calibre/gui2/add_filters.py
Normal file
147
src/calibre/gui2/add_filters.py
Normal file
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
from collections import OrderedDict
|
||||
from future_builtins import map
|
||||
|
||||
from calibre.db.adding import compile_glob, filter_filename, compile_rule
|
||||
from calibre.gui2 import elided_text, Application, error_dialog
|
||||
from calibre.gui2.tag_mapper import (
|
||||
RuleEdit as RuleEditBase, RuleEditDialog as
|
||||
RuleEditDialogBase, RuleItem as RuleItemBase, Rules as RulesBase,
|
||||
Tester as TesterBase, RulesDialog as RulesDialogBase
|
||||
)
|
||||
from calibre.utils.config import JSONConfig
|
||||
|
||||
add_filters = JSONConfig('add-filter-rules')
|
||||
|
||||
class RuleEdit(RuleEditBase):
|
||||
|
||||
ACTION_MAP = OrderedDict((
|
||||
('ignore', _('Ignore')),
|
||||
('add', _('Add')),
|
||||
))
|
||||
|
||||
MATCH_TYPE_MAP = OrderedDict((
|
||||
('startswith', _('starts with')),
|
||||
('not_startswith', _('does not start with')),
|
||||
('endswith', _('ends with')),
|
||||
('not_endswith', _('does not end with')),
|
||||
('glob', _('matches glob pattern')),
|
||||
('not_glob', _('does not match glob pattern')),
|
||||
('matches', _('matches regex pattern')),
|
||||
('not_matches', _('does not match regex pattern')),
|
||||
))
|
||||
|
||||
MSG = _('Create the rule below, the rule can be used to add or ignore files')
|
||||
SUBJECT = _('the file, if the filename')
|
||||
VALUE_ERROR = _('You must provide a value for the tag to match')
|
||||
|
||||
def update_state(self):
|
||||
tt = _('A comma separated list of tags')
|
||||
q = self.match_type.currentData()
|
||||
if 'with' in q:
|
||||
tt = _('Matching is case-insensitive')
|
||||
elif 'glob' in q:
|
||||
tt = _('A case-insensitive filename pattern, for example: {0} or {1}').format('*.pdf', 'number-?.epub')
|
||||
else:
|
||||
tt = _('A regular expression')
|
||||
self.query.setToolTip(tt)
|
||||
|
||||
@property
|
||||
def rule(self):
|
||||
return {
|
||||
'action': self.action.currentData(),
|
||||
'match_type': self.match_type.currentData(),
|
||||
'query': self.query.text().strip(),
|
||||
}
|
||||
|
||||
@rule.setter
|
||||
def rule(self, rule):
|
||||
def sc(name):
|
||||
c = getattr(self, name)
|
||||
idx = c.findData(unicode(rule.get(name, '')))
|
||||
if idx < 0:
|
||||
idx = 0
|
||||
c.setCurrentIndex(idx)
|
||||
sc('action'), sc('match_type')
|
||||
self.query.setText(unicode(rule.get('query', '')).strip())
|
||||
|
||||
def validate(self):
|
||||
ans = super(RuleEdit, self).validate()
|
||||
if ans:
|
||||
rule = self.rule
|
||||
if 'glob' in rule['match_type']:
|
||||
try:
|
||||
compile_glob(rule['query'])
|
||||
except Exception:
|
||||
error_dialog(self, _('Query invalid'), _(
|
||||
'%s is not a valid glob expression') % rule['query'], show=True)
|
||||
return False
|
||||
return ans
|
||||
|
||||
class RuleEditDialog(RuleEditDialogBase):
|
||||
|
||||
PREFS_NAME = 'edit-add-filter-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 file, if the filename <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)
|
||||
return text
|
||||
|
||||
class Rules(RulesBase):
|
||||
|
||||
RuleItemClass = RuleItem
|
||||
RuleEditDialogClass = RuleEditDialog
|
||||
MSG = _('You can specify rules to add/ignore files here. Click the "Add Rule" button'
|
||||
' below to get started. The rules will be processed in order for every file until either an'
|
||||
' "add" or an "ignore" rule matches. If no rules match, the file will be added only'
|
||||
' if its file extension is of a known ebook type.')
|
||||
|
||||
class Tester(TesterBase):
|
||||
|
||||
DIALOG_TITLE = _('Test filename filter rules')
|
||||
PREFS_NAME = 'test-file-filter-rules'
|
||||
LABEL = _('Enter a filename to test:')
|
||||
PLACEHOLDER = _('Enter filename and click the Test button')
|
||||
EMPTY_RESULT = '<p> </p>'
|
||||
|
||||
def do_test(self):
|
||||
filename = self.value.strip()
|
||||
allowed = filter_filename(map(compile_rule, self.rules), filename)
|
||||
if allowed is None:
|
||||
self.result.setText(_('The filename %s did not match any rules') % filename)
|
||||
else:
|
||||
self.result.setText(_('The filename {0} will be {1}').format(filename, _('added' if allowed else 'ignored')))
|
||||
|
||||
|
||||
class RulesDialog(RulesDialogBase):
|
||||
|
||||
DIALOG_TITLE = _('Edit file filter rules')
|
||||
PREFS_NAME = 'edit-file-filter-rules'
|
||||
RulesClass = Rules
|
||||
TesterClass = Tester
|
||||
PREFS_OBJECT = add_filters
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application([])
|
||||
d = RulesDialog()
|
||||
d.rules = [
|
||||
{'action':'ignore', 'query':'ignore-me', 'match_type':'startswith'},
|
||||
{'action':'add', 'query':'*.moose', 'match_type':'glob'},
|
||||
]
|
||||
d.exec_()
|
||||
from pprint import pprint
|
||||
pprint(d.rules)
|
||||
del d, app
|
Loading…
x
Reference in New Issue
Block a user