From 6a488a332033d17bfbf31f51c2237f96a0fcbc40 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 25 Jul 2013 10:50:58 +0200 Subject: [PATCH 1/3] First stage: adding a ProxyMetadata object to get_metadata --- src/calibre/db/cache.py | 3 +++ src/calibre/db/lazy.py | 5 +++-- src/calibre/ebooks/metadata/book/base.py | 6 ++++-- src/calibre/utils/formatter_functions.py | 22 +++++++++++++++++++++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index ef4c8854b4..1c224bce7e 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -184,6 +184,9 @@ class Cache(object): def _get_metadata(self, book_id, get_user_categories=True): # {{{ mi = Metadata(None, template_cache=self.formatter_template_cache) + + mi._proxy_metadata = ProxyMetadata(self, book_id, formatter=mi.formatter) + author_ids = self._field_ids_for('authors', book_id) adata = self._author_data(author_ids) aut_list = [adata[i] for i in author_ids] diff --git a/src/calibre/db/lazy.py b/src/calibre/db/lazy.py index 566e51b32a..303d47b697 100644 --- a/src/calibre/db/lazy.py +++ b/src/calibre/db/lazy.py @@ -278,9 +278,10 @@ for field in ('formats', 'format_metadata'): class ProxyMetadata(Metadata): - def __init__(self, db, book_id): + def __init__(self, db, book_id, formatter=None): sa(self, 'template_cache', db.formatter_template_cache) - sa(self, 'formatter', SafeFormat()) + if formatter is None: + sa(self, 'formatter', SafeFormat()) sa(self, '_db', weakref.ref(db)) sa(self, '_book_id', book_id) sa(self, '_cache', {'user_categories':{}, 'cover_data':(None,None), 'device_collections':[]}) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 4427121f37..93253b1dcc 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -66,7 +66,8 @@ class Metadata(object): becomes a reserved field name. ''' - def __init__(self, title, authors=(_('Unknown'),), other=None, template_cache=None): + def __init__(self, title, authors=(_('Unknown'),), other=None, template_cache=None, + formatter=None): ''' @param title: title or ``_('Unknown')`` @param authors: List of strings or [] @@ -85,7 +86,8 @@ class Metadata(object): self.author = list(authors) if authors else [] # Needed for backward compatibility self.authors = list(authors) if authors else [] from calibre.ebooks.metadata.book.formatter import SafeFormat - self.formatter = SafeFormat() + if formatter is None: + self.formatter = SafeFormat() self.template_cache = template_cache def is_null(self, field): diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index f2b973a1e7..606fe3b11b 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -1246,6 +1246,26 @@ class BuiltinFinishFormatting(BuiltinFormatterFunction): return val return prefix + formatter._do_format(val, fmt) + suffix +class BuiltinVirtualLibraries(BuiltinFormatterFunction): + name = 'virtual_libraries' + arg_count = 0 + category = 'Get values from metadata' + __doc__ = doc = _('virtual_libraries() -- return a comma-separated list of ' + 'virtual libraries that contain this book. This function ' + 'works only in the GUI. If you want to use these values ' + 'in save-to-disk or send-to-device templates then you ' + 'must make a custom "Column built from other columns", use ' + 'the function in that column\'s template, and use that ' + 'column\'s value in your save/send templates') + + def evaluate(self, formatter, kwargs, mi, locals_): + from calibre.db.lazy import ProxyMetadata + if isinstance(mi, ProxyMetadata): + return mi.virtual_libraries + if hasattr(mi, '_proxy_metadata'): + return mi._proxy_metadata.virtual_libraries + return _('This function can be used only in the GUI') + _formatter_builtins = [ BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinAssign(), BuiltinBooksize(), @@ -1267,7 +1287,7 @@ _formatter_builtins = [ BuiltinStrcmp(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(), BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundComma(), BuiltinSwitch(), BuiltinTemplate(), BuiltinTest(), BuiltinTitlecase(), - BuiltinToday(), BuiltinUppercase(), + BuiltinToday(), BuiltinUppercase(), BuiltinVirtualLibraries() ] class FormatterUserFunction(FormatterFunction): From 8467baea332a099a69ee6e3147da7c68be48833b Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 25 Jul 2013 11:36:34 +0200 Subject: [PATCH 2/3] Use ProxyMetadata in formatter functions for virt libs, ondevice, book_size, and approximate_formats. This saves the assignment in get_metadata() --- src/calibre/db/cache.py | 7 ++-- src/calibre/db/lazy.py | 4 +- src/calibre/gui2/library/models.py | 2 +- src/calibre/utils/formatter_functions.py | 53 ++++++++++++++++-------- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 1c224bce7e..0f187f0400 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -214,8 +214,8 @@ class Cache(object): default_value='dummy') mi.title_sort = self._field_for('sort', book_id, default_value=_('Unknown')) - mi.book_size = self._field_for('size', book_id, default_value=0) - mi.ondevice_col = self._field_for('ondevice', book_id, default_value='') +# mi.book_size = self._field_for('size', book_id, default_value=0) +# mi.ondevice_col = self._field_for('ondevice', book_id, default_value='') mi.last_modified = self._field_for('last_modified', book_id, default_value=n) formats = self._field_for('formats', book_id) @@ -227,7 +227,7 @@ class Cache(object): mi.format_metadata = FormatMetadata(self, book_id, formats) good_formats = FormatsList(formats, mi.format_metadata) mi.formats = good_formats - mi.db_approx_formats = formats +# mi.db_approx_formats = formats mi.has_cover = _('Yes') if self._field_for('cover', book_id, default_value=False) else '' mi.tags = list(self._field_for('tags', book_id, default_value=())) @@ -1413,6 +1413,7 @@ class Cache(object): def refresh_ondevice(self): self.fields['ondevice'].clear_caches() self.clear_search_caches() + self.clear_composite_caches() @read_api def tags_older_than(self, tag, delta=None, must_have_tag=None, must_have_authors=None): diff --git a/src/calibre/db/lazy.py b/src/calibre/db/lazy.py index 303d47b697..f522bc1cc1 100644 --- a/src/calibre/db/lazy.py +++ b/src/calibre/db/lazy.py @@ -355,4 +355,6 @@ class ProxyMetadata(Metadata): um = ga(self, '_user_metadata') return frozenset(ALL_METADATA_FIELDS.union(um.iterkeys())) - + @property + def _proxy_metadata(self): + return self diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index e70122fd36..5f9e7bfdc6 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -453,7 +453,7 @@ class BooksModel(QAbstractTableModel): # {{{ def get_book_display_info(self, idx): mi = self.db.get_metadata(idx) - mi.size = mi.book_size + mi.size = mi._proxy_metadata.book_size mi.cover_data = ('jpg', self.cover(idx)) mi.id = self.db.id(idx) mi.field_metadata = self.db.field_metadata diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 606fe3b11b..8fcc35e3bb 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -640,15 +640,22 @@ class BuiltinApproximateFormats(BuiltinFormatterFunction): 'although it probably is. ' 'This function can be called in template program mode using ' 'the template "{:\'approximate_formats()\'}". ' - 'Note that format names are always uppercase, as in EPUB.' + 'Note that format names are always uppercase, as in EPUB. ' + 'This function works only in the GUI. If you want to use these values ' + 'in save-to-disk or send-to-device templates then you ' + 'must make a custom "Column built from other columns", use ' + 'the function in that column\'s template, and use that ' + 'column\'s value in your save/send templates' ) def evaluate(self, formatter, kwargs, mi, locals): - fmt_data = mi.get('db_approx_formats', []) - if not fmt_data: - return '' - data = sorted(fmt_data) - return ','.join(v.upper() for v in data) + if hasattr(mi, '_proxy_metadata'): + fmt_data = mi._proxy_metadata.db_approx_formats + if not fmt_data: + return '' + data = sorted(fmt_data) + return ','.join(v.upper() for v in data) + return _('This function can be used only in the GUI') class BuiltinFormatsModtimes(BuiltinFormatterFunction): name = 'formats_modtimes' @@ -902,27 +909,42 @@ class BuiltinBooksize(BuiltinFormatterFunction): name = 'booksize' arg_count = 0 category = 'Get values from metadata' - __doc__ = doc = _('booksize() -- return value of the size field') + __doc__ = doc = _('booksize() -- return value of the size field. ' + 'This function works only in the GUI. If you want to use this value ' + 'in save-to-disk or send-to-device templates then you ' + 'must make a custom "Column built from other columns", use ' + 'the function in that column\'s template, and use that ' + 'column\'s value in your save/send templates') def evaluate(self, formatter, kwargs, mi, locals): - if mi.book_size is not None: + if hasattr(mi, '_proxy_metadata'): try: - return str(mi.book_size) + v = mi._proxy_metadata.book_size + if v is not None: + return str(mi._proxy_metadata.book_size) + return '' except: pass - return '' + return '' + return _('This function can be used only in the GUI') class BuiltinOndevice(BuiltinFormatterFunction): name = 'ondevice' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return ' - 'the empty string') + 'the empty string. This function works only in the GUI. If you want to ' + 'use this value in save-to-disk or send-to-device templates then you ' + 'must make a custom "Column built from other columns", use ' + 'the function in that column\'s template, and use that ' + 'column\'s value in your save/send templates') def evaluate(self, formatter, kwargs, mi, locals): - if mi.ondevice_col: - return _('Yes') - return '' + if hasattr(mi, '_proxy_metadata'): + if mi._proxy_metadata.ondevice_col: + return _('Yes') + return '' + return _('This function can be used only in the GUI') class BuiltinSeriesSort(BuiltinFormatterFunction): name = 'series_sort' @@ -1259,9 +1281,6 @@ class BuiltinVirtualLibraries(BuiltinFormatterFunction): 'column\'s value in your save/send templates') def evaluate(self, formatter, kwargs, mi, locals_): - from calibre.db.lazy import ProxyMetadata - if isinstance(mi, ProxyMetadata): - return mi.virtual_libraries if hasattr(mi, '_proxy_metadata'): return mi._proxy_metadata.virtual_libraries return _('This function can be used only in the GUI') From 6315a05142ac16d017bbf7382674ab5045de0273 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 25 Jul 2013 12:20:12 +0200 Subject: [PATCH 3/3] correctly handle the formatter parameter in the cache and in Metadata --- src/calibre/db/lazy.py | 3 +-- src/calibre/ebooks/metadata/book/base.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/calibre/db/lazy.py b/src/calibre/db/lazy.py index f522bc1cc1..166627c438 100644 --- a/src/calibre/db/lazy.py +++ b/src/calibre/db/lazy.py @@ -280,8 +280,7 @@ class ProxyMetadata(Metadata): def __init__(self, db, book_id, formatter=None): sa(self, 'template_cache', db.formatter_template_cache) - if formatter is None: - sa(self, 'formatter', SafeFormat()) + sa(self, 'formatter', SafeFormat() if formatter is None else formatter) sa(self, '_db', weakref.ref(db)) sa(self, '_book_id', book_id) sa(self, '_cache', {'user_categories':{}, 'cover_data':(None,None), 'device_collections':[]}) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 93253b1dcc..9e94678844 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -87,7 +87,7 @@ class Metadata(object): self.authors = list(authors) if authors else [] from calibre.ebooks.metadata.book.formatter import SafeFormat if formatter is None: - self.formatter = SafeFormat() + self.formatter = SafeFormat() if formatter is None else formatter self.template_cache = template_cache def is_null(self, field):