From a21bf30ff8207ec9a4e644d1ffbfcbee58a346c6 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 08:41:16 +0000 Subject: [PATCH 1/4] 1) finish JSON-storage of template function source code 2) fix for #8388. This fix was chosen because it emulates the behavior in 0.7.38, where get_metadata returned empty author lists --- .../gui2/preferences/template_functions.py | 15 +++++++++++++-- src/calibre/library/database2.py | 5 ++++- src/calibre/library/sqlite.py | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/preferences/template_functions.py b/src/calibre/gui2/preferences/template_functions.py index 2e16b0f4c3..8ffd65b2b5 100644 --- a/src/calibre/gui2/preferences/template_functions.py +++ b/src/calibre/gui2/preferences/template_functions.py @@ -5,7 +5,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import traceback +import json, traceback from calibre.gui2 import error_dialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget @@ -73,6 +73,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.textBrowser.setHtml(help_text) def initialize(self): + try: + with open(P('template-functions.json'), 'rb') as f: + self.builtin_source_dict = json.load(f, encoding='utf-8') + except: + self.builtin_source_dict = {} + self.funcs = formatter_functions.get_functions() self.builtins = formatter_functions.get_builtins() @@ -179,8 +185,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): func = self.funcs[txt] self.argument_count.setValue(func.arg_count) self.documentation.setText(func.doc) - self.program.setPlainText(func.program_text) if txt in self.builtins: + if hasattr(func, 'program_text'): + self.program.setPlainText(func.program_text) + elif txt in self.builtin_source_dict: + self.program.setPlainText(self.builtin_source_dict[txt]) + else: + self.program.setPlainText(_('function source code not available')) self.documentation.setReadOnly(True) self.argument_count.setReadOnly(True) self.program.setReadOnly(True) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3a2109e01e..df094347b8 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -690,7 +690,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): mi = Metadata(None) aut_list = row[fm['au_map']] - aut_list = [p.split(':::') for p in aut_list.split(':#:')] + if not aut_list: + aut_list = [] + else: + aut_list = [p.split(':::') for p in aut_list.split(':#:')] aum = [] aus = {} for (author, author_sort) in aut_list: diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py index 83f19b8711..622d6b8459 100644 --- a/src/calibre/library/sqlite.py +++ b/src/calibre/library/sqlite.py @@ -100,7 +100,7 @@ class AumSortedConcatenate(object): keys = self.ans.keys() l = len(keys) if l == 0: - return 'Unknown:::Unknown' + return None if l == 1: return self.ans[keys[0]] return ':#:'.join([self.ans[v] for v in sorted(keys)]) From e0b2d0b62a6b1f4c4a2f37cdcdffa5e1744b892b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 10:34:05 +0000 Subject: [PATCH 2/4] Add function documentation to template editing dialog (F2 on a composite column) --- src/calibre/gui2/dialogs/template_dialog.py | 40 +++++++++- src/calibre/gui2/dialogs/template_dialog.ui | 81 ++++++++++++++++----- 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 60d4025ef9..62accdc842 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -3,8 +3,11 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __license__ = 'GPL v3' +import json + from PyQt4.Qt import Qt, QDialog, QDialogButtonBox from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog +from calibre.utils.formatter_functions import formatter_functions class TemplateDialog(QDialog, Ui_TemplateDialog): @@ -17,9 +20,44 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint)) self.setWindowIcon(icon) + self.textbox.setTabStopWidth(10) + self.source_code.setTabStopWidth(10) + self.documentation.setReadOnly(True) + self.source_code.setReadOnly(True) + if text is not None: self.textbox.setPlainText(text) - self.textbox.setTabStopWidth(50) self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel')) + try: + with open(P('template-functions.json'), 'rb') as f: + self.builtin_source_dict = json.load(f, encoding='utf-8') + except: + self.builtin_source_dict = {} + + self.funcs = formatter_functions.get_functions() + self.builtins = formatter_functions.get_builtins() + + func_names = sorted(self.funcs) + self.function.clear() + self.function.addItem('') + self.function.addItems(func_names) + self.function.setCurrentIndex(0) + self.function.currentIndexChanged[str].connect(self.function_changed) + + print self.textbox.tabStopWidth() + print self.source_code.tabStopWidth() + + def function_changed(self, toWhat): + name = unicode(toWhat) + self.source_code.clear() + self.documentation.clear() + if name in self.funcs: + self.documentation.setPlainText(self.funcs[name].doc) + if name in self.builtins: + if name in self.builtin_source_dict: + self.source_code.setPlainText(self.builtin_source_dict[name]) + else: + self.source_code.setPlainText(self.funcs[name].program_text) + diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index a30d6ef273..e1980a8397 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 500 - 235 + 588 + 546 @@ -19,21 +19,68 @@ Edit Comments - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Function name: + + + + + + + + + + Documentation: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Python code: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 16777215 + 75 + + + + + + + + + + From 4a9e8bcb2fd0d60438e3265defb358891de6bc75 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 10:48:43 +0000 Subject: [PATCH 3/4] Remove some print statements. --- src/calibre/gui2/dialogs/template_dialog.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 62accdc842..174056ef80 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -46,9 +46,6 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.function.setCurrentIndex(0) self.function.currentIndexChanged[str].connect(self.function_changed) - print self.textbox.tabStopWidth() - print self.source_code.tabStopWidth() - def function_changed(self, toWhat): name = unicode(toWhat) self.source_code.clear() From 5c4154bb0d37a273e4925c2daa4dc15b4e9f752b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 11:26:34 +0000 Subject: [PATCH 4/4] Make date.format_date render UNDEFINED_DATE as ''. This makes composite columns and templates behave in the same fashion as the GUI has for some time. --- src/calibre/utils/date.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index f025a0c9bf..2551b90788 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -148,6 +148,9 @@ def format_date(dt, format, assume_utc=False, as_utc=False): if len(mo.group(0)) == 2: return '%02d'%(dt.year % 100) return '%04d'%dt.year + if dt == UNDEFINED_DATE: + return '' + format = re.sub('d{1,4}', format_day, format) format = re.sub('M{1,4}', format_month, format) return re.sub('yyyy|yy', format_year, format)