From c01b8ea033a2ae92ef011b942080a3f05bb603a4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 2 May 2013 12:31:56 +0530 Subject: [PATCH] Fix TableRow implementation and when iterating over ids iterate in ascending order to match legacy interface --- src/calibre/db/legacy.py | 9 +++++++++ src/calibre/db/tests/legacy.py | 19 +++++++++++++++++++ src/calibre/db/view.py | 23 ++++++++++++++++------- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 36e6fa152d..a367392607 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -11,6 +11,7 @@ from functools import partial from calibre.db.backend import DB from calibre.db.cache import Cache +from calibre.db.categories import CATEGORY_SORTS from calibre.db.view import View from calibre.utils.date import utcnow @@ -20,6 +21,10 @@ class LibraryDatabase(object): PATH_LIMIT = DB.PATH_LIMIT WINDOWS_LIBRARY_PATH_LIMIT = DB.WINDOWS_LIBRARY_PATH_LIMIT + CATEGORY_SORTS = CATEGORY_SORTS + MATCH_TYPE = ('any', 'all') + CUSTOM_DATA_TYPES = frozenset(['rating', 'text', 'comments', 'datetime', + 'int', 'float', 'bool', 'series', 'composite', 'enumeration']) @classmethod def exists_at(cls, path): @@ -148,3 +153,7 @@ class LibraryDatabase(object): os.makedirs(path) return path + def __iter__(self): + for row in self.data.iterall(): + yield row + diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 353e1bc4b5..509137fbac 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -82,6 +82,7 @@ class LegacyTest(BaseTest): # }}} def test_legacy_getters(self): # {{{ + ' Test various functions to get individual bits of metadata ' old = self.init_old() getters = ('path', 'abspath', 'title', 'authors', 'series', 'publisher', 'author_sort', 'authors', 'comments', @@ -89,11 +90,29 @@ class LegacyTest(BaseTest): 'timestamp', 'uuid', 'pubdate', 'ondevice', 'metadata_last_modified', 'languages') oldvals = {g:tuple(getattr(old, g)(x) for x in xrange(3)) + tuple(getattr(old, g)(x, True) for x in (1,2,3)) for g in getters} + old_rows = {tuple(r)[:5] for r in old} old.close() db = self.init_legacy() newvals = {g:tuple(getattr(db, g)(x) for x in xrange(3)) + tuple(getattr(db, g)(x, True) for x in (1,2,3)) for g in getters} + new_rows = {tuple(r)[:5] for r in db} for x in (oldvals, newvals): x['tags'] = tuple(set(y.split(',')) if y else y for y in x['tags']) self.assertEqual(oldvals, newvals) + self.assertEqual(old_rows, new_rows) + # }}} + def test_legacy_coverage(self): # {{{ + ' Check that the emulation of the legacy interface is (almost) total ' + cl = self.cloned_library + db = self.init_old(cl) + ndb = self.init_legacy() + + SKIP_ATTRS = {'TCat_Tag'} + + for attr in dir(db): + if attr in SKIP_ATTRS: + continue + self.assertTrue(hasattr(ndb, attr), 'The attribute %s is missing' % attr) + # obj = getattr(db, attr) + # }}} diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index ff41f20614..3f135860d9 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -29,11 +29,12 @@ class MarkedVirtualField(object): for book_id in candidates: yield self.marked_ids.get(book_id, default_value), {book_id} -class TableRow(list): +class TableRow(object): def __init__(self, book_id, view): self.book_id = book_id self.view = weakref.ref(view) + self.column_count = view.column_count def __getitem__(self, obj): view = self.view() @@ -43,6 +44,13 @@ class TableRow(list): else: return view._field_getters[obj](self.book_id) + def __len__(self): + return self.column_count + + def __iter__(self): + for i in xrange(self.column_count): + yield self[i] + def format_is_multiple(x, sep=',', repl=None): if not x: return None @@ -67,6 +75,7 @@ class View(object): self.search_restriction = self.base_restriction = '' self.search_restriction_name = self.base_restriction_name = '' self._field_getters = {} + self.column_count = len(cache.backend.FIELD_MAP) for col, idx in cache.backend.FIELD_MAP.iteritems(): label, fmt = col, lambda x:x func = { @@ -107,7 +116,7 @@ class View(object): fmt = partial(format_is_multiple, sep=sep) self._field_getters[idx] = partial(func, label, fmt=fmt) if func == self._get else func - self._map = tuple(self.cache.all_book_ids()) + self._map = tuple(sorted(self.cache.all_book_ids())) self._map_filtered = tuple(self._map) def get_property(self, id_or_index, index_is_id=False, loc=-1): @@ -124,21 +133,21 @@ class View(object): return idx if index_is_id else self.index_to_id(idx) def __getitem__(self, row): - return TableRow(self._map_filtered[row], self.cache) + return TableRow(self._map_filtered[row], self) def __len__(self): return len(self._map_filtered) def __iter__(self): for book_id in self._map_filtered: - yield self._data[book_id] + yield TableRow(book_id, self) def iterall(self): - for book_id in self._map: - yield self[book_id] + for book_id in self.iterallids(): + yield TableRow(book_id, self) def iterallids(self): - for book_id in self._map: + for book_id in sorted(self._map): yield book_id def get_field_map_field(self, row, col, index_is_id=True):