5466: On import and Check and Repair need to check references to absent objects
svn: r19327
This commit is contained in:
parent
df068471bb
commit
75d54125db
153
src/Utils.py
153
src/Utils.py
@ -53,7 +53,7 @@ from GrampsLocale import codeset
|
||||
from Date import Date
|
||||
import DateHandler
|
||||
|
||||
from const import TEMP_DIR, USER_HOME, GRAMPS_UUID
|
||||
from const import TEMP_DIR, USER_HOME, GRAMPS_UUID, IMAGE_DIR
|
||||
import constfunc
|
||||
from gen.ggettext import sgettext as _
|
||||
|
||||
@ -1581,3 +1581,154 @@ def format_time(secs):
|
||||
t = time.localtime(secs)
|
||||
d = Date(t.tm_year, t.tm_mon, t.tm_mday)
|
||||
return DateHandler.displayer.display(d) + time.strftime(' %X', t)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# 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 %s (%d)") %
|
||||
(time.strftime('%x %X', time.localtime()),
|
||||
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)
|
||||
|
||||
|
@ -128,3 +128,27 @@ class TagBase(object):
|
||||
"""
|
||||
for addendum in acquisition.get_tag_list():
|
||||
self.add_tag(addendum)
|
||||
|
||||
def replace_tag_references(self, old_handle, new_handle):
|
||||
"""
|
||||
Replace references to note handles in the list of this object and
|
||||
merge equivalent entries.
|
||||
|
||||
:param old_handle: The note handle to be replaced.
|
||||
:type old_handle: str
|
||||
:param new_handle: The note handle to replace the old one with.
|
||||
:type new_handle: str
|
||||
"""
|
||||
refs_list = self.tag_list[:]
|
||||
new_ref = None
|
||||
if new_handle in self.tag_list:
|
||||
new_ref = new_handle
|
||||
n_replace = refs_list.count(old_handle)
|
||||
for ix_replace in xrange(n_replace):
|
||||
idx = refs_list.index(old_handle)
|
||||
if new_ref:
|
||||
self.tag_list.pop(idx)
|
||||
refs_list.pop(idx)
|
||||
else:
|
||||
self.tag_list[idx] = new_handle
|
||||
|
||||
|
@ -47,6 +47,7 @@ from QuestionDialog import ErrorDialog, WarningDialog
|
||||
import gen.mime
|
||||
import gen.lib
|
||||
from gen.db import DbTxn
|
||||
from gen.db.write import CLASS_TO_KEY_MAP
|
||||
from Errors import GrampsImportError
|
||||
import Utils
|
||||
import DateHandler
|
||||
@ -85,6 +86,9 @@ EVENT_FAMILY_STR = _("%(event_name)s of %(family)s")
|
||||
# feature requests 2356, 1658: avoid genitive form
|
||||
EVENT_PERSON_STR = _("%(event_name)s of %(person)s")
|
||||
|
||||
HANDLE = 0
|
||||
INSTANTIATED = 1
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Importing data into the currently open database.
|
||||
@ -215,6 +219,8 @@ class ImportInfo(object):
|
||||
"""
|
||||
self.data_mergecandidate = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]
|
||||
self.data_newobject = [0] * 10
|
||||
self.data_unknownobject = [0] * 10
|
||||
self.expl_note = ''
|
||||
self.data_relpath = False
|
||||
|
||||
def add(self, category, key, obj, sec_obj=None):
|
||||
@ -227,6 +233,8 @@ class ImportInfo(object):
|
||||
self._extract_mergeinfo(key, obj, sec_obj)
|
||||
elif category == 'new-object':
|
||||
self.data_newobject[self.key2data[key]] += 1
|
||||
elif category == 'unknown-object':
|
||||
self.data_unknownobject[self.key2data[key]] += 1
|
||||
elif category == 'relative-path':
|
||||
self.data_relpath = True
|
||||
|
||||
@ -286,7 +294,20 @@ class ImportInfo(object):
|
||||
}
|
||||
txt = _("Number of new objects imported:\n")
|
||||
for key in self.keyorder:
|
||||
txt += key2string[key] % self.data_newobject[self.key2data[key]]
|
||||
if any(self.data_unknownobject):
|
||||
strng = key2string[key][0:-1] + ' (%d)\n'
|
||||
txt += strng % (self.data_newobject[self.key2data[key]],
|
||||
self.data_unknownobject[self.key2data[key]])
|
||||
else:
|
||||
txt += key2string[key] % self.data_newobject[self.key2data[key]]
|
||||
if any(self.data_unknownobject):
|
||||
txt += _("\n The imported file was not self-contained.\n"
|
||||
"To correct for that, %d objects were created and\n"
|
||||
"their typifying attribute was set to 'Unknown'.\n"
|
||||
"The breakdown per category is depicted by the\n"
|
||||
"number in parentheses. Where possible these\n"
|
||||
"'Unkown' objects are referenced by note %s.\n"
|
||||
) % (sum(self.data_unknownobject), self.expl_note)
|
||||
if self.data_relpath:
|
||||
txt += _("\nMedia objects with relative paths have been\n"
|
||||
"imported. These paths are considered relative to\n"
|
||||
@ -631,7 +652,7 @@ class GrampsParser(UpdateCallback):
|
||||
"stitle": (None, self.stop_stitle),
|
||||
"street": (None, self.stop_street),
|
||||
"style": (self.start_style, None),
|
||||
"tag": (self.start_tag, None),
|
||||
"tag": (self.start_tag, self.stop_tag),
|
||||
"tagref": (self.start_tagref, None),
|
||||
"tags": (None, None),
|
||||
"text": (None, self.stop_text),
|
||||
@ -666,9 +687,10 @@ class GrampsParser(UpdateCallback):
|
||||
:rtype: str
|
||||
"""
|
||||
handle = str(handle.replace('_', ''))
|
||||
if (handle in self.import_handles and
|
||||
target in self.import_handles[handle]):
|
||||
handle = self.import_handles[handle][target]
|
||||
orig_handle = handle
|
||||
if (orig_handle in self.import_handles and
|
||||
target in self.import_handles[orig_handle]):
|
||||
handle = self.import_handles[handle][target][HANDLE]
|
||||
if not callable(prim_obj):
|
||||
# This method is called by a start_<primary_object> method.
|
||||
get_raw_obj_data = {"person": self.db.get_raw_person_data,
|
||||
@ -683,15 +705,15 @@ class GrampsParser(UpdateCallback):
|
||||
"tag": self.db.get_raw_tag_data}[target]
|
||||
raw = get_raw_obj_data(handle)
|
||||
prim_obj.unserialize(raw)
|
||||
self.import_handles[orig_handle][target][INSTANTIATED] = True
|
||||
return handle
|
||||
elif handle in self.import_handles:
|
||||
LOG.warn("The file you import contains duplicate handles "
|
||||
"which is illegal and being fixed now.")
|
||||
orig_handle = handle
|
||||
handle = Utils.create_id()
|
||||
while handle in self.import_handles:
|
||||
handle = Utils.create_id()
|
||||
self.import_handles[orig_handle][target] = handle
|
||||
self.import_handles[orig_handle][target] = [handle, False]
|
||||
else:
|
||||
orig_handle = handle
|
||||
if self.replace_import_handle:
|
||||
@ -711,9 +733,11 @@ class GrampsParser(UpdateCallback):
|
||||
"tag": self.db.has_tag_handle}[target]
|
||||
while has_handle_func(handle):
|
||||
handle = Utils.create_id()
|
||||
self.import_handles[orig_handle] = {target: handle}
|
||||
self.import_handles[orig_handle] = {target: [handle, False]}
|
||||
if callable(prim_obj): # method is called by a reference
|
||||
prim_obj = prim_obj()
|
||||
else:
|
||||
self.import_handles[orig_handle][target][INSTANTIATED] = True
|
||||
prim_obj.set_handle(handle)
|
||||
if target == "tag":
|
||||
self.db.add_tag(prim_obj, self.trans)
|
||||
@ -878,6 +902,7 @@ class GrampsParser(UpdateCallback):
|
||||
"path in the Preferences."
|
||||
) % self.mediapath )
|
||||
|
||||
self.fix_not_instantiated()
|
||||
for key in self.func_map.keys():
|
||||
del self.func_map[key]
|
||||
del self.func_map
|
||||
@ -1050,6 +1075,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.placeobj.title = attrs.get('title', '')
|
||||
self.locations = 0
|
||||
self.update(self.p.CurrentLineNumber)
|
||||
return self.placeobj
|
||||
|
||||
def start_location(self, attrs):
|
||||
"""Bypass the function calls for this one, since it appears to
|
||||
@ -1152,6 +1178,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.event.private = bool(attrs.get("priv"))
|
||||
self.event.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', EVENT_KEY, self.event)
|
||||
return self.event
|
||||
|
||||
def start_eventref(self, attrs):
|
||||
"""
|
||||
@ -1236,7 +1263,7 @@ class GrampsParser(UpdateCallback):
|
||||
|
||||
# This is new XML, so we are guaranteed to have a handle ref
|
||||
handle = attrs['hlink'].replace('_', '')
|
||||
handle = self.import_handles[handle][target]
|
||||
handle = self.import_handles[handle][target][HANDLE]
|
||||
# Due to pre 2.2.9 bug, bookmarks might be handle of other object
|
||||
# Make sure those are filtered out.
|
||||
# Bookmarks are at end, so all handle must exist before we do bookmrks
|
||||
@ -1324,6 +1351,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.person.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', PERSON_KEY, self.person)
|
||||
self.convert_marker(attrs, self.person)
|
||||
return self.person
|
||||
|
||||
def start_people(self, attrs):
|
||||
"""
|
||||
@ -1459,6 +1487,7 @@ class GrampsParser(UpdateCallback):
|
||||
if 'type' in attrs:
|
||||
self.family.type.set_from_xml_str(attrs["type"])
|
||||
self.convert_marker(attrs, self.family)
|
||||
return self.family
|
||||
|
||||
def start_rel(self, attrs):
|
||||
if 'type' in attrs:
|
||||
@ -1606,7 +1635,7 @@ class GrampsParser(UpdateCallback):
|
||||
val = "gramps://%s/handle/%s" % (
|
||||
match.group('object_class'),
|
||||
self.import_handles[match.group('handle')]
|
||||
[target])
|
||||
[target][HANDLE])
|
||||
tagvalue = gen.lib.StyledTextTagType.STYLE_TYPE[int(tagtype)](val)
|
||||
except KeyError:
|
||||
tagvalue = None
|
||||
@ -1629,10 +1658,17 @@ class GrampsParser(UpdateCallback):
|
||||
self.inaugurate(attrs['handle'], "tag", self.tag)
|
||||
self.tag.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', TAG_KEY, self.tag)
|
||||
self.tag.set_name(attrs['name'])
|
||||
self.tag.set_color(attrs['color'])
|
||||
self.tag.set_priority(int(attrs['priority']))
|
||||
self.tag.set_name(attrs.get('name', _('Unknown when imported')))
|
||||
self.tag.set_color(attrs.get('color', '#000000000000'))
|
||||
self.tag.set_priority(int(attrs.get('priority', 0)))
|
||||
return self.tag
|
||||
|
||||
def stop_tag(self, *tag):
|
||||
if self.note is not None:
|
||||
# Styled text tag in notes (prior to v1.4.0)
|
||||
return
|
||||
self.db.commit_tag(self.tag, self.trans, self.tag.get_change_time())
|
||||
self.tag = None
|
||||
|
||||
def start_tagref(self, attrs):
|
||||
"""
|
||||
@ -1685,8 +1721,8 @@ class GrampsParser(UpdateCallback):
|
||||
self.note.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', NOTE_KEY, self.note)
|
||||
self.note.format = int(attrs.get('format', gen.lib.Note.FLOWED))
|
||||
self.note.type.set_from_xml_str(attrs['type'])
|
||||
|
||||
self.note.type.set_from_xml_str(attrs.get('type',
|
||||
gen.lib.NoteType.UNKNOWN))
|
||||
self.convert_marker(attrs, self.note)
|
||||
|
||||
# Since StyledText was introduced (XML v1.3.0) the clear text
|
||||
@ -1764,6 +1800,7 @@ class GrampsParser(UpdateCallback):
|
||||
#set correct change time
|
||||
self.db.commit_note(self.note, self.trans, self.change)
|
||||
self.info.add('new-object', NOTE_KEY, self.note)
|
||||
return self.note
|
||||
|
||||
def start_noteref(self, attrs):
|
||||
"""
|
||||
@ -1877,6 +1914,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.citation.change = int(attrs.get('change', self.change))
|
||||
self.citation.confidence = self.conf # default
|
||||
self.info.add('new-object', CITATION_KEY, self.citation)
|
||||
return self.citation
|
||||
|
||||
def start_sourceref(self, attrs):
|
||||
"""
|
||||
@ -1929,6 +1967,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.source.private = bool(attrs.get("priv"))
|
||||
self.source.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', SOURCE_KEY, self.source)
|
||||
return self.source
|
||||
|
||||
def start_reporef(self, attrs):
|
||||
"""
|
||||
@ -2016,6 +2055,7 @@ class GrampsParser(UpdateCallback):
|
||||
src = attrs.get("src", '')
|
||||
if src:
|
||||
self.object.path = src
|
||||
return self.object
|
||||
|
||||
def start_repo(self, attrs):
|
||||
"""
|
||||
@ -2041,6 +2081,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.repo.private = bool(attrs.get("priv"))
|
||||
self.repo.change = int(attrs.get('change', self.change))
|
||||
self.info.add('new-object', REPOSITORY_KEY, self.repo)
|
||||
return self.repo
|
||||
|
||||
def stop_people(self, *tag):
|
||||
pass
|
||||
@ -2776,6 +2817,10 @@ class GrampsParser(UpdateCallback):
|
||||
self.db.commit_note(self.note, self.trans, self.note.get_change_time())
|
||||
self.note = None
|
||||
|
||||
def stop_note_asothers(self, *tag):
|
||||
self.db.commit_note(self.note, self.trans, self.note.get_change_time())
|
||||
self.note = None
|
||||
|
||||
def stop_research(self, tag):
|
||||
self.owner.set_name(self.resname)
|
||||
self.owner.set_address(self.resaddr)
|
||||
@ -2872,6 +2917,44 @@ class GrampsParser(UpdateCallback):
|
||||
tag_handle = tag.get_handle()
|
||||
obj.add_tag(tag_handle)
|
||||
|
||||
def fix_not_instantiated(self):
|
||||
uninstantiated = [(orig_handle, target) for orig_handle in
|
||||
self.import_handles.keys() if
|
||||
[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)
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
self.func_map[target][0], self.stop_note_asothers,
|
||||
self.trans)
|
||||
else:
|
||||
if target == 'place':
|
||||
target = 'placeobj'
|
||||
elif target == 'media':
|
||||
target = 'object'
|
||||
objs = Utils.make_unknown(class_arg, expl_note.handle,
|
||||
self.func_map[target][0], self.func_map[target][1],
|
||||
self.trans)
|
||||
for obj in objs:
|
||||
key = CLASS_TO_KEY_MAP[obj.__class__.__name__]
|
||||
self.info.add('unknown-object', key, obj)
|
||||
|
||||
def append_value(orig, val):
|
||||
if orig:
|
||||
return "%s, %s" % (orig, val)
|
||||
|
@ -2565,6 +2565,7 @@ class GedcomParser(UpdateCallback):
|
||||
src.set_title(title)
|
||||
self.dbase.add_source(src, self.trans)
|
||||
|
||||
self.__check_xref()
|
||||
self.dbase.enable_signals()
|
||||
self.dbase.request_rebuild()
|
||||
if self.number_of_errors == 0:
|
||||
@ -2913,6 +2914,131 @@ class GedcomParser(UpdateCallback):
|
||||
"""
|
||||
self.backoff = True
|
||||
|
||||
def __check_xref(self):
|
||||
|
||||
def __check(map, trans, class_func, commit_func, gramps_id2handle, msg):
|
||||
for input_id, gramps_id in map.map().iteritems():
|
||||
# Check whether an object exists for the mapped gramps_id
|
||||
if not trans.get(str(gramps_id)):
|
||||
handle = self.__find_from_handle(gramps_id,
|
||||
gramps_id2handle)
|
||||
if msg == "FAM":
|
||||
Utils.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'"
|
||||
" (input as @%(xref)s@) not in input"
|
||||
" GEDCOM. Record synthesised") %
|
||||
{'msg' : msg, 'gramps_id' : gramps_id,
|
||||
'xref' : input_id})
|
||||
else:
|
||||
Utils.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'"
|
||||
" (input as @%(xref)s@) not in input"
|
||||
" GEDCOM. Record with typifying"
|
||||
" attribute 'Unknown' created") %
|
||||
{'msg' : msg, 'gramps_id' : gramps_id,
|
||||
'xref' : input_id})
|
||||
|
||||
self.explanation = Utils.create_explanation_note(self.dbase)
|
||||
|
||||
self.missing_references = 0
|
||||
previous_errors = self.number_of_errors
|
||||
__check(self.pid_map, self.dbase.id_trans, self.__find_or_create_person,
|
||||
self.dbase.commit_person, self.gid2id, "INDI")
|
||||
__check(self.fid_map, self.dbase.fid_trans, self.__find_or_create_family,
|
||||
self.dbase.commit_family, self.fid2id, "FAM")
|
||||
__check(self.sid_map, self.dbase.sid_trans, self.__find_or_create_source,
|
||||
self.dbase.commit_source, self.sid2id, "SOUR")
|
||||
__check(self.oid_map, self.dbase.oid_trans, self.__find_or_create_object,
|
||||
self.dbase.commit_media_object, self.oid2id, "OBJE")
|
||||
__check(self.rid_map, self.dbase.rid_trans, self.__find_or_create_repository,
|
||||
self.dbase.commit_repository, self.rid2id, "REPO")
|
||||
__check(self.nid_map, self.dbase.nid_trans, self.__find_or_create_note,
|
||||
self.dbase.commit_note, self.nid2id, "NOTE")
|
||||
|
||||
# Check persons membership in referenced families
|
||||
def __input_fid(gramps_id):
|
||||
for (k,v) in self.fid_map.map().iteritems():
|
||||
if v == gramps_id:
|
||||
return k
|
||||
|
||||
for input_id, gramps_id in self.pid_map.map().iteritems():
|
||||
person_handle = self.__find_from_handle(gramps_id, self.gid2id)
|
||||
person = self.dbase.get_person_from_handle(person_handle)
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.dbase.get_family_from_handle(family_handle)
|
||||
if family and family.get_father_handle() != person_handle and \
|
||||
family.get_mother_handle() != person_handle:
|
||||
person.remove_family_handle(family_handle)
|
||||
self.dbase.commit_person(person, self.trans)
|
||||
self.__add_msg(_("Error: family '%(family)s' (input as"
|
||||
" @%(orig_family)s@) person %(person)s"
|
||||
" (input as %(orig_person)s) is not a"
|
||||
" member of the referenced family."
|
||||
" Family reference removed from person") %
|
||||
{'family' : family.gramps_id,
|
||||
'orig_family' :
|
||||
__input_fid(family.gramps_id),
|
||||
'person' : person.gramps_id,
|
||||
'orig_person' : input_id})
|
||||
|
||||
def __input_pid(gramps_id):
|
||||
for (k,v) in self.pid_map.map().iteritems():
|
||||
if v == gramps_id:
|
||||
return k
|
||||
|
||||
for input_id, gramps_id in self.fid_map.map().iteritems():
|
||||
family_handle = self.__find_from_handle(gramps_id, self.fid2id)
|
||||
family = self.dbase.get_family_from_handle(family_handle)
|
||||
father_handle = family.get_father_handle()
|
||||
mother_handle = family.get_mother_handle()
|
||||
|
||||
if father_handle:
|
||||
father = self.dbase.get_person_from_handle(father_handle)
|
||||
if father and \
|
||||
family_handle not in father.get_family_handle_list():
|
||||
father.add_family_handle(family_handle)
|
||||
self.dbase.commit_person(father, self.trans)
|
||||
self.__add_msg("Error: family '%(family)s' (input as"
|
||||
" @%(orig_family)s@) father '%(father)s'"
|
||||
" (input as '%(orig_father)s') does not refer"
|
||||
" back to the family. Reference added." %
|
||||
{'family' : family.gramps_id,
|
||||
'orig_family' : input_id,
|
||||
'father' : father.gramps_id,
|
||||
'orig_father' :
|
||||
__input_pid(father.gramps_id)})
|
||||
|
||||
if mother_handle:
|
||||
mother = self.dbase.get_person_from_handle(mother_handle)
|
||||
if mother and \
|
||||
family_handle not in mother.get_family_handle_list():
|
||||
mother.add_family_handle(family_handle)
|
||||
self.dbase.commit_person(mother, self.trans)
|
||||
self.__add_msg("Error: family '%(family)s' (input as"
|
||||
" @%(orig_family)s@) mother '%(mother)s'"
|
||||
" (input as '%(orig_mother)s') does not refer"
|
||||
" back to the family. Reference added." %
|
||||
{'family' : family.gramps_id,
|
||||
'orig_family' : input_id,
|
||||
'mother' : mother.gramps_id,
|
||||
'orig_mother' :
|
||||
__input_pid(mother.gramps_id)})
|
||||
|
||||
if self.missing_references:
|
||||
self.dbase.commit_note(self.explanation, self.trans, time.time())
|
||||
txt = _("\nThe imported file was not self-contained.\n"
|
||||
"To correct for that, %d objects were created and\n"
|
||||
"their typifying attribute was set to 'Unknown'.\n"
|
||||
"Where possible these 'Unkown' objects are \n"
|
||||
"referenced by note %s.\n"
|
||||
) % (self.missing_references, self.explanation.gramps_id)
|
||||
self.__add_msg(txt)
|
||||
self.number_of_errors -= 1
|
||||
|
||||
def __parse_trailer(self):
|
||||
"""
|
||||
Looks for the expected TRLR token
|
||||
@ -3317,6 +3443,7 @@ class GedcomParser(UpdateCallback):
|
||||
if line.data and line.data[0] == '@':
|
||||
# Reference to a named multimedia object defined elsewhere
|
||||
gramps_id = self.oid_map[line.data]
|
||||
|
||||
handle = self.__find_object_handle(gramps_id)
|
||||
ref = gen.lib.MediaRef()
|
||||
ref.set_reference_handle(handle)
|
||||
@ -6835,6 +6962,13 @@ class GedcomParser(UpdateCallback):
|
||||
self.inline_srcs[title] = handle
|
||||
else:
|
||||
src = self.__find_or_create_source(self.sid_map[line.data])
|
||||
# We need to set the title to the cross reference identifier of the
|
||||
# SOURce record, just in case we never find the source record. If we
|
||||
# din't find the source record, then the source object would have
|
||||
# got deleted by Chack and repair because the record is empty. If we
|
||||
# find the source record, the title is overwritten in
|
||||
# __source_title.
|
||||
src.set_title(line.data)
|
||||
self.dbase.commit_source(src, self.trans)
|
||||
self.__parse_source_reference(citation, level, src.handle, state)
|
||||
citation.set_reference_handle(src.handle)
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user