0008322: Event address is lost on import, i.e. disconnected from event.

On GEDCOM import, Places are only merged if the Place Title and the
whole of the main location are identical.
This commit is contained in:
kulath 2015-03-12 12:34:31 +00:00
parent 8d57884b5b
commit 38b5b8ec54

View File

@ -1807,6 +1807,7 @@ class GedcomParser(UpdateCallback):
__TRUNC_MSG = _("Your GEDCOM file is corrupted. " __TRUNC_MSG = _("Your GEDCOM file is corrupted. "
"It appears to have been truncated.") "It appears to have been truncated.")
_EMPTY_LOC = Location().serialize()
SyntaxError = "Syntax Error" SyntaxError = "Syntax Error"
BadFile = "Not a GEDCOM file" BadFile = "Not a GEDCOM file"
@ -2298,12 +2299,12 @@ class GedcomParser(UpdateCallback):
TOKEN_CTRY : self.__location_ctry, TOKEN_CTRY : self.__location_ctry,
# Not legal GEDCOM - not clear why these are included at this level # Not legal GEDCOM - not clear why these are included at this level
TOKEN_ADDR : self.__ignore, TOKEN_ADDR : self.__ignore,
TOKEN_DATE : self.__location_date, TOKEN_DATE : self.__ignore, # there is nowhere to put a date
TOKEN_NOTE : self.__location_note, TOKEN_NOTE : self.__location_note,
TOKEN_RNOTE : self.__location_note, TOKEN_RNOTE : self.__location_note,
TOKEN__LOC : self.__ignore, TOKEN__LOC : self.__ignore,
TOKEN__NAME : self.__ignore, TOKEN__NAME : self.__ignore,
TOKEN_PHON : self.__ignore, TOKEN_PHON : self.__location_phone,
TOKEN_IGNORE : self.__ignore, TOKEN_IGNORE : self.__ignore,
} }
self.func_list.append(self.parse_loc_tbl) self.func_list.append(self.parse_loc_tbl)
@ -2638,12 +2639,12 @@ class GedcomParser(UpdateCallback):
self.func_list.append(self.note_parse_tbl) self.func_list.append(self.note_parse_tbl)
# look for existing place titles, build a map # look for existing place titles, build a map
self.place_names = {} self.place_names = defaultdict(list)
cursor = dbase.get_place_cursor() cursor = dbase.get_place_cursor()
data = next(cursor) data = next(cursor)
while data: while data:
(handle, val) = data (handle, val) = data
self.place_names[val[2]] = handle self.place_names[val[2]].append(handle)
data = next(cursor) data = next(cursor)
cursor.close() cursor.close()
@ -2869,40 +2870,60 @@ class GedcomParser(UpdateCallback):
self.dbase.add_note(note, self.trans) self.dbase.add_note(note, self.trans)
return note return note
def __find_or_create_place(self, title): def __loc_is_empty(self, location):
""" """
Finds or creates a place based on the GRAMPS ID. If the ID is Determines whether a location is empty.
already used (is in the db), we return the item in the db. Otherwise,
we create a new place, assign the handle and GRAMPS ID. @param location: The current location
@type location: gen.lib.Location
@return True of False
"""
if location is None:
return True
elif location.serialize() == self._EMPTY_LOC:
return True
elif location.is_empty():
return True
return False
def __find_place(self, title, location):
"""
Finds an existing place based on the title and primary location.
@param title: The place title
@type title: string
@param location: The current location
@type location: gen.lib.Location
@return gen.lib.Place
"""
for place_handle in self.place_names[title]:
place = self.dbase.get_place_from_handle(place_handle)
if place.get_title() == title:
if self.__loc_is_empty(location) and \
self.__loc_is_empty(self.__get_first_loc(place)):
return place
elif (not self.__loc_is_empty(location) and \
not self.__loc_is_empty(self.__get_first_loc(place)) and
self.__get_first_loc(place).is_equivalent(location) == IDENTICAL):
return place
return None
def __create_place(self, title, location):
"""
Create a new place based on the title and primary location.
@param title: The place title
@type title: string
@param location: The current location
@type location: gen.lib.Location
@return gen.lib.Place
""" """
place = Place() place = Place()
place.set_title(title)
# check to see if we've encountered this name before if location:
# if we haven't we need to get a new GRAMPS ID place.add_alternate_locations(location)
self.dbase.add_place(place, self.trans)
intid = self.place_names.get(title) self.place_names[title].append(place.get_handle())
if intid is None:
intid = self.lid2id.get(title)
if intid is None:
new_id = self.dbase.find_next_place_gramps_id()
else:
new_id = None
else:
new_id = None
# check to see if the name already existed in the database
# if it does, create a new name by appending the GRAMPS ID.
# generate a GRAMPS ID if needed
if self.dbase.has_place_handle(intid):
place.unserialize(self.dbase.get_raw_place_data(intid))
else:
intid = create_id()
place.set_handle(intid)
place.set_title(title)
place.set_gramps_id(new_id)
self.dbase.add_place(place, self.trans)
self.lid2id[title] = intid
return place return place
def __find_file(self, fullname, altpath): def __find_file(self, fullname, altpath):
@ -4466,8 +4487,12 @@ class GedcomParser(UpdateCallback):
@type state: CurrentState @type state: CurrentState
""" """
try: try:
state.place = self.__find_or_create_place(line.data) title = line.data
state.place.set_title(line.data) place = self.__find_place(title, None)
if place:
state.place = place
else:
state.place = self.__create_place(title, None)
state.lds_ord.set_place_handle(state.place.handle) state.lds_ord.set_place_handle(state.place.handle)
except NameError: except NameError:
return return
@ -5305,16 +5330,49 @@ class GedcomParser(UpdateCallback):
if self.is_ftw and state.event.type in FTW_BAD_PLACE: if self.is_ftw and state.event.type in FTW_BAD_PLACE:
state.event.set_description(line.data) state.event.set_description(line.data)
else: else:
# It is possible that we have already got an address structure title = line.data
# associated with this event. In that case, we will remember the
# location to re-insert later, and set the place as the place name
# and primary location
place_handle = state.event.get_place_handle() place_handle = state.event.get_place_handle()
if place_handle: if place_handle:
place = self.dbase.get_place_from_handle(place_handle) # We encounter a PLAC, having previously encountered an ADDR
old_place = self.dbase.get_place_from_handle(place_handle)
old_title = old_place.get_title()
location = self.__get_first_loc(old_place)
if old_title != "":
# We have previously found a PLAC
self.__add_msg(_("A second PLAC ignored"), line, state)
# ignore this second PLAC, and use the old one
title = old_title
place = old_place
else:
# This is the first PLAC
refs = list(self.dbase.find_backlink_handles(place_handle))
# We haven't commited the event yet, so the place will not
# be linked to it. If there are any refs they will be from
# other events (etc)
if len(refs) == 0:
place = self.__find_place(title, location)
if place is None:
place = old_place
place.set_title(title)
self.place_names[old_title].remove(place_handle)
self.place_names[title].append(place_handle)
else:
place.merge(old_place)
self.dbase.remove_place(place_handle, self.trans)
self.place_names[old_title].remove(place_handle)
else:
place = self.__find_place(title, location)
if place is None:
place = self.__create_place(title, location)
else:
pass
else: else:
place = self.__find_or_create_place(line.data) # The first thing we encounter is PLAC
place.set_title(line.data) location = None
place = self.__find_place(title, location)
if place is None:
place = self.__create_place(title, location)
state.event.set_place_handle(place.handle) state.event.set_place_handle(place.handle)
sub_state = CurrentState() sub_state = CurrentState()
@ -5430,8 +5488,8 @@ class GedcomParser(UpdateCallback):
sub_state = CurrentState(level=state.level+1) sub_state = CurrentState(level=state.level+1)
sub_state.location = Location() sub_state.location = Location()
sub_state.note = []
sub_state.event = state.event sub_state.event = state.event
sub_state.place = Place() # temp stash for notes, citations etc
self.__parse_level(sub_state, self.parse_loc_tbl, self.__undefined) self.__parse_level(sub_state, self.parse_loc_tbl, self.__undefined)
state.msg += sub_state.msg state.msg += sub_state.msg
@ -5439,21 +5497,52 @@ class GedcomParser(UpdateCallback):
self.__merge_address(free_form, sub_state.location, line, state) self.__merge_address(free_form, sub_state.location, line, state)
location = sub_state.location location = sub_state.location
note_list = sub_state.note
place_handle = state.event.get_place_handle() place_handle = state.event.get_place_handle()
if place_handle: if place_handle:
place = self.dbase.get_place_from_handle(place_handle) # We encounter an ADDR having previously encountered a PLAC
old_place = self.dbase.get_place_from_handle(place_handle)
title = old_place.get_title()
if len(old_place.get_alternate_locations()) != 0 and \
not self.__get_first_loc(old_place).is_empty():
# We have perviously found an ADDR, or have populated location
# from PLAC title
self.__add_msg(_("Location already populated; ADDR ignored"),
line, state)
# ignore this second ADDR, and use the old one
location = self.__get_first_loc(old_place)
place = old_place
else:
# This is the first ADDR
refs = list(self.dbase.find_backlink_handles(place_handle))
# We haven't commited the event yet, so the place will not be
# linked to it. If there are any refs they will be from other
# events (etc)
if len(refs) == 0:
place = self.__find_place(title, location)
if place is None:
place = old_place
self.__add_location(place, location)
else:
place.merge(old_place)
self.dbase.remove_place(place_handle, self.trans)
self.place_names[title].remove(place_handle)
else:
place = self.__find_place(title, location)
if place is None:
place = self.__create_place(title, location)
else:
pass
else: else:
place = self.__find_or_create_place(line.data) # The first thing we encounter is ADDR
place.set_title(line.data) title = ""
place_handle = place.handle place = self.__find_place(title, location)
if place is None:
place = self.__create_place(title, location)
self.__add_location(place, location) # merge notes etc into place
place.merge(sub_state.place)
list(map(place.add_note, note_list)) state.event.set_place_handle(place.get_handle())
state.event.set_place_handle(place_handle)
self.dbase.commit_place(place, self.trans) self.dbase.commit_place(place, self.trans)
def __add_location(self, place, location): def __add_location(self, place, location):
@ -5468,6 +5557,18 @@ class GedcomParser(UpdateCallback):
return return
place.add_alternate_locations(location) place.add_alternate_locations(location)
def __get_first_loc(self, place):
"""
@param place: A place object
@type place: Place
@return location: the first alternate location if any else None
@type location: gen.lib.location
"""
if len(place.get_alternate_locations()) == 0:
return None
else:
return place.get_alternate_locations()[0]
def __event_phon(self, line, state): def __event_phon(self, line, state):
""" """
@param line: The current line in GedLine format @param line: The current line in GedLine format
@ -6635,17 +6736,6 @@ class GedcomParser(UpdateCallback):
url.set_type(UrlType(UrlType.EMAIL)) url.set_type(UrlType(UrlType.EMAIL))
state.repo.add_url(url) state.repo.add_url(url)
def __location_date(self, line, state):
"""
@param line: The current line in GedLine format
@type line: GedLine
@param state: The current state
@type state: CurrentState
"""
if not state.location:
state.location = Location()
state.location.set_date_object(line.data)
def __location_adr1(self, line, state): def __location_adr1(self, line, state):
""" """
@param line: The current line in GedLine format @param line: The current line in GedLine format
@ -6714,7 +6804,7 @@ class GedcomParser(UpdateCallback):
state.location = Location() state.location = Location()
state.location.set_country(line.data) state.location.set_country(line.data)
def __location_note(self, line, state): def __location_phone(self, line, state):
""" """
@param line: The current line in GedLine format @param line: The current line in GedLine format
@type line: GedLine @type line: GedLine
@ -6723,9 +6813,19 @@ class GedcomParser(UpdateCallback):
""" """
if not state.location: if not state.location:
state.location = Location() state.location = Location()
state.location.set_phone(line.data)
def __location_note(self, line, state):
"""
@param line: The current line in GedLine format
@type line: GedLine
@param state: The current state
@type state: CurrentState
"""
if state.event: if state.event:
self.__parse_note(line, state.event, state.level+1, state) self.__parse_note(line, state.place, state.level, state)
else: else:
# This causes notes below SUBMitter to be ignored
self.__not_recognized(line, state.level, state) self.__not_recognized(line, state.level, state)
def __optional_note(self, line, state): def __optional_note(self, line, state):