probably_alive and CalcEstDates now share same codebase; rewritten probably_alive
svn: r14102
This commit is contained in:
		
							
								
								
									
										394
									
								
								src/Utils.py
									
									
									
									
									
								
							
							
						
						
									
										394
									
								
								src/Utils.py
									
									
									
									
									
								
							| @@ -452,12 +452,401 @@ 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,  | ||||||
|  |                  max_age_prob_alive,  | ||||||
|  |                  avg_generation_gap): | ||||||
|  |         self.db = db | ||||||
|  |         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): | ||||||
|  |         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(): | ||||||
|  |             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(): | ||||||
|  |                 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, "") # 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) | ||||||
|  |             for child_ref in family.get_child_ref_list(): | ||||||
|  |                 child_handle = child_ref.ref | ||||||
|  |                 child = self.db.get_person_from_handle(child_handle) | ||||||
|  |                 # 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), | ||||||
|  |                                         _("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), | ||||||
|  |                                         _("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 | ||||||
|  |  | ||||||
|  |         # 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) | ||||||
|  |                 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), | ||||||
|  |                                     _("descendent 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), | ||||||
|  |                                     _("descendent 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), | ||||||
|  |                                         _("descendent 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), | ||||||
|  |                                         _("descendent 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 Errors.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) | ||||||
|  |                 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 | # probably_alive | ||||||
| # | # | ||||||
| #------------------------------------------------------------------------- | #------------------------------------------------------------------------- | ||||||
| def probably_alive(person, db, current_date=None, limit=0): | 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 true if the person may be alive on current_date. | ||||||
|  |  | ||||||
|  |     This works by a process of emlimination. 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 | ||||||
|  |     """ | ||||||
|  |     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 | ||||||
|  |     pb = ProbablyAlive(db, max_sib_age_diff,  | ||||||
|  |                        max_age_prob_alive, avg_generation_gap) | ||||||
|  |     birth, death, explain, relative = pb.probably_alive_range(person, db) | ||||||
|  |     if death is None: # no evidence... can't say if alive/dead | ||||||
|  |         return True | ||||||
|  |     # must have est dates | ||||||
|  |     if current_date: # date in which to consider alive | ||||||
|  |         # SPECIAL CASE: Today: | ||||||
|  |         if current_date.match(gen.lib.date.Today(), "=="): | ||||||
|  |             if person.get_death_ref(): | ||||||
|  |                 return False | ||||||
|  |         if limit: | ||||||
|  |             death += limit # add these years to death | ||||||
|  |         if death.match(gen.lib.date.Today(), ">"): | ||||||
|  |             return True | ||||||
|  |         result = (current_date.match(birth, ">=") and  | ||||||
|  |                   current_date.match(death, "<=")) | ||||||
|  |         return result | ||||||
|  |     # else, they have a est death date, and no current_date give, thus dead | ||||||
|  |     return False | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  |     """ | ||||||
|  |     pb = ProbablyAlive(db, max_sib_age_diff,  | ||||||
|  |                        max_age_prob_alive, avg_generation_gap) | ||||||
|  |     return pb.probably_alive_range(person, db) | ||||||
|  |  | ||||||
|  | def probably_alive_old(person, db, current_date=None, limit=0): | ||||||
|     """Return true if the person may be alive on current_date. |     """Return true if the person may be alive on current_date. | ||||||
|  |  | ||||||
|     This works by a process of emlimination. If we can't find a good |     This works by a process of emlimination. If we can't find a good | ||||||
| @@ -468,8 +857,7 @@ def probably_alive(person, db, current_date=None, limit=0): | |||||||
|                    (defaults to today) |                    (defaults to today) | ||||||
|     :param limit:  number of years to check beyond death_date |     :param limit:  number of years to check beyond death_date | ||||||
|     """ |     """ | ||||||
|     today = gen.lib.Date() |     today = gen.lib.date.Today() | ||||||
|     today.set_yr_mon_day(*time.localtime(time.time())[0:3]) |  | ||||||
|     if current_date is None: |     if current_date is None: | ||||||
|         current_date = today |         current_date = today | ||||||
|     # Are we checking to see if they are alive right now? |     # Are we checking to see if they are alive right now? | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ from ReportBase import ReportUtils | |||||||
| from docgen import TextBufDoc | from docgen import TextBufDoc | ||||||
| from Simple import make_basic_stylesheet, SimpleAccess, SimpleDoc, SimpleTable | from Simple import make_basic_stylesheet, SimpleAccess, SimpleDoc, SimpleTable | ||||||
| from QuestionDialog import QuestionDialog | from QuestionDialog import QuestionDialog | ||||||
| from Utils import create_id | from Utils import create_id, probably_alive_range | ||||||
| import DateHandler | import DateHandler | ||||||
|  |  | ||||||
| #------------------------------------------------------------------------ | #------------------------------------------------------------------------ | ||||||
| @@ -509,317 +509,9 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): | |||||||
|         self.db.add_event(event, self.trans) |         self.db.add_event(event, self.trans) | ||||||
|         return event |         return event | ||||||
|  |  | ||||||
|     def calc_estimates(self, person, is_spouse=False): |     def calc_estimates(self, person): | ||||||
|         if person is None: |         return probably_alive_range(person, self.db, | ||||||
|             return (None, None, "", None) |                          self.MAX_SIB_AGE_DIFF,  | ||||||
|         birth_ref = person.get_birth_ref() |                          self.MAX_AGE_PROB_ALIVE,  | ||||||
|         death_ref = person.get_death_ref() |                          self.AVG_GENERATION_GAP) | ||||||
|         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(): |  | ||||||
|             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(): |  | ||||||
|                 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, "") # 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) |  | ||||||
|             for child_ref in family.get_child_ref_list(): |  | ||||||
|                 child_handle = child_ref.ref |  | ||||||
|                 child = self.db.get_person_from_handle(child_handle) |  | ||||||
|                 # 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), |  | ||||||
|                                         _("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), |  | ||||||
|                                         _("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.calc_estimates(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.calc_estimates(mother, is_spouse=True) |  | ||||||
|                         if date1 and date2: |  | ||||||
|                             return date1, date2, _("a spouse, ") + explain, 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) |  | ||||||
|                 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), |  | ||||||
|                                     _("descendent 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), |  | ||||||
|                                     _("descendent 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), |  | ||||||
|                                         _("descendent 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), |  | ||||||
|                                         _("descendent 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 Errors.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) |  | ||||||
|                 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) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user