mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Categories for identifiers
This commit is contained in:
parent
6a9fb786d9
commit
c7e8509e10
@ -11,6 +11,7 @@ import os, traceback
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial
|
||||||
|
|
||||||
|
from calibre.db.categories import get_categories
|
||||||
from calibre.db.locking import create_locks, RecordLock
|
from calibre.db.locking import create_locks, RecordLock
|
||||||
from calibre.db.fields import create_field
|
from calibre.db.fields import create_field
|
||||||
from calibre.db.search import Search
|
from calibre.db.search import Search
|
||||||
@ -445,6 +446,11 @@ class Cache(object):
|
|||||||
return self._search_api(self, query, restriction,
|
return self._search_api(self, query, restriction,
|
||||||
virtual_fields=virtual_fields)
|
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):
|
class SortKey(object):
|
||||||
|
@ -8,9 +8,11 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
from calibre.library.field_metadata import TagsIcons
|
from calibre.library.field_metadata import TagsIcons
|
||||||
from calibre.utils.config_base import tweaks
|
from calibre.utils.config_base import tweaks
|
||||||
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
CATEGORY_SORTS = { 'name', 'popularity', 'rating' }
|
CATEGORY_SORTS = { 'name', 'popularity', 'rating' }
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ class Tag(object):
|
|||||||
def find_categories(field_metadata):
|
def find_categories(field_metadata):
|
||||||
for category, cat in field_metadata.iteritems():
|
for category, cat in field_metadata.iteritems():
|
||||||
if (cat['is_category'] and cat['kind'] not in { 'user', 'search' } and
|
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)
|
yield (category, cat['is_multiple'].get('cache_to_list', None), False)
|
||||||
elif (cat['datatype'] == 'composite' and
|
elif (cat['datatype'] == 'composite' and
|
||||||
cat['display'].get('make_category', False)):
|
cat['display'].get('make_category', False)):
|
||||||
@ -61,7 +63,7 @@ def find_categories(field_metadata):
|
|||||||
def create_tag_class(category, fm, icon_map):
|
def create_tag_class(category, fm, icon_map):
|
||||||
cat = fm[category]
|
cat = fm[category]
|
||||||
icon = None
|
icon = None
|
||||||
tooltip = '(' + category + ')'
|
tooltip = None if category == 'identifiers' else ('(' + category + ')')
|
||||||
label = fm.key_to_label(category)
|
label = fm.key_to_label(category)
|
||||||
if icon_map:
|
if icon_map:
|
||||||
if not fm.is_custom_field(category):
|
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
|
fm = dbcache.field_metadata
|
||||||
book_rating_map = dbcache.fields['rating'].book_value_map
|
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 = {}
|
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):
|
for category, is_multiple, is_composite in find_categories(fm):
|
||||||
tag_class = create_tag_class(category, fm, icon_map)
|
tag_class = create_tag_class(category, fm, icon_map)
|
||||||
categories[category] = dbcache.fields[category].get_categories(
|
cats = dbcache.fields[category].get_categories(
|
||||||
tag_class, book_rating_map, sort, lang_map, book_ids)
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
from operator import attrgetter
|
|
||||||
|
|
||||||
from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY
|
from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY
|
||||||
from calibre.ebooks.metadata import title_sort
|
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 ==
|
self.is_multiple = (bool(self.metadata['is_multiple']) or self.name ==
|
||||||
'formats')
|
'formats')
|
||||||
self.category_formatter = type(u'')
|
self.category_formatter = type(u'')
|
||||||
self.category_sort_reverse = False
|
|
||||||
if dt == 'rating':
|
if dt == 'rating':
|
||||||
self.category_formatter = lambda x:'\u2605'*int(x/2)
|
self.category_formatter = lambda x:'\u2605'*int(x/2)
|
||||||
self.category_sort_reverse = True
|
|
||||||
elif name == 'languages':
|
elif name == 'languages':
|
||||||
self.category_formatter = calibre_langcode_to_name
|
self.category_formatter = calibre_langcode_to_name
|
||||||
|
|
||||||
@ -101,7 +98,7 @@ class Field(object):
|
|||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
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 = []
|
ans = []
|
||||||
if not self.is_many:
|
if not self.is_many:
|
||||||
return ans
|
return ans
|
||||||
@ -113,20 +110,13 @@ class Field(object):
|
|||||||
if item_book_ids:
|
if item_book_ids:
|
||||||
ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for
|
ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for
|
||||||
book_id in item_book_ids) if r > 0)
|
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])
|
name = self.category_formatter(self.table.id_map[item_id])
|
||||||
sval = (self.category_sort_value(item_id, item_book_ids, lang_map)
|
sval = (self.category_sort_value(item_id, item_book_ids, lang_map)
|
||||||
if special_sort else name)
|
if special_sort else name)
|
||||||
c = tag_class(name, id=item_id, sort=sval, avg=avg,
|
c = tag_class(name, id=item_id, sort=sval, avg=avg,
|
||||||
id_set=item_book_ids, count=len(item_book_ids))
|
id_set=item_book_ids, count=len(item_book_ids))
|
||||||
ans.append(c)
|
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
|
return ans
|
||||||
|
|
||||||
class OneToOneField(Field):
|
class OneToOneField(Field):
|
||||||
@ -282,7 +272,7 @@ class ManyToOneField(Field):
|
|||||||
@property
|
@property
|
||||||
def book_value_map(self):
|
def book_value_map(self):
|
||||||
return {book_id:self.table.id_map[item_id] for book_id, item_id in
|
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):
|
class ManyToManyField(Field):
|
||||||
|
|
||||||
@ -365,6 +355,18 @@ class IdentifiersField(ManyToManyField):
|
|||||||
if val:
|
if val:
|
||||||
yield val, {book_id}
|
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):
|
class AuthorsField(ManyToManyField):
|
||||||
|
|
||||||
def author_data(self, author_id):
|
def author_data(self, author_id):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user