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) 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
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -24,6 +25,7 @@
|
||||
# Standard Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from collections import deque
|
||||
from ....const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
@ -95,31 +97,52 @@ def get_person_family_people(db, person, person_handle):
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
while todo:
|
||||
handle = todo.popleft()
|
||||
|
||||
handle = person.get_handle()
|
||||
if handle in seen:
|
||||
return []
|
||||
seen.append(handle)
|
||||
if user:
|
||||
user.step_progress()
|
||||
|
||||
return_paths = []
|
||||
person_path = path + [handle]
|
||||
|
||||
if handle in target_people:
|
||||
return_paths += [person_path]
|
||||
if handle in target_people: # if we found a target
|
||||
prev_hndl = 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)
|
||||
for family_person in family_people:
|
||||
pers = db.get_person_from_handle(family_person)
|
||||
return_paths += find_deep_relations(db, user, pers, person_path, seen, target_people)
|
||||
person = db.get_person_from_handle(handle)
|
||||
if person is None:
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
@ -147,12 +170,12 @@ class DeepRelationshipPathBetween(Rule):
|
||||
user.begin_progress(_('Finding relationship paths'),
|
||||
_('Evaluating 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:
|
||||
user.end_progress()
|
||||
|
||||
self.__matches = set()
|
||||
list(map(self.__matches.update, paths))
|
||||
# self.__matches = set()
|
||||
# list(map(self.__matches.update, paths))
|
||||
|
||||
def reset(self):
|
||||
self.__matches = set()
|
||||
|
Loading…
Reference in New Issue
Block a user