gramps/src/plugins/tool/FindDupes.py

680 lines
22 KiB
Python
Raw Normal View History

2002-10-20 19:55:16 +05:30
#
# Gramps - a GTK+/GNOME based genealogy program
#
* src/TreeViews/_PersonTreeView.py: Use name_displayer. * src/ReportBase/_ReportUtils.py: Use name_displayer. * src/ReportBase/_CommandLineReport.py: Use name_displayer. * src/ReportBase/_BareReportDialog.py: Use name_displayer. * src/PluginUtils/_Tool.py: Use name_displayer. * src/plugins/TimeLine.py: Use name_displayer. * src/plugins/RelCalc.py: Use name_displayer. * src/plugins/ReadGrdb.py: Use name_displayer. * src/plugins/NarrativeWeb.py: Use name_displayer. * src/plugins/IndivComplete.py: Use name_displayer. * src/plugins/GraphViz.py: Use name_displayer. * src/plugins/FindDupes.py: Use name_displayer. * src/plugins/FamilyGroup.py: Use name_displayer. * src/plugins/DetDescendantReport.py: Use name_displayer. * src/plugins/DetAncestralReport.py: Use name_displayer. * src/plugins/DesGraph.py: Use name_displayer. * src/plugins/DescendReport.py: Use name_displayer. * src/plugins/DescendChart.py: Use name_displayer. * src/plugins/Check.py: Use name_displayer. * src/plugins/Ancestors.py: Use name_displayer. * src/plugins/AncestorReport.py: Use name_displayer. * src/plugins/AncestorChart2.py: Use name_displayer. * src/ObjectSelector/_PersonTreeFrame.py: Use name_displayer. * src/ObjectSelector/_PersonFrame.py: Use name_displayer. * src/Merge/_MergePerson.py: Use name_displayer. * src/GrampsDbUtils/_WriteGedcom.py: Use name_displayer. * src/GrampsDbUtils/_ReadXML.py: Use name_displayer. * src/GrampsDbUtils/_GedcomParse.py: Use name_displayer. * src/FilterEditor/_ShowResults.py: Use name_displayer. * src/FilterEditor/_EditRule.py: Use name_displayer. * src/Editors/_EditPrimary.py: Use name_displayer. * src/Editors/_EditPersonRef.py: Use name_displayer. * src/Editors/_EditPerson.py: Use name_displayer. * src/Editors/_EditName.py: Use name_displayer. * src/Editors/_EditLdsOrd.py: Use name_displayer. * src/Editors/_EditFamily.py: Use name_displayer. * src/DisplayTabs/_PersonRefModel.py: Use name_displayer. * src/DisplayTabs/_NameModel.py: Use name_displayer. * src/DisplayTabs/_ChildModel.py: Use name_displayer. * src/DisplayTabs/_BackRefModel.py: Use name_displayer. * src/DisplayModels/_PeopleModel.py: Use name_displayer. * src/DisplayModels/_FamilyModel.py: Use name_displayer. * src/DataViews/_PersonView.py: Use name_displayer. * src/DataViews/_RelationView.py: Use name_displayer. * src/DataViews/_PedigreeView.py: Use name_displayer. * src/Utils.py: Use name_displayer. * src/SubstKeywords.py: Use name_displayer. * src/Sort.py: Use name_displayer. * src/Reorder.py: Use name_displayer. * src/PageView.py (BookMarkView.add_bookmark): Use name_displayer. * src/Navigation.py: Use name_displayer. * src/DisplayState.py: Use name_displayer. * src/GrampsCfg.py: Use name_displayer. * src/Bookmarks.py (Bookmarks.make_label): Use name_displayer. * src/GrampsDb/Makefile.am (pkgdata_PYTHON): Ship new files. * src/Makefile.am (gdir_PYTHON): Ship ProgressDialog.py svn: r8680
2007-06-28 11:11:40 +05:30
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2008 Brian G. Matherly
# Copyright (C) 2010 Jakim Friant
2002-10-20 19:55:16 +05:30
#
# 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$
"""Tools/Database Processing/Find Possible Duplicate People"""
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# GNOME libraries
#
#-------------------------------------------------------------------------
import gtk
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
import gen.lib
from gui.utils import ProgressMeter
from gui.plug import tool
import soundex
from gen.display.name import displayer as name_displayer
from QuestionDialog import OkDialog
import ListModel
import Errors
from Merge import MergePeople
import GrampsDisplay
import ManagedWindow
from QuestionDialog import RunDatabaseRepair
from gen.ggettext import sgettext as _
from glade import Glade
2005-12-06 12:08:09 +05:30
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
_val2label = {
0.25 : _("Low"),
1.0 : _("Medium"),
2.0 : _("High"),
}
2002-10-20 19:55:16 +05:30
WIKI_HELP_PAGE = '%s_-_Tools' % const.URL_MANUAL_PAGE
WIKI_HELP_SEC = _('manual|Find_Possible_Duplicate_People...')
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def is_initial(name):
if len(name) > 2:
return 0
elif len(name) == 2:
if name[0] == name[0].upper() and name[1] == '.':
2002-10-20 19:55:16 +05:30
return 1
else:
return name[0] == name[0].upper()
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# The Actual tool.
2002-10-20 19:55:16 +05:30
#
#-------------------------------------------------------------------------
class Merge(tool.Tool,ManagedWindow.ManagedWindow):
def __init__(self, dbstate, uistate, options_class, name, callback=None):
tool.Tool.__init__(self, dbstate, options_class, name)
ManagedWindow.ManagedWindow.__init__(self, uistate, [],
self.__class__)
self.dbstate = dbstate
self.uistate = uistate
2002-10-20 19:55:16 +05:30
self.map = {}
self.list = []
self.index = 0
self.merger = None
self.mergee = None
self.removed = {}
self.update = callback
self.use_soundex = 1
top = Glade()
2005-12-06 12:08:09 +05:30
# retrieve options
threshold = self.options.handler.options_dict['threshold']
use_soundex = self.options.handler.options_dict['soundex']
my_menu = gtk.ListStore(str, object)
for val in sorted(_val2label):
my_menu.append([_val2label[val], val])
2002-10-20 19:55:16 +05:30
self.soundex_obj = top.get_object("soundex")
2005-12-06 12:08:09 +05:30
self.soundex_obj.set_active(use_soundex)
self.soundex_obj.show()
self.menu = top.get_object("menu")
self.menu.set_model(my_menu)
self.menu.set_active(0)
2002-10-20 19:55:16 +05:30
window = top.toplevel
window.show()
self.set_window(window, top.get_object('title'),
_('Find Possible Duplicate People'))
top.connect_signals({
"on_merge_ok_clicked" : self.on_merge_ok_clicked,
"destroy_passed_object" : self.close,
"on_help_clicked" : self.on_help_clicked,
"on_delete_merge_event" : self.close,
"on_delete_event" : self.close,
2002-10-20 19:55:16 +05:30
})
self.show()
def build_menu_names(self, obj):
return (_("Tool settings"),_("Find Duplicates tool"))
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC)
def ancestors_of(self, p1_id, id_list):
if (not p1_id) or (p1_id in id_list):
return
id_list.append(p1_id)
p1 = self.db.get_person_from_handle(p1_id)
f1_id = p1.get_main_parents_family_handle()
if f1_id:
* PeopleModel.py: simplify model interface * EditPerson.py: get_family_from_handle fixes * EditSource.py: get_family_from_handle fixes * GraphLayout.py: get_family_from_handle fixes * ImageSelect.py: get_family_from_handle fixes * MediaView.py: get_family_from_handle fixes * MergeData.py: get_family_from_handle fixes * PlaceView.py: get_family_from_handle fixes * ReadXML.py: get_family_from_handle fixes * RelLib.py: get_family_from_handle fixes * Relationship.py: get_family_from_handle fixes * SelectChild.py: get_family_from_handle fixes * SourceView.py: get_family_from_handle fixes * SubstKeywords.py: get_family_from_handle fixes * WriteXML.py: get_family_from_handle fixes * gramps_main.py: get_family_from_handle fixes * plugins/AncestorChart.py: get_family_from_handle fixes * plugins/AncestorChart2.py: get_family_from_handle fixes * plugins/AncestorReport.py: get_family_from_handle fixes * plugins/Ancestors.py: get_family_from_handle fixes * plugins/Check.py: get_family_from_handle fixes * plugins/CountAncestors.py: get_family_from_handle fixes * plugins/Desbrowser.py: get_family_from_handle fixes * plugins/DescendReport.py: get_family_from_handle fixes * plugins/DetAncestralReport.py: get_family_from_handle fixes * plugins/DetDescendantReport.py: get_family_from_handle fixes * plugins/FamilyGroup.py: get_family_from_handle fixes * plugins/FanChart.py: get_family_from_handle fixes * plugins/FtmStyleAncestors.py: get_family_from_handle fixes * plugins/FtmStyleDescendants.py: get_family_from_handle fixes * plugins/GraphViz.py: get_family_from_handle fixes * plugins/IndivComplete.py: get_family_from_handle fixes * plugins/IndivSummary.py: get_family_from_handle fixes * plugins/Merge.py: get_family_from_handle fixes * plugins/RelGraph.py: get_family_from_handle fixes * plugins/Verify.py: get_family_from_handle fixes * plugins/WebPage.py: get_family_from_handle fixes * plugins/WriteCD.py: get_family_from_handle fixes * plugins/WritePkg.py: get_family_from_handle fixes * plugins/rel_de.py: get_family_from_handle fixes * plugins/rel_hu.py: get_family_from_handle fixes * plugins/rel_ru.py: get_family_from_handle fixes svn: r3443
2004-08-20 03:05:16 +05:30
f1 = self.db.get_family_from_handle(f1_id)
self.ancestors_of(f1.get_father_handle(),id_list)
self.ancestors_of(f1.get_mother_handle(),id_list)
def on_merge_ok_clicked(self, obj):
threshold = self.menu.get_model()[self.menu.get_active()][1]
2005-12-06 12:08:09 +05:30
self.use_soundex = int(self.soundex_obj.get_active())
try:
self.find_potentials(threshold)
except AttributeError, msg:
RunDatabaseRepair(str(msg))
return
2005-12-06 12:08:09 +05:30
self.options.handler.options_dict['threshold'] = threshold
self.options.handler.options_dict['soundex'] = self.use_soundex
# Save options
self.options.handler.save_options()
if len(self.map) == 0:
OkDialog(
_("No matches found"),
_("No potential duplicate people were found"),
parent=self.window)
else:
try:
ShowMatches(self.dbstate,self.uistate,self.track,
self.list,self.map,self.update)
except Errors.WindowActiveError:
pass
2002-10-20 19:55:16 +05:30
def find_potentials(self, thresh):
self.progress = ProgressMeter(_('Find Duplicates'),
_('Looking for duplicate people')
)
2002-10-20 19:55:16 +05:30
index = 0
males = {}
females = {}
length = self.db.get_number_of_people()
self.progress.set_pass(_('Pass 1: Building preliminary lists'),
length)
for p1_id in self.db.iter_person_handles():
self.progress.step()
p1 = self.db.get_person_from_handle(p1_id)
key = self.gen_key(get_surnames(p1.get_primary_name()))
if p1.get_gender() == gen.lib.Person.MALE:
if key in males:
males[key].append(p1_id)
2002-10-20 19:55:16 +05:30
else:
males[key] = [p1_id]
2002-10-20 19:55:16 +05:30
else:
if key in females:
females[key].append(p1_id)
2002-10-20 19:55:16 +05:30
else:
females[key] = [p1_id]
2002-10-20 19:55:16 +05:30
self.progress.set_pass(_('Pass 2: Calculating potential matches'),
length)
2002-10-20 19:55:16 +05:30
for p1key in self.db.iter_person_handles():
self.progress.step()
p1 = self.db.get_person_from_handle(p1key)
2002-10-20 19:55:16 +05:30
key = self.gen_key(get_surnames(p1.get_primary_name()))
if p1.get_gender() == gen.lib.Person.MALE:
2002-10-20 19:55:16 +05:30
remaining = males[key]
else:
remaining = females[key]
#index = 0
for p2key in remaining:
#index += 1
if p1key == p2key:
2002-10-20 19:55:16 +05:30
continue
p2 = self.db.get_person_from_handle(p2key)
if p2key in self.map:
(v,c) = self.map[p2key]
if v == p1key:
2002-10-20 19:55:16 +05:30
continue
chance = self.compare_people(p1,p2)
if chance >= thresh:
if p1key in self.map:
val = self.map[p1key]
2002-10-20 19:55:16 +05:30
if val[1] > chance:
self.map[p1key] = (p2key,chance)
2002-10-20 19:55:16 +05:30
else:
self.map[p1key] = (p2key,chance)
2002-10-20 19:55:16 +05:30
self.list = sorted(self.map)
2002-10-20 19:55:16 +05:30
self.length = len(self.list)
self.progress.close()
2002-10-20 19:55:16 +05:30
def gen_key(self, val):
if self.use_soundex:
try:
return soundex.soundex(val)
except UnicodeEncodeError:
return val
else:
return val
def compare_people(self, p1, p2):
name1 = p1.get_primary_name()
name2 = p2.get_primary_name()
chance = self.name_match(name1, name2)
if chance == -1 :
return -1
birth1_ref = p1.get_birth_ref()
if birth1_ref:
birth1 = self.db.get_event_from_handle(birth1_ref.ref)
else:
birth1 = gen.lib.Event()
2002-10-20 19:55:16 +05:30
death1_ref = p1.get_death_ref()
if death1_ref:
death1 = self.db.get_event_from_handle(death1_ref.ref)
else:
death1 = gen.lib.Event()
2002-10-20 19:55:16 +05:30
birth2_ref = p2.get_birth_ref()
if birth2_ref:
birth2 = self.db.get_event_from_handle(birth2_ref.ref)
else:
birth2 = gen.lib.Event()
death2_ref = p2.get_death_ref()
if death2_ref:
death2 = self.db.get_event_from_handle(death2_ref.ref)
else:
death2 = gen.lib.Event()
value = self.date_match(birth1.get_date_object(),
birth2.get_date_object())
if value == -1 :
return -1
chance += value
2002-10-20 19:55:16 +05:30
value = self.date_match(death1.get_date_object(),
death2.get_date_object())
if value == -1 :
return -1
chance += value
value = self.place_match(birth1.get_place_handle(),
birth2.get_place_handle())
if value == -1 :
return -1
chance += value
value = self.place_match(death1.get_place_handle(),
death2.get_place_handle())
if value == -1 :
return -1
chance += value
ancestors = []
self.ancestors_of(p1.get_handle(),ancestors)
if p2.get_handle() in ancestors:
return -1
ancestors = []
self.ancestors_of(p2.get_handle(),ancestors)
if p1.get_handle() in ancestors:
2002-10-20 19:55:16 +05:30
return -1
f1_id = p1.get_main_parents_family_handle()
f2_id = p2.get_main_parents_family_handle()
if f1_id and f2_id:
f1 = self.db.get_family_from_handle(f1_id)
f2 = self.db.get_family_from_handle(f2_id)
dad1_id = f1.get_father_handle()
if dad1_id:
dad1 = get_name_obj(self.db.get_person_from_handle(dad1_id))
else:
dad1 = None
dad2_id = f2.get_father_handle()
if dad2_id:
dad2 = get_name_obj(self.db.get_person_from_handle(dad2_id))
else:
dad2 = None
value = self.name_match(dad1,dad2)
if value == -1:
return -1
chance += value
mom1_id = f1.get_mother_handle()
if mom1_id:
mom1 = get_name_obj(self.db.get_person_from_handle(mom1_id))
else:
mom1 = None
mom2_id = f2.get_mother_handle()
if mom2_id:
mom2 = get_name_obj(self.db.get_person_from_handle(mom2_id))
else:
mom2 = None
value = self.name_match(mom1,mom2)
if value == -1:
return -1
chance += value
for f1_id in p1.get_family_handle_list():
f1 = self.db.get_family_from_handle(f1_id)
for f2_id in p2.get_family_handle_list():
f2 = self.db.get_family_from_handle(f2_id)
if p1.get_gender() == gen.lib.Person.FEMALE:
father1_id = f1.get_father_handle()
father2_id = f2.get_father_handle()
if father1_id and father2_id:
if father1_id == father2_id:
chance += 1
else:
father1 = self.db.get_person_from_handle(father1_id)
father2 = self.db.get_person_from_handle(father2_id)
fname1 = get_name_obj(father1)
fname2 = get_name_obj(father2)
value = self.name_match(fname1,fname2)
if value != -1:
chance += value
else:
mother1_id = f1.get_mother_handle()
mother2_id = f2.get_mother_handle()
if mother1_id and mother2_id:
if mother1_id == mother2_id:
chance += 1
else:
mother1 = self.db.get_person_from_handle(mother1_id)
mother2 = self.db.get_person_from_handle(mother2_id)
mname1 = get_name_obj(mother1)
mname2 = get_name_obj(mother2)
value = self.name_match(mname1,mname2)
if value != -1:
chance += value
return chance
2002-10-20 19:55:16 +05:30
def name_compare(self, s1, s2):
2002-10-20 19:55:16 +05:30
if self.use_soundex:
try:
return soundex.compare(s1,s2)
except UnicodeEncodeError:
return s1 == s2
2002-10-20 19:55:16 +05:30
else:
return s1 == s2
def date_match(self, date1, date2):
if date1.is_empty() or date2.is_empty():
2002-10-20 19:55:16 +05:30
return 0
if date1.is_equal(date2):
2002-10-20 19:55:16 +05:30
return 1
if date1.is_compound() or date2.is_compound():
2002-10-20 19:55:16 +05:30
return self.range_compare(date1,date2)
if date1.get_year() == date2.get_year():
if date1.get_month() == date2.get_month():
2002-10-20 19:55:16 +05:30
return 0.75
if not date1.get_month_valid() or not date2.get_month_valid():
2002-10-20 19:55:16 +05:30
return 0.75
else:
return -1
else:
return -1
def range_compare(self, date1, date2):
start_date_1 = date1.get_start_date()[0:3]
start_date_2 = date2.get_start_date()[0:3]
stop_date_1 = date1.get_stop_date()[0:3]
stop_date_2 = date2.get_stop_date()[0:3]
if date1.is_compound() and date2.is_compound():
if (start_date_2 <= start_date_1 <= stop_date_2 or
start_date_1 <= start_date_2 <= stop_date_1 or
start_date_2 <= stop_date_1 <= stop_date_2 or
start_date_1 <= stop_date_2 <= stop_date_1):
2002-10-20 19:55:16 +05:30
return 0.5
else:
return -1
elif date2.is_compound():
if start_date_2 <= start_date_1 <= stop_date_2:
2002-10-20 19:55:16 +05:30
return 0.5
else:
return -1
else:
if start_date_1 <= start_date_2 <= stop_date_1:
2002-10-20 19:55:16 +05:30
return 0.5
else:
return -1
def name_match(self, name, name1):
2002-10-20 19:55:16 +05:30
if not name1 or not name:
return 0
srn1 = get_surnames(name)
sfx1 = name.get_suffix()
srn2 = get_surnames(name1)
sfx2 = name1.get_suffix()
2002-10-20 19:55:16 +05:30
if not self.name_compare(srn1,srn2):
return -1
if sfx1 != sfx2:
if sfx1 != "" and sfx2 != "":
return -1
if name.get_first_name() == name1.get_first_name():
2002-10-20 19:55:16 +05:30
return 1
else:
list1 = name.get_first_name().split()
list2 = name1.get_first_name().split()
2002-10-20 19:55:16 +05:30
if len(list1) < len(list2):
return self.list_reduce(list1,list2)
else:
return self.list_reduce(list2,list1)
def place_match(self, p1_id, p2_id):
if p1_id == p2_id:
2002-10-20 19:55:16 +05:30
return 1
if not p1_id:
2002-10-20 19:55:16 +05:30
name1 = ""
else:
p1 = self.db.get_place_from_handle(p1_id)
2002-10-20 19:55:16 +05:30
name1 = p1.get_title()
if not p2_id:
2002-10-20 19:55:16 +05:30
name2 = ""
else:
p2 = self.db.get_place_from_handle(p2_id)
2002-10-20 19:55:16 +05:30
name2 = p2.get_title()
if not (name1 and name2):
2002-10-20 19:55:16 +05:30
return 0
if name1 == name2:
return 1
list1 = name1.replace(","," ").split()
list2 = name2.replace(","," ").split()
2002-10-20 19:55:16 +05:30
value = 0
for name in list1:
for name2 in list2:
if name == name2:
value += 0.5
elif name[0] == name2[0] and self.name_compare(name, name2):
value += 0.25
return min(value,1) if value else -1
2002-10-20 19:55:16 +05:30
def list_reduce(self, list1, list2):
value = 0
for name in list1:
for name2 in list2:
if is_initial(name) and name[0] == name2[0]:
value += 0.25
elif is_initial(name2) and name2[0] == name[0]:
value += 0.25
elif name == name2:
value += 0.5
elif name[0] == name2[0] and self.name_compare(name, name2):
value += 0.25
return min(value,1) if value else -1
class ShowMatches(ManagedWindow.ManagedWindow):
def __init__(self, dbstate, uistate, track, the_list, the_map, callback):
ManagedWindow.ManagedWindow.__init__(self,uistate,track,self.__class__)
2002-10-20 19:55:16 +05:30
self.dellist = {}
self.list = the_list
self.map = the_map
self.length = len(self.list)
self.update = callback
self.db = dbstate.db
self.dbstate = dbstate
self.uistate = uistate
top = Glade(toplevel="mergelist")
window = top.toplevel
window.show()
self.set_window(window, top.get_object('title'),
_('Potential Merges'))
self.mlist = top.get_object("mlist")
top.connect_signals({
"destroy_passed_object" : self.close,
"on_do_merge_clicked" : self.on_do_merge_clicked,
"on_help_show_clicked" : self.on_help_clicked,
"on_delete_show_event" : self.close,
})
2002-10-20 19:55:16 +05:30
mtitles = [
(_('Rating'),3,75),
(_('First Person'),1,200),
(_('Second Person'),2,200),
('',-1,0)
]
self.list = ListModel.ListModel(self.mlist,mtitles,
event_func=self.on_do_merge_clicked)
self.redraw()
self.show()
2002-10-20 19:55:16 +05:30
def build_menu_names(self, obj):
return (_("Merge candidates"),None)
2002-10-20 19:55:16 +05:30
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC)
def redraw(self):
list = []
for p1key, p1data in self.map.iteritems():
if p1key in self.dellist:
continue
(p2key,c) = p1data
if p1key == p2key:
continue
list.append((c,p1key,p2key))
2002-10-20 19:55:16 +05:30
self.list.clear()
for (c,p1key,p2key) in list:
c1 = "%5.2f" % c
c2 = "%5.2f" % (100-c)
p1 = self.db.get_person_from_handle(p1key)
p2 = self.db.get_person_from_handle(p2key)
if not p1 or not p2:
continue
* src/TreeViews/_PersonTreeView.py: Use name_displayer. * src/ReportBase/_ReportUtils.py: Use name_displayer. * src/ReportBase/_CommandLineReport.py: Use name_displayer. * src/ReportBase/_BareReportDialog.py: Use name_displayer. * src/PluginUtils/_Tool.py: Use name_displayer. * src/plugins/TimeLine.py: Use name_displayer. * src/plugins/RelCalc.py: Use name_displayer. * src/plugins/ReadGrdb.py: Use name_displayer. * src/plugins/NarrativeWeb.py: Use name_displayer. * src/plugins/IndivComplete.py: Use name_displayer. * src/plugins/GraphViz.py: Use name_displayer. * src/plugins/FindDupes.py: Use name_displayer. * src/plugins/FamilyGroup.py: Use name_displayer. * src/plugins/DetDescendantReport.py: Use name_displayer. * src/plugins/DetAncestralReport.py: Use name_displayer. * src/plugins/DesGraph.py: Use name_displayer. * src/plugins/DescendReport.py: Use name_displayer. * src/plugins/DescendChart.py: Use name_displayer. * src/plugins/Check.py: Use name_displayer. * src/plugins/Ancestors.py: Use name_displayer. * src/plugins/AncestorReport.py: Use name_displayer. * src/plugins/AncestorChart2.py: Use name_displayer. * src/ObjectSelector/_PersonTreeFrame.py: Use name_displayer. * src/ObjectSelector/_PersonFrame.py: Use name_displayer. * src/Merge/_MergePerson.py: Use name_displayer. * src/GrampsDbUtils/_WriteGedcom.py: Use name_displayer. * src/GrampsDbUtils/_ReadXML.py: Use name_displayer. * src/GrampsDbUtils/_GedcomParse.py: Use name_displayer. * src/FilterEditor/_ShowResults.py: Use name_displayer. * src/FilterEditor/_EditRule.py: Use name_displayer. * src/Editors/_EditPrimary.py: Use name_displayer. * src/Editors/_EditPersonRef.py: Use name_displayer. * src/Editors/_EditPerson.py: Use name_displayer. * src/Editors/_EditName.py: Use name_displayer. * src/Editors/_EditLdsOrd.py: Use name_displayer. * src/Editors/_EditFamily.py: Use name_displayer. * src/DisplayTabs/_PersonRefModel.py: Use name_displayer. * src/DisplayTabs/_NameModel.py: Use name_displayer. * src/DisplayTabs/_ChildModel.py: Use name_displayer. * src/DisplayTabs/_BackRefModel.py: Use name_displayer. * src/DisplayModels/_PeopleModel.py: Use name_displayer. * src/DisplayModels/_FamilyModel.py: Use name_displayer. * src/DataViews/_PersonView.py: Use name_displayer. * src/DataViews/_RelationView.py: Use name_displayer. * src/DataViews/_PedigreeView.py: Use name_displayer. * src/Utils.py: Use name_displayer. * src/SubstKeywords.py: Use name_displayer. * src/Sort.py: Use name_displayer. * src/Reorder.py: Use name_displayer. * src/PageView.py (BookMarkView.add_bookmark): Use name_displayer. * src/Navigation.py: Use name_displayer. * src/DisplayState.py: Use name_displayer. * src/GrampsCfg.py: Use name_displayer. * src/Bookmarks.py (Bookmarks.make_label): Use name_displayer. * src/GrampsDb/Makefile.am (pkgdata_PYTHON): Ship new files. * src/Makefile.am (gdir_PYTHON): Ship ProgressDialog.py svn: r8680
2007-06-28 11:11:40 +05:30
pn1 = name_displayer.display(p1)
pn2 = name_displayer.display(p2)
self.list.add([c1, pn1, pn2,c2],(p1key,p2key))
def on_do_merge_clicked(self, obj):
store,iter = self.list.selection.get_selected()
if not iter:
return
2002-10-20 19:55:16 +05:30
(self.p1,self.p2) = self.list.get_object(iter)
MergePeople(self.dbstate, self.uistate, self.p1, self.p2,
self.on_update, True)
2002-10-20 19:55:16 +05:30
def on_update(self):
if self.db.has_person_handle(self.p1):
phoenix = self.p1
titanic = self.p2
else:
phoenix = self.p2
titanic = self.p1
self.dellist[titanic] = phoenix
for key, data in self.dellist.iteritems():
if data == titanic:
self.dellist[key] = phoenix
self.update()
self.redraw()
def update_and_destroy(self, obj):
self.update(1)
self.close()
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
2002-10-20 19:55:16 +05:30
def name_of(p):
if not p:
return ""
* src/TreeViews/_PersonTreeView.py: Use name_displayer. * src/ReportBase/_ReportUtils.py: Use name_displayer. * src/ReportBase/_CommandLineReport.py: Use name_displayer. * src/ReportBase/_BareReportDialog.py: Use name_displayer. * src/PluginUtils/_Tool.py: Use name_displayer. * src/plugins/TimeLine.py: Use name_displayer. * src/plugins/RelCalc.py: Use name_displayer. * src/plugins/ReadGrdb.py: Use name_displayer. * src/plugins/NarrativeWeb.py: Use name_displayer. * src/plugins/IndivComplete.py: Use name_displayer. * src/plugins/GraphViz.py: Use name_displayer. * src/plugins/FindDupes.py: Use name_displayer. * src/plugins/FamilyGroup.py: Use name_displayer. * src/plugins/DetDescendantReport.py: Use name_displayer. * src/plugins/DetAncestralReport.py: Use name_displayer. * src/plugins/DesGraph.py: Use name_displayer. * src/plugins/DescendReport.py: Use name_displayer. * src/plugins/DescendChart.py: Use name_displayer. * src/plugins/Check.py: Use name_displayer. * src/plugins/Ancestors.py: Use name_displayer. * src/plugins/AncestorReport.py: Use name_displayer. * src/plugins/AncestorChart2.py: Use name_displayer. * src/ObjectSelector/_PersonTreeFrame.py: Use name_displayer. * src/ObjectSelector/_PersonFrame.py: Use name_displayer. * src/Merge/_MergePerson.py: Use name_displayer. * src/GrampsDbUtils/_WriteGedcom.py: Use name_displayer. * src/GrampsDbUtils/_ReadXML.py: Use name_displayer. * src/GrampsDbUtils/_GedcomParse.py: Use name_displayer. * src/FilterEditor/_ShowResults.py: Use name_displayer. * src/FilterEditor/_EditRule.py: Use name_displayer. * src/Editors/_EditPrimary.py: Use name_displayer. * src/Editors/_EditPersonRef.py: Use name_displayer. * src/Editors/_EditPerson.py: Use name_displayer. * src/Editors/_EditName.py: Use name_displayer. * src/Editors/_EditLdsOrd.py: Use name_displayer. * src/Editors/_EditFamily.py: Use name_displayer. * src/DisplayTabs/_PersonRefModel.py: Use name_displayer. * src/DisplayTabs/_NameModel.py: Use name_displayer. * src/DisplayTabs/_ChildModel.py: Use name_displayer. * src/DisplayTabs/_BackRefModel.py: Use name_displayer. * src/DisplayModels/_PeopleModel.py: Use name_displayer. * src/DisplayModels/_FamilyModel.py: Use name_displayer. * src/DataViews/_PersonView.py: Use name_displayer. * src/DataViews/_RelationView.py: Use name_displayer. * src/DataViews/_PedigreeView.py: Use name_displayer. * src/Utils.py: Use name_displayer. * src/SubstKeywords.py: Use name_displayer. * src/Sort.py: Use name_displayer. * src/Reorder.py: Use name_displayer. * src/PageView.py (BookMarkView.add_bookmark): Use name_displayer. * src/Navigation.py: Use name_displayer. * src/DisplayState.py: Use name_displayer. * src/GrampsCfg.py: Use name_displayer. * src/Bookmarks.py (Bookmarks.make_label): Use name_displayer. * src/GrampsDb/Makefile.am (pkgdata_PYTHON): Ship new files. * src/Makefile.am (gdir_PYTHON): Ship ProgressDialog.py svn: r8680
2007-06-28 11:11:40 +05:30
return "%s (%s)" % (name_displayer.display(p),p.get_handle())
2002-10-20 19:55:16 +05:30
def get_name_obj(person):
if person:
return person.get_primary_name()
2002-10-20 19:55:16 +05:30
else:
return None
def get_surnames(name):
"""Construct a full surname of the surnames"""
return ' '.join([surn.get_surname() for surn in name.get_surname_list()])
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
2005-12-06 12:08:09 +05:30
def by_id(p1,p2):
return cmp(p1.get_handle(),p2.get_handle())
2002-10-20 19:55:16 +05:30
2005-12-06 12:08:09 +05:30
#------------------------------------------------------------------------
2002-10-20 19:55:16 +05:30
#
2005-12-06 12:08:09 +05:30
#
2002-10-20 19:55:16 +05:30
#
2005-12-06 12:08:09 +05:30
#------------------------------------------------------------------------
class MergeOptions(tool.ToolOptions):
2005-12-06 12:08:09 +05:30
"""
Defines options and provides handling interface.
"""
def __init__(self, name,person_id=None):
tool.ToolOptions.__init__(self, name,person_id)
2005-12-06 12:08:09 +05:30
# Options specific for this report
self.options_dict = {
'soundex' : 1,
'threshold' : 0.25,
}
self.options_help = {
'soundex' : ("=0/1","Whether to use SoundEx codes",
["Do not use SoundEx","Use SoundEx"],
True),
'threshold' : ("=num","Threshold for tolerance",
"Floating point number")
}