mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Playing with search & replace. Added 'global' template values to the replace expression. Also fixed some problems with exceptions, and problems with case-insensitive matching in the history boxes.
This commit is contained in:
parent
c59545a96a
commit
ed7597ae5f
@ -12,6 +12,7 @@ from calibre.ebooks.metadata.book import SC_COPYABLE_FIELDS
|
|||||||
from calibre.ebooks.metadata.book import SC_FIELDS_COPY_NOT_NULL
|
from calibre.ebooks.metadata.book import SC_FIELDS_COPY_NOT_NULL
|
||||||
from calibre.ebooks.metadata.book import STANDARD_METADATA_FIELDS
|
from calibre.ebooks.metadata.book import STANDARD_METADATA_FIELDS
|
||||||
from calibre.ebooks.metadata.book import TOP_LEVEL_CLASSIFIERS
|
from calibre.ebooks.metadata.book import TOP_LEVEL_CLASSIFIERS
|
||||||
|
from calibre.ebooks.metadata.book import ALL_METADATA_FIELDS
|
||||||
from calibre.library.field_metadata import FieldMetadata
|
from calibre.library.field_metadata import FieldMetadata
|
||||||
from calibre.utils.date import isoformat, format_date
|
from calibre.utils.date import isoformat, format_date
|
||||||
|
|
||||||
@ -131,6 +132,14 @@ class Metadata(object):
|
|||||||
def set(self, field, val, extra=None):
|
def set(self, field, val, extra=None):
|
||||||
self.__setattr__(field, val, extra)
|
self.__setattr__(field, val, extra)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_keys(self):
|
||||||
|
'''
|
||||||
|
All attribute keys known by this instance, even if their value is None
|
||||||
|
'''
|
||||||
|
_data = object.__getattribute__(self, '_data')
|
||||||
|
return frozenset(ALL_METADATA_FIELDS.union(_data['user_metadata'].iterkeys()))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_metadata_keys(self):
|
def user_metadata_keys(self):
|
||||||
'The set of user metadata names this object knows about'
|
'The set of user metadata names this object knows about'
|
||||||
|
@ -4,15 +4,15 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
'''Dialog to edit metadata in bulk'''
|
'''Dialog to edit metadata in bulk'''
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import re
|
import re, string
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QGridLayout
|
from PyQt4.Qt import Qt, QDialog, QGridLayout
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.ebooks.metadata import string_to_authors, \
|
from calibre.ebooks.metadata import string_to_authors, \
|
||||||
authors_to_string
|
authors_to_string, MetaInformation
|
||||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||||
from calibre.gui2.dialogs.progress import BlockingBusy
|
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||||
from calibre.gui2 import error_dialog, Dispatcher
|
from calibre.gui2 import error_dialog, Dispatcher
|
||||||
@ -99,6 +99,26 @@ class Worker(Thread):
|
|||||||
|
|
||||||
self.callback()
|
self.callback()
|
||||||
|
|
||||||
|
class SafeFormat(string.Formatter):
|
||||||
|
'''
|
||||||
|
Provides a format function that substitutes '' for any missing value
|
||||||
|
'''
|
||||||
|
def get_value(self, key, args, vals):
|
||||||
|
v = vals.get(key, None)
|
||||||
|
if v is None:
|
||||||
|
return ''
|
||||||
|
if isinstance(v, (tuple, list)):
|
||||||
|
v = ','.join(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
composite_formatter = SafeFormat()
|
||||||
|
|
||||||
|
def format_composite(x, mi):
|
||||||
|
try:
|
||||||
|
ans = composite_formatter.vformat(x, [], mi).strip()
|
||||||
|
except:
|
||||||
|
ans = x
|
||||||
|
return ans
|
||||||
|
|
||||||
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||||
|
|
||||||
@ -163,7 +183,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.s_r_number_of_books = min(7, len(self.ids))
|
self.s_r_number_of_books = min(7, len(self.ids))
|
||||||
for i in range(1,self.s_r_number_of_books+1):
|
for i in range(1,self.s_r_number_of_books+1):
|
||||||
w = QtGui.QLabel(self.tabWidgetPage3)
|
w = QtGui.QLabel(self.tabWidgetPage3)
|
||||||
w.setText(_('Book %d:'%i))
|
w.setText(_('Book %d:')%i)
|
||||||
self.gridLayout1.addWidget(w, i+offset, 0, 1, 1)
|
self.gridLayout1.addWidget(w, i+offset, 0, 1, 1)
|
||||||
w = QtGui.QLineEdit(self.tabWidgetPage3)
|
w = QtGui.QLineEdit(self.tabWidgetPage3)
|
||||||
w.setReadOnly(True)
|
w.setReadOnly(True)
|
||||||
@ -205,6 +225,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.test_text.editTextChanged[str].connect(self.s_r_paint_results)
|
self.test_text.editTextChanged[str].connect(self.s_r_paint_results)
|
||||||
self.central_widget.setCurrentIndex(0)
|
self.central_widget.setCurrentIndex(0)
|
||||||
|
|
||||||
|
self.search_for.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||||
|
self.replace_with.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||||
|
|
||||||
|
|
||||||
def s_r_field_changed(self, txt):
|
def s_r_field_changed(self, txt):
|
||||||
txt = unicode(txt)
|
txt = unicode(txt)
|
||||||
for i in range(0, self.s_r_number_of_books):
|
for i in range(0, self.s_r_number_of_books):
|
||||||
@ -220,6 +244,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
if val:
|
if val:
|
||||||
val.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
val.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||||
val = val[0]
|
val = val[0]
|
||||||
|
if txt == 'authors':
|
||||||
|
val = val.replace('|', ',')
|
||||||
else:
|
else:
|
||||||
val = ''
|
val = ''
|
||||||
else:
|
else:
|
||||||
@ -239,37 +265,55 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
for i in range(0,self.s_r_number_of_books):
|
for i in range(0,self.s_r_number_of_books):
|
||||||
getattr(self, 'book_%d_result'%(i+1)).setText('')
|
getattr(self, 'book_%d_result'%(i+1)).setText('')
|
||||||
|
|
||||||
|
field_match_re = re.compile(r'(^|[^\\])(\\g<)([^>]+)(>)')
|
||||||
|
|
||||||
def s_r_func(self, match):
|
def s_r_func(self, match):
|
||||||
rf = self.s_r_functions[unicode(self.replace_func.currentText())]
|
rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
|
||||||
rv = unicode(self.replace_with.text())
|
rtext = unicode(self.replace_with.text())
|
||||||
val = match.expand(rv)
|
mi_data = self.mi.get_all_non_none_attributes()
|
||||||
return rf(val)
|
|
||||||
|
def fm_func(m):
|
||||||
|
try:
|
||||||
|
if m.group(3) not in self.mi.all_keys: return m.group(0)
|
||||||
|
else: return '%s{%s}'%(m.group(1), m.group(3))
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return m.group(0)
|
||||||
|
|
||||||
|
rtext = re.sub(self.field_match_re, fm_func, rtext)
|
||||||
|
rtext = match.expand(rtext)
|
||||||
|
rtext = format_composite(rtext, mi_data)
|
||||||
|
return rfunc(rtext)
|
||||||
|
|
||||||
def s_r_paint_results(self, txt):
|
def s_r_paint_results(self, txt):
|
||||||
self.s_r_error = None
|
self.s_r_error = None
|
||||||
self.s_r_set_colors()
|
self.s_r_set_colors()
|
||||||
try:
|
try:
|
||||||
self.s_r_obj = re.compile(unicode(self.search_for.text()))
|
self.s_r_obj = re.compile(unicode(self.search_for.text()))
|
||||||
except re.error as e:
|
except Exception as e:
|
||||||
self.s_r_obj = None
|
self.s_r_obj = None
|
||||||
self.s_r_error = e
|
self.s_r_error = e
|
||||||
self.s_r_set_colors()
|
self.s_r_set_colors()
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
self.mi = MetaInformation(None, None)
|
||||||
self.test_result.setText(self.s_r_obj.sub(self.s_r_func,
|
self.test_result.setText(self.s_r_obj.sub(self.s_r_func,
|
||||||
unicode(self.test_text.text())))
|
unicode(self.test_text.text())))
|
||||||
except re.error as e:
|
except Exception as e:
|
||||||
self.s_r_error = e
|
self.s_r_error = e
|
||||||
self.s_r_set_colors()
|
self.s_r_set_colors()
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(0,self.s_r_number_of_books):
|
for i in range(0,self.s_r_number_of_books):
|
||||||
|
id = self.ids[i]
|
||||||
|
self.mi = self.db.get_metadata(id, index_is_id=True)
|
||||||
wt = getattr(self, 'book_%d_text'%(i+1))
|
wt = getattr(self, 'book_%d_text'%(i+1))
|
||||||
wr = getattr(self, 'book_%d_result'%(i+1))
|
wr = getattr(self, 'book_%d_result'%(i+1))
|
||||||
try:
|
try:
|
||||||
wr.setText(self.s_r_obj.sub(self.s_r_func, unicode(wt.text())))
|
wr.setText(self.s_r_obj.sub(self.s_r_func, unicode(wt.text())))
|
||||||
except re.error as e:
|
except Exception as e:
|
||||||
self.s_r_error = e
|
self.s_r_error = e
|
||||||
self.s_r_set_colors()
|
self.s_r_set_colors()
|
||||||
break
|
break
|
||||||
@ -303,6 +347,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
# The standard tags and authors values want to be lists.
|
# The standard tags and authors values want to be lists.
|
||||||
# All custom columns are to be strings
|
# All custom columns are to be strings
|
||||||
val = fm['is_multiple'].join(val)
|
val = fm['is_multiple'].join(val)
|
||||||
|
elif field == 'authors':
|
||||||
|
val = [v.replace('|', ',') for v in val]
|
||||||
else:
|
else:
|
||||||
val = apply_pattern(val)
|
val = apply_pattern(val)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user