HasCommonANcestorWith patch from Alexandre Duret-Lutz
svn: r1159
This commit is contained in:
parent
5df7da6100
commit
b537712152
@ -54,6 +54,7 @@ from RelLib import *
|
||||
import Date
|
||||
from intl import gettext
|
||||
_ = gettext
|
||||
from Utils import for_each_ancestor
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -98,7 +99,7 @@ class Rule:
|
||||
def check(self):
|
||||
return len(self.list) == len(self.labels)
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return 1
|
||||
|
||||
def display_values(self):
|
||||
@ -121,7 +122,7 @@ class Everyone(Rule):
|
||||
def name(self):
|
||||
return 'Everyone'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return 1
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -137,7 +138,7 @@ class HasIdOf(Rule):
|
||||
def name(self):
|
||||
return 'Has the Id'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return p.getId() == self.list[0]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -153,7 +154,7 @@ class IsFemale(Rule):
|
||||
def name(self):
|
||||
return 'Is a female'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return p.getGender() == Person.female
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -170,7 +171,7 @@ class IsDescendantOf(Rule):
|
||||
def name(self):
|
||||
return 'Is a descendant of'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return self.search(p)
|
||||
|
||||
def search(self,p):
|
||||
@ -197,7 +198,7 @@ class IsDescendantFamilyOf(Rule):
|
||||
def name(self):
|
||||
return "Is a descendant family member of"
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return self.search(p,1)
|
||||
|
||||
def search(self,p,val):
|
||||
@ -233,7 +234,7 @@ class IsAncestorOf(Rule):
|
||||
def name(self):
|
||||
return 'Is an ancestor of'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return self.search(p)
|
||||
|
||||
def search(self,p):
|
||||
@ -247,6 +248,44 @@ class IsAncestorOf(Rule):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# HasCommonAncestorWith
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class HasCommonAncestorWith(Rule):
|
||||
"""Rule that checks for a person that has a common ancestor with a specified person"""
|
||||
|
||||
labels = [ _('ID') ]
|
||||
|
||||
def name(self):
|
||||
return 'Has a common ancestor with'
|
||||
|
||||
def __init__(self,list):
|
||||
Rule.__init__(self,list)
|
||||
# Keys in `ancestor_cache' are ancestors of list[0].
|
||||
# We delay the computation of ancestor_cache until the
|
||||
# first use, because it's not uncommon to instantiate
|
||||
# this class and not use it.
|
||||
self.ancestor_cache = {}
|
||||
|
||||
def init_ancestor_cache(self,db):
|
||||
# list[0] is an Id, but we need to pass a Person to for_each_ancestor.
|
||||
p = db.getPerson(self.list[0])
|
||||
if p:
|
||||
def init(self,pid): self.ancestor_cache[pid] = 1
|
||||
for_each_ancestor([p],init,self)
|
||||
|
||||
def apply(self,db,p):
|
||||
# On the first call, we build the ancestor cache for the
|
||||
# reference person. Then, for each person to test,
|
||||
# we browse his ancestors until we found one in the cache.
|
||||
if len(self.ancestor_cache) == 0:
|
||||
self.init_ancestor_cache(db)
|
||||
return for_each_ancestor([p],
|
||||
lambda self,p: self.ancestor_cache.has_key(p),
|
||||
self);
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# IsMale
|
||||
@ -260,7 +299,7 @@ class IsMale(Rule):
|
||||
def name(self):
|
||||
return 'Is a male'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
return p.getGender() == Person.male
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -284,7 +323,7 @@ class HasEvent(Rule):
|
||||
def name(self):
|
||||
return 'Has the personal event'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
for event in p.getEventList():
|
||||
val = 1
|
||||
if self.list[0] and event.getName() != self.list[0]:
|
||||
@ -322,7 +361,7 @@ class HasFamilyEvent(Rule):
|
||||
def name(self):
|
||||
return 'Has the family event'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
for f in p.getFamilyList():
|
||||
for event in f.getEventList():
|
||||
val = 1
|
||||
@ -355,7 +394,7 @@ class HasRelationship(Rule):
|
||||
def name(self):
|
||||
return 'Has the relationships'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
rel_type = 0
|
||||
cnt = 0
|
||||
num_rel = len(p.getFamilyList())
|
||||
@ -411,7 +450,7 @@ class HasBirth(Rule):
|
||||
def name(self):
|
||||
return 'Has the birth'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
event = p.getBirth()
|
||||
if len(self.list) > 2 and find(event.getDescription(),self.list[2])==-1:
|
||||
return 0
|
||||
@ -443,7 +482,7 @@ class HasDeath(Rule):
|
||||
def name(self):
|
||||
return 'Has the death'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
event = p.getDeath()
|
||||
if self.list[2] and find(event.getDescription(),self.list[2])==-1:
|
||||
return 0
|
||||
@ -467,7 +506,7 @@ class HasAttribute(Rule):
|
||||
def name(self):
|
||||
return 'Has the personal attribute'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
for event in p.getAttributes():
|
||||
if self.list[0] and event.getType() != self.list[0]:
|
||||
return 0
|
||||
@ -488,7 +527,7 @@ class HasFamilyAttribute(Rule):
|
||||
def name(self):
|
||||
return 'Has the family attribute'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
for f in p.getFamilyList():
|
||||
for event in f.getAttributes():
|
||||
val = 1
|
||||
@ -513,7 +552,7 @@ class HasNameOf(Rule):
|
||||
def name(self):
|
||||
return 'Has a name'
|
||||
|
||||
def apply(self,p):
|
||||
def apply(self,db,p):
|
||||
self.f = self.list[0]
|
||||
self.l = self.list[1]
|
||||
self.s = self.list[2]
|
||||
@ -541,7 +580,7 @@ class MatchesFilter(Rule):
|
||||
def name(self):
|
||||
return 'Matches the filter named'
|
||||
|
||||
def apply(self, p):
|
||||
def apply(self,db,p):
|
||||
for filter in SystemFilters.get_filters():
|
||||
if filter.get_name() == self.list[0]:
|
||||
return filter.check(p)
|
||||
@ -608,10 +647,10 @@ class GenericFilter:
|
||||
def get_rules(self):
|
||||
return self.flist
|
||||
|
||||
def check_or(self,p):
|
||||
def check_or(self,db,p):
|
||||
test = 0
|
||||
for rule in self.flist:
|
||||
test = test or rule.apply(p)
|
||||
test = test or rule.apply(db,p)
|
||||
if test:
|
||||
break
|
||||
if self.invert:
|
||||
@ -619,20 +658,20 @@ class GenericFilter:
|
||||
else:
|
||||
return test
|
||||
|
||||
def check_xor(self,p):
|
||||
def check_xor(self,db,p):
|
||||
test = 0
|
||||
for rule in self.flist:
|
||||
temp = rule.apply(p)
|
||||
temp = rule.apply(db,p)
|
||||
test = ((not test) and temp) or (test and (not temp))
|
||||
if self.invert:
|
||||
return not test
|
||||
else:
|
||||
return test
|
||||
|
||||
def check_one(self,p):
|
||||
def check_one(self,db,p):
|
||||
count = 0
|
||||
for rule in self.flist:
|
||||
if rule.apply(p):
|
||||
if rule.apply(db,p):
|
||||
count = count + 1
|
||||
if count > 1:
|
||||
break
|
||||
@ -641,10 +680,10 @@ class GenericFilter:
|
||||
else:
|
||||
return count == 1
|
||||
|
||||
def check_and(self,p):
|
||||
def check_and(self,db,p):
|
||||
test = 1
|
||||
for rule in self.flist:
|
||||
test = test and rule.apply(p)
|
||||
test = test and rule.apply(db,p)
|
||||
if not test:
|
||||
break
|
||||
if self.invert:
|
||||
@ -652,21 +691,24 @@ class GenericFilter:
|
||||
else:
|
||||
return test
|
||||
|
||||
def check(self,p):
|
||||
def get_check_func(self):
|
||||
try:
|
||||
m = getattr(self, 'check_' + self.logical_op)
|
||||
except AttributeError:
|
||||
m = self.check_and
|
||||
return m
|
||||
|
||||
return m(p)
|
||||
def check(self,db,p):
|
||||
return self.get_check_func()(db,p)
|
||||
|
||||
def apply(self,list):
|
||||
try:
|
||||
m = getattr(self, 'check_' + self.logical_op)
|
||||
except AttributeError:
|
||||
m = self.check_and
|
||||
def apply(self,db,list):
|
||||
m = self.get_check_func()
|
||||
res = []
|
||||
for p in list:
|
||||
if m(db,p):
|
||||
res.append(p)
|
||||
return res
|
||||
|
||||
return filter(m, list)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -683,6 +725,7 @@ tasks = {
|
||||
_("Is a descendant of") : IsDescendantOf,
|
||||
_("Is a descendant family member of"): IsDescendantFamilyOf,
|
||||
_("Is an ancestor of") : IsAncestorOf,
|
||||
_("Has a common ancestor with") : HasCommonAncestorWith,
|
||||
_("Is a female") : IsFemale,
|
||||
_("Is a male") : IsMale,
|
||||
_("Has the personal event") : HasEvent,
|
||||
|
@ -1988,14 +1988,17 @@ class GrampsDB(Persistent):
|
||||
self.default.setAncestor(0)
|
||||
self.default = person
|
||||
self.default.setAncestor(1)
|
||||
|
||||
|
||||
def getDefaultPerson(self):
|
||||
"""returns the default Person of the database"""
|
||||
return self.default
|
||||
|
||||
def getPerson(self,id):
|
||||
"""returns a map of gramps's IDs to Person instances"""
|
||||
return self.personMap[id]
|
||||
"""returns the Person instance associated to id, or None"""
|
||||
if self.personMap.has_key(id):
|
||||
return self.personMap[id]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getPersonMap(self):
|
||||
"""returns a map of gramps's IDs to Person instances"""
|
||||
@ -2018,8 +2021,11 @@ class GrampsDB(Persistent):
|
||||
return extmap(self.familyMap)
|
||||
|
||||
def getFamily(self,id):
|
||||
"""returns a map of gramps's IDs to Family instances"""
|
||||
return self.familyMap[id]
|
||||
"""returns the Family instance associated to id, or None"""
|
||||
if self.familyMap.has_key(id):
|
||||
return self.familyMap[id]
|
||||
else:
|
||||
return None
|
||||
|
||||
def setFamilyMap(self,map):
|
||||
"""sets the map of gramps's IDs to Family instances"""
|
||||
@ -2320,18 +2326,23 @@ class GrampsDB(Persistent):
|
||||
return self.placeTable.keys()
|
||||
|
||||
def getPlace(self,key):
|
||||
return self.placeMap[key]
|
||||
"""returns the Place instance associated to key, or None"""
|
||||
if self.placeMap.has_key(key):
|
||||
return self.placeMap[key]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getPlaceDisplay(self,key):
|
||||
return self.placeTable[key]
|
||||
|
||||
|
||||
def getSourceKeys(self):
|
||||
return self.sourceTable.keys()
|
||||
|
||||
|
||||
def getSourceDisplay(self,key):
|
||||
return self.sourceTable[key]
|
||||
|
||||
def getSource(self,key):
|
||||
"""returns the Source instance associated to key, or None"""
|
||||
if self.sourceMap.has_key(key):
|
||||
return self.sourceMap[key]
|
||||
else:
|
||||
@ -2399,6 +2410,3 @@ class GrampsDB(Persistent):
|
||||
"""deletes the Family instance from the database"""
|
||||
if self.familyMap.has_key(family.getId()):
|
||||
del self.familyMap[family.getId()]
|
||||
|
||||
|
||||
|
||||
|
@ -553,3 +553,35 @@ def build_string_optmenu(mapping, start_val):
|
||||
myMenu.set_active(start_index)
|
||||
return myMenu
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Iterate over ancestors.
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def for_each_ancestor(start, func, data):
|
||||
"""
|
||||
Recursively iterate (breadth-first) over ancestors of
|
||||
people listed in start.
|
||||
Call func(data,pid) for the Id of each person encountered.
|
||||
Exit and return 1, as soon as func returns true.
|
||||
Return 0 otherwise.
|
||||
"""
|
||||
todo = start
|
||||
doneIds = {}
|
||||
while len(todo):
|
||||
p = todo.pop()
|
||||
pid = p.getId()
|
||||
# Don't process the same Id twice. This can happen
|
||||
# if there is a cycle in the database, or if the
|
||||
# initial list contains X and some of X's ancestors.
|
||||
if doneIds.has_key(pid):
|
||||
continue
|
||||
doneIds[pid] = 1
|
||||
if func(data,pid):
|
||||
return 1
|
||||
for fam, mrel, frel in p.getParentList():
|
||||
f = fam.getFather()
|
||||
m = fam.getMother()
|
||||
if f: todo.append(f)
|
||||
if m: todo.append(m)
|
||||
return 0
|
||||
|
@ -93,7 +93,11 @@ class GraphVizDialog(ReportDialog):
|
||||
ans.set_name(_("Ancestors of %s") % name)
|
||||
ans.add_rule(GenericFilter.IsAncestorOf([self.person.getId()]))
|
||||
|
||||
return [all,des,ans]
|
||||
com = GenericFilter.GenericFilter()
|
||||
com.set_name(_("People with common ancestor with %s") % name)
|
||||
com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.getId()]))
|
||||
|
||||
return [all,des,ans,com]
|
||||
|
||||
def add_user_options(self):
|
||||
self.arrowstyle_optionmenu = gtk.GtkOptionMenu()
|
||||
@ -270,7 +274,7 @@ class GraphVizDialog(ReportDialog):
|
||||
|
||||
file = open(self.target_path,"w")
|
||||
|
||||
ind_list = self.filter.apply(self.db.getPersonMap().values())
|
||||
ind_list = self.filter.apply(self.db, self.db.getPersonMap().values())
|
||||
|
||||
write_dot(file, ind_list, self.orien, width, height,
|
||||
self.tb_margin, self.lr_margin, self.hpages,
|
||||
@ -443,4 +447,3 @@ register_report(
|
||||
category=_("Graphical Reports"),
|
||||
description=get_description()
|
||||
)
|
||||
|
||||
|
@ -336,7 +336,7 @@ class IndivComplete:
|
||||
self.d.end_cell()
|
||||
|
||||
def write_report(self):
|
||||
ind_list = self.filter.apply(self.database.getPersonMap().values())
|
||||
ind_list = self.filter.apply(self.database,self.database.getPersonMap().values())
|
||||
count = 0
|
||||
for self.person in ind_list:
|
||||
self.write_person(count)
|
||||
@ -480,11 +480,15 @@ class IndivSummaryDialog(TextReportDialog):
|
||||
ans.set_name(_("Ancestors of %s") % name)
|
||||
ans.add_rule(GenericFilter.IsAncestorOf([self.person.getId()]))
|
||||
|
||||
com = GenericFilter.GenericFilter()
|
||||
com.set_name(_("People with common ancestor with %s") % name)
|
||||
com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.getId()]))
|
||||
|
||||
all = GenericFilter.GenericFilter()
|
||||
all.set_name(_("Entire Database"))
|
||||
all.add_rule(GenericFilter.Everyone([]))
|
||||
|
||||
return [id,des,ans,all]
|
||||
return [id,des,ans,com,all]
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@ -659,11 +663,3 @@ register_report(
|
||||
description=_("Produces a complete report on the selected people."),
|
||||
xpm=get_xpm_image()
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -766,7 +766,7 @@ class WebReport(Report):
|
||||
image_dir_name)
|
||||
return
|
||||
|
||||
ind_list = self.filter.apply(self.db.getPersonMap().values())
|
||||
ind_list = self.filter.apply(self.db,self.db.getPersonMap().values())
|
||||
self.progress_bar_setup(float(len(ind_list)))
|
||||
|
||||
doc = HtmlLinkDoc(self.selected_style,None,self.template_name,None)
|
||||
@ -925,7 +925,11 @@ class WebReportDialog(ReportDialog):
|
||||
ans.set_name(_("Ancestors of %s") % name)
|
||||
ans.add_rule(GenericFilter.IsAncestorOf([self.person.getId()]))
|
||||
|
||||
return [all,des,df,ans]
|
||||
com = GenericFilter.GenericFilter()
|
||||
com.set_name(_("People with common ancestor with %s") % name)
|
||||
com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.getId()]))
|
||||
|
||||
return [all,des,df,ans,com]
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@ -1253,4 +1257,3 @@ register_report(
|
||||
description=_("Generates web (HTML) pages for individuals, or a set of individuals."),
|
||||
xpm=get_xpm_image()
|
||||
)
|
||||
|
||||
|
@ -340,7 +340,12 @@ class GedcomWriter:
|
||||
ans.set_name(_("Ancestors of %s") % person.getPrimaryName().getName())
|
||||
ans.add_rule(GenericFilter.IsAncestorOf([person.getId()]))
|
||||
|
||||
self.filter_menu = GenericFilter.build_filter_menu([all,des,ans])
|
||||
com = GenericFilter.GenericFilter()
|
||||
com.set_name(_("People with common ancestor with %s") %
|
||||
person.getPrimaryName().getName())
|
||||
com.add_rule(GenericFilter.HasCommonAncestorWith([person.getId()]))
|
||||
|
||||
self.filter_menu = GenericFilter.build_filter_menu([all,des,ans,com])
|
||||
filter_obj.set_menu(self.filter_menu)
|
||||
|
||||
gedmap = GedcomInfoDB()
|
||||
@ -388,7 +393,7 @@ class GedcomWriter:
|
||||
for p in self.db.getPersonKeys():
|
||||
self.plist[p] = 1
|
||||
else:
|
||||
for p in cfilter.apply(self.db.getPersonMap().values()):
|
||||
for p in cfilter.apply(self.db,self.db.getPersonMap().values()):
|
||||
self.plist[p.getId()] = 1
|
||||
|
||||
self.flist = {}
|
||||
|
Loading…
Reference in New Issue
Block a user