Merge pull request #102 from SNoiraud/bug5449

5449: Pedigree view crashes if you happen to choose a child as ancestor
This commit is contained in:
Sam Manzi 2016-04-16 07:19:01 +10:00
commit 4342b319e3
6 changed files with 486 additions and 0 deletions

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE database PUBLIC "-//Gramps//DTD Gramps XML 1.6.0//EN"
"http://gramps-project.org/xml/1.6.0/grampsxml.dtd">
<database xmlns="http://gramps-project.org/xml/1.6.0/">
<header>
<created date="2015-07-21" version="GrampsAIO64-4.1.3-1"/>
<researcher>
</researcher>
</header>
<people>
<person handle="_d12d89b02aa5121726adba4f517" change="1437459556" id="I0000">
<gender>M</gender>
<name type="Birth Name">
<first>Child</first>
<surname>Child</surname>
</name>
<childof hlink="_d12d8a32ae01d9449d5da18eda0"/>
<parentin hlink="_d12d8a7573462f21848658c9c5d"/>
</person>
<person handle="_d12d89d4dce7eaaa68e31f6100e" change="1437459556" id="I0001">
<gender>M</gender>
<name type="Birth Name">
<first>Father</first>
<surname>Father</surname>
</name>
<childof hlink="_d12d8a7573462f21848658c9c5d"/>
<parentin hlink="_d12d8a32ae01d9449d5da18eda0"/>
</person>
</people>
<families>
<family handle="_d12d8a32ae01d9449d5da18eda0" change="1437459531" id="F0000">
<rel type="Unknown"/>
<father hlink="_d12d89d4dce7eaaa68e31f6100e"/>
<childref hlink="_d12d89b02aa5121726adba4f517"/>
</family>
<family handle="_d12d8a7573462f21848658c9c5d" change="1437459556" id="F0001">
<rel type="Unknown"/>
<father hlink="_d12d89b02aa5121726adba4f517"/>
<childref hlink="_d12d89d4dce7eaaa68e31f6100e"/>
</family>
</families>
</database>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE database PUBLIC "-//Gramps//DTD Gramps XML 1.7.1//EN"
"http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
<database xmlns="http://gramps-project.org/xml/1.7.1/">
<header>
<created date="2016-04-10" version="5.0.0"/>
<researcher>
</researcher>
</header>
<people>
<person handle="_d12d89b02aa5121726adba4f517" change="1456088166" id="I0000">
<gender>M</gender>
<name type="Birth Name">
<first>Child</first>
<surname>Child</surname>
</name>
<childof hlink="_d12d8a32ae01d9449d5da18eda0"/>
<childof hlink="_d3e382712c1429da71250fdc5b7"/>
<parentin hlink="_d12d8a7573462f21848658c9c5d"/>
</person>
<person handle="_d12d89d4dce7eaaa68e31f6100e" change="1437459556" id="I0001">
<gender>M</gender>
<name type="Birth Name">
<first>Father</first>
<surname>Father</surname>
</name>
<childof hlink="_d12d8a7573462f21848658c9c5d"/>
<parentin hlink="_d12d8a32ae01d9449d5da18eda0"/>
</person>
<person handle="_d3e381c6e76124b59bb9a4c312e" change="1456088118" id="I0002">
<gender>M</gender>
<name type="Birth Name">
<first>Child2</first>
<surname>Father</surname>
</name>
<childof hlink="_d12d8a32ae01d9449d5da18eda0"/>
<parentin hlink="_d3e38215ed8371bb62a9f7785f1"/>
</person>
<person handle="_d3e382494d07adfa45784539dce" change="1456088166" id="I0003">
<gender>M</gender>
<name type="Birth Name">
<first>Child3</first>
<surname>Father</surname>
</name>
<childof hlink="_d3e38215ed8371bb62a9f7785f1"/>
<parentin hlink="_d3e382712c1429da71250fdc5b7"/>
</person>
</people>
<families>
<family handle="_d12d8a32ae01d9449d5da18eda0" change="1456088074" id="F0000">
<rel type="Unknown"/>
<father hlink="_d12d89d4dce7eaaa68e31f6100e"/>
<childref hlink="_d12d89b02aa5121726adba4f517"/>
<childref hlink="_d3e381c6e76124b59bb9a4c312e"/>
</family>
<family handle="_d12d8a7573462f21848658c9c5d" change="1437459556" id="F0001">
<rel type="Unknown"/>
<father hlink="_d12d89b02aa5121726adba4f517"/>
<childref hlink="_d12d89d4dce7eaaa68e31f6100e"/>
</family>
<family handle="_d3e38215ed8371bb62a9f7785f1" change="1456088118" id="F0002">
<rel type="Unknown"/>
<father hlink="_d3e381c6e76124b59bb9a4c312e"/>
<childref hlink="_d3e382494d07adfa45784539dce"/>
</family>
<family handle="_d3e382712c1429da71250fdc5b7" change="1456088166" id="F0003">
<rel type="Unknown"/>
<father hlink="_d3e382494d07adfa45784539dce"/>
<childref hlink="_d12d89b02aa5121726adba4f517"/>
</family>
</families>
</database>

View File

@ -86,6 +86,7 @@ class ProbablyAlive(object):
self.MAX_SIB_AGE_DIFF = max_sib_age_diff self.MAX_SIB_AGE_DIFF = max_sib_age_diff
self.MAX_AGE_PROB_ALIVE = max_age_prob_alive self.MAX_AGE_PROB_ALIVE = max_age_prob_alive
self.AVG_GENERATION_GAP = avg_generation_gap self.AVG_GENERATION_GAP = avg_generation_gap
self.pset = set()
def probably_alive_range(self, person, is_spouse=False): def probably_alive_range(self, person, is_spouse=False):
# FIXME: some of these computed dates need to be a span. For # FIXME: some of these computed dates need to be a span. For
@ -95,6 +96,7 @@ class ProbablyAlive(object):
# "between 1930 and 1940") # "between 1930 and 1940")
if person is None: if person is None:
return (None, None, "", None) return (None, None, "", None)
self.pset = set()
birth_ref = person.get_birth_ref() birth_ref = person.get_birth_ref()
death_ref = person.get_death_ref() death_ref = person.get_death_ref()
death_date = None death_date = None
@ -272,6 +274,9 @@ class ProbablyAlive(object):
# ago. # ago.
def descendants_too_old (person, years): def descendants_too_old (person, years):
if person.handle in self.pset:
return (None, None, "", None)
self.pset.add(person.handle)
for family_handle in person.get_family_handle_list(): for family_handle in person.get_family_handle_list():
family = self.db.get_family_from_handle(family_handle) family = self.db.get_family_from_handle(family_handle)
if not family: if not family:
@ -343,6 +348,9 @@ class ProbablyAlive(object):
return (date1, date2, explain, other) return (date1, date2, explain, other)
def ancestors_too_old(person, year): def ancestors_too_old(person, year):
if person.handle in self.pset:
return (None, None, "", None)
self.pset.add(person.handle)
LOG.debug("ancestors_too_old('%s', %s)".format( LOG.debug("ancestors_too_old('%s', %s)".format(
name_displayer.display(person), year) ) name_displayer.display(person), year) )
family_handle = person.get_main_parents_family_handle() family_handle = person.get_main_parents_family_handle()

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkDialog" id="findloop">
<property name="can_focus">False</property>
<property name="default_width">450</property>
<property name="default_height">400</property>
<property name="type_hint">dialog</property>
<signal name="delete-event" handler="on_delete_event" swapped="no"/>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="close">
<property name="label">gtk-close</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="destroy_passed_object" swapped="yes"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="helpbutton1">
<property name="label">gtk-help</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_help_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">8</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">out</property>
<child>
<object class="GtkTreeView" id="treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<action-widgets>
<action-widget response="0">close</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -0,0 +1,219 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2009 Stephane Charette
# Copyright (C) 2016- Serge Noiraud
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
"Find possible loop in a people descendance"
#------------------------------------------------------------------------
#
# GNOME/GTK modules
#
#------------------------------------------------------------------------
from gi.repository import Gtk
from gi.repository import GObject
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
ngettext = glocale.translation.ngettext # else "nearby" comments are ignored
from gramps.gen.const import URL_MANUAL_PAGE
from gramps.gui.plug import tool
from gramps.gen.plug.report import utils as ReportUtils
from gramps.gui.editors import EditPerson, EditFamily
from gramps.gui.managedwindow import ManagedWindow
from gramps.gui.utils import ProgressMeter
from gramps.gui.display import display_help
from gramps.gui.glade import Glade
from gramps.gen.lib import Tag
from gramps.gen.db import DbTxn
from gramps.gen.display.name import displayer as _nd
from gramps.gui.editors import EditFamily
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
WIKI_HELP_PAGE = '%s_-_Tools' % URL_MANUAL_PAGE
WIKI_HELP_SEC = _('manual|Find_database_loop')
#------------------------------------------------------------------------
#
# FindLoop class
#
#------------------------------------------------------------------------
class FindLoop(ManagedWindow) :
def __init__(self, dbstate, user, options_class, name, callback=None):
uistate = user.uistate
self.title = _('Find database loop')
ManagedWindow.__init__(self, uistate, [], self.__class__)
self.dbstate = dbstate
self.uistate = uistate
self.db = dbstate.db
topDialog = Glade()
topDialog.connect_signals({
"destroy_passed_object" : self.close,
"on_help_clicked" : self.on_help_clicked,
"on_delete_event" : self.close,
})
window = topDialog.toplevel
title = topDialog.get_object("title")
self.set_window(window, title, self.title)
# start the progress indicator
self.progress = ProgressMeter(self.title,_('Starting'),
parent=self.window)
self.progress.set_pass(_('Looking for possible loop for each person'),
self.db.get_number_of_people())
self.model = Gtk.ListStore(
GObject.TYPE_STRING, # 0==father id
GObject.TYPE_STRING, # 1==father
GObject.TYPE_STRING, # 2==son id
GObject.TYPE_STRING, # 3==son
GObject.TYPE_STRING) # 4==family gid
self.treeView = topDialog.get_object("treeview")
self.treeView.set_model(self.model)
col1 = Gtk.TreeViewColumn(_('Gramps ID'), Gtk.CellRendererText(), text=0)
col2 = Gtk.TreeViewColumn(_('Ancestor'), Gtk.CellRendererText(), text=1)
col3 = Gtk.TreeViewColumn(_('Gramps ID'), Gtk.CellRendererText(), text=2)
col4 = Gtk.TreeViewColumn(_('Descendant'), Gtk.CellRendererText(), text=3)
col5 = Gtk.TreeViewColumn(_('Family ID'), Gtk.CellRendererText(), text=4)
col1.set_resizable(True)
col2.set_resizable(True)
col3.set_resizable(True)
col4.set_resizable(True)
col5.set_resizable(True)
col1.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
col2.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
col3.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
col4.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
col5.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
col1.set_sort_column_id(0)
col2.set_sort_column_id(1)
col3.set_sort_column_id(2)
col4.set_sort_column_id(3)
col5.set_sort_column_id(4)
self.treeView.append_column(col1)
self.treeView.append_column(col2)
self.treeView.append_column(col3)
self.treeView.append_column(col4)
self.treeView.append_column(col5)
self.treeSelection = self.treeView.get_selection()
self.treeView.connect('row-activated', self.rowActivated)
people = self.db.get_person_handles()
count = 0
for person_handle in people:
person = self.db.get_person_from_handle(person_handle)
count += 1
self.current = person
self.parent = None
self.descendants(person_handle, set())
self.progress.set_header("%d/%d" % (count, len(people)))
self.progress.step()
# close the progress bar
self.progress.close()
self.show()
def descendants(self, person_handle, new_list):
person = self.db.get_person_from_handle(person_handle)
pset = set()
for item in new_list:
pset.add(item)
if person.handle in pset:
# We found one loop
father_id = self.current.get_gramps_id()
father = _nd.display(self.current)
son_id = self.parent.get_gramps_id()
son = _nd.display(self.parent)
value = (father_id, father, son_id, son, self.curr_fam)
found = False
for pth in range(len(self.model)):
path = Gtk.TreePath(pth)
treeiter = self.model.get_iter(path)
find = (self.model.get_value(treeiter, 0),
self.model.get_value(treeiter, 1),
self.model.get_value(treeiter, 2),
self.model.get_value(treeiter, 3),
self.model.get_value(treeiter, 4))
if find == value:
found = True
if not found:
self.model.append(value)
return
pset.add(person.handle)
for family_handle in person.get_family_handle_list():
family = self.db.get_family_from_handle(family_handle)
self.curr_fam = family.get_gramps_id()
if not family:
# can happen with LivingProxyDb(PrivateProxyDb(db))
continue
for child_ref in family.get_child_ref_list():
child_handle = child_ref.ref
self.parent = person
self.descendants(child_handle, pset)
def rowActivated(self, treeView, path, column) :
# first we need to check that the row corresponds to a person
iter = self.model.get_iter(path)
From_id = self.model.get_value(iter, 0)
To_id = self.model.get_value(iter, 2)
Fam_id = self.model.get_value(iter, 4)
fam = self.dbstate.db.get_family_from_gramps_id(Fam_id)
if fam:
try:
EditFamily(self.dbstate, self.uistate, [], fam)
except WindowActiveError:
pass
return True
return False
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
def close(self, *obj):
ManagedWindow.close(self,*obj)
#------------------------------------------------------------------------
#
# FindLoopOptions
#
#------------------------------------------------------------------------
class FindLoopOptions(tool.ToolOptions):
"""
Defines options and provides handling interface.
"""
def __init__(self, name, person_id=None):
""" Initialize the options class """
tool.ToolOptions.__init__(self, name, person_id)

View File

@ -474,3 +474,25 @@ optionclass = 'MergeCitationsOptions',
tool_modes = [TOOL_MODE_GUI] tool_modes = [TOOL_MODE_GUI]
) )
#------------------------------------------------------------------------
#
# Find database Loop
#
#------------------------------------------------------------------------
register(TOOL,
id = 'loop',
name = _("Find database loop"),
description = _("Searches the entire database, looking for "
"a possible loop."),
version = '1.0',
gramps_target_version = MODULE_VERSION,
status = STABLE,
fname = 'findloop.py',
authors = ["Serge Noiraud"],
authors_email = ["serge.noiraud@free.fr"],
category = TOOL_UTILS,
toolclass = 'FindLoop',
optionclass = 'FindLoopOptions',
tool_modes = [TOOL_MODE_GUI]
)