Restore custom columns from db prefs instead of trying to aggregate them from the OPFs when doing a library restore

This commit is contained in:
Kovid Goyal 2012-06-20 13:29:09 +05:30
commit 222dbbd3c9
4 changed files with 54 additions and 28 deletions

View File

@ -100,9 +100,7 @@ def restore_database(db, parent=None):
'the database from the individual book ' 'the database from the individual book '
'metadata. This is useful if the ' 'metadata. This is useful if the '
'database has been corrupted and you get a ' 'database has been corrupted and you get a '
'blank list of books. Note that restoring only ' 'blank list of books.'
'restores books, not any settings stored in the '
'database, or any custom recipes.'
'<p>Do you want to restore the database?')): '<p>Do you want to restore the database?')):
return False return False
db.conn.close() db.conn.close()

View File

@ -162,7 +162,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return path and os.path.exists(os.path.join(path, 'metadata.db')) return path and os.path.exists(os.path.join(path, 'metadata.db'))
def __init__(self, library_path, row_factory=False, default_prefs=None, def __init__(self, library_path, row_factory=False, default_prefs=None,
read_only=False, is_second_db=False): read_only=False, is_second_db=False, progress_callback=None,
restore_all_prefs=False):
self.is_second_db = is_second_db self.is_second_db = is_second_db
try: try:
if isbytestring(library_path): if isbytestring(library_path):
@ -205,15 +206,21 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# if we are to copy the prefs and structure from some other DB, then # if we are to copy the prefs and structure from some other DB, then
# we need to do it before we call initialize_dynamic # we need to do it before we call initialize_dynamic
if apply_default_prefs and default_prefs is not None: if apply_default_prefs and default_prefs is not None:
if progress_callback is None:
progress_callback = lambda x, y: True
dbprefs = DBPrefs(self) dbprefs = DBPrefs(self)
for key in default_prefs: progress_callback(None, len(default_prefs))
for i, key in enumerate(default_prefs):
# be sure that prefs not to be copied are listed below # be sure that prefs not to be copied are listed below
if key in frozenset(['news_to_be_synced']): if not restore_all_prefs and key in frozenset(['news_to_be_synced']):
continue continue
dbprefs[key] = default_prefs[key] dbprefs[key] = default_prefs[key]
progress_callback(_('restored preference ') + key, i+1)
if 'field_metadata' in default_prefs: if 'field_metadata' in default_prefs:
fmvals = [f for f in default_prefs['field_metadata'].values() if f['is_custom']] 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'], self.create_custom_column(f['label'], f['name'], f['datatype'],
f['is_multiple'] is not None and len(f['is_multiple']) > 0, f['is_multiple'] is not None and len(f['is_multiple']) > 0,
f['is_editable'], f['display']) f['is_editable'], f['display'])

View File

@ -80,21 +80,27 @@ class DBPrefs(dict):
import traceback import traceback
traceback.print_exc() traceback.print_exc()
def read_serialized(self, library_path): @classmethod
def read_serialized(cls, library_path, recreate_prefs=False):
try: try:
from_filename = os.path.join(library_path, from_filename = os.path.join(library_path,
'metadata_db_prefs_backup.json') 'metadata_db_prefs_backup.json')
with open(from_filename, "rb") as f: with open(from_filename, "rb") as f:
self.clear()
d = json.load(f, object_hook=from_json) d = json.load(f, object_hook=from_json)
self.db.conn.execute('DELETE FROM preferences') if not recreate_prefs:
return d
cls.clear()
cls.db.conn.execute('DELETE FROM preferences')
for k,v in d.iteritems(): for k,v in d.iteritems():
raw = self.to_raw(v) raw = cls.to_raw(v)
self.db.conn.execute( cls.db.conn.execute(
'INSERT INTO preferences (key,val) VALUES (?,?)', (k, raw)) 'INSERT INTO preferences (key,val) VALUES (?,?)', (k, raw))
self.db.conn.commit() cls.db.conn.commit()
self.clear() cls.clear()
self.update(d) cls.update(d)
return d
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
raise
return None

View File

@ -12,6 +12,7 @@ from operator import itemgetter
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
from calibre.library.database2 import LibraryDatabase2 from calibre.library.database2 import LibraryDatabase2
from calibre.library.prefs import DBPrefs
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
from calibre.utils.date import utcfromtimestamp from calibre.utils.date import utcfromtimestamp
from calibre import isbytestring from calibre import isbytestring
@ -101,8 +102,14 @@ class Restore(Thread):
with TemporaryDirectory('_library_restore') as tdir: with TemporaryDirectory('_library_restore') as tdir:
self.library_path = tdir self.library_path = tdir
self.scan_library() self.scan_library()
self.load_preferences() if not self.load_preferences():
self.create_cc_metadata() # Something went wrong with preferences restore. Start over
# with a new database and attempt to rebuild the structure
# from the metadata in the opf
dbpath = os.path.join(self.library_path, 'metadata.db')
if os.path.exists(dbpath):
os.remove(dbpath)
self.create_cc_metadata()
self.restore_books() self.restore_books()
if self.successes == 0 and len(self.dirs) > 0: if self.successes == 0 and len(self.dirs) > 0:
raise Exception(('Something bad happened')) raise Exception(('Something bad happened'))
@ -112,21 +119,29 @@ class Restore(Thread):
def load_preferences(self): def load_preferences(self):
self.progress_callback(None, 1) self.progress_callback(None, 1)
self.progress_callback('Starting restore preferences', 0) self.progress_callback(_('Starting restoring preferences and column metadata'), 0)
dbpath = os.path.join(self.src_library_path, 'metadata_db_prefs_backup.json') prefs_path = os.path.join(self.src_library_path, 'metadata_db_prefs_backup.json')
ndbpath = os.path.join(self.library_path, 'metadata_db_prefs_backup.json') if not os.path.exists(prefs_path):
if not os.path.exists(dbpath): self.progress_callback(_('Cannot restore preferences. Backup file not found.'), 1)
self.progress_callback('Cannot restore preferences. Backup file not found.', 1) return False
return
try: try:
shutil.copyfile(dbpath, ndbpath) prefs = DBPrefs.read_serialized(self.src_library_path, recreate_prefs=False)
db = RestoreDatabase(self.library_path) db = RestoreDatabase(self.library_path, default_prefs=prefs,
db.prefs.read_serialized(self.library_path) restore_all_prefs=True,
progress_callback=self.progress_callback)
db.commit() db.commit()
db.conn.close() db.conn.close()
self.progress_callback('Finished restore preferences', 1) self.progress_callback(None, 1)
if 'field_metadata' in prefs:
self.progress_callback(_('Finished restoring preferences and column metadata'), 1)
return True
self.progress_callback(_('Finished restoring preferences'), 1)
return False
except: except:
self.progress_callback('Restoring preferences failed', 1) traceback.print_exc()
self.progress_callback(None, 1)
self.progress_callback(_('Restoring preferences and column metadata failed'), 0)
return False
def scan_library(self): def scan_library(self):
for dirpath, dirnames, filenames in os.walk(self.src_library_path): for dirpath, dirnames, filenames in os.walk(self.src_library_path):