diff --git a/gramps/gen/constfunc.py b/gramps/gen/constfunc.py index c3c768d2f..da8943526 100644 --- a/gramps/gen/constfunc.py +++ b/gramps/gen/constfunc.py @@ -181,11 +181,19 @@ def deprecated(func): @functools.wraps(func) def new_func(*args, **kwargs): - warnings.warn_explicit( - "Call to deprecated function {}.".format(func.__name__), - category=DeprecationWarning, - filename=func.func_code.co_filename, - lineno=func.func_code.co_firstlineno + 1 - ) + if sys.version_info[0] <3: + warnings.warn_explicit( + "Call to deprecated function {}.".format(func.__name__), + category=DeprecationWarning, + filename=func.func_code.co_filename, + lineno=func.func_code.co_firstlineno + 1 + ) + else: + warnings.warn_explicit( + "Call to deprecated function {}.".format(func.__name__), + category=DeprecationWarning, + filename=func.__code__.co_filename, + lineno=func.__code__.co_firstlineno + 1 + ) return func(*args, **kwargs) return new_func diff --git a/gramps/gen/lib/srctemplate.py b/gramps/gen/lib/srctemplate.py index b3638a18b..be3e45e3b 100644 --- a/gramps/gen/lib/srctemplate.py +++ b/gramps/gen/lib/srctemplate.py @@ -26,16 +26,14 @@ SrcTemplate class for GRAMPS. """ -from __future__ import print_function - #------------------------------------------------------------------------- # # Python modules # #------------------------------------------------------------------------- -from ..const import GRAMPS_LOCALE as glocale -_ = glocale.translation.gettext -import csv +from __future__ import print_function +from collections import defaultdict, OrderedDict +import sys #------------------------------------------------------------------------ # @@ -50,6 +48,8 @@ LOG = logging.getLogger('.template') # GRAMPS modules # #------------------------------------------------------------------------- +from ..const import GRAMPS_LOCALE as glocale +_ = glocale.translation.gettext from .srcattrtype import * from .date import Date from .tableobj import TableObject @@ -144,7 +144,7 @@ class SrcTemplate(TableObject): self.name = "" self.descr = "" self.template_element_list = [] - self.mapping_list = [] + self.mapdict = defaultdict(str) self.structure = {REF_TYPE_L: [], REF_TYPE_F: [], REF_TYPE_S: []} self.empty() @@ -172,7 +172,7 @@ class SrcTemplate(TableObject): self.name, self.descr, [template_element.serialize() for template_element in self.template_element_list], - [mapping.serialize() for mapping in self.mapping_list], + self.mapdict, ) def to_struct(self): @@ -199,7 +199,7 @@ class SrcTemplate(TableObject): "name": cuni(self.name), "descr": cuni(self.descr), "elements": [e.to_struct() for e in self.template_element_list], - "structure": (("%s: %s" % (s, self.structure[s])) for s in self.structure) + "mapdict" : self.mapdict, } def get_name(self): @@ -214,14 +214,19 @@ class SrcTemplate(TableObject): def set_descr(self, descr): self.descr = descr - def get_mapping_types_list(self): - return self.mapping_types_list + def get_map_dict(self): + """Return the map for the template""" + return self.mapdict - def set_mapping_list(self, mapping_list): - self.mapping_list = mapping_list + def set_map_dict(self, templmap): + """Set the map for the template""" + self.mapdict = templmap + + def set_map_element(self, key, value): + self.mapdict[key] = value - def add_mapping(self, mapping): - self.mapping_list.append(mapping) + def get_map_element(self, key): + return self.mapdict[key] def get_template_element_list(self): return self.template_element_list @@ -250,83 +255,72 @@ class SrcTemplate(TableObject): # map is field -> (normal value for ref L, # normal value for ref F/S, short value ref S) self.attrmap = {} + self.input_dict = defaultdict(str) def set_attr_list(self, attr_list, attr_list_citation=None, date_citation=None): """ Set the attribute list of this template. Setting once for different references saves some time. attr_list should be the source attribute list - If citation given, citation attributes overrule source attributes for + If citation given, citation attrib + utes overrule source attributes for the Full and Short references The citation date is not stored as attribute, so pass Date() object via date_citation if a date is known. """ self.empty() + self.attrmap = {} + self.input_dict = defaultdict(str) self.attr_list = attr_list or [] self.attr_list_cite = attr_list_citation or [] self.date_citation = date_citation - # store attributes in a dict last to first. this overwrites data so first - # attribute will be the one taken if duplicates are present - for attr in self.attr_list[::-1]: - lower = False - typ = attr.get_type() - key = int(typ) - keystr = typ.xml_str().lower() - if keystr.lower().endswith(' (short)'): - #a shorter version, we store with base type - key = int(SrcAttributeType(keystr[:-8])) - lower = True - if key == SrcAttributeType.CUSTOM: - key = str(typ) - if key in self.attrmap: - if lower: - self.attrmap[key] = (self.attrmap[key][0], - self.attrmap[key][0], attr.get_value()) + + # ----------------------------------------------------------------- + # Construct the input dictionary + # First pre-load the dictionary with default settings for citations + if not attr_list_citation: + for te in [x for x in self.get_template_element_list() + if x.get_citation()]: + name = str(SrcAttributeType(te.get_name())).upper().replace(' ', '_') + if te.get_display(): + val = te.get_display().upper().replace(' ', '_') else: - self.attrmap[key] = (attr.get_value(), - attr.get_value(), self.attrmap[key][1]) + val = name + self.input_dict[name] = "[" + val + "]" + + # Now get the actual attribute values. store attributes in a dict last + # to first. this overwrites data so first attribute will be the one + # taken if duplicates are present + for input_attr in ((attr_list or []) + (attr_list_citation or []))[::-1]: + typ = input_attr.get_type() + if int(typ) == SrcAttributeType.CUSTOM: + name = str(typ).upper().replace(' ', '_') else: - if lower: - #store also in normal already value of short - self.attrmap[key] = (attr.get_value(), - attr.get_value(), attr.get_value()) - else: - self.attrmap[key] = (attr.get_value(), - attr.get_value(), None) + name = typ.xml_str().upper().replace(' ', '_') + self.input_dict[name] = input_attr.get_value() + # if we haven't already got a value for the short attribute, we + # store the long attribute in the short attribute + if not name.endswith("(SHORT)"): + short_name = name + "_(SHORT)" + if self.input_dict.get(short_name) is None or \ + (self.input_dict.get(short_name) and \ + self.input_dict[short_name] == ("[" + short_name + "]")): + self.input_dict[short_name] = self.input_dict[name] - for attr in self.attr_list_cite[::-1]: - #we do same for citation information, but only update last two - # values of the attrmap - lower = False - typ = attr.get_type() - key = int(typ) - keystr = typ.xml_str().lower() - if keystr.lower().endswith(' (short)'): - #a shorter version, we store with base type - key = int(SrcAttributeType(keystr[:-8])) - lower = True - if key == SrcAttributeType.CUSTOM: - key = str(typ) - if key in self.attrmap: - if lower: - self.attrmap[key] = (self.attrmap[key][0], - self.attrmap[key][2], attr.get_value()) - else: - self.attrmap[key] = (self.attrmap[key][0], - attr.get_value(), self.attrmap[key][2]) - else: - #field only present in citation. - if lower: - #store also in normal already value of short, keep empty - #string for source fields - self.attrmap[key] = ('', attr.get_value(), attr.get_value()) - else: - self.attrmap[key] = ('', attr.get_value(), None) if self.date_citation: #we store the date of the citation in attrmap - key = SrcAttributeType.DATE - self.attrmap[key] = (None, self.date_citation, None) + name = SrcAttributeType(SrcAttributeType.DATE).xml_str().upper().replace(' ', '_') + self.input_dict[name] = str(self.date_citation) + short_name = name + "_(SHORT)" + if self.input_dict.get(short_name) is None or \ + (self.input_dict.get(short_name) and \ + self.input_dict[short_name] == ("[" + short_name + "]")): + self.input_dict[short_name] = self.input_dict[name] + # FIXME: REPOSITORY, REPOSITORY_ADDRESS and REPOSITORY_CALL_NUMBER all + # need to be added to the self.input_dict. See srctemplatetab.py + # _add_repo_entry() + def reference_L(self, attr_list=None): """ Return the list reference based on the passed source attribute list @@ -342,7 +336,8 @@ class SrcTemplate(TableObject): def reference_S(self, attr_list=None, attr_list_citation=None, date_citation=None): """ Return the short reference based on the passed source attribute list - If attr_list is None, same list as before is used. + If attr_list is None, same list as + before is used. """ if attr_list or attr_list_citation or date_citation: self.set_attr_list(attr_list, attr_list_citation, date_citation) @@ -363,155 +358,190 @@ class SrcTemplate(TableObject): self.refF = self._reference(REF_TYPE_F) return self.refF - def __ged_page_reflist(self): - """ - Construct a derived template reflist for use to construct the gedcom - page field - """ - reflist_F = self.structure[REF_TYPE_F] - reflist_L_fields = [field[1] for field in self.structure[REF_TYPE_L]] - result = [] - for entry in reflist_F: - if entry[1] in reflist_L_fields: - continue - if entry[1] == SrcAttributeType.DATE: - continue - result.append(entry) - def _reference(self, reftype, gedcomfield=None): """ Compute the reference based on data present. - At the moment no style is applied! - - THIS IS UGLY CODE AT THE MOMENT! SHOULD BE ENTIRELY REWRITTEN, FOR - NOW IT JUST GIVES ME SOMETHING TO USE IN THE PROTOTYPE !! """ - if gedcomfield == GED_PAGE: - self.__ged_page_reflist() - else: - reflist = self.structure[reftype] - # reflist is typically a list like - # [ ('', AUTHOR, '', ',', EMPTY, False, False, EMPTY, EMPTY, None, None), - # ('', TITLE, '', ',', STYLE_QUOTE, False, False, EMPTY, EMPTY, None, None), - # ('', PUB_INFO, '', '.', EMPTY, False, False, EMPTY, EMPTY, None, None), - # ('', DATE, '', ' -', EMPTY, False, False, EMPTY, EMPTY, None, None), - # ('', PAGE, 'Page(s)', '.', EMPTY, False, False, EMPTY, EMPTY, None, None), - # ] + # http://bugs.python.org/issue6081 + class DefaultBlank(dict): + def __missing__(self, key): + return "" + + class DefaultKey(dict): + def __missing__(self, key): + return "[" + key + "]" + + ged_table = { + GED_AUTHOR : "GEDCOM_A", + GED_TITLE : "GEDCOM_T", + GED_PUBINF : "GEDCOM_P", + GED_DATE : "GEDCOM_D", + GED_PAGE : "GEDCOM_PAGE", + } + if gedcomfield: + return (self.get_map_element(ged_table[gedcomfield]) % + DefaultKey(self.input_dict)) or "" + + use_CSL = False + try: + import citeproc + if sys.version_info[0] >= 3: + use_CSL = True + except: + pass + + if use_CSL: + # ----------------------------------------------------------------- + # Construct the standard output-elements + self.output_dict = OrderedDict() + LOG.debug(self.get_map_dict()) + LOG.debug("input_attributes \n" + + "".join(("%s: %s\n" % item) for item in list(self.input_dict.items()))) + for key, val in list(self.get_map_dict().items()): + if key[0].islower(): + try: + self.output_dict[key] = val % DefaultBlank(self.input_dict) + except: + LOG.warn("key error with key %s; val %s; input_dict %s" % + (key, val, self.input_dict)) + self.output_dict[key] = "" + + LOG.debug("CSL_attributes \n" + + "".join(("%s: %s\n" % item) for item in list(self.output_dict.items()))) + + # Temporary fix for not implemented yet templates + if len(self.output_dict) == 0: + return "" + + # Now fix CSL attributes that need special sub-elements + for name in ["author", "container_author", "some other name"]: + if name in self.output_dict: + self.output_dict[name] = [{"family": self.output_dict[name], + "given": ""}] + # ----------------------------------------------------------------- + # Modify the output-elements to allow the standard Chicago style to + # format the citations close to Evidence Style + + # literal dates are not specially treated. Date accessed is converted to + # a literal publication date to conform to how ESM formats the accessed + # date + if "accessed" in self.output_dict: + self.output_dict["issued"] = {'literal' : "accessed " + self.output_dict['accessed']} + del self.output_dict['accessed'] + # Website is rendered as publisher_place to conform to how ESM renders + # it. + if "url" in self.output_dict: + self.output_dict["publisher_place"] = \ + self.output_dict["publisher_place"] if "publisher_place" in self.output_dict \ + else "" + self.output_dict["url"] + LOG.debug("self.output_dictibutes modified \n" + + "".join((" %s: %s\n" % item) for item in self.output_dict.items())) + + try: + (refF, refS, refL) = self.get_CSL_references(self.output_dict) + if reftype == REF_TYPE_F: + return refF + elif reftype == REF_TYPE_S: + return refS + else: + return refL + except: + print(sys.exc_info()[0], sys.exc_info()[1]) + return "" - #set col of attrmap to use: - if reftype == REF_TYPE_L: - COL_NORMAL = 0 - COL_SHORT = 2 else: - COL_NORMAL = 1 - COL_SHORT = 2 - ref = [''] - fieldadded = [False] - for (ldel, field, label, rdel, style, priv, opt, short, gedcom, - hint, tooltip) in reflist: - if not gedcomfield is None and gedcom != gedcomfield: - continue - customshort = False - #left delimiter - if ldel in ['(', '[', '{']: - ref += [''] - fieldadded += [False] - ref[-1] += ldel - ldeltodo = '' - else: - ldeltodo = ldel - val = self.attrmap.get(field) - #field - field = '' - if val is not None: - if reftype == REF_TYPE_S and val[COL_SHORT] is not None: - customshort = True - field = val[COL_SHORT] - else: - field = val[COL_NORMAL] - if short and not customshort: - #we apply the shortening algorithm - ## TODO: not implemented yet - pass - #if field is a Date object, we now convert to string - if isinstance(field, Date): - field = str(field) - if field.strip(): - fieldadded[-1] = True - ref[-1] += ldeltodo - if len(ref[-1]) and ref[-1][-1] == '.': - ref[-1] += ' ' + field[0].capitalize() + field[1:] - elif len(ref[-1]) and ref[-1][-1] in [',', ':', '-']: - ref[-1] += ' ' + field - elif len(ref[-1]) and ref[-1] != ' ': - ref[-1] += ' ' + field - else: - ref[-1] += field - #right delimiter - nobracket = True - for bracketl, bracketr in [('(', ')'), ('[',']'), ('{','}')]: - if bracketr in rdel: - nobracket = False - if len(ref[-1] [ref[-1].find(bracketl)+1:]) > 0 : - newval = ref[-1] + rdel - ref = ref[:-1] - fieldadded = fieldadded[:-1] - fieldadded[-1] = True - ref[-1] += newval - else: - #no data inside of delimiter, we remove it entirely - ref = ref[:-1] - fieldadded = fieldadded[:-1] - #if . at end of rdel, add it - if rdel[-1] == '.': - if ref[-1] and ref[-1][-1] in [',', '.']: - ref[-1] = ref[-1][:-1] - if ref[-1]: - ref[-1] = ref[-1] + '.' - elif rdel[-1] == ',': - if ref[-1] and ref[-1][-1] in [',', '.']: - pass - elif ref[-1]: - ref[-1] = ref[-1] + ',' - if nobracket: - # we add rdel - if not ref[-1]: - #nothing there, don't add delimiter - pass - elif len(rdel) and rdel[0] == '.': - curval = ref[-1] - if len(curval) and curval[-1] == '.': - pass - elif len(curval) and curval[-1] in [',', ';']: - ref[-1] = ref[-1][:-1] + rdel - else: - ref[-1] = ref[-1] + rdel - #we only add delimiters after this if new fields are added - fieldadded[-1] = False - elif len(rdel) and rdel[0] == ',': - curval = ref[-1] - if len(curval) and curval[-1] in ['.', ';']: - pass - elif len(curval) and curval[-1] == ',': - pass - elif fieldadded[-1]: - ref[-1] = ref[-1] + rdel - #we only add delimiters after this if new fields are added - fieldadded[-1] = False - else: - if fieldadded[-1]: - ref[-1] = ref[-1] + rdel - #we only add delimiters after this if new fields are added - fieldadded[-1] = False - - ref = ' '.join(ref) - if ref: - ref = ref[0].capitalize() + ref[1:] - ref.replace(' ', ' ') - return ref - else: - return ref + # ----------------------------------------------------------------- + # Construct the standard output-elements + ref_table = { + REF_TYPE_L : "EE_L", + REF_TYPE_F : "EE_F", + REF_TYPE_S : "EE_S", + } + return (self.get_map_element(ref_table[reftype]) % + DefaultKey(self.input_dict)) or "" + + def get_CSL_references(self, CSL_attributes): + # Import the citeproc-py classes we'll use below. + from citeproc import CitationStylesStyle, CitationStylesBibliography + from citeproc import Citation, CitationItem + from citeproc import formatter, Locator + from citeproc.source.json import CiteProcJSON + # Process the JSON data to generate a citeproc-py BibliographySource. + if 'locator' in CSL_attributes: + loc = Locator("page", CSL_attributes["locator"]) + + import copy + c1 = copy.deepcopy(CSL_attributes) + c2 = copy.deepcopy(CSL_attributes) + + bib_source = {"full": c1, "subs" : c2} + bib_source = {"full": c1} + +# for key, entry in bib_source.items(): +# print(key) +# for name, value in entry.items(): +# print(' {}: {}'.format(name, value)) + + # load a CSL style (from the current directory) + + bib_style = CitationStylesStyle('chicago-fullnote-bibliography-no-ibid.csl') + + # Create the citeproc-py bibliography, passing it the: + # * CitationStylesStyle, + # * BibliographySource (CiteProcJSON in this case), and + # * a formatter (plain, html, or you can write a custom formatter) + + bibliography = CitationStylesBibliography(bib_style, bib_source, formatter.plain) + + + # Processing citations in a document need to be done in two passes as for some + # CSL styles, a citation can depend on the order of citations in the + # bibliography and thus on citations following the current one. + # For this reason, we first need to register all citations with the + # CitationStylesBibliography. + + if loc: + citation1 = Citation([CitationItem('full', locator=loc)]) + citation2 = Citation([CitationItem('subs', locator=loc)]) + else: + citation1 = Citation([CitationItem('full')]) + citation2 = Citation([CitationItem('subs')]) + + citation1 = Citation([CitationItem('full')]) + + bibliography.register(citation1) + bibliography.register(citation2) + + + # In the second pass, CitationStylesBibliography can generate citations. + # CitationStylesBibliography.cite() requires a callback function to be passed + # along to be called in case a CitationItem's key is not present in the + # bilbiography. + + def warn(citation_item): + print("WARNING: Reference with key '{}' not found in the bibliography." + .format(citation_item.key)) + + print('Citations') + print('---------') + + print(bibliography.cite(citation1, warn)) + print(bibliography.cite(citation2, warn)) + + + # And finally, the bibliography can be rendered. + + print('') + print('Bibliography') + print('------------') + + print(bibliography.bibliography()) + + return(bibliography.cite(citation1, warn), + bibliography.cite(citation2, warn), + bibliography.bibliography()) + def author_gedcom(self, attr_list=None): if attr_list: self.set_attr_list(attr_list) @@ -576,7 +606,6 @@ class TemplateElement(SecondaryObject): self.citation = source.citation self.short - source.short self.short_alg = source.short_alg - self.template_mapping_list = source.template_mapping_list else: self.name = "" self.display = "" @@ -585,7 +614,6 @@ class TemplateElement(SecondaryObject): self.citation = False self.short = False self.short_alg = "" - self.template_mapping_list = [] def serialize(self): """ @@ -595,7 +623,9 @@ class TemplateElement(SecondaryObject): self.display, self.hint, self.tooltip, -# [template_mapping.serialize() for template_mapping in self.template_mapping_list] + self.citation, + self.short, + self.short_alg ) def to_struct(self): @@ -622,13 +652,17 @@ class TemplateElement(SecondaryObject): "display": cuni(self.display), "hint": cuni(self.hint), "tooltip": cuni(self.tooltip), + "citation": cuni(self.citation), + "short": cuni(self.short), + "short_alg": cuni(self.short_alg), } def unserialize(self, data): """ Convert a serialized tuple of data to an object. """ - (self.name, self.type) = data + (self.name, self.display, self.hint, self.tooltip, self.citation, + self.short, self.short_alg) = data return self def get_name(self): @@ -715,96 +749,3 @@ class TemplateElement(SecondaryObject): Set the short_alg for the Template element according to the given argument. """ self.short_alg = short_alg - - def get_template_mapping_list(self): - return self.template_mapping_list - - def set_template_mapping_list(self, template_mapping_list): - self.template_mapping_list = template_mapping_list - - def add_template_mapping(self, template_mapping): - self.template_mapping_list.append(template_mapping) - -class MappingElement(SecondaryObject): - """ - TemplateEelement class. - - This class is for keeping information about how each [input] Template - Element is mapped to an Output form. - - Mapping: - - - Mapping_name - English name of the mapping. One mapping GEDCOM would - always be present. Other mappings are optional, but as we have decided - that (at least initially) we would use the CSL mapping, then this should - also be present). (The mappings should be the same as in the - MappingType). - - - map_target - the English interchange-element name onto which this - template-element is mapped. e.g. [WRITER FIRST] is mapped onto Recipient, - so this would contain recipient. - - - Mapping_order the sequence number for this mapping. So [WRITER LAST], - [WRITER FIRST] both map to 'Recipient', [WRITER FIRST] is 2, [WRITER - LAST] is 1 - - - separator - the separator after this element if there is another one - following, e.g.", " - """ - - def __init__(self, source=None): - """ - Create a new MappingEelement instance, copying from the source if - present. - """ - if source: - self.name = source.name - self.target = source.target - self.order = source.order - self.separator = source.separator - else: - self.name = "" - self.target = "" - self.order = "" - self.separator = "" - - def serialize(self): - """ - Convert the object to a serialized tuple of data. - """ - return (self.name, - self.target, - self.order, - self.separator - ) - - def unserialize(self, data): - """ - Convert a serialized tuple of data to an object. - """ - (self.name, self.type) = data - return self - - def get_name(self): - """ - Return the name for the mapping element. - """ - return self.name - - def set_name(self, name): - """ - Set the role according to the given argument. - """ - self.name = name - - def get_target(self): - """ - Return the tuple corresponding to the preset role. - """ - return self.target - - def set_target(self, target): - """ - Set the role according to the given argument. - """ - self.target = target diff --git a/gramps/gen/lib/srctemplatelist.py b/gramps/gen/lib/srctemplatelist.py index 3ea88cfc0..552aaee6c 100644 --- a/gramps/gen/lib/srctemplatelist.py +++ b/gramps/gen/lib/srctemplatelist.py @@ -33,6 +33,7 @@ from __future__ import print_function # Python modules # #------------------------------------------------------------------------- +import sys #------------------------------------------------------------------------ # @@ -56,7 +57,14 @@ class Singleton(type): cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] -class SrcTemplateList(object): +# See http://stackoverflow.com/questions/17237857/python3-singleton-metaclass- +# method-not-working the syntax has changed + +# http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ +# http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python/17840539 +STL = Singleton('STL', (object, ), {}) + +class SrcTemplateList(STL): """ Manages a singleton list of the source templates. @@ -68,7 +76,7 @@ class SrcTemplateList(object): of keys, and another method to return the Template object for a given key. In this way it would act like a database table. """ - __metaclass__ = Singleton +# __metaclass__ = Singleton def __init__(self): self.template_list = {} @@ -84,7 +92,7 @@ class SrcTemplateList(object): gedtempl = None if name == 'UNKNOWN': name = 'GEDCOM' - for template in self.template_list.itervalues(): + for template in list(self.template_list.values()): if template.get_name() == name: return template if template.get_name() == 'GEDCOM': diff --git a/gramps/gui/editors/displaytabs/embeddedlist.py b/gramps/gui/editors/displaytabs/embeddedlist.py index 37abc8a5f..3f4a6debd 100644 --- a/gramps/gui/editors/displaytabs/embeddedlist.py +++ b/gramps/gui/editors/displaytabs/embeddedlist.py @@ -537,7 +537,7 @@ class EmbeddedList(ButtonTab): column = Gtk.TreeViewColumn(name, self.pb_renderer) column.set_cell_data_func(self.pb_renderer, self.icon_func, pair[1]) else: - raise NotImplementedError, 'Unknown column type' + raise NotImplementedError('Unknown column type') if col_icon is not None: image = Gtk.Image() image.set_from_stock(col_icon, Gtk.IconSize.MENU) diff --git a/gramps/gui/editors/displaytabs/srctemplatetab.py b/gramps/gui/editors/displaytabs/srctemplatetab.py index 77c263954..e89402ddf 100644 --- a/gramps/gui/editors/displaytabs/srctemplatetab.py +++ b/gramps/gui/editors/displaytabs/srctemplatetab.py @@ -37,6 +37,14 @@ _ = glocale.translation.gettext from gi.repository import Gdk from gi.repository import Gtk +#------------------------------------------------------------------------ +# +# Set up logging +# +#------------------------------------------------------------------------ +import logging +LOG = logging.getLogger('.template') + #------------------------------------------------------------------------- # # Gramps libraries @@ -51,6 +59,7 @@ from ...widgets.srctemplatetreeview import SrcTemplateTreeView from ...widgets import (UndoableEntry, MonitoredEntryIndicator, MonitoredDate, ValidatableMaskedEntry) from .grampstab import GrampsTab +from gramps.gen.constfunc import STRTYPE #------------------------------------------------------------------------- # @@ -210,6 +219,7 @@ class TemplateFields(object): template = SrcTemplateList().get_template_from_name(key).get_structure() telist = SrcTemplateList().get_template_from_name(key).get_template_element_list() else: + LOG.warn("template not defined %s" % key) return # first remove old fields @@ -303,8 +313,8 @@ class TemplateFields(object): """ self.gridfields.insert_row(row) field = srcattrtype - if isinstance(field, basestring): - raise NotImplementedError, "type must be the integer key" + if isinstance(field, STRTYPE): + raise NotImplementedError("type must be the integer key") #setup label if alt_label: label = alt_label diff --git a/gramps/gui/editors/editsource.py b/gramps/gui/editors/editsource.py index aa3d350fb..033661ec4 100644 --- a/gramps/gui/editors/editsource.py +++ b/gramps/gui/editors/editsource.py @@ -357,15 +357,10 @@ class EditSource(EditPrimary): #we only construct once the template to use to format information if self.srctemp is None: self.srctemp = SrcTemplateList().get_template_from_name(self.obj.get_template()) - # FIXME: I am not sure what the code below was doing. The SrcTemplate - # had been set from the name in the Src record, then a check was made as - # to whether the old name from the Src record was the same as the name - # of the template. But since the template was found from its name, this - # must be the case. -# #if source template changed, reinit template -# if self.obj.get_template() != self.srctemp.get_template_key(): -# self.srctemp.set_template_key(self.obj.get_template()) + #if source template changed, reinit template + if self.obj.get_template() != self.srctemp.get_name(): + self.srctemp = SrcTemplateList().get_template_from_name(self.obj.get_template()) #set new attrlist in template if self.citation_loaded: citeattr = self.citation.get_attribute_list() @@ -377,15 +372,15 @@ class EditSource(EditPrimary): citedate) #set fields with the template - self.refL.set_text(self.srctemp.reference_L()) + self.refL.set_markup(self.srctemp.reference_L()) if self.citation_loaded: - self.refF.set_text(self.srctemp.reference_F()) - self.refS.set_text(self.srctemp.reference_S()) + self.refF.set_markup(self.srctemp.reference_F()) + self.refS.set_markup(self.srctemp.reference_S()) else: - self.refF.set_text(_("")) - self.refS.set_text(_("")) - self.author.set_text(self.srctemp.author_gedcom()) - self.pubinfo.set_text(self.srctemp.pubinfo_gedcom()) + self.refF.set_markup(_("")) + self.refS.set_markup(_("")) + self.author.set_markup(self.srctemp.author_gedcom()) + self.pubinfo.set_markup(self.srctemp.pubinfo_gedcom()) if self.template_tab and self.template_tab.autoset_title: title = self.srctemp.title_gedcom() self.obj.set_name(title) diff --git a/gramps/gui/glade/editsource.glade b/gramps/gui/glade/editsource.glade index bcb49b7b0..0f399aa6c 100644 --- a/gramps/gui/glade/editsource.glade +++ b/gramps/gui/glade/editsource.glade @@ -95,6 +95,146 @@ True False + + + True + True + start + True + + + True + False + 5 + 5 + 3 + 3 + + + True + False + start + start + 0 + 0 + Full footnote: + center + start + + + 0 + 0 + 1 + 2 + + + + + True + False + start + start + 0 + 0 + Bibliography: + + + 0 + 2 + 1 + 1 + + + + + True + False + start + True + 0.079999998211860657 + 0 + label + True + + + 1 + 0 + 1 + 1 + + + + + True + False + start + True + 0 + 0 + label + True + + + 1 + 1 + 1 + 1 + + + + + True + False + start + True + 0 + 0 + label + True + + + 1 + 2 + 1 + 1 + + + + + True + False + start + start + 0 + 0.029999999329447746 + Short footnote + + + 0 + 1 + 1 + 1 + + + + + + + True + False + 0.50999999046325684 + 0.50999999046325684 + <b>Reference Information</b> + True + + + + + False + True + 1 + + True @@ -106,7 +246,7 @@ False True - 0 + 1 @@ -390,17 +530,14 @@ 1 - - - True False 0 _Name: - cname True + cname 0 @@ -424,6 +561,9 @@ 1 + + + @@ -445,7 +585,7 @@ True True - 1 + 2 @@ -459,7 +599,7 @@ False True - 2 + 3 @@ -671,142 +811,6 @@ 1 - - - True - True - True - - - True - False - 5 - 5 - 3 - 3 - - - True - False - start - start - 0 - 0 - Listing: - - - 0 - 0 - 1 - 1 - - - - - True - False - start - start - 0 - 0 - Short footnote: - - - 0 - 2 - 1 - 1 - - - - - True - False - start - True - 0 - 0 - label - True - - - 1 - 0 - 1 - 1 - - - - - True - False - start - True - 0 - 0 - label - True - - - 1 - 1 - 1 - 1 - - - - - True - False - start - True - 0 - 0 - label - True - - - 1 - 2 - 1 - 1 - - - - - True - False - start - start - 0 - 0 - Full footnote: - - - 0 - 1 - 1 - 1 - - - - - - - True - False - <b>Reference Information</b> - True - - - - - 0 - 5 - 4 - 1 - - True @@ -991,6 +995,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -1157,7 +1185,7 @@ True True - 3 + 4 diff --git a/gramps/plugins/srctemplates/importcsv.py b/gramps/plugins/srctemplates/importcsv.py index 7966ad1ef..462d64470 100644 --- a/gramps/plugins/srctemplates/importcsv.py +++ b/gramps/plugins/srctemplates/importcsv.py @@ -37,6 +37,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale _ = glocale.translation.gettext import csv import collections +import sys #------------------------------------------------------------------------- # @@ -99,8 +100,13 @@ def load_srctemplates_data(): if mod: csvfilename = mod.csvfile LOG.debug("**** load_srctemplate_data. Loading csv from %s" % csvfilename) - with open(csvfilename, 'rb') as csvfile: - load_srctemplate_csv(csvfile) + if sys.version_info[0] <3: + with open(csvfilename, 'rb') as csvfile: + load_srctemplate_csv(csvfile) + else: + with open(csvfilename, 'r') as csvfile: + load_srctemplate_csv(csvfile) + LOG.debug("**** load_srctemplate_data. csv data loaded") def load_srctemplate_gedcom(): @@ -158,7 +164,7 @@ def load_srctemplate_gedcom(): tlist = SrcTemplateList() tlist.add_template(handle, template) - for (cite_type, slist) in TEMPLATES['GEDCOM'].iteritems(): + for (cite_type, slist) in list(TEMPLATES['GEDCOM'].items()): if cite_type != DESCR: for struct in slist: if cite_type == REF_TYPE_L or cite_type == REF_TYPE_F: @@ -197,6 +203,16 @@ def load_srctemplate_gedcom(): field_label, rdel, style, private, optional, shorteralg, gedcommap, hint, tooltip)]) + template.set_map_element("GEDCOM_A", "AUTHOR") + template.set_map_element("GEDCOM_T", "TITLE") + template.set_map_element("GEDCOM_P", "PUB_INFO") + template.set_map_element("GEDCOM_D", "DATE") + template.set_map_element("GEDCOM_PAGE", "PAGE") + + template.set_map_element("EE_L", "%(AUTHOR)s. %(TITLE)s. %(PUB_INFO)s") + template.set_map_element("EE_F", "%(AUTHOR)s, %(TITLE)s, %(PUB_INFO)s. %(DATE)s - %(PAGE)s") + template.set_map_element("EE_S", "%(AUTHOR_(SHORT))s, %(DATE_(SHORT))s - %(PAGE_(SHORT))s.") + for handle in SrcTemplateList().get_template_list(): template = SrcTemplateList().get_template_from_handle(handle) LOG.debug("source_type: %s" % template.get_name()) @@ -267,10 +283,10 @@ def load_srctemplate_csv(csvfile): source_descr = '%s - %s - %s' % (_(cat), _(cattype), _(types)) if source_type in TYPE2TEMPLATEMAP: if not TYPE2TEMPLATEMAP[source_type].get_descr() == source_descr: - raise NotImplementedError, source_type + ' ' + TYPE2TEMPLATEMAP[source_type].get_descr() + ' NOT equal to known description' + source_descr + raise NotImplementedError(source_type + ' ' + TYPE2TEMPLATEMAP[source_type].get_descr() + ' NOT equal to known description' + source_descr) if newtempl: #the template is new in this csv, but already defined, probably user error - raise NotImplementedError, 'Source template ' + prevtempl + ' is twice defined in the csv.' + raise NotImplementedError('Source template ' + prevtempl + ' is twice defined in the csv.') else: template = SrcTemplate() template.set_name(source_type) @@ -285,13 +301,13 @@ def load_srctemplate_csv(csvfile): if row[CITETYPECOL]: #new citation type, - cite_type = row[CITETYPECOL].strip() - assert cite_type in ['F', 'L', 'S'], str(cite_type) - if cite_type == 'S': + cite_type_text = row[CITETYPECOL].strip() + assert cite_type_text in ['F', 'L', 'S'], str(cite_type_text) + if cite_type_text == 'S': shortcite = True else: shortcite = False - cite_type = CITE_TYPES[cite_type] + cite_type = CITE_TYPES[cite_type_text] #add field for template to evidence style field = row[FIELDCOL].strip() field_type = field.replace('[', '').replace(']','').lower().capitalize() @@ -306,7 +322,7 @@ def load_srctemplate_csv(csvfile): ifield_type.set_from_xml_str(field_type) ifield_type = int(SrcAttributeType(ifield_type)) if ifield_type == SrcAttributeType.CUSTOM: - raise NotImplementedError, "field must be a known SrcAttributeType, is " + str(SrcAttributeType(field_type)) + raise NotImplementedError("field must be a known SrcAttributeType, is " + str(SrcAttributeType(field_type))) field_type = ifield_type #field_descr = field.replace('[', '').replace(']','').lower().capitalize() field_label = row[LABELCOL].strip() @@ -318,6 +334,7 @@ def load_srctemplate_csv(csvfile): if row[OPTCOL].strip(): optional = True shorteralg = SHORTERALG.get(row[SHORTERCOL].strip()) or EMPTY + gedcom_type_text = row[GEDCOMCOL].strip() gedcommap = GEDCOMFIELDS.get(row[GEDCOMCOL].strip()) or EMPTY style = STYLES.get(row[STYLECOL].strip()) or EMPTY @@ -361,11 +378,44 @@ def load_srctemplate_csv(csvfile): _(field_label), row[RDELCOL], style, private, optional, shorteralg, gedcommap, _(hint), _(tooltip))]) + # Setup the mapping. A typical mapping would look like: + # ('EE_Full' : '%(COMPILER)s, "%(TITLE)s", %(TYPE)s, %(WEBSITE CREATOR/OWNER)s, %(WEBSITE)s, (%(URL (DIGITAL LOCATION))s: accessed %(DATE ACCESSED)s), %(ITEM OF INTEREST)s; %(CREDIT LINE)s.') + target = "EE_" + cite_type_text + if te.get_short(): + txt = int(SrcAttributeType().short_version(field_type)) + else: + txt = field_type + txt = row[LDELCOL] + "%(" + \ + SrcAttributeType(txt).xml_str().upper().replace(' ', '_') + \ + ")s" + row[RDELCOL] + if len(row[RDELCOL]) and row[RDELCOL][-1] in ['.', ',', ':', ';', '-']: + txt += ' ' + if style == STYLE_QUOTE: + txt = '"' + txt + '"' + elif style == STYLE_QUOTECONT: + if template.get_map_element(target)[-1] == '"': + template.set_map_element(target, template.get_map_element(target)[:-1]) + txt = txt + '"' + else: + txt = '"' + txt + '"' + elif style == STYLE_EMPH: + txt = "" + txt + "" + elif style == STYLE_BOLD: + txt = "" + txt + "" + template.set_map_element(target, template.get_map_element(target) + txt) + + # Setup the GEDCOM fields. These are only stored in the L template + if cite_type == REF_TYPE_L and gedcom_type_text: + target = "GEDCOM_" + gedcom_type_text + if style == STYLE_QUOTECONT: + if template.get_map_element(target) and template.get_map_element(target)[-1] == '"': + template.set_map_element(target, template.get_map_element(target)[:-1]) + template.set_map_element(target, template.get_map_element(target) + txt) + # Now we adjust some fields that could not be changed till all the data had # been read in for source_type in TYPE2FIELDS: template = TYPE2TEMPLATEMAP[source_type] - LOG.debug("source_type: %s" % source_type) # First we determine which are citation fields cite_fields = [field for field in TYPE2FIELDS[source_type][REF_TYPE_F] + @@ -388,14 +438,51 @@ def load_srctemplate_csv(csvfile): # sources if te.get_short() == True: te.set_name(int(SrcAttributeType().short_version(te.get_name()))) - LOG.debug(" name: %s; display: %s; hint: %s; tooltip: %s; " - "citation %s; short %s; short_alg %s" % - (SrcAttributeType(te.get_name()).xml_str(), - te.get_display(), te.get_hint(), - te.get_tooltip(), te.get_citation(), - te.get_short(), te.get_short_alg() - )) + # Finally we setup the GEDCOM page fields + for source_type in TYPE2FIELDS: + template = TYPE2TEMPLATEMAP[source_type] + for te in [x for x in template.get_template_element_list() + if x.get_citation() and not x.get_short()]: + target = "GEDCOM_PAGE" + template.set_map_element(target, + ", ".join((template.get_map_element(target), txt))) + + # Proof of concept for CSL mapping + template = TYPE2TEMPLATEMAP.get('ESM254') + if template: + amap = { + 'type' : 'chapter', + 'author' : '%(COMPILER)s', + 'title' : '%(TITLE)s', + 'edition' : '%(TYPE)s', + 'container_author' : '%(WEBSITE_CREATOR/OWNER)s', + 'container_title' : '%(WEBSITE)s', + 'url' : '%(URL_(DIGITAL_LOCATION))s', + 'locator' : '%(ITEM_OF_INTEREST)s; %(CREDIT_LINE)s', + 'accessed' : '%(DATE_ACCESSED)s', + 'page' : '1-7' + } + for key, val in list(amap.items()): + template.set_map_element(key, val) + +# for source_type in TYPE2FIELDS: +# LOG.debug("source_type: %s" % source_type) +# template = TYPE2TEMPLATEMAP[source_type] +# for te in template.get_template_element_list(): +# LOG.debug(" name: %s; display: %s; hint: %s; tooltip: %s; " +# "citation %s; short %s; short_alg %s" % +# (SrcAttributeType(te.get_name()).xml_str(), +# te.get_display(), te.get_hint(), +# te.get_tooltip(), te.get_citation(), +# te.get_short(), te.get_short_alg() +# )) +# for target in template.get_map_dict(): +# LOG.debug("Type %s; target %s; map %s" % +# (source_type, target, template.get_map_element(target))) + + + # LOG.debug(tlist.get_template_list()) # for handle in tlist.get_template_list(): # LOG.debug(tlist.get_template_from_handle(handle).to_struct())