5466: On import and Check and Repair need to check references to absent objects
svn: r19328
This commit is contained in:
parent
ac3318e992
commit
a5cdd339ed
153
src/Utils.py
153
src/Utils.py
@ -53,7 +53,7 @@ from GrampsLocale import codeset
|
|||||||
from Date import Date
|
from Date import Date
|
||||||
import DateHandler
|
import DateHandler
|
||||||
|
|
||||||
from const import TEMP_DIR, USER_HOME, GRAMPS_UUID
|
from const import TEMP_DIR, USER_HOME, GRAMPS_UUID, IMAGE_DIR
|
||||||
import constfunc
|
import constfunc
|
||||||
from gen.ggettext import sgettext as _
|
from gen.ggettext import sgettext as _
|
||||||
|
|
||||||
@ -1581,3 +1581,154 @@ def format_time(secs):
|
|||||||
t = time.localtime(secs)
|
t = time.localtime(secs)
|
||||||
d = Date(t.tm_year, t.tm_mon, t.tm_mday)
|
d = Date(t.tm_year, t.tm_mon, t.tm_mday)
|
||||||
return DateHandler.displayer.display(d) + time.strftime(' %X', t)
|
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():
|
for addendum in acquisition.get_tag_list():
|
||||||
self.add_tag(addendum)
|
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.mime
|
||||||
import gen.lib
|
import gen.lib
|
||||||
from gen.db import DbTxn
|
from gen.db import DbTxn
|
||||||
|
from gen.db.write import CLASS_TO_KEY_MAP
|
||||||
from Errors import GrampsImportError
|
from Errors import GrampsImportError
|
||||||
import Utils
|
import Utils
|
||||||
import DateHandler
|
import DateHandler
|
||||||
@ -85,6 +86,9 @@ EVENT_FAMILY_STR = _("%(event_name)s of %(family)s")
|
|||||||
# feature requests 2356, 1658: avoid genitive form
|
# feature requests 2356, 1658: avoid genitive form
|
||||||
EVENT_PERSON_STR = _("%(event_name)s of %(person)s")
|
EVENT_PERSON_STR = _("%(event_name)s of %(person)s")
|
||||||
|
|
||||||
|
HANDLE = 0
|
||||||
|
INSTANTIATED = 1
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Importing data into the currently open database.
|
# Importing data into the currently open database.
|
||||||
@ -215,6 +219,8 @@ class ImportInfo(object):
|
|||||||
"""
|
"""
|
||||||
self.data_mergecandidate = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]
|
self.data_mergecandidate = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]
|
||||||
self.data_newobject = [0] * 10
|
self.data_newobject = [0] * 10
|
||||||
|
self.data_unknownobject = [0] * 10
|
||||||
|
self.expl_note = ''
|
||||||
self.data_relpath = False
|
self.data_relpath = False
|
||||||
|
|
||||||
def add(self, category, key, obj, sec_obj=None):
|
def add(self, category, key, obj, sec_obj=None):
|
||||||
@ -227,6 +233,8 @@ class ImportInfo(object):
|
|||||||
self._extract_mergeinfo(key, obj, sec_obj)
|
self._extract_mergeinfo(key, obj, sec_obj)
|
||||||
elif category == 'new-object':
|
elif category == 'new-object':
|
||||||
self.data_newobject[self.key2data[key]] += 1
|
self.data_newobject[self.key2data[key]] += 1
|
||||||
|
elif category == 'unknown-object':
|
||||||
|
self.data_unknownobject[self.key2data[key]] += 1
|
||||||
elif category == 'relative-path':
|
elif category == 'relative-path':
|
||||||
self.data_relpath = True
|
self.data_relpath = True
|
||||||
|
|
||||||
@ -286,7 +294,20 @@ class ImportInfo(object):
|
|||||||
}
|
}
|
||||||
txt = _("Number of new objects imported:\n")
|
txt = _("Number of new objects imported:\n")
|
||||||
for key in self.keyorder:
|
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:
|
if self.data_relpath:
|
||||||
txt += _("\nMedia objects with relative paths have been\n"
|
txt += _("\nMedia objects with relative paths have been\n"
|
||||||
"imported. These paths are considered relative to\n"
|
"imported. These paths are considered relative to\n"
|
||||||
@ -631,7 +652,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
"stitle": (None, self.stop_stitle),
|
"stitle": (None, self.stop_stitle),
|
||||||
"street": (None, self.stop_street),
|
"street": (None, self.stop_street),
|
||||||
"style": (self.start_style, None),
|
"style": (self.start_style, None),
|
||||||
"tag": (self.start_tag, None),
|
"tag": (self.start_tag, self.stop_tag),
|
||||||
"tagref": (self.start_tagref, None),
|
"tagref": (self.start_tagref, None),
|
||||||
"tags": (None, None),
|
"tags": (None, None),
|
||||||
"text": (None, self.stop_text),
|
"text": (None, self.stop_text),
|
||||||
@ -666,9 +687,10 @@ class GrampsParser(UpdateCallback):
|
|||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
handle = str(handle.replace('_', ''))
|
handle = str(handle.replace('_', ''))
|
||||||
if (handle in self.import_handles and
|
orig_handle = handle
|
||||||
target in self.import_handles[handle]):
|
if (orig_handle in self.import_handles and
|
||||||
handle = self.import_handles[handle][target]
|
target in self.import_handles[orig_handle]):
|
||||||
|
handle = self.import_handles[handle][target][HANDLE]
|
||||||
if not callable(prim_obj):
|
if not callable(prim_obj):
|
||||||
# This method is called by a start_<primary_object> method.
|
# This method is called by a start_<primary_object> method.
|
||||||
get_raw_obj_data = {"person": self.db.get_raw_person_data,
|
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]
|
"tag": self.db.get_raw_tag_data}[target]
|
||||||
raw = get_raw_obj_data(handle)
|
raw = get_raw_obj_data(handle)
|
||||||
prim_obj.unserialize(raw)
|
prim_obj.unserialize(raw)
|
||||||
|
self.import_handles[orig_handle][target][INSTANTIATED] = True
|
||||||
return handle
|
return handle
|
||||||
elif handle in self.import_handles:
|
elif handle in self.import_handles:
|
||||||
LOG.warn("The file you import contains duplicate handles "
|
LOG.warn("The file you import contains duplicate handles "
|
||||||
"which is illegal and being fixed now.")
|
"which is illegal and being fixed now.")
|
||||||
orig_handle = handle
|
|
||||||
handle = Utils.create_id()
|
handle = Utils.create_id()
|
||||||
while handle in self.import_handles:
|
while handle in self.import_handles:
|
||||||
handle = Utils.create_id()
|
handle = Utils.create_id()
|
||||||
self.import_handles[orig_handle][target] = handle
|
self.import_handles[orig_handle][target] = [handle, False]
|
||||||
else:
|
else:
|
||||||
orig_handle = handle
|
orig_handle = handle
|
||||||
if self.replace_import_handle:
|
if self.replace_import_handle:
|
||||||
@ -711,9 +733,11 @@ class GrampsParser(UpdateCallback):
|
|||||||
"tag": self.db.has_tag_handle}[target]
|
"tag": self.db.has_tag_handle}[target]
|
||||||
while has_handle_func(handle):
|
while has_handle_func(handle):
|
||||||
handle = Utils.create_id()
|
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
|
if callable(prim_obj): # method is called by a reference
|
||||||
prim_obj = prim_obj()
|
prim_obj = prim_obj()
|
||||||
|
else:
|
||||||
|
self.import_handles[orig_handle][target][INSTANTIATED] = True
|
||||||
prim_obj.set_handle(handle)
|
prim_obj.set_handle(handle)
|
||||||
if target == "tag":
|
if target == "tag":
|
||||||
self.db.add_tag(prim_obj, self.trans)
|
self.db.add_tag(prim_obj, self.trans)
|
||||||
@ -878,6 +902,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
"path in the Preferences."
|
"path in the Preferences."
|
||||||
) % self.mediapath )
|
) % self.mediapath )
|
||||||
|
|
||||||
|
self.fix_not_instantiated()
|
||||||
for key in self.func_map.keys():
|
for key in self.func_map.keys():
|
||||||
del self.func_map[key]
|
del self.func_map[key]
|
||||||
del self.func_map
|
del self.func_map
|
||||||
@ -1050,6 +1075,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.placeobj.title = attrs.get('title', '')
|
self.placeobj.title = attrs.get('title', '')
|
||||||
self.locations = 0
|
self.locations = 0
|
||||||
self.update(self.p.CurrentLineNumber)
|
self.update(self.p.CurrentLineNumber)
|
||||||
|
return self.placeobj
|
||||||
|
|
||||||
def start_location(self, attrs):
|
def start_location(self, attrs):
|
||||||
"""Bypass the function calls for this one, since it appears to
|
"""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.private = bool(attrs.get("priv"))
|
||||||
self.event.change = int(attrs.get('change', self.change))
|
self.event.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', EVENT_KEY, self.event)
|
self.info.add('new-object', EVENT_KEY, self.event)
|
||||||
|
return self.event
|
||||||
|
|
||||||
def start_eventref(self, attrs):
|
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
|
# This is new XML, so we are guaranteed to have a handle ref
|
||||||
handle = attrs['hlink'].replace('_', '')
|
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
|
# Due to pre 2.2.9 bug, bookmarks might be handle of other object
|
||||||
# Make sure those are filtered out.
|
# Make sure those are filtered out.
|
||||||
# Bookmarks are at end, so all handle must exist before we do bookmrks
|
# 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.person.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', PERSON_KEY, self.person)
|
self.info.add('new-object', PERSON_KEY, self.person)
|
||||||
self.convert_marker(attrs, self.person)
|
self.convert_marker(attrs, self.person)
|
||||||
|
return self.person
|
||||||
|
|
||||||
def start_people(self, attrs):
|
def start_people(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -1459,6 +1487,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
if 'type' in attrs:
|
if 'type' in attrs:
|
||||||
self.family.type.set_from_xml_str(attrs["type"])
|
self.family.type.set_from_xml_str(attrs["type"])
|
||||||
self.convert_marker(attrs, self.family)
|
self.convert_marker(attrs, self.family)
|
||||||
|
return self.family
|
||||||
|
|
||||||
def start_rel(self, attrs):
|
def start_rel(self, attrs):
|
||||||
if 'type' in attrs:
|
if 'type' in attrs:
|
||||||
@ -1606,7 +1635,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
val = "gramps://%s/handle/%s" % (
|
val = "gramps://%s/handle/%s" % (
|
||||||
match.group('object_class'),
|
match.group('object_class'),
|
||||||
self.import_handles[match.group('handle')]
|
self.import_handles[match.group('handle')]
|
||||||
[target])
|
[target][HANDLE])
|
||||||
tagvalue = gen.lib.StyledTextTagType.STYLE_TYPE[int(tagtype)](val)
|
tagvalue = gen.lib.StyledTextTagType.STYLE_TYPE[int(tagtype)](val)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
tagvalue = None
|
tagvalue = None
|
||||||
@ -1629,10 +1658,17 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.inaugurate(attrs['handle'], "tag", self.tag)
|
self.inaugurate(attrs['handle'], "tag", self.tag)
|
||||||
self.tag.change = int(attrs.get('change', self.change))
|
self.tag.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', TAG_KEY, self.tag)
|
self.info.add('new-object', TAG_KEY, self.tag)
|
||||||
self.tag.set_name(attrs['name'])
|
self.tag.set_name(attrs.get('name', _('Unknown when imported')))
|
||||||
self.tag.set_color(attrs['color'])
|
self.tag.set_color(attrs.get('color', '#000000000000'))
|
||||||
self.tag.set_priority(int(attrs['priority']))
|
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.db.commit_tag(self.tag, self.trans, self.tag.get_change_time())
|
||||||
|
self.tag = None
|
||||||
|
|
||||||
def start_tagref(self, attrs):
|
def start_tagref(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -1685,8 +1721,8 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.note.change = int(attrs.get('change', self.change))
|
self.note.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', NOTE_KEY, self.note)
|
self.info.add('new-object', NOTE_KEY, self.note)
|
||||||
self.note.format = int(attrs.get('format', gen.lib.Note.FLOWED))
|
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)
|
self.convert_marker(attrs, self.note)
|
||||||
|
|
||||||
# Since StyledText was introduced (XML v1.3.0) the clear text
|
# Since StyledText was introduced (XML v1.3.0) the clear text
|
||||||
@ -1764,6 +1800,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
#set correct change time
|
#set correct change time
|
||||||
self.db.commit_note(self.note, self.trans, self.change)
|
self.db.commit_note(self.note, self.trans, self.change)
|
||||||
self.info.add('new-object', NOTE_KEY, self.note)
|
self.info.add('new-object', NOTE_KEY, self.note)
|
||||||
|
return self.note
|
||||||
|
|
||||||
def start_noteref(self, attrs):
|
def start_noteref(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -1877,6 +1914,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.citation.change = int(attrs.get('change', self.change))
|
self.citation.change = int(attrs.get('change', self.change))
|
||||||
self.citation.confidence = self.conf # default
|
self.citation.confidence = self.conf # default
|
||||||
self.info.add('new-object', CITATION_KEY, self.citation)
|
self.info.add('new-object', CITATION_KEY, self.citation)
|
||||||
|
return self.citation
|
||||||
|
|
||||||
def start_sourceref(self, attrs):
|
def start_sourceref(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -1929,6 +1967,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.source.private = bool(attrs.get("priv"))
|
self.source.private = bool(attrs.get("priv"))
|
||||||
self.source.change = int(attrs.get('change', self.change))
|
self.source.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', SOURCE_KEY, self.source)
|
self.info.add('new-object', SOURCE_KEY, self.source)
|
||||||
|
return self.source
|
||||||
|
|
||||||
def start_reporef(self, attrs):
|
def start_reporef(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -2016,6 +2055,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
src = attrs.get("src", '')
|
src = attrs.get("src", '')
|
||||||
if src:
|
if src:
|
||||||
self.object.path = src
|
self.object.path = src
|
||||||
|
return self.object
|
||||||
|
|
||||||
def start_repo(self, attrs):
|
def start_repo(self, attrs):
|
||||||
"""
|
"""
|
||||||
@ -2041,6 +2081,7 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.repo.private = bool(attrs.get("priv"))
|
self.repo.private = bool(attrs.get("priv"))
|
||||||
self.repo.change = int(attrs.get('change', self.change))
|
self.repo.change = int(attrs.get('change', self.change))
|
||||||
self.info.add('new-object', REPOSITORY_KEY, self.repo)
|
self.info.add('new-object', REPOSITORY_KEY, self.repo)
|
||||||
|
return self.repo
|
||||||
|
|
||||||
def stop_people(self, *tag):
|
def stop_people(self, *tag):
|
||||||
pass
|
pass
|
||||||
@ -2776,6 +2817,10 @@ class GrampsParser(UpdateCallback):
|
|||||||
self.db.commit_note(self.note, self.trans, self.note.get_change_time())
|
self.db.commit_note(self.note, self.trans, self.note.get_change_time())
|
||||||
self.note = None
|
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):
|
def stop_research(self, tag):
|
||||||
self.owner.set_name(self.resname)
|
self.owner.set_name(self.resname)
|
||||||
self.owner.set_address(self.resaddr)
|
self.owner.set_address(self.resaddr)
|
||||||
@ -2872,6 +2917,44 @@ class GrampsParser(UpdateCallback):
|
|||||||
tag_handle = tag.get_handle()
|
tag_handle = tag.get_handle()
|
||||||
obj.add_tag(tag_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):
|
def append_value(orig, val):
|
||||||
if orig:
|
if orig:
|
||||||
return "%s, %s" % (orig, val)
|
return "%s, %s" % (orig, val)
|
||||||
|
@ -2565,6 +2565,7 @@ class GedcomParser(UpdateCallback):
|
|||||||
src.set_title(title)
|
src.set_title(title)
|
||||||
self.dbase.add_source(src, self.trans)
|
self.dbase.add_source(src, self.trans)
|
||||||
|
|
||||||
|
self.__check_xref()
|
||||||
self.dbase.enable_signals()
|
self.dbase.enable_signals()
|
||||||
self.dbase.request_rebuild()
|
self.dbase.request_rebuild()
|
||||||
if self.number_of_errors == 0:
|
if self.number_of_errors == 0:
|
||||||
@ -2913,6 +2914,131 @@ class GedcomParser(UpdateCallback):
|
|||||||
"""
|
"""
|
||||||
self.backoff = True
|
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):
|
def __parse_trailer(self):
|
||||||
"""
|
"""
|
||||||
Looks for the expected TRLR token
|
Looks for the expected TRLR token
|
||||||
@ -3317,6 +3443,7 @@ class GedcomParser(UpdateCallback):
|
|||||||
if line.data and line.data[0] == '@':
|
if line.data and line.data[0] == '@':
|
||||||
# Reference to a named multimedia object defined elsewhere
|
# Reference to a named multimedia object defined elsewhere
|
||||||
gramps_id = self.oid_map[line.data]
|
gramps_id = self.oid_map[line.data]
|
||||||
|
|
||||||
handle = self.__find_object_handle(gramps_id)
|
handle = self.__find_object_handle(gramps_id)
|
||||||
ref = gen.lib.MediaRef()
|
ref = gen.lib.MediaRef()
|
||||||
ref.set_reference_handle(handle)
|
ref.set_reference_handle(handle)
|
||||||
@ -6835,6 +6962,13 @@ class GedcomParser(UpdateCallback):
|
|||||||
self.inline_srcs[title] = handle
|
self.inline_srcs[title] = handle
|
||||||
else:
|
else:
|
||||||
src = self.__find_or_create_source(self.sid_map[line.data])
|
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.dbase.commit_source(src, self.trans)
|
||||||
self.__parse_source_reference(citation, level, src.handle, state)
|
self.__parse_source_reference(citation, level, src.handle, state)
|
||||||
citation.set_reference_handle(src.handle)
|
citation.set_reference_handle(src.handle)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user