mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Change is_multiple to a dict containing the split/join characters to use in various situations.
This commit is contained in:
parent
3fc513ec2a
commit
6de35843fb
@ -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]
|
||||||
|
|
||||||
|
@ -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,14 +655,14 @@ 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
|
# if dest == 'authors' or \
|
||||||
self.destination_field_fm['datatype'] == 'text' and
|
# (self.destination_field_fm['is_custom'] and
|
||||||
self.destination_field_fm['display'].get('is_names', False)):
|
# self.destination_field_fm['datatype'] == 'text' and
|
||||||
splitter = ' & '
|
# self.destination_field_fm['display'].get('is_names', False)):
|
||||||
else:
|
# splitter = ' & '
|
||||||
splitter = ','
|
# else:
|
||||||
|
# splitter = ','
|
||||||
res = []
|
res = []
|
||||||
for v in val:
|
for v in val:
|
||||||
for x in v.split(splitter):
|
for x in v.split(splitter):
|
||||||
|
@ -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)
|
||||||
|
@ -205,7 +205,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,7 @@ 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']['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 +665,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 +704,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 +1047,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)
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ class Rule(object): # {{{
|
|||||||
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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user