gramps/src/MergeData.py
Don Allingham fe49a978b5 First attempt at autosave
svn: r668
2001-12-28 01:22:20 +00:00

863 lines
29 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000 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
#
import RelLib
import utils
import Config
import const
from intl import gettext
_ = gettext
import string
import libglade
#-------------------------------------------------------------------------
#
# Merge People
#
#-------------------------------------------------------------------------
class MergePeople:
def __init__(self,db,person1,person2,update):
self.db = db
self.p1 = person1
self.p2 = person2
self.update = update
self.glade = libglade.GladeXML(const.mergeFile,"merge")
self.top = self.glade.get_widget("merge")
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,
})
label_text = "Merge %s and %s" % (Config.nameof(person1),Config.nameof(person2))
self.glade.get_widget("progress").set_text(label_text)
f1 = person1.getMainFamily()
f2 = person2.getMainFamily()
name1 = Config.nameof(person1)
death1 = person1.getDeath().getDate()
dplace1 = self.place_name(person1.getDeath())
birth1 = person1.getBirth().getDate()
bplace1 = self.place_name(person1.getBirth())
name2 = Config.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 = ""
label1 = "%s (%s)" % (_("First Person"),person1.getId())
label2 = "%s (%s)" % (_("Second Person"),person2.getId())
self.glade.get_widget("PersonFrame1").set_label(label1)
self.glade.get_widget("PersonFrame2").set_label(label2)
self.glade.get_widget("name1_text").set_text(name1)
self.glade.get_widget("name1_text").set_position(0)
self.glade.get_widget("name2_text").set_text(name2)
self.glade.get_widget("name2_text").set_position(0)
self.bname1 = self.glade.get_widget("bname1")
self.bname1.set_active(1)
self.glade.get_widget("birth1_text").set_text(birth1)
self.glade.get_widget("birth1_text").set_position(0)
self.glade.get_widget("birth2_text").set_text(birth2)
self.glade.get_widget("birth2_text").set_position(0)
self.glade.get_widget("bplace1_text").set_text(bplace1)
self.glade.get_widget("bplace1_text").set_position(0)
self.glade.get_widget("bplace2_text").set_text(bplace2)
self.glade.get_widget("bplace2_text").set_position(0)
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.glade.get_widget("death1_text").set_text(death1)
self.glade.get_widget("death1_text").set_position(0)
self.glade.get_widget("dplace1_text").set_text(dplace1)
self.glade.get_widget("dplace1_text").set_position(0)
self.glade.get_widget("death2_text").set_text(death2)
self.glade.get_widget("death2_text").set_position(0)
self.glade.get_widget("dplace2_text").set_text(dplace2)
self.glade.get_widget("dplace2_text").set_position(0)
self.glade.get_widget("father1").set_text(father1)
self.glade.get_widget("father1").set_position(0)
self.glade.get_widget("father2").set_text(father2)
self.glade.get_widget("father2").set_position(0)
self.glade.get_widget("mother1").set_text(mother1)
self.glade.get_widget("mother1").set_position(0)
self.glade.get_widget("mother2").set_text(mother2)
self.glade.get_widget("mother2").set_position(0)
p1list = person1.getFamilyList()
p2list = person2.getFamilyList()
length = min(len(p1list),3)
self.glade.get_widget("spouse1").clear()
for index in range(0,3):
if index < length and p1list[index]:
if person1.getGender() == RelLib.Person.male:
spouse = p1list[index].getMother()
else:
spouse = p1list[index].getFather()
if spouse == None:
name = "unknown"
else:
name = "%s (%s)" % (Config.nameof(spouse),spouse.getId())
self.glade.get_widget("spouse1").append([name])
length = min(len(p2list),3)
self.glade.get_widget("spouse2").clear()
for index in range(0,3):
if index < length and p2list[index]:
if person2.getGender() == RelLib.Person.male:
spouse = p2list[index].getMother()
else:
spouse = p2list[index].getFather()
if spouse == None:
name = "unknown"
else:
name = "%s (%s)" % (Config.nameof(spouse),spouse.getId())
self.glade.get_widget("spouse2").append([name])
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 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)
EditPerson.EditPerson(self.p1,self.db,self.empty)
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)
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.getMainFamily()
if orig_family:
orig_family.removeChild(self.p1)
source_family = self.p2.getMainFamily()
self.p1.setMainFamily(source_family)
if source_family:
if self.p2 in source_family.getChildList():
source_family.removeChild(self.p2)
if self.p1 not in source_family.getChildList():
source_family.addChild(self.p1)
else:
source_family = self.p2.getMainFamily()
if source_family:
source_family.removeChild(self.p2)
self.p2.setMainFamily(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:
del self.db.getPersonMap()[self.p2.getId()]
except:
print "%s is not in the person map!" % (Config.nameof(self.p2))
self.update(self.p1,self.p2)
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
# 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():
tgt_family.addChild(child)
if child.getMainFamily() == src_family:
child.setMainFamily(tgt_family)
else:
index = 0
for fam in child.getAltFamilies():
if fam[0] == src_family:
child.getAltFamilies()[index] = (tgt_family,fam[1],fam[2])
index = index + 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.getMainFamily() == family:
child.setMainFamily(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 :
return -1
birth1 = p1.getBirth()
death1 = p1.getDeath()
birth2 = p2.getBirth()
death2 = p2.getDeath()
value = date_match(birth1.getDateObj(),birth2.getDateObj())
if value == -1 :
return -1
chance = chance + value
value = date_match(death1.getDateObj(),death2.getDateObj())
if value == -1 :
return -1
chance = chance + value
value = place_match(birth1.getPlace(),birth2.getPlace())
if value == -1 :
return -1
chance = chance + value
value = place_match(death1.getPlace(),death2.getPlace())
if value == -1 :
return -1
chance = chance + value
ancestors = []
ancestors_of(p1,ancestors)
if p2 in ancestors:
return -1
ancestors = []
ancestors_of(p2,ancestors)
if p1 in ancestors:
return -1
f1 = p1.getMainFamily()
f2 = p2.getMainFamily()
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:
return -1
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:
return -1
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
else:
fname1 = Config.nameof(father1)
fname2 = Config.nameof(father2)
value = name_match(fname1,fname2)
if value != -1:
chance = chance + value
else:
mother1 = f1.getMother()
mother2 = f2.getMother()
if mother1 and mother2:
if mother1 == mother2:
chance = chance + 1
else:
mname1 = Config.nameof(mother1)
mname2 = Config.nameof(mother2)
value = name_match(mname1,mname2)
if value != -1:
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
if date1.getDate() == date2.getDate():
return 1
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
else:
return -1
#-----------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------
def range_compare(date1,date2):
if date1.isRange() and date2.isRange():
if date1.get_start_date() >= date2.get_start_date() and \
date1.get_start_date() <= date2.get_stop_date() or \
date2.get_start_date() >= date1.get_start_date() and \
date2.get_start_date() <= date1.get_stop_date() or \
date1.get_stop_date() >= date2.get_start_date() and \
date1.get_stop_date() <= date2.get_stop_date() or \
date2.get_stop_date() >= date1.get_start_date() and \
date2.get_stop_date() <= date1.get_stop_date():
return 0.5
else:
return -1
elif date2.isRange():
if date1.get_start_date() >= date2.get_start_date() and \
date1.get_start_date() <= date2.get_stop_date():
return 0.5
else:
return -1
else:
if date2.get_start_date() >= date1.get_start_date() and \
date2.get_start_date() <= date1.get_stop_date():
return 0.5
else:
return -1
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.getMainFamily()
if f1 != None:
ancestors_of(f1.getFather(),list)
ancestors_of(f1.getMother(),list)
def name_of(p):
if not p:
return ""
return "%s (%s)" % ( Config.nameof(p),p.getId())
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MergePlaces:
def __init__(self,database,place1,place2,update):
self.db = database
self.p1 = place1
self.p2 = place2
self.update = update
self.glade = libglade.GladeXML(const.mergeFile,"merge_places")
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):
t2active = self.glade.get_widget("title2").get_active()
if t2active:
self.p1.set_title(self.p2.get_title())
elif self.glade.get_widget("title3").get_active():
self.p1.set_title(self.t3.get_text())
if self.p1.get_longitude() == "" and self.p2.get_longitude() != "":
self.p1.set_longitude(self.p2.get_longitude())
if self.p1.get_latitude() == "" and self.p2.get_latitude() != "":
self.p1.set_latitude(self.p2.get_latitude())
for url in self.p2.getUrlList():
self.p1.addUrl(url)
for photo in self.p2.getPhotoList():
self.p1.addPhoto(photo)
for source in self.p2.getSourceRefList():
self.p1.addSource(source)
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)
for p in self.db.getPersonMap().values():
for event in [p.getBirth(), p.getDeath()] + p.getEventList():
if event.getPlace() == self.p2:
event.setPlace(self.p1)
for f in self.db.getFamilyMap().values():
for event in f.getEventList():
if event.getPlace() == self.p2:
event.setPlace(self.p1)
del self.db.getPlaceMap()[self.p2.getId()]
self.update()
utils.modified()
utils.destroy_passed_object(obj)