From aa58f3a559349e9e80ae25e79396b1889e65eeff Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 23 Aug 2013 17:06:52 +0530 Subject: [PATCH] newdb: Fix sorting of is_multiple fields Fix sorting of book list by multi-valued fields like tags not correct in the new backend. Fixes #1215820 [different ordering by tags in the 1.0 version](https://bugs.launchpad.net/calibre/+bug/1215820) --- src/calibre/db/fields.py | 12 ++++++++---- src/calibre/db/tests/reading.py | 19 +++++++++++++++++++ src/calibre/gui2/library/models.py | 2 +- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index e47e366c9b..35ed4a25ef 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -13,7 +13,7 @@ from collections import defaultdict, Counter from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY, null from calibre.db.write import Writer -from calibre.ebooks.metadata import title_sort +from calibre.ebooks.metadata import title_sort, author_to_author_sort from calibre.utils.config_base import tweaks from calibre.utils.icu import sort_key from calibre.utils.date import UNDEFINED_DATE @@ -48,6 +48,10 @@ class Field(object): self._sort_key = lambda x:sort_key(calibre_langcode_to_name(x)) self.is_multiple = (bool(self.metadata['is_multiple']) or self.name == 'formats') + self.sort_sort_key = True + if self.is_multiple and '&' in self.metadata['is_multiple']['list_to_ui']: + self._sort_key = lambda x: sort_key(author_to_author_sort(x)) + self.sort_sort_key = False self.default_value = {} if name == 'identifiers' else () if self.is_multiple else None self.category_formatter = type(u'') if dt == 'rating': @@ -378,9 +382,9 @@ class ManyToManyField(Field): all_cids = set() for cids in ans.itervalues(): all_cids = all_cids.union(set(cids)) - sk_map = {cid: self._sort_key(self.table.id_map[cid]) - for cid in all_cids} - return {id_: (tuple(sk_map[cid] for cid in cids) if cids else + sk_map = {cid: self._sort_key(self.table.id_map[cid]) for cid in all_cids} + sort_func = (lambda x:tuple(sorted(x))) if self.sort_sort_key else tuple + return {id_: (sort_func(sk_map[cid] for cid in cids) if cids else (self._default_sort_key,)) for id_, cids in ans.iteritems()} diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index c9d29475f6..5f752dbfa2 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -161,6 +161,25 @@ class ReadingTest(BaseTest): # Test subsorting self.assertEqual([3, 2, 1], cache.multisort([('identifiers', True), ('title', True)]), 'Subsort failed') + + # Test sorting of is_multiple fields. + + # Author like fields should be sorted by generating sort names from the + # actual values in entry order + for field in ('authors', '#authors'): + self.assertEqual( + cache.set_field(field, {1:('aa bb', 'bb cc', 'cc dd'), 2:('bb aa', 'xx yy'), 3: ('aa bb', 'bb aa')}), {1, 2, 3}) + self.assertEqual([2, 3, 1], cache.multisort([(field, True)], ids_to_sort=(1, 2, 3))) + self.assertEqual([1, 3, 2], cache.multisort([(field, False)], ids_to_sort=(1, 2, 3))) + + # All other is_multiple fields should be sorted by sorting the values + # for each book and using that as the sort key + for field in ('tags', '#tags'): + self.assertEqual( + cache.set_field(field, {1:('b', 'a'), 2:('c', 'y'), 3: ('b', 'z')}), {1, 2, 3}) + self.assertEqual([1, 3, 2], cache.multisort([(field, True)], ids_to_sort=(1, 2, 3))) + self.assertEqual([2, 3, 1], cache.multisort([(field, False)], ids_to_sort=(1, 2, 3))) + # }}} def test_get_metadata(self): # {{{ diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index ddd67f9ab7..181f145cbe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -685,7 +685,7 @@ class BooksModel(QAbstractTableModel): # {{{ elif dt in {'text', 'comments', 'composite', 'enumeration'}: if m['is_multiple'] and not field_obj.is_composite: jv = m['is_multiple']['list_to_ui'] - do_sort = field == 'tags' + do_sort = '&' not in jv if do_sort: def func(idx): return QVariant(jv.join(sorted(fffunc(field_obj, idfunc(idx), default_value=()), key=sort_key)))