GEPS008: Create new module for make_unknown functions

svn: r19907
This commit is contained in:
Nick Hall 2012-06-23 23:18:27 +00:00
parent 2a0b009bdf
commit 5e1535e125
7 changed files with 222 additions and 175 deletions

View File

@ -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

View File

@ -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)

View File

@ -20,7 +20,8 @@ pkgpython_PYTHON = \
mactrans.py \
place.py \
referent.py \
trans.py
trans.py \
unknown.py
pkgpyexecdir = @pkgpyexecdir@/gen/utils

193
src/gen/utils/unknown.py Normal file
View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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: