diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index d176f10c07..88c6908de7 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -15,12 +15,16 @@ from functools import partial from calibre.db.tables import ONE_ONE, MANY_ONE, MANY_MANY, null from calibre.db.write import Writer +from calibre.db.utils import force_to_bool 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, clean_date_for_sort, parse_date from calibre.utils.localization import calibre_langcode_to_name +def bool_sort_key(bools_are_tristate): + return (lambda x:{True: 1, False: 2, None: 3}.get(x, 3)) if bools_are_tristate else lambda x:{True: 1, False: 2, None: 2}.get(x, 2) + class Field(object): is_many = False @@ -44,10 +48,7 @@ class Field(object): self._default_sort_key = 0 elif dt == 'bool': self._default_sort_key = None - if bools_are_tristate: - self._sort_key = lambda x:{True: 1, False: 2, None: 3}.get(x, 3) - else: - self._sort_key = lambda x:{True: 1, False: 2, None: 2}.get(x, 2) + self._sort_key = bool_sort_key(bools_are_tristate) elif dt == 'datetime': self._default_sort_key = UNDEFINED_DATE if tweaks['sort_dates_using_visible_fields']: @@ -178,8 +179,8 @@ class CompositeField(OneToOneField): is_composite = True SIZE_SUFFIX_MAP = {suffix:i for i, suffix in enumerate(('', 'K', 'M', 'G', 'T', 'P', 'E'))} - def __init__(self, *args, **kwargs): - OneToOneField.__init__(self, *args, **kwargs) + def __init__(self, name, table, bools_are_tristate): + OneToOneField.__init__(self, name, table, bools_are_tristate) self._render_cache = {} self._lock = Lock() @@ -200,6 +201,10 @@ class CompositeField(OneToOneField): fmt = m.get('display', {}).get('date_format', None) self._filter_date = partial(clean_date_for_sort, fmt=fmt) self._sort_key = self.date_sort_key + elif composite_sort == 'bool': + self._default_sort_key = None + self._bool_sort_key = bool_sort_key(bools_are_tristate) + self._sort_key = self.bool_sort_key else: self._sort_key = sort_key @@ -221,6 +226,9 @@ class CompositeField(OneToOneField): val = UNDEFINED_DATE return val + def bool_sort_key(self, val): + return self._bool_sort_key(force_to_bool(val)) + def render_composite(self, book_id, mi): with self._lock: ans = self._render_cache.get(book_id, None) diff --git a/src/calibre/db/search.py b/src/calibre/db/search.py index 6a6fbb0a56..b5f2f3ed49 100644 --- a/src/calibre/db/search.py +++ b/src/calibre/db/search.py @@ -13,6 +13,7 @@ from datetime import timedelta from collections import deque from calibre.constants import preferred_encoding +from calibre.db.utils import force_to_bool from calibre.utils.config_base import prefs from calibre.utils.date import parse_date, UNDEFINED_DATE, now, dt_as_local from calibre.utils.icu import primary_find, sort_key @@ -25,22 +26,6 @@ REGEXP_MATCH = 2 # Utils {{{ -def force_to_bool(val): - if isinstance(val, (str, unicode)): - try: - val = icu_lower(val) - if not val: - val = None - elif val in [_('yes'), _('checked'), 'true', 'yes']: - val = True - elif val in [_('no'), _('unchecked'), 'false', 'no']: - val = False - else: - val = bool(int(val)) - except: - val = None - return val - def _matchkind(query): matchkind = CONTAINS_MATCH if (len(query) > 1): diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index f0477872d7..f2ad7337b3 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -564,6 +564,7 @@ class ReadingTest(BaseTest): cache.create_custom_column('size', 'CC4', 'composite', False, display={'composite_template': '{#float:human_readable()}', 'composite_sort':'number'}) cache.create_custom_column('ccdate', 'CC5', 'composite', False, display={'composite_template': '{pubdate:format_date(d-M-yy)}', 'composite_sort':'date'}) + cache.create_custom_column('bool', 'CC6', 'composite', False, display={'composite_template': '{#yesno}', 'composite_sort':'bool'}) cache = self.init_cache() # Test searching @@ -581,5 +582,8 @@ class ReadingTest(BaseTest): # Test date sorting cache.set_field('pubdate', {1:p('2001-2-6'), 2:p('2001-10-6'), 3:p('2001-6-6')}) self.assertEqual([1, 3, 2], cache.multisort([('#ccdate', True)])) + + # Test bool sorting + self.assertEqual([2, 1, 3], cache.multisort([('#bool', True)])) # }}} diff --git a/src/calibre/db/utils.py b/src/calibre/db/utils.py index 8f77b9204e..dad05c2022 100644 --- a/src/calibre/db/utils.py +++ b/src/calibre/db/utils.py @@ -14,6 +14,22 @@ from threading import Lock from calibre import as_unicode, prints from calibre.constants import cache_dir +def force_to_bool(val): + if isinstance(val, (str, unicode)): + try: + val = icu_lower(val) + if not val: + val = None + elif val in [_('yes'), _('checked'), 'true', 'yes']: + val = True + elif val in [_('no'), _('unchecked'), 'false', 'no']: + val = False + else: + val = bool(int(val)) + except: + val = None + return val + Entry = namedtuple('Entry', 'path size timestamp thumbnail_size') class CacheError(Exception): pass