From 238e21cb61afda0a6a2728f9a7fb0a944a3a097a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 14 Apr 2013 14:28:54 +0530 Subject: [PATCH 1/2] Start work on legacy interface --- src/calibre/db/backend.py | 22 +++++++--- src/calibre/db/legacy.py | 77 ++++++++++++++++++++++++++++++++++ src/calibre/db/tests/base.py | 18 +++++--- src/calibre/db/tests/legacy.py | 53 +++++++++++++++++++++++ src/calibre/db/view.py | 4 ++ 5 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 src/calibre/db/legacy.py create mode 100644 src/calibre/db/tests/legacy.py diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 40f2159cf4..dab7b1364d 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -306,7 +306,8 @@ class DB(object): # Initialize database {{{ - def __init__(self, library_path, default_prefs=None, read_only=False): + def __init__(self, library_path, default_prefs=None, read_only=False, + restore_all_prefs=False, progress_callback=lambda x, y:True): try: if isbytestring(library_path): library_path = library_path.decode(filesystem_encoding) @@ -377,23 +378,27 @@ class DB(object): UPDATE authors SET sort=author_to_author_sort(name) WHERE sort IS NULL; ''') - self.initialize_prefs(default_prefs) + self.initialize_prefs(default_prefs, restore_all_prefs, progress_callback) self.initialize_custom_columns() self.initialize_tables() - def initialize_prefs(self, default_prefs): # {{{ + def initialize_prefs(self, default_prefs, restore_all_prefs, progress_callback): # {{{ self.prefs = DBPrefs(self) if default_prefs is not None and not self._exists: + progress_callback(None, len(default_prefs)) # Only apply default prefs to a new database - for key in default_prefs: + for i, key in enumerate(default_prefs): # be sure that prefs not to be copied are listed below - if key not in frozenset(['news_to_be_synced']): + if restore_all_prefs or key not in frozenset(['news_to_be_synced']): self.prefs[key] = default_prefs[key] + progress_callback(_('restored preference ') + key, i+1) if 'field_metadata' in default_prefs: fmvals = [f for f in default_prefs['field_metadata'].values() if f['is_custom']] - for f in fmvals: + progress_callback(None, len(fmvals)) + for i, f in enumerate(fmvals): + progress_callback(_('creating custom column ') + f['label'], i) self.create_custom_column(f['label'], f['name'], f['datatype'], (f['is_multiple'] is not None and @@ -774,6 +779,11 @@ class DB(object): self._conn = Connection(self.dbpath) return self._conn + def close(self): + if self._conn is not None: + self._conn.close() + del self._conn + @dynamic_property def user_version(self): doc = 'The user version of this database' diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py new file mode 100644 index 0000000000..643af853b3 --- /dev/null +++ b/src/calibre/db/legacy.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +import os + +from calibre.db.backend import DB +from calibre.db.cache import Cache +from calibre.db.view import View + +class LibraryDatabase(object): + + PATH_LIMIT = DB.PATH_LIMIT + WINDOWS_LIBRARY_PATH_LIMIT = DB.WINDOWS_LIBRARY_PATH_LIMIT + + @classmethod + def exists_at(cls, path): + return path and os.path.exists(os.path.join(path, 'metadata.db')) + + def __init__(self, library_path, + default_prefs=None, read_only=False, is_second_db=False, + progress_callback=lambda x, y:True, restore_all_prefs=False): + + self.is_second_db = is_second_db # TODO: Use is_second_db + + backend = self.backend = DB(library_path, default_prefs=default_prefs, + read_only=read_only, restore_all_prefs=restore_all_prefs, + progress_callback=progress_callback) + cache = Cache(backend) + cache.init() + self.data = View(cache) + + self.get_property = self.data.get_property + self.all_ids = self.data.cache.all_book_ids + + def close(self): + self.backend.close() + + def break_cycles(self): + self.data.cache.backend = None + self.data.cache = None + self.data = self.backend = self.field_metadata = self.prefs = self.listeners = self.refresh_ondevice = None + + # Library wide properties {{{ + @property + def field_metadata(self): + return self.backend.field_metadata + + @property + def user_version(self): + return self.backend.user_version + + @property + def library_id(self): + return self.backend.library_id + + def last_modified(self): + return self.backend.last_modified() + + @property + def custom_column_num_map(self): + return self.backend.custom_column_num_map + + @property + def custom_column_label_map(self): + return self.backend.custom_column_label_map + + @property + def FIELD_MAP(self): + return self.backend.FIELD_MAP + # }}} + + diff --git a/src/calibre/db/tests/base.py b/src/calibre/db/tests/base.py index a1a033f1bf..cc8da89b05 100644 --- a/src/calibre/db/tests/base.py +++ b/src/calibre/db/tests/base.py @@ -16,6 +16,9 @@ rmtree = partial(shutil.rmtree, ignore_errors=True) class BaseTest(unittest.TestCase): + longMessage = True + maxDiff = None + def setUp(self): self.library_path = self.mkdtemp() self.create_db(self.library_path) @@ -40,10 +43,10 @@ class BaseTest(unittest.TestCase): db.conn.close() return dest - def init_cache(self, library_path): + def init_cache(self, library_path=None): from calibre.db.backend import DB from calibre.db.cache import Cache - backend = DB(library_path) + backend = DB(library_path or self.library_path) cache = Cache(backend) cache.init() return cache @@ -53,9 +56,13 @@ class BaseTest(unittest.TestCase): atexit.register(rmtree, ans) return ans - def init_old(self, library_path): + def init_old(self, library_path=None): from calibre.library.database2 import LibraryDatabase2 - return LibraryDatabase2(library_path) + return LibraryDatabase2(library_path or self.library_path) + + def init_legacy(self, library_path=None): + from calibre.db.legacy import LibraryDatabase + return LibraryDatabase(library_path or self.library_path) def clone_library(self, library_path): if not hasattr(self, 'clone_dir'): @@ -81,7 +88,8 @@ class BaseTest(unittest.TestCase): 'ondevice_col', 'last_modified', 'has_cover', 'cover_data'}.union(allfk1) for attr in all_keys: - if attr == 'user_metadata': continue + if attr == 'user_metadata': + continue attr1, attr2 = getattr(mi1, attr), getattr(mi2, attr) if attr == 'formats': attr1, attr2 = map(lambda x:tuple(x) if x else (), (attr1, attr2)) diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py new file mode 100644 index 0000000000..5bb6730bea --- /dev/null +++ b/src/calibre/db/tests/legacy.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +from calibre.db.tests.base import BaseTest + +class LegacyTest(BaseTest): + + ''' Test the emulation of the legacy interface. ''' + + def test_library_wide_properties(self): # {{{ + 'Test library wide properties' + old = self.init_old() + props = ('user_version', 'is_second_db', 'library_id', 'field_metadata', + 'custom_column_label_map', 'custom_column_num_map') + oldvals = {x:getattr(old, x) for x in props} + oldvals['last_modified'] = old.last_modified() + old.close() + old = None + db = self.init_legacy() + newvals = {x:getattr(db, x) for x in props} + newvals['last_modified'] = db.last_modified() + self.assertEqual(oldvals, newvals) + db.close() + # }}} + + def test_get_property(self): # {{{ + 'Test the get_property interface for reading data' + def get_values(db): + ans = {} + for label, loc in db.FIELD_MAP.iteritems(): + if isinstance(label, int): + label = '#'+db.custom_column_num_map[label]['label'] + label = type('')(label) + ans[label] = tuple(db.get_property(i, index_is_id=True, loc=loc) + for i in db.all_ids()) + return ans + + old = self.init_old() + old_vals = get_values(old) + old.close() + old = None + db = self.init_legacy() + new_vals = get_values(db) + db.close() + self.assertEqual(old_vals, new_vals) + + # }}} + diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index 507e309e7e..c4d0e382a8 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -72,6 +72,10 @@ class View(object): self._map = tuple(self.cache.all_book_ids()) self._map_filtered = tuple(self._map) + def get_property(self, id_or_index, index_is_id=False, loc=-1): + book_id = id_or_index if index_is_id else self._map_filtered[id_or_index] + return self._field_getters[loc](book_id) + @property def field_metadata(self): return self.cache.field_metadata From b9a0f3bd182dec5e33a1a9a302190157ad4bd806 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 14 Apr 2013 14:58:27 +0530 Subject: [PATCH 2/2] ... --- src/calibre/gui2/search_restriction_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index 02f9bb4fea..e797e95869 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -117,7 +117,7 @@ class CreateVirtualLibrary(QDialog): # {{{ '{1}, ' '{2}, ' '{3}, ' - '{4} ').format(_('Authors'), _('Tags'), + '{4}.').format(_('Authors'), _('Tags'), _('Publishers'), _('Series'), _('Saved Searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse)