mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix various bugs in column coloring. Fix the NOT template function. Migrate is_multiple to a dict
This commit is contained in:
commit
06de23b162
@ -204,7 +204,8 @@ class CollectionsBookList(BookList):
|
|||||||
elif fm['datatype'] == 'text' and fm['is_multiple']:
|
elif fm['datatype'] == 'text' and fm['is_multiple']:
|
||||||
val = orig_val
|
val = orig_val
|
||||||
elif fm['datatype'] == 'composite' and fm['is_multiple']:
|
elif fm['datatype'] == 'composite' and fm['is_multiple']:
|
||||||
val = [v.strip() for v in val.split(fm['is_multiple'])]
|
val = [v.strip() for v in
|
||||||
|
val.split(fm['is_multiple']['ui_to_list'])]
|
||||||
else:
|
else:
|
||||||
val = [val]
|
val = [val]
|
||||||
|
|
||||||
|
@ -621,10 +621,7 @@ class Metadata(object):
|
|||||||
orig_res = res
|
orig_res = res
|
||||||
datatype = cmeta['datatype']
|
datatype = cmeta['datatype']
|
||||||
if datatype == 'text' and cmeta['is_multiple']:
|
if datatype == 'text' and cmeta['is_multiple']:
|
||||||
if cmeta['display'].get('is_names', False):
|
res = cmeta['is_multiple']['list_to_ui'].join(res)
|
||||||
res = u' & '.join(res)
|
|
||||||
else:
|
|
||||||
res = u', '.join(sorted(res, key=sort_key))
|
|
||||||
elif datatype == 'series' and series_with_index:
|
elif datatype == 'series' and series_with_index:
|
||||||
if self.get_extra(key) is not None:
|
if self.get_extra(key) is not None:
|
||||||
res = res + \
|
res = res + \
|
||||||
@ -668,7 +665,7 @@ class Metadata(object):
|
|||||||
elif datatype == 'text' and fmeta['is_multiple']:
|
elif datatype == 'text' and fmeta['is_multiple']:
|
||||||
if isinstance(res, dict):
|
if isinstance(res, dict):
|
||||||
res = [k + ':' + v for k,v in res.items()]
|
res = [k + ':' + v for k,v in res.items()]
|
||||||
res = u', '.join(sorted(res, key=sort_key))
|
res = fmeta['is_multiple']['list_to_ui'].join(sorted(res, key=sort_key))
|
||||||
elif datatype == 'series' and series_with_index:
|
elif datatype == 'series' and series_with_index:
|
||||||
res = res + ' [%s]'%self.format_series_index()
|
res = res + ' [%s]'%self.format_series_index()
|
||||||
elif datatype == 'datetime':
|
elif datatype == 'datetime':
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
lxml based OPF parser.
|
lxml based OPF parser.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import re, sys, unittest, functools, os, uuid, glob, cStringIO, json
|
import re, sys, unittest, functools, os, uuid, glob, cStringIO, json, copy
|
||||||
from urllib import unquote
|
from urllib import unquote
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
@ -457,6 +457,14 @@ def serialize_user_metadata(metadata_elem, all_user_metadata, tail='\n'+(' '*8))
|
|||||||
|
|
||||||
for name, fm in all_user_metadata.items():
|
for name, fm in all_user_metadata.items():
|
||||||
try:
|
try:
|
||||||
|
if fm.get('is_multiple'):
|
||||||
|
# migrate is_multiple back to a character
|
||||||
|
fm = copy.copy(fm)
|
||||||
|
dt = fm.get('datatype', None)
|
||||||
|
if dt == 'composite':
|
||||||
|
fm['is_multiple'] = ','
|
||||||
|
else:
|
||||||
|
fm['is_multiple'] = '|'
|
||||||
fm = object_to_unicode(fm)
|
fm = object_to_unicode(fm)
|
||||||
fm = json.dumps(fm, default=to_json, ensure_ascii=False)
|
fm = json.dumps(fm, default=to_json, ensure_ascii=False)
|
||||||
except:
|
except:
|
||||||
@ -585,6 +593,17 @@ class OPF(object): # {{{
|
|||||||
fm = elem.get('content')
|
fm = elem.get('content')
|
||||||
try:
|
try:
|
||||||
fm = json.loads(fm, object_hook=from_json)
|
fm = json.loads(fm, object_hook=from_json)
|
||||||
|
im = fm.get('is_multiple', None)
|
||||||
|
if im and not isinstance(im, dict):
|
||||||
|
# Must migrate the is_multiple from char to dict
|
||||||
|
dt = fm.get('datatype', None)
|
||||||
|
if dt == 'composite':
|
||||||
|
im = {'cache_to_list': ',', 'ui_to_list': ',', 'list_to_ui': ', '}
|
||||||
|
elif fm.get('display', {}).get('is_names', False):
|
||||||
|
im = {'cache_to_list': '|', 'ui_to_list': '&', 'list_to_ui': ', '}
|
||||||
|
else:
|
||||||
|
im = {'cache_to_list': '|', 'ui_to_list': ',', 'list_to_ui': ', '}
|
||||||
|
fm['is_multiple'] = im
|
||||||
temp.set_user_metadata(name, fm)
|
temp.set_user_metadata(name, fm)
|
||||||
except:
|
except:
|
||||||
prints('Failed to read user metadata:', name)
|
prints('Failed to read user metadata:', name)
|
||||||
|
@ -226,16 +226,14 @@ class Comments(Base):
|
|||||||
class Text(Base):
|
class Text(Base):
|
||||||
|
|
||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
if self.col_metadata['display'].get('is_names', False):
|
self.sep = self.col_metadata['multiple_seps']
|
||||||
self.sep = u' & '
|
|
||||||
else:
|
|
||||||
self.sep = u', '
|
|
||||||
values = self.all_values = list(self.db.all_custom(num=self.col_id))
|
values = self.all_values = list(self.db.all_custom(num=self.col_id))
|
||||||
values.sort(key=sort_key)
|
values.sort(key=sort_key)
|
||||||
|
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
w = MultiCompleteLineEdit(parent)
|
w = MultiCompleteLineEdit(parent)
|
||||||
w.set_separator(self.sep.strip())
|
w.set_separator(self.sep['ui_to_list'])
|
||||||
if self.sep == u' & ':
|
if self.sep['ui_to_list'] == '&':
|
||||||
w.set_space_before_sep(True)
|
w.set_space_before_sep(True)
|
||||||
w.set_add_separator(tweaks['authors_completer_append_separator'])
|
w.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
w.update_items_cache(values)
|
w.update_items_cache(values)
|
||||||
@ -269,12 +267,12 @@ class Text(Base):
|
|||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
if not val:
|
if not val:
|
||||||
val = []
|
val = []
|
||||||
self.widgets[1].setText(self.sep.join(val))
|
self.widgets[1].setText(self.sep['list_to_ui'].join(val))
|
||||||
|
|
||||||
def getter(self):
|
def getter(self):
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
val = unicode(self.widgets[1].text()).strip()
|
val = unicode(self.widgets[1].text()).strip()
|
||||||
ans = [x.strip() for x in val.split(self.sep.strip()) if x.strip()]
|
ans = [x.strip() for x in val.split(self.sep['ui_to_list']) if x.strip()]
|
||||||
if not ans:
|
if not ans:
|
||||||
ans = None
|
ans = None
|
||||||
return ans
|
return ans
|
||||||
@ -899,9 +897,10 @@ class BulkText(BulkBase):
|
|||||||
if not self.a_c_checkbox.isChecked():
|
if not self.a_c_checkbox.isChecked():
|
||||||
return
|
return
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
|
ism = self.col_metadata['multiple_seps']
|
||||||
if self.col_metadata['display'].get('is_names', False):
|
if self.col_metadata['display'].get('is_names', False):
|
||||||
val = self.gui_val
|
val = self.gui_val
|
||||||
add = [v.strip() for v in val.split('&') if v.strip()]
|
add = [v.strip() for v in val.split(ism['ui_to_list']) if v.strip()]
|
||||||
self.db.set_custom_bulk(book_ids, add, num=self.col_id)
|
self.db.set_custom_bulk(book_ids, add, num=self.col_id)
|
||||||
else:
|
else:
|
||||||
remove_all, adding, rtext = self.gui_val
|
remove_all, adding, rtext = self.gui_val
|
||||||
@ -911,10 +910,10 @@ class BulkText(BulkBase):
|
|||||||
else:
|
else:
|
||||||
txt = rtext
|
txt = rtext
|
||||||
if txt:
|
if txt:
|
||||||
remove = set([v.strip() for v in txt.split(',')])
|
remove = set([v.strip() for v in txt.split(ism['ui_to_list'])])
|
||||||
txt = adding
|
txt = adding
|
||||||
if txt:
|
if txt:
|
||||||
add = set([v.strip() for v in txt.split(',')])
|
add = set([v.strip() for v in txt.split(ism['ui_to_list'])])
|
||||||
else:
|
else:
|
||||||
add = set()
|
add = set()
|
||||||
self.db.set_custom_bulk_multiple(book_ids, add=add,
|
self.db.set_custom_bulk_multiple(book_ids, add=add,
|
||||||
|
@ -520,7 +520,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
elif not fm['is_multiple']:
|
elif not fm['is_multiple']:
|
||||||
val = [val]
|
val = [val]
|
||||||
elif fm['datatype'] == 'composite':
|
elif fm['datatype'] == 'composite':
|
||||||
val = [v.strip() for v in val.split(fm['is_multiple'])]
|
val = [v.strip() for v in val.split(fm['is_multiple']['ui_to_list'])]
|
||||||
elif field == 'authors':
|
elif field == 'authors':
|
||||||
val = [v.replace('|', ',') for v in val]
|
val = [v.replace('|', ',') for v in val]
|
||||||
else:
|
else:
|
||||||
@ -655,19 +655,10 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
|
|
||||||
if self.destination_field_fm['is_multiple']:
|
if self.destination_field_fm['is_multiple']:
|
||||||
if self.comma_separated.isChecked():
|
if self.comma_separated.isChecked():
|
||||||
if dest == 'authors' or \
|
splitter = self.destination_field_fm['is_multiple']['ui_to_list']
|
||||||
(self.destination_field_fm['is_custom'] and
|
|
||||||
self.destination_field_fm['datatype'] == 'text' and
|
|
||||||
self.destination_field_fm['display'].get('is_names', False)):
|
|
||||||
splitter = ' & '
|
|
||||||
else:
|
|
||||||
splitter = ','
|
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for v in val:
|
for v in val:
|
||||||
for x in v.split(splitter):
|
res.extend([x.strip() for x in v.split(splitter) if x.strip()])
|
||||||
if x.strip():
|
|
||||||
res.append(x.strip())
|
|
||||||
val = res
|
val = res
|
||||||
else:
|
else:
|
||||||
val = [v.replace(',', '') for v in val]
|
val = [v.replace(',', '') for v in val]
|
||||||
|
@ -254,6 +254,15 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
self.textbox_changed()
|
self.textbox_changed()
|
||||||
self.rule = (None, '')
|
self.rule = (None, '')
|
||||||
|
|
||||||
|
tt = _('Template language tutorial')
|
||||||
|
self.template_tutorial.setText(
|
||||||
|
'<a href="http://manual.calibre-ebook.com/template_lang.html">'
|
||||||
|
'%s</a>'%tt)
|
||||||
|
tt = _('Template function reference')
|
||||||
|
self.template_func_reference.setText(
|
||||||
|
'<a href="http://manual.calibre-ebook.com/template_ref.html">'
|
||||||
|
'%s</a>'%tt)
|
||||||
|
|
||||||
def textbox_changed(self):
|
def textbox_changed(self):
|
||||||
cur_text = unicode(self.textbox.toPlainText())
|
cur_text = unicode(self.textbox.toPlainText())
|
||||||
if self.last_text != cur_text:
|
if self.last_text != cur_text:
|
||||||
@ -299,4 +308,4 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.rule = (unicode(self.colored_field.currentText()), txt)
|
self.rule = (unicode(self.colored_field.currentText()), txt)
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
@ -125,6 +125,20 @@
|
|||||||
<item row="9" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QPlainTextEdit" name="source_code"/>
|
<widget class="QPlainTextEdit" name="source_code"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QLabel" name="template_tutorial">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<widget class="QLabel" name="template_func_reference">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -608,10 +608,11 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
def text_type(r, mult=None, idx=-1):
|
def text_type(r, mult=None, idx=-1):
|
||||||
text = self.db.data[r][idx]
|
text = self.db.data[r][idx]
|
||||||
if text and mult is not None:
|
if text and mult:
|
||||||
if mult:
|
jv = mult['list_to_ui']
|
||||||
return QVariant(u' & '.join(text.split('|')))
|
sv = mult['cache_to_list']
|
||||||
return QVariant(u', '.join(sorted(text.split('|'),key=sort_key)))
|
return QVariant(jv.join(
|
||||||
|
sorted([t.strip() for t in text.split(sv)], key=sort_key)))
|
||||||
return QVariant(text)
|
return QVariant(text)
|
||||||
|
|
||||||
def decorated_text_type(r, idx=-1):
|
def decorated_text_type(r, idx=-1):
|
||||||
@ -665,8 +666,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
datatype = self.custom_columns[col]['datatype']
|
datatype = self.custom_columns[col]['datatype']
|
||||||
if datatype in ('text', 'comments', 'composite', 'enumeration'):
|
if datatype in ('text', 'comments', 'composite', 'enumeration'):
|
||||||
mult=self.custom_columns[col]['is_multiple']
|
mult=self.custom_columns[col]['is_multiple']
|
||||||
if mult is not None:
|
|
||||||
mult = self.custom_columns[col]['display'].get('is_names', False)
|
|
||||||
self.dc[col] = functools.partial(text_type, idx=idx, mult=mult)
|
self.dc[col] = functools.partial(text_type, idx=idx, mult=mult)
|
||||||
if datatype in ['text', 'composite', 'enumeration'] and not mult:
|
if datatype in ['text', 'composite', 'enumeration'] and not mult:
|
||||||
if self.custom_columns[col]['display'].get('use_decorations', False):
|
if self.custom_columns[col]['display'].get('use_decorations', False):
|
||||||
@ -722,9 +721,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
if id_ in self.color_cache:
|
if id_ in self.color_cache:
|
||||||
if key in self.color_cache[id_]:
|
if key in self.color_cache[id_]:
|
||||||
return self.color_cache[id_][key]
|
return self.color_cache[id_][key]
|
||||||
if mi is None:
|
|
||||||
mi = self.db.get_metadata(id_, index_is_id=True)
|
|
||||||
try:
|
try:
|
||||||
|
if mi is None:
|
||||||
|
mi = self.db.get_metadata(id_, index_is_id=True)
|
||||||
color = composite_formatter.safe_format(fmt, mi, '', mi)
|
color = composite_formatter.safe_format(fmt, mi, '', mi)
|
||||||
if color in self.colors:
|
if color in self.colors:
|
||||||
color = QColor(color)
|
color = QColor(color)
|
||||||
|
@ -192,6 +192,8 @@ class ConditionEditor(QWidget): # {{{
|
|||||||
action = self.current_action
|
action = self.current_action
|
||||||
if not action:
|
if not action:
|
||||||
return
|
return
|
||||||
|
m = self.fm[col]
|
||||||
|
dt = m['datatype']
|
||||||
tt = ''
|
tt = ''
|
||||||
if col == 'identifiers':
|
if col == 'identifiers':
|
||||||
tt = _('Enter either an identifier type or an '
|
tt = _('Enter either an identifier type or an '
|
||||||
@ -209,7 +211,7 @@ class ConditionEditor(QWidget): # {{{
|
|||||||
tt = _('Enter a regular expression')
|
tt = _('Enter a regular expression')
|
||||||
elif m.get('is_multiple', False):
|
elif m.get('is_multiple', False):
|
||||||
tt += '\n' + _('You can match multiple values by separating'
|
tt += '\n' + _('You can match multiple values by separating'
|
||||||
' them with %s')%m['is_multiple']
|
' them with %s')%m['is_multiple']['ui_to_list']
|
||||||
self.value_box.setToolTip(tt)
|
self.value_box.setToolTip(tt)
|
||||||
if action in ('is set', 'is not set', 'is true', 'is false',
|
if action in ('is set', 'is not set', 'is true', 'is false',
|
||||||
'is undefined'):
|
'is undefined'):
|
||||||
|
@ -13,6 +13,9 @@ from calibre.gui2 import error_dialog
|
|||||||
|
|
||||||
class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||||
|
|
||||||
|
# Note: in this class, we are treating is_multiple as the boolean that
|
||||||
|
# custom_columns expects to find in its structure. It does not use the dict
|
||||||
|
|
||||||
column_types = {
|
column_types = {
|
||||||
0:{'datatype':'text',
|
0:{'datatype':'text',
|
||||||
'text':_('Text, column shown in the tag browser'),
|
'text':_('Text, column shown in the tag browser'),
|
||||||
|
@ -509,7 +509,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
valq_mkind, valq = self._matchkind(query)
|
valq_mkind, valq = self._matchkind(query)
|
||||||
|
|
||||||
loc = self.field_metadata[location]['rec_index']
|
loc = self.field_metadata[location]['rec_index']
|
||||||
split_char = self.field_metadata[location]['is_multiple']
|
split_char = self.field_metadata[location]['is_multiple'].get(
|
||||||
|
'cache_to_list', ',')
|
||||||
for id_ in candidates:
|
for id_ in candidates:
|
||||||
item = self._data[id_]
|
item = self._data[id_]
|
||||||
if item is None:
|
if item is None:
|
||||||
@ -665,7 +666,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
if fm['is_multiple'] and \
|
if fm['is_multiple'] and \
|
||||||
len(query) > 1 and query.startswith('#') and \
|
len(query) > 1 and query.startswith('#') and \
|
||||||
query[1:1] in '=<>!':
|
query[1:1] in '=<>!':
|
||||||
vf = lambda item, loc=fm['rec_index'], ms=fm['is_multiple']:\
|
vf = lambda item, loc=fm['rec_index'], \
|
||||||
|
ms=fm['is_multiple']['cache_to_list']:\
|
||||||
len(item[loc].split(ms)) if item[loc] is not None else 0
|
len(item[loc].split(ms)) if item[loc] is not None else 0
|
||||||
return self.get_numeric_matches(location, query[1:],
|
return self.get_numeric_matches(location, query[1:],
|
||||||
candidates, val_func=vf)
|
candidates, val_func=vf)
|
||||||
@ -703,7 +705,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
['composite', 'text', 'comments', 'series', 'enumeration']:
|
['composite', 'text', 'comments', 'series', 'enumeration']:
|
||||||
exclude_fields.append(db_col[x])
|
exclude_fields.append(db_col[x])
|
||||||
col_datatype[db_col[x]] = self.field_metadata[x]['datatype']
|
col_datatype[db_col[x]] = self.field_metadata[x]['datatype']
|
||||||
is_multiple_cols[db_col[x]] = self.field_metadata[x]['is_multiple']
|
is_multiple_cols[db_col[x]] = \
|
||||||
|
self.field_metadata[x]['is_multiple'].get('cache_to_list', None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rating_query = int(query) * 2
|
rating_query = int(query) * 2
|
||||||
@ -1045,13 +1048,14 @@ class SortKeyGenerator(object):
|
|||||||
|
|
||||||
elif dt in ('text', 'comments', 'composite', 'enumeration'):
|
elif dt in ('text', 'comments', 'composite', 'enumeration'):
|
||||||
if val:
|
if val:
|
||||||
sep = fm['is_multiple']
|
if fm['is_multiple']:
|
||||||
if sep:
|
jv = fm['is_multiple']['list_to_ui']
|
||||||
if fm['display'].get('is_names', False):
|
sv = fm['is_multiple']['cache_to_list']
|
||||||
val = sep.join(
|
if '&' in jv:
|
||||||
[author_to_author_sort(v) for v in val.split(sep)])
|
val = jv.join(
|
||||||
|
[author_to_author_sort(v) for v in val.split(sv)])
|
||||||
else:
|
else:
|
||||||
val = sep.join(sorted(val.split(sep),
|
val = jv.join(sorted(val.split(sv),
|
||||||
key=self.string_sort_key))
|
key=self.string_sort_key))
|
||||||
val = self.string_sort_key(val)
|
val = self.string_sort_key(val)
|
||||||
|
|
||||||
|
@ -79,16 +79,19 @@ class Rule(object): # {{{
|
|||||||
if dt == 'bool':
|
if dt == 'bool':
|
||||||
return self.bool_condition(col, action, val)
|
return self.bool_condition(col, action, val)
|
||||||
|
|
||||||
if dt in ('int', 'float', 'rating'):
|
if dt in ('int', 'float'):
|
||||||
return self.number_condition(col, action, val)
|
return self.number_condition(col, action, val)
|
||||||
|
|
||||||
|
if dt == 'rating':
|
||||||
|
return self.rating_condition(col, action, val)
|
||||||
|
|
||||||
if dt == 'datetime':
|
if dt == 'datetime':
|
||||||
return self.date_condition(col, action, val)
|
return self.date_condition(col, action, val)
|
||||||
|
|
||||||
if dt in ('comments', 'series', 'text', 'enumeration', 'composite'):
|
if dt in ('comments', 'series', 'text', 'enumeration', 'composite'):
|
||||||
ism = m.get('is_multiple', False)
|
ism = m.get('is_multiple', False)
|
||||||
if ism:
|
if ism:
|
||||||
return self.multiple_condition(col, action, val, ',' if ism == '|' else ism)
|
return self.multiple_condition(col, action, val, ism['ui_to_list'])
|
||||||
return self.text_condition(col, action, val)
|
return self.text_condition(col, action, val)
|
||||||
|
|
||||||
def identifiers_condition(self, col, action, val):
|
def identifiers_condition(self, col, action, val):
|
||||||
@ -114,9 +117,16 @@ class Rule(object): # {{{
|
|||||||
'lt': ('1', '', ''),
|
'lt': ('1', '', ''),
|
||||||
'gt': ('', '', '1')
|
'gt': ('', '', '1')
|
||||||
}[action]
|
}[action]
|
||||||
lt, eq, gt = '', '1', ''
|
|
||||||
return "cmp(raw_field('%s'), %s, '%s', '%s', '%s')" % (col, val, lt, eq, gt)
|
return "cmp(raw_field('%s'), %s, '%s', '%s', '%s')" % (col, val, lt, eq, gt)
|
||||||
|
|
||||||
|
def rating_condition(self, col, action, val):
|
||||||
|
lt, eq, gt = {
|
||||||
|
'eq': ('', '1', ''),
|
||||||
|
'lt': ('1', '', ''),
|
||||||
|
'gt': ('', '', '1')
|
||||||
|
}[action]
|
||||||
|
return "cmp(field('%s'), %s, '%s', '%s', '%s')" % (col, val, lt, eq, gt)
|
||||||
|
|
||||||
def date_condition(self, col, action, val):
|
def date_condition(self, col, action, val):
|
||||||
lt, eq, gt = {
|
lt, eq, gt = {
|
||||||
'eq': ('', '1', ''),
|
'eq': ('', '1', ''),
|
||||||
|
@ -78,6 +78,18 @@ class CustomColumns(object):
|
|||||||
}
|
}
|
||||||
if data['display'] is None:
|
if data['display'] is None:
|
||||||
data['display'] = {}
|
data['display'] = {}
|
||||||
|
# set up the is_multiple separator dict
|
||||||
|
if data['is_multiple']:
|
||||||
|
if data['display'].get('is_names', False):
|
||||||
|
seps = {'cache_to_list': '|', 'ui_to_list': '&', 'list_to_ui': ' & '}
|
||||||
|
elif data['datatype'] == 'composite':
|
||||||
|
seps = {'cache_to_list': ',', 'ui_to_list': ',', 'list_to_ui': ', '}
|
||||||
|
else:
|
||||||
|
seps = {'cache_to_list': '|', 'ui_to_list': ',', 'list_to_ui': ', '}
|
||||||
|
else:
|
||||||
|
seps = {}
|
||||||
|
data['multiple_seps'] = seps
|
||||||
|
|
||||||
table, lt = self.custom_table_names(data['num'])
|
table, lt = self.custom_table_names(data['num'])
|
||||||
if table not in custom_tables or (data['normalized'] and lt not in
|
if table not in custom_tables or (data['normalized'] and lt not in
|
||||||
custom_tables):
|
custom_tables):
|
||||||
@ -119,7 +131,7 @@ class CustomColumns(object):
|
|||||||
if x is None:
|
if x is None:
|
||||||
return []
|
return []
|
||||||
if isinstance(x, (str, unicode, bytes)):
|
if isinstance(x, (str, unicode, bytes)):
|
||||||
x = x.split('&' if d['display'].get('is_names', False) else',')
|
x = x.split(d['multiple_seps']['ui_to_list'])
|
||||||
x = [y.strip() for y in x if y.strip()]
|
x = [y.strip() for y in x if y.strip()]
|
||||||
x = [y.decode(preferred_encoding, 'replace') if not isinstance(y,
|
x = [y.decode(preferred_encoding, 'replace') if not isinstance(y,
|
||||||
unicode) else y for y in x]
|
unicode) else y for y in x]
|
||||||
@ -181,10 +193,7 @@ class CustomColumns(object):
|
|||||||
is_category = True
|
is_category = True
|
||||||
else:
|
else:
|
||||||
is_category = False
|
is_category = False
|
||||||
if v['is_multiple']:
|
is_m = v['multiple_seps']
|
||||||
is_m = ',' if v['datatype'] == 'composite' else '|'
|
|
||||||
else:
|
|
||||||
is_m = None
|
|
||||||
tn = 'custom_column_{0}'.format(v['num'])
|
tn = 'custom_column_{0}'.format(v['num'])
|
||||||
self.field_metadata.add_custom_field(label=v['label'],
|
self.field_metadata.add_custom_field(label=v['label'],
|
||||||
table=tn, column='value', datatype=v['datatype'],
|
table=tn, column='value', datatype=v['datatype'],
|
||||||
@ -200,7 +209,7 @@ class CustomColumns(object):
|
|||||||
row = self.data._data[idx] if index_is_id else self.data[idx]
|
row = self.data._data[idx] if index_is_id else self.data[idx]
|
||||||
ans = row[self.FIELD_MAP[data['num']]]
|
ans = row[self.FIELD_MAP[data['num']]]
|
||||||
if data['is_multiple'] and data['datatype'] == 'text':
|
if data['is_multiple'] and data['datatype'] == 'text':
|
||||||
ans = ans.split('|') if ans else []
|
ans = ans.split(data['multiple_seps']['cache_to_list']) if ans else []
|
||||||
if data['display'].get('sort_alpha', False):
|
if data['display'].get('sort_alpha', False):
|
||||||
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
|
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
|
||||||
return ans
|
return ans
|
||||||
@ -571,9 +580,17 @@ class CustomColumns(object):
|
|||||||
if data['normalized']:
|
if data['normalized']:
|
||||||
query = '%s.value'
|
query = '%s.value'
|
||||||
if data['is_multiple']:
|
if data['is_multiple']:
|
||||||
query = 'group_concat(%s.value, "|")'
|
# query = 'group_concat(%s.value, "{0}")'.format(
|
||||||
if not display.get('sort_alpha', False):
|
# data['multiple_seps']['cache_to_list'])
|
||||||
query = 'sort_concat(link.id, %s.value)'
|
# if not display.get('sort_alpha', False):
|
||||||
|
if data['multiple_seps']['cache_to_list'] == '|':
|
||||||
|
query = 'sortconcat_bar(link.id, %s.value)'
|
||||||
|
elif data['multiple_seps']['cache_to_list'] == '&':
|
||||||
|
query = 'sortconcat_amper(link.id, %s.value)'
|
||||||
|
else:
|
||||||
|
prints('WARNING: unknown value in multiple_seps',
|
||||||
|
data['multiple_seps']['cache_to_list'])
|
||||||
|
query = 'sortconcat_bar(link.id, %s.value)'
|
||||||
line = '''(SELECT {query} FROM {lt} AS link INNER JOIN
|
line = '''(SELECT {query} FROM {lt} AS link INNER JOIN
|
||||||
{table} ON(link.value={table}.id) WHERE link.book=books.id)
|
{table} ON(link.value={table}.id) WHERE link.book=books.id)
|
||||||
custom_{num}
|
custom_{num}
|
||||||
|
@ -1250,7 +1250,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
dex = field['rec_index']
|
dex = field['rec_index']
|
||||||
for book in self.data.iterall():
|
for book in self.data.iterall():
|
||||||
if field['is_multiple']:
|
if field['is_multiple']:
|
||||||
vals = [v.strip() for v in book[dex].split(field['is_multiple'])
|
vals = [v.strip() for v in
|
||||||
|
book[dex].split(field['is_multiple']['cache_to_list'])
|
||||||
if v.strip()]
|
if v.strip()]
|
||||||
if id_ in vals:
|
if id_ in vals:
|
||||||
ans.add(book[0])
|
ans.add(book[0])
|
||||||
@ -1378,7 +1379,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
tcategories[category] = {}
|
tcategories[category] = {}
|
||||||
# create a list of category/field_index for the books scan to use.
|
# create a list of category/field_index for the books scan to use.
|
||||||
# This saves iterating through field_metadata for each book
|
# This saves iterating through field_metadata for each book
|
||||||
md.append((category, cat['rec_index'], cat['is_multiple'], False))
|
md.append((category, cat['rec_index'],
|
||||||
|
cat['is_multiple'].get('cache_to_list', None), False))
|
||||||
|
|
||||||
for category in tb_cats.iterkeys():
|
for category in tb_cats.iterkeys():
|
||||||
cat = tb_cats[category]
|
cat = tb_cats[category]
|
||||||
@ -1386,7 +1388,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
cat['display'].get('make_category', False):
|
cat['display'].get('make_category', False):
|
||||||
tids[category] = {}
|
tids[category] = {}
|
||||||
tcategories[category] = {}
|
tcategories[category] = {}
|
||||||
md.append((category, cat['rec_index'], cat['is_multiple'],
|
md.append((category, cat['rec_index'],
|
||||||
|
cat['is_multiple'].get('cache_to_list', None),
|
||||||
cat['datatype'] == 'composite'))
|
cat['datatype'] == 'composite'))
|
||||||
#print 'end phase "collection":', time.clock() - last, 'seconds'
|
#print 'end phase "collection":', time.clock() - last, 'seconds'
|
||||||
#last = time.clock()
|
#last = time.clock()
|
||||||
|
@ -50,9 +50,16 @@ class FieldMetadata(dict):
|
|||||||
|
|
||||||
datatype: the type of information in the field. Valid values are listed in
|
datatype: the type of information in the field. Valid values are listed in
|
||||||
VALID_DATA_TYPES below.
|
VALID_DATA_TYPES below.
|
||||||
is_multiple: valid for the text datatype. If None, the field is to be
|
is_multiple: valid for the text datatype. If {}, the field is to be
|
||||||
treated as a single term. If not None, it contains a string, and the field
|
treated as a single term. If not None, it contains a dict of the form
|
||||||
is assumed to contain a list of terms separated by that string
|
{'cache_to_list': ',',
|
||||||
|
'ui_to_list': ',',
|
||||||
|
'list_to_ui': ', '}
|
||||||
|
where the cache_to_list contains the character used to split the value in
|
||||||
|
the meta2 table, ui_to_list contains the character used to create a list
|
||||||
|
from a value shown in the ui (each resulting value must be strip()ed and
|
||||||
|
empty values removed), and list_to_ui contains the string used in join()
|
||||||
|
to create a displayable string from the list.
|
||||||
|
|
||||||
kind == field: is a db field.
|
kind == field: is a db field.
|
||||||
kind == category: standard tag category that isn't a field. see news.
|
kind == category: standard tag category that isn't a field. see news.
|
||||||
@ -97,7 +104,9 @@ class FieldMetadata(dict):
|
|||||||
'link_column':'author',
|
'link_column':'author',
|
||||||
'category_sort':'sort',
|
'category_sort':'sort',
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':',',
|
'is_multiple':{'cache_to_list': ',',
|
||||||
|
'ui_to_list': '&',
|
||||||
|
'list_to_ui': ' & '},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Authors'),
|
'name':_('Authors'),
|
||||||
'search_terms':['authors', 'author'],
|
'search_terms':['authors', 'author'],
|
||||||
@ -109,7 +118,7 @@ class FieldMetadata(dict):
|
|||||||
'link_column':'series',
|
'link_column':'series',
|
||||||
'category_sort':'(title_sort(name))',
|
'category_sort':'(title_sort(name))',
|
||||||
'datatype':'series',
|
'datatype':'series',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Series'),
|
'name':_('Series'),
|
||||||
'search_terms':['series'],
|
'search_terms':['series'],
|
||||||
@ -119,7 +128,9 @@ class FieldMetadata(dict):
|
|||||||
('formats', {'table':None,
|
('formats', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':',',
|
'is_multiple':{'cache_to_list': ',',
|
||||||
|
'ui_to_list': ',',
|
||||||
|
'list_to_ui': ', '},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Formats'),
|
'name':_('Formats'),
|
||||||
'search_terms':['formats', 'format'],
|
'search_terms':['formats', 'format'],
|
||||||
@ -131,7 +142,7 @@ class FieldMetadata(dict):
|
|||||||
'link_column':'publisher',
|
'link_column':'publisher',
|
||||||
'category_sort':'name',
|
'category_sort':'name',
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Publishers'),
|
'name':_('Publishers'),
|
||||||
'search_terms':['publisher'],
|
'search_terms':['publisher'],
|
||||||
@ -143,7 +154,7 @@ class FieldMetadata(dict):
|
|||||||
'link_column':'rating',
|
'link_column':'rating',
|
||||||
'category_sort':'rating',
|
'category_sort':'rating',
|
||||||
'datatype':'rating',
|
'datatype':'rating',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Ratings'),
|
'name':_('Ratings'),
|
||||||
'search_terms':['rating'],
|
'search_terms':['rating'],
|
||||||
@ -154,7 +165,7 @@ class FieldMetadata(dict):
|
|||||||
'column':'name',
|
'column':'name',
|
||||||
'category_sort':'name',
|
'category_sort':'name',
|
||||||
'datatype':None,
|
'datatype':None,
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'category',
|
'kind':'category',
|
||||||
'name':_('News'),
|
'name':_('News'),
|
||||||
'search_terms':[],
|
'search_terms':[],
|
||||||
@ -166,7 +177,9 @@ class FieldMetadata(dict):
|
|||||||
'link_column': 'tag',
|
'link_column': 'tag',
|
||||||
'category_sort':'name',
|
'category_sort':'name',
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':',',
|
'is_multiple':{'cache_to_list': ',',
|
||||||
|
'ui_to_list': ',',
|
||||||
|
'list_to_ui': ', '},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Tags'),
|
'name':_('Tags'),
|
||||||
'search_terms':['tags', 'tag'],
|
'search_terms':['tags', 'tag'],
|
||||||
@ -176,7 +189,9 @@ class FieldMetadata(dict):
|
|||||||
('identifiers', {'table':None,
|
('identifiers', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':',',
|
'is_multiple':{'cache_to_list': ',',
|
||||||
|
'ui_to_list': ',',
|
||||||
|
'list_to_ui': ', '},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Identifiers'),
|
'name':_('Identifiers'),
|
||||||
'search_terms':['identifiers', 'identifier', 'isbn'],
|
'search_terms':['identifiers', 'identifier', 'isbn'],
|
||||||
@ -186,7 +201,7 @@ class FieldMetadata(dict):
|
|||||||
('author_sort',{'table':None,
|
('author_sort',{'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Author Sort'),
|
'name':_('Author Sort'),
|
||||||
'search_terms':['author_sort'],
|
'search_terms':['author_sort'],
|
||||||
@ -196,7 +211,9 @@ class FieldMetadata(dict):
|
|||||||
('au_map', {'table':None,
|
('au_map', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':',',
|
'is_multiple':{'cache_to_list': ',',
|
||||||
|
'ui_to_list': None,
|
||||||
|
'list_to_ui': None},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name':None,
|
||||||
'search_terms':[],
|
'search_terms':[],
|
||||||
@ -206,7 +223,7 @@ class FieldMetadata(dict):
|
|||||||
('comments', {'table':None,
|
('comments', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Comments'),
|
'name':_('Comments'),
|
||||||
'search_terms':['comments', 'comment'],
|
'search_terms':['comments', 'comment'],
|
||||||
@ -216,7 +233,7 @@ class FieldMetadata(dict):
|
|||||||
('cover', {'table':None,
|
('cover', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'int',
|
'datatype':'int',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name':None,
|
||||||
'search_terms':['cover'],
|
'search_terms':['cover'],
|
||||||
@ -226,7 +243,7 @@ class FieldMetadata(dict):
|
|||||||
('id', {'table':None,
|
('id', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'int',
|
'datatype':'int',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name':None,
|
||||||
'search_terms':[],
|
'search_terms':[],
|
||||||
@ -236,7 +253,7 @@ class FieldMetadata(dict):
|
|||||||
('last_modified', {'table':None,
|
('last_modified', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'datetime',
|
'datatype':'datetime',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Modified'),
|
'name':_('Modified'),
|
||||||
'search_terms':['last_modified'],
|
'search_terms':['last_modified'],
|
||||||
@ -246,7 +263,7 @@ class FieldMetadata(dict):
|
|||||||
('ondevice', {'table':None,
|
('ondevice', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('On Device'),
|
'name':_('On Device'),
|
||||||
'search_terms':['ondevice'],
|
'search_terms':['ondevice'],
|
||||||
@ -256,7 +273,7 @@ class FieldMetadata(dict):
|
|||||||
('path', {'table':None,
|
('path', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Path'),
|
'name':_('Path'),
|
||||||
'search_terms':[],
|
'search_terms':[],
|
||||||
@ -266,7 +283,7 @@ class FieldMetadata(dict):
|
|||||||
('pubdate', {'table':None,
|
('pubdate', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'datetime',
|
'datatype':'datetime',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Published'),
|
'name':_('Published'),
|
||||||
'search_terms':['pubdate'],
|
'search_terms':['pubdate'],
|
||||||
@ -276,7 +293,7 @@ class FieldMetadata(dict):
|
|||||||
('marked', {'table':None,
|
('marked', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name': None,
|
'name': None,
|
||||||
'search_terms':['marked'],
|
'search_terms':['marked'],
|
||||||
@ -286,7 +303,7 @@ class FieldMetadata(dict):
|
|||||||
('series_index',{'table':None,
|
('series_index',{'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'float',
|
'datatype':'float',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name':None,
|
||||||
'search_terms':['series_index'],
|
'search_terms':['series_index'],
|
||||||
@ -296,7 +313,7 @@ class FieldMetadata(dict):
|
|||||||
('sort', {'table':None,
|
('sort', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Title Sort'),
|
'name':_('Title Sort'),
|
||||||
'search_terms':['title_sort'],
|
'search_terms':['title_sort'],
|
||||||
@ -306,7 +323,7 @@ class FieldMetadata(dict):
|
|||||||
('size', {'table':None,
|
('size', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'float',
|
'datatype':'float',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Size'),
|
'name':_('Size'),
|
||||||
'search_terms':['size'],
|
'search_terms':['size'],
|
||||||
@ -316,7 +333,7 @@ class FieldMetadata(dict):
|
|||||||
('timestamp', {'table':None,
|
('timestamp', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'datetime',
|
'datatype':'datetime',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Date'),
|
'name':_('Date'),
|
||||||
'search_terms':['date'],
|
'search_terms':['date'],
|
||||||
@ -326,7 +343,7 @@ class FieldMetadata(dict):
|
|||||||
('title', {'table':None,
|
('title', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':_('Title'),
|
'name':_('Title'),
|
||||||
'search_terms':['title'],
|
'search_terms':['title'],
|
||||||
@ -336,7 +353,7 @@ class FieldMetadata(dict):
|
|||||||
('uuid', {'table':None,
|
('uuid', {'table':None,
|
||||||
'column':None,
|
'column':None,
|
||||||
'datatype':'text',
|
'datatype':'text',
|
||||||
'is_multiple':None,
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name':None,
|
||||||
'search_terms':[],
|
'search_terms':[],
|
||||||
@ -508,7 +525,7 @@ class FieldMetadata(dict):
|
|||||||
if datatype == 'series':
|
if datatype == 'series':
|
||||||
key += '_index'
|
key += '_index'
|
||||||
self._tb_cats[key] = {'table':None, 'column':None,
|
self._tb_cats[key] = {'table':None, 'column':None,
|
||||||
'datatype':'float', 'is_multiple':None,
|
'datatype':'float', 'is_multiple':{},
|
||||||
'kind':'field', 'name':'',
|
'kind':'field', 'name':'',
|
||||||
'search_terms':[key], 'label':label+'_index',
|
'search_terms':[key], 'label':label+'_index',
|
||||||
'colnum':None, 'display':{},
|
'colnum':None, 'display':{},
|
||||||
@ -560,7 +577,7 @@ class FieldMetadata(dict):
|
|||||||
if icu_lower(label) != label:
|
if icu_lower(label) != label:
|
||||||
st.append(icu_lower(label))
|
st.append(icu_lower(label))
|
||||||
self._tb_cats[label] = {'table':None, 'column':None,
|
self._tb_cats[label] = {'table':None, 'column':None,
|
||||||
'datatype':None, 'is_multiple':None,
|
'datatype':None, 'is_multiple':{},
|
||||||
'kind':'user', 'name':name,
|
'kind':'user', 'name':name,
|
||||||
'search_terms':st, 'is_custom':False,
|
'search_terms':st, 'is_custom':False,
|
||||||
'is_category':True, 'is_csp': False}
|
'is_category':True, 'is_csp': False}
|
||||||
@ -570,7 +587,7 @@ class FieldMetadata(dict):
|
|||||||
if label in self._tb_cats:
|
if label in self._tb_cats:
|
||||||
raise ValueError('Duplicate user field [%s]'%(label))
|
raise ValueError('Duplicate user field [%s]'%(label))
|
||||||
self._tb_cats[label] = {'table':None, 'column':None,
|
self._tb_cats[label] = {'table':None, 'column':None,
|
||||||
'datatype':None, 'is_multiple':None,
|
'datatype':None, 'is_multiple':{},
|
||||||
'kind':'search', 'name':name,
|
'kind':'search', 'name':name,
|
||||||
'search_terms':[], 'is_custom':False,
|
'search_terms':[], 'is_custom':False,
|
||||||
'is_category':True, 'is_csp': False}
|
'is_category':True, 'is_csp': False}
|
||||||
|
@ -171,7 +171,7 @@ class Restore(Thread):
|
|||||||
for x in fields:
|
for x in fields:
|
||||||
if x in cfm:
|
if x in cfm:
|
||||||
if x == 'is_multiple':
|
if x == 'is_multiple':
|
||||||
args.append(cfm[x] is not None)
|
args.append(bool(cfm[x]))
|
||||||
else:
|
else:
|
||||||
args.append(cfm[x])
|
args.append(cfm[x])
|
||||||
if len(args) == len(fields):
|
if len(args) == len(fields):
|
||||||
|
@ -231,7 +231,8 @@ class MobileServer(object):
|
|||||||
book['size'] = human_readable(book['size'])
|
book['size'] = human_readable(book['size'])
|
||||||
|
|
||||||
aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
|
aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
|
||||||
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
aut_is = CFM['authors']['is_multiple']
|
||||||
|
authors = aut_is['list_to_ui'].join([i.replace('|', ',') for i in aus.split(',')])
|
||||||
book['authors'] = authors
|
book['authors'] = authors
|
||||||
book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
|
book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
|
||||||
book['series'] = record[FM['series']]
|
book['series'] = record[FM['series']]
|
||||||
@ -254,8 +255,10 @@ class MobileServer(object):
|
|||||||
continue
|
continue
|
||||||
if datatype == 'text' and CFM[key]['is_multiple']:
|
if datatype == 'text' and CFM[key]['is_multiple']:
|
||||||
book[key] = concat(name,
|
book[key] = concat(name,
|
||||||
format_tag_string(val, ',',
|
format_tag_string(val,
|
||||||
no_tag_count=True))
|
CFM[key]['is_multiple']['ui_to_list'],
|
||||||
|
no_tag_count=True,
|
||||||
|
joinval=CFM[key]['is_multiple']['list_to_ui']))
|
||||||
else:
|
else:
|
||||||
book[key] = concat(name, val)
|
book[key] = concat(name, val)
|
||||||
|
|
||||||
|
@ -180,9 +180,12 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
|
|||||||
if val:
|
if val:
|
||||||
datatype = CFM[key]['datatype']
|
datatype = CFM[key]['datatype']
|
||||||
if datatype == 'text' and CFM[key]['is_multiple']:
|
if datatype == 'text' and CFM[key]['is_multiple']:
|
||||||
extra.append('%s: %s<br />'%(xml(name), xml(format_tag_string(val, ',',
|
extra.append('%s: %s<br />'%
|
||||||
ignore_max=True,
|
(xml(name),
|
||||||
no_tag_count=True))))
|
xml(format_tag_string(val,
|
||||||
|
CFM[key]['is_multiple']['ui_to_list'],
|
||||||
|
ignore_max=True, no_tag_count=True,
|
||||||
|
joinval=CFM[key]['is_multiple']['list_to_ui']))))
|
||||||
elif datatype == 'comments':
|
elif datatype == 'comments':
|
||||||
extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
|
extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
|
||||||
else:
|
else:
|
||||||
|
@ -68,7 +68,7 @@ def strftime(fmt='%Y/%m/%d %H:%M:%S', dt=None):
|
|||||||
except:
|
except:
|
||||||
return _strftime(fmt, nowf().timetuple())
|
return _strftime(fmt, nowf().timetuple())
|
||||||
|
|
||||||
def format_tag_string(tags, sep, ignore_max=False, no_tag_count=False):
|
def format_tag_string(tags, sep, ignore_max=False, no_tag_count=False, joinval=', '):
|
||||||
MAX = sys.maxint if ignore_max else tweaks['max_content_server_tags_shown']
|
MAX = sys.maxint if ignore_max else tweaks['max_content_server_tags_shown']
|
||||||
if tags:
|
if tags:
|
||||||
tlist = [t.strip() for t in tags.split(sep)]
|
tlist = [t.strip() for t in tags.split(sep)]
|
||||||
@ -78,10 +78,10 @@ def format_tag_string(tags, sep, ignore_max=False, no_tag_count=False):
|
|||||||
if len(tlist) > MAX:
|
if len(tlist) > MAX:
|
||||||
tlist = tlist[:MAX]+['...']
|
tlist = tlist[:MAX]+['...']
|
||||||
if no_tag_count:
|
if no_tag_count:
|
||||||
return ', '.join(tlist) if tlist else ''
|
return joinval.join(tlist) if tlist else ''
|
||||||
else:
|
else:
|
||||||
return u'%s:&:%s'%(tweaks['max_content_server_tags_shown'],
|
return u'%s:&:%s'%(tweaks['max_content_server_tags_shown'],
|
||||||
', '.join(tlist)) if tlist else ''
|
joinval.join(tlist)) if tlist else ''
|
||||||
|
|
||||||
def quote(s):
|
def quote(s):
|
||||||
if isinstance(s, unicode):
|
if isinstance(s, unicode):
|
||||||
|
@ -121,8 +121,12 @@ class XMLServer(object):
|
|||||||
name = CFM[key]['name']
|
name = CFM[key]['name']
|
||||||
custcols.append(k)
|
custcols.append(k)
|
||||||
if datatype == 'text' and CFM[key]['is_multiple']:
|
if datatype == 'text' and CFM[key]['is_multiple']:
|
||||||
kwargs[k] = concat('#T#'+name, format_tag_string(val,',',
|
kwargs[k] = \
|
||||||
ignore_max=True))
|
concat('#T#'+name,
|
||||||
|
format_tag_string(val,
|
||||||
|
CFM[key]['is_multiple']['ui_to_list'],
|
||||||
|
ignore_max=True,
|
||||||
|
joinval=CFM[key]['is_multiple']['list_to_ui']))
|
||||||
else:
|
else:
|
||||||
kwargs[k] = concat(name, val)
|
kwargs[k] = concat(name, val)
|
||||||
kwargs['custcols'] = ','.join(custcols)
|
kwargs['custcols'] = ','.join(custcols)
|
||||||
|
@ -121,9 +121,12 @@ class SortedConcatenate(object):
|
|||||||
return None
|
return None
|
||||||
return self.sep.join(map(self.ans.get, sorted(self.ans.keys())))
|
return self.sep.join(map(self.ans.get, sorted(self.ans.keys())))
|
||||||
|
|
||||||
class SafeSortedConcatenate(SortedConcatenate):
|
class SortedConcatenateBar(SortedConcatenate):
|
||||||
sep = '|'
|
sep = '|'
|
||||||
|
|
||||||
|
class SortedConcatenateAmper(SortedConcatenate):
|
||||||
|
sep = '&'
|
||||||
|
|
||||||
class IdentifiersConcat(object):
|
class IdentifiersConcat(object):
|
||||||
'''String concatenation aggregator for the identifiers map'''
|
'''String concatenation aggregator for the identifiers map'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -220,7 +223,8 @@ class DBThread(Thread):
|
|||||||
self.conn.execute('pragma cache_size=5000')
|
self.conn.execute('pragma cache_size=5000')
|
||||||
encoding = self.conn.execute('pragma encoding').fetchone()[0]
|
encoding = self.conn.execute('pragma encoding').fetchone()[0]
|
||||||
self.conn.create_aggregate('sortconcat', 2, SortedConcatenate)
|
self.conn.create_aggregate('sortconcat', 2, SortedConcatenate)
|
||||||
self.conn.create_aggregate('sort_concat', 2, SafeSortedConcatenate)
|
self.conn.create_aggregate('sortconcat_bar', 2, SortedConcatenateBar)
|
||||||
|
self.conn.create_aggregate('sortconcat_amper', 2, SortedConcatenateAmper)
|
||||||
self.conn.create_aggregate('identifiers_concat', 2, IdentifiersConcat)
|
self.conn.create_aggregate('identifiers_concat', 2, IdentifiersConcat)
|
||||||
load_c_extensions(self.conn)
|
load_c_extensions(self.conn)
|
||||||
self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row)
|
self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row)
|
||||||
|
@ -141,6 +141,22 @@ static void sort_concat_finalize2(sqlite3_context *context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sort_concat_finalize3(sqlite3_context *context) {
|
||||||
|
SortConcatList *list;
|
||||||
|
unsigned char *ans;
|
||||||
|
|
||||||
|
list = (SortConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
|
||||||
|
|
||||||
|
if (list != NULL && list->vals != NULL && list->count > 0) {
|
||||||
|
qsort(list->vals, list->count, sizeof(list->vals[0]), sort_concat_cmp);
|
||||||
|
ans = sort_concat_do_finalize(list, '&');
|
||||||
|
if (ans != NULL) sqlite3_result_text(context, (char*)ans, -1, SQLITE_TRANSIENT);
|
||||||
|
free(ans);
|
||||||
|
sort_concat_free(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// identifiers_concat {{{
|
// identifiers_concat {{{
|
||||||
@ -237,7 +253,8 @@ MYEXPORT int sqlite3_extension_init(
|
|||||||
sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi){
|
sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi){
|
||||||
SQLITE_EXTENSION_INIT2(pApi);
|
SQLITE_EXTENSION_INIT2(pApi);
|
||||||
sqlite3_create_function(db, "sortconcat", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize);
|
sqlite3_create_function(db, "sortconcat", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize);
|
||||||
sqlite3_create_function(db, "sort_concat", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize2);
|
sqlite3_create_function(db, "sortconcat_bar", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize2);
|
||||||
|
sqlite3_create_function(db, "sortconcat_amper", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize3);
|
||||||
sqlite3_create_function(db, "identifiers_concat", 2, SQLITE_UTF8, NULL, NULL, identifiers_concat_step, identifiers_concat_finalize);
|
sqlite3_create_function(db, "identifiers_concat", 2, SQLITE_UTF8, NULL, NULL, identifiers_concat_step, identifiers_concat_finalize);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -727,13 +727,8 @@ class BuiltinNot(BuiltinFormatterFunction):
|
|||||||
'returns the empty string. This function works well with test or '
|
'returns the empty string. This function works well with test or '
|
||||||
'first_non_empty. You can have as many values as you want.')
|
'first_non_empty. You can have as many values as you want.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, *args):
|
def evaluate(self, formatter, kwargs, mi, locals, val):
|
||||||
i = 0
|
return '' if val else '1'
|
||||||
while i < len(args):
|
|
||||||
if args[i]:
|
|
||||||
return '1'
|
|
||||||
i += 1
|
|
||||||
return ''
|
|
||||||
|
|
||||||
class BuiltinMergeLists(BuiltinFormatterFunction):
|
class BuiltinMergeLists(BuiltinFormatterFunction):
|
||||||
name = 'merge_lists'
|
name = 'merge_lists'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user