diff --git a/src/plugins/tool/CalculateEstimatedDates.py b/src/plugins/tool/CalculateEstimatedDates.py index 4fdcf6849..1935a6b59 100644 --- a/src/plugins/tool/CalculateEstimatedDates.py +++ b/src/plugins/tool/CalculateEstimatedDates.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # # Gramps - a GTK+/GNOME based genealogy program # @@ -38,12 +39,17 @@ import time #------------------------------------------------------------------------ from PluginUtils import Tool, PluginWindows, MenuToolOptions from gen.plug.menu import BooleanOption, NumberOption, StringOption, \ - FilterOption, PersonOption + FilterOption, PersonOption, EnumeratedListOption import gen.lib import config from BasicUtils import name_displayer import Errors from ReportBase import ReportUtils +from docgen import TextBufDoc +from Simple import make_basic_stylesheet, SimpleAccess, SimpleDoc, SimpleTable +from QuestionDialog import QuestionDialog +from Utils import create_id +import DateHandler #------------------------------------------------------------------------ # @@ -54,7 +60,11 @@ class CalcEstDateOptions(MenuToolOptions): """ Calculate Estimated Date options """ def __init__(self, name, person_id=None, dbstate=None): self.__db = dbstate.get_database() + self.__dbstate = dbstate MenuToolOptions.__init__(self, name, person_id, dbstate) + + def get_dbstate(self): + return self.__dbstate def add_menu_options(self, menu): @@ -78,24 +88,25 @@ class CalcEstDateOptions(MenuToolOptions): source_text.set_help(_("Source to remove and/or add")) menu.add_option(category_name, "source_text", source_text) - remove = BooleanOption(_("Remove previously added dates"), True) - remove.set_help(_("Remove")) + remove = BooleanOption(_("Remove previously added events, notes, and source"), True) + remove.set_help(_("Remove calculated events, notes, and source; occurs immediately on Execute")) menu.add_option(category_name, "remove", remove) - birth = BooleanOption(_("Add estimated birth dates"), True) - birth.set_help(_("Add")) + birth = EnumeratedListOption(_("Birth"), 0) + birth.add_item(0, _("Do not add birth events")) + birth.add_item(1, _("Add birth events without dates")) + birth.add_item(2, _("Add birth events with dates")) + birth.set_help( _("Add a birth events with or without estimated dates")) menu.add_option(category_name, "add_birth", birth) - death = BooleanOption(_("Add estimated death dates"), True) - death.set_help(_("Add estimated death dates")) + death = EnumeratedListOption(_("Death"), 0) + death.add_item(0, _("Do not add death events")) + death.add_item(1, _("Add death events without dates")) + death.add_item(2, _("Add death events with dates")) + death.set_help( _("Add death events with or without estimated dates")) menu.add_option(category_name, "add_death", death) - display_details = BooleanOption(_("Display detailed results"), False) - display_details.set_help(_("Show details for every date entered")) - menu.add_option(category_name, "display_details", display_details) - # ----------------------------------------------------- - category_name = _("Config") num = NumberOption(_("Maximum age"), config.get('behavior.max-age-prob-alive'), 0, 200) @@ -145,34 +156,91 @@ class CalcEstDateOptions(MenuToolOptions): class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): + def __init__(self, *args, **kwargs): + PluginWindows.ToolManagedWindowBatch.__init__(self, *args, **kwargs) + self.help_page = self.add_page("Help") + self.write_to_page(self.help_page, + "The Calculate Estimated Dates Tool is used to add and remove " + "birth and death events for people that are missing these " + "events.\n\n" + "To use:\n" + "1. Go to the Options tab\n" + "2. Check the [ ] Remove option to remove previous estimates\n" + "3. Select the Add date options to date events with or without dates\n" + "4. Click on Execute\n" + "5. Select the people with which to add events\n" + "6. Click on 'Add Selected Events' button to create\n\n" + "NOTES: if you decide to make an event permanent, remove it from " + "the Source. Otherwise, it will get removed the next time you " + "automatically remove these events.\n\n" + "You may have to run the tool repeatedly (without removing previous " + "events) to add all of the events possible.") + def get_title(self): return _("Calculate Estimated Dates") def initial_frame(self): return _("Options") + def set_reselect(self): + self.reselect = True + def run(self): - self.add_results_frame(_("Results")) + BUTTONS = ((_("Select All"), self.select_all), + (_("Select None"), self.select_none), + (_("Toggle Selection"), self.toggle_select), + (_("Add Selected Events"), self.apply_selection), + ) + + if hasattr(self, "table") and self.table: + self.reselect = False + if self.options.handler.options_dict['remove']: + QuestionDialog(_("Remove Events, Notes, and Source and Reselect Data"), + _("Are you sure you want to remove previous events, notes, and source and reselect data?"), + _("Remove and Run Select Again"), + self.set_reselect, + self.window) + else: + QuestionDialog(_("Reselect Data"), + _("Are you sure you want to reselect data?"), + _("Run Select Again"), + self.set_reselect, + self.window) + if not self.reselect: + return + self.action = {} + widget = self.add_results_frame(_("Select")) + document = TextBufDoc(make_basic_stylesheet(), None) + document.dbstate = self.dbstate + document.uistate = self.uistate + document.open("", container=widget) + self.sdb = SimpleAccess(self.db) + sdoc = SimpleDoc(document) + stab = SimpleTable(self.sdb) + self.table = stab + stab.columns(_("Select"), _("Person"), _("Action"), + _("Birth Date"), _("Death Date"), + _("Evidence"), _("Relative")) self.results_write(_("Processing...\n")) - self.trans = self.db.transaction_begin("",batch=True) - self.db.disable_signals() self.filter_option = self.options.menu.get_option_by_name('filter') self.filter = self.filter_option.get_filter() # the actual filter people = self.filter.apply(self.db, self.db.iter_person_handles()) num_people = self.db.get_number_of_people() source_text = self.options.handler.options_dict['source_text'] + source = None add_birth = self.options.handler.options_dict['add_birth'] add_death = self.options.handler.options_dict['add_death'] remove_old = self.options.handler.options_dict['remove'] - display_details = self.options.handler.options_dict['display_details'] self.MIN_GENERATION_YEARS = self.options.handler.options_dict['MIN_GENERATION_YEARS'] self.MAX_SIB_AGE_DIFF = self.options.handler.options_dict['MAX_SIB_AGE_DIFF'] self.MAX_AGE_PROB_ALIVE = self.options.handler.options_dict['MAX_AGE_PROB_ALIVE'] self.AVG_GENERATION_GAP = self.options.handler.options_dict['AVG_GENERATION_GAP'] if remove_old: - self.results_write(_("Replacing...\n")) + self.trans = self.db.transaction_begin("",batch=True) + self.db.disable_signals() + self.results_write(_("Removing old estimations... ")) self.progress.set_pass((_("Removing '%s'...") % source_text), num_people) for person_handle in people: @@ -191,6 +259,12 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): if source.get_title() == source_text: person.set_birth_ref(None) person.remove_handle_references('Event',[birth_ref.ref]) + # remove note + note_list = birth.get_referenced_note_handles() + birth.remove_handle_references('Note', + [note_handle for (obj_type, note_handle) in note_list]) + for (obj_type, note_handle) in note_list: + self.db.remove_note(note_handle, self.trans) self.db.remove_event(birth_ref.ref, self.trans) self.db.commit_source(source, self.trans) pupdate = 1 @@ -207,64 +281,197 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): if source.get_title() == source_text: person.set_death_ref(None) person.remove_handle_references('Event',[death_ref.ref]) + # remove note + note_list = death.get_referenced_note_handles() + birth.remove_handle_references('Note', + [note_handle for (obj_type, note_handle) in note_list]) + for (obj_type, note_handle) in note_list: + self.db.remove_note(note_handle, self.trans) self.db.remove_event(death_ref.ref, self.trans) self.db.commit_source(source, self.trans) pupdate = 1 break if pupdate == 1: self.db.commit_person(person, self.trans) + if source: + self.db.remove_source(source.handle, self.trans) + self.results_write(_("done!\n")) + self.db.transaction_commit(self.trans, _("Removed date estimates")) + self.db.enable_signals() + self.db.request_rebuild() if add_birth or add_death: - self.results_write(_("Calculating...\n")) - self.progress.set_pass(_('Calculating estimated dates...'), + self.results_write(_("Selecting... \n\n")) + self.progress.set_pass(_('Selecting...'), num_people) - source = self.get_or_create_source(source_text) + row = 0 for person_handle in people: self.progress.step() - added_birth, added_death = 0, 0 person = self.db.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() death_ref = person.get_death_ref() - #print birth_ref, death_ref - date1, date2 = self.calc_estimates(person, birth_ref, death_ref) - #print date1, date2 - if not birth_ref and add_birth and date1: - #print "added birth" - birth = self.create_event(_("Estimated birth date"), - gen.lib.EventType.BIRTH, - date1, source) - event_ref = gen.lib.EventRef() - event_ref.set_reference_handle(birth.get_handle()) - person.set_birth_ref(event_ref) - self.db.commit_person(person, self.trans) - added_birth = 1 - if not death_ref and add_death and date2: - current_date = gen.lib.Date() - current_date.set_yr_mon_day(*time.localtime(time.time())[0:3]) - if current_date.match( date2, "<<"): - # don't add events in the future! - pass # FIXME: sometimes adds one in future? + add_birth_event, add_death_event = False, False + if not birth_ref or not death_ref: + date1, date2, explain, other = self.calc_estimates(person) + if birth_ref: + ev = self.db.get_event_from_handle(birth_ref.ref) + date1 = ev.get_date_object() + elif not birth_ref and add_birth and date1: + add_birth_event = True + date1.make_vague() else: - #print "added death" - death = self.create_event(_("Estimated death date"), - gen.lib.EventType.DEATH, - date2, source) - event_ref = gen.lib.EventRef() - event_ref.set_reference_handle(death.get_handle()) - person.set_death_ref(event_ref) - self.db.commit_person(person, self.trans) - added_death = 1 - if (added_birth or added_death) and display_details: - self.results_write_link(name_displayer.display(person), - person, person_handle) - if added_birth: - self.results_write(_(" added birth on %s") % date1) - if added_death: - self.results_write(_(" added death on %s") % date2) - self.results_write("\n") - self.db.transaction_commit(self.trans, _("Calculate date estimates")) + date1 = gen.lib.Date() + if death_ref: + ev = self.db.get_event_from_handle(death_ref.ref) + date2 = ev.get_date_object() + elif not death_ref and add_death and date2: + current_date = gen.lib.Date() + current_date.set_yr_mon_day(*time.localtime(time.time())[0:3]) + if date2.match( current_date, "<"): + add_death_event = True + date2.make_vague() + else: + date2 = gen.lib.Date() + else: + date2 = gen.lib.Date() + # Describe + if add_birth_event and add_death_event: + action = _("Add birth and death events") + elif add_birth_event: + action = _("Add birth event") + elif add_death_event: + action = _("Add death event") + else: + continue + #stab.columns(_("Select"), _("Person"), _("Action"), + # _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) + if add_birth == 1 and not birth_ref: # no date + date1 = gen.lib.Date() + if add_death == 1 and not death_ref: # no date + date2 = gen.lib.Date() + stab.row("checkbox", + person, + action, + date1, + date2, + explain or "", + other or "") + if add_birth_event: + stab.set_cell_markup(3, row, "%s" % DateHandler.displayer.display(date1)) + if add_death_event: + stab.set_cell_markup(4, row, "%s" % DateHandler.displayer.display(date2)) + self.action[person.handle] = (add_birth_event, add_death_event) + row += 1 + if row > 0: + self.results_write(" ") + for text, function in BUTTONS: + self.make_button(text, function, widget) + self.results_write("\n") + stab.write(sdoc) + self.results_write(" ") + for text, function in BUTTONS: + self.make_button(text, function, widget) + self.results_write("\n") + else: + self.results_write(_("No events to be added.")) + self.results_write("\n") + self.results_write("\n") + self.progress.close() + + def make_button(self, text, function, widget): + import gtk + button = gtk.Button(text) + buffer = widget.get_buffer() + iter = buffer.get_end_iter() + anchor = buffer.create_child_anchor(iter) + widget.add_child_at_anchor(button, anchor) + button.connect("clicked", function) + button.show() + self.results_write(" ") + + def select_all(self, obj): + select_col = self.table.model_index_of_column[_("Select")] + for row in self.table.treeview.get_model(): + row[select_col] = True + + def select_none(self, obj): + select_col = self.table.model_index_of_column[_("Select")] + for row in self.table.treeview.get_model(): + row[select_col] = False + + def toggle_select(self, obj): + select_col = self.table.model_index_of_column[_("Select")] + for row in self.table.treeview.get_model(): + row[select_col] = not row[select_col] + + def apply_selection(self, *args, **kwargs): + # Do not add birth or death event if one exists, no matter what + if self.table.treeview.get_model() is None: + return + self.pre_run() + source_text = self.options.handler.options_dict['source_text'] + select_col = self.table.model_index_of_column[_("Select")] + source = self.get_or_create_source(source_text) + self.trans = self.db.transaction_begin("",batch=True) + self.db.disable_signals() + self.results_write(_("Selecting... ")) + self.progress.set_pass((_("Adding events '%s'...") % source_text), + len(self.table.treeview.get_model())) + self.trans = self.db.transaction_begin("",batch=True) + count = 0 + for row in self.table.treeview.get_model(): + self.progress.step() + select = row[select_col] # live select value + if not select: + continue + pupdate = False + index = row[0] # order put in + row_data = self.table.get_raw_data(index) + person = row_data[1] # check, person, action, date1, date2 + date1 = row_data[3] # date + date2 = row_data[4] # date + evidence = row_data[5] # evidence + other = row_data[6] # other person + add_birth_event, add_death_event = self.action[person.handle] + birth_ref = person.get_birth_ref() + death_ref = person.get_death_ref() + if not birth_ref and add_birth_event: + other_name = self.sdb.name(other) + if other_name: + explanation = _("Added birth event based on %s, from %s") % (evidence, other_name) + else: + explanation = _("Added birth event based on %s") % evidence + birth = self.create_event(_("Estimated birth date"), + gen.lib.EventType.BIRTH, + date1, source, explanation) + event_ref = gen.lib.EventRef() + event_ref.set_reference_handle(birth.get_handle()) + person.set_birth_ref(event_ref) + pupdate = True + count += 1 + if not death_ref and add_death_event: + other_name = self.sdb.name(other) + if other_name: + explanation = _("Added death event based on %s, from %s") % (evidence, other_name) + else: + explanation = _("Added death event based on %s") % evidence + death = self.create_event(_("Estimated death date"), + gen.lib.EventType.DEATH, + date2, source, explanation) + event_ref = gen.lib.EventRef() + event_ref.set_reference_handle(death.get_handle()) + person.set_death_ref(event_ref) + pupdate = True + count += 1 + if pupdate: + self.db.commit_person(person, self.trans) + self.results_write(_(" Done! Committing...")) + self.results_write("\n") + self.db.transaction_commit(self.trans, _("Add date estimates")) self.db.enable_signals() self.db.request_rebuild() - self.results_write(_("Done!\n")) + self.results_write(_("Added %d events.") % count) + self.results_write("\n\n") + self.progress.close() def get_or_create_source(self, source_text): source_list = self.db.get_source_handles() @@ -278,9 +485,16 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): return source def create_event(self, description=_("Estimated date"), - type=None, date=None, source=None): + type=None, date=None, source=None, + note_text=""): event = gen.lib.Event() event.set_description(description) + note = gen.lib.Note() + note.handle = create_id() + note.type.set(gen.lib.NoteType.EVENT) + note.set(note_text) + self.db.add_note(note, self.trans) + event.add_note(note.handle) if type: event.set_type(gen.lib.EventType(type)) if date: @@ -296,54 +510,65 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): self.db.add_event(event, self.trans) return event - def calc_estimates(self, person, birth_ref, death_ref): + def calc_estimates(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() == gen.lib.EventRoleType.PRIMARY: + if death_ref and death_ref.get_role().is_primary(): death = self.db.get_event_from_handle(death_ref.ref) if 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. - 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 in [gen.lib.EventType.CAUSE_DEATH, - gen.lib.EventType.BURIAL, - gen.lib.EventType.CREMATION]: - if not death_date: + 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 100 years before current year then + # If they were born within X years before current year then # assume they are alive (we already know they are not dead). - if birth_ref and birth_ref.get_role() == gen.lib.EventRoleType.PRIMARY: - birth = self.db.get_event_from_handle(birth_ref.ref) - if birth.get_date_object().get_start_date() != gen.lib.Date.EMPTY: - if not birth_date: + 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.get_date_object().get_start_date() != gen.lib.Date.EMPTY: birth_date = birth.get_date_object() - #print " calculating...", birth_date, death_date + # 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) + 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 120 years past, - # or more than 20 future, then probably this person is - # not alive. If the sibling died more than 120 years - # past, or more than 120 years future, then probably not alive. - - #print " searching family..." + # 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: @@ -351,28 +576,70 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): 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: - # 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)) - 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: - # 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)) + # 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 doing a spouse lookup, then let's do: + 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(father, 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. @@ -392,32 +659,58 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): 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)) + 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.MIN_GENERATION_YEARS), - dobj.copy_offset_ymd(- self.MIN_GENERATION_YEARS + self.MAX_AGE_PROB_ALIVE)) - date1, date2 = descendants_too_old (child, years + self.MIN_GENERATION_YEARS) + dobj.copy_offset_ymd(- self.MIN_GENERATION_YEARS + self.MAX_AGE_PROB_ALIVE), + _("descendent death date"), + child) + date1, date2, explain, other = descendants_too_old (child, years + self.MIN_GENERATION_YEARS) if date1 and date2: - return date1, date2 - return (None, None) + 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.MIN_GENERATION_YEARS), + dobj.copy_offset_ymd(- self.MIN_GENERATION_YEARS + 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 = None, None + date1, date2, explain, other = None, None, "", None try: - date1, date2 = descendants_too_old(person, self.MIN_GENERATION_YEARS) + date1, date2, explain, other = descendants_too_old(person, self.MIN_GENERATION_YEARS) 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) + return (date1, date2, explain, other) def ancestors_too_old(person, year): family_handle = person.get_main_parents_family_handle() @@ -427,53 +720,104 @@ class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): 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() == gen.lib.EventRoleType.PRIMARY: + 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)) + 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() == gen.lib.EventRoleType.PRIMARY: + 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)) - date1, date2 = ancestors_too_old (father, year - self.AVG_GENERATION_GAP) + 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 + 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() == gen.lib.EventRoleType.PRIMARY: + 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)) + 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() == gen.lib.EventRoleType.PRIMARY: + 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)) - date1, date2 = ancestors_too_old (mother, year - self.AVG_GENERATION_GAP) + 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) - return (None, None) + 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 = ancestors_too_old (person, - self.MIN_GENERATION_YEARS) + date1, date2, explain, other = ancestors_too_old (person, - self.MIN_GENERATION_YEARS) if date1 and date2: - return (date1, date2) - #print " FAIL" + 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) + + return (None, None, "", None)