From b3c42fc0af1c21ad46ac9e54fb61a6278c4a494c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 7 Mar 2011 18:57:31 +0000 Subject: [PATCH] An attempt at S/R for identifiers. --- src/calibre/gui2/dialogs/metadata_bulk.py | 54 ++++++++++++++++++----- src/calibre/gui2/dialogs/metadata_bulk.ui | 52 +++++++++++++++++++++- src/calibre/library/database2.py | 4 ++ 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index af540ea4c3..584b88a097 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -7,7 +7,7 @@ import re, os, inspect from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \ pyqtSignal, QDialogButtonBox, QInputDialog, QLineEdit, \ - QDate + QDate, QCompleter from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.tag_editor import TagEditor @@ -363,7 +363,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): if (f in ['author_sort'] or (fm[f]['datatype'] in ['text', 'series', 'enumeration'] and fm[f].get('search_terms', None) - and f not in ['formats', 'ondevice']) or + and f not in ['formats', 'ondevice', 'id']) or fm[f]['datatype'] in ['int', 'float', 'bool'] ): self.all_fields.append(f) self.writable_fields.append(f) @@ -393,6 +393,11 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.book_1_text.setObjectName(name) self.testgrid.addWidget(w, i+offset, 2, 1, 1) + ident_types = sorted(self.db.get_all_identifier_types(), key=sort_key) + self.s_r_dst_ident.setCompleter(QCompleter(ident_types)) + ident_types.insert(0, '') + self.s_r_src_ident.addItems(ident_types) + self.main_heading = _( 'You can destroy your library using this feature. ' 'Changes are permanent. There is no undo function. ' @@ -449,6 +454,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.test_text.editTextChanged[str].connect(self.s_r_paint_results) self.comma_separated.stateChanged.connect(self.s_r_paint_results) self.case_sensitive.stateChanged.connect(self.s_r_paint_results) + self.s_r_src_ident.currentIndexChanged[int].connect(self.s_r_paint_results) + self.s_r_dst_ident.textChanged.connect(self.s_r_paint_results) self.s_r_template.lost_focus.connect(self.s_r_template_changed) self.central_widget.setCurrentIndex(0) @@ -471,6 +478,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.query_field.addItems(sorted([q for q in self.queries], key=sort_key)) self.query_field.currentIndexChanged[str].connect(self.s_r_query_change) self.query_field.setCurrentIndex(0) + self.search_field.setCurrentIndex(0) + self.s_r_search_field_changed(0) def s_r_sf_itemdata(self, idx): if idx is None: @@ -497,7 +506,11 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): val = str(val) elif fm['is_csp']: # convert the csp dict into a list - val = [u'%s:%s'%(t[0], t[1]) for t in val.iteritems()] + id_type = unicode(self.s_r_src_ident.currentText()) + if id_type: + val = [val.get(id_type, '')] + else: + val = [u'%s:%s'%(t[0], t[1]) for t in val.iteritems()] if val is None: val = [] if fm['is_multiple'] else [''] elif not fm['is_multiple']: @@ -515,12 +528,17 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.s_r_search_field_changed(self.search_field.currentIndex()) def s_r_search_field_changed(self, idx): - if self.search_mode.currentIndex() != 0 and idx == 1: # Template + self.s_r_template.setVisible(False) + self.template_label.setVisible(False) + self.s_r_src_ident_label.setVisible(False) + self.s_r_src_ident.setVisible(False) + if idx == 1: # Template self.s_r_template.setVisible(True) self.template_label.setVisible(True) - else: - self.s_r_template.setVisible(False) - self.template_label.setVisible(False) + elif self.s_r_sf_itemdata(idx) == 'identifiers': + self.s_r_src_ident_label.setVisible(True) + self.s_r_src_ident.setVisible(True) + for i in range(0, self.s_r_number_of_books): w = getattr(self, 'book_%d_text'%(i+1)) mi = self.db.get_metadata(self.ids[i], index_is_id=True) @@ -538,10 +556,15 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.s_r_paint_results(None) def s_r_destination_field_changed(self, idx): + self.s_r_dst_ident_label.setVisible(False) + self.s_r_dst_ident.setVisible(False) txt = self.s_r_df_itemdata(idx) if not txt: txt = self.s_r_sf_itemdata(None) if txt and txt in self.writable_fields: + if txt == 'identifiers': + self.s_r_dst_ident_label.setVisible(True) + self.s_r_dst_ident.setVisible(True) self.destination_field_fm = self.db.metadata_for_field(txt) self.s_r_paint_results(None) @@ -639,8 +662,12 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): if dest_mode != 0: dest_val = mi.get(dest, '') if self.db.metadata_for_field(dest)['is_csp']: - # convert the csp dict into a list - dest_val = [u'%s:%s'%(t[0], t[1]) for t in dest_val.iteritems()] + dst_id_type = unicode(self.s_r_dst_ident.text()) + if dst_id_type: + dest_val = [dest_val.get(dst_id_type, '')] + else: + # convert the csp dict into a list + dest_val = [u'%s:%s'%(t[0], t[1]) for t in dest_val.iteritems()] if dest_val is None: dest_val = [] elif not isinstance(dest_val, list): @@ -726,7 +753,14 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): # convert the colon-separated pair strings back into a dict, which # is what set_identifiers wants if dfm['is_csp']: - val = dict([(t.split(':')) for t in val]) + dst_id_type = unicode(self.s_r_dst_ident.text()) + if dst_id_type: + v = ''.join(val) + ids = mi.get(dest) + ids[dst_id_type] = v + val = ids + else: + val = dict([(t.split(':')) for t in val]) else: val = self.s_r_replace_mode_separator().join(val) if dest == 'title' and len(val) == 0: diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 1654ff8261..75ea1ce8bd 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -732,6 +732,29 @@ Future conversion of these books will use the default settings. + + + + Identifier: + + + s_r_src_ident + + + + + + + + 100 + 0 + + + + Choose which identifier to operate upon + + + @@ -910,7 +933,30 @@ not multiple and the destination field is multiple - + + + + Identifier: + + + s_r_dst_ident + + + + + + + + 100 + 0 + + + + Choose which identifier to operate upon + + + + @@ -996,7 +1042,7 @@ not multiple and the destination field is multiple - + QFrame::NoFrame @@ -1120,6 +1166,7 @@ not multiple and the destination field is multiple remove_button search_field search_mode + s_r_src_ident s_r_template search_for case_sensitive @@ -1128,6 +1175,7 @@ not multiple and the destination field is multiple destination_field replace_mode comma_separated + s_r_dst_ident results_count starting_from multiple_separator diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index f1a5884e9f..3b712e1c10 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -2551,6 +2551,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return ans + def get_all_identifier_types(self): + idents = self.conn.get('SELECT DISTINCT type FROM identifiers') + return [ident[0] for ident in idents] + def _clean_identifier(self, typ, val): typ = icu_lower(typ).strip().replace(':', '').replace(',', '') val = val.strip().replace(',', '|').replace(':', '|')