Version 1 of the recursive descendant routine. More recursive classes to come.

ander882@hotmail.com



svn: r23412
This commit is contained in:
Craig J. Anderson 2013-10-25 20:33:55 +00:00
parent c29af0860b
commit 824a9a5134
2 changed files with 338 additions and 14 deletions

View File

@ -26,7 +26,7 @@
# libcairo
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libcairodoc',
name = "Cairodoc lib",
description = _("Provides a library for using Cairo to "
@ -45,7 +45,7 @@ authors_email = ["http://gramps-project.org"],
# libgedcom
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libgedcom',
name = "GEDCOM library",
description = _("Provides GEDCOM processing functionality"),
@ -57,12 +57,29 @@ authors = ["The Gramps project"],
authors_email = ["http://gramps-project.org"],
)
#------------------------------------------------------------------------
#
# librecurse
#
#------------------------------------------------------------------------
register(GENERAL,
id='librecurse',
name="Recursive lib",
description= _("Provides recursive routines for reports"),
version='1.0',
gramps_target_version='4.1',
status=STABLE,
fname='librecurse.py',
authors=["The Gramps project"],
authors_email=["http://gramps-project.org"],
)
#------------------------------------------------------------------------
#
# libgrampsxml
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libgrampsxml',
name = "Grampsxml lib",
description = _("Provides common functionality for Gramps XML "
@ -80,7 +97,7 @@ authors_email = ["http://gramps-project.org"],
# libholiday
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libholiday',
name = "holiday lib",
description = _("Provides holiday information for different countries.") ,
@ -98,7 +115,7 @@ authors_email = ["http://gramps-project.org"],
# llibhtmlbackend
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libhtmlbackend',
name = "htmlbackend lib",
description = _("Manages a HTML file implementing DocBackend.") ,
@ -116,7 +133,7 @@ authors_email = ["http://gramps-project.org"],
# libhtmlconst
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libhtmlconst',
name = "htmlconst lib",
description = _("Common constants for html files.") ,
@ -134,7 +151,7 @@ authors_email = ["http://gramps-project.org"],
# libhtml
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libhtml',
name = "html lib",
description = _("Manages an HTML DOM tree.") ,
@ -152,7 +169,7 @@ authors_email = ["gerald.britton@gmail.com"],
# libmapservice
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libmapservice',
name = "mapservice lib",
description = _("Provides base functionality for map services.") ,
@ -169,7 +186,7 @@ authors_email = ["http://gramps-project.org"],
# libnarrate
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libnarrate',
name = "narration lib",
description = _("Provides Textual Narration.") ,
@ -186,7 +203,7 @@ authors_email = ["brian@gramps-project.org"],
# libodfbackend
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libodfbackend',
name = "odfbackend lib",
description = _("Manages an ODF file implementing DocBackend.") ,
@ -203,7 +220,7 @@ authors_email = ["http://gramps-project.org"],
# libpersonview
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libpersonview',
name = "person list lib",
description = _("Provides the Base needed for the List People views.") ,
@ -220,7 +237,7 @@ authors_email = ["http://gramps-project.org"],
# libplaceview
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libplaceview',
name = "place list lib",
description = _("Provides the Base needed for the List Place views.") ,
@ -237,7 +254,7 @@ authors_email = ["http://gramps-project.org"],
# libsubstkeyword
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libsubstkeyword',
name = "Substitution Values",
description = _("Provides variable substitution on display lines.") ,
@ -253,7 +270,7 @@ authors_email = ["http://gramps-project.org"],
# libtreebase
#
#------------------------------------------------------------------------
register(GENERAL,
register(GENERAL,
id = 'libtreebase',
name = "Graphical report lib",
description = _("Provides the base needed for the ancestor and "

View File

@ -0,0 +1,307 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Craig Anderson
#
# 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:
""" Recursive base classes for reports
"""
from gramps.gen.plug.report import utils as ReportUtils
#------------------------------------------------------------------------
#
# Class DescendPerson
#
#------------------------------------------------------------------------
class DescendPerson(object):
""" Recursive (down) 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 more more times)
if you don't want to see marriages don't subclass the following two
. add_marriage
. add_marriage_again (when a marriage is seen a second more more times)
Public variables:
. families_seen a set of all famalies seen.
. 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
. appending can be useful for excluding parts of the tree
Methods (tools):
is_direct_descendant - is this person a direct descendant
. in the example 'kid 1 of mom and other spouse' is NOT
stop_descending - tells the recursion to stop going down
. mostly used in add_person_again and add_marriage_again
has_children - checks to see if the person:
. is NOT already seen and has hildren.
Methods (informative)
. These are the methods that need to be subclassed
. all methods are given the:
. . level in (Generagion, Spousal level) tuple
. . person_handle of the person
. . 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_again
Methods (recursive)
recurse - The main recursive routine. needs:
. person_handle
. g_level - Generation level of this person
. . if max_gen is 2 and g_level is 1, only this generation
. . will be displayed.
. s_level - spousal level - most always 0
recurse_parents - Thes same as above except:
. mom (the spouse) is still shown even if s_level == 0
. . father will have a level of (g_level,0), mother (g_level, 1)
"""
def __init__(self, dbase, maxgen, maxspouse):
""" initalized with the
. database
. maxgen is the max generations (down) of people to return
. maxspouse is the level of spouses to recruse through
. . 0 = no spouses, 1 = spouses of a direct descendant
. . 2 = spouses of 1, 3 = spouses of 2, etc. See example below
"""
# example: maxgen = 2, maxspouses = 2
# (1,0) father
# (1,1) Mother
# (1,2) Mothers other spouse
# (2,0) kid 1 of mom and other spouse
# (2,0) Kid 1 of father and mother
# (1,1) fathers other spouse
# (2,0) Kid 1 of father and fathers other spouse
# (2,1) Spouse of Kid 1 of father and fathers other spouse
self.database = dbase
self.families_seen = set()
self.people_seen = set()
self.max_generations = maxgen
self.max_spouses = maxspouse
#can we bold direct descendants?
#bold_now will have only three values
#0 - no bolding
#1 - Only bold the first person
#2 - Bold all direct descendants
self.__bold_now = 1
self.__this_slevel = -1
self.__stop_descending = False
def is_direct_descendant(self):
return self.__bold_now != 0 and self.__this_slevel == 0
def stop_descending(self):
self.__stop_descending = True
def has_children(self, person_handle):
"""
Quickly check to see if this person has children
still we want to respect the people_seen list
"""
if not person_handle or person_handle in self.people_seen:
return False
person = self.database.get_person_from_handle(person_handle)
for family_handle in person.get_family_handle_list():
if family_handle not in self.families_seen:
family = self.database.get_family_from_handle(family_handle)
if family.get_child_ref_list():
return True
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):
"""traverse the descendants recursively
until either the end of a line is found,
or until we reach the maximum number of generations
or we reach the max number of spouses
that we want to deal with"""
if not person_handle:
return
if g_level > self.max_generations:
return
if s_level > 0 and s_level == self.max_spouses:
return
#if person_handle in self.people_seen: return
person = self.database.get_person_from_handle(person_handle)
family_handles = person.get_family_handle_list()
if s_level == 0:
val = family_handles[0] if family_handles else None
self.__this_slevel = s_level
self.__add_person((g_level, s_level), person_handle, val)
if self.__bold_now == 1:
self.__bold_now = 0
if self.__stop_descending:
self.__stop_descending = False
return
if s_level == 1:
tmp_bold = self.__bold_now
self.__bold_now = 0
for family_handle in family_handles:
#Marriage box if the option is there.
self.__add_marriage((g_level, s_level + 1),
person_handle, family_handle)
if self.__stop_descending:
self.__stop_descending = False
continue
family = self.database.get_family_from_handle(family_handle)
spouse_handle = ReportUtils.find_spouse(person, family)
if self.max_spouses > s_level:
self.__this_slevel = s_level + 1
self.__add_person((g_level, s_level + 1),
spouse_handle, family_handle)
if self.__stop_descending:
self.__stop_descending = False
continue
mykids = [kid.ref for kid in family.get_child_ref_list()]
if not self.__stop_descending:
for child_ref in mykids:
self.recurse(child_ref, g_level + 1, 0)
else:
self.__stop_descending = False
if self.max_spouses > s_level:
#spouse_handle = ReportUtils.find_spouse(person,family)
self.recurse(spouse_handle, g_level, s_level + 1)
if s_level == 1:
self.__bold_now = tmp_bold
def recurse_parents(self, family_handle, g_level):
"""
Adds a family.
ignoring maxspouse, s_level assumed 0 and 1
father is (g_level,0) and mother is (g_level,1)
children are (g_level+1,0) and respects maxgen
"""
if family_handle is None:
return
family = self.database.get_family_from_handle(family_handle)
father_h = family.get_father_handle()
mother_h = family.get_mother_handle()
self.__bold_now = 2
self.__this_slevel = 0
#if father_h:
father_b = self.__add_person((g_level, 0), father_h, family_handle)
#else:
# #TODO - should send family_h instead of None?
# father_b = self.__add_person((g_level, 0), None, family_h)
#self.people_seen.add(father_h)
family_b = self.__add_marriage((g_level, 1), father_h, family_handle)
self.__bold_now = 0
self.__this_slevel = 1
mother_b = self.__add_person((g_level, 1), mother_h, family_handle)
self.__bold_now = 2
for child_ref in family.get_child_ref_list():
self.recurse(child_ref.ref, g_level + 1, 0)
self.__bold_now = 0
return (father_b, family_b, mother_b)
def recurse_if(self, person_handle, g_level):
"""
Quickly check to see if we want to continue recursion
we still we want to respect the FamiliesSeen list
"""
person = self.database.get_person_from_handle(person_handle)
show = False
myfams = person.get_family_handle_list()
if len(myfams) > 1: # and self.max_spouses > 0
show = True
#if self.max_spouses == 0 and not self.has_children(person_handle):
# self.people_seen.add(person_handle)
# show = False
if show:
self.__bold_now = 1
self.recurse(person_handle, g_level, 0)
#------------
# 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
# and a future."