2f962b5f96
svn: r5482
2605 lines
88 KiB
Python
2605 lines
88 KiB
Python
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2002-2005 Donald N. Allingham
|
|
#
|
|
# 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$
|
|
|
|
"""Generic Filtering Routines"""
|
|
|
|
__author__ = "Don Allingham"
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Try to abstract SAX1 from SAX2
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
from xml.sax import make_parser,handler,SAXParseException
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Standard Python modules
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
import os
|
|
import sets
|
|
from gettext import gettext as _
|
|
from TransUtils import strip_context as __
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# gtk
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
import gtk
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# GRAMPS modules
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
import const
|
|
import RelLib
|
|
import Date
|
|
import DateHandler
|
|
import NameDisplay
|
|
from TransTable import TransTable
|
|
from Utils import for_each_ancestor,probably_alive,get_source_referents
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# date_cmp
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
def date_cmp(rule,value):
|
|
sd = rule.get_start_date()
|
|
s = rule.get_modifier()
|
|
od = value.get_start_date()
|
|
cmp_rule = (sd[2],sd[1],sd[0])
|
|
cmp_value = (od[2],od[1],od[0])
|
|
if s == Date.MOD_BEFORE:
|
|
return cmp_rule > cmp_value
|
|
elif s == Date.MOD_AFTER:
|
|
return cmp_rule < cmp_value
|
|
else:
|
|
return cmp_rule == cmp_value
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Rule
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class Rule:
|
|
"""Base rule class"""
|
|
|
|
labels = []
|
|
name = ''
|
|
category = _('Miscellaneous filters')
|
|
description = _('No description')
|
|
|
|
def __init__(self,list):
|
|
self.set_list(list)
|
|
|
|
def prepare(self,db):
|
|
pass
|
|
|
|
def reset(self):
|
|
pass
|
|
|
|
def set_list(self,list):
|
|
assert type(list) == type([]) or list == None, "Argument is not a list"
|
|
assert len(list) == len(self.labels), \
|
|
"Number of arguments does not match number of labels.\n"\
|
|
"list: %s\nlabels: %s" % (list,self.labels)
|
|
self.list = list
|
|
|
|
def values(self):
|
|
return self.list
|
|
|
|
def check(self):
|
|
return len(self.list) == len(self.labels)
|
|
|
|
def apply(self,db,person):
|
|
return True
|
|
|
|
def display_values(self):
|
|
v = []
|
|
for i in range(0,len(self.list)):
|
|
if self.list[i]:
|
|
v.append('%s="%s"' % (_(self.labels[i]),_(self.list[i])))
|
|
return ';'.join(v)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Everyone
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class Everyone(Rule):
|
|
"""Matches Everyone"""
|
|
|
|
name = _('Everyone')
|
|
category = _('General filters')
|
|
description = _('Matches everyone in the database')
|
|
|
|
def apply(self,db,person):
|
|
return True
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Disconnected
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class Disconnected(Rule):
|
|
"""Matches disconnected people"""
|
|
|
|
name = _('Disconnected people')
|
|
category = _('General filters')
|
|
description = _('Matches people that have no family relationships '
|
|
'to any other person in the database')
|
|
|
|
def apply(self,db,person):
|
|
return (not person.get_main_parents_family_handle() and
|
|
not len(person.get_family_handle_list()))
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# RelationshipPathBetween
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class RelationshipPathBetween(Rule):
|
|
"""Rule that checks for a person that is a descendant of a specified person
|
|
not more than N generations away"""
|
|
|
|
labels = [ _('ID:'), _('ID:') ]
|
|
name = _("Relationship path between <persons>")
|
|
category = _('Relationship filters')
|
|
description = _("Matches the ancestors of two persons back to a common ancestor, "
|
|
"producing the relationship path between two persons.")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
root1_handle = db.get_person_from_gramps_id(self.list[0]).get_handle()
|
|
root2_handle = db.get_person_from_gramps_id(self.list[1]).get_handle()
|
|
self.init_list(root1_handle,root2_handle)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def desc_list(self, handle, map, first):
|
|
if not first:
|
|
map[handle] = 1
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
for fam_id in p.get_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
for child_handle in fam.get_child_handle_list():
|
|
if child_handle:
|
|
self.desc_list(child_handle,map,0)
|
|
|
|
def apply_filter(self,rank,handle,plist,pmap):
|
|
person = self.db.get_person_from_handle(handle)
|
|
if person == None:
|
|
return
|
|
plist.append(person)
|
|
pmap[person.get_handle()] = rank
|
|
|
|
fam_id = person.get_main_parents_family_handle()
|
|
family = self.db.get_family_from_handle(fam_id)
|
|
if family != None:
|
|
self.apply_filter(rank+1,family.get_father_handle(),plist,pmap)
|
|
self.apply_filter(rank+1,family.get_mother_handle(),plist,pmap)
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,p1_handle,p2_handle):
|
|
firstMap = {}
|
|
firstList = []
|
|
secondMap = {}
|
|
secondList = []
|
|
common = []
|
|
rank = 9999999
|
|
|
|
self.apply_filter(0,p1_handle,firstList,firstMap)
|
|
self.apply_filter(0,p2_handle,secondList,secondMap)
|
|
|
|
for person_handle in firstList:
|
|
if person_handle in secondList:
|
|
new_rank = firstMap[person_handle]
|
|
if new_rank < rank:
|
|
rank = new_rank
|
|
common = [ person_handle ]
|
|
elif new_rank == rank:
|
|
common.append(person_handle)
|
|
|
|
path1 = { p1_handle : 1}
|
|
path2 = { p2_handle : 1}
|
|
|
|
for person_handle in common:
|
|
new_map = {}
|
|
self.desc_list(person_handle,new_map,1)
|
|
self.get_intersection(path1,firstMap,new_map)
|
|
self.get_intersection(path2,secondMap,new_map)
|
|
|
|
for e in path1:
|
|
self.map[e] = 1
|
|
for e in path2:
|
|
self.map[e] = 1
|
|
for e in common:
|
|
self.map[e] = 1
|
|
|
|
def get_intersection(self,target, map1, map2):
|
|
for e in map1.keys():
|
|
if map2.has_key(e):
|
|
target[e] = map2[e]
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasIdOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasIdOf(Rule):
|
|
"""Rule that checks for a person with a specific GRAMPS ID"""
|
|
|
|
labels = [ _('ID:') ]
|
|
name = _('People with <Id>')
|
|
description = _("Matches people with a specified GRAMPS ID")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
return person.gramps_id == self.list[0]
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsDefaultPerson
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsDefaultPerson(Rule):
|
|
"""Rule that checks for a default person in the database"""
|
|
|
|
name = _('Default person')
|
|
category = _('General filters')
|
|
description = _("Matches the default person")
|
|
|
|
def prepare(self,db):
|
|
p = db.get_default_person()
|
|
if p:
|
|
self.def_handle = p.get_handle()
|
|
self.apply = self.apply_real
|
|
else:
|
|
self.apply = lambda db,p: False
|
|
|
|
def apply_real(self,db,person):
|
|
return person.handle == self.def_handle
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsBookmarked
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsBookmarked(Rule):
|
|
"""Rule that checks for the bookmark list in the database"""
|
|
|
|
name = _('Bookmarked people')
|
|
category = _('General filters')
|
|
description = _("Matches the people on the bookmark list")
|
|
|
|
def prepare(self,db):
|
|
bookmarks = db.get_bookmarks()
|
|
if len(bookmarks) == 0:
|
|
self.apply = lambda db,p : False
|
|
else:
|
|
self.bookmarks = sets.Set(bookmarks)
|
|
self.apply = self.apply_real
|
|
|
|
def apply_real(self,db,person):
|
|
return person.handle in self.bookmarks
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasCompleteRecord
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasCompleteRecord(Rule):
|
|
"""Rule that checks for a person whose record is complete"""
|
|
|
|
name = _('People with complete records')
|
|
category = _('General filters')
|
|
description = _('Matches all people whose records are complete')
|
|
|
|
def apply(self,db,person):
|
|
return person.get_complete_flag() == 1
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsFemale
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsFemale(Rule):
|
|
"""Rule that checks for a person that is a female"""
|
|
|
|
name = _('Females')
|
|
category = _('General filters')
|
|
description = _('Matches all females')
|
|
|
|
def apply(self,db,person):
|
|
return person.gender == RelLib.Person.FEMALE
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasUnknownGender
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasUnknownGender(Rule):
|
|
"""Rule that checks for a person that has unknown gender"""
|
|
|
|
name = _('People with unknown gender')
|
|
category = _('General filters')
|
|
description = _('Matches all people with unknown gender')
|
|
|
|
def apply(self,db,person):
|
|
return person.gender == RelLib.Person.UNKNOWN
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsDescendantOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsDescendantOf(Rule):
|
|
"""Rule that checks for a person that is a descendant
|
|
of a specified person"""
|
|
|
|
labels = [ _('ID:'), _('Inclusive:') ]
|
|
name = _('Descendants of <person>')
|
|
category = _('Descendant filters')
|
|
description = _('Matches all descendants for the specified person')
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
if int(self.list[1]):
|
|
first = False
|
|
else:
|
|
first = True
|
|
except IndexError:
|
|
first = True
|
|
try:
|
|
root_person = db.get_person_from_gramps_id(self.list[0])
|
|
self.init_list(root_person,first)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person,first):
|
|
if not person:
|
|
return
|
|
if not first:
|
|
self.map[person.handle] = 1
|
|
|
|
for fam_id in person.get_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
for child_handle in fam.get_child_handle_list():
|
|
self.init_list(self.db.get_person_from_handle(child_handle),0)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsDescendantOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsDescendantOfFilterMatch(IsDescendantOf):
|
|
"""Rule that checks for a person that is a descendant
|
|
of someone matched by a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('Descendants of <filter> match')
|
|
category = _('Descendant filters')
|
|
description = _("Matches people that are descendants of anybody matched by a filter")
|
|
|
|
|
|
# def __init__(self,list):
|
|
# IsDescendantOf.__init__(self,list)
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
if int(self.list[1]):
|
|
first = 0
|
|
else:
|
|
first = 1
|
|
except IndexError:
|
|
first = 1
|
|
|
|
filt = MatchesFilter(self.list[0:1])
|
|
filt.prepare(db)
|
|
for person_handle in db.get_person_handles(sort_handles=False):
|
|
person = db.get_person_from_handle( person_handle)
|
|
if filt.apply (db, person):
|
|
self.init_list (person,first)
|
|
filt.reset()
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsLessThanNthGenerationDescendantOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsLessThanNthGenerationDescendantOf(Rule):
|
|
"""Rule that checks for a person that is a descendant of a specified person
|
|
not more than N generations away"""
|
|
|
|
labels = [ _('ID:'), _('Number of generations:') ]
|
|
name = _('Descendants of <person> not more than <N> generations away')
|
|
category = _('Descendant filters')
|
|
description = _("Matches people that are descendants of a specified person "
|
|
"not more than N generations away")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
root_person = db.get_person_from_gramps_id(self.list[0])
|
|
self.init_list(root_person,0)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person,gen):
|
|
if not person:
|
|
return
|
|
if gen:
|
|
self.map[person.handle] = 1
|
|
if gen >= int(self.list[1]):
|
|
return
|
|
|
|
for fam_id in person.get_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
for child_handle in fam.get_child_handle_list():
|
|
self.init_list(self.db.get_person_from_handle(child_handle),gen+1)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsMoreThanNthGenerationDescendantOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsMoreThanNthGenerationDescendantOf(Rule):
|
|
"""Rule that checks for a person that is a descendant of a specified person
|
|
at least N generations away"""
|
|
|
|
labels = [ _('ID:'), _('Number of generations:') ]
|
|
name = _('Descendants of <person> at least <N> generations away')
|
|
category = _("Descendant filters")
|
|
description = _("Matches people that are descendants of a specified "
|
|
"person at least N generations away")
|
|
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
root_person = db.get_person_from_gramps_id(self.list[0])
|
|
self.init_list(root_person,0)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person,gen):
|
|
if not person:
|
|
return
|
|
if gen >= int(self.list[1]):
|
|
self.map[person.handle] = 1
|
|
|
|
for fam_id in person.get_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
for child_handle in fam.get_child_handle_list():
|
|
self.init_list(self.db.get_person_from_handle(child_handle),gen+1)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsChildOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsChildOfFilterMatch(Rule):
|
|
"""Rule that checks for a person that is a child
|
|
of someone matched by a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('Children of <filter> match')
|
|
category = _('Family filters')
|
|
description = _("Matches children of anybody matched by a filter")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
filt = MatchesFilter(self.list)
|
|
filt.prepare(db)
|
|
for person_handle in db.get_person_handles(sort_handles=False):
|
|
person = db.get_person_from_handle( person_handle)
|
|
if filt.apply (db, person):
|
|
self.init_list (person)
|
|
filt.reset()
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person):
|
|
if not person:
|
|
return
|
|
for fam_id in person.get_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
for child_handle in fam.get_child_handle_list():
|
|
self.map[child_handle] = 1
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsSiblingOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsSiblingOfFilterMatch(Rule):
|
|
"""Rule that checks for siblings of someone matched by a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('Siblings of <filter> match')
|
|
category = _('Family filters')
|
|
description = _("Matches siblings of anybody matched by a filter")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
filt = MatchesFilter(self.list)
|
|
filt.prepare(db)
|
|
for person_handle in db.get_person_handles(sort_handles=False):
|
|
person = db.get_person_from_handle( person_handle)
|
|
if filt.apply (db, person):
|
|
self.init_list (person)
|
|
filt.reset()
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person):
|
|
if not person:
|
|
return
|
|
fam_id = person.get_main_parents_family_handle()
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
for child_handle in fam.get_child_handle_list():
|
|
if child_handle != person.handle:
|
|
self.map[child_handle] = 1
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsDescendantFamilyOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsDescendantFamilyOf(Rule):
|
|
"""Rule that checks for a person that is a descendant or the spouse
|
|
of a descendant of a specified person"""
|
|
|
|
labels = [ _('ID:') ]
|
|
name = _('Descendant family members of <person>')
|
|
category = _('Descendant filters')
|
|
description = _("Matches people that are descendants or the spouse "
|
|
"of a descendant of a specified person")
|
|
|
|
def apply(self,db,person):
|
|
self.map = {}
|
|
self.orig_handle = person.handle
|
|
self.db = db
|
|
return self.search(person.handle,1)
|
|
|
|
def search(self,handle,val):
|
|
try:
|
|
if handle == self.db.get_person_from_gramps_id(self.list[0]).get_handle():
|
|
self.map[handle] = 1
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
for (f,r1,r2) in p.get_parent_family_handle_list():
|
|
family = self.db.get_family_from_handle(f)
|
|
for person_handle in [family.get_mother_handle(),family.get_father_handle()]:
|
|
if person_handle:
|
|
if self.search(person_handle,0):
|
|
return True
|
|
if val:
|
|
for family_handle in p.get_family_handle_list():
|
|
family = self.db.get_family_from_handle(family_handle)
|
|
if handle == family.get_father_handle():
|
|
spouse_id = family.get_mother_handle()
|
|
else:
|
|
spouse_id = family.get_father_handle()
|
|
if spouse_id:
|
|
if self.search(spouse_id,0):
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsAncestorOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsAncestorOf(Rule):
|
|
"""Rule that checks for a person that is an ancestor of a specified person"""
|
|
|
|
labels = [ _('ID:'), _('Inclusive:') ]
|
|
name = _('Ancestors of <person>')
|
|
category = _("Ancestral filters")
|
|
description = _("Matches people that are ancestors of a specified person")
|
|
|
|
def prepare(self,db):
|
|
"""Assume that if 'Inclusive' not defined, assume inclusive"""
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
if int(self.list[1]):
|
|
first = 0
|
|
else:
|
|
first = 1
|
|
except IndexError:
|
|
first = 1
|
|
try:
|
|
root_person = db.get_person_from_gramps_id(self.list[0])
|
|
self.init_ancestor_list(db,root_person,first)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_ancestor_list(self,db,person,first):
|
|
if not person:
|
|
return
|
|
if not first:
|
|
self.map[person.handle] = 1
|
|
|
|
fam_id = person.get_main_parents_family_handle()
|
|
fam = db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
f_id = fam.get_father_handle()
|
|
m_id = fam.get_mother_handle()
|
|
|
|
if f_id:
|
|
self.init_ancestor_list(db,db.get_person_from_handle(f_id),0)
|
|
if m_id:
|
|
self.init_ancestor_list(db,db.get_person_from_handle(m_id),0)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsAncestorOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsAncestorOfFilterMatch(IsAncestorOf):
|
|
"""Rule that checks for a person that is an ancestor of
|
|
someone matched by a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('Ancestors of <filter> match')
|
|
category = _("Ancestral filters")
|
|
description = _("Matches people that are ancestors "
|
|
"of anybody matched by a filter")
|
|
|
|
|
|
def __init__(self,list):
|
|
IsAncestorOf.__init__(self,list)
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
if int(self.list[1]):
|
|
first = 0
|
|
else:
|
|
first = 1
|
|
except IndexError:
|
|
first = 1
|
|
|
|
filt = MatchesFilter(self.list[0:1])
|
|
filt.prepare(db)
|
|
for person_handle in db.get_person_handles(sort_handles=False):
|
|
person = db.get_person_from_handle( person_handle)
|
|
if filt.apply (db, person):
|
|
self.init_ancestor_list (db,person,first)
|
|
filt.reset()
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsLessThanNthGenerationAncestorOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsLessThanNthGenerationAncestorOf(Rule):
|
|
"""Rule that checks for a person that is an ancestor of a specified person
|
|
not more than N generations away"""
|
|
|
|
labels = [ _('ID:'), _('Number of generations:') ]
|
|
name = _('Ancestors of <person> not more than <N> generations away')
|
|
category = _("Ancestral filters")
|
|
description = _("Matches people that are ancestors "
|
|
"of a specified person not more than N generations away")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
root_handle = db.get_person_from_gramps_id(self.list[0]).get_handle()
|
|
self.init_ancestor_list(root_handle,0)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_ancestor_list(self,handle,gen):
|
|
# if self.map.has_key(p.get_handle()) == 1:
|
|
# loop_error(self.orig,p)
|
|
if not handle:
|
|
return
|
|
if gen:
|
|
self.map[handle] = 1
|
|
if gen >= int(self.list[1]):
|
|
return
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
fam_id = p.get_main_parents_family_handle()
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
f_id = fam.get_father_handle()
|
|
m_id = fam.get_mother_handle()
|
|
|
|
if f_id:
|
|
self.init_ancestor_list(f_id,gen+1)
|
|
if m_id:
|
|
self.init_ancestor_list(m_id,gen+1)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsMoreThanNthGenerationAncestorOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsMoreThanNthGenerationAncestorOf(Rule):
|
|
"""Rule that checks for a person that is an ancestor of a specified person
|
|
at least N generations away"""
|
|
|
|
labels = [ _('ID:'), _('Number of generations:') ]
|
|
name = _('Ancestors of <person> at least <N> generations away')
|
|
category = _("Ancestral filters")
|
|
description = _("Matches people that are ancestors "
|
|
"of a specified person at least N generations away")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
try:
|
|
root_handle = db.get_person_from_gramps_id(self.list[0]).get_handle()
|
|
self.init_ancestor_list(root_handle,0)
|
|
except:
|
|
pass
|
|
|
|
def reset(self):
|
|
self.map = []
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_ancestor_list(self,handle,gen):
|
|
# if self.map.has_key(p.get_handle()) == 1:
|
|
# loop_error(self.orig,p)
|
|
if not handle:
|
|
return
|
|
if gen >= int(self.list[1]):
|
|
self.map[handle] = 1
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
fam_id = p.get_main_parents_family_handle()
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
f_id = fam.get_father_handle()
|
|
m_id = fam.get_mother_handle()
|
|
|
|
if f_id:
|
|
self.init_ancestor_list(f_id,gen+1)
|
|
if m_id:
|
|
self.init_ancestor_list(m_id,gen+1)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsParentOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsParentOfFilterMatch(Rule):
|
|
"""Rule that checks for a person that is a parent
|
|
of someone matched by a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('Parents of <filter> match')
|
|
category = _('Family filters')
|
|
description = _("Matches parents of anybody matched by a filter")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = {}
|
|
filt = MatchesFilter(self.list)
|
|
filt.prepare(db)
|
|
for person_handle in db.get_person_handles(sort_handles=False):
|
|
person = db.get_person_from_handle(person_handle)
|
|
if filt.apply (db, person):
|
|
self.init_list (person)
|
|
filt.reset()
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
def apply(self,db,person):
|
|
return self.map.has_key(person.handle)
|
|
|
|
def init_list(self,person):
|
|
for fam_id,frel,mrel in person.get_parent_family_handle_list():
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
for parent_id in [fam.get_father_handle (), fam.get_mother_handle ()]:
|
|
if parent_id:
|
|
self.map[parent_id] = 1
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasCommonAncestorWith
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasCommonAncestorWith(Rule):
|
|
"""Rule that checks for a person that has a common ancestor with a specified person"""
|
|
|
|
labels = [ _('ID:') ]
|
|
name = _('People with a common ancestor with <person>')
|
|
category = _("Ancestral filters")
|
|
description = _("Matches people that have a common ancestor "
|
|
"with a specified person")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
# 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 reset(self):
|
|
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.
|
|
try:
|
|
handle = db.get_person_from_gramps_id(self.list[0]).get_handle()
|
|
if handle:
|
|
def init(self,handle): self.ancestor_cache[handle] = 1
|
|
for_each_ancestor(db,[handle],init,self)
|
|
except:
|
|
pass
|
|
|
|
def apply(self,db,person):
|
|
# 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)
|
|
handle = person.handle
|
|
return for_each_ancestor(db,[handle],
|
|
lambda self,handle: self.ancestor_cache.has_key(handle),
|
|
self);
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasCommonAncestorWithFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasCommonAncestorWithFilterMatch(HasCommonAncestorWith):
|
|
"""Rule that checks for a person that has a common ancestor with
|
|
someone matching a filter"""
|
|
|
|
labels = [ _('Filter name:') ]
|
|
name = _('People with a common ancestor with <filter> match')
|
|
description = _("Matches people that have a common ancestor "
|
|
"with anybody matched by a filter")
|
|
category = _("Ancestral filters")
|
|
|
|
def __init__(self,list):
|
|
HasCommonAncestorWith.__init__(self,list)
|
|
self.ancestor_cache = {}
|
|
|
|
def init_ancestor_cache(self,db):
|
|
filt = MatchesFilter(self.list)
|
|
filt.prepare(db)
|
|
def init(self,h): self.ancestor_cache[h] = 1
|
|
for handle in db.get_person_handles(sort_handles=False):
|
|
if (not self.ancestor_cache.has_key (handle)
|
|
and filt.apply (db, db.get_person_from_handle(handle))):
|
|
for_each_ancestor(db,[handle],init,self)
|
|
filt.reset()
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsMale
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsMale(Rule):
|
|
"""Rule that checks for a person that is a male"""
|
|
|
|
name = _('Males')
|
|
category = _('General filters')
|
|
description = _('Matches all males')
|
|
|
|
def apply(self,db,person):
|
|
return person.gender == RelLib.Person.MALE
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasEvent
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasEvent(Rule):
|
|
"""Rule that checks for a person with a particular value"""
|
|
|
|
labels = [ _('Personal event:'),
|
|
_('Date:'),
|
|
_('Place:'),
|
|
_('Description:') ]
|
|
name = _('People with the personal <event>')
|
|
description = _("Matches people with a personal event of a particular value")
|
|
category = _('Event filters')
|
|
|
|
def prepare(self,db):
|
|
self.date = None
|
|
try:
|
|
if self.list[1]:
|
|
self.date = DateHandler.parser.parse(self.list[1])
|
|
except: pass
|
|
|
|
def apply(self,db,person):
|
|
for event_handle in person.get_event_list():
|
|
if not event_handle:
|
|
continue
|
|
event = db.get_event_from_handle(event_handle)
|
|
val = 1
|
|
if self.list[0] and event.get_name() != self.list[0]:
|
|
val = 0
|
|
if self.list[3] and event.get_description().upper().find(
|
|
self.list[3].upper())==-1:
|
|
val = 0
|
|
if self.date:
|
|
if date_cmp(self.date,event.get_date_object()):
|
|
val = 0
|
|
if self.list[2]:
|
|
pl_id = event.get_place_handle()
|
|
if pl_id:
|
|
pl = db.get_place_from_handle(pl_id)
|
|
pn = pl.get_title()
|
|
if pn.upper().find(self.list[2].upper()) == -1:
|
|
val = 0
|
|
if val == 1:
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasFamilyEvent
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasFamilyEvent(Rule):
|
|
"""Rule that checks for a person who has a relationship event
|
|
with a particular value"""
|
|
|
|
labels = [ _('Family event:'),
|
|
_('Date:'),
|
|
_('Place:'),
|
|
_('Description:') ]
|
|
name = _('People with the family <event>')
|
|
description = _("Matches people with a family event of a particular value")
|
|
category = _('Event filters')
|
|
|
|
def prepare(self,db):
|
|
self.date = None
|
|
try:
|
|
if self.list[1]:
|
|
self.date = DateHandler.parser.parse(self.list[1])
|
|
except:
|
|
pass
|
|
|
|
def apply(self,db,person):
|
|
for f_id in person.get_family_handle_list():
|
|
f = db.get_family_from_handle(f_id)
|
|
for event_handle in f.get_event_list():
|
|
if not event_handle:
|
|
continue
|
|
event = db.get_event_from_handle(event_handle)
|
|
val = 1
|
|
if self.list[0] and event.get_name() != self.list[0]:
|
|
val = 0
|
|
v = self.list[3]
|
|
if v and event.get_description().upper().find(v.upper())==-1:
|
|
val = 0
|
|
if self.date:
|
|
if date_cmp(self.date,event.get_date_object()):
|
|
val = 0
|
|
if self.list[2]:
|
|
pl_id = event.get_place_handle()
|
|
if pl_id:
|
|
pl = db.get_place_from_handle(pl_id)
|
|
pn = pl.get_title().upper()
|
|
if pn.find(self.list[2].upper()) == -1:
|
|
val = 0
|
|
else:
|
|
val = 0
|
|
|
|
if val == 1:
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasRelationship
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasRelationship(Rule):
|
|
"""Rule that checks for a person who has a particular relationship"""
|
|
|
|
labels = [ _('Number of relationships:'),
|
|
_('Relationship type:'),
|
|
_('Number of children:') ]
|
|
name = _('People with the <relationships>')
|
|
description = _("Matches people with a particular relationship")
|
|
category = _('Family filters')
|
|
|
|
def apply(self,db,person):
|
|
rel_type = 0
|
|
cnt = 0
|
|
num_rel = len(person.get_family_handle_list())
|
|
|
|
# count children and look for a relationship type match
|
|
for f_id in person.get_family_handle_list():
|
|
f = db.get_family_from_handle(f_id)
|
|
cnt = cnt + len(f.get_child_handle_list())
|
|
if self.list[1] and int(self.list[1]) == f.get_relationship():
|
|
rel_type = 1
|
|
|
|
# if number of relations specified
|
|
if self.list[0]:
|
|
try:
|
|
v = int(self.list[0])
|
|
except:
|
|
return False
|
|
if v != num_rel:
|
|
return False
|
|
|
|
# number of childred
|
|
if self.list[2]:
|
|
try:
|
|
v = int(self.list[2])
|
|
except:
|
|
return False
|
|
if v != cnt:
|
|
return False
|
|
|
|
# relation
|
|
if self.list[1]:
|
|
return rel_type == 1
|
|
else:
|
|
return True
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasBirth
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasBirth(Rule):
|
|
"""Rule that checks for a person with a birth of a particular value"""
|
|
|
|
labels = [ _('Date:'), _('Place:'), _('Description:') ]
|
|
name = _('People with the <birth data>')
|
|
description = _("Matches people with birth data of a particular value")
|
|
category = _('Event filters')
|
|
|
|
def __init__(self,list):
|
|
Rule.__init__(self,list)
|
|
if self.list[0]:
|
|
self.date = DateHandler.parser.parse(self.list[0])
|
|
else:
|
|
self.date = None
|
|
|
|
def apply(self,db,person):
|
|
event_handle = person.get_birth_handle()
|
|
if not event_handle:
|
|
return False
|
|
event = db.get_event_from_handle(event_handle)
|
|
ed = event.get_description().upper()
|
|
if self.list[2] \
|
|
and ed.find(self.list[2].upper())==-1:
|
|
return False
|
|
if self.date:
|
|
if date_cmp(self.date,event.get_date_object()) == 0:
|
|
return False
|
|
if self.list[1]:
|
|
pl_id = event.get_place_handle()
|
|
if pl_id:
|
|
pl = db.get_place_from_handle(pl_id)
|
|
pn = pl.get_title().upper()
|
|
if pn.find(self.list[1].upper()) == -1:
|
|
return False
|
|
else:
|
|
return False
|
|
return True
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasDeath
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasDeath(Rule):
|
|
"""Rule that checks for a person with a death of a particular value"""
|
|
|
|
labels = [ _('Date:'), _('Place:'), _('Description:') ]
|
|
name = _('People with the <death data>')
|
|
description = _("Matches people with death data of a particular value")
|
|
category = _('Event filters')
|
|
|
|
def __init__(self,list):
|
|
Rule.__init__(self,list)
|
|
if self.list[0]:
|
|
self.date = DateHandler.parser.parse(self.list[0])
|
|
else:
|
|
self.date = None
|
|
|
|
def apply(self,db,person):
|
|
event_handle = person.get_death_handle()
|
|
if not event_handle:
|
|
return False
|
|
event = db.get_event_from_handle(event_handle)
|
|
ed = event.get_description().upper()
|
|
if self.list[2] \
|
|
and ed.find(self.list[2].upper())==-1:
|
|
return False
|
|
if self.date:
|
|
if date_cmp(self.date,event.get_date_object()) == 0:
|
|
return False
|
|
if self.list[1]:
|
|
pl_id = event.get_place_handle()
|
|
if pl_id:
|
|
pl = db.get_place_from_handle(pl_id)
|
|
pn = pl.get_title().upper()
|
|
if self.list[1] and pn.find(self.list[1].upper()) == -1:
|
|
return False
|
|
else:
|
|
return False
|
|
return True
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasAttribute
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasAttribute(Rule):
|
|
"""Rule that checks for a person with a particular personal attribute"""
|
|
|
|
labels = [ _('Personal attribute:'), _('Value:') ]
|
|
name = _('People with the personal <attribute>')
|
|
description = _("Matches people with the personal attribute of a particular value")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
if not self.list[0]:
|
|
return False
|
|
for attr in person.get_attribute_list():
|
|
name_match = self.list[0] == attr.get_type()
|
|
value_match = \
|
|
attr.get_value().upper().find(self.list[1].upper()) != -1
|
|
if name_match and value_match:
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasFamilyAttribute
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasFamilyAttribute(Rule):
|
|
"""Rule that checks for a person with a particular family attribute"""
|
|
|
|
labels = [ _('Family attribute:'), _('Value:') ]
|
|
name = _('People with the family <attribute>')
|
|
description = _("Matches people with the family attribute of a particular value")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
if not self.list[0]:
|
|
return False
|
|
for f_id in person.get_family_handle_list():
|
|
f = db.get_family_from_handle(f_id)
|
|
for attr in f.get_attribute_list():
|
|
name_match = self.list[0] == attr.get_type()
|
|
value_match = \
|
|
attr.get_value().upper().find(self.list[1].upper()) != -1
|
|
if name_match and value_match:
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasNameOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasNameOf(Rule):
|
|
"""Rule that checks for full or partial name matches"""
|
|
|
|
labels = [ _('Given name:'),
|
|
_('Family name:'),
|
|
_('Suffix:'),
|
|
__('person|Title:')]
|
|
name = _('People with the <name>')
|
|
description = _("Matches people with a specified (partial) name")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
self.f = self.list[0]
|
|
self.l = self.list[1]
|
|
self.s = self.list[2]
|
|
self.t = self.list[3]
|
|
for name in [person.get_primary_name()] + person.get_alternate_names():
|
|
val = 1
|
|
if self.f and name.get_first_name().upper().find(self.f.upper()) == -1:
|
|
val = 0
|
|
if self.l and name.get_surname().upper().find(self.l.upper()) == -1:
|
|
val = 0
|
|
if self.s and name.get_suffix().upper().find(self.s.upper()) == -1:
|
|
val = 0
|
|
if self.t and name.get_title().upper().find(self.t.upper()) == -1:
|
|
val = 0
|
|
if val == 1:
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasNameOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class SearchName(Rule):
|
|
"""Rule that checks for full or partial name matches"""
|
|
|
|
labels = [_('Substring:')]
|
|
name = _('People matching the <name>')
|
|
description = _("Matches people with a specified (partial) name")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
self.f = self.list[0]
|
|
n = NameDisplay.displayer.display(person)
|
|
return self.f and n.upper().find(self.f.upper()) != -1
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IncompleteNames
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IncompleteNames(Rule):
|
|
"""People with incomplete names"""
|
|
|
|
name = _('People with incomplete names')
|
|
description = _("Matches people with firstname or lastname missing")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
for name in [person.get_primary_name()] + person.get_alternate_names():
|
|
if name.get_first_name() == "":
|
|
return True
|
|
if name.get_surname() == "":
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# MatchesFilter
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class MatchesFilter(Rule):
|
|
"""Rule that checks against another filter"""
|
|
|
|
labels = [_('Filter name:')]
|
|
name = _('People matching the <filter>')
|
|
description = _("Matches people macthed by the specified filter name")
|
|
category = _('General filters')
|
|
|
|
def prepare(self,db):
|
|
for filt in SystemFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
for rule in filt.flist:
|
|
rule.prepare(db)
|
|
for filt in CustomFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
for rule in filt.flist:
|
|
rule.prepare(db)
|
|
|
|
def reset(self):
|
|
for filt in SystemFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
for rule in filt.flist:
|
|
rule.reset()
|
|
for filt in CustomFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
for rule in filt.flist:
|
|
rule.reset()
|
|
|
|
def apply(self,db,person):
|
|
for filt in SystemFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
return filt.check(db,person.handle)
|
|
for filt in CustomFilters.get_filters():
|
|
if filt.get_name() == self.list[0]:
|
|
return filt.check(db,person.handle)
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsSpouseOfFilterMatch
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsSpouseOfFilterMatch(Rule):
|
|
"""Rule that checks for a person married to someone matching
|
|
a filter"""
|
|
|
|
labels = [_('Filter name:')]
|
|
name = _('Spouses of <filter> match')
|
|
description = _("Matches people married to anybody matching a filter")
|
|
category = _('Family filters')
|
|
|
|
def prepare(self,db):
|
|
self.filt = MatchesFilter (self.list)
|
|
self.filt.prepare(db)
|
|
|
|
def apply(self,db,person):
|
|
for family_handle in person.get_family_handle_list ():
|
|
family = db.get_family_from_handle(family_handle)
|
|
for spouse_id in [family.get_father_handle (), family.get_mother_handle ()]:
|
|
if not spouse_id:
|
|
continue
|
|
if spouse_id == person.handle:
|
|
continue
|
|
if self.filt.apply (db, db.get_person_from_handle( spouse_id)):
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People who were adopted"
|
|
#-------------------------------------------------------------------------
|
|
class HaveAltFamilies(Rule):
|
|
"""People who were adopted"""
|
|
|
|
name = _('Adopted people')
|
|
description = _("Matches people who were adopted")
|
|
category = _('Family filters')
|
|
|
|
def apply(self,db,person):
|
|
for (fam,rel1,rel2) in person.get_parent_family_handle_list():
|
|
if rel1 == RelLib.Person.CHILD_ADOPTED \
|
|
or rel2 == RelLib.Person.CHILD_ADOPTED:
|
|
return True
|
|
return False
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People who have images"
|
|
#-------------------------------------------------------------------------
|
|
class HavePhotos(Rule):
|
|
"""People who have images"""
|
|
|
|
name = _('People with images')
|
|
description = _("Matches people with images in the gallery")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
return len( person.get_media_list()) > 0
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People with children"
|
|
#-------------------------------------------------------------------------
|
|
class HaveChildren(Rule):
|
|
"""People with children"""
|
|
|
|
name = _('People with children')
|
|
description = _("Matches people who have children")
|
|
category = _('Family filters')
|
|
|
|
def apply(self,db,person):
|
|
for family_handle in person.get_family_handle_list():
|
|
family = db.get_family_from_handle(family_handle)
|
|
return len(family.get_child_handle_list()) > 0
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People with no marriage records"
|
|
#-------------------------------------------------------------------------
|
|
class NeverMarried(Rule):
|
|
"""People with no marriage records"""
|
|
|
|
name = _('People with no marriage records')
|
|
description = _("Matches people who have no spouse")
|
|
category = _('Family filters')
|
|
|
|
def apply(self,db,person):
|
|
return len(person.get_family_handle_list()) == 0
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People with multiple marriage records"
|
|
#-------------------------------------------------------------------------
|
|
class MultipleMarriages(Rule):
|
|
"""People with multiple marriage records"""
|
|
|
|
name = _('People with multiple marriage records')
|
|
description = _("Matches people who have more than one spouse")
|
|
category = _('Family filters')
|
|
|
|
def apply(self,db,person):
|
|
return len(person.get_family_handle_list()) > 1
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People without a birth date"
|
|
#-------------------------------------------------------------------------
|
|
class NoBirthdate(Rule):
|
|
"""People without a birth date"""
|
|
|
|
name = _('People without a known birth date')
|
|
description = _("Matches people without a known birthdate")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
birth_handle = person.get_birth_handle()
|
|
if not birth_handle:
|
|
return True
|
|
birth = db.get_event_from_handle(birth_handle)
|
|
if not birth.get_date_object():
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People with incomplete events"
|
|
#-------------------------------------------------------------------------
|
|
class PersonWithIncompleteEvent(Rule):
|
|
"""People with incomplete events"""
|
|
|
|
name = _('People with incomplete events')
|
|
description = _("Matches people with missing date or place in an event")
|
|
category = _('Event filters')
|
|
|
|
def apply(self,db,person):
|
|
for event_handle in person.get_event_list() + [person.get_birth_handle(), person.get_death_handle()]:
|
|
event = db.get_event_from_handle(event_handle)
|
|
if event:
|
|
if not event.get_place_handle():
|
|
return True
|
|
if not event.get_date_object():
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "Families with incomplete events"
|
|
#-------------------------------------------------------------------------
|
|
class FamilyWithIncompleteEvent(Rule):
|
|
"""Families with incomplete events"""
|
|
|
|
name = _('Families with incomplete events')
|
|
description = _("Matches people with missing date or place in an event of the family")
|
|
category = _('Event filters')
|
|
|
|
def apply(self,db,person):
|
|
for family_handle in person.get_family_handle_list():
|
|
family = db.get_family_from_handle(family_handle)
|
|
for event_handle in family.get_event_list():
|
|
event = db.get_event_from_handle(event_handle)
|
|
if event:
|
|
if not event.get_place_handle():
|
|
return True
|
|
if not event.get_date_object():
|
|
return True
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People probably alive"
|
|
#-------------------------------------------------------------------------
|
|
class ProbablyAlive(Rule):
|
|
"""People probably alive"""
|
|
|
|
labels = [_("On year:")]
|
|
name = _('People probably alive')
|
|
description = _("Matches people without indications of death that are not too old")
|
|
category = _('General filters')
|
|
|
|
def prepare(self,db):
|
|
try:
|
|
self.current_year = int(self.list[0])
|
|
except:
|
|
self.current_year = None
|
|
|
|
def apply(self,db,person):
|
|
return probably_alive(person,db,self.current_year)
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People marked private"
|
|
#-------------------------------------------------------------------------
|
|
class PeoplePrivate(Rule):
|
|
"""People marked private"""
|
|
|
|
name = _('People marked private')
|
|
description = _("Matches people that are indicated as private")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
return person.get_privacy()
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "Witnesses"
|
|
#-------------------------------------------------------------------------
|
|
class IsWitness(Rule):
|
|
"""Witnesses"""
|
|
|
|
labels = [_('Personal event:'), _('Family event:')]
|
|
name = _('Witnesses')
|
|
description = _("Matches people who are witnesses in any event")
|
|
category = _('Event filters')
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.map = []
|
|
self.build_witness_list()
|
|
|
|
def reset(self):
|
|
self.map = []
|
|
|
|
def apply(self,db,person):
|
|
return person.handle in self.map
|
|
|
|
def build_witness_list(self):
|
|
for person_handle in self.db.get_person_handles():
|
|
p = self.db.get_person_from_handle(person_handle)
|
|
self.get_witness_of_events(self.list[0],
|
|
p.get_event_list()+
|
|
[p.get_birth_handle(),
|
|
p.get_death_handle()]
|
|
)
|
|
|
|
for family_handle in self.db.get_family_handles():
|
|
f = self.db.get_family_from_handle(family_handle)
|
|
self.get_witness_of_events(self.list[1],f.get_event_list())
|
|
|
|
def get_witness_of_events(self, event_type, event_list):
|
|
if not event_list:
|
|
return
|
|
for event_handle in event_list:
|
|
event = self.db.get_event_from_handle(event_handle)
|
|
if event:
|
|
if event_type and not event.get_name() == event_type:
|
|
continue
|
|
wlist = event.get_witness_list()
|
|
if wlist:
|
|
for w in wlist:
|
|
if w.get_type() == RelLib.Event.ID:
|
|
self.map.append(w.get_value())
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "HasTextMatchingSubstringOf"
|
|
#-------------------------------------------------------------------------
|
|
class HasTextMatchingSubstringOf(Rule):
|
|
"""Rule that checks for string matches in any textual information"""
|
|
|
|
labels = [ _('Substring:'),
|
|
_('Case sensitive:'),
|
|
_('Regular-Expression matching:')]
|
|
name = _('People with records containing <substring>')
|
|
description = _("Matches people whose records contain text matching a substring")
|
|
category = _('General filters')
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.person_map = {}
|
|
self.event_map = {}
|
|
self.source_map = {}
|
|
self.repo_map = {}
|
|
self.family_map = {}
|
|
self.place_map = {}
|
|
self.media_map = {}
|
|
try:
|
|
if int(self.list[1]):
|
|
self.case_sensitive = True
|
|
else:
|
|
self.case_sensitive = False
|
|
except IndexError:
|
|
self.case_sensitive = False
|
|
try:
|
|
if int(self.list[2]):
|
|
self.regexp_match = True
|
|
else:
|
|
self.regexp_match = False
|
|
except IndexError:
|
|
self.regexp_match = False
|
|
self.cache_repos()
|
|
self.cache_sources()
|
|
|
|
def reset(self):
|
|
self.person_map = {}
|
|
self.event_map = {}
|
|
self.source_map = {}
|
|
self.repo_map = {}
|
|
self.family_map = {}
|
|
self.place_map = {}
|
|
self.media_map = {}
|
|
|
|
def apply(self,db,person):
|
|
if person.handle in self.person_map: # Cached by matching Source?
|
|
return self.person_map[person.handle]
|
|
if self.match_object(person): # first match the person itself
|
|
return True
|
|
for event_handle in person.get_event_list()+[person.get_birth_handle(), person.get_death_handle()]:
|
|
if self.search_event(event_handle): # match referenced events
|
|
return True
|
|
for family_handle in person.get_family_handle_list(): # match families
|
|
if self.search_family(family_handle):
|
|
return True
|
|
for media_ref in person.get_media_list(): # match Media object
|
|
if self.search_media(media_ref.get_reference_handle()):
|
|
return True
|
|
return False
|
|
|
|
def search_family(self,family_handle):
|
|
if not family_handle:
|
|
return False
|
|
# search inside the family and cache the result to not search a family twice
|
|
if not family_handle in self.family_map:
|
|
match = 0
|
|
family = self.db.get_family_from_handle(family_handle)
|
|
if self.match_object(family):
|
|
match = 1
|
|
else:
|
|
for event_handle in family.get_event_list():
|
|
if self.search_event(event_handle):
|
|
match = 1
|
|
break
|
|
for media_ref in family.get_media_list(): # match Media object
|
|
if self.search_media(media_ref.get_reference_handle()):
|
|
return True
|
|
self.family_map[family_handle] = match
|
|
return self.family_map[family_handle]
|
|
|
|
def search_event(self,event_handle):
|
|
if not event_handle:
|
|
return False
|
|
# search inside the event and cache the result (event sharing)
|
|
if not event_handle in self.event_map:
|
|
match = 0
|
|
event = self.db.get_event_from_handle(event_handle)
|
|
if self.match_object(event):
|
|
match = 1
|
|
elif event:
|
|
place_handle = event.get_place_handle()
|
|
if place_handle:
|
|
if self.search_place(place_handle):
|
|
match = 1
|
|
for media_ref in event.get_media_list(): # match Media object
|
|
if self.search_media(media_ref.get_reference_handle()):
|
|
return True
|
|
self.event_map[event_handle] = match
|
|
return self.event_map[event_handle]
|
|
|
|
def search_place(self,place_handle):
|
|
if not place_handle:
|
|
return False
|
|
# search inside the place and cache the result
|
|
if not place_handle in self.place_map:
|
|
place = self.db.get_place_from_handle(place_handle)
|
|
self.place_map[place_handle] = self.match_object(place)
|
|
return self.place_map[place_handle]
|
|
|
|
def search_media(self,media_handle):
|
|
if not media_handle:
|
|
return False
|
|
# search inside the place and cache the result
|
|
if not media_handle in self.media_map:
|
|
media = self.db.get_object_from_handle(media_handle)
|
|
self.media_map[media_handle] = self.match_object(media)
|
|
return self.media_map[media_handle]
|
|
|
|
def cache_repos(self):
|
|
# search all matching repositories
|
|
for repo_handle in self.db.get_repository_handles():
|
|
repo = self.db.get_repository_from_handle(repo_handle)
|
|
if( self.match_object(repo)):
|
|
self.repo_map[repo_handle] = 1
|
|
|
|
def cache_sources(self):
|
|
# search all sources and match all referents of a matching source
|
|
for source_handle in self.db.get_source_handles():
|
|
source = self.db.get_source_from_handle(source_handle)
|
|
match = self.match_object(source)
|
|
if not match:
|
|
for reporef in source.get_reporef_list():
|
|
if reporef.get_reference_handle() in self.repo_map:
|
|
match = 1
|
|
if match:
|
|
(person_list,family_list,event_list,
|
|
place_list,source_list,media_list
|
|
) = get_source_referents(source_handle,self.db)
|
|
for handle in person_list:
|
|
self.person_map[handle] = 1
|
|
for handle in family_list:
|
|
self.family_map[handle] = 1
|
|
for handle in event_list:
|
|
self.event_map[handle] = 1
|
|
for handle in place_list:
|
|
self.place_map[handle] = 1
|
|
for handle in media_list:
|
|
self.media_map[handle] = 1
|
|
|
|
def match_object(self,obj):
|
|
if not obj:
|
|
return False
|
|
if self.regexp_match:
|
|
return obj.matches_regexp(self.list[0],self.case_sensitive)
|
|
return obj.matches_string(self.list[0],self.case_sensitive)
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "HasTextMatchingRegexOf"
|
|
#-------------------------------------------------------------------------
|
|
class HasTextMatchingRegexpOf(HasTextMatchingSubstringOf):
|
|
"""This is wrapping HasTextMatchingSubstringOf to enable the regex_match parameter"""
|
|
def __init__(self,list):
|
|
HasTextMatchingSubstringOf.__init__(self,list)
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
self.person_map = {}
|
|
self.event_map = {}
|
|
self.source_map = {}
|
|
self.repo_map = {}
|
|
self.family_map = {}
|
|
self.place_map = {}
|
|
self.media_map = {}
|
|
self.case_sensitive = False
|
|
self.regexp_match = True
|
|
self.cache_sources()
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# HasSourceOf
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class HasSourceOf(Rule):
|
|
"""Rule that checks people that have a particular source."""
|
|
|
|
labels = [ _('Source ID:') ]
|
|
name = _('People with the <source>')
|
|
category = _('General filters')
|
|
description = _('Matches people who have a particular source')
|
|
|
|
def prepare(self,db):
|
|
try:
|
|
self.source_handle = db.get_source_from_gramps_id(self.list[0]).get_handle()
|
|
except:
|
|
self.source_handle = None
|
|
|
|
def apply(self,db,person):
|
|
if not self.source_handle:
|
|
return False
|
|
return person.has_source_reference( self.source_handle)
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People having notes"
|
|
#-------------------------------------------------------------------------
|
|
class HasNote(Rule):
|
|
"""People having notes"""
|
|
|
|
name = _('People having notes')
|
|
description = _("Matches people that have a note")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
return bool(person.get_note())
|
|
|
|
#-------------------------------------------------------------------------
|
|
# "People having notes that contain a substring"
|
|
#-------------------------------------------------------------------------
|
|
class HasNoteMatchingSubstringOf(Rule):
|
|
"""People having notes containing <subtring>"""
|
|
|
|
labels = [ _('Substring:')]
|
|
name = _('People having notes containing <subtring>')
|
|
description = _("Matches people whose notes contain text matching a substring")
|
|
category = _('General filters')
|
|
|
|
def apply(self,db,person):
|
|
n = person.get_note()
|
|
if n:
|
|
return n.upper().find(self.list[0].upper()) != -1
|
|
return False
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# GenericFilter
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class GenericFilter:
|
|
"""Filter class that consists of several rules"""
|
|
|
|
logical_functions = ['or', 'and', 'xor', 'one']
|
|
|
|
def __init__(self,source=None):
|
|
if source:
|
|
self.need_param = source.need_param
|
|
self.flist = source.flist[:]
|
|
self.name = source.name
|
|
self.comment = source.comment
|
|
self.logical_op = source.logical_op
|
|
self.invert = source.invert
|
|
else:
|
|
self.need_param = 0
|
|
self.flist = []
|
|
self.name = ''
|
|
self.comment = ''
|
|
self.logical_op = 'and'
|
|
self.invert = False
|
|
|
|
def set_logical_op(self,val):
|
|
if val in GenericFilter.logical_functions:
|
|
self.logical_op = val
|
|
else:
|
|
self.logical_op = 'and'
|
|
|
|
def get_logical_op(self):
|
|
return self.logical_op
|
|
|
|
def set_invert(self, val):
|
|
self.invert = not not val
|
|
|
|
def get_invert(self):
|
|
return self.invert
|
|
|
|
def get_name(self):
|
|
return self.name
|
|
|
|
def set_name(self,name):
|
|
self.name = name
|
|
|
|
def set_comment(self,comment):
|
|
self.comment = comment
|
|
|
|
def get_comment(self):
|
|
return self.comment
|
|
|
|
def add_rule(self,rule):
|
|
self.flist.append(rule)
|
|
|
|
def delete_rule(self,rule):
|
|
self.flist.remove(rule)
|
|
|
|
def set_rules(self,rules):
|
|
self.flist = rules
|
|
|
|
def get_rules(self):
|
|
return self.flist
|
|
|
|
def check_func(self,db,id_list,task):
|
|
final_list = []
|
|
|
|
if id_list == None:
|
|
cursor = db.get_person_cursor()
|
|
data = cursor.next()
|
|
while data:
|
|
person = RelLib.Person()
|
|
person.unserialize(data[1])
|
|
if self.invert ^ task(db,person):
|
|
final_list.append(data[0])
|
|
data = cursor.next()
|
|
else:
|
|
for handle in id_list:
|
|
person = db.get_person_from_handle(handle)
|
|
if self.invert ^ task(db,person):
|
|
final_list.append(handle)
|
|
return final_list
|
|
|
|
def check_or(self,db,id_list):
|
|
return self.check_func(db,id_list,self.or_test)
|
|
|
|
def check_and(self,db,id_list):
|
|
final_list = []
|
|
flist = self.flist
|
|
invert = self.invert
|
|
if id_list == None:
|
|
cursor = db.get_person_cursor()
|
|
data = cursor.next()
|
|
p = RelLib.Person
|
|
while data:
|
|
person = p(data[1])
|
|
val = True
|
|
for rule in flist:
|
|
if not rule.apply(db,person):
|
|
val = False
|
|
break
|
|
if invert ^ val:
|
|
final_list.append(data[0])
|
|
data = cursor.next()
|
|
else:
|
|
for handle in id_list:
|
|
person = db.get_person_from_handle(handle)
|
|
val = True
|
|
for rule in flist:
|
|
if not rule.apply(db,person):
|
|
val = False
|
|
if invert ^ val:
|
|
final_list.append(handle)
|
|
return final_list
|
|
|
|
def check_one(self,db,id_list):
|
|
return self.check_func(db,id_list,self.one_test)
|
|
|
|
def check_xor(self,db,id_list):
|
|
return self.check_func(db,id_list,self.xor_test)
|
|
|
|
def xor_test(self,db,person):
|
|
test = False
|
|
for rule in self.flist:
|
|
test = test ^ rule.apply(db,person)
|
|
return test
|
|
|
|
def one_test(self,db,person):
|
|
count = 0
|
|
for rule in self.flist:
|
|
if rule.apply(db,person):
|
|
if count:
|
|
return False
|
|
count += 1
|
|
return count == 1
|
|
|
|
def or_test(self,db,person):
|
|
for rule in self.flist:
|
|
if rule.apply(db,person):
|
|
return True
|
|
return False
|
|
|
|
def get_check_func(self):
|
|
try:
|
|
m = getattr(self, 'check_' + self.logical_op)
|
|
except AttributeError:
|
|
m = self.check_and
|
|
return m
|
|
|
|
def check(self,db,handle):
|
|
return self.get_check_func()(db,[handle])
|
|
|
|
def apply(self,db,id_list=None):
|
|
m = self.get_check_func()
|
|
for rule in self.flist:
|
|
rule.prepare(db)
|
|
res = m(db,id_list)
|
|
for rule in self.flist:
|
|
rule.reset()
|
|
return res
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsLessThanNthGenerationAncestorOfBookmarked
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsLessThanNthGenerationAncestorOfBookmarked(Rule):
|
|
# Submitted by Wayne Bergeron
|
|
"""Rule that checks for a person that is an ancestor of bookmarked persons
|
|
not more than N generations away"""
|
|
|
|
labels = [ _('Number of generations:') ]
|
|
name = _('Ancestors of bookmarked people not more '
|
|
'than <N> generations away')
|
|
category = _('Ancestral filters')
|
|
description = _("Matches ancestors of the people on the bookmark list"
|
|
"not more than N generations away")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
bookmarks = self.db.get_bookmarks()
|
|
if len(bookmarks) == 0:
|
|
self.apply = lambda db,p : False
|
|
else:
|
|
self.map = {}
|
|
self.bookmarks = sets.Set(bookmarks)
|
|
self.apply = self.apply_real
|
|
for self.bookmarkhandle in self.bookmarks:
|
|
self.init_ancestor_list(self.bookmarkhandle, 1)
|
|
|
|
|
|
def init_ancestor_list(self,handle,gen):
|
|
# if self.map.has_key(p.get_handle()) == 1:
|
|
# loop_error(self.orig,p)
|
|
if not handle:
|
|
return
|
|
if gen:
|
|
self.map[handle] = 1
|
|
if gen >= int(self.list[0]):
|
|
return
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
fam_id = p.get_main_parents_family_handle()
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
f_id = fam.get_father_handle()
|
|
m_id = fam.get_mother_handle()
|
|
|
|
if f_id:
|
|
self.init_ancestor_list(f_id,gen+1)
|
|
if m_id:
|
|
self.init_ancestor_list(m_id,gen+1)
|
|
|
|
def apply_real(self,db,person):
|
|
return person.handle in self.map
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# IsLessThanNthGenerationAncestorOfDefaultPerson
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class IsLessThanNthGenerationAncestorOfDefaultPerson(Rule):
|
|
# Submitted by Wayne Bergeron
|
|
"""Rule that checks for a person that is an ancestor of the default person
|
|
not more than N generations away"""
|
|
|
|
labels = [ _('Number of generations:') ]
|
|
name = _('Ancestors of the default person '
|
|
'not more than <N> generations away')
|
|
category = _('Ancestral filters')
|
|
description = _("Matches ancestors of the default person "
|
|
"not more than N generations away")
|
|
|
|
def prepare(self,db):
|
|
self.db = db
|
|
p = db.get_default_person()
|
|
if p == 0:
|
|
self.apply = lambda db,p: False
|
|
else:
|
|
self.def_handle = p.get_handle()
|
|
self.apply = self.apply_real
|
|
self.map = {}
|
|
self.init_ancestor_list(self.def_handle, 1)
|
|
|
|
|
|
def init_ancestor_list(self,handle,gen):
|
|
# if self.map.has_key(p.get_handle()) == 1:
|
|
# loop_error(self.orig,p)
|
|
if not handle:
|
|
return
|
|
if gen:
|
|
self.map[handle] = 1
|
|
if gen >= int(self.list[0]):
|
|
return
|
|
|
|
p = self.db.get_person_from_handle(handle)
|
|
fam_id = p.get_main_parents_family_handle()
|
|
fam = self.db.get_family_from_handle(fam_id)
|
|
if fam:
|
|
f_id = fam.get_father_handle()
|
|
m_id = fam.get_mother_handle()
|
|
|
|
if f_id:
|
|
self.init_ancestor_list(f_id,gen+1)
|
|
if m_id:
|
|
self.init_ancestor_list(m_id,gen+1)
|
|
|
|
def apply_real(self,db,person):
|
|
return person.handle in self.map
|
|
|
|
def reset(self):
|
|
self.map = {}
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Name to class mappings
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
# This dict is mapping from old names to new names, so that the existing
|
|
# custom_filters.xml will continue working
|
|
old_names_2_class = {
|
|
"Everyone" : Everyone,
|
|
"Is default person" : IsDefaultPerson,
|
|
"Is bookmarked person" : IsBookmarked,
|
|
"Has the Id" : HasIdOf,
|
|
"Has a name" : HasNameOf,
|
|
"Has the relationships" : HasRelationship,
|
|
"Has the death" : HasDeath,
|
|
"Has the birth" : HasBirth,
|
|
"Is a descendant of" : IsDescendantOf,
|
|
"Is a descendant family member of" : IsDescendantFamilyOf,
|
|
"Is a descendant of filter match": IsDescendantOfFilterMatch,
|
|
"Is a descendant of person not more than N generations away":
|
|
IsLessThanNthGenerationDescendantOf,
|
|
"Is a descendant of person at least N generations away":
|
|
IsMoreThanNthGenerationDescendantOf,
|
|
"Is an descendant of person at least N generations away" :
|
|
IsMoreThanNthGenerationDescendantOf,
|
|
"Is a child of filter match" : IsChildOfFilterMatch,
|
|
"Is an ancestor of" : IsAncestorOf,
|
|
"Is an ancestor of filter match": IsAncestorOfFilterMatch,
|
|
"Is an ancestor of person not more than N generations away" :
|
|
IsLessThanNthGenerationAncestorOf,
|
|
"Is an ancestor of person at least N generations away":
|
|
IsMoreThanNthGenerationAncestorOf,
|
|
"Is a parent of filter match" : IsParentOfFilterMatch,
|
|
"Has a common ancestor with" : HasCommonAncestorWith,
|
|
"Has a common ancestor with filter match" :HasCommonAncestorWithFilterMatch,
|
|
"Is a female" : IsFemale,
|
|
"Is a male" : IsMale,
|
|
"Has complete record" : HasCompleteRecord,
|
|
"Has the personal event" : HasEvent,
|
|
"Has the family event" : HasFamilyEvent,
|
|
"Has the personal attribute" : HasAttribute,
|
|
"Has the family attribute" : HasFamilyAttribute,
|
|
"Has source of" : HasSourceOf,
|
|
"Matches the filter named" : HasSourceOf,
|
|
"Is spouse of filter match" : IsSpouseOfFilterMatch,
|
|
"Is a sibling of filter match" : IsSiblingOfFilterMatch,
|
|
"Relationship path between two people" : RelationshipPathBetween,
|
|
"People who were adopted" : HaveAltFamilies,
|
|
"People who have images" : HavePhotos,
|
|
"People with children" : HaveChildren,
|
|
"People with incomplete names" : IncompleteNames,
|
|
"People with no marriage records" : NeverMarried,
|
|
"People with multiple marriage records": MultipleMarriages,
|
|
"People without a birth date" : NoBirthdate,
|
|
"People with incomplete events" : PersonWithIncompleteEvent,
|
|
"Families with incomplete events" :FamilyWithIncompleteEvent,
|
|
"People probably alive" : ProbablyAlive,
|
|
"People marked private" : PeoplePrivate,
|
|
"Witnesses" : IsWitness,
|
|
"Has text matching substring of":HasTextMatchingSubstringOf,
|
|
}
|
|
|
|
editor_rule_list = [
|
|
Everyone,
|
|
IsFemale,
|
|
HasUnknownGender,
|
|
IsMale,
|
|
IsDefaultPerson,
|
|
IsBookmarked,
|
|
HasIdOf,
|
|
HasNameOf,
|
|
HasRelationship,
|
|
HasDeath,
|
|
HasBirth,
|
|
HasCompleteRecord,
|
|
HasEvent,
|
|
HasFamilyEvent,
|
|
HasAttribute,
|
|
HasFamilyAttribute,
|
|
HasSourceOf,
|
|
HaveAltFamilies,
|
|
HavePhotos,
|
|
HaveChildren,
|
|
IncompleteNames,
|
|
NeverMarried,
|
|
MultipleMarriages,
|
|
NoBirthdate,
|
|
PersonWithIncompleteEvent,
|
|
FamilyWithIncompleteEvent,
|
|
ProbablyAlive,
|
|
PeoplePrivate,
|
|
IsWitness,
|
|
IsDescendantOf,
|
|
IsDescendantFamilyOf,
|
|
IsLessThanNthGenerationAncestorOfDefaultPerson,
|
|
IsDescendantOfFilterMatch,
|
|
IsLessThanNthGenerationDescendantOf,
|
|
IsMoreThanNthGenerationDescendantOf,
|
|
IsAncestorOf,
|
|
IsAncestorOfFilterMatch,
|
|
IsLessThanNthGenerationAncestorOf,
|
|
IsLessThanNthGenerationAncestorOfBookmarked,
|
|
IsMoreThanNthGenerationAncestorOf,
|
|
HasCommonAncestorWith,
|
|
HasCommonAncestorWithFilterMatch,
|
|
MatchesFilter,
|
|
IsChildOfFilterMatch,
|
|
IsParentOfFilterMatch,
|
|
IsSpouseOfFilterMatch,
|
|
IsSiblingOfFilterMatch,
|
|
RelationshipPathBetween,
|
|
HasTextMatchingSubstringOf,
|
|
HasNote,
|
|
HasNoteMatchingSubstringOf
|
|
]
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# GenericFilterList
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class GenericFilterList:
|
|
"""Container class for the generic filters. Stores, saves, and
|
|
loads the filters."""
|
|
|
|
def __init__(self,file):
|
|
self.filter_list = []
|
|
self.file = os.path.expanduser(file)
|
|
|
|
def get_filters(self):
|
|
return self.filter_list
|
|
|
|
def add(self,filt):
|
|
self.filter_list.append(filt)
|
|
|
|
def load(self):
|
|
try:
|
|
parser = make_parser()
|
|
parser.setContentHandler(FilterParser(self))
|
|
if self.file[0:7] != "file://":
|
|
parser.parse("file://" + self.file)
|
|
else:
|
|
parser.parse(self.file)
|
|
except (IOError,OSError):
|
|
pass
|
|
except SAXParseException:
|
|
print "Parser error"
|
|
|
|
def fix(self,line):
|
|
l = line.strip()
|
|
l = l.replace('&','&')
|
|
l = l.replace('>','>')
|
|
l = l.replace('<','<')
|
|
return l.replace('"','"')
|
|
|
|
def save(self):
|
|
f = open(self.file.encode('utf-8'),'w')
|
|
|
|
f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
|
f.write('<filters>\n')
|
|
for i in self.filter_list:
|
|
f.write(' <filter name="%s"' % self.fix(i.get_name()))
|
|
if i.get_invert():
|
|
f.write(' invert="1"')
|
|
f.write(' function="%s"' % i.get_logical_op())
|
|
comment = i.get_comment()
|
|
if comment:
|
|
f.write(' comment="%s"' % self.fix(comment))
|
|
f.write('>\n')
|
|
for rule in i.get_rules():
|
|
rule_module_name = rule.__module__
|
|
rule_class_name = rule.__class__.__name__
|
|
rule_save_name = "%s.%s" % (rule_module_name,rule_class_name)
|
|
f.write(' <rule class="%s">\n' % rule_save_name)
|
|
for v in rule.values():
|
|
f.write(' <arg value="%s"/>\n' % self.fix(v))
|
|
f.write(' </rule>\n')
|
|
f.write(' </filter>\n')
|
|
f.write('</filters>\n')
|
|
f.close()
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# FilterParser
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class FilterParser(handler.ContentHandler):
|
|
"""Parses the XML file and builds the list of filters"""
|
|
|
|
def __init__(self,gfilter_list):
|
|
handler.ContentHandler.__init__(self)
|
|
self.gfilter_list = gfilter_list
|
|
self.f = None
|
|
self.r = None
|
|
self.a = []
|
|
self.cname = None
|
|
|
|
def setDocumentLocator(self,locator):
|
|
self.locator = locator
|
|
|
|
def startElement(self,tag,attrs):
|
|
if tag == "filter":
|
|
self.f = GenericFilter()
|
|
self.f.set_name(attrs['name'])
|
|
if attrs.has_key('function'):
|
|
try:
|
|
if int(attrs['function']):
|
|
op = 'or'
|
|
else:
|
|
op = 'and'
|
|
except ValueError:
|
|
op = attrs['function']
|
|
self.f.set_logical_op(op)
|
|
if attrs.has_key('comment'):
|
|
self.f.set_comment(attrs['comment'])
|
|
if attrs.has_key('invert'):
|
|
try:
|
|
self.f.set_invert(int(attrs['invert']))
|
|
except ValueError:
|
|
pass
|
|
self.gfilter_list.add(self.f)
|
|
elif tag == "rule":
|
|
save_name = attrs['class']
|
|
if save_name in old_names_2_class.keys():
|
|
self.r = old_names_2_class[save_name]
|
|
else:
|
|
mc_match = save_name.split('.')
|
|
if len(mc_match) != 2:
|
|
# rule has an old style name, that is not in old_names_2_class
|
|
# or is not in the format "module.class"
|
|
print "ERROR: Filter rule '%s' in filter '%s' not found!" % (
|
|
save_name,self.f.get_name())
|
|
self.r = None
|
|
return
|
|
module_name,class_name = mc_match
|
|
try:
|
|
if module_name == self.__module__:
|
|
exec 'self.r = %s' % class_name
|
|
else:
|
|
exec 'import %s' % module_name
|
|
exec 'self.r = %s.%s' % (module_name,class_name)
|
|
except (ImportError,NameError):
|
|
print "ERROR: Filter rule '%s' in filter '%s' not found!" % (
|
|
save_name,self.f.get_name())
|
|
self.r = None
|
|
return
|
|
self.a = []
|
|
elif tag == "arg":
|
|
self.a.append(attrs['value'])
|
|
|
|
def endElement(self,tag):
|
|
if tag == "rule" and self.r != None:
|
|
if len(self.r.labels) < len(self.a):
|
|
print "WARNING: Invalid number of arguments in filter '%s'!" %\
|
|
self.f.get_name()
|
|
nargs = len(self.r.labels)
|
|
rule = self.r(self.a[0:nargs])
|
|
self.f.add_rule(rule)
|
|
elif len(self.r.labels) > len(self.a):
|
|
print "ERROR: Invalid number of arguments in filter '%s'!" %\
|
|
self.f.get_name()
|
|
else:
|
|
rule = self.r(self.a)
|
|
self.f.add_rule(rule)
|
|
|
|
def characters(self, data):
|
|
pass
|
|
|
|
class ParamFilter(GenericFilter):
|
|
|
|
def __init__(self,source=None):
|
|
GenericFilter.__init__(self,source)
|
|
self.need_param = 1
|
|
self.param_list = []
|
|
|
|
def set_parameter(self,param):
|
|
self.param_list = [param]
|
|
|
|
def apply(self,db,id_list=None):
|
|
for rule in self.flist:
|
|
#rule.set_list(self.param_list)
|
|
#
|
|
# The above breaks filters with more than one param
|
|
# Need to change existing params one by one to keep
|
|
# the correct number of arguments
|
|
new_list = rule.list[:]
|
|
for ix in range(len(self.param_list)):
|
|
new_list[ix] = self.param_list[ix]
|
|
rule.set_list(new_list)
|
|
for rule in self.flist:
|
|
rule.prepare(db)
|
|
result = GenericFilter.apply(self,db,id_list)
|
|
for rule in self.flist:
|
|
rule.reset()
|
|
return result
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
#
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
SystemFilters = None
|
|
CustomFilters = None
|
|
|
|
def reload_system_filters():
|
|
global SystemFilters
|
|
SystemFilters = GenericFilterList(const.system_filters)
|
|
SystemFilters.load()
|
|
|
|
def reload_custom_filters():
|
|
global CustomFilters
|
|
CustomFilters = GenericFilterList(const.custom_filters)
|
|
CustomFilters.load()
|
|
|
|
if not SystemFilters:
|
|
reload_system_filters()
|
|
|
|
if not CustomFilters:
|
|
reload_custom_filters()
|
|
|
|
|
|
class GrampsFilterComboBox(gtk.ComboBox):
|
|
|
|
def set(self,local_filters,default=""):
|
|
self.store = gtk.ListStore(str)
|
|
self.set_model(self.store)
|
|
cell = gtk.CellRendererText()
|
|
self.pack_start(cell,True)
|
|
self.add_attribute(cell,'text',0)
|
|
|
|
self.map = {}
|
|
|
|
active = 0
|
|
cnt = 0
|
|
for filt in local_filters:
|
|
self.store.append(row=[filt.get_name()])
|
|
self.map[filt.get_name()] = filt
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
for filt in SystemFilters.get_filters():
|
|
self.store.append(row=[filt.get_name()])
|
|
self.map[filt.get_name()] = filt
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
for filt in CustomFilters.get_filters():
|
|
self.store.append(row=[filt.get_name()])
|
|
self.map[filt.get_name()] = filt
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
if active:
|
|
self.set_active(active)
|
|
elif len(local_filters):
|
|
self.set_active(2)
|
|
elif len(SystemFilters.get_filters()):
|
|
self.set_active(4 + len(local_filters))
|
|
elif len(CustomFilters.get_filters()):
|
|
self.set_active(6 + len(local_filters) + len(SystemFilters.get_filters()))
|
|
else:
|
|
self.set_active(0)
|
|
|
|
def get_value(self):
|
|
active = self.get_active()
|
|
if active < 0:
|
|
return None
|
|
key = unicode(self.store[active][0])
|
|
return self.map[key]
|
|
|
|
|
|
class FilterStore(gtk.ListStore):
|
|
|
|
def __init__(self,local_filters=[], default=""):
|
|
gtk.ListStore.__init__(self,str)
|
|
self.list_map = []
|
|
self.def_index = 0
|
|
|
|
cnt = 0
|
|
for filt in local_filters:
|
|
name = filt.get_name()
|
|
self.append(row=[name])
|
|
self.list_map.append(filt)
|
|
if default != "" and default == name:
|
|
self.def_index = cnt
|
|
cnt += 1
|
|
|
|
for filt in SystemFilters.get_filters() + CustomFilters.get_filters():
|
|
name = _(filt.get_name())
|
|
self.append(row=[name])
|
|
self.list_map.append(filt)
|
|
if default != "" and default == name:
|
|
self.def_index = cnt
|
|
cnt += 1
|
|
|
|
def default_index(self):
|
|
return self.def_index
|
|
|
|
def get_filter(self,index):
|
|
return self.list_map[index]
|
|
|
|
|
|
def build_filter_menu(local_filters = [], default=""):
|
|
menu = gtk.Menu()
|
|
|
|
active = 0
|
|
cnt = 0
|
|
for filt in local_filters:
|
|
menuitem = gtk.MenuItem(filt.get_name())
|
|
menuitem.show()
|
|
menu.append(menuitem)
|
|
menuitem.set_data("filter", filt)
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
for filt in SystemFilters.get_filters():
|
|
menuitem = gtk.MenuItem(_(filt.get_name()))
|
|
menuitem.show()
|
|
menu.append(menuitem)
|
|
menuitem.set_data("filter", filt)
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
for filt in CustomFilters.get_filters():
|
|
menuitem = gtk.MenuItem(_(filt.get_name()))
|
|
menuitem.show()
|
|
menu.append(menuitem)
|
|
menuitem.set_data("filter", filt)
|
|
if default != "" and default == filt.get_name():
|
|
active = cnt
|
|
cnt += 1
|
|
|
|
if active:
|
|
menu.set_active(active)
|
|
elif len(local_filters):
|
|
menu.set_active(2)
|
|
elif len(SystemFilters.get_filters()):
|
|
menu.set_active(4 + len(local_filters))
|
|
elif len(CustomFilters.get_filters()):
|
|
menu.set_active(6 + len(local_filters) + len(SystemFilters.get_filters()))
|
|
else:
|
|
menu.set_active(0)
|
|
|
|
return menu
|