Fix Deep Relationship filter to avoid recursion crash
Fixes #10279 This also changes filter to use shortest paths.
This commit is contained in:
parent
85014bd92c
commit
e7bc887f60
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2009 Robert Ham <rah@bash.sh>
|
# Copyright (C) 2009 Robert Ham <rah@bash.sh>
|
||||||
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
|
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
|
||||||
|
# Copyright (C) 2017 Paul Culley <paulr2787@gmail.com>
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -24,6 +25,7 @@
|
|||||||
# Standard Python modules
|
# Standard Python modules
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
from collections import deque
|
||||||
from ....const import GRAMPS_LOCALE as glocale
|
from ....const import GRAMPS_LOCALE as glocale
|
||||||
_ = glocale.translation.gettext
|
_ = glocale.translation.gettext
|
||||||
|
|
||||||
@ -95,31 +97,52 @@ def get_person_family_people(db, person, person_handle):
|
|||||||
|
|
||||||
return people
|
return people
|
||||||
|
|
||||||
def find_deep_relations(db, user, person, path, seen, target_people):
|
|
||||||
if len(target_people) < 1:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
def find_deep_relations(db, user, person, target_people):
|
||||||
|
""" This explores all possible paths between a person and one or more
|
||||||
|
targets. The algorithm processes paths in a breadth first wave, one
|
||||||
|
remove at a time. The first path that reaches a target causes the target
|
||||||
|
to be marked complete and the path to be stored in the return_paths set.
|
||||||
|
By processing in wave, the return path should be a shortest path.
|
||||||
|
The function stores to do data and intermediate results in an ordered dict,
|
||||||
|
rather than using a recursive algorithm becasue some trees have been found
|
||||||
|
that exceed the standard python recursive depth. """
|
||||||
|
return_paths = set() # all people in paths between targets and person
|
||||||
if person is None:
|
if person is None:
|
||||||
return []
|
return return_paths
|
||||||
|
todo = deque([person.handle]) # list of work to do, handles, add to right,
|
||||||
|
# pop from left
|
||||||
|
done = {} # The key records handles already examined,
|
||||||
|
# the value is a handle of the previous person in the path, or None at
|
||||||
|
# head of path. This forms a linked list of handles along the path.
|
||||||
|
done[person.handle] = None
|
||||||
|
|
||||||
handle = person.get_handle()
|
while todo:
|
||||||
if handle in seen:
|
handle = todo.popleft()
|
||||||
return []
|
|
||||||
seen.append(handle)
|
|
||||||
if user:
|
|
||||||
user.step_progress()
|
|
||||||
|
|
||||||
return_paths = []
|
if user:
|
||||||
person_path = path + [handle]
|
user.step_progress()
|
||||||
|
|
||||||
if handle in target_people:
|
if handle in target_people: # if we found a target
|
||||||
return_paths += [person_path]
|
prev_hndl = handle
|
||||||
target_people.remove(handle)
|
# Go through linked list and save the handles in return_paths
|
||||||
|
while prev_hndl:
|
||||||
|
return_paths.add(prev_hndl)
|
||||||
|
prev_hndl = done[prev_hndl]
|
||||||
|
target_people.remove(handle)
|
||||||
|
if not target_people: # Quit searching if all targets found
|
||||||
|
break
|
||||||
|
|
||||||
family_people = get_person_family_people(db, person, handle)
|
person = db.get_person_from_handle(handle)
|
||||||
for family_person in family_people:
|
if person is None:
|
||||||
pers = db.get_person_from_handle(family_person)
|
continue
|
||||||
return_paths += find_deep_relations(db, user, pers, person_path, seen, target_people)
|
|
||||||
|
people = get_person_family_people(db, person, handle)
|
||||||
|
for p_hndl in people:
|
||||||
|
if p_hndl in done: # check if we have already been here
|
||||||
|
continue # and ignore if we have
|
||||||
|
todo.append(p_hndl) # Add to the todo list
|
||||||
|
done[p_hndl] = handle # Add to the (almost) done list
|
||||||
|
|
||||||
return return_paths
|
return return_paths
|
||||||
|
|
||||||
@ -147,12 +170,12 @@ class DeepRelationshipPathBetween(Rule):
|
|||||||
user.begin_progress(_('Finding relationship paths'),
|
user.begin_progress(_('Finding relationship paths'),
|
||||||
_('Evaluating people'),
|
_('Evaluating people'),
|
||||||
db.get_number_of_people())
|
db.get_number_of_people())
|
||||||
paths = find_deep_relations(db, user, root_person, [], [], target_people)
|
self.__matches = find_deep_relations(db, user, root_person, target_people)
|
||||||
if user:
|
if user:
|
||||||
user.end_progress()
|
user.end_progress()
|
||||||
|
|
||||||
self.__matches = set()
|
# self.__matches = set()
|
||||||
list(map(self.__matches.update, paths))
|
# list(map(self.__matches.update, paths))
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.__matches = set()
|
self.__matches = set()
|
||||||
|
Loading…
Reference in New Issue
Block a user