gramps/gramps2/src/MergeData.py
Don Allingham 4a6595cc4d * src/MergeData: Remove reference to personTable
* src/RelLib.py: Remove the personTable, since it is no longer needed
with the dropping of ZODB, and we don't have to worry about keeping
personTable and personMap in sync. Add sort field to Name class.
* src/GrampsParser.py: build the primary sort name
* src/GrampsXML.py: remove personTable references
* src/Find.py: don't user personTable


svn: r2596
2004-01-07 05:08:32 +00:00

908 lines
30 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2003 Donald N. Allingham
#
# 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
#
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import string
#-------------------------------------------------------------------------
#
# GNOME
#
#-------------------------------------------------------------------------
import gtk.glade
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import RelLib
import Utils
import GrampsCfg
import ListModel
import const
from gettext import gettext as _
#-------------------------------------------------------------------------
#
# Merge People
#
#-------------------------------------------------------------------------
class MergePeople:
def __init__(self,db,person1,person2,update,ep_update=None):
self.db = db
self.p1 = person1
self.p2 = person2
self.update = update
self.ep_update = ep_update
self.glade = gtk.glade.XML(const.mergeFile,"merge","gramps")
self.top = self.glade.get_widget("merge")
Utils.set_titles(self.top,self.glade.get_widget('title'),
_('Merge Places'), _('Select the title for the merged place'))
self.altname = self.glade.get_widget("altname")
self.altbirth = self.glade.get_widget("altbirth")
self.altdeath = self.glade.get_widget("altdeath")
self.family_list = db.getFamilyMap().values()
self.glade.signal_autoconnect({
"on_merge_clicked" : self.on_merge_clicked,
"on_next_clicked" : self.on_merge_edit_clicked,
"destroy_passed_object" : Utils.destroy_passed_object,
})
fname = GrampsCfg.nameof(person1)
mname = GrampsCfg.nameof(person2)
Utils.set_titles(self.top, self.glade.get_widget('title'),
_("Merge %s and %s") % (fname,mname),
_("Merge people"))
f1 = person1.getMainParents()
f2 = person2.getMainParents()
name1 = GrampsCfg.nameof(person1)
death1 = person1.getDeath().getDate()
dplace1 = self.place_name(person1.getDeath())
birth1 = person1.getBirth().getDate()
bplace1 = self.place_name(person1.getBirth())
name2 = GrampsCfg.nameof(person2)
death2 = person2.getDeath().getDate()
dplace2 = self.place_name(person2.getDeath())
birth2 = person2.getBirth().getDate()
bplace2 = self.place_name(person2.getBirth())
if f2 and not f1:
self.glade.get_widget("bfather2").set_active(1)
else:
self.glade.get_widget("bfather1").set_active(1)
if f1:
father1 = name_of(f1.getFather())
mother1 = name_of(f1.getMother())
else:
father1 = ""
mother1 = ""
if f2:
father2 = name_of(f2.getFather())
mother2 = name_of(f2.getMother())
else:
father2 = ""
mother2 = ""
self.set_field(self.glade.get_widget("id1_text"),person1.getId())
self.set_field(self.glade.get_widget("id2_text"),person2.getId())
self.set_field(self.glade.get_widget("name1_text"),name1)
self.set_field(self.glade.get_widget("name2_text"),name2)
self.bname1 = self.glade.get_widget("bname1")
self.bname1.set_active(1)
self.set_field(self.glade.get_widget("birth1_text"),birth1)
self.set_field(self.glade.get_widget("birth2_text"),birth2)
self.set_field(self.glade.get_widget("bplace1_text"),bplace1)
self.set_field(self.glade.get_widget("bplace2_text"),bplace2)
if ((not birth1 and not bplace1) and (birth2 or bplace2) or
(not birth1 or not bplace1) and (birth2 and bplace2)):
self.glade.get_widget('bbirth2').set_active(1)
else:
self.glade.get_widget('bbirth1').set_active(1)
if ((not death1 and not dplace1) and (death2 or dplace2) or
(not death1 or not dplace1) and (death2 and dplace2)):
self.glade.get_widget('death2').set_active(1)
else:
self.glade.get_widget('death1').set_active(1)
self.set_field(self.glade.get_widget("death1_text"),death1)
self.set_field(self.glade.get_widget("dplace1_text"),dplace1)
self.set_field(self.glade.get_widget("death2_text"),death2)
self.set_field(self.glade.get_widget("dplace2_text"),dplace2)
self.set_field(self.glade.get_widget("father1"),father1)
self.set_field(self.glade.get_widget("father2"),father2)
self.set_field(self.glade.get_widget("mother1"),mother1)
self.set_field(self.glade.get_widget("mother2"),mother2)
sp1_list = [('-',0,100)]
self.sp1 = ListModel.ListModel(self.glade.get_widget('spouse1'),sp1_list)
self.sp2 = ListModel.ListModel(self.glade.get_widget('spouse2'),sp1_list)
self.build_spouse_list(person1,self.sp1)
self.build_spouse_list(person2,self.sp2)
if name1 != name2:
self.altname.set_sensitive(1)
self.altname.set_active(1)
else:
self.altname.set_sensitive(0)
self.altname.set_active(0)
if birth1 and birth2 and birth1 != birth2:
self.altbirth.set_active(1)
if bplace1 and bplace2 or bplace1 != bplace2:
self.altbirth.set_active(1)
else:
self.altbirth.set_active(0)
if death1 and death2 and death1 != death2:
self.altdeath.set_active(1)
if dplace1 and dplace2 or dplace1 != dplace2:
self.altdeath.set_active(1)
else:
self.altdeath.set_active(0)
def build_spouse_list(self,person,widget):
widget.clear()
for fam in person.getFamilyList():
if person.getGender() == RelLib.Person.male:
spouse = fam.getMother()
else:
spouse = fam.getFather()
if spouse == None:
name = "unknown"
else:
sname = GrampsCfg.nameof(spouse)
name = "%s [%s]" % (sname,spouse.getId())
widget.add([name])
def set_field(self,widget,value):
"""Sets the string of the entry field at positions it a space 0"""
widget.set_text(value)
def place_name(self,event):
place = event.getPlace()
if place:
return "%s (%s)" % (place.get_title(),place.getId())
else:
return ""
def empty(self,junk):
pass
def on_merge_edit_clicked(self,obj):
import EditPerson
self.on_merge_clicked(obj)
# This needs to be fixed to provide an update call
EditPerson.EditPerson(self.p1,self.db,self.ep_update)
def copy_note(self,one,two):
if one.getNote() != two.getNote():
one.setNote("%s\n\n%s" % (one.getNote(),two.getNote()))
def copy_sources(self,one,two):
slist = one.getSourceRefList()[:]
for xsrc in two.getSourceRefList():
for src in slist:
if src.are_equal(xsrc):
break
else:
one.addSourceRef(xsrc)
def on_merge_clicked(self,obj):
Utils.modified()
list = self.p1.getAlternateNames()[:]
for xdata in self.p2.getAlternateNames():
for data in list:
if data.are_equal(xdata):
self.copy_note(xdata,data)
self.copy_sources(xdata,data)
break
else:
self.p1.addAlternateName(xdata)
list = self.p1.getAttributeList()[:]
for xdata in self.p2.getAttributeList():
for data in list:
if data.getType() == xdata.getType() and \
data.getValue() == xdata.getValue():
self.copy_note(xdata,data)
self.copy_sources(xdata,data)
break
else:
self.p1.addAttribute(xdata)
list = self.p1.getEventList()[:]
for xdata in self.p2.getEventList():
for data in list:
if data.are_equal(xdata):
self.copy_note(xdata,data)
self.copy_sources(xdata,data)
break
else:
self.p1.addEvent(xdata)
list = self.p1.getUrlList()[:]
for xdata in self.p2.getUrlList():
for data in list:
if data.are_equal(xdata):
break
else:
self.p1.addUrl(xdata)
self.id2 = self.glade.get_widget("id2")
old_id = self.p1.getId()
if self.id2.get_active():
self.p1.setId(self.p2.getId())
if self.bname1.get_active():
if self.altname.get_active():
self.p1.addAlternateName(self.p2.getPrimaryName())
else:
if self.altname.get_active():
self.p1.addAlternateName(self.p1.getPrimaryName())
self.p1.setPrimaryName(self.p2.getPrimaryName())
alt = self.glade.get_widget("altbirth").get_active()
if self.glade.get_widget("bbirth2").get_active():
if alt:
event = self.p1.getBirth()
event.setName("Alternate Birth")
self.p1.addEvent(event)
self.p1.setBirth(self.p2.getBirth())
else:
if alt:
event = self.p2.getBirth()
event.setName("Alternate Birth")
self.p1.addEvent(event)
alt = self.glade.get_widget("altdeath").get_active()
if self.glade.get_widget("bbirth2").get_active():
if alt:
event = self.p1.getDeath()
event.setName("Alternate Death")
self.p1.addEvent(event)
self.p1.setDeath(self.p2.getDeath())
else:
if alt:
event = self.p2.getDeath()
event.setName("Alternate Death")
self.p1.addEvent(event)
if self.glade.get_widget("bfather2").get_active():
orig_family = self.p1.getMainParents()
if orig_family:
orig_family.removeChild(self.p1)
self.p1.removeAltFamily(orig_family)
(source_family,mrel,frel) = self.p2.getMainParentsRel()
self.p1.setMainParents(source_family)
if source_family:
if self.p2 in source_family.getChildList():
source_family.removeChild(self.p2)
self.p2.removeAltFamily(source_family)
if self.p1 not in source_family.getChildList():
source_family.addChild(self.p1)
self.p1.addAltFamily(source_family,mrel,frel)
else:
source_family = self.p2.getMainParents()
if source_family:
source_family.removeChild(self.p2)
self.p2.setMainParents(None)
self.merge_families()
for photo in self.p2.getPhotoList():
self.p1.addPhoto(photo)
if self.p1.getNickName() == "":
self.p1.setNickName(self.p2.getNickName())
if self.p2.getNote() != "":
old_note = self.p1.getNote()
if old_note:
old_note = old_note + "\n\n"
self.p1.setNote(old_note + self.p2.getNote())
try:
self.db.removePerson(self.p2.getId())
self.db.personMap[self.p1.getId()] = self.p1
self.db.buildPersonDisplay(self.p1.getId(),old_id)
except:
print "%s is not in the person map!" % (GrampsCfg.nameof(self.p2))
self.update(self.p1,self.p2,old_id)
Utils.destroy_passed_object(self.top)
def find_family(self,family):
if self.p1.getGender() == RelLib.Person.male:
mother = family.getMother()
father = self.p1
else:
father = family.getFather()
mother = self.p1
for myfamily in self.family_list:
if myfamily.getFather() == father and myfamily.getMother() == mother:
return myfamily
return None
def merge_families(self):
family_num = 0
mylist = self.p2.getFamilyList()[:]
for src_family in mylist:
family_num = family_num + 1
if not self.db.getFamilyMap().has_key(src_family.getId()):
continue
if src_family in self.p1.getFamilyList():
continue
tgt_family = self.find_family(src_family)
#
# This is the case where a new family to be added to the
# p1 as a result of the merge already exists as a
# family. In this case, we need to remove the old source
# family (with the pre-merge identity of the p1) from
# both the parents
#
if tgt_family in self.p1.getFamilyList():
if tgt_family.getFather() != None and \
src_family in tgt_family.getFather().getFamilyList():
tgt_family.getFather().removeFamily(src_family)
if tgt_family.getMother() != None and \
src_family in tgt_family.getMother().getFamilyList():
tgt_family.getMother().removeFamily(src_family)
# copy children from source to target
for child in src_family.getChildList():
if child not in tgt_family.getChildList():
parents = child.getParentList()
tgt_family.addChild(child)
if child.getMainParents() == src_family:
child.setMainParents(tgt_family)
i = 0
for fam in parents[:]:
if fam[0] == src_family:
parents[i] = (tgt_family,fam[1],fam[2])
i = i + 1
# delete the old source family
del self.db.getFamilyMap()[src_family.getId()]
continue
# This is the case where a new family to be added
# and it is not already in the list.
if tgt_family:
# tgt_family a duplicate family, transfer children from
# the p2 family, and delete the family. Not sure
# what to do about marriage/divorce date/place yet.
# transfer child to new family, alter children to
# point to the correct family
for child in src_family.getChildList():
if child not in tgt_family.getChildList():
parents = child.getParentList()
tgt_family.addChild(child)
if child.getMainParents() == src_family:
child.setMainParents(tgt_family)
i = 0
for fam in parents[:]:
if fam[0] == src_family:
parents[i] = (tgt_family,fam[1],fam[2])
i = i + 1
# add family events from the old to the new
for event in src_family.getEventList():
tgt_family.addEvent(event)
# change parents of the family to point to the new
# family
if src_family.getFather():
src_family.getFather().removeFamily(src_family)
src_family.getFather().addFamily(tgt_family)
if src_family.getMother():
src_family.getMother().removeFamily(src_family)
src_family.getMother().addFamily(tgt_family)
del self.db.getFamilyMap()[src_family.getId()]
else:
if src_family not in self.p1.getFamilyList():
self.p1.addFamily(src_family)
if self.p1.getGender() == RelLib.Person.male:
src_family.setFather(self.p1)
else:
src_family.setMother(self.p1)
self.remove_marriage(src_family,self.p2)
# a little debugging here
for fam in self.db.getFamilyMap().values():
if self.p2 in fam.getChildList():
fam.removeChild(self.p2)
fam.addChild(self.p1)
if self.p2 == fam.getFather():
fam.setFather(self.p1)
if self.p2 == fam.getMother():
fam.setMother(self.p1)
if fam.getFather() == None and fam.getMother() == None:
self.delete_empty_family(fam)
def remove_marriage(self,family,person):
if person:
person.removeFamily(family)
if family.getFather() == None and family.getMother() == None:
self.delete_empty_family(family)
def delete_empty_family(self,family):
for child in family.getChildList():
if child.getMainParents() == family:
child.setMainParents(None)
else:
child.removeAltFamily(family)
self.db.deleteFamily(family)
def compare_people(p1,p2):
name1 = p1.getPrimaryName()
name2 = p2.getPrimaryName()
chance = name_match(name1,name2)
if chance == -1.0 :
return -1.0
birth1 = p1.getBirth()
death1 = p1.getDeath()
birth2 = p2.getBirth()
death2 = p2.getDeath()
value = date_match(birth1.getDateObj(),birth2.getDateObj())
if value == -1.0 :
return -1.0
chance = chance + value
value = date_match(death1.getDateObj(),death2.getDateObj())
if value == -1.0 :
return -1.0
chance = chance + value
value = place_match(birth1.getPlace(),birth2.getPlace())
if value == -1.0 :
return -1.0
chance = chance + value
value = place_match(death1.getPlace(),death2.getPlace())
if value == -1.0 :
return -1.0
chance = chance + value
ancestors = []
ancestors_of(p1,ancestors)
if p2 in ancestors:
return -1.0
ancestors = []
ancestors_of(p2,ancestors)
if p1 in ancestors:
return -1.0
f1 = p1.getMainParents()
f2 = p2.getMainParents()
if f1 and f1.getFather():
dad1 = f1.getFather().getPrimaryName()
else:
dad1 = None
if f2 and f2.getFather():
dad2 = f2.getFather().getPrimaryName()
else:
dad2 = None
value = name_match(dad1,dad2)
if value == -1.0:
return -1.0
chance = chance + value
if f1 and f1.getMother():
mom1 = f1.getMother().getPrimaryName()
else:
mom1 = None
if f2 and f2.getMother():
mom2 = f2.getMother().getPrimaryName()
else:
mom2 = None
value = name_match(mom1,mom2)
if value == -1.0:
return -1.0
chance = chance + value
for f1 in p1.getFamilyList():
for f2 in p2.getFamilyList():
if p1.getGender() == RelLib.Person.female:
father1 = f1.getFather()
father2 = f2.getFather()
if father1 and father2:
if father1 == father2:
chance = chance + 1.0
else:
fname1 = GrampsCfg.nameof(father1)
fname2 = GrampsCfg.nameof(father2)
value = name_match(fname1,fname2)
if value != -1.0:
chance = chance + value
else:
mother1 = f1.getMother()
mother2 = f2.getMother()
if mother1 and mother2:
if mother1 == mother2:
chance = chance + 1.0
else:
mname1 = GrampsCfg.nameof(mother1)
mname2 = GrampsCfg.nameof(mother2)
value = name_match(mname1,mname2)
if value != -1.0:
chance = chance + value
return chance
#-----------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------
def name_compare(s1,s2):
return s1 == s2
#-----------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------
def date_match(date1,date2):
if date1.getDate() == "" or date2.getDate() == "":
return 0.0
if date1.getDate() == date2.getDate():
return 1.0
if date1.isRange() or date2.isRange():
return range_compare(date1,date2)
date1 = date1.get_start_date()
date2 = date2.get_start_date()
if date1.getYear() == date2.getYear():
if date1.getMonth() == date2.getMonth():
return 0.75
if not date1.getMonthValid() or not date2.getMonthValid():
return 0.75
else:
return -1.0
else:
return -1.0
#-----------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------
def range_compare(date1,date2):
d1_start = date1.get_start_date()
d2_start = date2.get_start_date()
d1_stop = date1.get_stop_date()
d2_stop = date2.get_stop_date()
if date1.isRange() and date2.isRange():
if d1_start >= d2_start and d1_start <= d2_stop or \
d2_start >= d1_start and d2_start <= d1_stop or \
d1_stop >= d2_start and d1_stop <= d2_stop or \
d2_stop >= d1_start and d2_stop <= d1_stop:
return 0.5
else:
return -1.0
elif date2.isRange():
if d1_start >= d2_start and d1_start <= d2_stop:
return 0.5
else:
return -1.0
else:
if d2_start >= d1_start and d2_start <= d1_stop:
return 0.5
else:
return -1.0
#---------------------------------------------------------------------
#
#
#
#---------------------------------------------------------------------
def name_match(name,name1):
if not name1 or not name:
return 0
srn1 = name.getSurname()
sfx1 = name.getSuffix()
srn2 = name1.getSurname()
sfx2 = name1.getSuffix()
if not name_compare(srn1,srn2):
return -1
if sfx1 != sfx2:
if sfx1 != "" and sfx2 != "":
return -1
if name.getFirstName() == name1.getFirstName():
return 1
else:
list1 = string.split(name.getFirstName())
list2 = string.split(name1.getFirstName())
if len(list1) < len(list2):
return list_reduce(list1,list2)
else:
return list_reduce(list2,list1)
#---------------------------------------------------------------------
#
#
#
#---------------------------------------------------------------------
def list_reduce(list1,list2):
value = 0
for name in list1:
for name2 in list2:
if is_initial(name) and name[0] == name2[0]:
value = value + 0.25
break
if is_initial(name2) and name2[0] == name[0]:
value = value + 0.25
break
if name == name2:
value = value + 0.5
break
if name[0] == name2[0] and name_compare(name,name2):
value = value + 0.25
break
if value == 0:
return -1
else:
return min(value,1)
#---------------------------------------------------------------------
#
#
#
#---------------------------------------------------------------------
def place_match(p1,p2):
if p1 == p2:
return 1
if p1 == None:
name1 = ""
else:
name1 = p1.get_title()
if p2 == None:
name2 = ""
else:
name2 = p2.get_title()
if name1 == "" or name2 == "":
return 0
if name1 == name2:
return 1
list1 = string.split(string.replace(name1,","," "))
list2 = string.split(string.replace(name2,","," "))
value = 0
for name in list1:
for name2 in list2:
if name == name2:
value = value + 0.5
break
if name[0] == name2[0] and name_compare(name,name2):
value = value + 0.25
break
if value == 0:
return -1
else:
return min(value,1)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def is_initial(name):
if len(name) > 2:
return 0
elif len(name) == 2:
if name[0] in string.uppercase and name[1] == '.':
return 1
else:
return name[0] in string.uppercase
#---------------------------------------------------------------------
#
#
#
#---------------------------------------------------------------------
def ancestors_of(p1,list):
if p1 == None:
return
list.append(p1)
f1 = p1.getMainParents()
if f1 != None:
ancestors_of(f1.getFather(),list)
ancestors_of(f1.getMother(),list)
#---------------------------------------------------------------------
#
#
#
#---------------------------------------------------------------------
def name_of(p):
if not p:
return ""
return "%s (%s)" % (GrampsCfg.nameof(p),p.getId())
#-------------------------------------------------------------------------
#
# Merge Places
#
#-------------------------------------------------------------------------
class MergePlaces:
"""
Merges to places into a single place. Displays a dialog box that
allows the places to be combined into one.
"""
def __init__(self,database,place1,place2,update):
self.db = database
self.p1 = place1
self.p2 = place2
self.update = update
self.glade = gtk.glade.XML(const.mergeFile,"merge_places","gramps")
self.top = self.glade.get_widget("merge_places")
self.glade.get_widget("title1_text").set_text(place1.get_title())
self.glade.get_widget("title2_text").set_text(place2.get_title())
self.t3 = self.glade.get_widget("title3_text")
self.t3.set_text(place1.get_title())
self.glade.signal_autoconnect({
"destroy_passed_object" : Utils.destroy_passed_object,
"on_merge_places_clicked" : self.on_merge_places_clicked,
})
self.top.show()
def on_merge_places_clicked(self,obj):
"""
Performs the merge of the places when the merge button is clicked.
"""
t2active = self.glade.get_widget("title2").get_active()
old_id = self.p1.getId()
if t2active:
self.p1.set_title(self.p2.get_title())
elif self.glade.get_widget("title3").get_active():
self.p1.set_title(unicode(self.t3.get_text()))
# Set longitude
if self.p1.get_longitude() == "" and self.p2.get_longitude() != "":
self.p1.set_longitude(self.p2.get_longitude())
# Set latitude
if self.p1.get_latitude() == "" and self.p2.get_latitude() != "":
self.p1.set_latitude(self.p2.get_latitude())
# Add URLs from P2 to P1
for url in self.p2.getUrlList():
self.p1.addUrl(url)
# Copy photos from P2 to P1
for photo in self.p2.getPhotoList():
self.p1.addPhoto(photo)
# Copy sources from P2 to P1
for source in self.p2.getSourceRefList():
self.p1.addSource(source)
# Add notes from P2 to P1
note = self.p2.getNote()
if note != "":
if self.p1.getNote() == "":
self.p1.setNote(note)
elif self.p1.getNote() != note:
self.p1.setNote("%s\n\n%s" % (self.p1.getNote(),note))
if t2active:
list = [self.p1.get_main_location()] + self.p1.get_alternate_locations()
self.p1.set_main_location(self.p2.get_main_location())
for l in list:
if not l.is_empty():
self.p1.add_alternate_locations(l)
else:
list = [self.p2.get_main_location()] + self.p2.get_alternate_locations()
for l in list:
if not l.is_empty():
self.p1.add_alternate_locations(l)
# loop through people, changing event references to P2 to P1
for key in self.db.getPersonKeys():
p = self.db.getPerson(key)
for event in [p.getBirth(), p.getDeath()] + p.getEventList():
if event.getPlace() == self.p2:
event.setPlace(self.p1)
# loop through families, changing event references to P2 to P1
for f in self.db.getFamilyMap().values():
for event in f.getEventList():
if event.getPlace() == self.p2:
event.setPlace(self.p1)
self.db.removePlace(self.p2.getId())
self.db.buildPlaceDisplay(self.p1.getId(),old_id)
self.update(self.p1.getId())
Utils.modified()
Utils.destroy_passed_object(obj)