diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.py b/src/calibre/gui2/catalog/catalog_epub_mobi.py
index ac251ff801..8a2035c76f 100644
--- a/src/calibre/gui2/catalog/catalog_epub_mobi.py
+++ b/src/calibre/gui2/catalog/catalog_epub_mobi.py
@@ -38,7 +38,7 @@ class PluginWidget(QWidget,Ui_Form):
self._initControlArrays()
def _initControlArrays(self):
-
+ # Default values for controls
CheckBoxControls = []
ComboBoxControls = []
DoubleSpinBoxControls = []
@@ -72,13 +72,27 @@ class PluginWidget(QWidget,Ui_Form):
# LineEditControls
option_fields += zip(['exclude_genre'],['\[.+\]|\+'],['line_edit'])
- option_fields += zip(['exclude_pattern'],[None],['line_edit'])
- option_fields += zip(['exclude_tags'],['~,'+_('Catalog')],['line_edit'])
+ #***option_fields += zip(['exclude_pattern'],[None],['line_edit'])
+ #***option_fields += zip(['exclude_tags'],['~,'+_('Catalog')],['line_edit'])
# SpinBoxControls
option_fields += zip(['thumb_width'],[1.00],['spin_box'])
- # Prefix rules TableWidget
+ # Exclusion rules
+ option_fields += zip(['exclusion_rules_tw','exclusion_rules_tw'],
+ [{'ordinal':0,
+ 'enabled':True,
+ 'name':'Catalogs',
+ 'field':'Tags',
+ 'pattern':'Catalog'},
+ {'ordinal':1,
+ 'enabled':False,
+ 'name':'New rule',
+ 'field':'',
+ 'pattern':''}],
+ ['table_widget','table_widget'])
+
+ # Prefix rules
option_fields += zip(['prefix_rules_tw','prefix_rules_tw','prefix_rules_tw'],
[{'ordinal':0,
'enabled':True,
@@ -123,13 +137,13 @@ class PluginWidget(QWidget,Ui_Form):
['exclude_source_field','header_note_source_field',
'merge_source_field']
LineEditControls (c_type: line_edit):
- ['exclude_genre','exclude_pattern','exclude_tags']
+ ['exclude_genre']
RadioButtonControls (c_type: radio_button):
['merge_before','merge_after']
SpinBoxControls (c_type: spin_box):
['thumb_width']
TableWidgetControls (c_type: table_widget):
- ['prefix_rules_tw']
+ ['exclusion_rules_tw','prefix_rules_tw']
'''
self.name = name
@@ -139,6 +153,7 @@ class PluginWidget(QWidget,Ui_Form):
# Update dialog fields from stored options
+ exclusion_rules = []
prefix_rules = []
for opt in self.OPTION_FIELDS:
c_name, c_def, c_type = opt
@@ -159,16 +174,22 @@ class PluginWidget(QWidget,Ui_Form):
getattr(self, c_name).setChecked(opt_value)
elif c_type in ['spin_box']:
getattr(self, c_name).setValue(float(opt_value))
+ elif c_type in ['table_widget'] and c_name == 'exclusion_rules_tw':
+ if opt_value not in exclusion_rules:
+ exclusion_rules.append(opt_value)
elif c_type in ['table_widget'] and c_name == 'prefix_rules_tw':
if opt_value not in prefix_rules:
prefix_rules.append(opt_value)
+ '''
+ ***
# Init self.exclude_source_field_name
self.exclude_source_field_name = ''
cs = unicode(self.exclude_source_field.currentText())
if cs > '':
exclude_source_spec = self.exclude_source_fields[cs]
self.exclude_source_field_name = exclude_source_spec['field']
+ '''
# Init self.merge_source_field_name
self.merge_source_field_name = ''
@@ -190,10 +211,13 @@ class PluginWidget(QWidget,Ui_Form):
# Hook changes to Description section
self.generate_descriptions.stateChanged.connect(self.generate_descriptions_changed)
+ # Initialize exclusion rules
+ self.exclusion_rules_table = ExclusionRules(self.exclusion_rules_gb_hl,
+ "exclusion_rules_tw",exclusion_rules, self.eligible_custom_fields,self.db)
+
# Initialize prefix rules
- self.prefix_rules_table = PrefixRules(self.prefix_rules_gb_hl, "prefix_rules_tw",
- prefix_rules, self.eligible_custom_fields,
- self.db)
+ self.prefix_rules_table = PrefixRules(self.prefix_rules_gb_hl,
+ "prefix_rules_tw",prefix_rules, self.eligible_custom_fields,self.db)
def options(self):
# Save/return the current options
@@ -204,6 +228,8 @@ class PluginWidget(QWidget,Ui_Form):
opts_dict = {}
# Save values to gprefs
prefix_rules_processed = False
+ exclusion_rules_processed = False
+
for opt in self.OPTION_FIELDS:
c_name, c_def, c_type = opt
if c_type in ['check_box', 'radio_button']:
@@ -215,7 +241,10 @@ class PluginWidget(QWidget,Ui_Form):
elif c_type in ['spin_box']:
opt_value = unicode(getattr(self, c_name).value())
elif c_type in ['table_widget']:
- opt_value = self.prefix_rules_table.get_data()
+ if c_name == 'prefix_rules_tw':
+ opt_value = self.prefix_rules_table.get_data()
+ if c_name == 'exclusion_rules_tw':
+ opt_value = self.exclusion_rules_table.get_data()
gprefs.set(self.name + '_' + c_name, opt_value)
@@ -246,19 +275,49 @@ class PluginWidget(QWidget,Ui_Form):
pr = (rule['name'],rule['field'],rule['pattern'],rule['prefix'])
rule_set.append(pr)
-
opt_value = tuple(rule_set)
opts_dict['prefix_rules'] = opt_value
prefix_rules_processed = True
+ elif c_name == 'exclusion_rules_tw':
+ if exclusion_rules_processed:
+ continue
+ rule_set = []
+ for rule in opt_value:
+ # Test for empty name/field/pattern/prefix, continue
+ # If pattern = any or unspecified, convert to regex
+ if not rule['enabled']:
+ continue
+ elif not rule['field'] or not rule['pattern']:
+ continue
+ else:
+ if rule['field'] != 'Tags':
+ # Look up custom column name
+ #print(self.eligible_custom_fields[rule['field']]['field'])
+ rule['field'] = self.eligible_custom_fields[rule['field']]['field']
+ if rule['pattern'].startswith('any'):
+ rule['pattern'] = '.*'
+ elif rule['pattern'] == 'unspecified':
+ rule['pattern'] = 'None'
+
+ pr = (rule['name'],rule['field'],rule['pattern'])
+ rule_set.append(pr)
+
+ opt_value = tuple(rule_set)
+ opts_dict['exclusion_rules'] = opt_value
+ exclusion_rules_processed = True
+
else:
opts_dict[c_name] = opt_value
+ '''
+ ***
# Generate markers for hybrids
#opts_dict['read_book_marker'] = "%s:%s" % (self.read_source_field_name,
# self.read_pattern.text())
opts_dict['exclude_book_marker'] = "%s:%s" % (self.exclude_source_field_name,
self.exclude_pattern.text())
+ '''
# Generate specs for merge_comments, header_note_source_field
checked = ''
@@ -306,6 +365,8 @@ class PluginWidget(QWidget,Ui_Form):
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
+ '''
+ ***
# Blank field first
self.exclude_source_field.addItem('')
# Add the sorted eligible fields to the combo box
@@ -313,7 +374,7 @@ class PluginWidget(QWidget,Ui_Form):
self.exclude_source_field.addItem(cf)
self.exclude_source_fields = custom_fields
self.exclude_source_field.currentIndexChanged.connect(self.exclude_source_field_changed)
-
+ '''
# Populate the 'Header note' combo box
custom_fields = {}
@@ -488,7 +549,7 @@ class NoWheelComboBox(QComboBox):
# Disable the mouse wheel on top of the combo box changing selection as plays havoc in a grid
event.ignore()
-class PrefixRulesComboBox(NoWheelComboBox):
+class ComboBox(NoWheelComboBox):
# Caller is responsible for providing the list in the preferred order
def __init__(self, parent, items, selected_text,insert_blank=True):
NoWheelComboBox.__init__(self, parent)
@@ -511,8 +572,8 @@ class GenericRulesTable(QTableWidget):
placeholders for basic methods to be overriden
'''
- def __init__(self, parent_gb_hl, object_name, prefix_rules, eligible_custom_fields, db):
- self.prefix_rules = prefix_rules
+ def __init__(self, parent_gb_hl, object_name, rules, eligible_custom_fields, db):
+ self.rules = rules
self.eligible_custom_fields = eligible_custom_fields
self.db = db
QTableWidget.__init__(self)
@@ -525,12 +586,11 @@ class GenericRulesTable(QTableWidget):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
self.setSizePolicy(sizePolicy)
- self.setMaximumSize(QSize(16777215, 118))
+ self.setMaximumSize(QSize(16777215, 114))
self.setColumnCount(0)
self.setRowCount(0)
self.layout.addWidget(self)
-
self._init_table_widget()
self._init_controls()
self._initialize()
@@ -686,10 +746,138 @@ class GenericRulesTable(QTableWidget):
'''
pass
+ def resize_name(self, scale):
+ current_width = self.columnWidth(1)
+ self.setColumnWidth(1, min(225,int(current_width * scale)))
+
+ def rule_name_edited(self):
+ current_row = self.currentRow()
+ self.cellWidget(current_row,1).home(False)
+ self.setFocus()
+ self.select_and_scroll_to_row(current_row)
+
+ def select_and_scroll_to_row(self, row):
+ self.selectRow(row)
+ self.scrollToItem(self.currentItem())
+
+class ExclusionRules(GenericRulesTable):
+
+ def _init_table_widget(self):
+ header_labels = ['','Name','Field','Value']
+ self.setColumnCount(len(header_labels))
+ self.setHorizontalHeaderLabels(header_labels)
+ self.setSortingEnabled(False)
+ self.setSelectionBehavior(QAbstractItemView.SelectRows)
+
+ def _initialize(self):
+ # Override max size (118) set in GenericRulesTable
+ self.setMaximumSize(QSize(16777215, 83))
+
+ self.populate()
+ self.resizeColumnsToContents()
+ self.resize_name(1.5)
+ self.horizontalHeader().setStretchLastSection(True)
+
+ def convert_row_to_data(self, row):
+ data = self.create_blank_row_data()
+ data['ordinal'] = row
+ data['enabled'] = self.item(row,0).checkState() == Qt.Checked
+ data['name'] = unicode(self.cellWidget(row,1).text()).strip()
+ data['field'] = unicode(self.cellWidget(row,2).currentText()).strip()
+ data['pattern'] = unicode(self.cellWidget(row,3).currentText()).strip()
+ return data
+
+ def create_blank_row_data(self):
+ data = {}
+ data['ordinal'] = -1
+ data['enabled'] = False
+ data['name'] = 'New rule'
+ data['field'] = ''
+ data['pattern'] = ''
+ return data
+
+ def get_data(self):
+ data_items = []
+ for row in range(self.rowCount()):
+ data = self.convert_row_to_data(row)
+ data_items.append(
+ {'ordinal':data['ordinal'],
+ 'enabled':data['enabled'],
+ 'name':data['name'],
+ 'field':data['field'],
+ 'pattern':data['pattern']})
+ return data_items
+
+ def populate(self):
+ # Format of rules list is different if default values vs retrieved JSON
+ # Hack to normalize list style
+ rules = self.rules
+ if rules and type(rules[0]) is list:
+ rules = rules[0]
+ self.setFocus()
+ rules = sorted(rules, key=lambda k: k['ordinal'])
+ for row, rule in enumerate(rules):
+ self.insertRow(row)
+ self.select_and_scroll_to_row(row)
+ self.populate_table_row(row, rule)
+ self.selectRow(0)
+
+ def populate_table_row(self, row, data):
+
+ def set_rule_name_in_row(row, col, name=''):
+ rule_name = QLineEdit(name)
+ rule_name.home(False)
+ rule_name.editingFinished.connect(self.rule_name_edited)
+ self.setCellWidget(row, col, rule_name)
+
+ def set_source_field_in_row(row, col, field=''):
+ source_combo = ComboBox(self, sorted(self.eligible_custom_fields.keys(), key=sort_key), field)
+ source_combo.currentIndexChanged.connect(partial(self.source_index_changed, source_combo, row))
+ self.setCellWidget(row, col, source_combo)
+ return source_combo
+
+ # Entry point
+ self.blockSignals(True)
+
+ # Column 0: Enabled
+ self.setItem(row, 0, CheckableTableWidgetItem(data['enabled']))
+
+ # Column 1: Rule name
+ set_rule_name_in_row(row, 1, name=data['name'])
+
+ # Column 2: Source field
+ source_combo = set_source_field_in_row(row, 2, field=data['field'])
+
+ # Column 3: Pattern
+ # The contents of the Pattern field is driven by the Source field
+ self.source_index_changed(source_combo, row, 3, pattern=data['pattern'])
+
+ self.blockSignals(False)
+
+ def source_index_changed(self, combo, row, col, pattern=''):
+ # Populate the Pattern field based upon the Source field
+ source_field = str(combo.currentText())
+ if source_field == '':
+ values = []
+ elif source_field == 'Tags':
+ values = sorted(self.db.all_tags(), key=sort_key)
+ else:
+ if self.eligible_custom_fields[source_field]['datatype'] in ['enumeration', 'text']:
+ values = self.db.all_custom(self.db.field_metadata.key_to_label(
+ self.eligible_custom_fields[source_field]['field']))
+ values = sorted(values, key=sort_key)
+ elif self.eligible_custom_fields[source_field]['datatype'] in ['bool']:
+ values = ['True','False','unspecified']
+ elif self.eligible_custom_fields[source_field]['datatype'] in ['composite']:
+ values = ['any value','unspecified']
+
+ values_combo = ComboBox(self, values, pattern)
+ self.setCellWidget(row, 3, values_combo)
+
class PrefixRules(GenericRulesTable):
def _init_table_widget(self):
- header_labels = ['','Name','Prefix','Source','Pattern']
+ header_labels = ['','Name','Prefix','Field','Value']
self.setColumnCount(len(header_labels))
self.setHorizontalHeaderLabels(header_labels)
self.setSortingEnabled(False)
@@ -873,8 +1061,8 @@ class PrefixRules(GenericRulesTable):
def populate(self):
# Format of rules list is different if default values vs retrieved JSON
# Hack to normalize list style
- rules = self.prefix_rules
- if type(rules[0]) is list:
+ rules = self.rules
+ if rules and type(rules[0]) is list:
rules = rules[0]
self.setFocus()
rules = sorted(rules, key=lambda k: k['ordinal'])
@@ -887,7 +1075,7 @@ class PrefixRules(GenericRulesTable):
def populate_table_row(self, row, data):
def set_prefix_field_in_row(row, col, field=''):
- prefix_combo = PrefixRulesComboBox(self, self.prefix_list, field)
+ prefix_combo = ComboBox(self, self.prefix_list, field)
self.setCellWidget(row, col, prefix_combo)
def set_rule_name_in_row(row, col, name=''):
@@ -897,7 +1085,7 @@ class PrefixRules(GenericRulesTable):
self.setCellWidget(row, col, rule_name)
def set_source_field_in_row(row, col, field=''):
- source_combo = PrefixRulesComboBox(self, sorted(self.eligible_custom_fields.keys(), key=sort_key), field)
+ source_combo = ComboBox(self, sorted(self.eligible_custom_fields.keys(), key=sort_key), field)
source_combo.currentIndexChanged.connect(partial(self.source_index_changed, source_combo, row))
self.setCellWidget(row, col, source_combo)
return source_combo
@@ -926,22 +1114,9 @@ class PrefixRules(GenericRulesTable):
self.blockSignals(False)
- def resize_name(self, scale):
- current_width = self.columnWidth(1)
- self.setColumnWidth(1, min(225,int(current_width * scale)))
-
- def rule_name_edited(self):
- current_row = self.currentRow()
- self.cellWidget(current_row,1).home(False)
- self.setFocus()
- self.select_and_scroll_to_row(current_row)
-
- def select_and_scroll_to_row(self, row):
- self.selectRow(row)
- self.scrollToItem(self.currentItem())
-
def source_index_changed(self, combo, row, col, pattern=''):
# Populate the Pattern field based upon the Source field
+ # row, col are the control that changed
source_field = str(combo.currentText())
if source_field == '':
@@ -960,6 +1135,6 @@ class PrefixRules(GenericRulesTable):
elif self.eligible_custom_fields[source_field]['datatype'] in ['composite']:
values = ['any value','unspecified']
- values_combo = PrefixRulesComboBox(self, values, pattern)
+ values_combo = ComboBox(self, values, pattern)
self.setCellWidget(row, 4, values_combo)
diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.ui b/src/calibre/gui2/catalog/catalog_epub_mobi.ui
index bf10cacd38..29ca39bf10 100644
--- a/src/calibre/gui2/catalog/catalog_epub_mobi.ui
+++ b/src/calibre/gui2/catalog/catalog_epub_mobi.ui
@@ -184,7 +184,7 @@ p, li { white-space: pre-wrap; }
-
-
+
0
@@ -198,130 +198,14 @@ p, li { white-space: pre-wrap; }
- Books matching either pattern will not be included in generated catalog.
+ Matching books will not be included in generated catalog.
Excluded books
-
-
- QFormLayout::FieldsStayAtSizeHint
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 175
- 0
-
-
-
-
- 200
- 16777215
-
-
-
- Tags to &exclude
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- true
-
-
- exclude_tags
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- <p>Comma-separated list of tags to exclude.
-Default: ~,Catalog
-
-
-
-
-
- -
-
-
-
-
-
-
- 175
- 0
-
-
-
-
- 200
- 16777215
-
-
-
- &Column/value
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- true
-
-
- exclude_source_field
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Column containing additional exclusion criteria
-
-
- QComboBox::AdjustToMinimumContentsLengthWithIcon
-
-
- 18
-
-
-
- -
-
-
-
- 150
- 0
-
-
-
- Exclusion pattern
-
-
-
-
+
+ -
+
@@ -335,7 +219,7 @@ Default: ~,Catalog
- The first matching rule will be used to add a prefix to book listings in the generated catalog.
+ The earliest enabled matching rule will be used to add a prefix to book listings in the generated catalog.
Prefix rules
@@ -369,9 +253,9 @@ Default: ~,Catalog
QFormLayout::FieldsStayAtSizeHint
-
-
+
-
-
+
175
@@ -385,16 +269,13 @@ Default: ~,Catalog
- &Thumbnail width
+ &Thumb width
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
- true
-
- thumb_width
+ merge_source_field
@@ -406,6 +287,12 @@ Default: ~,Catalog
0
+
+
+ 137
+ 16777215
+
+
Size hint for Description cover thumbnails
@@ -426,38 +313,17 @@ Default: ~,Catalog
-
-
- -
-
-
-
-
-
- 0
- 0
-
-
-
-
- 175
- 0
-
-
-
-
- 200
- 16777215
-
-
-
-
+
+
+ Qt::Vertical
+
+
+ -
+
- &Description note
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ &Extra note
header_note_source_field
@@ -478,6 +344,12 @@ Default: ~,Catalog
0
+
+
+ 16777215
+ 16777215
+
+
Custom column source for note to include in Description header area
@@ -485,7 +357,7 @@ Default: ~,Catalog
- -
+
-
-
diff --git a/src/calibre/library/catalogs/epub_mobi.py b/src/calibre/library/catalogs/epub_mobi.py
index a7da345dc2..385a699c7b 100644
--- a/src/calibre/library/catalogs/epub_mobi.py
+++ b/src/calibre/library/catalogs/epub_mobi.py
@@ -48,29 +48,13 @@ class EPUB_MOBI(CatalogPlugin):
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--exclude-genre',
- default='\[.+\]',
+ default='\[.+\]|\+',
dest='exclude_genre',
action = None,
- help=_("Regex describing tags to exclude as genres.\n" "Default: '%default' excludes bracketed tags, e.g. '[]'\n"
+ help=_("Regex describing tags to exclude as genres.\n"
+ "Default: '%default' excludes bracketed tags, e.g. '[Project Gutenberg]', and '+', the default tag for read books.\n"
"Applies to: ePub, MOBI output formats")),
-# Option('--exclude-book-marker',
-# default=':',
-# dest='exclude_book_marker',
-# action = None,
-# help=_("#:pattern specifying custom field/contents indicating book should be excluded.\n"
-# "For example: '#status:Archived' will exclude a book with a value of 'Archived' in the custom column 'status'.\n"
-# "Default: '%default'\n"
-# "Applies to ePub, MOBI output formats")),
-# Option('--exclude-tags',
-# default=('~,Catalog'),
-# dest='exclude_tags',
-# action = None,
-# help=_("Comma-separated list of tag words indicating book should be excluded from output. "
-# "For example: 'skip' will match 'skip this book' and 'Skip will like this'. "
-# "Default:'%default'\n"
-# "Applies to: ePub, MOBI output formats")),
-
Option('--exclusion-rules',
default="(('Excluded tags','Tags','~,Catalog'),)",
dest='exclusion_rules',
diff --git a/src/calibre/library/catalogs/epub_mobi_builder.py b/src/calibre/library/catalogs/epub_mobi_builder.py
index 6cc94c0e08..111ceda9fe 100644
--- a/src/calibre/library/catalogs/epub_mobi_builder.py
+++ b/src/calibre/library/catalogs/epub_mobi_builder.py
@@ -657,14 +657,36 @@ Author '{0}':
# Merge opts.exclude_tags with opts.search_text
# Updated to use exact match syntax
- empty_exclude_tags = False if len(self.opts.exclude_tags) else True
+
+ exclude_tags = []
+ for rule in self.opts.exclusion_rules:
+ if rule[1].lower() == 'tags':
+ exclude_tags.extend(rule[2].split(','))
+
+ # Remove dups
+ self.exclude_tags = exclude_tags = list(set(exclude_tags))
+
+ if self.opts.verbose and self.exclude_tags:
+ #self.opts.log.info(" excluding tag list %s" % exclude_tags)
+ search_terms = []
+ for tag in exclude_tags:
+ search_terms.append("tag:=%s" % tag)
+ search_phrase = "%s" % " or ".join(search_terms)
+ self.opts.search_text = search_phrase
+ data = self.plugin.search_sort_db(self.db, self.opts)
+ for record in data:
+ self.opts.log.info("\t- %s (Exclusion rule %s)" % (record['title'], exclude_tags))
+ # Reset the database
+ self.opts.search_text = ''
+ data = self.plugin.search_sort_db(self.db, self.opts)
+
search_phrase = ''
- if not empty_exclude_tags:
- exclude_tags = self.opts.exclude_tags.split(',')
+ if exclude_tags:
search_terms = []
for tag in exclude_tags:
search_terms.append("tag:=%s" % tag)
search_phrase = "not (%s)" % " or ".join(search_terms)
+
# If a list of ids are provided, don't use search_text
if self.opts.ids:
self.opts.search_text = search_phrase
@@ -1672,14 +1694,13 @@ Author '{0}':
self.opts.sort_by = 'series'
- # Merge opts.exclude_tags with opts.search_text
+ # Merge self.exclude_tags with opts.search_text
# Updated to use exact match syntax
- empty_exclude_tags = False if len(self.opts.exclude_tags) else True
+
search_phrase = 'series:true '
- if not empty_exclude_tags:
- exclude_tags = self.opts.exclude_tags.split(',')
+ if self.exclude_tags:
search_terms = []
- for tag in exclude_tags:
+ for tag in self.exclude_tags:
search_terms.append("tag:=%s" % tag)
search_phrase += "not (%s)" % " or ".join(search_terms)
@@ -3120,7 +3141,7 @@ Author '{0}':
Evaluate conditions for including prefixes in various listings
'''
def log_prefix_rule_match_info(rule, record):
- self.opts.log.info(" %s %s by %s (Prefix rule '%s': %s:%s)" %
+ self.opts.log.info("\t%s %s by %s (Prefix rule '%s': %s:%s)" %
(rule['prefix'],record['title'],
record['authors'][0], rule['name'],
rule['field'],rule['pattern']))
@@ -3816,9 +3837,14 @@ Author '{0}':
return friendly_tag
def getMarkerTags(self):
- ''' Return a list of special marker tags to be excluded from genre list '''
+ '''
+ Return a list of special marker tags to be excluded from genre list
+ exclusion_rules = ('name','Tags|#column','[]|pattern')
+ '''
markerTags = []
- markerTags.extend(self.opts.exclude_tags.split(','))
+ for rule in self.opts.exclusion_rules:
+ if rule[1].lower() == 'tags':
+ markerTags.extend(rule[2].split(','))
return markerTags
def letter_or_symbol(self,char):
@@ -3996,21 +4022,40 @@ Author '{0}':
'''
Remove excluded entries
'''
- field, pat = self.opts.exclude_book_marker.split(':')
- if pat == '':
- return data_set
filtered_data_set = []
- for record in data_set:
- field_contents = self.__db.get_field(record['id'],
- field,
- index_is_id=True)
- if field_contents:
- if re.search(pat, unicode(field_contents),
- re.IGNORECASE) is not None:
- continue
- filtered_data_set.append(record)
+ exclusion_pairs = []
+ exclusion_set = []
+ for rule in self.opts.exclusion_rules:
+ if rule[1].startswith('#') and rule[2] != '':
+ field = rule[1]
+ pat = rule[2]
+ exclusion_pairs.append((field,pat))
+ else:
+ continue
- return filtered_data_set
+ if exclusion_pairs:
+ for record in data_set:
+ for exclusion_pair in exclusion_pairs:
+ field,pat = exclusion_pair
+ field_contents = self.__db.get_field(record['id'],
+ field,
+ index_is_id=True)
+ if field_contents:
+ if re.search(pat, unicode(field_contents),
+ re.IGNORECASE) is not None:
+ if self.opts.verbose:
+ self.opts.log.info(" excluding '%s' (%s:%s)" % (record['title'], field, pat))
+ exclusion_set.append(record)
+ if record in filtered_data_set:
+ filtered_data_set.remove(record)
+ break
+ else:
+ if (record not in filtered_data_set and
+ record not in exclusion_set):
+ filtered_data_set.append(record)
+ return filtered_data_set
+ else:
+ return data_set
def processSpecialTags(self, tags, this_title, opts):