From 5e1535e125f5fad6cf321c491c762df13c049165 Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Sat, 23 Jun 2012 23:18:27 +0000 Subject: [PATCH] GEPS008: Create new module for make_unknown functions svn: r19907 --- po/POTFILES.in | 1 + src/Utils.py | 151 ------------------------- src/gen/utils/Makefile.am | 3 +- src/gen/utils/unknown.py | 193 ++++++++++++++++++++++++++++++++ src/plugins/import/importxml.py | 11 +- src/plugins/lib/libgedcom.py | 7 +- src/plugins/tool/check.py | 31 ++--- 7 files changed, 222 insertions(+), 175 deletions(-) create mode 100644 src/gen/utils/unknown.py diff --git a/po/POTFILES.in b/po/POTFILES.in index f80004fef..f6c23de2e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -327,6 +327,7 @@ src/gen/utils/keyword.py src/gen/utils/lds.py src/gen/utils/place.py src/gen/utils/trans.py +src/gen/utils/unknown.py # gui - GUI code src/gui/aboutdialog.py diff --git a/src/Utils.py b/src/Utils.py index ce378ad04..ee11a1090 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -659,154 +659,3 @@ def navigation_label(db, nav_type, handle): label = '[%s] %s' % (obj.get_gramps_id(), label) return (label, obj) - -#------------------------------------------------------------------------- -# -# make_unknown -# -#------------------------------------------------------------------------- -def make_unknown(class_arg, explanation, class_func, commit_func, transaction, - **argv): - """ - Make a primary object and set some property so that it qualifies as - "Unknown". - - Some object types need extra parameters: - Family: db, Event: type (optional), - Citation: methods to create/store source. - - Some theoretical underpinning - This function exploits the fact that all import methods basically do the - same thing: Create an object of the right type, fill it with some - attributes, store it in the database. This function does the same, so - the observation is why not use the creation and storage methods that the - import routines use themselves, that makes nice reuse of code. To do this - formally correct we would need to specify a interface (in the OOP sence) - which the import methods would need to implement. For now, that is deemed - too restrictive and here we just slip through because of the similarity in - code of both GEDCOM and XML import methods. - - :param class_arg: The argument the class_func needs, typically a kind of id. - :type class_arg: unspecified - :param explanation: Handle of a note that explains the origin of primary obj - :type explanation: str - :param class_func: Method to create primary object. - :type class_func: method - :param commit_func: Method to store primary object in db. - :type commit_func: method - :param transactino: Database transaction handle - :type transaction: str - :param argv: Possible additional parameters - :type param: unspecified - :returns: List of newly created objects. - :rtype: list - """ - retval = [] - obj = class_func(class_arg) - if isinstance(obj, gen.lib.Person): - surname = gen.lib.Surname() - surname.set_surname('Unknown') - name = gen.lib.Name() - name.add_surname(surname) - name.set_type(gen.lib.NameType.UNKNOWN) - obj.set_primary_name(name) - elif isinstance(obj, gen.lib.Family): - obj.set_relationship(gen.lib.FamilyRelType.UNKNOWN) - handle = obj.handle - if getattr(argv['db'].transaction, 'no_magic', False): - backlinks = argv['db'].find_backlink_handles( - handle, [gen.lib.Person.__name__]) - for dummy, person_handle in backlinks: - person = argv['db'].get_person_from_handle(person_handle) - add_personref_to_family(obj, person) - else: - for person in argv['db'].iter_people(): - if person._has_handle_reference('Family', handle): - add_personref_to_family(obj, person) - elif isinstance(obj, gen.lib.Event): - if 'type' in argv: - obj.set_type(argv['type']) - else: - obj.set_type(gen.lib.EventType.UNKNOWN) - elif isinstance(obj, gen.lib.Place): - obj.set_title(_('Unknown')) - elif isinstance(obj, gen.lib.Source): - obj.set_title(_('Unknown')) - elif isinstance(obj, gen.lib.Citation): - #TODO create a new source for every citation? - obj2 = argv['source_class_func'](argv['source_class_arg']) - obj2.set_title(_('Unknown')) - obj2.add_note(explanation) - argv['source_commit_func'](obj2, transaction, time.time()) - retval.append(obj2) - obj.set_reference_handle(obj2.handle) - elif isinstance(obj, gen.lib.Repository): - obj.set_name(_('Unknown')) - obj.set_type(gen.lib.RepositoryType.UNKNOWN) - elif isinstance(obj, gen.lib.MediaObject): - obj.set_path(os.path.join(IMAGE_DIR, "image-missing.png")) - obj.set_mime_type('image/png') - obj.set_description(_('Unknown')) - elif isinstance(obj, gen.lib.Note): - obj.set_type(gen.lib.NoteType.UNKNOWN); - text = _('Unknown, created to replace a missing note object.') - link_start = text.index(',') + 2 - link_end = len(text) - 1 - tag = gen.lib.StyledTextTag(gen.lib.StyledTextTagType.LINK, - 'gramps://Note/handle/%s' % explanation, - [(link_start, link_end)]) - obj.set_styledtext(gen.lib.StyledText(text, [tag])) - elif isinstance(obj, gen.lib.Tag): - if not hasattr(make_unknown, 'count'): - make_unknown.count = 1 #primitive static variable - obj.set_name(_("Unknown, was missing %(time)s (%(count)d)") % { - 'time': time.strftime('%x %X', time.localtime()), - 'count': make_unknown.count}) - make_unknown.count += 1 - else: - raise TypeError("Object if of unsupported type") - - if hasattr(obj, 'add_note'): - obj.add_note(explanation) - commit_func(obj, transaction, time.time()) - retval.append(obj) - return retval - -def create_explanation_note(dbase): - """ - When creating objects to fill missing primary objects in imported files, - those objects of type "Unknown" need a explanatory note. This funcion - provides such a note for import methods. - """ - note = gen.lib.Note( _('Objects referenced by this note ' - 'were missing in a file imported on %s.') % - time.strftime('%x %X', time.localtime())) - note.set_handle(create_id()) - note.set_gramps_id(dbase.find_next_note_gramps_id()) - # Use defaults for privacy, format and type. - return note - -def add_personref_to_family(family, person): - """ - Given a family and person, set the parent/child references in the family, - that match the person. - """ - handle = family.handle - person_handle = person.handle - if handle in person.get_family_handle_list(): - if ((person.get_gender() == gen.lib.Person.FEMALE) and - (family.get_mother_handle() is None)): - family.set_mother_handle(person_handle) - else: - # This includes cases of gen.lib.Person.UNKNOWN - if family.get_father_handle() is None: - family.set_father_handle(person_handle) - else: - family.set_mother_handle(person_handle) - if handle in person.get_parent_family_handle_list(): - childref = gen.lib.ChildRef() - childref.set_reference_handle(person_handle) - childref.set_mother_relation(gen.lib.ChildRefType.UNKNOWN) - childref.set_father_relation(gen.lib.ChildRefType.UNKNOWN) - family.add_child_ref(childref) - diff --git a/src/gen/utils/Makefile.am b/src/gen/utils/Makefile.am index 609db3704..b4ceea376 100644 --- a/src/gen/utils/Makefile.am +++ b/src/gen/utils/Makefile.am @@ -20,7 +20,8 @@ pkgpython_PYTHON = \ mactrans.py \ place.py \ referent.py \ - trans.py + trans.py \ + unknown.py pkgpyexecdir = @pkgpyexecdir@/gen/utils diff --git a/src/gen/utils/unknown.py b/src/gen/utils/unknown.py new file mode 100644 index 000000000..e35ad7af9 --- /dev/null +++ b/src/gen/utils/unknown.py @@ -0,0 +1,193 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2007 Donald N. Allingham +# Copyright (C) 2009 Gary Burton +# Copyright (C) 2011 Tim G L Lyons +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +""" +Make an 'Unknown' primary object +""" + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import time + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +import gen.lib +from Utils import create_id +from gen.ggettext import sgettext as _ + +#------------------------------------------------------------------------- +# +# make_unknown +# +#------------------------------------------------------------------------- +def make_unknown(class_arg, explanation, class_func, commit_func, transaction, + **argv): + """ + Make a primary object and set some property so that it qualifies as + "Unknown". + + Some object types need extra parameters: + Family: db, Event: type (optional), + Citation: methods to create/store source. + + Some theoretical underpinning + This function exploits the fact that all import methods basically do the + same thing: Create an object of the right type, fill it with some + attributes, store it in the database. This function does the same, so + the observation is why not use the creation and storage methods that the + import routines use themselves, that makes nice reuse of code. To do this + formally correct we would need to specify a interface (in the OOP sence) + which the import methods would need to implement. For now, that is deemed + too restrictive and here we just slip through because of the similarity in + code of both GEDCOM and XML import methods. + + :param class_arg: The argument the class_func needs, typically a kind of id. + :type class_arg: unspecified + :param explanation: Handle of a note that explains the origin of primary obj + :type explanation: str + :param class_func: Method to create primary object. + :type class_func: method + :param commit_func: Method to store primary object in db. + :type commit_func: method + :param transactino: Database transaction handle + :type transaction: str + :param argv: Possible additional parameters + :type param: unspecified + :returns: List of newly created objects. + :rtype: list + """ + retval = [] + obj = class_func(class_arg) + if isinstance(obj, gen.lib.Person): + surname = gen.lib.Surname() + surname.set_surname('Unknown') + name = gen.lib.Name() + name.add_surname(surname) + name.set_type(gen.lib.NameType.UNKNOWN) + obj.set_primary_name(name) + elif isinstance(obj, gen.lib.Family): + obj.set_relationship(gen.lib.FamilyRelType.UNKNOWN) + handle = obj.handle + if getattr(argv['db'].transaction, 'no_magic', False): + backlinks = argv['db'].find_backlink_handles( + handle, [gen.lib.Person.__name__]) + for dummy, person_handle in backlinks: + person = argv['db'].get_person_from_handle(person_handle) + add_personref_to_family(obj, person) + else: + for person in argv['db'].iter_people(): + if person._has_handle_reference('Family', handle): + add_personref_to_family(obj, person) + elif isinstance(obj, gen.lib.Event): + if 'type' in argv: + obj.set_type(argv['type']) + else: + obj.set_type(gen.lib.EventType.UNKNOWN) + elif isinstance(obj, gen.lib.Place): + obj.set_title(_('Unknown')) + elif isinstance(obj, gen.lib.Source): + obj.set_title(_('Unknown')) + elif isinstance(obj, gen.lib.Citation): + #TODO create a new source for every citation? + obj2 = argv['source_class_func'](argv['source_class_arg']) + obj2.set_title(_('Unknown')) + obj2.add_note(explanation) + argv['source_commit_func'](obj2, transaction, time.time()) + retval.append(obj2) + obj.set_reference_handle(obj2.handle) + elif isinstance(obj, gen.lib.Repository): + obj.set_name(_('Unknown')) + obj.set_type(gen.lib.RepositoryType.UNKNOWN) + elif isinstance(obj, gen.lib.MediaObject): + obj.set_path(os.path.join(IMAGE_DIR, "image-missing.png")) + obj.set_mime_type('image/png') + obj.set_description(_('Unknown')) + elif isinstance(obj, gen.lib.Note): + obj.set_type(gen.lib.NoteType.UNKNOWN); + text = _('Unknown, created to replace a missing note object.') + link_start = text.index(',') + 2 + link_end = len(text) - 1 + tag = gen.lib.StyledTextTag(gen.lib.StyledTextTagType.LINK, + 'gramps://Note/handle/%s' % explanation, + [(link_start, link_end)]) + obj.set_styledtext(gen.lib.StyledText(text, [tag])) + elif isinstance(obj, gen.lib.Tag): + if not hasattr(make_unknown, 'count'): + make_unknown.count = 1 #primitive static variable + obj.set_name(_("Unknown, was missing %(time)s (%(count)d)") % { + 'time': time.strftime('%x %X', time.localtime()), + 'count': make_unknown.count}) + make_unknown.count += 1 + else: + raise TypeError("Object if of unsupported type") + + if hasattr(obj, 'add_note'): + obj.add_note(explanation) + commit_func(obj, transaction, time.time()) + retval.append(obj) + return retval + +def create_explanation_note(dbase): + """ + When creating objects to fill missing primary objects in imported files, + those objects of type "Unknown" need a explanatory note. This funcion + provides such a note for import methods. + """ + note = gen.lib.Note( _('Objects referenced by this note ' + 'were missing in a file imported on %s.') % + time.strftime('%x %X', time.localtime())) + note.set_handle(create_id()) + note.set_gramps_id(dbase.find_next_note_gramps_id()) + # Use defaults for privacy, format and type. + return note + +def add_personref_to_family(family, person): + """ + Given a family and person, set the parent/child references in the family, + that match the person. + """ + handle = family.handle + person_handle = person.handle + if handle in person.get_family_handle_list(): + if ((person.get_gender() == gen.lib.Person.FEMALE) and + (family.get_mother_handle() is None)): + family.set_mother_handle(person_handle) + else: + # This includes cases of gen.lib.Person.UNKNOWN + if family.get_father_handle() is None: + family.set_father_handle(person_handle) + else: + family.set_mother_handle(person_handle) + if handle in person.get_parent_family_handle_list(): + childref = gen.lib.ChildRef() + childref.set_reference_handle(person_handle) + childref.set_mother_relation(gen.lib.ChildRefType.UNKNOWN) + childref.set_father_relation(gen.lib.ChildRefType.UNKNOWN) + family.add_child_ref(childref) diff --git a/src/plugins/import/importxml.py b/src/plugins/import/importxml.py index 33047f5f6..e342a779b 100644 --- a/src/plugins/import/importxml.py +++ b/src/plugins/import/importxml.py @@ -49,6 +49,7 @@ from gen.db import DbTxn from gen.db.write import CLASS_TO_KEY_MAP from gen.errors import GrampsImportError import Utils +from gen.utils.unknown import make_unknown, create_explanation_note import gen.datehandler from gen.display.name import displayer as name_displayer from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, @@ -2921,24 +2922,24 @@ class GrampsParser(UpdateCallback): [target for target in self.import_handles[orig_handle].keys() if not self.import_handles[orig_handle][target][INSTANTIATED]]] if uninstantiated: - expl_note = Utils.create_explanation_note(self.db) + expl_note = create_explanation_note(self.db) self.db.commit_note(expl_note, self.trans, time.time()) self.info.expl_note = expl_note.get_gramps_id() for orig_handle, target in uninstantiated: class_arg = {'handle': orig_handle, 'id': None, 'priv': False} if target == 'family': - objs = Utils.make_unknown(class_arg, expl_note.handle, + objs = make_unknown(class_arg, expl_note.handle, self.func_map[target][0], self.func_map[target][1], self.trans, db=self.db) elif target == 'citation': - objs = Utils.make_unknown(class_arg, expl_note.handle, + objs = make_unknown(class_arg, expl_note.handle, self.func_map[target][0], self.func_map[target][1], self.trans, source_class_func=self.func_map['source'][0], source_commit_func=self.func_map['source'][1], source_class_arg={'handle':Utils.create_id(), 'id':None, 'priv':False}) elif target == 'note': - objs = Utils.make_unknown(class_arg, expl_note.handle, + objs = make_unknown(class_arg, expl_note.handle, self.func_map[target][0], self.stop_note_asothers, self.trans) else: @@ -2946,7 +2947,7 @@ class GrampsParser(UpdateCallback): target = 'placeobj' elif target == 'media': target = 'object' - objs = Utils.make_unknown(class_arg, expl_note.handle, + objs = make_unknown(class_arg, expl_note.handle, self.func_map[target][0], self.func_map[target][1], self.trans) for obj in objs: diff --git a/src/plugins/lib/libgedcom.py b/src/plugins/lib/libgedcom.py index 2c6bc64d9..d5a9165ef 100644 --- a/src/plugins/lib/libgedcom.py +++ b/src/plugins/lib/libgedcom.py @@ -119,6 +119,7 @@ from gen.db import DbTxn from gen.updatecallback import UpdateCallback import gen.mime from gen.utils.lds import TEMPLES +from gen.utils.unknown import make_unknown, create_explanation_note import Utils from gen.datehandler._dateparser import DateParser from gen.db.dbconst import EVENT_KEY @@ -2924,7 +2925,7 @@ class GedcomParser(UpdateCallback): handle = self.__find_from_handle(gramps_id, gramps_id2handle) if msg == "FAM": - Utils.make_unknown(gramps_id, self.explanation.handle, + make_unknown(gramps_id, self.explanation.handle, class_func, commit_func, self.trans, db=self.dbase) self.__add_msg(_("Error: %(msg)s '%(gramps_id)s'" @@ -2933,7 +2934,7 @@ class GedcomParser(UpdateCallback): {'msg' : msg, 'gramps_id' : gramps_id, 'xref' : input_id}) else: - Utils.make_unknown(gramps_id, self.explanation.handle, + make_unknown(gramps_id, self.explanation.handle, class_func, commit_func, self.trans) self.missing_references +=1 self.__add_msg(_("Error: %(msg)s '%(gramps_id)s'" @@ -2943,7 +2944,7 @@ class GedcomParser(UpdateCallback): {'msg' : msg, 'gramps_id' : gramps_id, 'xref' : input_id}) - self.explanation = Utils.create_explanation_note(self.dbase) + self.explanation = create_explanation_note(self.dbase) self.missing_references = 0 previous_errors = self.number_of_errors diff --git a/src/plugins/tool/check.py b/src/plugins/tool/check.py index 0a3e2b39e..302b4aba5 100644 --- a/src/plugins/tool/check.py +++ b/src/plugins/tool/check.py @@ -66,6 +66,7 @@ import gen.lib from gen.db import DbTxn from gen.config import config import Utils +from gen.utils.unknown import make_unknown from gen.utils.file import (get_unicode_path_from_file_chooser, media_path_full, find_file) @@ -945,7 +946,7 @@ class CheckIntegrity(object): # The birth event referenced by the birth handle # does not exist in the database # This is tested by TestcaseGenerator person "Broken11" - Utils.make_unknown(birth_handle, self.explanation.handle, + make_unknown(birth_handle, self.explanation.handle, self.class_event, self.commit_event, self.trans, type=gen.lib.EventType.BIRTH) LOG(' FAIL: the person "%s" refers to a birth event' @@ -982,7 +983,7 @@ class CheckIntegrity(object): LOG(' FAIL: the person "%s" refers to a death event' ' "%s" which does not exist in the database' % (person.gramps_id, death_handle)) - Utils.make_unknown(death_handle, self.explanation.handle, + make_unknown(death_handle, self.explanation.handle, self.class_event, self.commit_event, self.trans, type=gen.lib.EventType.DEATH) self.invalid_events.add(key) @@ -1020,7 +1021,7 @@ class CheckIntegrity(object): LOG(' FAIL: the person "%s" refers to an event' ' "%s" which does not exist in the database' % (person.gramps_id, event_handle)) - Utils.make_unknown(event_handle, + make_unknown(event_handle, self.explanation.handle, self.class_event, self.commit_event, self.trans) self.invalid_events.add(key) @@ -1054,7 +1055,7 @@ class CheckIntegrity(object): LOG(' FAIL: the family "%s" refers to an event' ' "%s" which does not exist in the database' % (family.gramps_id, event_handle)) - Utils.make_unknown(event_handle, self.explanation, + make_unknown(event_handle, self.explanation, self.class_event, self.commit_event, self.trans) self.invalid_events.add(key) if none_handle: @@ -1092,7 +1093,7 @@ class CheckIntegrity(object): p = self.db.get_person_from_handle( pref.ref) if not p: # The referenced person does not exist in the database - Utils.make_unknown(pref.ref, self.explanation.handle, + make_unknown(pref.ref, self.explanation.handle, self.class_person, self.commit_person, self.trans) self.invalid_person_references.add(key) if none_handle: @@ -1118,7 +1119,7 @@ class CheckIntegrity(object): family = self.db.get_family_from_handle(family_handle) if not family: # The referenced family does not exist in the database - Utils.make_unknown(family_handle, + make_unknown(family_handle, self.explanation.handle, self.class_family, self.commit_family, self.trans, db=self.db) self.invalid_family_references.add(key) @@ -1146,7 +1147,7 @@ class CheckIntegrity(object): r = self.db.get_repository_from_handle(reporef.ref) if not r: # The referenced repository does not exist in the database - Utils.make_unknown(reporef.ref, self.explanation.handle, + make_unknown(reporef.ref, self.explanation.handle, self.class_repo, self.commit_repo, self.trans) self.invalid_repo_references.add(key) if none_handle: @@ -1176,7 +1177,7 @@ class CheckIntegrity(object): # The referenced place does not exist in the database # This is tested by TestcaseGenerator person "Broken17" # This is tested by TestcaseGenerator person "Broken18" - Utils.make_unknown(place_handle, + make_unknown(place_handle, self.explanation.handle, self.class_place, self.commit_place, self.trans) LOG(' FAIL: the person "%s" refers to an LdsOrd' @@ -1193,7 +1194,7 @@ class CheckIntegrity(object): place = self.db.get_place_from_handle(place_handle) if not place: # The referenced place does not exist in the database - Utils.make_unknown(place_handle, + make_unknown(place_handle, self.explanation.handle, self.class_place, self.commit_place, self.trans) LOG(' FAIL: the family "%s" refers to an LdsOrd' @@ -1209,7 +1210,7 @@ class CheckIntegrity(object): place = self.db.get_place_from_handle(place_handle) if not place: # The referenced place does not exist in the database - Utils.make_unknown(place_handle, + make_unknown(place_handle, self.explanation.handle, self.class_place, self.commit_place, self.trans) LOG(' FAIL: the event "%s" refers to an LdsOrd place' @@ -1351,7 +1352,7 @@ class CheckIntegrity(object): self.invalid_citation_references.add(item[1]) for bad_handle in self.invalid_citation_references: - created = Utils.make_unknown(bad_handle, self.explanation.handle, + created = make_unknown(bad_handle, self.explanation.handle, self.class_citation, self.commit_citation, self.trans, source_class_func=self.class_source, source_commit_func=self.commit_source, @@ -1379,7 +1380,7 @@ class CheckIntegrity(object): source = self.db.get_source_from_handle(source_handle) if not source: # The referenced source does not exist in the database - Utils.make_unknown(source_handle, self.explanation.handle, + make_unknown(source_handle, self.explanation.handle, self.class_source, self.commit_source, self.trans) LOG(' FAIL: the citation "%s" refers to source ' ' "%s" which does not exist in the database' % @@ -1501,7 +1502,7 @@ class CheckIntegrity(object): self.invalid_media_references.add(item[1]) for bad_handle in self.invalid_media_references: - Utils.make_unknown(bad_handle, self.explanation.handle, + make_unknown(bad_handle, self.explanation.handle, self.class_object, self.commit_object, self.trans) if len (self.invalid_media_references) == 0: @@ -1669,7 +1670,7 @@ class CheckIntegrity(object): self.invalid_note_references.add(item[1]) for bad_handle in self.invalid_note_references: - Utils.make_unknown(bad_handle, self.explanation.handle, + make_unknown(bad_handle, self.explanation.handle, self.class_note, self.commit_note, self.trans) if len (self.invalid_note_references) == 0: @@ -1757,7 +1758,7 @@ class CheckIntegrity(object): self.invalid_tag_references.add(item[1]) for bad_handle in self.invalid_tag_references: - Utils.make_unknown(bad_handle, None, self.class_tag, + make_unknown(bad_handle, None, self.class_tag, self.commit_tag, self.trans) if len(self.invalid_tag_references) == 0: