# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id$ """ A plugin to verify the data against user-adjsted tests. This is the research tool, not the low-level data ingerity check. """ #------------------------------------------------------------------------ # # standard python modules # #------------------------------------------------------------------------ import os from gettext import gettext as _ import cPickle import md5 import Errors #------------------------------------------------------------------------ # # GNOME/GTK modules # #------------------------------------------------------------------------ import gtk import gtk.glade import gobject #------------------------------------------------------------------------ # # GRAMPS modules # #------------------------------------------------------------------------ import const import gen.lib import Utils import GrampsDisplay from ManagedWindow import ManagedWindow from BasicUtils import UpdateCallback from PluginUtils import Tool, register_tool #------------------------------------------------------------------------- # # temp storage and related functions # #------------------------------------------------------------------------- _person_cache = {} _family_cache = {} _event_cache = {} def find_event(db,handle): try: obj = _event_cache[handle] except KeyError: obj = db.get_event_from_handle(handle) _event_cache[handle] = obj return obj def find_person(db,handle): try: obj = _person_cache[handle] except KeyError: obj = db.get_person_from_handle(handle) _person_cache[handle] = obj return obj def find_family(db,handle): try: obj = _family_cache[handle] except KeyError: obj = db.get_family_from_handle(handle) _family_cache[handle] = obj return obj def clear_cache(): _person_cache.clear() _family_cache.clear() _event_cache.clear() #------------------------------------------------------------------------- # # helper functions # #------------------------------------------------------------------------- def get_date_from_event_handle(db,event_handle): if not event_handle: return 0 event = find_event(db,event_handle) date_obj = event.get_date_object() if date_obj.get_day() == 0 or date_obj.get_month() == 0: return 0 return date_obj.get_sort_value() def get_date_from_event_type(db,person,event_type): if not person: return 0 for event_ref in person.get_event_ref_list(): event = find_event(db,event_ref.ref) if event.get_type() == event_type: date_obj = event.get_date_object() if date_obj.get_day() == 0 or date_obj.get_month() == 0: return 0 return date_obj.get_sort_value() return 0 def get_bapt_date(db,person): return get_date_from_event_type(db,person,gen.lib.EventType.BAPTISM) def get_bury_date(db,person): return get_date_from_event_type(db,person,gen.lib.EventType.BURIAL) def get_birth_date(db,person,estimate=False): if not person: return 0 birth_ref = person.get_birth_ref() if not birth_ref: ret = 0 else: ret = get_date_from_event_handle(db,birth_ref.ref) if estimate and (ret == 0): ret = get_bapt_date(db,person) return ret def get_death_date(db,person,estimate=False): if not person: return 0 death_ref = person.get_death_ref() if not death_ref: ret = 0 else: ret = get_date_from_event_handle(db,death_ref.ref) if estimate and (ret == 0): ret = get_bury_date(db,person) return ret def get_age_at_death(db,person,estimate): birth_date = get_birth_date(db,person,estimate) death_date = get_death_date(db,person,estimate) if (birth_date > 0) and (death_date > 0): return death_date - birth_date return 0 def get_father(db,family): if not family: return None father_handle = family.get_father_handle() if father_handle: return find_person(db,father_handle) return None def get_mother(db,family): if not family: return None mother_handle = family.get_mother_handle() if mother_handle: return find_person(db,mother_handle) return None def get_child_birth_dates(db,family,estimate): dates = [] for child_ref in family.get_child_ref_list(): child = find_person(db,child_ref.ref) child_birth_date = get_birth_date(db,child,estimate) if child_birth_date > 0: dates.append(child_birth_date) return dates def get_n_children(db,person): n = 0 for family_handle in person.get_family_handle_list(): family = find_family(db,family_handle) n += len(family.get_child_ref_list()) def get_marriage_date(db,family): if not family: return 0 for event_ref in family.get_event_ref_list(): event = find_event(db,event_ref.ref) if event.get_type() == gen.lib.EventType.MARRIAGE: date_obj = event.get_date_object() return date_obj.get_sort_value() return 0 #------------------------------------------------------------------------- # # Actual tool # #------------------------------------------------------------------------- class Verify(Tool.Tool, ManagedWindow, UpdateCallback): def __init__(self, dbstate, uistate, options_class, name,callback=None): self.label = _('Database Verify tool') Tool.Tool.__init__(self, dbstate, options_class, name) ManagedWindow.__init__(self,uistate,[],self.__class__) UpdateCallback.__init__(self,self.uistate.pulse_progressbar) self.dbstate = dbstate if uistate: self.init_gui() else: self.add_results = self.add_results_cli self.run_tool(cli=True) def add_results_cli(self,results): # print data for the user, no GUI (msg,gramps_id,name,the_type,rule_id,severity,handle) = results if severity == Rule.WARNING: print "W: %s, %s: %s, %s" % (msg,the_type,gramps_id,name) elif severity == Rule.ERROR: print "E: %s, %s: %s, %s" % (msg,the_type,gramps_id,name) else: print "S: %s, %s: %s, %s" % (msg,the_type,gramps_id,name) def init_gui(self): # Draw dialog and make it handle everything base = os.path.dirname(__file__) self.glade_file = base + os.sep + "verify.glade" self.vr = None self.top = gtk.glade.XML(self.glade_file,"verify_settings","gramps") self.top.signal_autoconnect({ "destroy_passed_object" : self.close, "on_help_clicked" : self.on_help_clicked, "on_verify_ok_clicked" : self.on_apply_clicked }) window = self.top.get_widget('verify_settings') self.set_window(window,self.top.get_widget('title'),self.label) self.top.get_widget("oldage").set_value( self.options.handler.options_dict['oldage']) self.top.get_widget("hwdif").set_value( self.options.handler.options_dict['hwdif']) self.top.get_widget("cspace").set_value( self.options.handler.options_dict['cspace']) self.top.get_widget("cbspan").set_value( self.options.handler.options_dict['cbspan']) self.top.get_widget("yngmar").set_value( self.options.handler.options_dict['yngmar']) self.top.get_widget("oldmar").set_value( self.options.handler.options_dict['oldmar']) self.top.get_widget("oldmom").set_value( self.options.handler.options_dict['oldmom']) self.top.get_widget("yngmom").set_value( self.options.handler.options_dict['yngmom']) self.top.get_widget("olddad").set_value( self.options.handler.options_dict['olddad']) self.top.get_widget("yngdad").set_value( self.options.handler.options_dict['yngdad']) self.top.get_widget("wedder").set_value( self.options.handler.options_dict['wedder']) self.top.get_widget("mxchildmom").set_value( self.options.handler.options_dict['mxchildmom']) self.top.get_widget("mxchilddad").set_value( self.options.handler.options_dict['mxchilddad']) self.top.get_widget("lngwdw").set_value( self.options.handler.options_dict['lngwdw']) self.top.get_widget("oldunm").set_value( self.options.handler.options_dict['oldunm']) self.top.get_widget("estimate").set_active( self.options.handler.options_dict['estimate_age']) self.show() def build_menu_names(self,obj): return (_("Tool settings"),self.label) def on_help_clicked(self,obj): """Display the relevant portion of GRAMPS manual""" GrampsDisplay.help('tools-util-other') def on_apply_clicked(self,obj): self.options.handler.options_dict['oldage'] = self.top.get_widget( "oldage").get_value_as_int() self.options.handler.options_dict['hwdif'] = self.top.get_widget( "hwdif").get_value_as_int() self.options.handler.options_dict['cspace'] = self.top.get_widget( "cspace").get_value_as_int() self.options.handler.options_dict['cbspan'] = self.top.get_widget( "cbspan").get_value_as_int() self.options.handler.options_dict['yngmar'] = self.top.get_widget( "yngmar").get_value_as_int() self.options.handler.options_dict['oldmar'] = self.top.get_widget( "oldmar").get_value_as_int() self.options.handler.options_dict['oldmom'] = self.top.get_widget( "oldmom").get_value_as_int() self.options.handler.options_dict['yngmom'] = self.top.get_widget( "yngmom").get_value_as_int() self.options.handler.options_dict['olddad'] = self.top.get_widget( "olddad").get_value_as_int() self.options.handler.options_dict['yngdad'] = self.top.get_widget( "yngdad").get_value_as_int() self.options.handler.options_dict['wedder'] = self.top.get_widget( "wedder").get_value_as_int() self.options.handler.options_dict['mxchildmom'] = self.top.get_widget( "mxchildmom").get_value_as_int() self.options.handler.options_dict['mxchilddad'] = self.top.get_widget( "mxchilddad").get_value_as_int() self.options.handler.options_dict['lngwdw'] = self.top.get_widget( "lngwdw").get_value_as_int() self.options.handler.options_dict['oldunm'] = self.top.get_widget( "oldunm").get_value_as_int() self.options.handler.options_dict['estimate_age'] = \ self.top.get_widget( "estimate").get_active() try: self.vr = VerifyResults(self.dbstate, self.uistate, self.track) self.add_results = self.vr.add_results self.vr.load_ignored(self.db.full_name) except Errors.WindowActiveError: pass self.uistate.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) self.uistate.progress.show() self.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) try: self.vr.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) except AttributeError: pass self.run_tool(cli=False) self.uistate.progress.hide() self.uistate.window.window.set_cursor(None) self.window.window.set_cursor(None) try: self.vr.window.window.set_cursor(None) except AttributeError: pass self.reset() # Save options self.options.handler.save_options() def run_tool(self,cli=False): person_handles = self.db.get_person_handles(sort_handles=False) oldage = self.options.handler.options_dict['oldage'] hwdif = self.options.handler.options_dict['hwdif'] cspace = self.options.handler.options_dict['cspace'] cbspan = self.options.handler.options_dict['cbspan'] yngmar = self.options.handler.options_dict['yngmar'] oldmar = self.options.handler.options_dict['oldmar'] oldmom = self.options.handler.options_dict['oldmom'] yngmom = self.options.handler.options_dict['yngmom'] olddad = self.options.handler.options_dict['olddad'] yngdad = self.options.handler.options_dict['yngdad'] wedder = self.options.handler.options_dict['wedder'] mxchildmom = self.options.handler.options_dict['mxchildmom'] mxchilddad = self.options.handler.options_dict['mxchilddad'] lngwdw = self.options.handler.options_dict['lngwdw'] oldunm = self.options.handler.options_dict['oldunm'] estimate_age = self.options.handler.options_dict['estimate_age'] self.vr.real_model.clear() self.set_total(self.db.get_number_of_people() + self.db.get_number_of_families()) for person_handle in person_handles: person = find_person(self.db,person_handle) rule_list = [ BirthAfterBapt(self.db,person), DeathBeforeBapt(self.db,person), BirthAfterBury(self.db,person), DeathAfterBury(self.db,person), BirthAfterDeath(self.db,person), BaptAfterBury(self.db,person), OldAge(self.db,person,oldage,estimate_age), UnknownGender(self.db,person), MultipleParents(self.db,person), MarriedOften(self.db,person,wedder), OldUnmarried(self.db,person,oldunm,estimate_age), TooManyChildren(self.db,person,mxchilddad,mxchildmom), Disconnected(self.db,person), ] for rule in rule_list: if rule.broken(): self.add_results(rule.report_itself()) clear_cache() if not cli: self.update() # Family-based rules for family_handle in self.db.get_family_handles(): family = find_family(self.db,family_handle) rule_list = [ SameSexFamily(self.db,family), FemaleHusband(self.db,family), MaleWife(self.db,family), SameSurnameFamily(self.db,family), LargeAgeGapFamily(self.db,family,hwdif,estimate_age), MarriageBeforeBirth(self.db,family,estimate_age), MarriageAfterDeath(self.db,family,estimate_age), EarlyMarriage(self.db,family,yngmar,estimate_age), LateMarriage(self.db,family,oldmar,estimate_age), OldParent(self.db,family,oldmom,olddad,estimate_age), YoungParent(self.db,family,yngmom,yngdad,estimate_age), UnbornParent(self.db,family,estimate_age), DeadParent(self.db,family,estimate_age), LargeChildrenSpan(self.db,family,cbspan,estimate_age), LargeChildrenAgeDiff(self.db,family,cspace,estimate_age), ] for rule in rule_list: if rule.broken(): self.add_results(rule.report_itself()) clear_cache() if not cli: self.update() #------------------------------------------------------------------------- # # Display the results # #------------------------------------------------------------------------- class VerifyResults(ManagedWindow): IGNORE_COL = 0 WARNING_COL = 1 OBJ_ID_COL = 2 OBJ_NAME_COL = 3 OBJ_TYPE_COL = 4 RULE_ID_COL = 5 OBJ_HANDLE_COL = 6 FG_COLOR_COL = 7 TRUE_COL = 8 SHOW_COL = 9 def __init__(self,dbstate,uistate,track): self.title = _('Database Verification Results') ManagedWindow.__init__(self,uistate,track,self.__class__) self.dbstate = dbstate base = os.path.dirname(__file__) self.glade_file = base + os.sep + "verify.glade" self.top = gtk.glade.XML(self.glade_file,"verify_result","gramps") window = self.top.get_widget("verify_result") self.set_window(window,self.top.get_widget('title'),self.title) self.top.signal_autoconnect({ "destroy_passed_object" : self.close, }) self.warn_tree = self.top.get_widget('warn_tree') self.warn_tree.connect('button_press_event', self.double_click) self.selection = self.warn_tree.get_selection() self.hide_button = self.top.get_widget('hide_button') self.hide_button.connect('toggled',self.hide_toggled) self.mark_button = self.top.get_widget('mark_all') self.mark_button.connect('clicked',self.mark_clicked) self.unmark_button = self.top.get_widget('unmark_all') self.unmark_button.connect('clicked',self.unmark_clicked) self.invert_button = self.top.get_widget('invert_all') self.invert_button.connect('clicked',self.invert_clicked) self.real_model = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, object, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN) self.filt_model = self.real_model.filter_new() self.filt_model.set_visible_column(VerifyResults.TRUE_COL) self.sort_model = gtk.TreeModelSort(self.filt_model) self.warn_tree.set_model(self.sort_model) self.renderer = gtk.CellRendererText() self.img_renderer = gtk.CellRendererPixbuf() self.bool_renderer = gtk.CellRendererToggle() self.bool_renderer.connect('toggled',self.selection_toggled) # Add ignore column ignore_column = gtk.TreeViewColumn(_('Mark'),self.bool_renderer, active=VerifyResults.IGNORE_COL) ignore_column.set_sort_column_id(VerifyResults.IGNORE_COL) self.warn_tree.append_column(ignore_column) # Add image column img_column = gtk.TreeViewColumn(None, self.img_renderer ) img_column.set_cell_data_func(self.img_renderer,self.get_image) self.warn_tree.append_column(img_column) # Add column with the warning text warn_column = gtk.TreeViewColumn(_('Warning'), self.renderer, text=VerifyResults.WARNING_COL, foreground=VerifyResults.FG_COLOR_COL) warn_column.set_sort_column_id(VerifyResults.WARNING_COL) self.warn_tree.append_column(warn_column) # Add column with object gramps_id id_column = gtk.TreeViewColumn(_('ID'), self.renderer, text=VerifyResults.OBJ_ID_COL, foreground=VerifyResults.FG_COLOR_COL) id_column.set_sort_column_id(VerifyResults.OBJ_ID_COL) self.warn_tree.append_column(id_column) # Add column with object name name_column = gtk.TreeViewColumn(_('Name'), self.renderer, text=VerifyResults.OBJ_NAME_COL, foreground=VerifyResults.FG_COLOR_COL) name_column.set_sort_column_id(VerifyResults.OBJ_NAME_COL) self.warn_tree.append_column(name_column) self.window.show_all() self.window_shown = False def load_ignored(self,db_filename): md5sum = md5.md5(db_filename) self.ignores_filename = os.path.join( const.HOME_DIR,md5sum.hexdigest() + os.path.extsep + 'vfm') if not self._load_ignored(self.ignores_filename): self.ignores = {} def _load_ignored(self,filename): try: f = open(filename) self.ignores = cPickle.load(f) f.close() return True except IOError: return False def save_ignored(self,new_ignores): self.ignores = new_ignores self._save_ignored(self.ignores_filename) def _save_ignored(self,filename): try: f = open(filename,'w') cPickle.dump(self.ignores,f,1) f.close() return True except IOError: return False def get_marking(self,handle,rule_id): try: return (rule_id in self.ignores[handle]) except KeyError: return False def get_new_marking(self): new_ignores = {} for row_num in range(len(self.real_model)): path = (row_num,) row = self.real_model[path] ignore = row[VerifyResults.IGNORE_COL] if ignore: handle = row[VerifyResults.OBJ_HANDLE_COL] rule_id = row[VerifyResults.RULE_ID_COL] if not new_ignores.has_key(handle): new_ignores[handle] = set() new_ignores[handle].add(rule_id) return new_ignores def close(self,*obj): new_ignores = self.get_new_marking() self.save_ignored(new_ignores) ManagedWindow.close(self,*obj) def hide_toggled(self,button): if button.get_active(): button.set_label(_("_Show all")) self.filt_model = self.real_model.filter_new() self.filt_model.set_visible_column(VerifyResults.SHOW_COL) self.sort_model = gtk.TreeModelSort(self.filt_model) self.warn_tree.set_model(self.sort_model) else: self.filt_model = self.real_model.filter_new() self.filt_model.set_visible_column(VerifyResults.TRUE_COL) self.sort_model = gtk.TreeModelSort(self.filt_model) self.warn_tree.set_model(self.sort_model) button.set_label(_("_Hide marked")) def selection_toggled(self,cell,path_string): sort_path = tuple([int (i) for i in path_string.split(':')]) filt_path = self.sort_model.convert_path_to_child_path(sort_path) real_path = self.filt_model.convert_path_to_child_path(filt_path) row = self.real_model[real_path] row[VerifyResults.IGNORE_COL] = not row[VerifyResults.IGNORE_COL] row[VerifyResults.SHOW_COL] = not row[VerifyResults.IGNORE_COL] self.real_model.row_changed(real_path,row.iter) def mark_clicked(self,mark_button): for row_num in range(len(self.real_model)): path = (row_num,) row = self.real_model[path] row[VerifyResults.IGNORE_COL] = True row[VerifyResults.SHOW_COL] = False self.filt_model.refilter() def unmark_clicked(self,unmark_button): for row_num in range(len(self.real_model)): path = (row_num,) row = self.real_model[path] row[VerifyResults.IGNORE_COL] = False row[VerifyResults.SHOW_COL] = True self.filt_model.refilter() def invert_clicked(self,invert_button): for row_num in range(len(self.real_model)): path = (row_num,) row = self.real_model[path] row[VerifyResults.IGNORE_COL] = not row[VerifyResults.IGNORE_COL] row[VerifyResults.SHOW_COL] = not row[VerifyResults.SHOW_COL] self.filt_model.refilter() def double_click(self,obj,event): if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: (model,node) = self.selection.get_selected() if not node: return sort_path = self.sort_model.get_path(node) filt_path = self.sort_model.convert_path_to_child_path(sort_path) real_path = self.filt_model.convert_path_to_child_path(filt_path) row = self.real_model[real_path] the_type = row[VerifyResults.OBJ_TYPE_COL] handle = row[VerifyResults.OBJ_HANDLE_COL] if the_type == 'Person': try: from Editors import EditPerson person = self.dbstate.db.get_person_from_handle(handle) EditPerson(self.dbstate, self.uistate, [], person) except Errors.WindowActiveError: pass elif the_type == 'Family': try: from Editors import EditFamily family = self.dbstate.db.get_family_from_handle(handle) EditFamily(self.dbstate, self.uistate, [], family) except Errors.WindowActiveError: pass def get_image(self, column, cell, model, iter, user_data=None): the_type = model.get_value(iter, VerifyResults.OBJ_TYPE_COL) if the_type == 'Person': cell.set_property('stock-id', 'gramps-person' ) elif the_type == 'Family': cell.set_property('stock-id', 'gramps-family' ) def add_results(self,results): (msg,gramps_id,name,the_type,rule_id,severity,handle) = results ignore = self.get_marking(handle,rule_id) if severity == Rule.ERROR: fg = 'red' # fg = '#8b008b' # purple # elif severity == Rule.WARNING: # fg = '#008b00' # green else: fg = None self.real_model.append(row=[ignore,msg,gramps_id,name, the_type,rule_id,handle,fg, True, not ignore]) if not self.window_shown: self.show() self.window_shown = True def build_menu_names(self,obj): return (self.title,None) #------------------------------------------------------------------------ # # # #------------------------------------------------------------------------ class VerifyOptions(Tool.ToolOptions): """ Defines options and provides handling interface. """ def __init__(self,name,person_id=None): Tool.ToolOptions.__init__(self,name,person_id) # Options specific for this report self.options_dict = { 'oldage' : 90, 'hwdif' : 30, 'cspace' : 8, 'cbspan' : 25, 'yngmar' : 17, 'oldmar' : 50, 'oldmom' : 48, 'yngmom' : 17, 'yngdad' : 18, 'olddad' : 65, 'wedder' : 3, 'mxchildmom' : 12, 'mxchilddad' : 15, 'lngwdw' : 30, 'oldunm' : 99, 'estimate_age' : 0, } self.options_help = { 'oldage' : ("=num","Maximum age","Age in years"), 'hwdif' : ("=num","Maximum husband-wife age difference", "Age difference in years"), 'cspace' : ("=num", "Maximum number of years between children", "Number of years"), 'cbspan' : ("=num", "Maximum span of years for all children", "Span in years"), 'yngmar' : ("=num","Minimum age to marry","Age in years"), 'oldmar' : ("=num","Maximum age to marry","Age in years"), 'oldmom' : ("=num","Maximum age to bear a child", "Age in years"), 'yngmom' : ("=num","Minimum age to bear a child", "Age in years"), 'yngdad' : ("=num","Minimum age to father a child", "Age in years"), 'olddad' : ("=num","Maximum age to father a child", "Age in years"), 'wedder' : ("=num","Maximum number of spouses for a person", "Number of spouses"), 'mxchildmom' : ("=num","Maximum number of children for a woman", "Number of children"), 'mxchilddad' : ("=num","Maximum number of children for a man", "Number of chidlren"), 'lngwdw' : ("=num","Maximum number of consecutive years " "of widowhood before next marriage", "Number of years"), 'oldunm' : ("=num","Maximum age for an unmarried person" "Number of years"), 'estimate_age' : ("=0/1","Whether to estimate missing dates", ["Do not estimate","Estimate dates"], True), } #------------------------------------------------------------------------- # # Base classes for different tests -- the rules # #------------------------------------------------------------------------- class Rule: """ Basic class for use in this tool. Other rules must inherit from this. """ ID = 0 TYPE = '' ERROR = 1 WARNING = 2 SEVERITY = WARNING def __init__(self,db,obj): self.db = db self.obj = obj def broken(self): """ Return boolean indicating whether this rule is violated. """ return False def get_message(self): assert False, "Need to be overriden in the derived class" def get_name(self): assert False, "Need to be overriden in the derived class" def get_handle(self): return self.obj.handle def get_id(self): return self.obj.gramps_id def get_level(self): return Rule.WARNING def get_rule_id(self): params = self._get_params() return (self.ID,params) def _get_params(self): return tuple() def report_itself(self): handle = self.get_handle() the_type = self.TYPE rule_id = self.get_rule_id() severity = self.SEVERITY name = self.get_name() gramps_id = self.get_id() msg = self.get_message() return (msg,gramps_id,name,the_type,rule_id,severity,handle) class PersonRule(Rule): """ Person-based class. """ TYPE = 'Person' def get_name(self): return self.obj.get_primary_name().get_name() class FamilyRule(Rule): """ Family-based class. """ TYPE = 'Family' def get_name(self): return Utils.family_name(self.obj,self.db) #------------------------------------------------------------------------- # # Actual rules for testing # #------------------------------------------------------------------------- class BirthAfterBapt(PersonRule): ID = 1 SEVERITY = Rule.ERROR def broken(self): birth_date = get_birth_date(self.db,self.obj) bapt_date = get_bapt_date(self.db,self.obj) birth_ok = birth_date > 0 bapt_ok = bapt_date > 0 birth_after_death = birth_date > bapt_date return (birth_ok and bapt_ok and birth_after_death) def get_message(self): return _("Baptism before birth") class DeathBeforeBapt(PersonRule): ID = 2 SEVERITY = Rule.ERROR def broken(self): death_date = get_death_date(self.db,self.obj) bapt_date = get_bapt_date(self.db,self.obj) bapt_ok = bapt_date > 0 death_ok = death_date > 0 death_before_bapt = bapt_date > death_date return (death_ok and bapt_ok and death_before_bapt) def get_message(self): return _("Death before baptism") class BirthAfterBury(PersonRule): ID = 3 SEVERITY = Rule.ERROR def broken(self): birth_date = get_birth_date(self.db,self.obj) bury_date = get_bury_date(self.db,self.obj) birth_ok = birth_date > 0 bury_ok = bury_date > 0 birth_after_bury = birth_date > bury_date return (birth_ok and bury_ok and birth_after_bury) def get_message(self): return _("Burial before birth") class DeathAfterBury(PersonRule): ID = 4 SEVERITY = Rule.ERROR def broken(self): death_date = get_death_date(self.db,self.obj) bury_date = get_bury_date(self.db,self.obj) death_ok = death_date > 0 bury_ok = bury_date > 0 death_after_bury = death_date > bury_date return (death_ok and bury_ok and death_after_bury) def get_message(self): return _("Burial before death") class BirthAfterDeath(PersonRule): ID = 5 SEVERITY = Rule.ERROR def broken(self): birth_date = get_birth_date(self.db,self.obj) death_date = get_death_date(self.db,self.obj) birth_ok = birth_date > 0 death_ok = death_date > 0 birth_after_death = birth_date > death_date return (birth_ok and death_ok and birth_after_death) def get_message(self): return _("Death before birth") class BaptAfterBury(PersonRule): ID = 6 SEVERITY = Rule.ERROR def broken(self): bapt_date = get_bapt_date(self.db,self.obj) bury_date = get_bury_date(self.db,self.obj) bapt_ok = bapt_date > 0 bury_ok = bury_date > 0 bapt_after_bury = bapt_date > bury_date return (bapt_ok and bury_ok and bapt_after_bury) def get_message(self): return _("Burial before baptism") class OldAge(PersonRule): ID = 7 SEVERITY = Rule.WARNING def __init__(self,db,person,old_age,est): PersonRule.__init__(self,db,person) self.old_age = old_age self.est = est def _get_params(self): return (self.old_age,self.est) def broken(self): age_at_death = get_age_at_death(self.db,self.obj,self.est) return (age_at_death/365 > self.old_age) def get_message(self): return _("Old age at death") class UnknownGender(PersonRule): ID = 8 SEVERITY = Rule.WARNING def broken(self): female = self.obj.get_gender() == gen.lib.Person.FEMALE male = self.obj.get_gender() == gen.lib.Person.MALE return not (male or female) def get_message(self): return _("Unknown gender") class MultipleParents(PersonRule): ID = 9 SEVERITY = Rule.WARNING def broken(self): n_parent_sets = len(self.obj.get_parent_family_handle_list()) return (n_parent_sets>1) def get_message(self): return _("Multiple parents") class MarriedOften(PersonRule): ID = 10 SEVERITY = Rule.WARNING def __init__(self,db,person,wedder): PersonRule.__init__(self,db,person) self.wedder = wedder def _get_params(self): return (self.wedder,) def broken(self): n_spouses = len(self.obj.get_family_handle_list()) return (n_spouses>self.wedder) def get_message(self): return _("Married often") class OldUnmarried(PersonRule): ID = 11 SEVERITY = Rule.WARNING def __init__(self,db,person,old_unm,est): PersonRule.__init__(self,db,person) self.old_unm = old_unm self.est = est def _get_params(self): return (self.old_unm,self.est) def broken(self): age_at_death = get_age_at_death(self.db,self.obj,self.est) n_spouses = len(self.obj.get_family_handle_list()) return (age_at_death/365>self.old_unm and n_spouses==0) def get_message(self): return _("Old and unmarried") class TooManyChildren(PersonRule): ID = 12 SEVERITY = Rule.WARNING def __init__(self,db,obj,mx_child_dad,mx_child_mom): PersonRule.__init__(self,db,obj) self.mx_child_dad = mx_child_dad self.mx_child_mom = mx_child_mom def _get_params(self): return (self.mx_child_dad,self.mx_child_mom) def broken(self): n_child = get_n_children(self.db,self.obj) if (self.obj.get_gender == gen.lib.Person.MALE) \ and (n_child > self.mx_child_dad): return True if (self.obj.get_gender == gen.lib.Person.FEMALE) \ and (n_child > self.mx_child_mom): return True return False def get_message(self): return _("Too many children") class SameSexFamily(FamilyRule): ID = 13 SEVERITY = Rule.WARNING def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) same_sex = (mother and father and (mother.get_gender() == father.get_gender())) unknown_sex = (mother and (mother.get_gender() == gen.lib.Person.UNKNOWN)) return (same_sex and not unknown_sex) def get_message(self): return _("Same sex marriage") class FemaleHusband(FamilyRule): ID = 14 SEVERITY = Rule.WARNING def broken(self): father = get_father(self.db,self.obj) return (father and (father.get_gender() == gen.lib.Person.FEMALE)) def get_message(self): return _("Female husband") class MaleWife(FamilyRule): ID = 15 SEVERITY = Rule.WARNING def broken(self): mother = get_mother(self.db,self.obj) return (mother and (mother.get_gender() == gen.lib.Person.MALE)) def get_message(self): return _("Male wife") class SameSurnameFamily(FamilyRule): ID = 16 SEVERITY = Rule.WARNING def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) same_surname = (father and mother and (mother.get_primary_name().get_surname() == father.get_primary_name().get_surname())) empty_surname = mother and \ (len(mother.get_primary_name().get_surname())==0) return (same_surname and not empty_surname) def get_message(self): return _("Husband and wife with the same surname") class LargeAgeGapFamily(FamilyRule): ID = 17 SEVERITY = Rule.WARNING def __init__(self,db,obj,hw_diff,est): FamilyRule.__init__(self,db,obj) self.hw_diff = hw_diff self.est = est def _get_params(self): return (self.hw_diff,self.est) def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 large_diff = \ abs(father_birth_date-mother_birth_date)/365 > self.hw_diff return (mother_birth_date_ok and father_birth_date_ok and large_diff) def get_message(self): return _("Large age difference between spouses") class MarriageBeforeBirth(FamilyRule): ID = 18 SEVERITY = Rule.ERROR def __init__(self,db,obj,est): FamilyRule.__init__(self,db,obj) self.est = est def _get_params(self): return (self.est,) def broken(self): marr_date = get_marriage_date(self.db,self.obj) marr_date_ok = marr_date > 0 mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 father_broken = (father_birth_date_ok and marr_date_ok and (father_birth_date > marr_date)) mother_broken = (mother_birth_date_ok and marr_date_ok and (mother_birth_date > marr_date)) return (father_broken or mother_broken) def get_message(self): return _("Marriage before birth") class MarriageAfterDeath(FamilyRule): ID = 19 SEVERITY = Rule.ERROR def __init__(self,db,obj,est): FamilyRule.__init__(self,db,obj) self.est = est def _get_params(self): return (self.est,) def broken(self): marr_date = get_marriage_date(self.db,self.obj) marr_date_ok = marr_date > 0 mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_death_date = get_death_date(self.db,mother,self.est) father_death_date = get_death_date(self.db,father,self.est) mother_death_date_ok = mother_death_date > 0 father_death_date_ok = father_death_date > 0 father_broken = (father_death_date_ok and marr_date_ok and (father_death_date < marr_date)) mother_broken = (mother_death_date_ok and marr_date_ok and (mother_death_date < marr_date)) return (father_broken or mother_broken) def get_message(self): return _("Marriage after death") class EarlyMarriage(FamilyRule): ID = 20 SEVERITY = Rule.WARNING def __init__(self,db,obj,yng_mar,est): FamilyRule.__init__(self,db,obj) self.yng_mar = yng_mar self.est = est def _get_params(self): return (self.yng_mar,self.est,) def broken(self): marr_date = get_marriage_date(self.db,self.obj) marr_date_ok = marr_date > 0 mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 father_broken = (father_birth_date_ok and marr_date_ok and father_birth_date < marr_date and ((marr_date - father_birth_date)/365 < self.yng_mar)) mother_broken = (mother_birth_date_ok and marr_date_ok and mother_birth_date < marr_date and ((marr_date - mother_birth_date)/365 < self.yng_mar)) return (father_broken or mother_broken) def get_message(self): return _("Early marriage") class LateMarriage(FamilyRule): ID = 21 SEVERITY = Rule.WARNING def __init__(self,db,obj,old_mar,est): FamilyRule.__init__(self,db,obj) self.old_mar = old_mar self.est = est def _get_params(self): return (self.old_mar,self.est) def broken(self): marr_date = get_marriage_date(self.db,self.obj) marr_date_ok = marr_date > 0 mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 father_broken = (father_birth_date_ok and marr_date_ok and ((marr_date - father_birth_date)/365 > self.old_mar)) mother_broken = (mother_birth_date_ok and marr_date_ok and ((marr_date - mother_birth_date)/365 > self.old_mar)) return (father_broken or mother_broken) def get_message(self): return _("Late marriage") ## class MarriageBeforePrefiousMarrChild(PersonRule): ## def broken(self): ## marr_date = get_marriage_date(self.obj) ## prev_marr_child_date = get_prev_marr_child_date(self.obj) ## return (prev_marr_child_date>marr_date) ## def get_message(self): ## return _("Marriage before having a child from previous marriage") ## class LongWidowhood(FamilyRule): ## def broken(self): ## marr_date = get_marriage_date(self.obj) ## prev_marr_spouse_death_date = get_prev_marr_spouse_death_date(self.obj) ## birth_date = get_birth_date(self.obj) ## return (marr_date-prev_marr_spouse_death_date>lngwdw) ## def get_message(self): ## return _("Long Windowhood") class OldParent(FamilyRule): ID = 22 SEVERITY = Rule.WARNING def __init__(self,db,obj,old_mom,old_dad,est): FamilyRule.__init__(self,db,obj) self.old_mom = old_mom self.old_dad = old_dad self.est = est def _get_params(self): return (self.old_mom,self.old_dad,self.est) def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 for child_ref in self.obj.get_child_ref_list(): child = find_person(self.db,child_ref.ref) child_birth_date = get_birth_date(self.db,child,self.est) child_birth_date_ok = child_birth_date > 0 if not child_birth_date_ok: continue father_broken = (father_birth_date_ok and ((child_birth_date - father_birth_date)/365 > self.old_dad)) if father_broken: self.get_message = self.father_message return True mother_broken = (mother_birth_date_ok and ((child_birth_date - mother_birth_date)/365 > self.old_mom)) if mother_broken: self.get_message = self.mother_message return True return False def father_message(self): return _("Old father") def mother_message(self): return _("Old mother") class YoungParent(FamilyRule): ID = 23 SEVERITY = Rule.WARNING def __init__(self,db,obj,yng_mom,yng_dad,est): FamilyRule.__init__(self,db,obj) self.yng_dad = yng_dad self.yng_mom = yng_mom self.est = est def _get_params(self): return (self.yng_mom,self.yng_dad,self.est) def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 for child_ref in self.obj.get_child_ref_list(): child = find_person(self.db,child_ref.ref) child_birth_date = get_birth_date(self.db,child,self.est) child_birth_date_ok = child_birth_date > 0 if not child_birth_date_ok: continue father_broken = (father_birth_date_ok and ((child_birth_date - father_birth_date)/365 < self.yng_dad)) if father_broken: self.get_message = self.father_message return True mother_broken = (mother_birth_date_ok and ((child_birth_date - mother_birth_date)/365 < self.yng_mom)) if mother_broken: self.get_message = self.mother_message return True return False def father_message(self): return _("Young father") def mother_message(self): return _("Young mother") class UnbornParent(FamilyRule): ID = 24 SEVERITY = Rule.ERROR def __init__(self,db,obj,est): FamilyRule.__init__(self,db,obj) self.est = est def _get_params(self): return (self.est,) def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_birth_date = get_birth_date(self.db,mother,self.est) father_birth_date = get_birth_date(self.db,father,self.est) mother_birth_date_ok = mother_birth_date > 0 father_birth_date_ok = father_birth_date > 0 for child_ref in self.obj.get_child_ref_list(): child = find_person(self.db,child_ref.ref) child_birth_date = get_birth_date(self.db,child,self.est) child_birth_date_ok = child_birth_date > 0 if not child_birth_date_ok: continue father_broken = (father_birth_date_ok and (father_birth_date > child_birth_date)) if father_broken: self.get_message = self.father_message return True mother_broken = (mother_birth_date_ok and (mother_birth_date > child_birth_date)) if mother_broken: self.get_message = self.mother_message return True def father_message(self): return _("Unborn father") def mother_message(self): return _("Unborn mother") class DeadParent(FamilyRule): ID = 25 SEVERITY = Rule.ERROR def __init__(self,db,obj,est): FamilyRule.__init__(self,db,obj) self.est = est def _get_params(self): return (self.est,) def broken(self): mother = get_mother(self.db,self.obj) father = get_father(self.db,self.obj) mother_death_date = get_death_date(self.db,mother,self.est) father_death_date = get_death_date(self.db,father,self.est) mother_death_date_ok = mother_death_date > 0 father_death_date_ok = father_death_date > 0 for child_ref in self.obj.get_child_ref_list(): child = find_person(self.db,child_ref.ref) child_birth_date = get_birth_date(self.db,child,self.est) child_birth_date_ok = child_birth_date > 0 if not child_birth_date_ok: continue hasBirthRelToMother = child_ref.mrel == gen.lib.ChildRefType.BIRTH hasBirthRelToFather = child_ref.frel == gen.lib.ChildRefType.BIRTH father_broken = (hasBirthRelToFather and father_death_date_ok and ((father_death_date + 294) < child_birth_date)) if father_broken: self.get_message = self.father_message return True mother_broken = (hasBirthRelToMother and mother_death_date_ok and (mother_death_date < child_birth_date)) if mother_broken: self.get_message = self.mother_message return True def father_message(self): return _("Dead father") def mother_message(self): return _("Dead mother") class LargeChildrenSpan(FamilyRule): ID = 26 SEVERITY = Rule.WARNING def __init__(self,db,obj,cb_span,est): FamilyRule.__init__(self,db,obj) self.cb_span = cb_span self.est = est def _get_params(self): return (self.cb_span,self.est) def broken(self): child_birh_dates = get_child_birth_dates(self.db,self.obj,self.est) child_birh_dates.sort() return (child_birh_dates and ((child_birh_dates[-1] - child_birh_dates[0])/365 > self.cb_span)) def get_message(self): return _("Large year span for all children") class LargeChildrenAgeDiff(FamilyRule): ID = 27 SEVERITY = Rule.WARNING def __init__(self,db,obj,c_space,est): FamilyRule.__init__(self,db,obj) self.c_space = c_space self.est = est def _get_params(self): return (self.c_space,self.est) def broken(self): child_birh_dates = get_child_birth_dates(self.db,self.obj,self.est) child_birh_dates_diff = [child_birh_dates[i+1] - child_birh_dates[i] for i in range(len(child_birh_dates)-1) ] return (child_birh_dates_diff and max(child_birh_dates_diff)/365 > self.c_space) def get_message(self): return _("Large age differences between children") class Disconnected(PersonRule): ID = 28 SEVERITY = Rule.WARNING def broken(self): return (len(self.obj.get_parent_family_handle_list()) + len(self.obj.get_family_handle_list()) == 0) def get_message(self): return _("Disconnected individual") #------------------------------------------------------------------------- # # # #------------------------------------------------------------------------- register_tool( name = 'verify', category = Tool.TOOL_UTILS, tool_class = Verify, options_class = VerifyOptions, modes = Tool.MODE_GUI | Tool.MODE_CLI, translated_name = _("Verify the data"), description = _("Verifies the data against user-defined tests") )