GEPS008: Create new module for probably alive
svn: r19812
This commit is contained in:
468
src/Utils.py
468
src/Utils.py
@ -56,23 +56,6 @@ from const import TEMP_DIR, USER_HOME, GRAMPS_UUID, IMAGE_DIR
|
|||||||
from gen.constfunc import mac, win
|
from gen.constfunc import mac, win
|
||||||
from gen.ggettext import sgettext as _
|
from gen.ggettext import sgettext as _
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Constants from config .ini keys
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------
|
|
||||||
# cache values; use refresh_constants() if they change
|
|
||||||
try:
|
|
||||||
from gen.config import config
|
|
||||||
_MAX_AGE_PROB_ALIVE = config.get('behavior.max-age-prob-alive')
|
|
||||||
_MAX_SIB_AGE_DIFF = config.get('behavior.max-sib-age-diff')
|
|
||||||
_AVG_GENERATION_GAP = config.get('behavior.avg-generation-gap')
|
|
||||||
except ImportError:
|
|
||||||
# Utils used as module not part of GRAMPS
|
|
||||||
_MAX_AGE_PROB_ALIVE = 110
|
|
||||||
_MAX_SIB_AGE_DIFF = 20
|
|
||||||
_AVG_GENERATION_GAP = 20
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Integer to String mappings for constants
|
# Integer to String mappings for constants
|
||||||
@ -507,447 +490,6 @@ def create_uid(self, handle=None):
|
|||||||
uid = uuid.uuid4()
|
uid = uuid.uuid4()
|
||||||
return uid.hex.upper()
|
return uid.hex.upper()
|
||||||
|
|
||||||
class ProbablyAlive(object):
|
|
||||||
"""
|
|
||||||
An object to hold the parameters for considering someone alive.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
db,
|
|
||||||
max_sib_age_diff=None,
|
|
||||||
max_age_prob_alive=None,
|
|
||||||
avg_generation_gap=None):
|
|
||||||
self.db = db
|
|
||||||
if max_sib_age_diff is None:
|
|
||||||
max_sib_age_diff = _MAX_SIB_AGE_DIFF
|
|
||||||
if max_age_prob_alive is None:
|
|
||||||
max_age_prob_alive = _MAX_AGE_PROB_ALIVE
|
|
||||||
if avg_generation_gap is None:
|
|
||||||
avg_generation_gap = _AVG_GENERATION_GAP
|
|
||||||
self.MAX_SIB_AGE_DIFF = max_sib_age_diff
|
|
||||||
self.MAX_AGE_PROB_ALIVE = max_age_prob_alive
|
|
||||||
self.AVG_GENERATION_GAP = avg_generation_gap
|
|
||||||
|
|
||||||
def probably_alive_range(self, person, is_spouse=False):
|
|
||||||
# FIXME: some of these computed dates need to be a span. For
|
|
||||||
# example, if a person could be born +/- 20 yrs around
|
|
||||||
# a date then it should be a span, and yr_offset should
|
|
||||||
# deal with it as well ("between 1920 and 1930" + 10 =
|
|
||||||
# "between 1930 and 1940")
|
|
||||||
if person is None:
|
|
||||||
return (None, None, "", None)
|
|
||||||
birth_ref = person.get_birth_ref()
|
|
||||||
death_ref = person.get_death_ref()
|
|
||||||
death_date = None
|
|
||||||
birth_date = None
|
|
||||||
explain = ""
|
|
||||||
# If the recorded death year is before current year then
|
|
||||||
# things are simple.
|
|
||||||
if death_ref and death_ref.get_role().is_primary():
|
|
||||||
if death_ref:
|
|
||||||
death = self.db.get_event_from_handle(death_ref.ref)
|
|
||||||
if death and death.get_date_object().get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
death_date = death.get_date_object()
|
|
||||||
|
|
||||||
# Look for Cause Of Death, Burial or Cremation events.
|
|
||||||
# These are fairly good indications that someone's not alive.
|
|
||||||
if not death_date:
|
|
||||||
for ev_ref in person.get_primary_event_ref_list():
|
|
||||||
if ev_ref:
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_death_fallback():
|
|
||||||
death_date = ev.get_date_object()
|
|
||||||
explain = _("death-related evidence")
|
|
||||||
|
|
||||||
# If they were born within X years before current year then
|
|
||||||
# assume they are alive (we already know they are not dead).
|
|
||||||
if not birth_date:
|
|
||||||
if birth_ref and birth_ref.get_role().is_primary():
|
|
||||||
birth = self.db.get_event_from_handle(birth_ref.ref)
|
|
||||||
if birth and birth.get_date_object().get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
birth_date = birth.get_date_object()
|
|
||||||
|
|
||||||
# Look for Baptism, etc events.
|
|
||||||
# These are fairly good indications that someone's birth.
|
|
||||||
if not birth_date:
|
|
||||||
for ev_ref in person.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth_fallback():
|
|
||||||
birth_date = ev.get_date_object()
|
|
||||||
explain = _("birth-related evidence")
|
|
||||||
|
|
||||||
if not birth_date and death_date:
|
|
||||||
# person died more than MAX after current year
|
|
||||||
birth_date = death_date.copy_offset_ymd(year=-self.MAX_AGE_PROB_ALIVE)
|
|
||||||
explain = _("death date")
|
|
||||||
|
|
||||||
if not death_date and birth_date:
|
|
||||||
# person died more than MAX after current year
|
|
||||||
death_date = birth_date.copy_offset_ymd(year=self.MAX_AGE_PROB_ALIVE)
|
|
||||||
explain = _("birth date")
|
|
||||||
|
|
||||||
if death_date and birth_date:
|
|
||||||
return (birth_date, death_date, explain, person) # direct self evidence
|
|
||||||
|
|
||||||
# Neither birth nor death events are available. Try looking
|
|
||||||
# at siblings. If a sibling was born more than X years past,
|
|
||||||
# or more than Z future, then probably this person is
|
|
||||||
# not alive. If the sibling died more than X years
|
|
||||||
# past, or more than X years future, then probably not alive.
|
|
||||||
|
|
||||||
family_list = person.get_parent_family_handle_list()
|
|
||||||
for family_handle in family_list:
|
|
||||||
family = self.db.get_family_from_handle(family_handle)
|
|
||||||
if family is None:
|
|
||||||
continue
|
|
||||||
for child_ref in family.get_child_ref_list():
|
|
||||||
child_handle = child_ref.ref
|
|
||||||
child = self.db.get_person_from_handle(child_handle)
|
|
||||||
if child is None:
|
|
||||||
continue
|
|
||||||
# Go through once looking for direct evidence:
|
|
||||||
for ev_ref in child.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
# if sibling birth date too far away, then not alive:
|
|
||||||
year = dobj.get_year()
|
|
||||||
if year != 0:
|
|
||||||
# sibling birth date
|
|
||||||
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF),
|
|
||||||
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("sibling birth date"),
|
|
||||||
child)
|
|
||||||
elif ev and ev.type.is_death():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
# if sibling death date too far away, then not alive:
|
|
||||||
year = dobj.get_year()
|
|
||||||
if year != 0:
|
|
||||||
# sibling death date
|
|
||||||
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE
|
|
||||||
+ self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("sibling death date"),
|
|
||||||
child)
|
|
||||||
# Go through again looking for fallback:
|
|
||||||
for ev_ref in child.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
# if sibling birth date too far away, then not alive:
|
|
||||||
year = dobj.get_year()
|
|
||||||
if year != 0:
|
|
||||||
# sibling birth date
|
|
||||||
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF),
|
|
||||||
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("sibling birth-related date"),
|
|
||||||
child)
|
|
||||||
elif ev and ev.type.is_death_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
# if sibling death date too far away, then not alive:
|
|
||||||
year = dobj.get_year()
|
|
||||||
if year != 0:
|
|
||||||
# sibling death date
|
|
||||||
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("sibling death-related date"),
|
|
||||||
child)
|
|
||||||
|
|
||||||
if not is_spouse: # if you are not in recursion, let's recurse:
|
|
||||||
for family_handle in person.get_family_handle_list():
|
|
||||||
family = self.db.get_family_from_handle(family_handle)
|
|
||||||
if family:
|
|
||||||
mother_handle = family.get_mother_handle()
|
|
||||||
father_handle = family.get_father_handle()
|
|
||||||
if mother_handle == person.handle and father_handle:
|
|
||||||
father = self.db.get_person_from_handle(father_handle)
|
|
||||||
date1, date2, explain, other = self.probably_alive_range(father, is_spouse=True)
|
|
||||||
if date1 and date2:
|
|
||||||
return date1, date2, _("a spouse, ") + explain, other
|
|
||||||
elif father_handle == person.handle and mother_handle:
|
|
||||||
mother = self.db.get_person_from_handle(mother_handle)
|
|
||||||
date1, date2, explain, other = self.probably_alive_range(mother, is_spouse=True)
|
|
||||||
if date1 and date2:
|
|
||||||
return date1, date2, _("a spouse, ") + explain, other
|
|
||||||
# Let's check the family events and see if we find something
|
|
||||||
for ref in family.get_event_ref_list():
|
|
||||||
if ref:
|
|
||||||
event = self.db.get_event_from_handle(ref.ref)
|
|
||||||
if event:
|
|
||||||
date = event.get_date_object()
|
|
||||||
year = date.get_year()
|
|
||||||
if year != 0:
|
|
||||||
other = None
|
|
||||||
if person.handle == mother_handle and father_handle:
|
|
||||||
other = self.db.get_person_from_handle(father_handle)
|
|
||||||
elif person.handle == father_handle and mother_handle:
|
|
||||||
other = self.db.get_person_from_handle(mother_handle)
|
|
||||||
return (gen.lib.Date().copy_ymd(year - self.AVG_GENERATION_GAP),
|
|
||||||
gen.lib.Date().copy_ymd(year - self.AVG_GENERATION_GAP +
|
|
||||||
self.MAX_AGE_PROB_ALIVE),
|
|
||||||
|
|
||||||
_("event with spouse"), other)
|
|
||||||
|
|
||||||
# Try looking for descendants that were born more than a lifespan
|
|
||||||
# ago.
|
|
||||||
|
|
||||||
def descendants_too_old (person, years):
|
|
||||||
for family_handle in person.get_family_handle_list():
|
|
||||||
family = self.db.get_family_from_handle(family_handle)
|
|
||||||
if not family:
|
|
||||||
# can happen with LivingProxyDb(PrivateProxyDb(db))
|
|
||||||
continue
|
|
||||||
for child_ref in family.get_child_ref_list():
|
|
||||||
child_handle = child_ref.ref
|
|
||||||
child = self.db.get_person_from_handle(child_handle)
|
|
||||||
child_birth_ref = child.get_birth_ref()
|
|
||||||
if child_birth_ref:
|
|
||||||
child_birth = self.db.get_event_from_handle(child_birth_ref.ref)
|
|
||||||
dobj = child_birth.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
d = gen.lib.Date(dobj)
|
|
||||||
val = d.get_start_date()
|
|
||||||
val = d.get_year() - years
|
|
||||||
d.set_year(val)
|
|
||||||
return (d, d.copy_offset_ymd(self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("descendant birth date"),
|
|
||||||
child)
|
|
||||||
child_death_ref = child.get_death_ref()
|
|
||||||
if child_death_ref:
|
|
||||||
child_death = self.db.get_event_from_handle(child_death_ref.ref)
|
|
||||||
dobj = child_death.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP),
|
|
||||||
dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("descendant death date"),
|
|
||||||
child)
|
|
||||||
date1, date2, explain, other = descendants_too_old (child, years + self.AVG_GENERATION_GAP)
|
|
||||||
if date1 and date2:
|
|
||||||
return date1, date2, explain, other
|
|
||||||
# Check fallback data:
|
|
||||||
for ev_ref in child.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
d = gen.lib.Date(dobj)
|
|
||||||
val = d.get_start_date()
|
|
||||||
val = d.get_year() - years
|
|
||||||
d.set_year(val)
|
|
||||||
return (d, d.copy_offset_ymd(self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("descendant birth-related date"),
|
|
||||||
child)
|
|
||||||
|
|
||||||
elif ev and ev.type.is_death_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP),
|
|
||||||
dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("descendant death-related date"),
|
|
||||||
child)
|
|
||||||
|
|
||||||
return (None, None, "", None)
|
|
||||||
|
|
||||||
# If there are descendants that are too old for the person to have
|
|
||||||
# been alive in the current year then they must be dead.
|
|
||||||
|
|
||||||
date1, date2, explain, other = None, None, "", None
|
|
||||||
try:
|
|
||||||
date1, date2, explain, other = descendants_too_old(person, self.AVG_GENERATION_GAP)
|
|
||||||
except RuntimeError:
|
|
||||||
raise DatabaseError(
|
|
||||||
_("Database error: %s is defined as his or her own ancestor") %
|
|
||||||
name_displayer.display(person))
|
|
||||||
|
|
||||||
if date1 and date2:
|
|
||||||
return (date1, date2, explain, other)
|
|
||||||
|
|
||||||
def ancestors_too_old(person, year):
|
|
||||||
family_handle = person.get_main_parents_family_handle()
|
|
||||||
if family_handle:
|
|
||||||
family = self.db.get_family_from_handle(family_handle)
|
|
||||||
if not family:
|
|
||||||
# can happen with LivingProxyDb(PrivateProxyDb(db))
|
|
||||||
return (None, None, "", None)
|
|
||||||
father_handle = family.get_father_handle()
|
|
||||||
if father_handle:
|
|
||||||
father = self.db.get_person_from_handle(father_handle)
|
|
||||||
father_birth_ref = father.get_birth_ref()
|
|
||||||
if father_birth_ref and father_birth_ref.get_role().is_primary():
|
|
||||||
father_birth = self.db.get_event_from_handle(
|
|
||||||
father_birth_ref.ref)
|
|
||||||
dobj = father_birth.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year),
|
|
||||||
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor birth date"),
|
|
||||||
father)
|
|
||||||
father_death_ref = father.get_death_ref()
|
|
||||||
if father_death_ref and father_death_ref.get_role().is_primary():
|
|
||||||
father_death = self.db.get_event_from_handle(
|
|
||||||
father_death_ref.ref)
|
|
||||||
dobj = father_death.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor death date"),
|
|
||||||
father)
|
|
||||||
|
|
||||||
# Check fallback data:
|
|
||||||
for ev_ref in father.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year),
|
|
||||||
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor birth-related date"),
|
|
||||||
father)
|
|
||||||
|
|
||||||
elif ev and ev.type.is_death_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor death-related date"),
|
|
||||||
father)
|
|
||||||
|
|
||||||
date1, date2, explain, other = ancestors_too_old (father, year - self.AVG_GENERATION_GAP)
|
|
||||||
if date1 and date2:
|
|
||||||
return date1, date2, explain, other
|
|
||||||
|
|
||||||
mother_handle = family.get_mother_handle()
|
|
||||||
if mother_handle:
|
|
||||||
mother = self.db.get_person_from_handle(mother_handle)
|
|
||||||
mother_birth_ref = mother.get_birth_ref()
|
|
||||||
if mother_birth_ref and mother_birth_ref.get_role().is_primary():
|
|
||||||
mother_birth = self.db.get_event_from_handle(mother_birth_ref.ref)
|
|
||||||
dobj = mother_birth.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year),
|
|
||||||
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor birth date"),
|
|
||||||
mother)
|
|
||||||
mother_death_ref = mother.get_death_ref()
|
|
||||||
if mother_death_ref and mother_death_ref.get_role().is_primary():
|
|
||||||
mother_death = self.db.get_event_from_handle(
|
|
||||||
mother_death_ref.ref)
|
|
||||||
dobj = mother_death.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor death date"),
|
|
||||||
mother)
|
|
||||||
|
|
||||||
# Check fallback data:
|
|
||||||
for ev_ref in mother.get_primary_event_ref_list():
|
|
||||||
ev = self.db.get_event_from_handle(ev_ref.ref)
|
|
||||||
if ev and ev.type.is_birth_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year),
|
|
||||||
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor birth-related date"),
|
|
||||||
mother)
|
|
||||||
|
|
||||||
elif ev and ev.type.is_death_fallback():
|
|
||||||
dobj = ev.get_date_object()
|
|
||||||
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
|
||||||
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
|
||||||
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
|
||||||
_("ancestor death-related date"),
|
|
||||||
mother)
|
|
||||||
|
|
||||||
date1, date2, explain, other = ancestors_too_old (mother, year - self.AVG_GENERATION_GAP)
|
|
||||||
if date1 and date2:
|
|
||||||
return (date1, date2, explain, other)
|
|
||||||
|
|
||||||
return (None, None, "", None)
|
|
||||||
|
|
||||||
# If there are ancestors that would be too old in the current year
|
|
||||||
# then assume our person must be dead too.
|
|
||||||
date1, date2, explain, other = ancestors_too_old (person, - self.AVG_GENERATION_GAP)
|
|
||||||
if date1 and date2:
|
|
||||||
return (date1, date2, explain, other)
|
|
||||||
|
|
||||||
# If we can't find any reason to believe that they are dead we
|
|
||||||
# must assume they are alive.
|
|
||||||
|
|
||||||
return (None, None, "", None)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# probably_alive
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------
|
|
||||||
def probably_alive(person, db,
|
|
||||||
current_date=None,
|
|
||||||
limit=0,
|
|
||||||
max_sib_age_diff=None,
|
|
||||||
max_age_prob_alive=None,
|
|
||||||
avg_generation_gap=None,
|
|
||||||
return_range=False):
|
|
||||||
"""
|
|
||||||
Return true if the person may be alive on current_date.
|
|
||||||
|
|
||||||
This works by a process of elimination. If we can't find a good
|
|
||||||
reason to believe that someone is dead then we assume they must
|
|
||||||
be alive.
|
|
||||||
|
|
||||||
:param current_date: a date object that is not estimated or modified
|
|
||||||
(defaults to today)
|
|
||||||
:param limit: number of years to check beyond death_date
|
|
||||||
:param max_sib_age_diff: maximum sibling age difference, in years
|
|
||||||
:param max_age_prob_alive: maximum age of a person, in years
|
|
||||||
:param avg_generation_gap: average generation gap, in years
|
|
||||||
"""
|
|
||||||
# First, get the real database to use all people
|
|
||||||
# for determining alive status:
|
|
||||||
basedb = db.basedb
|
|
||||||
# Now, we create a wrapper for doing work:
|
|
||||||
pb = ProbablyAlive(basedb, max_sib_age_diff,
|
|
||||||
max_age_prob_alive,
|
|
||||||
avg_generation_gap)
|
|
||||||
birth, death, explain, relative = pb.probably_alive_range(person)
|
|
||||||
if current_date is None:
|
|
||||||
current_date = gen.lib.date.Today()
|
|
||||||
if not birth or not death:
|
|
||||||
# no evidence, must consider alive
|
|
||||||
return (True, None, None, _("no evidence"), None)
|
|
||||||
# must have dates from here:
|
|
||||||
if limit:
|
|
||||||
death += limit # add these years to death
|
|
||||||
# Finally, check to see if current_date is between dates
|
|
||||||
result = (current_date.match(birth, ">=") and
|
|
||||||
current_date.match(death, "<="))
|
|
||||||
if return_range:
|
|
||||||
return (result, birth, death, explain, relative)
|
|
||||||
else:
|
|
||||||
return result
|
|
||||||
|
|
||||||
def probably_alive_range(person, db,
|
|
||||||
max_sib_age_diff=None,
|
|
||||||
max_age_prob_alive=None,
|
|
||||||
avg_generation_gap=None):
|
|
||||||
"""
|
|
||||||
Computes estimated birth and death dates.
|
|
||||||
Returns: (birth_date, death_date, explain_text, related_person)
|
|
||||||
"""
|
|
||||||
# First, find the real database to use all people
|
|
||||||
# for determining alive status:
|
|
||||||
from gen.proxy.proxybase import ProxyDbBase
|
|
||||||
basedb = db
|
|
||||||
while isinstance(basedb, ProxyDbBase):
|
|
||||||
basedb = basedb.db
|
|
||||||
# Now, we create a wrapper for doing work:
|
|
||||||
pb = ProbablyAlive(basedb, max_sib_age_diff,
|
|
||||||
max_age_prob_alive, avg_generation_gap)
|
|
||||||
return pb.probably_alive_range(person)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Other util functions
|
# Other util functions
|
||||||
@ -1353,16 +895,6 @@ def get_researcher():
|
|||||||
|
|
||||||
return owner
|
return owner
|
||||||
|
|
||||||
def update_constants():
|
|
||||||
"""
|
|
||||||
Used to update the constants that are cached in this module.
|
|
||||||
"""
|
|
||||||
from gen.config import config
|
|
||||||
global _MAX_AGE_PROB_ALIVE, _MAX_SIB_AGE_DIFF, _AVG_GENERATION_GAP
|
|
||||||
_MAX_AGE_PROB_ALIVE = config.get('behavior.max-age-prob-alive')
|
|
||||||
_MAX_SIB_AGE_DIFF = config.get('behavior.max-sib-age-diff')
|
|
||||||
_AVG_GENERATION_GAP = config.get('behavior.avg-generation-gap')
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Function to return the name of the main participant of an event
|
# Function to return the name of the main participant of an event
|
||||||
|
@ -32,7 +32,7 @@ from gen.ggettext import gettext as _
|
|||||||
# GRAMPS modules
|
# GRAMPS modules
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
from gen.filters.rules import Rule
|
from gen.filters.rules import Rule
|
||||||
import gen.datehandler
|
import gen.datehandler
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ from itertools import ifilter
|
|||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
from proxybase import ProxyDbBase
|
from proxybase import ProxyDbBase
|
||||||
from gen.lib import Date, Person, Name, Surname
|
from gen.lib import Date, Person, Name, Surname
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
from gen.config import config
|
from gen.config import config
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
@ -8,6 +8,7 @@ pkgpythondir = $(datadir)/@PACKAGE@/gen/utils
|
|||||||
|
|
||||||
pkgpython_PYTHON = \
|
pkgpython_PYTHON = \
|
||||||
__init__.py \
|
__init__.py \
|
||||||
|
alive.py \
|
||||||
callback.py \
|
callback.py \
|
||||||
callman.py \
|
callman.py \
|
||||||
configmanager.py \
|
configmanager.py \
|
||||||
|
519
src/gen/utils/alive.py
Normal file
519
src/gen/utils/alive.py
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
#
|
||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||||
|
# Copyright (C) 2009 Gary Burton
|
||||||
|
# Copyright (C) 2011 Tim G L Lyons
|
||||||
|
#
|
||||||
|
# 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$
|
||||||
|
|
||||||
|
"""
|
||||||
|
A utility to make a best guess if a person is alive. This is used to provide
|
||||||
|
privacy in reports and exports.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Standard python modules
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
import logging
|
||||||
|
LOG = logging.getLogger(".gen.utils.alive")
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Gramps modules
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
from gen.display.name import displayer as name_displayer
|
||||||
|
import gen.lib
|
||||||
|
from gen.errors import DatabaseError
|
||||||
|
from gen.ggettext import sgettext as _
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Constants from config .ini keys
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# cache values; use refresh_constants() if they change
|
||||||
|
try:
|
||||||
|
from gen.config import config
|
||||||
|
_MAX_AGE_PROB_ALIVE = config.get('behavior.max-age-prob-alive')
|
||||||
|
_MAX_SIB_AGE_DIFF = config.get('behavior.max-sib-age-diff')
|
||||||
|
_AVG_GENERATION_GAP = config.get('behavior.avg-generation-gap')
|
||||||
|
except ImportError:
|
||||||
|
# Utils used as module not part of GRAMPS
|
||||||
|
_MAX_AGE_PROB_ALIVE = 110
|
||||||
|
_MAX_SIB_AGE_DIFF = 20
|
||||||
|
_AVG_GENERATION_GAP = 20
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# ProbablyAlive class
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
class ProbablyAlive(object):
|
||||||
|
"""
|
||||||
|
An object to hold the parameters for considering someone alive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
db,
|
||||||
|
max_sib_age_diff=None,
|
||||||
|
max_age_prob_alive=None,
|
||||||
|
avg_generation_gap=None):
|
||||||
|
self.db = db
|
||||||
|
if max_sib_age_diff is None:
|
||||||
|
max_sib_age_diff = _MAX_SIB_AGE_DIFF
|
||||||
|
if max_age_prob_alive is None:
|
||||||
|
max_age_prob_alive = _MAX_AGE_PROB_ALIVE
|
||||||
|
if avg_generation_gap is None:
|
||||||
|
avg_generation_gap = _AVG_GENERATION_GAP
|
||||||
|
self.MAX_SIB_AGE_DIFF = max_sib_age_diff
|
||||||
|
self.MAX_AGE_PROB_ALIVE = max_age_prob_alive
|
||||||
|
self.AVG_GENERATION_GAP = avg_generation_gap
|
||||||
|
|
||||||
|
def probably_alive_range(self, person, is_spouse=False):
|
||||||
|
# FIXME: some of these computed dates need to be a span. For
|
||||||
|
# example, if a person could be born +/- 20 yrs around
|
||||||
|
# a date then it should be a span, and yr_offset should
|
||||||
|
# deal with it as well ("between 1920 and 1930" + 10 =
|
||||||
|
# "between 1930 and 1940")
|
||||||
|
if person is None:
|
||||||
|
return (None, None, "", None)
|
||||||
|
birth_ref = person.get_birth_ref()
|
||||||
|
death_ref = person.get_death_ref()
|
||||||
|
death_date = None
|
||||||
|
birth_date = None
|
||||||
|
explain = ""
|
||||||
|
# If the recorded death year is before current year then
|
||||||
|
# things are simple.
|
||||||
|
if death_ref and death_ref.get_role().is_primary():
|
||||||
|
if death_ref:
|
||||||
|
death = self.db.get_event_from_handle(death_ref.ref)
|
||||||
|
if death and death.get_date_object().get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
death_date = death.get_date_object()
|
||||||
|
|
||||||
|
# Look for Cause Of Death, Burial or Cremation events.
|
||||||
|
# These are fairly good indications that someone's not alive.
|
||||||
|
if not death_date:
|
||||||
|
for ev_ref in person.get_primary_event_ref_list():
|
||||||
|
if ev_ref:
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_death_fallback():
|
||||||
|
death_date = ev.get_date_object()
|
||||||
|
explain = _("death-related evidence")
|
||||||
|
|
||||||
|
# If they were born within X years before current year then
|
||||||
|
# assume they are alive (we already know they are not dead).
|
||||||
|
if not birth_date:
|
||||||
|
if birth_ref and birth_ref.get_role().is_primary():
|
||||||
|
birth = self.db.get_event_from_handle(birth_ref.ref)
|
||||||
|
if birth and birth.get_date_object().get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
birth_date = birth.get_date_object()
|
||||||
|
|
||||||
|
# Look for Baptism, etc events.
|
||||||
|
# These are fairly good indications that someone's birth.
|
||||||
|
if not birth_date:
|
||||||
|
for ev_ref in person.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth_fallback():
|
||||||
|
birth_date = ev.get_date_object()
|
||||||
|
explain = _("birth-related evidence")
|
||||||
|
|
||||||
|
if not birth_date and death_date:
|
||||||
|
# person died more than MAX after current year
|
||||||
|
birth_date = death_date.copy_offset_ymd(year=-self.MAX_AGE_PROB_ALIVE)
|
||||||
|
explain = _("death date")
|
||||||
|
|
||||||
|
if not death_date and birth_date:
|
||||||
|
# person died more than MAX after current year
|
||||||
|
death_date = birth_date.copy_offset_ymd(year=self.MAX_AGE_PROB_ALIVE)
|
||||||
|
explain = _("birth date")
|
||||||
|
|
||||||
|
if death_date and birth_date:
|
||||||
|
return (birth_date, death_date, explain, person) # direct self evidence
|
||||||
|
|
||||||
|
# Neither birth nor death events are available. Try looking
|
||||||
|
# at siblings. If a sibling was born more than X years past,
|
||||||
|
# or more than Z future, then probably this person is
|
||||||
|
# not alive. If the sibling died more than X years
|
||||||
|
# past, or more than X years future, then probably not alive.
|
||||||
|
|
||||||
|
family_list = person.get_parent_family_handle_list()
|
||||||
|
for family_handle in family_list:
|
||||||
|
family = self.db.get_family_from_handle(family_handle)
|
||||||
|
if family is None:
|
||||||
|
continue
|
||||||
|
for child_ref in family.get_child_ref_list():
|
||||||
|
child_handle = child_ref.ref
|
||||||
|
child = self.db.get_person_from_handle(child_handle)
|
||||||
|
if child is None:
|
||||||
|
continue
|
||||||
|
# Go through once looking for direct evidence:
|
||||||
|
for ev_ref in child.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
# if sibling birth date too far away, then not alive:
|
||||||
|
year = dobj.get_year()
|
||||||
|
if year != 0:
|
||||||
|
# sibling birth date
|
||||||
|
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF),
|
||||||
|
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("sibling birth date"),
|
||||||
|
child)
|
||||||
|
elif ev and ev.type.is_death():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
# if sibling death date too far away, then not alive:
|
||||||
|
year = dobj.get_year()
|
||||||
|
if year != 0:
|
||||||
|
# sibling death date
|
||||||
|
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE
|
||||||
|
+ self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("sibling death date"),
|
||||||
|
child)
|
||||||
|
# Go through again looking for fallback:
|
||||||
|
for ev_ref in child.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
# if sibling birth date too far away, then not alive:
|
||||||
|
year = dobj.get_year()
|
||||||
|
if year != 0:
|
||||||
|
# sibling birth date
|
||||||
|
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF),
|
||||||
|
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("sibling birth-related date"),
|
||||||
|
child)
|
||||||
|
elif ev and ev.type.is_death_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
# if sibling death date too far away, then not alive:
|
||||||
|
year = dobj.get_year()
|
||||||
|
if year != 0:
|
||||||
|
# sibling death date
|
||||||
|
return (gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
gen.lib.Date().copy_ymd(year - self.MAX_SIB_AGE_DIFF - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("sibling death-related date"),
|
||||||
|
child)
|
||||||
|
|
||||||
|
if not is_spouse: # if you are not in recursion, let's recurse:
|
||||||
|
for family_handle in person.get_family_handle_list():
|
||||||
|
family = self.db.get_family_from_handle(family_handle)
|
||||||
|
if family:
|
||||||
|
mother_handle = family.get_mother_handle()
|
||||||
|
father_handle = family.get_father_handle()
|
||||||
|
if mother_handle == person.handle and father_handle:
|
||||||
|
father = self.db.get_person_from_handle(father_handle)
|
||||||
|
date1, date2, explain, other = self.probably_alive_range(father, is_spouse=True)
|
||||||
|
if date1 and date2:
|
||||||
|
return date1, date2, _("a spouse, ") + explain, other
|
||||||
|
elif father_handle == person.handle and mother_handle:
|
||||||
|
mother = self.db.get_person_from_handle(mother_handle)
|
||||||
|
date1, date2, explain, other = self.probably_alive_range(mother, is_spouse=True)
|
||||||
|
if date1 and date2:
|
||||||
|
return date1, date2, _("a spouse, ") + explain, other
|
||||||
|
# Let's check the family events and see if we find something
|
||||||
|
for ref in family.get_event_ref_list():
|
||||||
|
if ref:
|
||||||
|
event = self.db.get_event_from_handle(ref.ref)
|
||||||
|
if event:
|
||||||
|
date = event.get_date_object()
|
||||||
|
year = date.get_year()
|
||||||
|
if year != 0:
|
||||||
|
other = None
|
||||||
|
if person.handle == mother_handle and father_handle:
|
||||||
|
other = self.db.get_person_from_handle(father_handle)
|
||||||
|
elif person.handle == father_handle and mother_handle:
|
||||||
|
other = self.db.get_person_from_handle(mother_handle)
|
||||||
|
return (gen.lib.Date().copy_ymd(year - self.AVG_GENERATION_GAP),
|
||||||
|
gen.lib.Date().copy_ymd(year - self.AVG_GENERATION_GAP +
|
||||||
|
self.MAX_AGE_PROB_ALIVE),
|
||||||
|
|
||||||
|
_("event with spouse"), other)
|
||||||
|
|
||||||
|
# Try looking for descendants that were born more than a lifespan
|
||||||
|
# ago.
|
||||||
|
|
||||||
|
def descendants_too_old (person, years):
|
||||||
|
for family_handle in person.get_family_handle_list():
|
||||||
|
family = self.db.get_family_from_handle(family_handle)
|
||||||
|
if not family:
|
||||||
|
# can happen with LivingProxyDb(PrivateProxyDb(db))
|
||||||
|
continue
|
||||||
|
for child_ref in family.get_child_ref_list():
|
||||||
|
child_handle = child_ref.ref
|
||||||
|
child = self.db.get_person_from_handle(child_handle)
|
||||||
|
child_birth_ref = child.get_birth_ref()
|
||||||
|
if child_birth_ref:
|
||||||
|
child_birth = self.db.get_event_from_handle(child_birth_ref.ref)
|
||||||
|
dobj = child_birth.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
d = gen.lib.Date(dobj)
|
||||||
|
val = d.get_start_date()
|
||||||
|
val = d.get_year() - years
|
||||||
|
d.set_year(val)
|
||||||
|
return (d, d.copy_offset_ymd(self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("descendant birth date"),
|
||||||
|
child)
|
||||||
|
child_death_ref = child.get_death_ref()
|
||||||
|
if child_death_ref:
|
||||||
|
child_death = self.db.get_event_from_handle(child_death_ref.ref)
|
||||||
|
dobj = child_death.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP),
|
||||||
|
dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("descendant death date"),
|
||||||
|
child)
|
||||||
|
date1, date2, explain, other = descendants_too_old (child, years + self.AVG_GENERATION_GAP)
|
||||||
|
if date1 and date2:
|
||||||
|
return date1, date2, explain, other
|
||||||
|
# Check fallback data:
|
||||||
|
for ev_ref in child.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
d = gen.lib.Date(dobj)
|
||||||
|
val = d.get_start_date()
|
||||||
|
val = d.get_year() - years
|
||||||
|
d.set_year(val)
|
||||||
|
return (d, d.copy_offset_ymd(self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("descendant birth-related date"),
|
||||||
|
child)
|
||||||
|
|
||||||
|
elif ev and ev.type.is_death_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP),
|
||||||
|
dobj.copy_offset_ymd(- self.AVG_GENERATION_GAP + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("descendant death-related date"),
|
||||||
|
child)
|
||||||
|
|
||||||
|
return (None, None, "", None)
|
||||||
|
|
||||||
|
# If there are descendants that are too old for the person to have
|
||||||
|
# been alive in the current year then they must be dead.
|
||||||
|
|
||||||
|
date1, date2, explain, other = None, None, "", None
|
||||||
|
try:
|
||||||
|
date1, date2, explain, other = descendants_too_old(person, self.AVG_GENERATION_GAP)
|
||||||
|
except RuntimeError:
|
||||||
|
raise DatabaseError(
|
||||||
|
_("Database error: %s is defined as his or her own ancestor") %
|
||||||
|
name_displayer.display(person))
|
||||||
|
|
||||||
|
if date1 and date2:
|
||||||
|
return (date1, date2, explain, other)
|
||||||
|
|
||||||
|
def ancestors_too_old(person, year):
|
||||||
|
family_handle = person.get_main_parents_family_handle()
|
||||||
|
if family_handle:
|
||||||
|
family = self.db.get_family_from_handle(family_handle)
|
||||||
|
if not family:
|
||||||
|
# can happen with LivingProxyDb(PrivateProxyDb(db))
|
||||||
|
return (None, None, "", None)
|
||||||
|
father_handle = family.get_father_handle()
|
||||||
|
if father_handle:
|
||||||
|
father = self.db.get_person_from_handle(father_handle)
|
||||||
|
father_birth_ref = father.get_birth_ref()
|
||||||
|
if father_birth_ref and father_birth_ref.get_role().is_primary():
|
||||||
|
father_birth = self.db.get_event_from_handle(
|
||||||
|
father_birth_ref.ref)
|
||||||
|
dobj = father_birth.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year),
|
||||||
|
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor birth date"),
|
||||||
|
father)
|
||||||
|
father_death_ref = father.get_death_ref()
|
||||||
|
if father_death_ref and father_death_ref.get_role().is_primary():
|
||||||
|
father_death = self.db.get_event_from_handle(
|
||||||
|
father_death_ref.ref)
|
||||||
|
dobj = father_death.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor death date"),
|
||||||
|
father)
|
||||||
|
|
||||||
|
# Check fallback data:
|
||||||
|
for ev_ref in father.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year),
|
||||||
|
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor birth-related date"),
|
||||||
|
father)
|
||||||
|
|
||||||
|
elif ev and ev.type.is_death_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor death-related date"),
|
||||||
|
father)
|
||||||
|
|
||||||
|
date1, date2, explain, other = ancestors_too_old (father, year - self.AVG_GENERATION_GAP)
|
||||||
|
if date1 and date2:
|
||||||
|
return date1, date2, explain, other
|
||||||
|
|
||||||
|
mother_handle = family.get_mother_handle()
|
||||||
|
if mother_handle:
|
||||||
|
mother = self.db.get_person_from_handle(mother_handle)
|
||||||
|
mother_birth_ref = mother.get_birth_ref()
|
||||||
|
if mother_birth_ref and mother_birth_ref.get_role().is_primary():
|
||||||
|
mother_birth = self.db.get_event_from_handle(mother_birth_ref.ref)
|
||||||
|
dobj = mother_birth.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year),
|
||||||
|
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor birth date"),
|
||||||
|
mother)
|
||||||
|
mother_death_ref = mother.get_death_ref()
|
||||||
|
if mother_death_ref and mother_death_ref.get_role().is_primary():
|
||||||
|
mother_death = self.db.get_event_from_handle(
|
||||||
|
mother_death_ref.ref)
|
||||||
|
dobj = mother_death.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor death date"),
|
||||||
|
mother)
|
||||||
|
|
||||||
|
# Check fallback data:
|
||||||
|
for ev_ref in mother.get_primary_event_ref_list():
|
||||||
|
ev = self.db.get_event_from_handle(ev_ref.ref)
|
||||||
|
if ev and ev.type.is_birth_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year),
|
||||||
|
dobj.copy_offset_ymd(- year + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor birth-related date"),
|
||||||
|
mother)
|
||||||
|
|
||||||
|
elif ev and ev.type.is_death_fallback():
|
||||||
|
dobj = ev.get_date_object()
|
||||||
|
if dobj.get_start_date() != gen.lib.Date.EMPTY:
|
||||||
|
return (dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE),
|
||||||
|
dobj.copy_offset_ymd(- year - self.MAX_AGE_PROB_ALIVE + self.MAX_AGE_PROB_ALIVE),
|
||||||
|
_("ancestor death-related date"),
|
||||||
|
mother)
|
||||||
|
|
||||||
|
date1, date2, explain, other = ancestors_too_old (mother, year - self.AVG_GENERATION_GAP)
|
||||||
|
if date1 and date2:
|
||||||
|
return (date1, date2, explain, other)
|
||||||
|
|
||||||
|
return (None, None, "", None)
|
||||||
|
|
||||||
|
# If there are ancestors that would be too old in the current year
|
||||||
|
# then assume our person must be dead too.
|
||||||
|
date1, date2, explain, other = ancestors_too_old (person, - self.AVG_GENERATION_GAP)
|
||||||
|
if date1 and date2:
|
||||||
|
return (date1, date2, explain, other)
|
||||||
|
|
||||||
|
# If we can't find any reason to believe that they are dead we
|
||||||
|
# must assume they are alive.
|
||||||
|
|
||||||
|
return (None, None, "", None)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# probably_alive
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
def probably_alive(person, db,
|
||||||
|
current_date=None,
|
||||||
|
limit=0,
|
||||||
|
max_sib_age_diff=None,
|
||||||
|
max_age_prob_alive=None,
|
||||||
|
avg_generation_gap=None,
|
||||||
|
return_range=False):
|
||||||
|
"""
|
||||||
|
Return true if the person may be alive on current_date.
|
||||||
|
|
||||||
|
This works by a process of elimination. If we can't find a good
|
||||||
|
reason to believe that someone is dead then we assume they must
|
||||||
|
be alive.
|
||||||
|
|
||||||
|
:param current_date: a date object that is not estimated or modified
|
||||||
|
(defaults to today)
|
||||||
|
:param limit: number of years to check beyond death_date
|
||||||
|
:param max_sib_age_diff: maximum sibling age difference, in years
|
||||||
|
:param max_age_prob_alive: maximum age of a person, in years
|
||||||
|
:param avg_generation_gap: average generation gap, in years
|
||||||
|
"""
|
||||||
|
# First, get the real database to use all people
|
||||||
|
# for determining alive status:
|
||||||
|
basedb = db.basedb
|
||||||
|
# Now, we create a wrapper for doing work:
|
||||||
|
pb = ProbablyAlive(basedb, max_sib_age_diff,
|
||||||
|
max_age_prob_alive,
|
||||||
|
avg_generation_gap)
|
||||||
|
birth, death, explain, relative = pb.probably_alive_range(person)
|
||||||
|
if current_date is None:
|
||||||
|
current_date = gen.lib.date.Today()
|
||||||
|
if not birth or not death:
|
||||||
|
# no evidence, must consider alive
|
||||||
|
return (True, None, None, _("no evidence"), None)
|
||||||
|
# must have dates from here:
|
||||||
|
if limit:
|
||||||
|
death += limit # add these years to death
|
||||||
|
# Finally, check to see if current_date is between dates
|
||||||
|
result = (current_date.match(birth, ">=") and
|
||||||
|
current_date.match(death, "<="))
|
||||||
|
if return_range:
|
||||||
|
return (result, birth, death, explain, relative)
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
|
||||||
|
def probably_alive_range(person, db,
|
||||||
|
max_sib_age_diff=None,
|
||||||
|
max_age_prob_alive=None,
|
||||||
|
avg_generation_gap=None):
|
||||||
|
"""
|
||||||
|
Computes estimated birth and death dates.
|
||||||
|
Returns: (birth_date, death_date, explain_text, related_person)
|
||||||
|
"""
|
||||||
|
# First, find the real database to use all people
|
||||||
|
# for determining alive status:
|
||||||
|
from gen.proxy.proxybase import ProxyDbBase
|
||||||
|
basedb = db
|
||||||
|
while isinstance(basedb, ProxyDbBase):
|
||||||
|
basedb = basedb.db
|
||||||
|
# Now, we create a wrapper for doing work:
|
||||||
|
pb = ProbablyAlive(basedb, max_sib_age_diff,
|
||||||
|
max_age_prob_alive, avg_generation_gap)
|
||||||
|
return pb.probably_alive_range(person)
|
||||||
|
|
||||||
|
def update_constants():
|
||||||
|
"""
|
||||||
|
Used to update the constants that are cached in this module.
|
||||||
|
"""
|
||||||
|
from gen.config import config
|
||||||
|
global _MAX_AGE_PROB_ALIVE, _MAX_SIB_AGE_DIFF, _AVG_GENERATION_GAP
|
||||||
|
_MAX_AGE_PROB_ALIVE = config.get('behavior.max-age-prob-alive')
|
||||||
|
_MAX_SIB_AGE_DIFF = config.get('behavior.max-sib-age-diff')
|
||||||
|
_AVG_GENERATION_GAP = config.get('behavior.avg-generation-gap')
|
@ -46,7 +46,7 @@ from gen.plug.menu import (BooleanOption, EnumeratedListOption,
|
|||||||
from gen.plug.report import Report
|
from gen.plug.report import Report
|
||||||
from gen.plug.report import utils as ReportUtils
|
from gen.plug.report import utils as ReportUtils
|
||||||
from gen.plug.report import MenuReportOptions
|
from gen.plug.report import MenuReportOptions
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
|
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
@ -48,7 +48,7 @@ from gen.plug.menu import (BooleanOption, StringOption, NumberOption,
|
|||||||
from gen.plug.report import Report
|
from gen.plug.report import Report
|
||||||
from gen.plug.report import utils as ReportUtils
|
from gen.plug.report import utils as ReportUtils
|
||||||
from gen.plug.report import MenuReportOptions
|
from gen.plug.report import MenuReportOptions
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
from gen.datehandler import displayer as _dd
|
from gen.datehandler import displayer as _dd
|
||||||
import gen.locale
|
import gen.locale
|
||||||
import gen.lib
|
import gen.lib
|
||||||
|
@ -45,7 +45,7 @@ log = logging.getLogger(".WriteFtree")
|
|||||||
# GRAMPS modules
|
# GRAMPS modules
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
import Utils
|
from gen.utils.alive import probably_alive
|
||||||
from gui.plug.export import WriterOptionBox
|
from gui.plug.export import WriterOptionBox
|
||||||
from gui.glade import Glade
|
from gui.glade import Glade
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ class FtreeWriter(object):
|
|||||||
death = None
|
death = None
|
||||||
|
|
||||||
#if self.restrict:
|
#if self.restrict:
|
||||||
# alive = Utils.probably_alive(p, self.db)
|
# alive = probably_alive(p, self.db)
|
||||||
#else:
|
#else:
|
||||||
# alive = 0
|
# alive = 0
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ log = logging.getLogger(".WriteGeneWeb")
|
|||||||
import gen.lib
|
import gen.lib
|
||||||
from gui.plug.export import WriterOptionBox
|
from gui.plug.export import WriterOptionBox
|
||||||
#import const
|
#import const
|
||||||
import Utils
|
from gen.utils.alive import probably_alive
|
||||||
from gui.glade import Glade
|
from gui.glade import Glade
|
||||||
from gen.config import config
|
from gen.config import config
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ class GeneWebWriter(object):
|
|||||||
place = self.db.get_place_from_handle(place_handle)
|
place = self.db.get_place_from_handle(place_handle)
|
||||||
b_place = place.get_title()
|
b_place = place.get_title()
|
||||||
|
|
||||||
if Utils.probably_alive(person,self.db):
|
if probably_alive(person,self.db):
|
||||||
d_date = ""
|
d_date = ""
|
||||||
else:
|
else:
|
||||||
d_date = "0"
|
d_date = "0"
|
||||||
@ -334,7 +334,7 @@ class GeneWebWriter(object):
|
|||||||
#missing_surname = config.get("preferences.no-surname-text")
|
#missing_surname = config.get("preferences.no-surname-text")
|
||||||
surname = self.rem_spaces( person.get_primary_name().get_surname())
|
surname = self.rem_spaces( person.get_primary_name().get_surname())
|
||||||
#firstname = config.get('preferences.private-given-text')
|
#firstname = config.get('preferences.private-given-text')
|
||||||
#if not (Utils.probably_alive(person,self.db) and \
|
#if not (probably_alive(person,self.db) and \
|
||||||
# self.restrict and self.living):
|
# self.restrict and self.living):
|
||||||
firstname = self.rem_spaces( person.get_primary_name().get_first_name())
|
firstname = self.rem_spaces( person.get_primary_name().get_first_name())
|
||||||
if person.get_handle() not in self.person_ids:
|
if person.get_handle() not in self.person_ids:
|
||||||
@ -346,7 +346,7 @@ class GeneWebWriter(object):
|
|||||||
#missing_first_name = config.get("preferences.no-given-text")
|
#missing_first_name = config.get("preferences.no-given-text")
|
||||||
surname = self.rem_spaces( person.get_primary_name().get_surname())
|
surname = self.rem_spaces( person.get_primary_name().get_surname())
|
||||||
#firstname = config.get('preferences.private-given-text')
|
#firstname = config.get('preferences.private-given-text')
|
||||||
#if not (Utils.probably_alive(person,self.db) and \
|
#if not (probably_alive(person,self.db) and \
|
||||||
# self.restrict and self.living):
|
# self.restrict and self.living):
|
||||||
firstname = self.rem_spaces( person.get_primary_name().get_first_name())
|
firstname = self.rem_spaces( person.get_primary_name().get_first_name())
|
||||||
if person.get_handle() not in self.person_ids:
|
if person.get_handle() not in self.person_ids:
|
||||||
|
@ -40,7 +40,7 @@ from gen.lib.eventroletype import EventRoleType
|
|||||||
from gen.lib.eventtype import EventType
|
from gen.lib.eventtype import EventType
|
||||||
from gen.lib.familyreltype import FamilyRelType
|
from gen.lib.familyreltype import FamilyRelType
|
||||||
from gen.display.name import displayer as _nd
|
from gen.display.name import displayer as _nd
|
||||||
import Utils
|
from gen.utils.alive import probably_alive
|
||||||
from gen.plug.report import utils as ReportUtils
|
from gen.plug.report import utils as ReportUtils
|
||||||
from libtranslate import Translator
|
from libtranslate import Translator
|
||||||
|
|
||||||
@ -2186,7 +2186,7 @@ class Narrator(object):
|
|||||||
'unknown_gender_name' : self.__first_name,
|
'unknown_gender_name' : self.__first_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
dead = not Utils.probably_alive(self.__person, self.__db)
|
dead = not probably_alive(self.__person, self.__db)
|
||||||
|
|
||||||
if not self.__first_name_used:
|
if not self.__first_name_used:
|
||||||
index = _NAME_INDEX_INCLUDE_NAME
|
index = _NAME_INDEX_INCLUDE_NAME
|
||||||
|
@ -29,7 +29,7 @@ Display references for any object
|
|||||||
|
|
||||||
from gen.simple import SimpleAccess, SimpleDoc
|
from gen.simple import SimpleAccess, SimpleDoc
|
||||||
from gui.plug.quick import QuickTable
|
from gui.plug.quick import QuickTable
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
from gen.ggettext import gettext as _
|
from gen.ggettext import gettext as _
|
||||||
import gen.datehandler
|
import gen.datehandler
|
||||||
import gen.lib
|
import gen.lib
|
||||||
|
@ -49,7 +49,7 @@ from gen.plug.menu import (BooleanOption, StringOption, NumberOption,
|
|||||||
from gen.plug.report import Report
|
from gen.plug.report import Report
|
||||||
from gen.plug.report import utils as ReportUtils
|
from gen.plug.report import utils as ReportUtils
|
||||||
from gen.plug.report import MenuReportOptions
|
from gen.plug.report import MenuReportOptions
|
||||||
from Utils import probably_alive
|
from gen.utils.alive import probably_alive
|
||||||
import gen.locale
|
import gen.locale
|
||||||
from gen.datehandler import displayer as _dd
|
from gen.datehandler import displayer as _dd
|
||||||
|
|
||||||
|
@ -51,8 +51,9 @@ import gen.lib
|
|||||||
from gui.views.navigationview import NavigationView
|
from gui.views.navigationview import NavigationView
|
||||||
from gui.editors import FilterEditor
|
from gui.editors import FilterEditor
|
||||||
from gen.display.name import displayer as name_displayer
|
from gen.display.name import displayer as name_displayer
|
||||||
from Utils import (media_path_full, probably_alive, find_children,
|
from gen.utils.alive import probably_alive
|
||||||
find_parents, find_witnessed_people)
|
from Utils import (media_path_full, find_children, find_parents,
|
||||||
|
find_witnessed_people)
|
||||||
from libformatting import FormattingHelper
|
from libformatting import FormattingHelper
|
||||||
from gui.thumbnails import get_thumbnail_path
|
from gui.thumbnails import get_thumbnail_path
|
||||||
from gen.errors import WindowActiveError
|
from gen.errors import WindowActiveError
|
||||||
|
@ -52,7 +52,8 @@ from gui.views.navigationview import NavigationView
|
|||||||
from gui.editors import EditPerson, EditFamily
|
from gui.editors import EditPerson, EditFamily
|
||||||
from gui.editors import FilterEditor
|
from gui.editors import FilterEditor
|
||||||
from gen.display.name import displayer as name_displayer
|
from gen.display.name import displayer as name_displayer
|
||||||
from Utils import media_path_full, probably_alive
|
from Utils import media_path_full
|
||||||
|
from gen.utils.alive import probably_alive
|
||||||
from gui.utils import open_file_with_default_application
|
from gui.utils import open_file_with_default_application
|
||||||
import gen.datehandler
|
import gen.datehandler
|
||||||
from gui.thumbnails import get_thumbnail_image
|
from gui.thumbnails import get_thumbnail_image
|
||||||
|
@ -105,6 +105,7 @@ from libhtml import Html
|
|||||||
from libhtmlbackend import HtmlBackend, process_spaces
|
from libhtmlbackend import HtmlBackend, process_spaces
|
||||||
|
|
||||||
from libgedcom import make_gedcom_date
|
from libgedcom import make_gedcom_date
|
||||||
|
from gen.utils.alive import probably_alive
|
||||||
from gen.utils.place import conv_lat_lon
|
from gen.utils.place import conv_lat_lon
|
||||||
from gui.pluginmanager import GuiPluginManager
|
from gui.pluginmanager import GuiPluginManager
|
||||||
|
|
||||||
@ -6119,7 +6120,7 @@ class IndividualPage(BasePage):
|
|||||||
birth_date = birth.get_date_object()
|
birth_date = birth.get_date_object()
|
||||||
|
|
||||||
if birth_date and birth_date is not gen.lib.Date.EMPTY:
|
if birth_date and birth_date is not gen.lib.Date.EMPTY:
|
||||||
alive = Utils.probably_alive(self.person, self.dbase_, date.Today() )
|
alive = probably_alive(self.person, self.dbase_, date.Today() )
|
||||||
|
|
||||||
death_date = _find_death_date(self.dbase_, self.person)
|
death_date = _find_death_date(self.dbase_, self.person)
|
||||||
if not alive and death_date is not None:
|
if not alive and death_date is not None:
|
||||||
|
@ -59,7 +59,8 @@ from gen.plug.menu import BooleanOption, NumberOption, StringOption, \
|
|||||||
EnumeratedListOption, FilterOption, PersonOption, \
|
EnumeratedListOption, FilterOption, PersonOption, \
|
||||||
DestinationOption, NoteOption
|
DestinationOption, NoteOption
|
||||||
import gen.locale
|
import gen.locale
|
||||||
from Utils import probably_alive, xml_lang, get_researcher
|
from Utils import xml_lang, get_researcher
|
||||||
|
from gen.utils.alive import probably_alive
|
||||||
from gen.datehandler import displayer as _dd
|
from gen.datehandler import displayer as _dd
|
||||||
|
|
||||||
from gen.display.name import displayer as _nd
|
from gen.display.name import displayer as _nd
|
||||||
|
@ -57,7 +57,7 @@ from webapp.dbdjango import DbDjango
|
|||||||
#
|
#
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
from gen.simple import SimpleTable, SimpleAccess, make_basic_stylesheet
|
from gen.simple import SimpleTable, SimpleAccess, make_basic_stylesheet
|
||||||
import Utils
|
from gen.utils.alive import probably_alive as alive
|
||||||
from gen.dbstate import DbState
|
from gen.dbstate import DbState
|
||||||
from gen.datehandler import displayer, parser
|
from gen.datehandler import displayer, parser
|
||||||
from gen.lib.date import Date as GDate, Today
|
from gen.lib.date import Date as GDate, Today
|
||||||
@ -140,7 +140,7 @@ def get_person_from_handle(db, handle):
|
|||||||
def probably_alive(handle):
|
def probably_alive(handle):
|
||||||
return False
|
return False
|
||||||
person = db.get_person_from_handle(handle)
|
person = db.get_person_from_handle(handle)
|
||||||
return Utils.probably_alive(person, db)
|
return alive(person, db)
|
||||||
|
|
||||||
def format_number(number, with_grouping=True):
|
def format_number(number, with_grouping=True):
|
||||||
# FIXME: should be user's setting
|
# FIXME: should be user's setting
|
||||||
|
Reference in New Issue
Block a user