Add language dependent sorting for series like columns

This commit is contained in:
Kovid Goyal 2013-01-19 22:37:27 +05:30
parent 3e4d847eee
commit 419f3b6394
4 changed files with 68 additions and 40 deletions

View File

@ -269,11 +269,11 @@ class Cache(object):
return ()
@read_api
def all_book_ids(self):
def all_book_ids(self, type=frozenset):
'''
Frozen set of all known book ids.
'''
return frozenset(self.fields['uuid'])
return type(self.fields['uuid'])
@read_api
def all_field_ids(self, name):
@ -316,6 +316,10 @@ class Cache(object):
self.format_metadata_cache[book_id][fmt] = ans
return ans
@read_api
def pref(self, name):
return self.backend.prefs[name]
@api
def get_metadata(self, book_id,
get_cover=False, get_user_categories=True, cover_as_data=False):
@ -378,17 +382,21 @@ class Cache(object):
all_book_ids = frozenset(self._all_book_ids() if ids_to_sort is None
else ids_to_sort)
get_metadata = partial(self._get_metadata, get_user_categories=False)
def get_lang(book_id):
ans = self._field_for('languages', book_id)
return ans[0] if ans else None
fm = {'title':'sort', 'authors':'author_sort'}
def sort_key(field):
'Handle series type fields'
ans = self.fields[fm.get(field, field)].sort_keys_for_books(get_metadata,
all_book_ids)
idx = field + '_index'
if idx in self.fields:
idx_ans = self.fields[idx].sort_keys_for_books(get_metadata,
all_book_ids)
is_series = idx in self.fields
ans = self.fields[fm.get(field, field)].sort_keys_for_books(
get_metadata, get_lang, all_book_ids,)
if is_series:
idx_ans = self.fields[idx].sort_keys_for_books(
get_metadata, get_lang, all_book_ids)
ans = {k:(v, idx_ans[k]) for k, v in ans.iteritems()}
return ans

View File

@ -11,6 +11,8 @@ __docformat__ = 'restructuredtext en'
from threading import Lock
from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY
from calibre.ebooks.metadata import title_sort
from calibre.utils.config_base import tweaks
from calibre.utils.icu import sort_key
from calibre.utils.date import UNDEFINED_DATE
from calibre.utils.localization import calibre_langcode_to_name
@ -72,7 +74,7 @@ class Field(object):
'''
return iter(())
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
'''
Return a mapping of book_id -> sort_key. The sort key is suitable for
use in sorting the list of all books by this field, via the python cmp
@ -96,7 +98,7 @@ class OneToOneField(Field):
def __iter__(self):
return self.table.book_col_map.iterkeys()
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
return {id_ : self._sort_key(self.table.book_col_map.get(id_,
self._default_sort_key)) for id_ in all_book_ids}
@ -133,7 +135,7 @@ class CompositeField(OneToOneField):
ans = mi.get('#'+self.metadata['label'])
return ans
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
return {id_ : sort_key(self.get_value_with_cache(id_, get_metadata)) for id_ in
all_book_ids}
@ -170,7 +172,7 @@ class OnDeviceField(OneToOneField):
def __iter__(self):
return iter(())
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
return {id_ : self.for_book(id_) for id_ in
all_book_ids}
@ -196,7 +198,7 @@ class ManyToOneField(Field):
def __iter__(self):
return self.table.id_map.iterkeys()
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
ans = {id_ : self.table.book_col_map.get(id_, None)
for id_ in all_book_ids}
sk_map = {cid : (self._default_sort_key if cid is None else
@ -227,7 +229,7 @@ class ManyToManyField(Field):
def __iter__(self):
return self.table.id_map.iterkeys()
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
ans = {id_ : self.table.book_col_map.get(id_, ())
for id_ in all_book_ids}
all_cids = set()
@ -248,7 +250,7 @@ class IdentifiersField(ManyToManyField):
ids = default_value
return ids
def sort_keys_for_books(self, get_metadata, all_book_ids):
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
'Sort by identifier keys'
ans = {id_ : self.table.book_col_map.get(id_, ())
for id_ in all_book_ids}
@ -274,6 +276,21 @@ class FormatsField(ManyToManyField):
def format_fname(self, book_id, fmt):
return self.table.fname_map[book_id][fmt.upper()]
class SeriesField(ManyToOneField):
def sort_key_for_series(self, book_id, get_lang, series_sort_order):
sid = self.table.book_col_map.get(book_id, None)
if sid is None:
return self._default_sort_key
return self._sort_key(title_sort(self.table.id_map[sid],
order=series_sort_order,
lang=get_lang(book_id)))
def sort_keys_for_books(self, get_metadata, get_lang, all_book_ids):
sso = tweaks['title_series_sorting']
return {book_id:self.sort_key_for_series(book_id, get_lang, sso) for book_id
in all_book_ids}
def create_field(name, table):
cls = {
ONE_ONE : OneToOneField,
@ -290,5 +307,7 @@ def create_field(name, table):
cls = IdentifiersField
elif table.metadata['datatype'] == 'composite':
cls = CompositeField
elif table.metadata['datatype'] == 'series':
cls = SeriesField
return cls(name, table)

Binary file not shown.

View File

@ -63,7 +63,7 @@ class ReadingTest(BaseTest):
'sort': 'One',
'authors': ('Author One',),
'author_sort': 'One, Author',
'series' : 'Series One',
'series' : 'A Series One',
'series_index': 1.0,
'tags':('Tag Two', 'Tag One'),
'formats': (),
@ -92,7 +92,7 @@ class ReadingTest(BaseTest):
'sort': 'Title Two',
'authors': ('Author Two', 'Author One'),
'author_sort': 'Two, Author & One, Author',
'series' : 'Series One',
'series' : 'A Series One',
'series_index': 2.0,
'rating': 6.0,
'tags': ('Tag One',),
@ -132,17 +132,16 @@ class ReadingTest(BaseTest):
for field, order in {
'title' : [2, 1, 3],
'authors': [2, 1, 3],
'series' : [3, 2, 1],
'series' : [3, 1, 2],
'tags' : [3, 1, 2],
'rating' : [3, 2, 1],
# 'identifiers': [3, 2, 1], There is no stable sort since 1 and
# 2 have the same identifier keys
# TODO: Add an empty book to the db and ensure that empty
# fields sort the same as they do in db2
# 'last_modified': [3, 2, 1], There is no stable sort as two
# records have the exact same value
'timestamp': [2, 1, 3],
'pubdate' : [1, 2, 3],
'publisher': [3, 2, 1],
'last_modified': [2, 1, 3],
'languages': [3, 2, 1],
'comments': [3, 2, 1],
'#enum' : [3, 2, 1],
@ -153,6 +152,8 @@ class ReadingTest(BaseTest):
'#tags':[3, 2, 1],
'#yesno':[3, 1, 2],
'#comments':[3, 2, 1],
# TODO: Add an empty book to the db and ensure that empty
# fields sort the same as they do in db2
}.iteritems():
x = list(reversed(order))
self.assertEqual(order, cache.multisort([(field, True)],