When copying books from library A to library B, check that B contains the necessary custom columns. If not, tell the user and allow them to create any non-conflicting columns. Inform them about conflicting columns.

This commit is contained in:
Charles Haley 2013-09-15 14:24:17 +02:00
parent 3d12ae2e9e
commit 7d77e8fe98

View File

@ -10,7 +10,8 @@ from functools import partial
from threading import Thread
from contextlib import closing
from PyQt4.Qt import (QToolButton, QDialog, QGridLayout, QIcon, QLabel, QDialogButtonBox)
from PyQt4.Qt import (QToolButton, QDialog, QGridLayout, QIcon, QLabel, QDialogButtonBox,
QFormLayout, QCheckBox)
from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import (error_dialog, Dispatcher, warning_dialog, gprefs,
@ -19,6 +20,8 @@ from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.widgets import HistoryLineEdit
from calibre.utils.config import prefs, tweaks
from calibre.utils.date import now
from calibre.utils.icu import sort_key
class Worker(Thread): # {{{
@ -47,11 +50,11 @@ class Worker(Thread): # {{{
self.done()
def add_formats(self, id, paths, newdb, replace=True):
def add_formats(self, id_, paths, newdb, replace=True):
for path in paths:
fmt = os.path.splitext(path)[-1].replace('.', '').upper()
with open(path, 'rb') as f:
newdb.add_format(id, fmt, f, index_is_id=True,
newdb.add_format(id_, fmt, f, index_is_id=True,
notify=False, replace=replace)
def doit(self):
@ -174,6 +177,10 @@ class ChooseLibrary(QDialog): # {{{
return (unicode(self.le.text()), self.delete_after_copy)
# }}}
# Static session-long set of pairs of libraries that have had their custom columns
# checked for compatibility
libraries_with_checked_columns = {}
class CopyToLibraryAction(InterfaceAction):
name = 'Copy To Library'
@ -231,6 +238,11 @@ class CopyToLibraryAction(InterfaceAction):
_('Cannot copy to current library.'), show=True)
self.copy_to_library(path, delete_after)
def _column_is_compatible(self, source_metadata, dest_metadata):
return (source_metadata['datatype'] == dest_metadata['datatype'] and
(source_metadata['datatype'] != 'text' or
source_metadata['is_multiple'] == dest_metadata['is_multiple']))
def copy_to_library(self, loc, delete_after=False):
rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
@ -252,6 +264,39 @@ class CopyToLibraryAction(InterfaceAction):
self.pd.set_msg(title)
self.pd.set_value(idx)
# Open the new db so we can check the custom columns.
global libraries_with_checked_columns
if db.library_id not in libraries_with_checked_columns:
libraries_with_checked_columns[db.library_id] = set()
from calibre.db.legacy import LibraryDatabase
newdb = LibraryDatabase(loc, is_second_db=True)
continue_processing = True;
with closing(newdb):
if newdb.library_id not in libraries_with_checked_columns[db.library_id]:
newdb_meta = newdb.field_metadata.custom_field_metadata()
incompatible_columns = []
missing_columns = []
for k, m in db.field_metadata.custom_iteritems():
if k not in newdb_meta:
missing_columns.append(k)
elif not self._column_is_compatible(m, newdb_meta[k]):
incompatible_columns.append(k)
if missing_columns or incompatible_columns:
continue_processing = self.custom_column_dialog(db, newdb,
missing_columns, incompatible_columns)
if continue_processing:
libraries_with_checked_columns[db.library_id].add(newdb.library_id)
newdb.break_cycles()
del newdb
if not continue_processing:
return;
self.worker = Worker(ids, db, loc, Dispatcher(progress),
Dispatcher(self.pd.accept), delete_after)
self.worker.start()
@ -295,4 +340,63 @@ class CopyToLibraryAction(InterfaceAction):
_('You cannot use other libraries while using the environment'
' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
def custom_column_dialog(self, db, newdb, missing_cols, incompatible_cols):
source_metadata = db.field_metadata.custom_field_metadata(include_composites=True)
d = QDialog(self.gui)
d.setWindowTitle(_('Create link'))
l = QFormLayout()
d.setLayout(l)
d.setMinimumWidth(600)
d.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
d.la = la = QLabel(_(
'The custom columns in the source library are different from the '
'custom columns in the destination library. Incompatible columns '
'are columns with the same lookup key but different column '
'types. These cannot be copied. Missing columns are columns '
'in the source library but not in the destination library. '
'If you check the "Create" box, these columns will be added '
'to the destination and their values copied with the books.'))
la.setWordWrap(True)
la.setStyleSheet('QLabel { margin-bottom: 1.5ex }')
l.setWidget(0, l.SpanningRole, la)
if incompatible_cols:
l.addRow(_('Incompatible custom columns:'),
QLabel(', '.join(sorted(incompatible_cols, key=sort_key))))
incompatible_cols_widgets = []
if missing_cols:
l.addRow(QLabel(_('Missing custom columns')), QLabel(''))
for k in missing_cols:
widgets = (k, QCheckBox(_('Add column to new library')))
l.addRow(QLabel(k), widgets[1])
incompatible_cols_widgets.append(widgets)
l.addRow(d.bb)
d.bb.accepted.connect(d.accept)
d.bb.rejected.connect(d.reject)
d.resize(d.sizeHint())
if d.exec_() == d.Accepted:
count = 0
for k,cb in incompatible_cols_widgets:
if cb.isChecked():
count += 1
if count:
pd = ProgressDialog(_('Creating custom columns'), min=0, max=count,
parent=self.gui, cancelable=False)
pd.show()
done_count = 0
for k,cb in incompatible_cols_widgets:
if cb.isChecked():
pd.set_value(done_count)
pd.set_msg(_('Creating column {0}').format(k))
done_count += 1
col_meta = source_metadata[k]
newdb.create_custom_column(
col_meta['label'], col_meta['name'], col_meta['datatype'],
len(col_meta['is_multiple']) > 0,
col_meta['is_editable'], col_meta['display'])
pd.done(0)
return True
return False