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:
parent
8d57884b5b
commit
38b5b8ec54
@ -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()
|
||||||
|
|
||||||
# check to see if we've encountered this name before
|
|
||||||
# if we haven't we need to get a new GRAMPS ID
|
|
||||||
|
|
||||||
intid = self.place_names.get(title)
|
|
||||||
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_title(title)
|
||||||
place.set_gramps_id(new_id)
|
if location:
|
||||||
|
place.add_alternate_locations(location)
|
||||||
self.dbase.add_place(place, self.trans)
|
self.dbase.add_place(place, self.trans)
|
||||||
self.lid2id[title] = intid
|
self.place_names[title].append(place.get_handle())
|
||||||
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:
|
else:
|
||||||
place = self.__find_or_create_place(line.data)
|
# This is the first PLAC
|
||||||
place.set_title(line.data)
|
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:
|
||||||
|
# The first thing we encounter is PLAC
|
||||||
|
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:
|
else:
|
||||||
place = self.__find_or_create_place(line.data)
|
# This is the first ADDR
|
||||||
place.set_title(line.data)
|
refs = list(self.dbase.find_backlink_handles(place_handle))
|
||||||
place_handle = 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)
|
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:
|
||||||
|
# The first thing we encounter is ADDR
|
||||||
|
title = ""
|
||||||
|
place = self.__find_place(title, location)
|
||||||
|
if place is None:
|
||||||
|
place = self.__create_place(title, location)
|
||||||
|
|
||||||
list(map(place.add_note, note_list))
|
# merge notes etc into place
|
||||||
|
place.merge(sub_state.place)
|
||||||
|
|
||||||
state.event.set_place_handle(place_handle)
|
state.event.set_place_handle(place.get_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):
|
||||||
|
Loading…
Reference in New Issue
Block a user