From 4cf1539c37464233ada7a7f4e1779dd6a1688380 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 25 Aug 2013 10:08:29 +0530 Subject: [PATCH] Fix sorting for composite cols that are marked as numeric Incidentally, the implementation of size suffixes in the old db was broken. --- src/calibre/db/fields.py | 25 ++++++++++++++++++++++--- src/calibre/db/tests/reading.py | 10 +++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index bf97c97309..1d4215434e 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -8,6 +8,7 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' +from locale import atof from threading import Lock from collections import defaultdict, Counter from functools import partial @@ -175,17 +176,35 @@ class OneToOneField(Field): 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) self._render_cache = {} self._lock = Lock() - self._composite_name = '#' + self.metadata['label'] + m = self.metadata + self._composite_name = '#' + m['label'] try: - self.splitter = self.metadata['is_multiple'].get('cache_to_list', None) + self.splitter = m['is_multiple'].get('cache_to_list', None) except AttributeError: self.splitter = None + composite_sort = m.get('display', {}).get('composite_sort', None) + if composite_sort == 'number': + self._sort_key = self.number_sort_key + else: + self._sort_key = sort_key + + def number_sort_key(self, val): + try: + p = 1 + if val and val.endswith('B'): + p = 1 << (10 * self.SIZE_SUFFIX_MAP.get(val[-2:-1], 0)) + val = val[:(-2 if p > 1 else -1)].strip() + val = atof(val) * p + except (TypeError, AttributeError, ValueError): + val = 0.0 + return val def render_composite(self, book_id, mi): with self._lock: @@ -215,7 +234,7 @@ class CompositeField(OneToOneField): return ans def sort_keys_for_books(self, get_metadata, lang_map, all_book_ids): - return {id_: sort_key(self.get_value_with_cache(id_, get_metadata)) for id_ in + return {id_: self._sort_key(self.get_value_with_cache(id_, get_metadata)) for id_ in all_book_ids} def iter_searchable_values(self, get_metadata, candidates, default_value=None): diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index a292340013..691a115617 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -558,7 +558,9 @@ class ReadingTest(BaseTest): def test_composites(self): # {{{ cache = self.init_cache() cache.create_custom_column('mult', 'CC1', 'composite', True, display={'composite_template': 'b,a,c'}) - cache.create_custom_column('single', 'CC1', 'composite', False, display={'composite_template': 'b,a,c'}) + cache.create_custom_column('single', 'CC2', 'composite', False, display={'composite_template': 'b,a,c'}) + cache.create_custom_column('number', 'CC3', 'composite', False, display={'composite_template': '{#float}', 'composite_sort':'number'}) + cache.create_custom_column('size', 'CC4', 'composite', False, display={'composite_template': '{#float:human_readable()}', 'composite_sort':'number'}) cache = self.init_cache() # Test searching @@ -566,5 +568,11 @@ class ReadingTest(BaseTest): self.assertEqual(set(), cache.search('#mult:=b,a,c')) self.assertEqual({1,2,3}, cache.search('#single:=b,a,c')) self.assertEqual(set(), cache.search('#single:=b')) + + # Test numeric sorting + cache.set_field('#float', {1:2, 2:10, 3:0.0001}) + self.assertEqual([3, 1, 2], cache.multisort([('#number', True)])) + cache.set_field('#float', {1:3, 2:2*1024, 3:3*1024*1024}) + self.assertEqual([1, 2, 3], cache.multisort([('#size', True)])) # }}}