From 0a72140cac41989951f2101bcc4df2b4cef01f1e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 12:48:21 +0530 Subject: [PATCH 1/2] Various library wide properties --- src/calibre/db/cache.py | 24 ++++++++++++++++++++++++ src/calibre/db/legacy.py | 14 ++++++++++++++ src/calibre/db/tests/legacy.py | 23 ++++++++++++++++++++--- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 49f5bd24ec..a322c9b6d7 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -360,6 +360,30 @@ class Cache(object): ''' return frozenset(iter(self.fields[name])) + @read_api + def all_field_names(self, field): + ''' Frozen set of all fields names (should only be used for many-one and many-many fields) ''' + try: + return frozenset(self.fields[field].table.id_map.itervalues()) + except AttributeError: + raise ValueError('%s is not a many-one or many-many field' % field) + + @read_api + def get_usage_count_by_id(self, field): + try: + return {k:len(v) for k, v in self.fields[field].table.col_book_map.iteritems()} + except AttributeError: + raise ValueError('%s is not a many-one or many-many field' % field) + + @read_api + def get_id_map(self, field): + try: + return self.fields[field].table.id_map.copy() + except AttributeError: + if field == 'title': + return self.fields[field].table.book_col_map.copy() + raise ValueError('%s is not a many-one or many-many field' % field) + @read_api def author_data(self, author_id): ''' diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 54d0887954..5f04dd18b2 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -65,6 +65,14 @@ class LibraryDatabase(object): for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'): setattr(self, meth, getattr(self.new_api, meth)) + for field in ('authors', 'tags', 'publisher', 'series'): + name = field[:-1] if field in {'authors', 'tags'} else field + setattr(self, 'all_%s_names' % name, partial(self.new_api.all_field_names, field)) + + for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems(): + setattr(self, func, partial(self.field_id_map, field)) + self.all_tags = lambda : list(self.all_tag_names()) + self.last_update_check = self.last_modified() def close(self): @@ -129,6 +137,12 @@ class LibraryDatabase(object): for book_id in self.data.cache.all_book_ids(): yield book_id + def get_usage_count_by_id(self, field): + return [[k, v] for k, v in self.new_api.get_usage_count_by_id(field).iteritems()] + + def field_id_map(self, field): + return [(k, v) for k, v in self.new_api.get_id_map(field).iteritems()] + def refresh(self, field=None, ascending=True): self.data.cache.refresh() self.data.refresh(field=field, ascending=ascending) diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index ced21f479d..b7fd99378a 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -139,9 +139,25 @@ class LegacyTest(BaseTest): 'get_next_series_num_for': [('A Series One',)], 'author_sort_from_authors': [(['Author One', 'Author Two', 'Unknown'],)], 'has_book':[(Metadata('title one'),), (Metadata('xxxx1111'),)], + 'all_author_names':[()], + 'all_tag_names':[()], + 'all_series_names':[()], + 'all_publisher_names':[()], + 'all_authors':[()], + 'all_tags2':[()], + 'all_tags':[()], + 'all_publishers':[()], + 'all_titles':[()], + 'all_series':[()], + 'get_usage_count_by_id':[('authors',), ('tags',), ('series',), ('publisher',), ('#tags',), ('languages',)], }.iteritems(): for a in args: - self.assertEqual(getattr(db, meth)(*a), getattr(ndb, meth)(*a), + fmt = lambda x: x + if meth in {'get_usage_count_by_id', 'all_series', 'all_authors', 'all_tags2', 'all_publishers', 'all_titles'}: + fmt = dict + elif meth in {'all_tags'}: + fmt = frozenset + self.assertEqual(fmt(getattr(db, meth)(*a)), fmt(getattr(ndb, meth)(*a)), 'The method: %s() returned different results for argument %s' % (meth, a)) db.close() # }}} @@ -220,7 +236,7 @@ class LegacyTest(BaseTest): 'get_feeds', 'get_feed', 'update_feed', 'remove_feeds', 'add_feed', 'set_feeds', } SKIP_ARGSPEC = { - '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', + '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', 'all_tags', } missing = [] @@ -238,10 +254,11 @@ class LegacyTest(BaseTest): if attr not in SKIP_ARGSPEC: try: argspec = inspect.getargspec(obj) + nargspec = inspect.getargspec(nobj) except TypeError: pass else: - compare_argspecs(argspec, inspect.getargspec(nobj), attr) + compare_argspecs(argspec, nargspec, attr) finally: for db in (ndb, db): db.close() From c1cca5213a310c0e0b0483fa455b2aeab20002dc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 13:21:08 +0530 Subject: [PATCH 2/2] The field API --- src/calibre/db/legacy.py | 13 +++++++++++++ src/calibre/db/tests/legacy.py | 8 ++++++++ src/calibre/library/database2.py | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 5f04dd18b2..4c825247e9 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -73,6 +73,14 @@ class LibraryDatabase(object): setattr(self, func, partial(self.field_id_map, field)) self.all_tags = lambda : list(self.all_tag_names()) + for func in ( + 'standard_field_keys', 'custom_field_keys', 'all_field_keys', + 'searchable_fields', 'sortable_field_keys', + 'search_term_to_field_key', 'custom_field_metadata', + 'all_metadata'): + setattr(self, func, getattr(self.field_metadata, func)) + self.metadata_for_field = self.field_metadata.get + self.last_update_check = self.last_modified() def close(self): @@ -273,6 +281,11 @@ class LibraryDatabase(object): return list(self.new_api.get_ids_for_custom_book_data(name)) # }}} + def get_field(self, index, key, default=None, index_is_id=False): + book_id = index if index_is_id else self.data.index_to_id(index) + mi = self.new_api.get_metadata(book_id, get_cover=key == 'cover') + return mi.get(key, default) + # Private interface {{{ def __iter__(self): diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index b7fd99378a..6413dc0d8f 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -149,7 +149,15 @@ class LegacyTest(BaseTest): 'all_publishers':[()], 'all_titles':[()], 'all_series':[()], + 'standard_field_keys':[()], + 'all_field_keys':[()], + 'searchable_fields':[()], + 'search_term_to_field_key':[('author',), ('tag',)], + 'metadata_for_field':[('title',), ('tags',)], + 'sortable_field_keys':[()], + 'custom_field_keys':[(True,), (False,)], 'get_usage_count_by_id':[('authors',), ('tags',), ('series',), ('publisher',), ('#tags',), ('languages',)], + 'get_field':[(1, 'title'), (2, 'tags'), (0, 'rating'), (1, 'authors'), (2, 'series'), (1, '#tags')], }.iteritems(): for a in args: fmt = lambda x: x diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 0540e8ede4..bd3f155c9d 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -776,10 +776,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return self.field_metadata.sortable_field_keys() def searchable_fields(self): - return self.field_metadata.searchable_field_keys() + return self.field_metadata.searchable_fields() def search_term_to_field_key(self, term): - return self.field_metadata.search_term_to_key(term) + return self.field_metadata.search_term_to_field_key(term) def custom_field_metadata(self, include_composites=True): return self.field_metadata.custom_field_metadata(include_composites)