bug 9416 merge conflict

This commit is contained in:
prculley 2016-05-13 09:45:24 -05:00
commit b2e0f437f4
7 changed files with 143 additions and 24 deletions

View File

@ -139,6 +139,8 @@ class DbGenericUndo(DbUndo):
subitems = transaction.get_recnos()
# Process all records in the transaction
try:
self.db.transaction_backend_begin()
for record_id in subitems:
(key, trans_type, handle, old_data, new_data) = \
pickle.loads(self.undodb[record_id])
@ -148,6 +150,11 @@ class DbGenericUndo(DbUndo):
else:
self.undo_data(new_data, handle, self.mapbase[key],
db.emit, SIGBASE[key])
self.db.transaction_backend_commit()
except:
self.db.transaction_backend_abort()
raise
# Notify listeners
if db.undo_callback:
db.undo_callback(_("_Undo %s")
@ -847,6 +854,27 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
return self.get_table_func(table_name)
return None
def transaction_backend_begin(self):
"""
Lowlevel interface to the backend transaction.
Executes a db BEGIN;
"""
pass
def transaction_backend_commit(self):
"""
Lowlevel interface to the backend transaction.
Executes a db END;
"""
pass
def transaction_backend_abort(self):
"""
Lowlevel interface to the backend transaction.
Executes a db ROLLBACK;
"""
pass
def transaction_begin(self, transaction):
"""
Transactions are handled automatically by the db layer.

6
gramps/gen/lib/place.py Normal file → Executable file
View File

@ -631,9 +631,13 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:param acquisition: instance to merge
:type acquisition: :class:'~.place.Place
"""
if acquisition.name and (acquisition.name not in self.alt_names):
if acquisition.name.value:
if acquisition.name != self.name:
if acquisition.name not in self.alt_names:
self.alt_names.append(acquisition.name)
for addendum in acquisition.alt_names:
if addendum.value:
if addendum != self.name:
if addendum not in self.alt_names:
self.alt_names.append(addendum)

6
gramps/gen/lib/placename.py Normal file → Executable file
View File

@ -199,6 +199,12 @@ class PlaceName(SecondaryObject, DateBase):
else:
return EQUAL
def __eq__(self, other):
return self.is_equal(other)
def __ne__(self, other):
return not self.is_equal(other)
def set_value(self, value):
"""
Set the name for the PlaceName instance.

32
gramps/gen/lib/test/merge_test.py Normal file → Executable file
View File

@ -1389,8 +1389,14 @@ class PlaceCheck(unittest.TestCase, PrivacyBaseTest, MediaBaseTest,
def setUp(self):
self.phoenix = Place()
self.phoenix.set_title('Place 1')
self.titanic = Place(self.phoenix)
self.ref_obj = Place(self.phoenix)
# __init__ copy has bad side effects, don't use it
# self.titanic = Place(self.phoenix)
self.titanic = Place()
self.titanic.set_title('Place 1')
# __init__ copy has bad side effects, don't use it
# self.ref_obj = Place(self.phoenix)
self.ref_obj = Place()
self.ref_obj.set_title('Place 1')
self.amsterdam = PlaceName()
self.amsterdam.set_value('Amsterdam')
self.rotterdam = PlaceName()
@ -1433,9 +1439,11 @@ class PlaceCheck(unittest.TestCase, PrivacyBaseTest, MediaBaseTest,
self.titanic.add_alternative_name(self.leiden)
self.ref_obj.set_name(self.amsterdam)
self.ref_obj.set_type(PlaceType.CITY)
self.ref_obj.add_alternative_name(self.amsterdam)
self.ref_obj.add_alternative_name(self.rotterdam)
# Base name shouldn't be in alt_names list
# self.ref_obj.add_alternative_name(self.amsterdam)
# alt_names must be in correct order for test to pass
self.ref_obj.add_alternative_name(self.utrecht)
self.ref_obj.add_alternative_name(self.rotterdam)
self.ref_obj.add_alternative_name(self.leiden)
self.phoenix.merge(self.titanic)
self.assertEqual(self.phoenix.serialize(), self.ref_obj.serialize())
@ -1496,6 +1504,22 @@ class PlaceCheck(unittest.TestCase, PrivacyBaseTest, MediaBaseTest,
self.phoenix.merge(self.titanic)
self.assertEqual(self.phoenix.serialize(), self.ref_obj.serialize())
def test_merge_empty(self):
self.phoenix.set_name(self.amsterdam)
self.phoenix.set_type(PlaceType.CITY)
self.phoenix.add_alternative_name(self.rotterdam)
self.titanic.set_title('Place 2')
# titanic gets empty name
self.titanic.set_type(PlaceType.CITY)
self.titanic.add_alternative_name(self.utrecht)
self.titanic.add_alternative_name(PlaceName()) # empty alt_name
self.ref_obj.set_name(self.amsterdam)
self.ref_obj.set_type(PlaceType.CITY)
self.ref_obj.add_alternative_name(self.rotterdam)
self.ref_obj.add_alternative_name(self.utrecht)
self.phoenix.merge(self.titanic)
self.assertEqual(self.phoenix.serialize(), self.ref_obj.serialize())
class RepoCheck(unittest.TestCase, PrivacyBaseTest, NoteBaseTest, UrlBaseTest):
def setUp(self):
self.phoenix = Repository()

View File

@ -329,6 +329,27 @@ class DBAPI(DbGeneric):
def close_backend(self):
self.dbapi.close()
def transaction_backend_begin(self):
"""
Lowlevel interface to the backend transaction.
Executes a db BEGIN;
"""
self.dbapi.begin()
def transaction_backend_commit(self):
"""
Lowlevel interface to the backend transaction.
Executes a db END;
"""
self.dbapi.commit()
def transaction_backend_abort(self):
"""
Lowlevel interface to the backend transaction.
Executes a db ROLLBACK;
"""
self.dbapi.rollback()
def transaction_begin(self, transaction):
"""
Transactions are handled automatically by the db layer.
@ -579,6 +600,7 @@ class DBAPI(DbGeneric):
def commit_person(self, person, trans, change_time=None):
emit = None
old_person = None
person.change = int(change_time or time.time())
if person.handle in self.person_map:
emit = "person-update"
old_person = self.get_person_from_handle(person.handle)
@ -670,6 +692,7 @@ class DBAPI(DbGeneric):
def commit_family(self, family, trans, change_time=None):
emit = None
old_family = None
family.change = int(change_time or time.time())
if family.handle in self.family_map:
emit = "family-update"
old_family = self.get_family_from_handle(family.handle).serialize()
@ -733,6 +756,7 @@ class DBAPI(DbGeneric):
def commit_citation(self, citation, trans, change_time=None):
emit = None
old_citation = None
citation.change = int(change_time or time.time())
if citation.handle in self.citation_map:
emit = "citation-update"
old_citation = self.get_citation_from_handle(citation.handle).serialize()
@ -778,6 +802,7 @@ class DBAPI(DbGeneric):
def commit_source(self, source, trans, change_time=None):
emit = None
old_source = None
source.change = int(change_time or time.time())
if source.handle in self.source_map:
emit = "source-update"
old_source = self.get_source_from_handle(source.handle).serialize()
@ -825,6 +850,7 @@ class DBAPI(DbGeneric):
def commit_repository(self, repository, trans, change_time=None):
emit = None
old_repository = None
repository.change = int(change_time or time.time())
if repository.handle in self.repository_map:
emit = "repository-update"
old_repository = self.get_repository_from_handle(repository.handle).serialize()
@ -860,6 +886,7 @@ class DBAPI(DbGeneric):
def commit_note(self, note, trans, change_time=None):
emit = None
old_note = None
note.change = int(change_time or time.time())
if note.handle in self.note_map:
emit = "note-update"
old_note = self.get_note_from_handle(note.handle).serialize()
@ -892,6 +919,7 @@ class DBAPI(DbGeneric):
def commit_place(self, place, trans, change_time=None):
emit = None
old_place = None
place.change = int(change_time or time.time())
if place.handle in self.place_map:
emit = "place-update"
old_place = self.get_place_from_handle(place.handle).serialize()
@ -938,6 +966,7 @@ class DBAPI(DbGeneric):
def commit_event(self, event, trans, change_time=None):
emit = None
old_event = None
event.change = int(change_time or time.time())
if event.handle in self.event_map:
emit = "event-update"
old_event = self.get_event_from_handle(event.handle).serialize()
@ -979,6 +1008,7 @@ class DBAPI(DbGeneric):
def commit_tag(self, tag, trans, change_time=None):
emit = None
tag.change = int(change_time or time.time())
if tag.handle in self.tag_map:
emit = "tag-update"
self.dbapi.execute("""UPDATE tag SET blob_data = ?,
@ -1004,6 +1034,7 @@ class DBAPI(DbGeneric):
def commit_media(self, media, trans, change_time=None):
emit = None
old_media = None
media.change = int(change_time or time.time())
if media.handle in self.media_map:
emit = "media-update"
old_media = self.get_media_from_handle(media.handle).serialize()

View File

@ -31,6 +31,7 @@ class MySQL:
query = query.replace("REAL", "DOUBLE")
query = query.replace("change", "change_")
query = query.replace("desc", "desc_")
query = query.replace(" long ", " long_ ")
## LIMIT offset, count
## count can be -1, for all
## LIMIT -1

View File

@ -1705,8 +1705,6 @@ class PlaceParser:
loc.get_state(),
loc.get_country())
place_import.store_location(location, place.handle)
for level, name in enumerate(location):
if name:
break
@ -1720,6 +1718,10 @@ class PlaceParser:
place.set_type(PlaceType(type_num))
code = loc.get_postal_code()
place.set_code(code)
if place.handle: # if handle is available, store immediately
place_import.store_location(location, place.handle)
else: # return for storage later
return location
#-------------------------------------------------------------------------
#
@ -2954,15 +2956,22 @@ class GedcomParser(UpdateCallback):
sub_state.place.get_placeref_list())
if place is None:
place = sub_state.place
place_title = place_displayer.display(self.dbase, place)
location = sub_state.pf.load_place(self.place_import, place, place_title)
self.dbase.add_place(place, self.trans)
# if 'location was created, then store it, now that we have a handle.
if location:
self.place_import.store_location(location, place.handle)
self.place_names[place.get_title()].append(place.get_handle())
event.set_place_handle(place.get_handle())
else:
place.merge(sub_state.place)
self.dbase.commit_place(place, self.trans)
event.set_place_handle(place.get_handle())
place_title = place_displayer.display(self.dbase, place)
sub_state.pf.load_place(self.place_import, place, place_title)
location = sub_state.pf.load_place(self.place_import, place, place_title)
self.dbase.commit_place(place, self.trans)
if location:
self.place_import.store_location(location, place.handle)
event.set_place_handle(place.get_handle())
def __find_file(self, fullname, altpath):
tries = []
@ -3319,15 +3328,19 @@ class GedcomParser(UpdateCallback):
If just ADR1, ADR2, CITY, STAE, POST or CTRY are provided (this is not
actually legal GEDCOM symtax, but may be possible by GEDCOM extensions)
then just the structrued address is used.
The routine returns a string suitable for a title.
"""
title = ''
free_form_address = free_form_address.replace('\n', ', ')
if not (addr.get_street() or addr.get_locality() or
addr.get_city() or addr.get_state() or
addr.get_postal_code()):
addr.set_street(free_form_address)
return free_form_address
else:
# structured address provided
addr_list = free_form_address.split("\n")
addr_list = free_form_address.split(",")
str_list = []
for func in (addr.get_street(), addr.get_locality(),
addr.get_city(), addr.get_state(),
@ -3341,6 +3354,13 @@ class GedcomParser(UpdateCallback):
self.__add_msg(_("ADDR element ignored '%s'"
% elmn), line, state)
# The free-form address ADDR is discarded
# Assemble a title out of structured address
for elmn in str_list:
if elmn:
if title != '':
title += ', '
title += elmn
return title
def __parse_trailer(self):
"""
@ -5395,7 +5415,7 @@ class GedcomParser(UpdateCallback):
place = state.place
if place:
# We encounter a PLAC, having previously encountered an ADDR
if place.get_title() and place.get_title() != "":
if state.place.place_type.string != _("Address"):
# We have previously found a PLAC
self.__add_msg(_("A second PLAC ignored"), line, state)
# ignore this second PLAC, and use the old one
@ -5419,6 +5439,8 @@ class GedcomParser(UpdateCallback):
state.msg += sub_state.msg
if sub_state.pf: # if we found local PLAC:FORM
state.pf = sub_state.pf # save to override global value
# merge notes etc into place
state.place.merge(sub_state.place)
def __event_place_note(self, line, state):
"""
@ -5525,7 +5547,7 @@ class GedcomParser(UpdateCallback):
self.__parse_level(sub_state, self.parse_loc_tbl, self.__undefined)
state.msg += sub_state.msg
self.__merge_address(free_form, sub_state.location, line, state)
title = self.__merge_address(free_form, sub_state.location, line, state)
location = sub_state.location
@ -5572,9 +5594,12 @@ class GedcomParser(UpdateCallback):
state.place = Place()
place = state.place
place.add_alternate_locations(location)
place.set_name(PlaceName(value=title))
place.set_title(title)
place.set_type((PlaceType.CUSTOM, _("Address")))
# merge notes etc into place
place.merge(sub_state.place)
state.place.merge(sub_state.place)
def __add_location(self, place, location):
"""