5466: On import and Check and Repair need to check references to absent objects

svn: r19327
This commit is contained in:
Michiel Nauta 2012-04-14 12:19:00 +00:00
parent df068471bb
commit 75d54125db
5 changed files with 963 additions and 351 deletions

View File

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

View File

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

View File

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

View File

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