From 65a79c7dd8e533742ce9f2659666239c95e359c1 Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Mon, 24 Oct 2022 22:56:55 +0100 Subject: [PATCH] Add support for CITE plugins Provide a single default plugin that replicates the existing functionality. --- gramps/gen/config.py | 1 + gramps/gen/plug/_manager.py | 5 ++ gramps/gen/plug/_pluginreg.py | 11 ++- gramps/gen/plug/report/endnotes.py | 69 ++------------- gramps/gen/plug/utils.py | 21 +++++ gramps/gui/configure.py | 42 ++++++++++ gramps/plugins/cite/__init__.py | 0 gramps/plugins/cite/cite.gpr.py | 43 ++++++++++ gramps/plugins/cite/default.py | 129 +++++++++++++++++++++++++++++ po/POTFILES.in | 2 + po/POTFILES.skip | 4 + 11 files changed, 264 insertions(+), 63 deletions(-) create mode 100644 gramps/plugins/cite/__init__.py create mode 100644 gramps/plugins/cite/cite.gpr.py create mode 100644 gramps/plugins/cite/default.py diff --git a/gramps/gen/config.py b/gramps/gen/config.py index 5ee96d440..2faee1c12 100644 --- a/gramps/gen/config.py +++ b/gramps/gen/config.py @@ -289,6 +289,7 @@ register('preferences.last-views', []) register('preferences.family-relation-type', 3) # UNKNOWN register('preferences.age-display-precision', 1) register('preferences.age-after-death', True) +register('preferences.cite-plugin', 'cite-default') register('colors.scheme', 0) register('colors.male-alive', ['#b8cee6', '#1f344a']) diff --git a/gramps/gen/plug/_manager.py b/gramps/gen/plug/_manager.py index 6a724378a..c3e0fed48 100644 --- a/gramps/gen/plug/_manager.py +++ b/gramps/gen/plug/_manager.py @@ -458,6 +458,11 @@ class BasePluginManager: """ return self.__pgr.thumbnailer_plugins() + def get_reg_cite(self): + """ Return list of registered cite plugins. + """ + return self.__pgr.cite_plugins() + def get_external_opt_dict(self): """ Return the dictionary of external options. """ return self.__external_opt_dict diff --git a/gramps/gen/plug/_pluginreg.py b/gramps/gen/plug/_pluginreg.py index f7ec9e2fd..5ea3cf7db 100644 --- a/gramps/gen/plug/_pluginreg.py +++ b/gramps/gen/plug/_pluginreg.py @@ -90,9 +90,10 @@ SIDEBAR = 11 DATABASE = 12 RULE = 13 THUMBNAILER = 14 +CITE = 15 PTYPE = [REPORT, QUICKREPORT, TOOL, IMPORT, EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, RELCALC, GRAMPLET, SIDEBAR, DATABASE, RULE, - THUMBNAILER] + THUMBNAILER, CITE] PTYPE_STR = { REPORT: _('Report') , QUICKREPORT: _('Quickreport'), @@ -109,6 +110,7 @@ PTYPE_STR = { DATABASE: _('Database'), RULE: _('Rule'), THUMBNAILER: _('Thumbnailer'), + CITE: _('Citation formatter'), } #possible report categories @@ -1201,6 +1203,7 @@ def make_environment(**kwargs): 'GRAMPLET': GRAMPLET, 'SIDEBAR': SIDEBAR, 'THUMBNAILER': THUMBNAILER, + 'CITE': CITE, 'CATEGORY_TEXT': CATEGORY_TEXT, 'CATEGORY_DRAW': CATEGORY_DRAW, 'CATEGORY_CODE': CATEGORY_CODE, @@ -1529,6 +1532,12 @@ class PluginRegister: """ return self.type_plugins(THUMBNAILER) + def cite_plugins(self): + """ + Return a list of :class:`PluginData` that are of type CITE + """ + return self.type_plugins(CITE) + def filter_load_on_reg(self): """ Return a list of :class:`PluginData` that have load_on_reg == True diff --git a/gramps/gen/plug/report/endnotes.py b/gramps/gen/plug/report/endnotes.py index 8573b33dd..ef5fca5f9 100644 --- a/gramps/gen/plug/report/endnotes.py +++ b/gramps/gen/plug/report/endnotes.py @@ -43,6 +43,8 @@ _ = glocale.translation.gettext from ..docgen import FontStyle, ParagraphStyle, FONT_SANS_SERIF from ...lib import NoteType, Citation from ...utils.string import conf_strings +from ...config import config +from ..utils import get_cite def add_endnote_styles(style_sheet): """ @@ -157,85 +159,28 @@ def write_endnotes(bibliography, database, doc, printnotes=False, links=False, doc.end_paragraph() cindex = 0 - for citation in bibliography.get_citation_list(): + cite = get_cite() + for citation in cite.format(bibliography, database, elocale): cindex += 1 - source = database.get_source_from_handle(citation.get_source_handle()) - first = True doc.start_paragraph('Endnotes-Source', "%d." % cindex) - doc.write_text(_format_source_text(source, elocale), links=links) + doc.write_text(citation[0], links=links) doc.end_paragraph() if printnotes: _print_notes(source, database, doc, 'Endnotes-Source-Notes', links) - for key, ref in citation.get_ref_list(): + for key, ref in citation[1]: # Translators: needed for French, ignore otherwise doc.start_paragraph('Endnotes-Ref', trans_text('%s:') % key) - doc.write_text(_format_ref_text(ref, key, elocale), links=links) + doc.write_text(ref, links=links) doc.end_paragraph() if printnotes: _print_notes(ref, database, doc, 'Endnotes-Ref-Notes', links) -def _format_source_text(source, elocale): - if not source: return "" - - trans_text = elocale.translation.gettext - # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh) - - src_txt = "" - - if source.get_author(): - src_txt += source.get_author() - - if source.get_title(): - if src_txt: - # Translators: needed for Arabic, ignore otherwise - src_txt += trans_text(', ') - # Translators: used in French+Russian, ignore otherwise - src_txt += trans_text('"%s"') % source.get_title() - - if source.get_publication_info(): - if src_txt: - # Translators: needed for Arabic, ignore otherwise - src_txt += trans_text(', ') - src_txt += source.get_publication_info() - - if source.get_abbreviation(): - if src_txt: - # Translators: needed for Arabic, ignore otherwise - src_txt += trans_text(', ') - src_txt += "(%s)" % source.get_abbreviation() - - return src_txt - -def _format_ref_text(ref, key, elocale): - if not ref: return "" - - ref_txt = "" - - datepresent = False - date = ref.get_date_object() - if date is not None and not date.is_empty(): - datepresent = True - if datepresent: - if ref.get_page(): - ref_txt = "%s - %s" % (ref.get_page(), elocale.get_date(date)) - else: - ref_txt = elocale.get_date(date) - else: - ref_txt = ref.get_page() - - # Print only confidence level if it is not Normal - if ref.get_confidence_level() != Citation.CONF_NORMAL: - ref_txt += " [" + elocale.translation.gettext( - conf_strings[ref.get_confidence_level()]) + "]" - - return ref_txt - def _print_notes(obj, db, doc, style, links): note_list = obj.get_note_list() ind = 1 diff --git a/gramps/gen/plug/utils.py b/gramps/gen/plug/utils.py index 31ac135b0..7f0399977 100644 --- a/gramps/gen/plug/utils.py +++ b/gramps/gen/plug/utils.py @@ -454,3 +454,24 @@ class OpenFileOrStdin: if self.filename != '-': self.filehandle.close() return False + +#------------------------------------------------------------------------- +# +# get_cite function +# +#------------------------------------------------------------------------- +def get_cite(): + """ + Function that returns the active cite plugin. + """ + plugman = BasePluginManager.get_instance() + for pdata in plugman.get_reg_cite(): + if pdata.id != config.get('preferences.cite-plugin'): + continue + module = plugman.load_plugin(pdata) + if not module: + print("Error loading formatter '%s': skipping content" + % pdata.name) + continue + cite = getattr(module, 'Formatter')() + return cite diff --git a/gramps/gui/configure.py b/gramps/gui/configure.py index c222a43e5..bbae0ad76 100644 --- a/gramps/gui/configure.py +++ b/gramps/gui/configure.py @@ -1420,6 +1420,12 @@ class GrampsPreferences(ConfigureDialog): grid.attach(lwidget, 1, row, 1, 1) grid.attach(obox, 2, row, 2, 1) + row += 1 + lwidget = BasicLabel(_("%s: ") % _('Citation formatter')) + grid.attach(lwidget, 1, row, 1, 1) + obox = self.__create_cite_combo() + grid.attach(obox, 2, row, 2, 1) + row += 1 label = self.add_text( grid, _("\nInput Options"), row, @@ -1796,6 +1802,15 @@ class GrampsPreferences(ConfigureDialog): else: widget.set_sensitive(True) + def cite_changed(self, obj): + """ + Update Database Backend. + """ + the_list = obj.get_model() + the_iter = obj.get_active_iter() + cite_choice = the_list.get_value(the_iter, 2) + config.set('preferences.cite-plugin', cite_choice) + def add_famtree_panel(self, configdialog): """ Config tab for family tree, backup and Media path settings. @@ -1967,6 +1982,33 @@ class GrampsPreferences(ConfigureDialog): obox.connect('changed', self.database_backend_changed) return obox + def __create_cite_combo(self): + """ + Create cite selection widget. + """ + backend_plugins = self.uistate.viewmanager._pmgr.get_reg_cite() + obox = Gtk.ComboBox() + cell = Gtk.CellRendererText() + obox.pack_start(cell, True) + obox.add_attribute(cell, 'text', 1) + # Build model: + model = Gtk.ListStore(GObject.TYPE_INT, + GObject.TYPE_STRING, + GObject.TYPE_STRING) + count = 0 + active = 0 + default = config.get('preferences.cite-plugin') + for plugin in sorted(backend_plugins, key=lambda plugin: plugin.name): + if plugin.id == default: + active = count + model.append(row=[count, plugin.name, plugin.id]) + count += 1 + obox.set_model(model) + # set the default value as active in the combo + obox.set_active(active) + obox.connect('changed', self.cite_changed) + return obox + def set_mediapath(self, *obj): if self.path_entry.get_text().strip(): self.dbstate.db.set_mediapath(self.path_entry.get_text()) diff --git a/gramps/plugins/cite/__init__.py b/gramps/plugins/cite/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/gramps/plugins/cite/cite.gpr.py b/gramps/plugins/cite/cite.gpr.py new file mode 100644 index 000000000..ea946d4d8 --- /dev/null +++ b/gramps/plugins/cite/cite.gpr.py @@ -0,0 +1,43 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2022 Nick Hall +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.translation.gettext + +MODULE_VERSION = "5.2" + +#------------------------------------------------------------------------ +# +# Default citation formatters for Gramps +# +#------------------------------------------------------------------------ + +register( + CITE, + id="cite-default", + name=_("Default"), + description=_("Default citation formatter"), + version="1.0", + gramps_target_version=MODULE_VERSION, + status=STABLE, + fname="default.py", + authors=["The Gramps project"], + authors_email=["http://gramps-project.org"], +) diff --git a/gramps/plugins/cite/default.py b/gramps/plugins/cite/default.py new file mode 100644 index 000000000..6e89c1679 --- /dev/null +++ b/gramps/plugins/cite/default.py @@ -0,0 +1,129 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2022 Nick Hall +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +A ciation formatter that implements the current functionality. +""" + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import logging + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +from gramps.gen.lib import Citation +from gramps.gen.utils.string import conf_strings + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +LOG = logging.getLogger(".cite") + + +class Formatter(): + + def __init__(self): + pass + + def format(self, bibliography, db, elocale): + + result = [] + for citation in bibliography.get_citation_list(): + source = db.get_source_from_handle(citation.get_source_handle()) + src = _format_source_text(source, elocale) + + refs = [] + for key, ref in citation.get_ref_list(): + ref = _format_ref_text(ref, key, elocale) + refs.append([key, ref]) + + result.append([src, refs]) + + return result + +def _format_source_text(source, elocale): + if not source: return "" + + trans_text = elocale.translation.gettext + # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh) + + src_txt = "" + + if source.get_author(): + src_txt += source.get_author() + + if source.get_title(): + if src_txt: + # Translators: needed for Arabic, ignore otherwise + src_txt += trans_text(', ') + # Translators: used in French+Russian, ignore otherwise + src_txt += trans_text('"%s"') % source.get_title() + + if source.get_publication_info(): + if src_txt: + # Translators: needed for Arabic, ignore otherwise + src_txt += trans_text(', ') + src_txt += source.get_publication_info() + + if source.get_abbreviation(): + if src_txt: + # Translators: needed for Arabic, ignore otherwise + src_txt += trans_text(', ') + src_txt += "(%s)" % source.get_abbreviation() + + return src_txt + +def _format_ref_text(ref, key, elocale): + if not ref: return "" + + ref_txt = "" + + datepresent = False + date = ref.get_date_object() + if date is not None and not date.is_empty(): + datepresent = True + if datepresent: + if ref.get_page(): + ref_txt = "%s - %s" % (ref.get_page(), elocale.get_date(date)) + else: + ref_txt = elocale.get_date(date) + else: + ref_txt = ref.get_page() + + # Print only confidence level if it is not Normal + if ref.get_confidence_level() != Citation.CONF_NORMAL: + ref_txt += " [" + elocale.translation.gettext( + conf_strings[ref.get_confidence_level()]) + "]" + + return ref_txt diff --git a/po/POTFILES.in b/po/POTFILES.in index a80a60c55..3e2860cf9 100755 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -578,6 +578,8 @@ gramps/gui/widgets/reorderfam.py gramps/gui/widgets/styledtextbuffer.py gramps/gui/widgets/styledtexteditor.py gramps/gui/widgets/validatedmaskedentry.py +gramps/plugins/cite/cite.gpr.py +gramps/plugins/cite/default.py gramps/plugins/db/bsddb/bsddb.gpr.py gramps/plugins/db/dbapi/sqlite.gpr.py gramps/plugins/db/dbapi/sqlite.py diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 1bf3c13b7..e31837c5a 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -457,6 +457,10 @@ gramps/gui/widgets/validatedcomboentry.py # gramps/plugins/__init__.py # +# plugins/cite directory +# +gramps/plugins/cite/__init__.py +# # plugins/db/bsddb directory # gramps/plugins/db/bsddb/__init__.py