mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
WIP - changes to support prefix rules
This commit is contained in:
parent
c28175adfc
commit
add5bb824d
@ -151,14 +151,23 @@ p.title {
|
|||||||
font-size:xx-large;
|
font-size:xx-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.wishlist_item, p.unread_book, p.read_book {
|
p.wishlist_item, p.unread_book, p.read_book, p.line_item {
|
||||||
text-align:left;
|
font-family:monospace;
|
||||||
margin-top:0px;
|
margin-top:0px;
|
||||||
margin-bottom:0px;
|
margin-bottom:0px;
|
||||||
margin-left:2em;
|
margin-left:2em;
|
||||||
|
text-align:left;
|
||||||
text-indent:-2em;
|
text-indent:-2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.prefix {}
|
||||||
|
span.entry {
|
||||||
|
font-family: serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Book Descriptions
|
||||||
|
*/
|
||||||
td.publisher, td.date {
|
td.publisher, td.date {
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
|
@ -10,8 +10,11 @@ from calibre.ebooks.conversion.config import load_defaults
|
|||||||
from calibre.gui2 import gprefs
|
from calibre.gui2 import gprefs
|
||||||
|
|
||||||
from catalog_epub_mobi_ui import Ui_Form
|
from catalog_epub_mobi_ui import Ui_Form
|
||||||
from PyQt4.Qt import QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit, \
|
from PyQt4 import QtGui
|
||||||
QRadioButton, QWidget
|
from PyQt4.Qt import (Qt, QCheckBox, QComboBox, QDialog, QDialogButtonBox, QDoubleSpinBox,
|
||||||
|
QHBoxLayout, QIcon, QLabel, QLineEdit,
|
||||||
|
QPlainTextEdit, QRadioButton, QSize, QTableWidget, QTableWidgetItem,
|
||||||
|
QVBoxLayout, QWidget)
|
||||||
|
|
||||||
class PluginWidget(QWidget,Ui_Form):
|
class PluginWidget(QWidget,Ui_Form):
|
||||||
|
|
||||||
@ -36,6 +39,7 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
DoubleSpinBoxControls = []
|
DoubleSpinBoxControls = []
|
||||||
LineEditControls = []
|
LineEditControls = []
|
||||||
RadioButtonControls = []
|
RadioButtonControls = []
|
||||||
|
TableWidgetControls = []
|
||||||
|
|
||||||
for item in self.__dict__:
|
for item in self.__dict__:
|
||||||
if type(self.__dict__[item]) is QCheckBox:
|
if type(self.__dict__[item]) is QCheckBox:
|
||||||
@ -48,6 +52,8 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
LineEditControls.append(str(self.__dict__[item].objectName()))
|
LineEditControls.append(str(self.__dict__[item].objectName()))
|
||||||
elif type(self.__dict__[item]) is QRadioButton:
|
elif type(self.__dict__[item]) is QRadioButton:
|
||||||
RadioButtonControls.append(str(self.__dict__[item].objectName()))
|
RadioButtonControls.append(str(self.__dict__[item].objectName()))
|
||||||
|
elif type(self.__dict__[item]) is QTableWidget:
|
||||||
|
TableWidgetControls.append(str(self.__dict__[item].objectName()))
|
||||||
|
|
||||||
option_fields = zip(CheckBoxControls,
|
option_fields = zip(CheckBoxControls,
|
||||||
[True for i in CheckBoxControls],
|
[True for i in CheckBoxControls],
|
||||||
@ -60,15 +66,41 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
['radio_button' for i in RadioButtonControls])
|
['radio_button' for i in RadioButtonControls])
|
||||||
|
|
||||||
# LineEditControls
|
# LineEditControls
|
||||||
option_fields += zip(['exclude_genre'],['\[.+\]'],['line_edit'])
|
option_fields += zip(['exclude_genre'],['\[.+\]|\+'],['line_edit'])
|
||||||
option_fields += zip(['exclude_pattern'],[None],['line_edit'])
|
option_fields += zip(['exclude_pattern'],[None],['line_edit'])
|
||||||
option_fields += zip(['exclude_tags'],['~,'+_('Catalog')],['line_edit'])
|
option_fields += zip(['exclude_tags'],['~,'+_('Catalog')],['line_edit'])
|
||||||
option_fields += zip(['read_pattern'],['+'],['line_edit'])
|
|
||||||
option_fields += zip(['wishlist_tag'],['Wishlist'],['line_edit'])
|
|
||||||
|
|
||||||
# SpinBoxControls
|
# SpinBoxControls
|
||||||
option_fields += zip(['thumb_width'],[1.00],['spin_box'])
|
option_fields += zip(['thumb_width'],[1.00],['spin_box'])
|
||||||
|
|
||||||
|
# Prefix rules TableWidget
|
||||||
|
# ordinal/enabled/rule name/source field/pattern/prefix
|
||||||
|
#option_fields += zip(['prefix_rules_tw','prefix_rules_tw','prefix_rules_tw'],
|
||||||
|
# [(1,True,'Read book','Tags','+','+'),
|
||||||
|
# (2,True,'Wishlist','Tags','Wishlist','x'),
|
||||||
|
# (3,False,'<new rule>','','','')],
|
||||||
|
# ['table_widget','table_widget','table_widget'])
|
||||||
|
option_fields += zip(['prefix_rules_tw','prefix_rules_tw','prefix_rules_tw'],
|
||||||
|
[{'ordinal':1,
|
||||||
|
'enabled':True,
|
||||||
|
'name':'Read book',
|
||||||
|
'field':'Tags',
|
||||||
|
'pattern':'+',
|
||||||
|
'prefix':'+'},
|
||||||
|
{'ordinal':2,
|
||||||
|
'enabled':True,
|
||||||
|
'name':'Wishlist',
|
||||||
|
'field':'Tags',
|
||||||
|
'pattern':'Wishlist',
|
||||||
|
'prefix':'x'},
|
||||||
|
{'ordinal':3,
|
||||||
|
'enabled':False,
|
||||||
|
'name':'<new rule>',
|
||||||
|
'field':'',
|
||||||
|
'pattern':'',
|
||||||
|
'prefix':''}],
|
||||||
|
['table_widget','table_widget','table_widget'])
|
||||||
|
|
||||||
self.OPTION_FIELDS = option_fields
|
self.OPTION_FIELDS = option_fields
|
||||||
|
|
||||||
def initialize(self, name, db):
|
def initialize(self, name, db):
|
||||||
@ -78,23 +110,25 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
['generate_titles','generate_series','generate_genres',
|
['generate_titles','generate_series','generate_genres',
|
||||||
'generate_recently_added','generate_descriptions','include_hr']
|
'generate_recently_added','generate_descriptions','include_hr']
|
||||||
ComboBoxControls (c_type: combo_box):
|
ComboBoxControls (c_type: combo_box):
|
||||||
['read_source_field','exclude_source_field','header_note_source_field',
|
['exclude_source_field','header_note_source_field',
|
||||||
'merge_source_field']
|
'merge_source_field']
|
||||||
LineEditControls (c_type: line_edit):
|
LineEditControls (c_type: line_edit):
|
||||||
['exclude_genre','exclude_pattern','exclude_tags','read_pattern',
|
['exclude_genre','exclude_pattern','exclude_tags']
|
||||||
'wishlist_tag']
|
|
||||||
RadioButtonControls (c_type: radio_button):
|
RadioButtonControls (c_type: radio_button):
|
||||||
['merge_before','merge_after']
|
['merge_before','merge_after']
|
||||||
SpinBoxControls (c_type: spin_box):
|
SpinBoxControls (c_type: spin_box):
|
||||||
['thumb_width']
|
['thumb_width']
|
||||||
|
TableWidgetControls (c_type: table_widget):
|
||||||
|
['prefix_rules_tw']
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.db = db
|
self.db = db
|
||||||
self.populateComboBoxes()
|
self.all_custom_fields = self.db.custom_field_keys()
|
||||||
|
self.populate_combo_boxes()
|
||||||
|
|
||||||
# Update dialog fields from stored options
|
# Update dialog fields from stored options
|
||||||
|
prefix_rules = []
|
||||||
for opt in self.OPTION_FIELDS:
|
for opt in self.OPTION_FIELDS:
|
||||||
c_name, c_def, c_type = opt
|
c_name, c_def, c_type = opt
|
||||||
opt_value = gprefs.get(self.name + '_' + c_name, c_def)
|
opt_value = gprefs.get(self.name + '_' + c_name, c_def)
|
||||||
@ -114,11 +148,8 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
getattr(self, c_name).setChecked(opt_value)
|
getattr(self, c_name).setChecked(opt_value)
|
||||||
elif c_type in ['spin_box']:
|
elif c_type in ['spin_box']:
|
||||||
getattr(self, c_name).setValue(float(opt_value))
|
getattr(self, c_name).setValue(float(opt_value))
|
||||||
|
elif c_type in ['table_widget'] and c_name == 'prefix_rules_tw':
|
||||||
# Init self.read_source_field_name
|
prefix_rules.append(opt_value)
|
||||||
cs = unicode(self.read_source_field.currentText())
|
|
||||||
read_source_spec = self.read_source_fields[cs]
|
|
||||||
self.read_source_field_name = read_source_spec['field']
|
|
||||||
|
|
||||||
# Init self.exclude_source_field_name
|
# Init self.exclude_source_field_name
|
||||||
self.exclude_source_field_name = ''
|
self.exclude_source_field_name = ''
|
||||||
@ -147,6 +178,32 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
# Hook changes to Description section
|
# Hook changes to Description section
|
||||||
self.generate_descriptions.stateChanged.connect(self.generate_descriptions_changed)
|
self.generate_descriptions.stateChanged.connect(self.generate_descriptions_changed)
|
||||||
|
|
||||||
|
# Init prefix_rules
|
||||||
|
self.initialize_prefix_rules(prefix_rules)
|
||||||
|
self.populate_prefix_rules(prefix_rules)
|
||||||
|
|
||||||
|
def initialize_prefix_rules(self, rules):
|
||||||
|
|
||||||
|
# Assign icons to buttons
|
||||||
|
self.move_rule_up_tb.setToolTip('Move rule up')
|
||||||
|
self.move_rule_up_tb.setIcon(QIcon(I('arrow-up.png')))
|
||||||
|
self.add_rule_tb.setToolTip('Add a new rule')
|
||||||
|
self.add_rule_tb.setIcon(QIcon(I('plus.png')))
|
||||||
|
self.delete_rule_tb.setToolTip('Delete selected rule')
|
||||||
|
self.delete_rule_tb.setIcon(QIcon(I('list_remove.png')))
|
||||||
|
self.move_rule_down_tb.setToolTip('Move rule down')
|
||||||
|
self.move_rule_down_tb.setIcon(QIcon(I('arrow-down.png')))
|
||||||
|
|
||||||
|
# Configure the QTableWidget
|
||||||
|
# enabled/rule name/source field/pattern/prefix
|
||||||
|
self.prefix_rules_tw.clear()
|
||||||
|
header_labels = ['','Name','Source','Pattern','Prefix']
|
||||||
|
self.prefix_rules_tw.setColumnCount(len(header_labels))
|
||||||
|
self.prefix_rules_tw.setHorizontalHeaderLabels(header_labels)
|
||||||
|
self.prefix_rules_tw.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.prefix_rules_tw.setRowCount(len(rules))
|
||||||
|
self.prefix_rules_tw.setSortingEnabled(False)
|
||||||
|
|
||||||
def options(self):
|
def options(self):
|
||||||
# Save/return the current options
|
# Save/return the current options
|
||||||
# exclude_genre stores literally
|
# exclude_genre stores literally
|
||||||
@ -203,7 +260,7 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
print " %s: %s" % (opt, repr(opts_dict[opt]))
|
print " %s: %s" % (opt, repr(opts_dict[opt]))
|
||||||
return opts_dict
|
return opts_dict
|
||||||
|
|
||||||
def populateComboBoxes(self):
|
def populate_combo_boxes(self):
|
||||||
# Custom column types declared in
|
# Custom column types declared in
|
||||||
# gui2.preferences.create_custom_column:CreateCustomColumn()
|
# gui2.preferences.create_custom_column:CreateCustomColumn()
|
||||||
# As of 0.7.34:
|
# As of 0.7.34:
|
||||||
@ -219,25 +276,9 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
# text Column shown in the tag browser
|
# text Column shown in the tag browser
|
||||||
# *text Comma-separated text, like tags, shown in tag browser
|
# *text Comma-separated text, like tags, shown in tag browser
|
||||||
|
|
||||||
all_custom_fields = self.db.custom_field_keys()
|
|
||||||
# Populate the 'Read book' hybrid
|
|
||||||
custom_fields = {}
|
|
||||||
custom_fields['Tag'] = {'field':'tag', 'datatype':u'text'}
|
|
||||||
for custom_field in all_custom_fields:
|
|
||||||
field_md = self.db.metadata_for_field(custom_field)
|
|
||||||
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
|
||||||
custom_fields[field_md['name']] = {'field':custom_field,
|
|
||||||
'datatype':field_md['datatype']}
|
|
||||||
# Add the sorted eligible fields to the combo box
|
|
||||||
for cf in sorted(custom_fields):
|
|
||||||
self.read_source_field.addItem(cf)
|
|
||||||
self.read_source_fields = custom_fields
|
|
||||||
self.read_source_field.currentIndexChanged.connect(self.read_source_field_changed)
|
|
||||||
|
|
||||||
|
|
||||||
# Populate the 'Excluded books' hybrid
|
# Populate the 'Excluded books' hybrid
|
||||||
custom_fields = {}
|
custom_fields = {}
|
||||||
for custom_field in all_custom_fields:
|
for custom_field in self.all_custom_fields:
|
||||||
field_md = self.db.metadata_for_field(custom_field)
|
field_md = self.db.metadata_for_field(custom_field)
|
||||||
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
||||||
custom_fields[field_md['name']] = {'field':custom_field,
|
custom_fields[field_md['name']] = {'field':custom_field,
|
||||||
@ -253,7 +294,7 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
|
|
||||||
# Populate the 'Header note' combo box
|
# Populate the 'Header note' combo box
|
||||||
custom_fields = {}
|
custom_fields = {}
|
||||||
for custom_field in all_custom_fields:
|
for custom_field in self.all_custom_fields:
|
||||||
field_md = self.db.metadata_for_field(custom_field)
|
field_md = self.db.metadata_for_field(custom_field)
|
||||||
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
||||||
custom_fields[field_md['name']] = {'field':custom_field,
|
custom_fields[field_md['name']] = {'field':custom_field,
|
||||||
@ -269,7 +310,7 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
|
|
||||||
# Populate the 'Merge with Comments' combo box
|
# Populate the 'Merge with Comments' combo box
|
||||||
custom_fields = {}
|
custom_fields = {}
|
||||||
for custom_field in all_custom_fields:
|
for custom_field in self.all_custom_fields:
|
||||||
field_md = self.db.metadata_for_field(custom_field)
|
field_md = self.db.metadata_for_field(custom_field)
|
||||||
if field_md['datatype'] in ['text','comments','composite']:
|
if field_md['datatype'] in ['text','comments','composite']:
|
||||||
custom_fields[field_md['name']] = {'field':custom_field,
|
custom_fields[field_md['name']] = {'field':custom_field,
|
||||||
@ -285,6 +326,42 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
self.merge_after.setEnabled(False)
|
self.merge_after.setEnabled(False)
|
||||||
self.include_hr.setEnabled(False)
|
self.include_hr.setEnabled(False)
|
||||||
|
|
||||||
|
def populate_prefix_rules(self,rules):
|
||||||
|
|
||||||
|
def populate_table_row(row, data):
|
||||||
|
self.prefix_rules_tw.blockSignals(True)
|
||||||
|
self.prefix_rules_tw.setItem(row, 0, CheckableTableWidgetItem(data['enabled']))
|
||||||
|
self.prefix_rules_tw.setItem(row, 1, QTableWidgetItem(data['name']))
|
||||||
|
set_source_field_in_row(row, field=data['field'])
|
||||||
|
|
||||||
|
self.prefix_rules_tw.setItem(row, 3, QTableWidgetItem(data['pattern']))
|
||||||
|
self.prefix_rules_tw.setItem(row, 4, QTableWidgetItem(data['prefix']))
|
||||||
|
self.prefix_rules_tw.blockSignals(False)
|
||||||
|
|
||||||
|
def set_source_field_in_row(row, field=''):
|
||||||
|
custom_fields = {}
|
||||||
|
custom_fields['Tags'] = {'field':'tag', 'datatype':u'text'}
|
||||||
|
for custom_field in self.all_custom_fields:
|
||||||
|
field_md = self.db.metadata_for_field(custom_field)
|
||||||
|
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
|
||||||
|
custom_fields[field_md['name']] = {'field':custom_field,
|
||||||
|
'datatype':field_md['datatype']}
|
||||||
|
self.prefix_rules_tw.setCellWidget(row, 2, SourceFieldComboBox(self, sorted(custom_fields.keys()), field))
|
||||||
|
# Need to connect to something that monitors changes to control pattern field based on source_field type
|
||||||
|
|
||||||
|
# Entry point
|
||||||
|
|
||||||
|
for row, data in enumerate(sorted(rules, key=lambda k: k['ordinal'])):
|
||||||
|
populate_table_row(row, data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Do this after populating cells
|
||||||
|
self.prefix_rules_tw.resizeColumnsToContents()
|
||||||
|
|
||||||
|
|
||||||
def read_source_field_changed(self,new_index):
|
def read_source_field_changed(self,new_index):
|
||||||
'''
|
'''
|
||||||
Process changes in the read_source_field combo box
|
Process changes in the read_source_field combo box
|
||||||
@ -388,3 +465,55 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
Process changes in the thumb_width spin box
|
Process changes in the thumb_width spin box
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CheckableTableWidgetItem(QTableWidgetItem):
|
||||||
|
'''
|
||||||
|
Borrowed from kiwidude
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, checked=False, is_tristate=False):
|
||||||
|
QTableWidgetItem.__init__(self, '')
|
||||||
|
self.setFlags(Qt.ItemFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled ))
|
||||||
|
if is_tristate:
|
||||||
|
self.setFlags(self.flags() | Qt.ItemIsTristate)
|
||||||
|
if checked:
|
||||||
|
self.setCheckState(Qt.Checked)
|
||||||
|
else:
|
||||||
|
if is_tristate and checked is None:
|
||||||
|
self.setCheckState(Qt.PartiallyChecked)
|
||||||
|
else:
|
||||||
|
self.setCheckState(Qt.Unchecked)
|
||||||
|
|
||||||
|
def get_boolean_value(self):
|
||||||
|
'''
|
||||||
|
Return a boolean value indicating whether checkbox is checked
|
||||||
|
If this is a tristate checkbox, a partially checked value is returned as None
|
||||||
|
'''
|
||||||
|
if self.checkState() == Qt.PartiallyChecked:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.checkState() == Qt.Checked
|
||||||
|
|
||||||
|
class NoWheelComboBox(QComboBox):
|
||||||
|
|
||||||
|
def wheelEvent (self, event):
|
||||||
|
# Disable the mouse wheel on top of the combo box changing selection as plays havoc in a grid
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
class SourceFieldComboBox(NoWheelComboBox):
|
||||||
|
|
||||||
|
def __init__(self, parent, format_names, selected_text):
|
||||||
|
NoWheelComboBox.__init__(self, parent)
|
||||||
|
self.populate_combo(format_names, selected_text)
|
||||||
|
|
||||||
|
def populate_combo(self, format_names, selected_text):
|
||||||
|
self.addItems([''])
|
||||||
|
self.addItems(format_names)
|
||||||
|
if selected_text:
|
||||||
|
idx = self.findText(selected_text)
|
||||||
|
self.setCurrentIndex(idx)
|
||||||
|
else:
|
||||||
|
self.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>650</width>
|
<width>650</width>
|
||||||
<height>596</height>
|
<height>603</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -203,6 +203,9 @@ e.g., [Project Gutenberg]</p></string>
|
|||||||
<string>Excluded books</string>
|
<string>Excluded books</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||||
|
</property>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
@ -323,97 +326,67 @@ Default: ~,Catalog</string>
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="readBooks">
|
<widget class="QGroupBox" name="prefixRules">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Matching books will be displayed with a check mark</string>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Read books</string>
|
<string>Prefix rules</string>
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<item row="0" column="0" colspan="2">
|
|
||||||
<layout class="QHBoxLayout" name="read_spec_hl">
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetDefaultConstraint</enum>
|
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<property name="minimumSize">
|
<item>
|
||||||
<size>
|
<widget class="QTableWidget" name="prefix_rules_tw">
|
||||||
<width>175</width>
|
<property name="sizePolicy">
|
||||||
<height>0</height>
|
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||||
</size>
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>200</width>
|
<width>16777215</width>
|
||||||
<height>16777215</height>
|
<height>118</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="move_rule_up_tb">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Column/value</string>
|
<string>...</string>
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>read_source_field</cstring>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="read_source_field">
|
<widget class="QToolButton" name="add_rule_tb">
|
||||||
<property name="minimumSize">
|
<property name="text">
|
||||||
<size>
|
<string>...</string>
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Column containing 'read' status</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="sizeAdjustPolicy">
|
|
||||||
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
|
||||||
</property>
|
|
||||||
<property name="minimumContentsLength">
|
|
||||||
<number>18</number>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="read_pattern">
|
<widget class="QToolButton" name="delete_rule_tb">
|
||||||
<property name="minimumSize">
|
<property name="text">
|
||||||
<size>
|
<string>...</string>
|
||||||
<width>150</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>'read book' pattern</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="move_rule_down_tb">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -440,49 +413,7 @@ Default: ~,Catalog</string>
|
|||||||
<property name="fieldGrowthPolicy">
|
<property name="fieldGrowthPolicy">
|
||||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||||
</property>
|
</property>
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>175</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>200</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Wishlist tag</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>wishlist_tag</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="wishlist_tag">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Books tagged as Wishlist items will be displayed with an X</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
@ -542,7 +473,7 @@ Default: ~,Catalog</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="1" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
@ -599,7 +530,7 @@ Default: ~,Catalog</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="2" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_9">
|
<widget class="QLabel" name="label_9">
|
||||||
|
@ -51,7 +51,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
default=':',
|
default=':',
|
||||||
dest='exclude_book_marker',
|
dest='exclude_book_marker',
|
||||||
action = None,
|
action = None,
|
||||||
help=_("field:pattern specifying custom field/contents indicating book should be excluded.\n"
|
help=_("#<custom field>: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"
|
"Default: '%default'\n"
|
||||||
"Applies to ePub, MOBI output formats")),
|
"Applies to ePub, MOBI output formats")),
|
||||||
Option('--exclude-genre',
|
Option('--exclude-genre',
|
||||||
@ -121,7 +122,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
default='::',
|
default='::',
|
||||||
dest='merge_comments',
|
dest='merge_comments',
|
||||||
action = None,
|
action = None,
|
||||||
help=_("<custom field>:[before|after]:[True|False] specifying:\n"
|
help=_("#<custom field>:[before|after]:[True|False] specifying:\n"
|
||||||
" <custom field> Custom field containing notes to merge with Comments\n"
|
" <custom field> Custom field containing notes to merge with Comments\n"
|
||||||
" [before|after] Placement of notes with respect to Comments\n"
|
" [before|after] Placement of notes with respect to Comments\n"
|
||||||
" [True|False] - A horizontal rule is inserted between notes and Comments\n"
|
" [True|False] - A horizontal rule is inserted between notes and Comments\n"
|
||||||
@ -134,11 +135,14 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
help=_("Specifies the output profile. In some cases, an output profile is required to optimize the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured Table of Contents with Sections and Articles.\n"
|
help=_("Specifies the output profile. In some cases, an output profile is required to optimize the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured Table of Contents with Sections and Articles.\n"
|
||||||
"Default: '%default'\n"
|
"Default: '%default'\n"
|
||||||
"Applies to: ePub, MOBI output formats")),
|
"Applies to: ePub, MOBI output formats")),
|
||||||
Option('--read-book-marker',
|
Option('--prefix-rules',
|
||||||
default='tag:+',
|
default="(('Read books','tags','+','\u2713'),('Wishlist items','tags','Wishlist','\u00d7'))",
|
||||||
dest='read_book_marker',
|
dest='prefix_rules',
|
||||||
action = None,
|
action=None,
|
||||||
help=_("field:pattern indicating book has been read.\n" "Default: '%default'\n"
|
help=_("Specifies the rules used to include prefixes indicating read books, wishlist items and other user-specifed prefixes.\n"
|
||||||
|
"The model for a prefix rule is ('<rule name>','<source field>','<pattern>','<prefix>').\n"
|
||||||
|
"When multiple rules are defined, the first matching rule will be used.\n"
|
||||||
|
"Default: '%default'\n"
|
||||||
"Applies to ePub, MOBI output formats")),
|
"Applies to ePub, MOBI output formats")),
|
||||||
Option('--thumb-width',
|
Option('--thumb-width',
|
||||||
default='1.0',
|
default='1.0',
|
||||||
@ -148,12 +152,6 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
"Range: 1.0 - 2.0\n"
|
"Range: 1.0 - 2.0\n"
|
||||||
"Default: '%default'\n"
|
"Default: '%default'\n"
|
||||||
"Applies to ePub, MOBI output formats")),
|
"Applies to ePub, MOBI output formats")),
|
||||||
Option('--wishlist-tag',
|
|
||||||
default='Wishlist',
|
|
||||||
dest='wishlist_tag',
|
|
||||||
action = None,
|
|
||||||
help=_("Tag indicating book to be displayed as wishlist item.\n" "Default: '%default'\n"
|
|
||||||
"Applies to: ePub, MOBI output formats")),
|
|
||||||
]
|
]
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -276,6 +274,15 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width,self.THUMB_SMALLEST))
|
log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width,self.THUMB_SMALLEST))
|
||||||
opts.thumb_width = "1.0"
|
opts.thumb_width = "1.0"
|
||||||
|
|
||||||
|
# Pre-process prefix_rules
|
||||||
|
try:
|
||||||
|
opts.prefix_rules = eval(opts.prefix_rules)
|
||||||
|
except:
|
||||||
|
log.error("malformed --prefix-rules: %s" % opts.prefix_rules)
|
||||||
|
raise
|
||||||
|
for rule in opts.prefix_rules:
|
||||||
|
if len(rule) != 4:
|
||||||
|
log.error("incorrect number of args for --prefix-rules: %s" % repr(rule))
|
||||||
|
|
||||||
# Display opts
|
# Display opts
|
||||||
keys = opts_dict.keys()
|
keys = opts_dict.keys()
|
||||||
@ -285,7 +292,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
if key in ['catalog_title','authorClip','connected_kindle','descriptionClip',
|
if key in ['catalog_title','authorClip','connected_kindle','descriptionClip',
|
||||||
'exclude_book_marker','exclude_genre','exclude_tags',
|
'exclude_book_marker','exclude_genre','exclude_tags',
|
||||||
'header_note_source_field','merge_comments',
|
'header_note_source_field','merge_comments',
|
||||||
'output_profile','read_book_marker',
|
'output_profile','prefix_rules','read_book_marker',
|
||||||
'search_text','sort_by','sort_descriptions_by_author','sync',
|
'search_text','sort_by','sort_descriptions_by_author','sync',
|
||||||
'thumb_width','wishlist_tag']:
|
'thumb_width','wishlist_tag']:
|
||||||
build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
|
build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
|
||||||
|
@ -72,6 +72,7 @@ class CatalogBuilder(object):
|
|||||||
self.__currentStep = 0.0
|
self.__currentStep = 0.0
|
||||||
self.__creator = opts.creator
|
self.__creator = opts.creator
|
||||||
self.__db = db
|
self.__db = db
|
||||||
|
self.__defaultPrefix = None
|
||||||
self.__descriptionClip = opts.descriptionClip
|
self.__descriptionClip = opts.descriptionClip
|
||||||
self.__error = []
|
self.__error = []
|
||||||
self.__generateForKindle = True if (self.opts.fmt == 'mobi' and \
|
self.__generateForKindle = True if (self.opts.fmt == 'mobi' and \
|
||||||
@ -91,10 +92,9 @@ class CatalogBuilder(object):
|
|||||||
self.__output_profile = None
|
self.__output_profile = None
|
||||||
self.__playOrder = 1
|
self.__playOrder = 1
|
||||||
self.__plugin = plugin
|
self.__plugin = plugin
|
||||||
|
self.__prefixRules = []
|
||||||
self.__progressInt = 0.0
|
self.__progressInt = 0.0
|
||||||
self.__progressString = ''
|
self.__progressString = ''
|
||||||
f, _, p = opts.read_book_marker.partition(':')
|
|
||||||
self.__read_book_marker = {'field':f, 'pattern':p}
|
|
||||||
f, p, hr = self.opts.merge_comments.split(':')
|
f, p, hr = self.opts.merge_comments.split(':')
|
||||||
self.__merge_comments = {'field':f, 'position':p, 'hr':hr}
|
self.__merge_comments = {'field':f, 'position':p, 'hr':hr}
|
||||||
self.__reporter = report_progress
|
self.__reporter = report_progress
|
||||||
@ -113,6 +113,9 @@ class CatalogBuilder(object):
|
|||||||
self.__output_profile = profile
|
self.__output_profile = profile
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Process prefix rules
|
||||||
|
self.processPrefixRules()
|
||||||
|
|
||||||
# Confirm/create thumbs archive.
|
# Confirm/create thumbs archive.
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
if not os.path.exists(self.__cache_dir):
|
if not os.path.exists(self.__cache_dir):
|
||||||
@ -269,6 +272,13 @@ class CatalogBuilder(object):
|
|||||||
return self.__db
|
return self.__db
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def defaultPrefix(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__defaultPrefix
|
||||||
|
def fset(self, val):
|
||||||
|
self.__defaultPrefix = val
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
|
@dynamic_property
|
||||||
def descriptionClip(self):
|
def descriptionClip(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return self.__descriptionClip
|
return self.__descriptionClip
|
||||||
@ -363,6 +373,13 @@ class CatalogBuilder(object):
|
|||||||
return self.__plugin
|
return self.__plugin
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def prefixRules(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__prefixRules
|
||||||
|
def fset(self, val):
|
||||||
|
self.__prefixRules = val
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
|
@dynamic_property
|
||||||
def progressInt(self):
|
def progressInt(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return self.__progressInt
|
return self.__progressInt
|
||||||
@ -437,27 +454,12 @@ class CatalogBuilder(object):
|
|||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def MISSING_SYMBOL(self):
|
|
||||||
def fget(self):
|
|
||||||
return self.__output_profile.missing_char
|
|
||||||
return property(fget=fget)
|
|
||||||
@dynamic_property
|
|
||||||
def NOT_READ_SYMBOL(self):
|
|
||||||
def fget(self):
|
|
||||||
return '<span style="color:white">%s</span>' % self.__output_profile.read_char
|
|
||||||
return property(fget=fget)
|
|
||||||
@dynamic_property
|
|
||||||
def READING_SYMBOL(self):
|
def READING_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return '<span style="color:black">▷</span>' if self.generateForKindle else \
|
return '<span style="color:black">▷</span>' if self.generateForKindle else \
|
||||||
'<span style="color:white">+</span>'
|
'<span style="color:white">+</span>'
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def READ_SYMBOL(self):
|
|
||||||
def fget(self):
|
|
||||||
return self.__output_profile.read_char
|
|
||||||
return property(fget=fget)
|
|
||||||
@dynamic_property
|
|
||||||
def FULL_RATING_SYMBOL(self):
|
def FULL_RATING_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return self.__output_profile.ratings_char
|
return self.__output_profile.ratings_char
|
||||||
@ -750,7 +752,7 @@ Author '{0}':
|
|||||||
if record['cover']:
|
if record['cover']:
|
||||||
this_title['cover'] = re.sub('&', '&', record['cover'])
|
this_title['cover'] = re.sub('&', '&', record['cover'])
|
||||||
|
|
||||||
this_title['read'] = self.discoverReadStatus(record)
|
this_title['prefix'] = self.discoverPrefix(record)
|
||||||
|
|
||||||
if record['tags']:
|
if record['tags']:
|
||||||
this_title['tags'] = self.processSpecialTags(record['tags'],
|
this_title['tags'] = self.processSpecialTags(record['tags'],
|
||||||
@ -991,29 +993,17 @@ Author '{0}':
|
|||||||
|
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
pBookTag.insert(ptc, self.formatPrefix(book['prefix'],soup))
|
||||||
if self.opts.wishlist_tag in book.get('tags', []):
|
|
||||||
pBookTag['class'] = "wishlist_item"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if book['read']:
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif book['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
spanTag['class'] = "entry"
|
||||||
|
stc = 0
|
||||||
|
|
||||||
|
|
||||||
# Link to book
|
# Link to book
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
@ -1026,12 +1016,12 @@ Author '{0}':
|
|||||||
else:
|
else:
|
||||||
formatted_title = self.by_titles_normal_title_template.format(**args).rstrip()
|
formatted_title = self.by_titles_normal_title_template.format(**args).rstrip()
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(stc, aTag)
|
||||||
ptc += 1
|
stc += 1
|
||||||
|
|
||||||
# Dot
|
# Dot
|
||||||
pBookTag.insert(ptc, NavigableString(" · "))
|
spanTag.insert(stc, NavigableString(" · "))
|
||||||
ptc += 1
|
stc += 1
|
||||||
|
|
||||||
# Link to author
|
# Link to author
|
||||||
emTag = Tag(soup, "em")
|
emTag = Tag(soup, "em")
|
||||||
@ -1040,7 +1030,10 @@ Author '{0}':
|
|||||||
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author']))
|
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author']))
|
||||||
aTag.insert(0, NavigableString(book['author']))
|
aTag.insert(0, NavigableString(book['author']))
|
||||||
emTag.insert(0,aTag)
|
emTag.insert(0,aTag)
|
||||||
pBookTag.insert(ptc, emTag)
|
spanTag.insert(stc, emTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
if divRunningTag is not None:
|
if divRunningTag is not None:
|
||||||
@ -1172,10 +1165,8 @@ Author '{0}':
|
|||||||
aTag['href'] = "%s.html#%s_series" % ('BySeries',
|
aTag['href'] = "%s.html#%s_series" % ('BySeries',
|
||||||
re.sub('\W','',book['series']).lower())
|
re.sub('\W','',book['series']).lower())
|
||||||
aTag.insert(0, book['series'])
|
aTag.insert(0, book['series'])
|
||||||
#pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
pSeriesTag.insert(0, aTag)
|
pSeriesTag.insert(0, aTag)
|
||||||
else:
|
else:
|
||||||
#pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
|
|
||||||
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
|
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
|
||||||
|
|
||||||
if author_count == 1:
|
if author_count == 1:
|
||||||
@ -1189,29 +1180,16 @@ Author '{0}':
|
|||||||
|
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
pBookTag.insert(ptc, self.formatPrefix(book['prefix'],soup))
|
||||||
if self.opts.wishlist_tag in book.get('tags', []):
|
|
||||||
pBookTag['class'] = "wishlist_item"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if book['read']:
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif book['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
spanTag['class'] = "entry"
|
||||||
|
stc = 0
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
|
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
|
||||||
@ -1227,7 +1205,9 @@ Author '{0}':
|
|||||||
non_series_books += 1
|
non_series_books += 1
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
|
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(ptc, aTag)
|
||||||
|
stc += 1
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
if author_count == 1:
|
if author_count == 1:
|
||||||
@ -1337,29 +1317,16 @@ Author '{0}':
|
|||||||
|
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
pBookTag.insert(ptc, self.formatPrefix(book['prefix'],soup))
|
||||||
if self.opts.wishlist_tag in new_entry.get('tags', []):
|
|
||||||
pBookTag['class'] = "wishlist_item"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if new_entry['read']:
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif new_entry['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
spanTag['class'] = "entry"
|
||||||
|
stc = 0
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
|
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
|
||||||
@ -1372,7 +1339,10 @@ Author '{0}':
|
|||||||
formatted_title = self.by_month_added_normal_title_template.format(**args).rstrip()
|
formatted_title = self.by_month_added_normal_title_template.format(**args).rstrip()
|
||||||
non_series_books += 1
|
non_series_books += 1
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(stc, aTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
divTag.insert(dtc, pBookTag)
|
divTag.insert(dtc, pBookTag)
|
||||||
@ -1393,29 +1363,16 @@ Author '{0}':
|
|||||||
for new_entry in date_range_list:
|
for new_entry in date_range_list:
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
pBookTag.insert(ptc, self.formatPrefix(new_entry['prefix'],soup))
|
||||||
if self.opts.wishlist_tag in new_entry.get('tags', []):
|
|
||||||
pBookTag['class'] = "wishlist_item"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if new_entry['read']:
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif new_entry['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
spanTag['class'] = "entry"
|
||||||
|
stc = 0
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
|
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
|
||||||
@ -1427,12 +1384,12 @@ Author '{0}':
|
|||||||
else:
|
else:
|
||||||
formatted_title = self.by_recently_added_normal_title_template.format(**args).rstrip()
|
formatted_title = self.by_recently_added_normal_title_template.format(**args).rstrip()
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(stc, aTag)
|
||||||
ptc += 1
|
stc += 1
|
||||||
|
|
||||||
# Dot
|
# Dot
|
||||||
pBookTag.insert(ptc, NavigableString(" · "))
|
spanTag.insert(stc, NavigableString(" · "))
|
||||||
ptc += 1
|
stc += 1
|
||||||
|
|
||||||
# Link to author
|
# Link to author
|
||||||
emTag = Tag(soup, "em")
|
emTag = Tag(soup, "em")
|
||||||
@ -1441,7 +1398,10 @@ Author '{0}':
|
|||||||
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(new_entry['author']))
|
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(new_entry['author']))
|
||||||
aTag.insert(0, NavigableString(new_entry['author']))
|
aTag.insert(0, NavigableString(new_entry['author']))
|
||||||
emTag.insert(0,aTag)
|
emTag.insert(0,aTag)
|
||||||
pBookTag.insert(ptc, emTag)
|
spanTag.insert(stc, emTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
divTag.insert(dtc, pBookTag)
|
divTag.insert(dtc, pBookTag)
|
||||||
@ -1799,30 +1759,16 @@ Author '{0}':
|
|||||||
|
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
book['read'] = self.discoverReadStatus(book)
|
book['prefix'] = self.discoverPrefix(book)
|
||||||
|
pBookTag.insert(ptc, self.formatPrefix(book['prefix'],soup))
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
spanTag = Tag(soup, "span")
|
||||||
if self.opts.wishlist_tag in book.get('tags', []):
|
spanTag['class'] = "entry"
|
||||||
pBookTag['class'] = "wishlist_item"
|
stc = 0
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if book.get('read', False):
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif book['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
@ -1838,12 +1784,13 @@ Author '{0}':
|
|||||||
args = self.generateFormatArgs(book)
|
args = self.generateFormatArgs(book)
|
||||||
formatted_title = self.by_series_title_template.format(**args).rstrip()
|
formatted_title = self.by_series_title_template.format(**args).rstrip()
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
pBookTag.insert(ptc, aTag)
|
|
||||||
ptc += 1
|
spanTag.insert(stc, aTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
# ·
|
# ·
|
||||||
pBookTag.insert(ptc, NavigableString(' · '))
|
spanTag.insert(stc, NavigableString(' · '))
|
||||||
ptc += 1
|
stc += 1
|
||||||
|
|
||||||
# Link to author
|
# Link to author
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -1851,7 +1798,10 @@ Author '{0}':
|
|||||||
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor",
|
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor",
|
||||||
self.generateAuthorAnchor(escape(' & '.join(book['authors']))))
|
self.generateAuthorAnchor(escape(' & '.join(book['authors']))))
|
||||||
aTag.insert(0, NavigableString(' & '.join(book['authors'])))
|
aTag.insert(0, NavigableString(' & '.join(book['authors'])))
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(stc, aTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
divTag.insert(dtc, pBookTag)
|
divTag.insert(dtc, pBookTag)
|
||||||
@ -1905,7 +1855,7 @@ Author '{0}':
|
|||||||
this_book['author'] = book['author']
|
this_book['author'] = book['author']
|
||||||
this_book['title'] = book['title']
|
this_book['title'] = book['title']
|
||||||
this_book['author_sort'] = capitalize(book['author_sort'])
|
this_book['author_sort'] = capitalize(book['author_sort'])
|
||||||
this_book['read'] = book['read']
|
this_book['prefix'] = book['prefix']
|
||||||
this_book['tags'] = book['tags']
|
this_book['tags'] = book['tags']
|
||||||
this_book['id'] = book['id']
|
this_book['id'] = book['id']
|
||||||
this_book['series'] = book['series']
|
this_book['series'] = book['series']
|
||||||
@ -3165,7 +3115,7 @@ Author '{0}':
|
|||||||
if not os.path.isdir(images_path):
|
if not os.path.isdir(images_path):
|
||||||
os.makedirs(images_path)
|
os.makedirs(images_path)
|
||||||
|
|
||||||
def discoverReadStatus(self, record):
|
def discoverPrefix(self, record):
|
||||||
'''
|
'''
|
||||||
Given a field:pattern spec, discover if this book marked as read
|
Given a field:pattern spec, discover if this book marked as read
|
||||||
|
|
||||||
@ -3177,25 +3127,42 @@ Author '{0}':
|
|||||||
datatype datetime: #field_name:.*
|
datatype datetime: #field_name:.*
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# Legacy handling of special 'read' tag
|
if self.opts.verbose:
|
||||||
field = self.__read_book_marker['field']
|
self.opts.log.info("\tevaluating %s (%s) for prefix matches" % (record['title'], record['authors']))
|
||||||
pat = self.__read_book_marker['pattern']
|
# Compare the record to each rule looking for a match
|
||||||
if field == 'tag' and pat in record['tags']:
|
for rule in self.prefixRules:
|
||||||
return True
|
if False and self.opts.verbose:
|
||||||
|
self.opts.log.info("\t evaluating prefix_rule '%s'" % rule['name'])
|
||||||
|
|
||||||
|
# Literal comparison for Tags field
|
||||||
|
if rule['field'].lower() == 'tags':
|
||||||
|
if rule['pattern'].lower() in map(unicode.lower,record['tags']):
|
||||||
|
if self.opts.verbose:
|
||||||
|
self.opts.log.info("\t '%s' found in '%s' (%s)" %
|
||||||
|
(rule['pattern'], rule['field'], rule['name']))
|
||||||
|
return rule['prefix']
|
||||||
|
|
||||||
|
# Regex comparison for custom field
|
||||||
|
elif rule['field'].startswith('#'):
|
||||||
field_contents = self.__db.get_field(record['id'],
|
field_contents = self.__db.get_field(record['id'],
|
||||||
field,
|
rule['field'],
|
||||||
index_is_id=True)
|
index_is_id=True)
|
||||||
if field_contents:
|
if field_contents:
|
||||||
try:
|
try:
|
||||||
if re.search(pat, unicode(field_contents),
|
if re.search(rule['pattern'], unicode(field_contents),
|
||||||
re.IGNORECASE) is not None:
|
re.IGNORECASE) is not None:
|
||||||
return True
|
if self.opts.verbose:
|
||||||
|
self.opts.log.info("\t '%s' found in '%s' (%s)" %
|
||||||
|
(rule['pattern'], rule['field'], rule['name']))
|
||||||
|
return rule['prefix']
|
||||||
except:
|
except:
|
||||||
# Compiling of pat failed, ignore it
|
# Compiling of pat failed, ignore it
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
if False and self.opts.verbose:
|
||||||
|
self.opts.log.info("\t No prefix match found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def filterDbTags(self, tags):
|
def filterDbTags(self, tags):
|
||||||
# Remove the special marker tags from the database's tag list,
|
# Remove the special marker tags from the database's tag list,
|
||||||
@ -3227,9 +3194,13 @@ Author '{0}':
|
|||||||
if tag in self.markerTags:
|
if tag in self.markerTags:
|
||||||
excluded_tags.append(tag)
|
excluded_tags.append(tag)
|
||||||
continue
|
continue
|
||||||
|
try:
|
||||||
if re.search(self.opts.exclude_genre, tag):
|
if re.search(self.opts.exclude_genre, tag):
|
||||||
excluded_tags.append(tag)
|
excluded_tags.append(tag)
|
||||||
continue
|
continue
|
||||||
|
except:
|
||||||
|
self.opts.log.error("\tfilterDbTags(): malformed --exclude-genre regex pattern: %s" % self.opts.exclude_genre)
|
||||||
|
|
||||||
if tag == ' ':
|
if tag == ' ':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -3266,6 +3237,20 @@ Author '{0}':
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def formatPrefix(self,prefix_char,soup):
|
||||||
|
# Generate the HTML for the prefix portion of the listing
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
if prefix_char is None:
|
||||||
|
spanTag['style'] = "color:white"
|
||||||
|
spanTag.insert(0,NavigableString(self.defaultPrefix))
|
||||||
|
# 2e3a is 'two-em dash', which matches width in Kindle Previewer
|
||||||
|
# too wide in calibre viewer
|
||||||
|
# minimal visual distraction
|
||||||
|
# spanTag.insert(0,NavigableString(u'\u2e3a'))
|
||||||
|
else:
|
||||||
|
spanTag.insert(0,NavigableString(prefix_char))
|
||||||
|
return spanTag
|
||||||
|
|
||||||
def generateAuthorAnchor(self, author):
|
def generateAuthorAnchor(self, author):
|
||||||
# Strip white space to ''
|
# Strip white space to ''
|
||||||
return re.sub("\W","", author)
|
return re.sub("\W","", author)
|
||||||
@ -3359,29 +3344,16 @@ Author '{0}':
|
|||||||
|
|
||||||
# Add books
|
# Add books
|
||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
|
pBookTag['class'] = "line_item"
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read|reading|unread symbol or wishlist item
|
pBookTag.insert(ptc, self.formatPrefix(book['prefix'],soup))
|
||||||
if self.opts.wishlist_tag in book.get('tags', []):
|
|
||||||
pBookTag['class'] = "wishlist_item"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
if book['read']:
|
|
||||||
# check mark
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
elif book['id'] in self.bookmarked_books:
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
|
||||||
pBookTag['class'] = "read_book"
|
|
||||||
ptc += 1
|
|
||||||
else:
|
|
||||||
# hidden check mark
|
|
||||||
pBookTag['class'] = "unread_book"
|
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
|
spanTag = Tag(soup, "span")
|
||||||
|
spanTag['class'] = "entry"
|
||||||
|
stc = 0
|
||||||
|
|
||||||
# Add the book title
|
# Add the book title
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
@ -3398,7 +3370,10 @@ Author '{0}':
|
|||||||
non_series_books += 1
|
non_series_books += 1
|
||||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||||
|
|
||||||
pBookTag.insert(ptc, aTag)
|
spanTag.insert(stc, aTag)
|
||||||
|
stc += 1
|
||||||
|
|
||||||
|
pBookTag.insert(ptc, spanTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
divTag.insert(dtc, pBookTag)
|
divTag.insert(dtc, pBookTag)
|
||||||
@ -3463,11 +3438,9 @@ Author '{0}':
|
|||||||
|
|
||||||
# Author, author_prefix (read|reading|none symbol or missing symbol)
|
# Author, author_prefix (read|reading|none symbol or missing symbol)
|
||||||
author = book['author']
|
author = book['author']
|
||||||
if self.opts.wishlist_tag in book.get('tags', []):
|
|
||||||
author_prefix = self.MISSING_SYMBOL + " by "
|
if book['prefix']:
|
||||||
else:
|
author_prefix = book['prefix'] + " by "
|
||||||
if book['read']:
|
|
||||||
author_prefix = self.READ_SYMBOL + " by "
|
|
||||||
elif self.opts.connected_kindle and book['id'] in self.bookmarked_books:
|
elif self.opts.connected_kindle and book['id'] in self.bookmarked_books:
|
||||||
author_prefix = self.READING_SYMBOL + " by "
|
author_prefix = self.READING_SYMBOL + " by "
|
||||||
else:
|
else:
|
||||||
@ -4005,6 +3978,22 @@ Author '{0}':
|
|||||||
|
|
||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
def processPrefixRules(self):
|
||||||
|
# Put the prefix rules into an ordered list of dicts
|
||||||
|
try:
|
||||||
|
for rule in self.opts.prefix_rules:
|
||||||
|
prefix_rule = {}
|
||||||
|
prefix_rule['name'] = rule[0]
|
||||||
|
prefix_rule['field'] = rule[1]
|
||||||
|
prefix_rule['pattern'] = rule[2]
|
||||||
|
prefix_rule['prefix'] = rule[3]
|
||||||
|
self.prefixRules.append(prefix_rule)
|
||||||
|
except:
|
||||||
|
self.opts.log.error("malformed self.opts.prefix_rules: %s" % repr(self.opts.prefix_rules))
|
||||||
|
raise
|
||||||
|
# Use the highest order prefix symbol as default
|
||||||
|
self.defaultPrefix = self.opts.prefix_rules[0][3]
|
||||||
|
|
||||||
def processExclusions(self, data_set):
|
def processExclusions(self, data_set):
|
||||||
'''
|
'''
|
||||||
Remove excluded entries
|
Remove excluded entries
|
||||||
@ -4026,17 +4015,20 @@ Author '{0}':
|
|||||||
return filtered_data_set
|
return filtered_data_set
|
||||||
|
|
||||||
def processSpecialTags(self, tags, this_title, opts):
|
def processSpecialTags(self, tags, this_title, opts):
|
||||||
|
|
||||||
tag_list = []
|
tag_list = []
|
||||||
|
|
||||||
|
try:
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
tag = self.convertHTMLEntities(tag)
|
tag = self.convertHTMLEntities(tag)
|
||||||
if re.search(opts.exclude_genre, tag):
|
if re.search(opts.exclude_genre, tag):
|
||||||
continue
|
continue
|
||||||
elif self.__read_book_marker['field'] == 'tag' and \
|
|
||||||
tag == self.__read_book_marker['pattern']:
|
|
||||||
# remove 'read' tag
|
|
||||||
continue
|
|
||||||
else:
|
else:
|
||||||
tag_list.append(tag)
|
tag_list.append(tag)
|
||||||
|
except:
|
||||||
|
self.opts.log.error("\tprocessSpecialTags(): malformed --exclude-genre regex pattern: %s" % opts.exclude_genre)
|
||||||
|
return tags
|
||||||
|
|
||||||
return tag_list
|
return tag_list
|
||||||
|
|
||||||
def updateProgressFullStep(self, description):
|
def updateProgressFullStep(self, description):
|
||||||
|
@ -719,6 +719,7 @@ def catalog_option_parser(args):
|
|||||||
def add_plugin_parser_options(fmt, parser, log):
|
def add_plugin_parser_options(fmt, parser, log):
|
||||||
|
|
||||||
# Fetch the extension-specific CLI options from the plugin
|
# Fetch the extension-specific CLI options from the plugin
|
||||||
|
# library.catalogs.<format>.py
|
||||||
plugin = plugin_for_catalog_format(fmt)
|
plugin = plugin_for_catalog_format(fmt)
|
||||||
for option in plugin.cli_options:
|
for option in plugin.cli_options:
|
||||||
if option.action:
|
if option.action:
|
||||||
@ -796,7 +797,6 @@ def catalog_option_parser(args):
|
|||||||
return parser, plugin, log
|
return parser, plugin, log
|
||||||
|
|
||||||
def command_catalog(args, dbpath):
|
def command_catalog(args, dbpath):
|
||||||
print("library.cli:command_catalog() EXPERIMENTAL MODE")
|
|
||||||
parser, plugin, log = catalog_option_parser(args)
|
parser, plugin, log = catalog_option_parser(args)
|
||||||
opts, args = parser.parse_args(sys.argv[1:])
|
opts, args = parser.parse_args(sys.argv[1:])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user