# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2010 Michiel D. Nauta # Copyright (C) 2010 Jakim Friant # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id$ """ Provide merge capabilities for persons. """ #------------------------------------------------------------------------- # # Gramps modules # #------------------------------------------------------------------------- from gen.db import DbTxn from gen.ggettext import sgettext as _ from Errors import MergeError #------------------------------------------------------------------------- # # MergePersonQuery # #------------------------------------------------------------------------- class MergePersonQuery(object): """ Create database query to merge two persons. """ def __init__(self, database, phoenix, titanic): self.database = database self.phoenix = phoenix self.titanic = titanic if self.check_for_spouse(self.phoenix, self.titanic): raise MergeError(_("Spouses cannot be merged. To merge these " "people, you must first break the relationship between them.")) if self.check_for_child(self.phoenix, self.titanic): raise MergeError(_("A parent and child cannot be merged. To merge " "these people, you must first break the relationship between " "them.")) def check_for_spouse(self, person1, person2): """Return if person1 and person2 are spouses of eachother.""" fs1 = set(person1.get_family_handle_list()) fs2 = set(person2.get_family_handle_list()) return len(fs1.intersection(fs2)) != 0 def check_for_child(self, person1, person2): """Return if person1 and person2 have a child-parent relationship.""" fs1 = set(person1.get_family_handle_list()) fp1 = set(person1.get_parent_family_handle_list()) fs2 = set(person2.get_family_handle_list()) fp2 = set(person2.get_parent_family_handle_list()) return len(fs1.intersection(fp2)) != 0 or len(fs2.intersection(fp1)) def merge_families(self, main_family_handle, family, trans): """ Merge content of family into the family with handle main_family_handle. """ new_handle = self.phoenix.get_handle() if self.phoenix else None old_handle = self.titanic.get_handle() if self.titanic else None family_handle = family.get_handle() main_family = self.database.get_family_from_handle(main_family_handle) main_family.merge(family) for childref in family.get_child_ref_list(): child = self.database.get_person_from_handle( childref.get_reference_handle()) if main_family_handle in child.parent_family_list: child.remove_handle_references('Family', [family_handle]) else: child.replace_handle_reference('Family', family_handle, main_family_handle) self.database.commit_person(child, trans) if self.phoenix: self.phoenix.remove_family_handle(family_handle) self.database.commit_person(self.phoenix, trans) family_father_handle = family.get_father_handle() spouse_handle = family.get_mother_handle() if \ new_handle == family_father_handle else family_father_handle spouse = self.database.get_person_from_handle(spouse_handle) if spouse: spouse.remove_family_handle(family_handle) self.database.commit_person(spouse, trans) # replace the family in lds ordinances for (dummy, person_handle) in self.database.find_backlink_handles( family_handle, ['Person']): if person_handle == old_handle: continue person = self.database.get_person_from_handle(person_handle) person.replace_handle_reference('Family', family_handle, main_family_handle) self.database.commit_person(person, trans) self.database.remove_family(family_handle, trans) self.database.commit_family(main_family, trans) def execute(self, family_merger=True, trans=None): """ Merges two persons into a single person. """ if trans is None: with DbTxn(_('Merge Person'), self.database) as trans: self.__execute(family_merger, trans) else: self.__execute(family_merger, trans) def __execute(self, family_merger, trans): """ Merges two persons into a single person; trans is compulsory. """ new_handle = self.phoenix.get_handle() old_handle = self.titanic.get_handle() self.phoenix.merge(self.titanic) self.database.commit_person(self.phoenix, trans) for (dummy, person_handle) in self.database.find_backlink_handles( old_handle, ['Person']): person = self.database.get_person_from_handle(person_handle) assert person.has_handle_reference('Person', old_handle) person.replace_handle_reference('Person', old_handle, new_handle) if person_handle != old_handle: self.database.commit_person(person, trans) for family_handle in self.phoenix.get_parent_family_handle_list(): family = self.database.get_family_from_handle(family_handle) if family.has_handle_reference('Person', old_handle): family.replace_handle_reference('Person', old_handle,new_handle) self.database.commit_family(family, trans) family_merge_guard = False parent_list = [] parent_list_orig = [] family_handle_list = self.phoenix.get_family_handle_list()[:] for family_handle in family_handle_list: family = self.database.get_family_from_handle(family_handle) parents = (family.get_father_handle(), family.get_mother_handle()) parent_list_orig.append(parents) if family.has_handle_reference('Person', old_handle): if family_merger and parent_list_orig.count(parents) > 1: raise MergeError(_("A person with multiple relations with " "the same spouse is about to be merged. This is beyond " "the capabilities of the merge routine. The merge is " "aborted.")) family.replace_handle_reference('Person', old_handle,new_handle) parents = (family.get_father_handle(), family.get_mother_handle()) # prune means merging families in this case. if family_merger and parents in parent_list: # also merge when father_handle or mother_handle == None! if family_merge_guard: raise MergeError(_("Multiple families get merged. " "This is unusual, the merge is aborted.")) idx = parent_list.index(parents) main_family_handle = family_handle_list[idx] self.merge_families(main_family_handle, family, trans) family_merge_guard = True continue self.database.commit_family(family, trans) parent_list.append(parents) self.database.remove_person(old_handle, trans)