From 467dcf1a8711969358a7758c7c9a0e620a3d5b99 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 19 Jul 2013 12:11:07 +0530 Subject: [PATCH] API coverage is now 100% --- src/calibre/db/backend.py | 53 ++++++++++++++++++++++++++++++ src/calibre/db/cache.py | 12 +++++++ src/calibre/db/legacy.py | 2 ++ src/calibre/db/tests/filesystem.py | 17 ++++++++++ src/calibre/db/tests/legacy.py | 1 + src/calibre/library/database2.py | 6 ++-- 6 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index fc7d556dc0..4f33a917fa 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -1452,6 +1452,59 @@ class DB(object): options = [(book_id, fmt.upper(), buffer(cPickle.dumps(data, -1))) for book_id, data in options.iteritems()] self.conn.executemany('INSERT OR REPLACE INTO conversion_options(book,format,data) VALUES (?,?,?)', options) + def get_top_level_move_items(self, all_paths): + items = set(os.listdir(self.library_path)) + paths = set(all_paths) + paths.update({'metadata.db', 'metadata_db_prefs_backup.json'}) + path_map = {x:x for x in paths} + if not self.is_case_sensitive: + for x in items: + path_map[x.lower()] = x + items = set(path_map) + paths = {x.lower() for x in paths} + items = items.intersection(paths) + return items, path_map + + def move_library_to(self, all_paths, newloc, progress=lambda x: x): + if not os.path.exists(newloc): + os.makedirs(newloc) + old_dirs = set() + items, path_map = self.get_top_level_move_items(all_paths) + for x in items: + src = os.path.join(self.library_path, x) + dest = os.path.join(newloc, path_map[x]) + if os.path.isdir(src): + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(src, dest) + old_dirs.add(src) + else: + if os.path.exists(dest): + os.remove(dest) + shutil.copyfile(src, dest) + x = path_map[x] + if not isinstance(x, unicode): + x = x.decode(filesystem_encoding, 'replace') + progress(x) + + dbpath = os.path.join(newloc, os.path.basename(self.dbpath)) + opath = self.dbpath + self.conn.close() + self.library_path, self.dbpath = newloc, dbpath + if self._conn is not None: + self._conn.close() + self._conn = None + self.conn + try: + os.unlink(opath) + except: + pass + for loc in old_dirs: + try: + shutil.rmtree(loc) + except: + pass + # }}} diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 19e8d52134..4faa819b42 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -1504,6 +1504,18 @@ class Cache(object): identical_book_ids.add(book_id) return identical_book_ids + @read_api + def get_top_level_move_items(self): + all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()} + return self.backend.get_top_level_move_items(all_paths) + + @write_api + def move_library_to(self, newloc, progress=None): + if progress is None: + progress = lambda x:x + all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()} + self.backend.move_library_to(all_paths, newloc, progress=progress) + # }}} class SortKey(object): # {{{ diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 48d1c8b78c..97babb3b60 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -747,6 +747,7 @@ LibraryDatabase.get_books_for_category = MT( lambda self, category, id_:self.new_api.get_books_for_category(category, id_)) LibraryDatabase.get_data_as_dict = MT(get_data_as_dict) LibraryDatabase.find_identical_books = MT(lambda self, mi:self.new_api.find_identical_books(mi)) +LibraryDatabase.get_top_level_move_items = MT(lambda self:self.new_api.get_top_level_move_items()) # }}} # Legacy setter API {{{ @@ -878,6 +879,7 @@ for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'): return func setattr(LibraryDatabase, meth, MT(getter(meth))) +LibraryDatabase.move_library_to = MT(lambda self, newloc, progress=None:self.new_api.move_library_to(newloc, progress=progress)) # Cleaning is not required anymore LibraryDatabase.clean = LibraryDatabase.clean_custom = MT(lambda self:None) LibraryDatabase.clean_standard_field = MT(lambda self, field, commit=False:None) diff --git a/src/calibre/db/tests/filesystem.py b/src/calibre/db/tests/filesystem.py index 168eec53a4..c99f5ad512 100644 --- a/src/calibre/db/tests/filesystem.py +++ b/src/calibre/db/tests/filesystem.py @@ -79,4 +79,21 @@ class FilesystemTest(BaseTest): f.close() self.assertNotEqual(cache.field_for('title', 1), 'Moved', 'Title was changed despite file lock') + def test_library_move(self): + ' Test moving of library ' + from calibre.ptempfile import TemporaryDirectory + cache = self.init_cache() + self.assertIn('metadata.db', cache.get_top_level_move_items()[0]) + all_ids = cache.all_book_ids() + fmt1 = cache.format(1, 'FMT1') + cov = cache.cover(1) + with TemporaryDirectory('moved_lib') as tdir: + cache.move_library_to(tdir) + self.assertIn('moved_lib', cache.backend.library_path) + self.assertIn('moved_lib', cache.backend.dbpath) + cache.reload_from_db() + self.assertEqual(all_ids, cache.all_book_ids()) + self.assertEqual(fmt1, cache.format(1, 'FMT1')) + self.assertEqual(cov, cache.cover(1)) + cache.backend.close() diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index d3a672bee5..3e1a1457b3 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -167,6 +167,7 @@ class LegacyTest(BaseTest): for meth, args in { 'find_identical_books': [(Metadata('title one', ['author one']),), (Metadata('unknown'),), (Metadata('xxxx'),)], + 'get_top_level_move_items': [()], 'get_books_for_category': [('tags', newstag), ('#formats', 'FMT1')], 'get_next_series_num_for': [('A Series One',)], 'get_id_from_uuid':[('ddddd',), (db.uuid(1, True),)], diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index e0cd9c613e..9022605024 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -3561,7 +3561,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): path = self.path(x, index_is_id=True) path = path.split(os.sep)[0] paths.add(path) - paths.add('metadata.db') + paths.update({'metadata.db', 'metadata_db_prefs_backup.json'}) path_map = {} for x in paths: path_map[x] = x @@ -3573,7 +3573,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): items = items.intersection(paths) return items, path_map - def move_library_to(self, newloc, progress=lambda x: x): + def move_library_to(self, newloc, progress=None): + if progress is None: + progress = lambda x:x if not os.path.exists(newloc): os.makedirs(newloc) old_dirs = set([])