# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2003-2005 Donald N. Allingham # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id$ #------------------------------------------------------------------------- # # GRAMPS modules # #------------------------------------------------------------------------- import gen.lib import types from TransUtils import sgettext as _ #------------------------------------------------------------------------- # # # #------------------------------------------------------------------------- STEP= 'step' INLAW='-in-law' HALF = 'half' _level_name = [ "", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth" ] _removed_level = [ "", " once removed", " twice removed", " three times removed", " four times removed", " five times removed", " six times removed", " seven times removed", " eight times removed", " nine times removed", " ten times removed", " eleven times removed", " twelve times removed", " thirteen times removed", " fourteen times removed", " fifteen times removed", " sixteen times removed", " seventeen times removed", " eighteen times removed", " nineteen times removed", " twenty times removed" ] _parents_level = [ "", "parents", "grandparents", "great grandparents", "second great grandparents", "third great grandparents", "fourth great grandparents", "fifth great grandparents", "sixth great grandparents", "seventh great grandparents", "eighth great grandparents", "ninth great grandparents", "tenth great grandparents", "eleventh great grandparents", "twelfth great grandparents", "thirteenth great grandparents", "fourteenth great grandparents", "fifteenth great grandparents", "sixteenth great grandparents", "seventeenth great grandparents", "eighteenth great grandparents", "nineteenth great grandparents", "twentieth great grandparents", ] _father_level = [ "", "%sfather%s", "%sgrandfather%s", "great %sgrandfather%s", "second great %sgrandfather%s", "third great %sgrandfather%s", "fourth great %sgrandfather%s", "fifth great %sgrandfather%s", "sixth great %sgrandfather%s", "seventh great %sgrandfather%s", "eighth great %sgrandfather%s", "ninth great %sgrandfather%s", "tenth great %sgrandfather%s", "eleventh great %sgrandfather%s", "twelfth great %sgrandfather%s", "thirteenth great %sgrandfather%s", "fourteenth great %sgrandfather%s", "fifteenth great %sgrandfather%s", "sixteenth great %sgrandfather%s", "seventeenth great %sgrandfather%s", "eighteenth great %sgrandfather%s", "nineteenth great %sgrandfather%s", "twentieth great %sgrandfather%s", ] _mother_level = [ "", "%smother%s", "%sgrandmother%s", "great %sgrandmother%s", "second great %sgrandmother%s", "third great %sgrandmother%s", "fourth great %sgrandmother%s", "fifth great %sgrandmother%s", "sixth great %sgrandmother%s", "seventh great %sgrandmother%s", "eighth great %sgrandmother%s", "ninth great %sgrandmother%s", "tenth great %sgrandmother%s", "eleventh great %sgrandmother%s", "twelfth great %sgrandmother%s", "thirteenth great %sgrandmother%s", "fourteenth great %sgrandmother%s", "fifteenth great %sgrandmother%s", "sixteenth great %sgrandmother%s", "seventeenth great %sgrandmother%s", "eighteenth great %sgrandmother%s", "nineteenth great %sgrandmother%s", "twentieth great %sgrandmother%s", ] _son_level = [ "", "%sson", "%sgrandson", "great %sgrandson", "second great %sgrandson", "third great %sgrandson", "fourth great %sgrandson", "fifth great %sgrandson", "sixth great %sgrandson", "seventh great %sgrandson", "eighth great %sgrandson", "ninth great %sgrandson", "tenth great %sgrandson", "eleventh great %sgrandson", "twelfth great %sgrandson", "thirteenth great %sgrandson", "fourteenth great %sgrandson", "fifteenth great %sgrandson", "sixteenth great %sgrandson", "seventeenth great %sgrandson", "eighteenth great %sgrandson", "nineteenth great %sgrandson", "twentieth great %sgrandson", ] _daughter_level = [ "", "%sdaughter", "%sgranddaughter", "great %sgranddaughter", "second great %sgranddaughter", "third great %sgranddaughter", "fourth great %sgranddaughter", "fifth great %sgranddaughter", "sixth great %sgranddaughter", "seventh great %sgranddaughter", "eighth great %sgranddaughter", "ninth great %sgranddaughter", "tenth great %sgranddaughter", "eleventh great %sgranddaughter", "twelfth great %sgranddaughter", "thirteenth great %sgranddaughter", "fourteenth great %sgranddaughter", "fifteenth great %sgranddaughter", "sixteenth great %sgranddaughter", "seventeenth great %sgranddaughter", "eighteenth great %sgranddaughter", "nineteenth great %sgranddaughter", "twentieth great %sgranddaughter", ] _sister_level = [ "", "%ssister%s", "%saunt%s", "%sgrandaunt%s", "great %sgrandaunt%s", "second great %sgrandaunt%s", "third great %sgrandaunt%s", "fourth great %sgrandaunt%s", "fifth great %sgrandaunt%s", "sixth great %sgrandaunt%s", "seventh great %sgrandaunt%s", "eighth great %sgrandaunt%s", "ninth great %sgrandaunt%s", "tenth great %sgrandaunt%s", "eleventh great %sgrandaunt%s", "twelfth great %sgrandaunt%s", "thirteenth great %sgrandaunt%s", "fourteenth great %sgrandaunt%s", "fifteenth great %sgrandaunt%s", "sixteenth great %sgrandaunt%s", "seventeenth great %sgrandaunt%s", "eighteenth great %sgrandaunt%s", "nineteenth great %sgrandaunt%s", "twentieth great %sgrandaunt%s", ] _brother_level = [ "", "%sbrother%s", "%suncle%s", "%sgranduncle%s", "great %sgranduncle%s", "second great %sgranduncle%s", "third great %sgranduncle%s", "fourth great %sgranduncle%s", "fifth great %sgranduncle%s", "sixth great %sgranduncle%s", "seventh great %sgranduncle%s", "eighth great %sgranduncle%s", "ninth great %sgranduncle%s", "tenth great %sgranduncle%s", "eleventh great %sgranduncle%s", "twelfth great %sgranduncle%s", "thirteenth great %sgranduncle%s", "fourteenth great %sgranduncle%s", "fifteenth great %sgranduncle%s", "sixteenth great %sgranduncle%s", "seventeenth great %sgranduncle%s", "eighteenth great %sgranduncle%s", "nineteenth great %sgranduncle%s", "twentieth great %sgranduncle%s", ] _nephew_level = [ "", "%snephew%s", "%sgrandnephew%s", "great %sgrandnephew%s", "second great %sgrandnephew%s", "third great %sgrandnephew%s", "fourth great %sgrandnephew%s", "fifth great %sgrandnephew%s", "sixth great %sgrandnephew%s", "seventh great %sgrandnephew%s", "eighth great %sgrandnephew%s", "ninth great %sgrandnephew%s", "tenth great %sgrandnephew%s", "eleventh great %sgrandnephew%s", "twelfth great %sgrandnephew%s", "thirteenth great %sgrandnephew%s", "fourteenth great %sgrandnephew%s", "fifteenth great %sgrandnephew%s", "sixteenth great %sgrandnephew%s", "seventeenth great %sgrandnephew%s", "eighteenth great %sgrandnephew%s", "nineteenth great %sgrandnephew%s", "twentieth great %sgrandnephew%s", ] _niece_level = [ "", "%sniece%s", "%sgrandniece%s", "great %sgrandniece%s", "second great %sgrandniece%s", "third great %sgrandniece%s", "fourth great %sgrandniece%s", "fifth great %sgrandniece%s", "sixth great %sgrandniece%s", "seventh great %sgrandniece%s", "eighth great %sgrandniece%s", "ninth great %sgrandniece%s", "tenth great %sgrandniece%s", "eleventh great %sgrandniece%s", "twelfth great %sgrandniece%s", "thirteenth great %sgrandniece%s", "fourteenth great %sgrandniece%s", "fifteenth great %sgrandniece%s", "sixteenth great %sgrandniece%s", "seventeenth great %sgrandniece%s", "eighteenth great %sgrandniece%s", "nineteenth great %sgrandniece%s", "twentieth great %sgrandniece%s", ] _children_level = [ "", "children", "grandchildren", "great grandchildren", "second great grandchildren", "third great grandchildren", "fourth great grandchildren", "fifth great grandchildren", "sixth great grandchildren", "seventh great grandchildren", "eighth great grandchildren", "ninth great grandchildren", "tenth great grandchildren", "eleventh great grandchildren", "twelfth great grandchildren", "thirteenth great grandchildren", "fourteenth great grandchildren", "fifteenth great grandchildren", "sixteenth great grandchildren", "seventeenth great grandchildren", "eighteenth great grandchildren", "nineteenth great grandchildren", "twentieth great grandchildren", ] _siblings_level = [ "", "siblings", "uncles/aunts", "granduncles/aunts", "great granduncles/aunts", "second great granduncles/aunts", "third great granduncles/aunts", "fourth great granduncles/aunts", "fifth great granduncles/aunts", "sixth great granduncles/aunts", "seventh great granduncles/aunts", "eighth great granduncles/aunts", "ninth great granduncles/aunts", "tenth great granduncles/aunts", "eleventh great granduncles/aunts", "twelfth great granduncles/aunts", "thirteenth great granduncles/aunts", "fourteenth great granduncles/aunts", "fifteenth great granduncles/aunts", "sixteenth great granduncles/aunts", "seventeenth great granduncles/aunts", "eighteenth great granduncles/aunts", "nineteenth great granduncles/aunts", "twentieth great granduncles/aunts", ] _sibling_level = [ "", "%ssibling%s", "%suncle/aunt%s", "%sgranduncle/aunt%s", "great %sgranduncle/aunt%s", "second great %sgranduncle/aunt%s", "third great %sgranduncle/aunt%s", "fourth great %sgranduncle/aunt%s", "fifth great %sgranduncle/aunt%s", "sixth great %sgranduncle/aunt%s", "seventh great %sgranduncle/aunt%s", "eighth great %sgranduncle/aunt%s", "ninth great %sgranduncle/aunt%s", "tenth great %sgranduncle/aunt%s", "eleventh great %sgranduncle/aunt%s", "twelfth great %sgranduncle/aunt%s", "thirteenth great %sgranduncle/aunt%s", "fourteenth great %sgranduncle/aunt%s", "fifteenth great %sgranduncle/aunt%s", "sixteenth great %sgranduncle/aunt%s", "seventeenth great %sgranduncle/aunt%s", "eighteenth great %sgranduncle/aunt%s", "nineteenth great %sgranduncle/aunt%s", "twentieth great %sgranduncle/aunt%s", ] _nephews_nieces_level = [ "", "siblings", "nephews/nieces", "grandnephews/nieces", "great grandnephews/nieces", "second great grandnephews/nieces", "third great grandnephews/nieces", "fourth great grandnephews/nieces", "fifth great grandnephews/nieces", "sixth great grandnephews/nieces", "seventh great grandnephews/nieces", "eighth great grandnephews/nieces", "ninth great grandnephews/nieces", "tenth great grandnephews/nieces", "eleventh great grandnephews/nieces", "twelfth great grandnephews/nieces", "thirteenth great grandnephews/nieces", "fourteenth great grandnephews/nieces", "fifteenth great grandnephews/nieces", "sixteenth great grandnephews/nieces", "seventeenth great grandnephews/nieces", "eighteenth great grandnephews/nieces", "nineteenth great grandnephews/nieces", "twentieth great grandnephews/nieces", ] #------------------------------------------------------------------------- # # # #------------------------------------------------------------------------- MAX_DEPTH = 15 class RelationshipCalculator: REL_MOTHER = 'm' # going up to mother REL_FATHER = 'f' # going up to father REL_MOTHER_NOTBIRTH = 'M' # going up to mother, not birth relation REL_FATHER_NOTBIRTH = 'F' # going up to father, not birth relation REL_SIBLING = 's' # going sideways to sibling (no parents) REL_FAM_BIRTH = 'a' # going up to family (mother and father) REL_FAM_NONBIRTH = 'A' # going up to family, not birth relation REL_FAM_BIRTH_MOTH_ONLY = 'b' # going up to fam, only birth rel to mother REL_FAM_BIRTH_FATH_ONLY = 'c' # going up to fam, only birth rel to father REL_FAM_INLAW_PREFIX = 'L' # going to the partner. #sibling types NORM_SIB = 0 HALF_SIB = 1 STEP_SIB = 2 UNKNOWN_SIB = 3 #partner types PARTNER_MARRIED = 1 PARTNER_UNMARRIED = 2 PARTNER_CIVIL_UNION = 3 PARTNER_UNKNOWN_REL = 4 PARTNER_EX_MARRIED = 5 PARTNER_EX_UNMARRIED = 6 PARTNER_EX_CIVIL_UNION = 7 PARTNER_EX_UNKNOWN_REL = 8 def __init__(self): pass def get_parents(self, level): if level>len(_parents_level)-1: return "distant ancestors (%d generations)" % level else: return _parents_level[level] def get_father(self, level, step='', inlaw=''): if level>len(_father_level)-1: return "distant %sancestor%s (%d generations)" % (step, inlaw, level) else: return _father_level[level] % (step, inlaw) def get_son(self, level, step=''): if level>len(_son_level)-1: return "distant %sdescendant (%d generations)" % (step, level) else: return _son_level[level] % step def get_mother(self, level, step='', inlaw=''): if level>len(_mother_level)-1: return "distant %sancestor%s (%d generations)" % (step, inlaw, level) else: return _mother_level[level] % (step, inlaw) def get_daughter(self, level, step=''): if level>len(_daughter_level)-1: return "distant %sdescendant (%d generations)" % (step, level) else: return _daughter_level[level] % step def get_parent_unknown(self, level, step='', inlaw=''): if level < len(_level_name): return _level_name[level] + ' ' + '%sancestor%s' % (step, inlaw) else: return "distant %sancestor%s (%d generations)" % (step, inlaw, level) def get_child_unknown(self, level, step=''): if level < len(_level_name): return _level_name[level] + ' ' + '%sdescendant' % step else: return "distant %sdescendant (%d generations)" % (step, level) def get_aunt(self, level, step='', inlaw=''): if level>len(_sister_level)-1: return "distant %saunt%s" % (step, inlaw) else: return _sister_level[level] % (step, inlaw) def get_uncle(self, level, step='', inlaw=''): if level>len(_brother_level)-1: return "distant %suncle%s" % (step, inlaw) else: return _brother_level[level] % (step, inlaw) def get_sibling(self, level, step='', inlaw=''): if level < len(_sibling_level): return _sibling_level[level] % (step, inlaw) else: return "distant %suncle%s/%saunt%s " % (step, inlaw, step, inlaw) def get_sibling_type(self, db, orig, other): """ Translation free determination of type of orig and other as siblings The procedure returns sibling types, these can be passed to get_sibling_relationship_string. Only call this method if known that orig and other are siblings """ fatherorig, motherorig = self._get_birth_parents(db, orig) fatherother, motherother = self._get_birth_parents(db, other) if fatherorig and motherorig and fatherother and motherother: if fatherother == fatherorig and motherother == motherorig: return self.NORM_SIB elif fatherother == fatherorig or motherother == motherorig: return self.HALF_SIB else : return self.STEP_SIB else: #other has unknown father or mother, # or orig has unknown father or mother, hence we cannot know # how the siblings are related: return self.UNKNOWN_SIB def _get_birth_parents(self, db, person): """ method that returns the birthparents of a person as tuple (mother handle, father handle), if no known birthparent, the handle is replaced by None """ birthfather = None birthmother = None for f in person.get_parent_family_handle_list(): family = db.get_family_from_handle(f) childrel = [(ref.get_mother_relation(), ref.get_father_relation()) for ref in family.get_child_ref_list() if ref.ref == person.handle] if not birthmother and childrel[0][0] == gen.lib.ChildRefType.BIRTH: birthmother = family.get_mother_handle() if not birthfather and childrel[0][1] == gen.lib.ChildRefType.BIRTH: birthfather = family.get_father_handle() if birthmother and birthfather: break return (birthmother, birthfather) def get_nephew(self, level, step='', inlaw=''): if level>len(_nephew_level)-1: return "distant %snephew%s" % (step, inlaw) else: return _nephew_level[level] % (step, inlaw) def get_niece(self, level, step='', inlaw=''): if level>len(_niece_level)-1: return "distant %sniece%s" % (step, inlaw) else: return _niece_level[level] % (step, inlaw) def get_cousin(self, level, removed, dir = '', step='', inlaw=''): if removed == 0 and level < len(_level_name): return "%s %scousin%s" % (_level_name[level], step, inlaw) elif removed > len(_removed_level)-1 or level>len(_level_name)-1: return "distant %srelative%s" % (step, inlaw) else: return "%s %scousin%s%s%s" % (_level_name[level], step, inlaw, _removed_level[removed], dir) def get_spouse_type(self, db, orig, other, all_rel = False): """ Translation free determination if orig and other are partners. The procedure returns partner types, these can be passed to get_partner_relationship_string. If all_rel=False, returns None or a partner type. If all_rel=True, returns a list, empty if no partner """ val = [] for f in orig.get_family_handle_list(): family = db.get_family_from_handle(f) # return first found spouse type if family and other.get_handle() in [family.get_father_handle(), family.get_mother_handle()]: family_rel = family.get_relationship() #check for divorce event: ex = False for eventref in family.get_event_ref_list(): event = db.get_event_from_handle(eventref.ref) if event and (event.get_type() == gen.lib.EventType.DIVORCE or event.get_type() == gen.lib.EventType.ANNULMENT): ex = True break if family_rel == gen.lib.FamilyRelType.MARRIED: if ex: val.append(self.PARTNER_EX_MARRIED) else: val.append(self.PARTNER_MARRIED) elif family_rel == gen.lib.FamilyRelType.UNMARRIED: if ex: val.append(self.PARTNER_EX_UNMARRIED) else: val.append(self.PARTNER_UNMARRIED) elif family_rel == gen.lib.FamilyRelType.CIVIL_UNION: if ex: val.append(self.PARTNER_EX_CIVIL_UNION) else: val.append(self.PARTNER_CIVIL_UNION) else: if ex: val.append(self.PARTNER_EX_UNKNOWN_REL) else: val.append(self.PARTNER_UNKNOWN_REL) if all_rel : return val else: #last relation is normally the defenitive relation if val: return val[-1] else: return None def is_spouse(self, db, orig, other, all_rel=False): """ determine the spouse relation """ type = self.get_spouse_type(db, orig, other, all_rel) if type: return self.get_partner_relationship_string(type, orig.get_gender(), other.get_gender()) else: return None def get_relationship_distance_old(self, db, orig_person, other_person): """ ** DEPRECATED -- USE NEW ** NOTE: CHANGED ORDER OF RETURN, now first is rel to orig, second to other (as it should, but wasn't !! ) Returns a tuple (firstRel, secondRel, common): firstRel Number of generations from the orig_person to their closest common ancestors, as eg 'ffmm' secondRel Number of generations from the other_person to that firstRel closest common ancestors, as eg 'ffmm' common list of all these common ancestors (so same generation difference with firstRel), no specific order !! in the Rel, f is father, m is mother """ print "get_relationship_distance_old is deprecated, use new instead!" firstRel = -1 secondRel = -1 common = [] firstMap = {} firstList = [] secondMap = {} secondList = [] rank = 9999999 try: self.__apply_filter(db, orig_person, '', firstList, firstMap) self.__apply_filter(db, other_person, '', secondList, secondMap) except RuntimeError: return (firstRel, secondRel, _("Relationship loop detected")) for person_handle in firstList: if person_handle in secondList: new_rank = len(firstMap[person_handle]) if new_rank < rank: rank = new_rank common = [ person_handle ] elif new_rank == rank: common.append(person_handle) if common: person_handle = common[0] firstRel = firstMap[person_handle] secondRel = secondMap[person_handle] return (firstRel,secondRel,common) def __apply_filter(self, db, person, rel_str, plist, pmap, depth=1): if person == None or depth > MAX_DEPTH: return depth += 1 plist.append(person.handle) pmap[person.handle] = rel_str # ?? this overwrites if person is double! family_handle = person.get_main_parents_family_handle() try: if family_handle: family = db.get_family_from_handle(family_handle) fhandle = family.father_handle if fhandle: father = db.get_person_from_handle(fhandle) self.__apply_filter(db, father, rel_str+'f', plist, pmap, depth) mhandle = family.mother_handle if mhandle: mother = db.get_person_from_handle(mhandle) self.__apply_filter(db, mother, rel_str+'m', plist, pmap, depth) except: return def get_relationship_distance(self, db, orig_person, other_person): """ wrapper around get_relationship_distance_new to return a value like the old method (note, firstRel is now to orig person, not to other as it was in 2.2.x series !!!) *** DO NOT USE, IS INCORRECT IN SOME CASES, eg person common ancestor along two paths, only one returned, however this should not matter for number of generation or last gender, eg firstRel is 'ffffm' or 'mmfmm', only one returned *** Returns a tuple (firstRel, secondRel, common): firstRel Number of generations from the orig_person to their closest common ancestors, as eg 'ffmm' secondRel Number of generations from the other_person to that firstRel closest common ancestors, as eg 'ffmm' common list of all these common ancestors (so same generation difference with firstRel), no specific order !! in the Rel, f is father, m is mother """ warn( "Use get_relationship_distance_new or get_one_relationship", DeprecationWarning, 2) firstRel = -1 secondRel = -1 common = [] rank = 9999999 data, msg = self.get_relationship_distance_new( db, orig_person, other_person, all_dist=True, all_families=False, only_birth=True) #data is sorted on rank, we need closest to orig instead if data[0][0] == -1 : return firstRel, secondRel, common for common_anc in data : # common_anc looks like: #(total dist, handle_common, 'ffffff', [0,0,0,0,0,0],'ff',[0, 0]) #where 2&3 are related to orig_pers, 4&5 other_pers new_rank = len(common_anc[2]) if new_rank < rank: rank = new_rank common = [ common_anc[1] ] firstRel = common_anc[2] secondRel = common_anc[4] elif new_rank == rank: common.append( common_anc[1] ) return (firstRel, secondRel, common) def get_relationship_distance_new(self, db, orig_person, other_person, all_families=False, all_dist=False, only_birth=True, max_depth = MAX_DEPTH): """ Returns if all_dist == True a 'tuple, string': (rank, person handle, firstRel_str, firstRel_fam, secondRel_str, secondRel_fam), msg or if all_dist == True a 'list of tuple, string': [.....], msg: The tuple or list of tuples consists of: *rank Total number of generations from common ancestor to the two persons, rank is -1 if no relations found *person_handle The Common ancestor *firstRel_str String with the path to the common ancestor from orig Person *firstRel_fam Family numbers along the path as a list, eg [0,0,1]. For parent in multiple families, eg [0. [0, 2], 1] *secondRel_str String with the path to the common ancestor from otherPerson *secondRel_fam Family numbers along the path, eg [0,0,1]. For parent in multiple families, eg [0. [0, 2], 1] *msg List of messages indicating errors. Empyt list if no errors. Example: firstRel_str = 'ffm' and firstRel_fam = [2,0,1] means common ancestor is mother of the second family of the father of the first family of the father of the third family. Note that the same person might be present twice if the person is reached via a different branch too. Path (firstRel_str and secondRel_str) will of course be different @param db: database to work on @param orig_person: first person @type orig_person: Person Obj @param other_person: second person, relation is sought between first and second person @type other_person: Person Obj @param all_families: if False only Main family is searched, otherwise all families are used @type all_families: bool @param all_dist: if False only the shortest distance is returned, otherwise all relationships @type all_dist: bool @param only_birth: if True only parents with birth relation are considered @type only_birth: bool @param max_depth: how many generations deep do we search? @type max_depth: int """ #data storage to communicate with recursive functions self.__maxDepthReached = False self.__loopDetected = False self.__max_depth = max_depth self.__all_families = all_families self.__all_dist = all_dist self.__only_birth = only_birth firstRel = -1 secondRel = -1 common_str = [] common_fam = [] self.__msg = [] common = [] firstMap = {} firstList = [] secondMap = {} secondList = [] rank = 9999999 try: self.__apply_filter_new(db, orig_person, '', [], firstMap) self.__apply_filter_new(db, other_person, '', [], secondMap, stoprecursemap = firstMap, store_all=False) ## print firstMap ## print secondMap except RuntimeError: return (-1,None,-1,[],-1,[] ) , \ [_("Relationship loop detected")] + self.__msg for person_handle in secondMap.keys() : if firstMap.has_key(person_handle) : com = [] #a common ancestor for rel1,fam1 in zip(firstMap[person_handle][0], firstMap[person_handle][1]): l1 = len(rel1) for rel2,fam2 in zip(secondMap[person_handle][0], secondMap[person_handle][1]): l2 = len(rel2) #collect paths to arrive at common ancestor com.append((l1+l2,person_handle,rel1,fam1, rel2,fam2)) #insert common ancestor in correct position, # if shorter links, check if not subset # if longer links, check if not superset pos=0 for ranknew,handlenew,rel1new,fam1new,rel2new,fam2new in com : insert = True for rank, handle, rel1, fam1, rel2, fam2 in common : if ranknew < rank : break elif ranknew >= rank : #check subset if rel1 == rel1new[:len(rel1)] and \ rel2 == rel2new[:len(rel2)] : #subset relation exists already insert = False break pos += 1 if insert : if common : common.insert(pos, (ranknew, handlenew, rel1new, fam1new,rel2new,fam2new)) else: common = [(ranknew, handlenew, rel1new, fam1new, rel2new, fam2new)] #now check if superset must be deleted from common deletelist=[] index = pos+1 for rank,handle,rel1,fam1,rel2,fam2 in common[pos+1:]: if rel1new == rel1[:len(rel1new)] and \ rel2new == rel2[:len(rel2new)] : deletelist.append(index) index += 1 deletelist.reverse() for index in deletelist: del common[index] #check for extra messages if self.__maxDepthReached : self.__msg += [_('Family tree reaches back more than the maximum ' '%d generations searched.\nIt is possible that ' 'relationships have been missed') % (max_depth)] ## print 'common list :', common if common and not self.__all_dist : rank = common[0][0] person_handle = common[0][1] firstRel = common[0][2] firstFam = common[0][3] secondRel = common[0][4] secondFam = common[0][5] return (rank,person_handle,firstRel,firstFam,secondRel,secondFam),\ self.__msg if common : #list with tuples (rank,handle person,rel_str_orig,rel_fam_orig, # rel_str_other,rel_fam_str) and messages return common, self.__msg if not self.__all_dist : return (-1,None,'',[],'',[]), self.__msg else : return [(-1,None,'',[],'',[])], self.__msg def __apply_filter_new(self, db, person, rel_str, rel_fam, pmap, depth=1, stoprecursemap=None, store_all=True): '''We recursively add parents of person in pmap with correct rel_str, if store_all. If store_all false, only store parents if in the stoprecursemap. Stop recursion if parent is in the stoprecursemap (no need to look parents of otherpers if done so already for origpers) store pers ''' if person == None or not person.handle : return if depth > self.__max_depth: self.__maxDepthReached = True print 'Maximum ancestor generations ('+str(depth)+') reached', \ '(' + rel_str + ').',\ 'Stopping relation algorithm.' return depth += 1 commonancestor = False if stoprecursemap and stoprecursemap.has_key(person.handle) : commonancestor = True #add person to the map, take into account that person can be obtained #from different sides if pmap.has_key(person.handle) : #person is already a grandparent in another branch, we already have # had lookup of all parents pmap[person.handle][0] += [rel_str] pmap[person.handle][1] += [rel_fam] #check if there is no loop father son of his son, ... # loop means person is twice reached, same rel_str in begin for rel1 in pmap[person.handle][0]: for rel2 in pmap[person.handle][0] : if len(rel1) < len(rel2) and \ rel1 == rel2[:len(rel1)]: #loop, keep one message in storage! self.__loopDetected = True self.__msg += [_("Relationship loop detected:") + \ _("Person %s connects to himself via %s") % \ (person.get_primary_name().get_name(), rel2[len(rel1):])] return elif store_all or commonancestor: pmap[person.handle] = [[rel_str],[rel_fam]] #having added person to the pmap, we only look up recursively to # parents if this person is not common relative if commonancestor : ## print 'common ancestor found' return family_handles = [] main = person.get_main_parents_family_handle() ## print 'main',main if main : family_handles = [main] if self.__all_families : family_handles = person.get_parent_family_handle_list() ## print 'all_families', family_handles try: parentstodo = {} fam = 0 for family_handle in family_handles : rel_fam_new = rel_fam +[fam] family = db.get_family_from_handle(family_handle) #obtain childref for this person childrel = [(ref.get_mother_relation(), ref.get_father_relation()) for ref in family.get_child_ref_list() if ref.ref == person.handle] fhandle = family.father_handle mhandle = family.mother_handle for data in [(fhandle, self.REL_FATHER, self.REL_FATHER_NOTBIRTH, childrel[0][1]), (mhandle, self.REL_MOTHER, self.REL_MOTHER_NOTBIRTH, childrel[0][0])]: if data[0] and not data[0] in parentstodo.keys() : persontodo = db.get_person_from_handle(data[0]) if data[3] == gen.lib.ChildRefType.BIRTH : addstr = data[1] elif not self.__only_birth : addstr = data[2] else : addstr = '' if addstr : parentstodo[data[0]] = (persontodo, rel_str + addstr, rel_fam_new) elif data [0] and data[0] in parentstodo.keys(): #this person is already scheduled to research #update family list famlist = parentstodo[data[0]][2] if not isinstance(famlist[-1], list) and \ not fam == famlist[-1]: famlist = famlist[:-1] + [[famlist[-1]]] if isinstance(famlist[-1], list) and \ fam not in famlist[-1] : famlist = famlist[:-1] + [famlist[-1] + [fam]] parentstodo[data[0]] = (parentstodo[data[0]][0], parentstodo[data[0]][1], famlist ) if not fhandle and not mhandle and stoprecursemap is None: #family without parents, add brothers for orig person #other person has recusemap, and will stop when seeing #the brother. child_list = [ref.ref for ref in family.get_child_ref_list() if ref.ref != person.handle] addstr = self.REL_SIBLING for chandle in child_list : if pmap.has_key(chandle) : pmap[chandle][0] += [rel_str + addstr] pmap[chandle][1] += [rel_fam_new] #person is already a grandparent in another branch else: pmap[chandle] = [[rel_str+addstr],[rel_fam_new]] fam += 1 for handle in parentstodo.keys(): data = parentstodo[handle] self.__apply_filter_new(db, data[0], data[1], data[2], pmap, depth, stoprecursemap, store_all) except: import traceback print traceback.print_exc() return def collapse_relations(self, relations): """ Internal method to condense the relationships as returned by get_relationship_distance_new. Common ancestors in the same family are collapsed to one entry, changing the person paths to family paths, eg 'mf' and 'mm' become 'ma' relations : list of relations as returned by get_relationship_distance_new with all_dist = True returns : the same data as relations, but collapsed, hence the handle entry is now a list of handles, and the path to common ancestors can now contain family identifiers (eg 'a', ...) In the case of sibling, this is replaced by family with common ancestor handles empty list []! """ if relations[0][0] == -1 : return relations commonnew = [] existing_path = [] for relation in relations: relstrfirst = None commonhandle = [relation[1]] if relation[2] : relstrfirst = relation[2][:-1] relstrsec = None if relation[4] : relstrsec = relation[4][:-1] relfamfirst = relation[3][:] relfamsec = relation[5][:] #handle pure sibling: if relation[2] and relation[2][-1] == self.REL_SIBLING: #sibling will be the unique common ancestor, #change to a family with unknown handle for common ancestor relation[2] = relation[2][:-1] + self.REL_FAM_BIRTH relation[4] = relation[4] + self.REL_FAM_BIRTH relfamsec = relfamsec + [relfamfirst[-1]] relstrsec = relation[4][:-1] commonhandle = [] # a unique path to family of common person: familypaths = [] if relfamfirst and isinstance(relfamfirst[-1], list): if relfamsec and isinstance(relfamsec[-1], list): for val1 in relfamfirst[-1]: for val2 in relfamsec[-1]: familypaths.append((relstrfirst, relstrsec, relfamfirst[:-1] + [val1], relfamsec[:-1] + [val2])) else: for val1 in relfamfirst[-1]: familypaths.append((relstrfirst, relstrsec, relfamfirst[:-1] + [val1], relfamsec)) elif relfamsec and isinstance(relfamsec[-1], list): for val2 in relfamsec[-1]: familypaths.append((relstrfirst, relstrsec, relfamfirst, relfamsec[:-1] + [val2])) else: familypaths.append((relstrfirst, relstrsec, relfamfirst, relfamsec)) ##print 'resulting fam path', familypaths for familypath in familypaths: #familypath = (relstrfirst, relstrsec, relfamfirst, relfamsec) try: posfam = existing_path.index(familypath) except ValueError: posfam = None #if relstr is '', the ancestor is unique, if posfam None, # first time we see this family path if (posfam is not None and relstrfirst is not None and relstrsec is not None): #we already have a common ancestor of this family, just add the #other, setting correct family relation tmp = commonnew[posfam] frstcomstr = relation[2][-1] scndcomstr = tmp[2][-1] newcomstra = self.famrel_from_persrel(frstcomstr, scndcomstr) frstcomstr = relation[4][-1] scndcomstr = tmp[4][-1] newcomstrb = self.famrel_from_persrel(frstcomstr, scndcomstr) commonnew[posfam] = (tmp[0], tmp[1]+commonhandle, relation[2][:-1]+newcomstra, tmp[3], relation[4][:-1]+newcomstrb, tmp[5]) else : existing_path.append(familypath) commonnew.append((relation[0], commonhandle, relation[2], relfamfirst, relation[4], relfamsec) ) ##print 'commonnew', commonnew return commonnew def famrel_from_persrel(self, persrela, persrelb): """ Conversion from eg 'f' and 'm' to 'a', so relation to the two persons of a common family is converted to a family relation """ if persrela == persrelb: #should not happen, procedure called in error, just return value return persrela if (persrela == self.REL_MOTHER and persrelb == self.REL_FATHER) or \ (persrelb == self.REL_MOTHER and persrela == self.REL_FATHER): return self.REL_FAM_BIRTH if (persrela == self.REL_MOTHER and persrelb == self.REL_FATHER_NOTBIRTH) or \ (persrelb == self.REL_MOTHER and persrela == self.REL_FATHER_NOTBIRTH): return self.REL_FAM_BIRTH_MOTH_ONLY if (persrela == self.REL_FATHER and persrelb == self.REL_MOTHER_NOTBIRTH) or \ (persrelb == self.REL_FATHER and persrela == self.REL_MOTHER_NOTBIRTH): return self.REL_FAM_BIRTH_FATH_ONLY #catch calling with family relations already, return val if (persrela == self.REL_FAM_BIRTH or persrela == self.REL_FAM_BIRTH_FATH_ONLY or persrela == self.REL_FAM_BIRTH_MOTH_ONLY or persrela == self.REL_FAM_NONBIRTH): return persrela if (persrelb == self.REL_FAM_BIRTH or persrelb == self.REL_FAM_BIRTH_FATH_ONLY or persrelb == self.REL_FAM_BIRTH_MOTH_ONLY or persrelb == self.REL_FAM_NONBIRTH): return persrelb return self.REL_FAM_NONBIRTH def only_birth(self, path): """ given a path to common ancestor. Return True if only birth relations, False otherwise """ only_birth = True for str in path: only_birth = only_birth and (str not in [self.REL_FAM_NONBIRTH, self.REL_FATHER_NOTBIRTH, self.REL_MOTHER_NOTBIRTH]) return only_birth def get_one_relationship(self, db, orig_person, other_person): """ returns a string representing the most relevant relationship between the two people """ if orig_person == None: return _("undefined") if orig_person.get_handle() == other_person.get_handle(): return '' is_spouse = self.is_spouse(db, orig_person, other_person) if is_spouse: return is_spouse data, msg = self.get_relationship_distance_new( db, orig_person, other_person, all_dist=True, all_families=True, only_birth=False) if data[0][0] == -1: return '' data = self.collapse_relations(data) #most relevant relationship is a birth family relation of lowest rank databest = [data[0]] rankbest = data[0][0] for rel in data : if rel[0] == rankbest: databest.append(rel) rel = databest[0] dist_orig = len(rel[2]) dist_other= len(rel[4]) if len(databest) == 1: birth = self.only_birth(rel[2]) and self.only_birth(rel[4]) if dist_orig == 1 and dist_other == 1: return self.get_sibling_relationship_string( self.get_sibling_type( db, orig_person, other_person), orig_person.get_gender(), other_person.get_gender()) return self.get_single_relationship_string(dist_orig, dist_other, orig_person.get_gender(), other_person.get_gender(), rel[2], rel[4], only_birth=birth, in_law_a=False, in_law_b=False) else: order = [self.REL_FAM_BIRTH, self.REL_FAM_BIRTH_MOTH_ONLY, self.REL_FAM_BIRTH_FATH_ONLY, self.REL_MOTHER, self.REL_FATHER, self.REL_SIBLING, self.REL_FAM_NONBIRTH, self.REL_MOTHER_NOTBIRTH, self.REL_FATHER_NOTBIRTH] orderbest = order.index(self.REL_MOTHER) for relother in databest: relbirth = self.only_birth(rel[2]) and self.only_birth(rel[4]) if relother[2] == '' or relother[4]== '': #direct relation, take that rel = relother break if not relbirth and self.only_birth(relother[2]) \ and self.only_birth(relother[4]) : #birth takes precedence rel = relother continue if order.index(relother[2][-1]) < order.index(rel[2][-1]) and\ order.index(relother[2][-1]) < orderbest: rel = relother continue if order.index(relother[4][-1]) < order.index(rel[4][-1]) and\ order.index(relother[4][-1]) < orderbest: rel = relother continue if order.index(rel[2][-1]) < orderbest or \ order.index(rel[4][-1]) < orderbest: #keep the good one continue if order.index(relother[2][-1]) < order.index(rel[2][-1]): rel = relother continue if order.index(relother[2][-1]) == order.index(rel[2][-1]) and\ order.index(relother[4][-1]) < order.index(rel[4][-1]): rel = relother continue dist_orig = len(rel[2]) dist_other= len(rel[4]) birth = self.only_birth(rel[2]) and self.only_birth(rel[4]) if dist_orig == 1 and dist_other == 1: return self.get_sibling_relationship_string( self.get_sibling_type( db, orig_person, other_person), orig_person.get_gender(), other_person.get_gender()) return self.get_single_relationship_string(dist_orig, dist_other, orig_person.get_gender(), other_person.get_gender(), rel[2], rel[4], only_birth=birth, in_law_a=False, in_law_b=False) def get_relationship(self, db, orig_person, other_person): """ returns a string representing the relationshp between the two people, along with a list of common ancestors (typically father,mother) """ if orig_person == None: return (_("undefined"),[]) if orig_person.get_handle() == other_person.get_handle(): return ('', []) is_spouse = self.is_spouse(db,orig_person,other_person) (firstRel,secondRel,common) = \ self.get_relationship_distance(db,orig_person,other_person) if type(common) == types.StringType or \ type(common) == types.UnicodeType: if is_spouse: return (is_spouse,[]) else: return (common,[]) elif common: person_handle = common[0] else: if is_spouse: return (is_spouse,[]) else: return ("",[]) #distance from common ancestor to the people dist_orig = len(firstRel) dist_other= len(secondRel) rel_str = self.get_single_relationship_string(dist_orig, dist_other, orig_person.get_gender(), other_person.get_gender(), firstRel, secondRel ) if is_spouse: return (_('%(spouse_relation)s and %(other_relation)s') % { 'spouse_relation': is_spouse, 'other_relation': rel_str} , common ) else: return (rel_str, common) def get_grandparents_string(self,db,orig_person,other_person): """ returns a string representing the relationshp between the two people, along with a list of common ancestors (typically father,mother) """ if orig_person == None: return ("undefined",[]) if orig_person == other_person: return ('', []) (firstRel,secondRel,common) = \ self.get_relationship_distance(db,orig_person,other_person) if type(common) == types.StringType or \ type(common) == types.UnicodeType: return (common,[]) elif common: person_handle = common[0] else: return ("",[]) if len(firstRel) == 0: if len(secondRel) == 0: return ('',common) else: return (self.get_parents(len(secondRel)),common) else: return None def get_plural_relationship_string(self, Ga, Gb): """ Provides a string that describes the relationsip between a person, and a group of people with the same relationship. E.g. "grandparents" or "children". Ga and Gb can be used to mathematically calculate the relationship. See the Wikipedia entry for more information: http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions @param Ga: The number of generations between the main person and the common ancestor. @type Ga: int @param Gb: The number of generations between the group of people and the common ancestor @type Gb: int @returns: A string describing the relationship between the person and the group. @rtype: str """ rel_str = "distant relatives" if Ga == 0: # These are descendants if Gb < len(_children_level): rel_str = _children_level[Gb] else: rel_str = "distant descendants" elif Gb == 0: # These are parents/grand parents if Ga < len(_parents_level): rel_str = _parents_level[Ga] else: rel_str = "distant ancestors" elif Gb == 1: # These are siblings/aunts/uncles if Ga < len(_siblings_level): rel_str = _siblings_level[Ga] else: rel_str = "distant uncles/aunts" elif Ga == 1: # These are nieces/nephews if Gb < len(_nephews_nieces_level): rel_str = _nephews_nieces_level[Gb] else: rel_str = "distant nephews/nieces" elif Ga > 1 and Ga == Gb: # These are cousins in the same generation if Ga <= len(_level_name): rel_str = "%s cousins" % _level_name[Ga-1] else: rel_str = "distant cousins" elif Ga > 1 and Ga > Gb: # These are cousins in different generations with the second person # being in a higher generation from the common ancestor than the # first person. if Gb <= len(_level_name) and (Ga-Gb) < len(_removed_level): rel_str = "%s cousins%s (up)" % ( _level_name[Gb-1], _removed_level[Ga-Gb] ) else: rel_str = "distant cousins" elif Gb > 1 and Gb > Ga: # These are cousins in different generations with the second person # being in a lower generation from the common ancestor than the # first person. if Ga <= len(_level_name) and (Gb-Ga) < len(_removed_level): rel_str = "%s cousins%s (down)" % ( _level_name[Ga-1], _removed_level[Gb-Ga] ) else: rel_str = "distant cousins" return rel_str def get_single_relationship_string(self, Ga, Gb, gender_a, gender_b, reltocommon_a, reltocommon_b, only_birth=True, in_law_a=False, in_law_b=False): """ Provides a string that describes the relationsip between a person, and another person. E.g. "grandparent" or "child". To be used as: 'person b is the grandparent of a', this will be in translation string : 'person b is the %(relation)s of a' Note that languages with gender should add 'the' inside the translation, so eg in french: 'person b est %(relation)s de a' where relation will be here: le grandparent Ga and Gb can be used to mathematically calculate the relationship. See the Wikipedia entry for more information: http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions Some languages need to know the specific path to the common ancestor. Those languages should use reltocommon_a and reltocommon_b which is a string like 'mfmf'. The possible string codes are: REL_MOTHER # going up to mother REL_FATHER # going up to father REL_MOTHER_NOTBIRTH # going up to mother, not birth relation REL_FATHER_NOTBIRTH # going up to father, not birth relation REL_FAM_BIRTH # going up to family (mother and father) REL_FAM_NONBIRTH # going up to family, not birth relation REL_FAM_BIRTH_MOTH_ONLY # going up to fam, only birth rel to mother REL_FAM_BIRTH_FATH_ONLY # going up to fam, only birth rel to father Prefix codes are stripped, so REL_FAM_INLAW_PREFIX is not present. If the relation starts with the inlaw of the person a, then 'in_law_a' is True, if it starts with the inlaw of person b, then 'in_law_b' is True. Also REL_SIBLING (# going sideways to sibling (no parents)) is not passed to this routine. The collapse_relations changes this to a family relation. Hence, calling routines should always strip REL_SIBLING and REL_FAM_INLAW_PREFIX before calling get_single_relationship_string() Note that only_birth=False, means that in the reltocommon one of the NOTBIRTH specifiers is present. The REL_FAM identifiers mean that the relation is not via a common ancestor, but via a common family (note that that is not possible for direct descendants or direct ancestors!). If the relation to one of the parents in that common family is by birth, then 'only_birth' is not set to False. The only_birth() method is normally used for this. @param Ga: The number of generations between the main person and the common ancestor. @type Ga: int @param Gb: The number of generations between the other person and the common ancestor @type Gb: int @param gender_a : gender of person a @type gender_a: int gender @param gender_b : gender of person b @type gender_b: int gender @param reltocommon_a : relation path to common ancestor or common Family for person a. Note that length = Ga @type reltocommon_a: str @param reltocommon_b : relation path to common ancestor or common Family for person b. Note that length = Gb @type reltocommon_b: str @param in_law_a : True if path to common ancestors is via the partner of person a @type in_law_a: bool @param in_law_b : True if path to common ancestors is via the partner of person b @type in_law_b: bool @param only_birth : True if relation between a and b is by birth only False otherwise @type only_birth: bool @returns: A string describing the relationship between the two people @rtype: str NOTE: 1/the self.REL_SIBLING should not be passed to this routine, so we should not check on it. All other self. 2/for better determination of siblings, use if Ga=1=Gb get_sibling_relationship_string """ ## print 'Ga, Gb :', Ga, Gb if only_birth: step = '' else: step = STEP if in_law_a or in_law_b : inlaw = INLAW else: inlaw = '' rel_str = "distant %srelative%s" % (step, inlaw) if Ga == 0: # b is descendant of a if Gb == 0 : rel_str = 'same person' elif Gb == 1 and inlaw: #inlaw children only exist up to first level: if gender_b == gen.lib.Person.MALE: rel_str = self.get_son(Gb, step)+inlaw elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_daughter(Gb, step)+inlaw else: rel_str = self.get_child_unknown(Gb, step)+inlaw elif gender_b == gen.lib.Person.MALE: rel_str = self.get_son(Gb, step) elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_daughter(Gb, step) else: rel_str = self.get_child_unknown(Gb, step) elif Gb == 0: # b is parents/grand parent of a if gender_b == gen.lib.Person.MALE: rel_str = self.get_father(Ga, step, inlaw) elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_mother(Ga, step, inlaw) else: rel_str = self.get_parent_unknown(Ga, step, inlaw) elif Gb == 1: # b is sibling/aunt/uncle of a if gender_b == gen.lib.Person.MALE: rel_str = self.get_uncle(Ga, step, inlaw) elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_aunt(Ga, step, inlaw) else: rel_str = self.get_sibling(Ga, step, inlaw) elif Ga == 1: # b is niece/nephew of a if gender_b == gen.lib.Person.MALE: rel_str = self.get_nephew(Gb, step, inlaw) elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_niece(Gb, step, inlaw) elif Gb < len(_niece_level) and Gb < len(_nephew_level): rel_str = "%s or %s" % (self.get_nephew(Gb, step, inlaw), self.get_niece(Gb, step, inlaw)) else: rel_str = "distant %snephews/nieces%s" % (step, inlaw) elif Ga == Gb: # a and b cousins in the same generation rel_str = self.get_cousin(Ga-1, 0, dir = '', step=step, inlaw=inlaw) elif Ga > Gb: # These are cousins in different generations with the second person # being in a higher generation from the common ancestor than the # first person. rel_str = self.get_cousin(Gb-1, Ga-Gb, dir = ' (up)', step=step, inlaw=inlaw) elif Gb > Ga: # These are cousins in different generations with the second person # being in a lower generation from the common ancestor than the # first person. rel_str = self.get_cousin(Ga-1, Gb-Ga, dir = ' (down)', step=step, inlaw=inlaw) return rel_str def get_sibling_relationship_string(self, sib_type, gender_a, gender_b, in_law_a=False, in_law_b=False): """ Determine the string giving the relation between two siblings of type sib_type. Eg: b is the brother of a Here 'brother' is the string we need to determine This method gives more details about siblings than get_single_relationship_string can do. DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR LANGUAGE, AND SAME METHODS EXIST (get_uncle, get_aunt, get_sibling) """ if sib_type == self.NORM_SIB or sib_type == self.UNKNOWN_SIB: typestr = '' elif sib_type == self.HALF_SIB: typestr = HALF elif sib_type == self.STEP_SIB: typestr = STEP if in_law_a or in_law_b : inlaw = INLAW else: inlaw = '' if gender_b == gen.lib.Person.MALE: rel_str = self.get_uncle(1, typestr, inlaw) elif gender_b == gen.lib.Person.FEMALE: rel_str = self.get_aunt(1, typestr, inlaw) else: rel_str = self.get_sibling(1, typestr, inlaw) return rel_str def get_partner_relationship_string(self, spouse_type, gender_a, gender_b): """ Determine the string giving the relation between two partnes of type spouse_type. Eg: b is the spouse of a Here 'spouse' is the string we need to determine DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR LANGUAGE, AS GETTEXT IS ALREADY USED ! """ #english only needs gender of b, we don't guess if unknown like in old # procedure as that is stupid in present day cases! gender = gender_b if not spouse_type: return '' if spouse_type == self.PARTNER_MARRIED: if gender == gen.lib.Person.MALE: return _("husband") elif gender == gen.lib.Person.FEMALE: return _("wife") else: return _("gender unknown|spouse") elif spouse_type == self.PARTNER_EX_MARRIED: if gender == gen.lib.Person.MALE: return _("ex-husband") elif gender == gen.lib.Person.FEMALE: return _("ex-wife") else: return _("gender unknown|ex-spouse") elif spouse_type == self.PARTNER_UNMARRIED: if gender == gen.lib.Person.MALE: return _("unmarried|husband") elif gender == gen.lib.Person.FEMALE: return _("unmarried|wife") else: return _("gender unknown,unmarried|spouse") elif spouse_type == self.PARTNER_EX_UNMARRIED: if gender == gen.lib.Person.MALE: return _("unmarried|ex-husband") elif gender == gen.lib.Person.FEMALE: return _("unmarried|ex-wife") else: return _("gender unknown,unmarried|ex-spouse") elif spouse_type == self.PARTNER_CIVIL_UNION: if gender == gen.lib.Person.MALE: return _("male,civil union|partner") elif gender == gen.lib.Person.FEMALE: return _("female,civil union|partner") else: return _("gender unknown,civil union|partner") elif spouse_type == self.PARTNER_EX_CIVIL_UNION: if gender == gen.lib.Person.MALE: return _("male,civil union|former partner") elif gender == gen.lib.Person.FEMALE: return _("female,civil union|former partner") else: return _("gender unknown,civil union|former partner") elif spouse_type == self.PARTNER_UNKNOWN_REL: if gender == gen.lib.Person.MALE: return _("male,unknown relation|partner") elif gender == gen.lib.Person.FEMALE: return _("female,unknown relation|partner") else: return _("gender unknown,unknown relation|partner") else: # here we have spouse_type == self.PARTNER_EX_UNKNOWN_REL # or other not catched types if gender == gen.lib.Person.MALE: return _("male,unknown relation|former partner") elif gender == gen.lib.Person.FEMALE: return _("female,unknown relation|former partner") else: return _("gender unknown,unknown relation|former partner") def _test(rc, onlybirth, inlawa, inlawb, printrelstr): """ this is a generic test suite for the singular relationship TRANSLATORS: do NOT translate, use __main__ ! """ import sys import random random.seed() def _rand_f_m(): if random.randint(0, 1) == 0 : return 'f' else: return 'm' def _rand_relstr(len, endstr): if len == 0: return '' else: relstr = '' for i in range(len-1): relstr += _rand_f_m() return relstr + endstr FMT = '%+50s' MAX = 30 #rc = RelationshipCalculator() if inlawa or inlawb : print '\ngrandchildren cannot have in-law extension; only testing'\ 'children\n' print FMT % rc.get_single_relationship_string(0, 1, gen.lib.Person.MALE, gen.lib.Person.MALE, '', 'f', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) print FMT % rc.get_single_relationship_string(0, 1, gen.lib.Person.MALE, gen.lib.Person.FEMALE, '', 'f', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) print FMT % rc.get_single_relationship_string(0, 1, gen.lib.Person.MALE, gen.lib.Person.UNKNOWN, '', 'f', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) else: print '\ntesting sons (Enter to start)\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(0, i, gen.lib.Person.MALE, gen.lib.Person.MALE, '', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting daughters\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(0, i, gen.lib.Person.MALE, gen.lib.Person.FEMALE, '', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting unknown children\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(0, i, gen.lib.Person.MALE, gen.lib.Person.UNKNOWN, '', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting grandfathers\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(i, 0, gen.lib.Person.FEMALE, gen.lib.Person.MALE, relstr, '', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting grandmothers\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(i, 0, gen.lib.Person.FEMALE, gen.lib.Person.FEMALE, relstr, '', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting unknown parents\n' sys.stdin.readline() for i in range(MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(i, 0, gen.lib.Person.FEMALE, gen.lib.Person.UNKNOWN, relstr, '', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting nieces\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(1, i, gen.lib.Person.FEMALE, gen.lib.Person.FEMALE, 'm', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting nephews\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(1, i, gen.lib.Person.FEMALE, gen.lib.Person.MALE, 'f', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting unknown nephews/nieces\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(1, i, gen.lib.Person.FEMALE, gen.lib.Person.UNKNOWN, 'f', relstr, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting uncles\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(i, 1, gen.lib.Person.FEMALE, gen.lib.Person.MALE, relstr, 'f', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting aunts\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(i, 1, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstr, 'f', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting unknown uncles/aunts\n' sys.stdin.readline() for i in range(1,MAX) : relstr = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(i, 1, gen.lib.Person.MALE, gen.lib.Person.UNKNOWN, relstr, 'm', only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstr else: print rel print '\n\ntesting male cousins same generation\n' sys.stdin.readline() for i in range(1,MAX) : relstra = _rand_relstr(i,'f') relstrb = _rand_relstr(i,'f') rel = FMT % rc.get_single_relationship_string(i, i, gen.lib.Person.MALE, gen.lib.Person.MALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel print '\n\ntesting female cousins same generation\n' sys.stdin.readline() for i in range(1,MAX) : relstra = _rand_relstr(i,'m') relstrb = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(i, i, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel print '\n\ntesting unknown cousins same generation\n' sys.stdin.readline() for i in range(1,MAX) : relstra = _rand_relstr(i,'m') relstrb = _rand_relstr(i,'m') rel = FMT % rc.get_single_relationship_string(i, i, gen.lib.Person.MALE, gen.lib.Person.UNKNOWN, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel print '\n\ntesting some cousins up\n' sys.stdin.readline() import random random.seed() for i in range(1,MAX) : for j in range (i,MAX) : rnd = random.randint(0, 100) if rnd < 10 : relstra = _rand_relstr(j,'f') relstrb = _rand_relstr(i,'f') if rnd < 5 : rel = (FMT + ' |info: female, Ga=%2d, Gb=%2d') % ( rc.get_single_relationship_string(j, i, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb), j, i ) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel else: rel = (FMT + ' |info: male, Ga=%2d, Gb=%2d') % ( rc.get_single_relationship_string(j, i, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb), j, i ) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel print '\n\ntesting some cousins down\n' sys.stdin.readline() for i in range(1,MAX) : for j in range (i,MAX) : rnd = random.randint(0, 100) if rnd < 10 : relstra = _rand_relstr(i,'f') relstrb = _rand_relstr(j,'f') if rnd < 5 : rel = (FMT + ' |info: female, Ga=%2d, Gb=%2d') % ( rc.get_single_relationship_string(i, j, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb), i, j) if printrelstr : print rel + ' |info:', relstra, relstrb else: print rel else: rel = (FMT + ' |info: male, Ga=%2d, Gb=%2d') % ( rc.get_single_relationship_string(i, j, gen.lib.Person.MALE, gen.lib.Person.FEMALE, relstra, relstrb, only_birth=onlybirth, in_law_a=inlawa, in_law_b=inlawb), i, j) if printrelstr : print rel + ' |info:', relstr else: print rel def _testsibling(rc): vals = [(rc.NORM_SIB, 'sibling'), (rc.HALF_SIB, 'half sib'), (rc.STEP_SIB, 'step sib'), (rc.UNKNOWN_SIB, 'undetermined sib')] FMT = '%+50s' for gendr, strgen in [(gen.lib.Person.MALE, 'male'), (gen.lib.Person.FEMALE, 'female'), (gen.lib.Person.UNKNOWN, 'unknown')]: for inlaw in [False, True]: for sibt, str in vals: print FMT % rc.get_sibling_relationship_string( sibt, gen.lib.Person.MALE, gendr, in_law_a = inlaw) + ' |info:', str, strgen def _test_spouse(rc): FMT = '%+50s' vals = [(rc.PARTNER_MARRIED, 'married'), (rc.PARTNER_UNMARRIED, 'unmarried'), (rc.PARTNER_CIVIL_UNION, 'civil union'), (rc.PARTNER_UNKNOWN_REL, 'unknown rel'), (rc.PARTNER_EX_MARRIED, 'ex-married'), (rc.PARTNER_EX_UNMARRIED, 'ex-unmarried'), (rc.PARTNER_EX_CIVIL_UNION, 'ex civil union'), (rc.PARTNER_EX_UNKNOWN_REL, 'ex unknown rel')] for gender, strgen in [(gen.lib.Person.MALE, 'male'), (gen.lib.Person.FEMALE, 'female'), (gen.lib.Person.UNKNOWN, 'unknown')] : for spouse_type, str in vals: print FMT % rc.get_partner_relationship_string( spouse_type, gen.lib.Person.MALE, gender) + \ ' |info: gender='+strgen+', rel='+str def test(rc, printrelstr): """ this is a generic test suite for the singular relationship TRANSLATORS: do NOT translate, call this from __main__ in the rel_xx.py module. """ import sys print '\nType y to do a test\n\n' print 'Test normal relations?' data = sys.stdin.readline() if data == 'y\n': _test(rc, True, False, False, printrelstr) print '\n\nTest step relations?' data = sys.stdin.readline() if data == 'y\n': _test(rc, False, False, False, printrelstr) print '\n\nTest in-law relations (first pers)?' data = sys.stdin.readline() if data == 'y\n': _test(rc, True, True, False, printrelstr) print '\n\nTest step and in-law relations?' data = sys.stdin.readline() if data == 'y\n': _test(rc, False, True, False, printrelstr) print '\n\nTest sibling types?' data = sys.stdin.readline() if data == 'y\n': _testsibling(rc) print '\n\nTest partner types?' data = sys.stdin.readline() if data == 'y\n': _test_spouse(rc) if __name__ == "__main__": # Test function. Call it as follows from the command line (so as to find # imported modules): # export PYTHONPATH=/path/to/gramps/src python src/plugins/rel_fr.py # (Above not needed here) """TRANSLATORS, copy this if statement at the bottom of your rel_xx.py module, after adding: 'from Relationship import test' and test your work with: export PYTHONPATH=/path/to/gramps/src python src/plugins/rel_xx.py See eg rel_fr.py at the bottom """ rc = RelationshipCalculator() test(rc, True)