From c7e8509e10b9a6d4ded0322b12ec3f903b136d14 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Jan 2013 17:28:14 +0530 Subject: [PATCH] Categories for identifiers --- src/calibre/db/cache.py | 6 ++++++ src/calibre/db/categories.py | 24 ++++++++++++++++++------ src/calibre/db/fields.py | 28 +++++++++++++++------------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 65e2e614eb..47197aff3a 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -11,6 +11,7 @@ import os, traceback from collections import defaultdict from functools import wraps, partial +from calibre.db.categories import get_categories from calibre.db.locking import create_locks, RecordLock from calibre.db.fields import create_field from calibre.db.search import Search @@ -445,6 +446,11 @@ class Cache(object): return self._search_api(self, query, restriction, virtual_fields=virtual_fields) + @read_api + def get_categories(self, sort='name', book_ids=None, icon_map=None): + return get_categories(self, sort=sort, book_ids=book_ids, + icon_map=icon_map) + # }}} class SortKey(object): diff --git a/src/calibre/db/categories.py b/src/calibre/db/categories.py index 0def3b144e..f439cb5543 100644 --- a/src/calibre/db/categories.py +++ b/src/calibre/db/categories.py @@ -8,9 +8,11 @@ __copyright__ = '2013, Kovid Goyal ' __docformat__ = 'restructuredtext en' from functools import partial +from operator import attrgetter from calibre.library.field_metadata import TagsIcons from calibre.utils.config_base import tweaks +from calibre.utils.icu import sort_key CATEGORY_SORTS = { 'name', 'popularity', 'rating' } @@ -52,7 +54,7 @@ class Tag(object): def find_categories(field_metadata): for category, cat in field_metadata.iteritems(): if (cat['is_category'] and cat['kind'] not in { 'user', 'search' } and - category not in {'news', 'formats'} and not cat.get('is_csp', False)): + category not in {'news', 'formats'}): yield (category, cat['is_multiple'].get('cache_to_list', None), False) elif (cat['datatype'] == 'composite' and cat['display'].get('make_category', False)): @@ -61,7 +63,7 @@ def find_categories(field_metadata): def create_tag_class(category, fm, icon_map): cat = fm[category] icon = None - tooltip = '(' + category + ')' + tooltip = None if category == 'identifiers' else ('(' + category + ')') label = fm.key_to_label(category) if icon_map: if not fm.is_custom_field(category): @@ -94,13 +96,23 @@ def get_categories(dbcache, sort='name', book_ids=None, icon_map=None): fm = dbcache.field_metadata book_rating_map = dbcache.fields['rating'].book_value_map - lang_map = dbcache.fileds['languages'].book_value_map + lang_map = dbcache.fields['languages'].book_value_map categories = {} - book_ids = frozenset(book_ids) + book_ids = frozenset(book_ids) if book_ids else book_ids for category, is_multiple, is_composite in find_categories(fm): tag_class = create_tag_class(category, fm, icon_map) - categories[category] = dbcache.fields[category].get_categories( - tag_class, book_rating_map, sort, lang_map, book_ids) + cats = dbcache.fields[category].get_categories( + tag_class, book_rating_map, lang_map, book_ids) + if sort == 'popularity': + key=attrgetter('count') + elif sort == 'rating': + key=attrgetter('avg_rating') + else: + key=lambda x:sort_key(x.sort or x.name) + cats.sort(key=key) + categories[category] = cats + + return categories diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index 6a6deacf10..13983ea5d5 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -10,7 +10,6 @@ __docformat__ = 'restructuredtext en' from threading import Lock from collections import defaultdict, Counter -from operator import attrgetter from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY from calibre.ebooks.metadata import title_sort @@ -41,10 +40,8 @@ class Field(object): self.is_multiple = (bool(self.metadata['is_multiple']) or self.name == 'formats') self.category_formatter = type(u'') - self.category_sort_reverse = False if dt == 'rating': self.category_formatter = lambda x:'\u2605'*int(x/2) - self.category_sort_reverse = True elif name == 'languages': self.category_formatter = calibre_langcode_to_name @@ -101,7 +98,7 @@ class Field(object): ''' raise NotImplementedError() - def get_categories(self, tag_class, book_rating_map, sort, lang_map, book_ids=None): + def get_categories(self, tag_class, book_rating_map, lang_map, book_ids=None): ans = [] if not self.is_many: return ans @@ -113,20 +110,13 @@ class Field(object): if item_book_ids: ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for book_id in item_book_ids) if r > 0) - avg = sum(ratings)/len(ratings) + avg = sum(ratings)/len(ratings) if ratings else 0 name = self.category_formatter(self.table.id_map[item_id]) sval = (self.category_sort_value(item_id, item_book_ids, lang_map) if special_sort else name) c = tag_class(name, id=item_id, sort=sval, avg=avg, id_set=item_book_ids, count=len(item_book_ids)) ans.append(c) - if sort == 'popularity': - key=attrgetter('count') - elif sort == 'rating': - key=attrgetter('avg_rating') - else: - key=lambda x:sort_key(x.sort or x.name) - ans.sort(key=key, reverse=self.category_sort_reverse) return ans class OneToOneField(Field): @@ -282,7 +272,7 @@ class ManyToOneField(Field): @property def book_value_map(self): return {book_id:self.table.id_map[item_id] for book_id, item_id in - self.book_col_map.iteritems()} + self.table.book_col_map.iteritems()} class ManyToManyField(Field): @@ -365,6 +355,18 @@ class IdentifiersField(ManyToManyField): if val: yield val, {book_id} + def get_categories(self, tag_class, book_rating_map, lang_map, book_ids=None): + ans = [] + + for id_key, item_book_ids in self.table.col_book_map.iteritems(): + if book_ids is not None: + item_book_ids = item_book_ids.intersection(book_ids) + if item_book_ids: + name = id_key + c = tag_class(name, id_set=item_book_ids, count=len(item_book_ids)) + ans.append(c) + return ans + class AuthorsField(ManyToManyField): def author_data(self, author_id):