Updated library recursive routines. Not used (yet)

This commit is contained in:
Craig J. Anderson 2015-04-07 10:04:17 -04:00
parent ef965caf4a
commit 1d1a4de566

View File

@ -1,7 +1,7 @@
# #
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2013 Craig Anderson # Copyright (C) 2015 Craig Anderson
# #
# 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
@ -22,6 +22,86 @@
""" """
from gramps.gen.plug.report import utils as ReportUtils from gramps.gen.plug.report import utils as ReportUtils
from gramps.gen.lib import ChildRefType
#------------------------------------------------------------------------
#
# Livrecurse base objects only
#
#------------------------------------------------------------------------
class _PersonSeen(object):
""" librecurse base boject only
Keep track of people that have been seen so we can call the correct
virtual method.
"""
def __init__(self):
self.people_seen = set()
def add_person(self, level, person_handle, family_handle):
""" a person is seen for the first time """
pass
def add_person_again(self, level, person_handle, family_handle):
""" a person is seen again """
pass
def _add_person(self, level, person_handle, family_handle):
""" Which virtual method to call?
"""
if person_handle is not None and person_handle in self.people_seen:
self.add_person_again(level, person_handle, family_handle)
else:
self.add_person(level, person_handle, family_handle)
if person_handle is not None:
self.people_seen.add(person_handle)
class _FamilySeen(object):
""" librecurse base boject only
Keep track of the famalies that have been seen so we can call the correct
virtual method.
"""
def __init__(self):
self.families_seen = set()
def add_marriage(self, level, person_handle, family_handle):
""" Makes a marriage """
pass
def add_marriage_again(self, level, person_handle, family_handle):
""" Makes a marriage """
pass
def _add_marriage(self, level, person_handle, family_handle):
""" Makes a marriage """
if family_handle in self.families_seen:
self.add_marriage_again(level, person_handle, family_handle)
else:
self.add_marriage(level, person_handle, family_handle)
self.families_seen.add(family_handle)
class _StopRecurse(object):
""" A simple class to break out the
. stop_recursion
. can_recurse
. continue_recursion
methods
"""
def __init__(self):
# The default value. Lets recurse.
self.__stop_recursion = False
def stop_recursion(self):
""" Stop Recursion at theis person/family """
self.__stop_recursion = True
def continue_recursion(self):
""" Used to allow recursion again """
self.__stop_recursion = False
def can_recurse(self):
""" Has the upper class told up to stop or can we continue? """
return self.__stop_recursion == False
#------------------------------------------------------------------------ #------------------------------------------------------------------------
@ -29,15 +109,28 @@ from gramps.gen.plug.report import utils as ReportUtils
# Class DescendPerson # Class DescendPerson
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
class DescendPerson(object): class DescendPerson(_StopRecurse, _PersonSeen, _FamilySeen):
""" Recursive (down) base class """ Recursive (down) base class
The following methods need to be sub-classed as needed: The following methods need to be sub-classed as needed:
. add_person . add_person
. add_person_again (called when a person is seen a second more more times) . add_person_again (called when a person is seen a second or more times)
if you don't want to see marriages don't subclass the following two if you don't want to see marriages don't subclass the following two
. add_marriage . add_marriage
. add_marriage_again (when a marriage is seen a second more more times) . add_marriage_again (when a marriage is seen a second or more times)
returns:
. add_person, add_person_again, add_marriage, add_marriage_again return
. . index -> a tuple in the form
. . . generation, which generational level >= 1
. . . level
. . . . 0 = A direct child
. . . . 1 = spouse of above (0)
. . . . 2 = spouse of 1
. . . . 3 = spouse of 2
. . . . 4 etc
. . person_handle
. . family_handle
Public variables: Public variables:
. families_seen a set of all famalies seen. . families_seen a set of all famalies seen.
@ -50,7 +143,7 @@ class DescendPerson(object):
Methods (tools): Methods (tools):
is_direct_descendant - is this person a direct descendant is_direct_descendant - is this person a direct descendant
. in the example 'kid 1 of mom and other spouse' is NOT . in the example 'kid 1 of mom and other spouse' is NOT
stop_descending - tells the recursion to stop going down stop_recursion - tells the recursion to stop going down
. mostly used in add_person_again and add_marriage_again . mostly used in add_person_again and add_marriage_again
has_children - checks to see if the person: has_children - checks to see if the person:
. is NOT already seen and has hildren. . is NOT already seen and has hildren.
@ -61,12 +154,14 @@ class DescendPerson(object):
. . level in (Generagion, Spousal level) tuple . . level in (Generagion, Spousal level) tuple
. . person_handle of the person . . person_handle of the person
. . family_handle of the family . . family_handle of the family
add_person - The recursion found a new person in the tree
add_person_again - found a person again
. a prolific person or recursion
add_marriage add_marriage
add_marriage_again add_marriage_again
Virtual methods in PersonSeen
. add_person - The recursion found a new person in the tree
. add_person_again - found a person again
. . a prolific person or recursion
Methods (recursive) Methods (recursive)
recurse - The main recursive routine. needs: recurse - The main recursive routine. needs:
. person_handle . person_handle
@ -78,7 +173,11 @@ class DescendPerson(object):
. mom (the spouse) is still shown even if s_level == 0 . mom (the spouse) is still shown even if s_level == 0
. . father will have a level of (g_level,0), mother (g_level, 1) . . father will have a level of (g_level,0), mother (g_level, 1)
""" """
def __init__(self, dbase, maxgen, maxspouse): def __init__(self, dbase, maxgen, maxspouse=0):
_PersonSeen.__init__(self)
_FamilySeen.__init__(self)
_StopRecurse.__init__(self)
""" initalized with the """ initalized with the
. database . database
. maxgen is the max generations (down) of people to return . maxgen is the max generations (down) of people to return
@ -99,10 +198,10 @@ class DescendPerson(object):
self.database = dbase self.database = dbase
self.families_seen = set() assert maxgen > 0
self.people_seen = set()
self.max_generations = maxgen self.max_generations = maxgen
assert maxspouse >= 0
self.max_spouses = maxspouse self.max_spouses = maxspouse
#can we bold direct descendants? #can we bold direct descendants?
@ -112,14 +211,15 @@ class DescendPerson(object):
#2 - Bold all direct descendants #2 - Bold all direct descendants
self.__bold_now = 1 self.__bold_now = 1
self.__this_slevel = -1 self.__this_slevel = -1
self.__stop_descending = False
def is_direct_descendant(self): def is_direct_descendant(self):
""" Is this person a direct descendant?
. Can we bold this perosn and
. are they a direct child of the father/mother
. . not a spouse
"""
return self.__bold_now != 0 and self.__this_slevel == 0 return self.__bold_now != 0 and self.__this_slevel == 0
def stop_descending(self):
self.__stop_descending = True
def has_children(self, person_handle): def has_children(self, person_handle):
""" """
Quickly check to see if this person has children Quickly check to see if this person has children
@ -140,37 +240,6 @@ class DescendPerson(object):
return True return True
return False return False
def add_person(self, level, person_handle, family_handle):
""" Makes a person box """
pass
def add_person_again(self, level, person_handle, family_handle):
pass
def __add_person(self, level, person_handle, family_handle):
if person_handle is not None and person_handle in self.people_seen:
self.add_person_again(level, person_handle, family_handle)
else:
self.add_person(level, person_handle, family_handle)
if person_handle is not None:
self.people_seen.add(person_handle)
def add_marriage(self, level, person_handle, family_handle):
""" Makes a marriage box """
pass
def add_marriage_again(self, level, person_handle, family_handle):
""" Makes a marriage box """
pass
def __add_marriage(self, level, person_handle, family_handle):
""" Makes a marriage box """
if family_handle in self.families_seen:
self.add_marriage_again(level, person_handle, family_handle)
else:
self.add_marriage(level, person_handle, family_handle)
self.families_seen.add(family_handle)
def recurse(self, person_handle, g_level, s_level): def recurse(self, person_handle, g_level, s_level):
"""traverse the descendants recursively """traverse the descendants recursively
until either the end of a line is found, until either the end of a line is found,
@ -181,7 +250,7 @@ class DescendPerson(object):
if not person_handle: if not person_handle:
return return
if g_level > self.max_generations: if g_level > self.max_generations:
return return # one generation too many
if s_level > 0 and s_level == self.max_spouses: if s_level > 0 and s_level == self.max_spouses:
return return
#if person_handle in self.people_seen: return #if person_handle in self.people_seen: return
@ -191,13 +260,13 @@ class DescendPerson(object):
if s_level == 0: if s_level == 0:
val = family_handles[0] if family_handles else None val = family_handles[0] if family_handles else None
self.__this_slevel = s_level self.__this_slevel = s_level
self.__add_person((g_level, s_level), person_handle, val) self._add_person((g_level, s_level), person_handle, val)
if self.__bold_now == 1: if self.__bold_now == 1:
self.__bold_now = 0 self.__bold_now = 0
if self.__stop_descending: if not self.can_recurse():
self.__stop_descending = False self.continue_recursion()
return return
if s_level == 1: if s_level == 1:
@ -206,32 +275,32 @@ class DescendPerson(object):
for family_handle in family_handles: for family_handle in family_handles:
#Marriage box if the option is there. #Marriage box if the option is there.
self.__add_marriage((g_level, s_level + 1), self._add_marriage((g_level, s_level + 1),
person_handle, family_handle) person_handle, family_handle)
if self.__stop_descending: if not self.can_recurse():
self.__stop_descending = False self.continue_recursion()
continue return
family = self.database.get_family_from_handle(family_handle) family = self.database.get_family_from_handle(family_handle)
spouse_handle = ReportUtils.find_spouse(person, family) spouse_handle = ReportUtils.find_spouse(person, family)
if self.max_spouses > s_level: if self.max_spouses > s_level:
self.__this_slevel = s_level + 1 self.__this_slevel = s_level + 1
self.__add_person((g_level, s_level + 1), self._add_person((g_level, s_level + 1),
spouse_handle, family_handle) spouse_handle, family_handle)
if self.__stop_descending: if not self.can_recurse:
self.__stop_descending = False self.continue_recursion()
continue return
mykids = [kid.ref for kid in family.get_child_ref_list()] mykids = [kid.ref for kid in family.get_child_ref_list()]
if not self.__stop_descending: if self.can_recurse():
for child_ref in mykids: for child_ref in mykids:
self.recurse(child_ref, g_level + 1, 0) self.recurse(child_ref, g_level + 1, 0)
else: else:
self.__stop_descending = False self.continue_recursion()
if self.max_spouses > s_level: if self.max_spouses > s_level:
#spouse_handle = ReportUtils.find_spouse(person,family) #spouse_handle = ReportUtils.find_spouse(person,family)
@ -258,17 +327,17 @@ class DescendPerson(object):
self.__bold_now = 2 self.__bold_now = 2
self.__this_slevel = 0 self.__this_slevel = 0
#if father_h: #if father_h:
father_b = self.__add_person((g_level, 0), father_h, family_handle) father_b = self._add_person((g_level, 0), father_h, family_handle)
#else: #else:
# #TODO - should send family_h instead of None? # #TODO - should send family_h instead of None?
# father_b = self.__add_person((g_level, 0), None, family_h) # father_b = self._add_person((g_level, 0), None, family_h)
#self.people_seen.add(father_h) #self.people_seen.add(father_h)
family_b = self.__add_marriage((g_level, 1), father_h, family_handle) family_b = self._add_marriage((g_level, 1), father_h, family_handle)
self.__bold_now = 0 self.__bold_now = 0
self.__this_slevel = 1 self.__this_slevel = 1
mother_b = self.__add_person((g_level, 1), mother_h, family_handle) mother_b = self._add_person((g_level, 1), mother_h, family_handle)
self.__bold_now = 2 self.__bold_now = 2
for child_ref in family.get_child_ref_list(): for child_ref in family.get_child_ref_list():
@ -299,6 +368,175 @@ class DescendPerson(object):
self.__bold_now = 1 self.__bold_now = 1
self.recurse(person_handle, g_level, 0) self.recurse(person_handle, g_level, 0)
#------------------------------------------------------------------------
#
# Class AscendPerson
#
#------------------------------------------------------------------------
class AscendPerson(_StopRecurse, _PersonSeen):
""" Recursive (up) base class
The following methods need to be sub-classed as needed:
. add_person
. add_person_again (called when a person is seen a second or more times)
if you don't want to see marriages don't subclass the following
. add_marriage
. . index (below) will be the same as the father
returns:
. add_person, add_person_again, add_marriage all return
. . index -> a tuple in the form
. . . generation, which generational level >= 1
. . . . Center person is 1
. . . . Father/Mother is the generational level of the child + 1
. . . index
. . . . The center person is 1
. . . . A father is the index of the child * 2
. . . . A mother is the (index of the child * 2) + 1
. . person_handle (May be None)
. . family_handle (May be None)
Public variables:
. people_seen, a set of all people seen.
. . useful for knowing if a recursion (kid marring a grandparent)
. . has happened.
These can be edited if needed
. people_seen, a set of all people seen.
. . appending can be useful for excluding parts of the tree
"""
def __init__(self, dbase, maxgen, maxfill=0):
_PersonSeen.__init__(self)
_StopRecurse.__init__(self)
""" initalized with the
. database
. maxgen is the max generations (up) of people to return
. . maxgen >= 1. 1 will only be the person.
. maxfill is the max generations of blank (null) people to return
. . maxfil >= 0. 0 (default) is no empty generations
"""
self.database = dbase
assert maxgen > 0
self.max_generations = maxgen
assert maxfill >= 0
self.fill_out = maxfill
def add_marriage(self, index, indi_handle, fams_handle):
""" Makes a marriage box and add that person into the Canvas. """
# We are not using add_marriage only and not add_marriage_again
# because the father will do any _again stuff if needed.
pass
def __fill(self, generation, index, mx_fill):
"""
A skeleton of __iterate as person_handle == family_handle == None
"""
if generation > self.max_generations or mx_fill == 0:
# Gone too far.
return
self.add_person((generation, index), None, None)
if not self.can_recurse():
self.continue_recursion()
return
# Recursively call the function. It is okay if the handle is None,
# since routine handles a handle of None
self.__fill(index*2, generation+1, mx_fill-1)
if mx_fill > 1: # marriage of parents
self.add_marriage((generation+1, index*2), None, None)
if not self.can_recurse():
self.continue_recursion()
return
self.__fill((index*2)+1, generation+1, mx_fill-1)
def __iterate(self, person_handle, generation, index):
"""
Recursive function to walk back all parents of the current person.
When max_generations are hit, we stop the traversal.
Code pilfered from gramps/plugins/textreports/ancestorreport.py
"""
# check for end of the current recursion level. This happens
# if the person handle is None, or if the max_generations is hit
if generation > self.max_generations: # too many generations
return
if person_handle is None: # Ran out of people
return self.__fill(generation, index, self.fill_out)
# retrieve the Person instance from the database from the
# passed person_handle and find the parents from the list.
# Since this report is for natural parents (birth parents),
# we have to handle that parents may not
person = self.database.get_person_from_handle(person_handle)
father_handle = None
mother_handle = None
fam_handle = None
for family_handle in person.get_parent_family_handle_list():
family = self.database.get_family_from_handle(family_handle)
# filter the child_ref_list to find the reference that matches
# the passed person. There should be exactly one, but there is
# nothing that prevents the same child in the list multiple times.
ref = [c for c in family.get_child_ref_list()
if c.get_reference_handle() == person_handle]
if ref:
# If the father_handle is not defined and the relationship is
# BIRTH, then we have found the birth father. Same applies to
# the birth mother. If for some reason, the we have multiple
# people defined as the birth parents, we will select based on
# priority in the list
if not father_handle and \
ref[0].get_father_relation() == ChildRefType.BIRTH:
father_handle = family.get_father_handle()
if fam_handle is None: # Take the first seen only
fam_handle = family_handle
if not mother_handle and \
ref[0].get_mother_relation() == ChildRefType.BIRTH:
mother_handle = family.get_mother_handle()
if fam_handle is None:
fam_handle = family_handle
if father_handle and mother_handle:
break
# we have a valid person, add him/her
self._add_person((generation, index), person_handle, fam_handle)
# has the user canceled recursion?
if not self.can_recurse():
self.continue_recursion()
return
# Recursively call the function. It is okay if the handle is None,
self.__iterate(father_handle, generation+1, index*2) #recurse on dad
if generation < self.max_generations or self.fill_out > 0:
if father_handle is not None: # Stil winin max_generations
self.add_marriage((generation+1, index*2), father_handle, fam_handle)
else:
self.add_marriage((generation+1, index*2), mother_handle, fam_handle)
if not self.can_recurse():
self.continue_recursion()
return
self.__iterate(mother_handle, generation+1, (index*2)+1) #recurse mom
def recurse(self, person_handle):
"""
A simple header to make sure we pass in the correct information
"""
return self.__iterate(person_handle, 1, 1)
#------------ #------------
# Jer 29:11: "For I know the plans I have for you," declares the LORD, # Jer 29:11: "For I know the plans I have for you," declares the LORD,
# "plans to prosper you and not to harm you, plans to give you hope # "plans to prosper you and not to harm you, plans to give you hope