From 86d85fd06c059f2561d172b003e70b40fb293f54 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 21 Mar 2012 15:29:57 +0100 Subject: [PATCH] 1) Add 'series_sort' virtual column. 2) fixed bug causing exceptions when using zero-parameter functions at the innermost position in the value stack. --- src/calibre/gui2/tag_browser/model.py | 2 ++ src/calibre/library/caches.py | 30 +++++++++++++++++++----- src/calibre/library/database2.py | 2 ++ src/calibre/library/field_metadata.py | 10 ++++++++ src/calibre/utils/formatter.py | 2 +- src/calibre/utils/formatter_functions.py | 16 +++++++++++-- 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index b6a83d740c..c82fd8be58 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -1170,6 +1170,8 @@ class TagsModel(QAbstractItemModel): # {{{ charclass = ''.join(letters_seen) if k == 'author_sort': expr = r'%s:"~(^[%s])|(&\s*[%s])"'%(k, charclass, charclass) + elif k == 'series': + expr = r'series_sort:"~^[%s]"'%(charclass) else: expr = r'%s:"~^[%s]"'%(k, charclass) if node_searches[tag_item.tag.state] == 'true': diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 03f3590252..6e7421c7f0 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -172,11 +172,14 @@ def force_to_bool(val): class CacheRow(list): # {{{ - def __init__(self, db, composites, val): + def __init__(self, db, composites, val, series_col, series_sort_col): self.db = db self._composites = composites list.__init__(self, val) self._must_do = len(composites) > 0 + self._series_col = series_col + self._series_sort_col = series_sort_col + self._series_sort = None def __getitem__(self, col): if self._must_do: @@ -191,12 +194,19 @@ class CacheRow(list): # {{{ elif col in self._composites: is_comp = True if is_comp: - id = list.__getitem__(self, 0) + id_ = list.__getitem__(self, 0) self._must_do = False - mi = self.db.get_metadata(id, index_is_id=True, + mi = self.db.get_metadata(id_, index_is_id=True, get_user_categories=False) for c in self._composites: self[c] = mi.get(self._composites[c]) + if col == self._series_sort_col and self._series_sort is None: + if self[self._series_col]: + self._series_sort = title_sort(self[self._series_col]) + self[self._series_sort_col] = self._series_sort + else: + self._series_sort = '' + self[self._series_sort_col] = '' return list.__getitem__(self, col) def __getslice__(self, i, j): @@ -226,6 +236,8 @@ class ResultCache(SearchQueryParser): # {{{ for key in field_metadata: if field_metadata[key]['datatype'] == 'composite': self.composites[field_metadata[key]['rec_index']] = key + self.series_col = field_metadata['series']['rec_index'] + self.series_sort_col = field_metadata['series_sort']['rec_index'] self._data = [] self._map = self._map_filtered = [] self.first_sort = True @@ -918,9 +930,11 @@ class ResultCache(SearchQueryParser): # {{{ for id in ids: try: self._data[id] = CacheRow(db, self.composites, - db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0]) + db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0], + self.series_col, self.series_sort_col) self._data[id].append(db.book_on_device_string(id)) self._data[id].append(self.marked_ids_dict.get(id, None)) + self._data[id].append(None) except IndexError: return None try: @@ -935,9 +949,11 @@ class ResultCache(SearchQueryParser): # {{{ self._data.extend(repeat(None, max(ids)-len(self._data)+2)) for id in ids: self._data[id] = CacheRow(db, self.composites, - db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0]) + db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0], + self.series_col, self.series_sort_col) self._data[id].append(db.book_on_device_string(id)) self._data[id].append(self.marked_ids_dict.get(id, None)) + self._data[id].append(None) self._map[0:0] = ids self._map_filtered[0:0] = ids @@ -962,11 +978,13 @@ class ResultCache(SearchQueryParser): # {{{ temp = db.conn.get('SELECT * FROM meta2') self._data = list(itertools.repeat(None, temp[-1][0]+2)) if temp else [] for r in temp: - self._data[r[0]] = CacheRow(db, self.composites, r) + self._data[r[0]] = CacheRow(db, self.composites, r, + self.series_col, self.series_sort_col) for item in self._data: if item is not None: item.append(db.book_on_device_string(item[0])) item.append(None) + item.append(None) marked_col = self.FIELD_MAP['marked'] for id_,val in self.marked_ids_dict.iteritems(): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index bcc4b05399..1b4e8390f1 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -434,6 +434,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.field_metadata.set_field_record_index('ondevice', base, prefer_custom=False) self.FIELD_MAP['marked'] = base = base+1 self.field_metadata.set_field_record_index('marked', base, prefer_custom=False) + self.FIELD_MAP['series_sort'] = base = base+1 + self.field_metadata.set_field_record_index('series_sort', base, prefer_custom=False) script = ''' DROP VIEW IF EXISTS meta2; diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 58914e7572..cfe57aa11d 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -327,6 +327,16 @@ class FieldMetadata(dict): 'is_custom':False, 'is_category':False, 'is_csp': False}), + ('series_sort', {'table':None, + 'column':None, + 'datatype':'text', + 'is_multiple':{}, + 'kind':'field', + 'name':_('Series Sort'), + 'search_terms':['series_sort'], + 'is_custom':False, + 'is_category':False, + 'is_csp': False}), ('sort', {'table':None, 'column':None, 'datatype':'text', diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 9932abc988..97b985487e 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -218,7 +218,7 @@ class _CompileParser(_Parser): def expr(self, level): if self.compile_text: - self.max_level = max(level, self.max_level) + self.max_level = max(level+1, self.max_level) if self.token_is_id(): funcs = formatter_functions().get_functions() diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index c4eb80d3e0..f7b5ea7bca 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -12,6 +12,7 @@ import inspect, re, traceback from calibre import human_readable from calibre.constants import DEBUG +from calibre.ebooks.metadata import title_sort from calibre.utils.titlecase import titlecase from calibre.utils.icu import capitalize, strcmp, sort_key from calibre.utils.date import parse_date, format_date, now, UNDEFINED_DATE @@ -836,6 +837,17 @@ class BuiltinOndevice(BuiltinFormatterFunction): return _('Yes') return '' +class BuiltinSeriesSort(BuiltinFormatterFunction): + name = 'series_sort' + arg_count = 0 + category = 'Get values from metadata' + __doc__ = doc = _('booksize() -- return the series sort value') + + def evaluate(self, formatter, kwargs, mi, locals): + if mi.series: + return title_sort(mi.series) + return '' + class BuiltinHasCover(BuiltinFormatterFunction): name = 'has_cover' arg_count = 0 @@ -1149,8 +1161,8 @@ _formatter_builtins = [ BuiltinListSort(), BuiltinListUnion(), BuiltinLookup(), BuiltinLowercase(), BuiltinMultiply(), BuiltinNot(), BuiltinOndevice(), BuiltinOr(), BuiltinPrint(), BuiltinRawField(), - BuiltinRe(), BuiltinSelect(), BuiltinShorten(), BuiltinStrcat(), - BuiltinStrcatMax(), + BuiltinRe(), BuiltinSelect(), BuiltinSeriesSort(), BuiltinShorten(), + BuiltinStrcat(), BuiltinStrcatMax(), BuiltinStrcmp(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(), BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundComma(), BuiltinSwitch(), BuiltinTemplate(), BuiltinTest(), BuiltinTitlecase(),