Difference between revisions of "Hebrew Relationship Calculator"

From Gramps
(Created page with "# # 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 # i...")
 
(preformated)
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
#
+
{{man note|Hebrew Relationship Calculator - added to [https://github.com/gramps-project/gramps/pull/1575 Gramps 5.2]}}
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
#
 
"""
 
Classes for relationships.
 
"""
 
  
# -------------------------------------------------------------------------
+
<pre>
#
+
    # Gramps - a GTK+/GNOME based genealogy program
# Python modules
+
    #
#
+
    # Copyright (C) 2003-2005  Donald N. Allingham
# -------------------------------------------------------------------------
+
    #
import logging
+
    # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +
    #
 +
    """
 +
    Classes for relationships.
 +
    """
 +
    # -------------------------------------------------------------------------
 +
    #
 +
    # Python modules
 +
    #
 +
    # -------------------------------------------------------------------------
 +
    import logging
 +
    # -------------------------------------------------------------------------
 +
    #
 +
    # Gramps modules
 +
    #
 +
    # -------------------------------------------------------------------------
 +
    from gramps.gen.lib import Person, ChildRefType, EventType, FamilyRelType
 +
    import gramps.gen.relationship
 +
    from gramps.gen.plug import PluginRegister, BasePluginManager
 +
    from gramps.gen.const import GRAMPS_LOCALE as glocale
  
# -------------------------------------------------------------------------
+
    _ = glocale.translation.sgettext
#
 
# Gramps modules
 
#
 
# -------------------------------------------------------------------------
 
from gramps.gen.lib import Person, ChildRefType, EventType, FamilyRelType
 
import gramps.gen.relationship
 
from gramps.gen.plug import PluginRegister, BasePluginManager
 
from gramps.gen.const import GRAMPS_LOCALE as glocale
 
  
_ = glocale.translation.sgettext
+
    MALE = Person.MALE
 +
    FEMALE = Person.FEMALE
 +
    UNKNOWN = Person.UNKNOWN
  
MALE = Person.MALE
+
    LOG = logging.getLogger("gen.relationship")
FEMALE = Person.FEMALE
+
    LOG.addHandler(logging.StreamHandler())
UNKNOWN = Person.UNKNOWN
 
  
LOG = logging.getLogger("gen.relationship")
+
    # -------------------------------------------------------------------------
LOG.addHandler(logging.StreamHandler())
+
    #
 +
    #
 +
    #
 +
    # -------------------------------------------------------------------------
  
# -------------------------------------------------------------------------
+
    _LEVEL_NAME = [
#
+
        "",
#
+
        "מרמה ראשונה",
#
+
        "מרמה שניה",
# -------------------------------------------------------------------------
+
        "מרמה שלישית",
 +
        "מרמה רביעית",
 +
        "מרמה חמישית",
 +
        "מרמה שישית",
 +
        "מרמה שביעית",
 +
        "מרמה שמינית",
 +
        "מרמה תשיעית",
 +
        "מרמה עשירית",
 +
        "מרמה אחת עשרה",
 +
    ]
  
_LEVEL_NAME = [
+
    _REMOVED_LEVEL = [
    "",
+
        "",
    "מרמה ראשונה",
+
        "מדרגה שניה",
    "מרמה שניה",
+
        "פעמיים מדרגה שניה",
    "מרמה שלישית",
+
        "שלוש פעמים מדרגה שניה",
    "מרמה רביעית",
+
        "ארבע פעמים מדרגה שניה",
    "מרמה חמישית",
+
        "חמש פעמים מדרגה שניה",
    "מרמה שישית",
+
        "שש פעמים מדרגה שניה",
    "מרמה שביעית",
+
        "שבע פעמים מדרגה שניה",
    "מרמה שמינית",
+
        "שמונה פעמים מדרגה שניה",
    "מרמה תשיעית",
+
        "תשע פעמים מדרגה שניה",
    "מרמה עשירית",
+
        "עשר פעמים מדרגה שניה",
    "מרמה אחת עשרה",
+
        "אחת עשרה פעמים מדרגה שניה",
 +
    ]
  
]
+
    _PARENTS_LEVEL = [
 +
        "",
 +
        "הורים",
 +
        "סבים",
 +
        "סבות",
 +
        "סב גדול",
 +
        "סבה גדולה",
 +
        "סב וסבה גדולים",
 +
        "סב וסבה מרמה שלישית",
 +
        "סב וסבה מרמה רביעית",
 +
        "סב וסבה מרמה חמישית",
 +
        "סב וסבה מרמה שישית",
 +
        "סב וסבה מרמה שביעית",
 +
        "סב וסבה מרמה שמינית",
 +
        "סב וסבה מרמה תשיעית",
 +
        "סב וסבה מרמה עשירית",
 +
        "סב וסבה מרמה אחת עשרה",
 +
    ]
  
_REMOVED_LEVEL = [
+
    _FATHER_LEVEL = [
    "",
+
        "",
    "מדרגה שניה",
+
        "סב %(step)s %(inlaw)s",
    "פעמיים מדרגה שניה",
+
        "סב גדול %(step)s %(inlaw)s",
    "שלוש פעמים מדרגה שניה",
+
        "סב מרמה שלישית %(step)s %(inlaw)s",
    "ארבע פעמים מדרגה שניה",
+
        "סב מרמה רביעית %(step)s %(inlaw)s",
    "חמש פעמים מדרגה שניה",
+
        "סב מרמה חמישית %(step)s %(inlaw)s",
    "שש פעמים מדרגה שניה",
+
        "סב מרמה שישית %(step)s %(inlaw)s",
    "שבע פעמים מדרגה שניה",
+
        "סב מרמה שביעית %(step)s %(inlaw)s",
    "שמונה פעמים מדרגה שניה",
+
        "סב מרמה שמינית %(step)s %(inlaw)s",
    "תשע פעמים מדרגה שניה",
+
        "סב מרמה תשיעית %(step)s %(inlaw)s",
    "עשר פעמים מדרגה שניה",
+
        "סב מרמה עשירית %(step)s %(inlaw)s",
    "אחת עשרה פעמים מדרגה שניה",
+
        "סב מרמה אחת עשרה %(step)s %(inlaw)s",
 +
    ]
  
]
+
    _MOTHER_LEVEL = [
 +
        "",
 +
        "סבה %(step)s %(inlaw)s",
 +
        "סבה גדולה %(step)s %(inlaw)s",
 +
        "סבה מרמה שלישית %(step)s %(inlaw)s",
 +
        "סבה מרמה רביעית %(step)s %(inlaw)s",
 +
        "סבה מרמה חמישית %(step)s %(inlaw)s",
 +
        "סבה מרמה שישית %(step)s %(inlaw)s",
 +
        "סבה מרמה שביעית %(step)s %(inlaw)s",
 +
        "סבה מרמה שמינית %(step)s %(inlaw)s",
 +
        "סבה מרמה תשיעית %(step)s %(inlaw)s",
 +
        "סבה מרמה עשירית %(step)s %(inlaw)s",
 +
        "סבה מרמה אחת עשרה %(step)s %(inlaw)s",
 +
    ]
  
_PARENTS_LEVEL = [
+
    _SON_LEVEL = [
    "",
+
        "",
    "הורים",
+
        "בן %(step)s %(inlaw)s",
    "סבים",
+
        "נכד %(step)s %(inlaw)s",
    "סבות",
+
        "נין %(step)s %(inlaw)s",
    "סב גדול",
+
        "חימש %(step)s %(inlaw)s",
    "סבה גדולה",
+
        "נכד מרמה חמישית %(step)s %(inlaw)s",
    "סב וסבה גדולים",
+
        "נכד מרמה שישית %(step)s %(inlaw)s",
    "סב וסבה מרמה שלישית",
+
        "נכד מרמה שביעית %(step)s %(inlaw)s",
    "סב וסבה מרמה רביעית",
+
        "נכד מרמה שמינית %(step)s %(inlaw)s",
    "סב וסבה מרמה חמישית",
+
        "נכד מרמה תשיעית %(step)s %(inlaw)s",
    "סב וסבה מרמה שישית",
+
        "נכד מרמה עשירית %(step)s %(inlaw)s",
    "סב וסבה מרמה שביעית",
+
        "נכד מרמה אחת עשרה %(step)s %(inlaw)s",
    "סב וסבה מרמה שמינית",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב וסבה מרמה תשיעית",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב וסבה מרמה עשירית",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב וסבה מרמה אחת עשרה",
+
        "נכד רחוק %(step)s %(inlaw)s",
]
+
        "נכד רחוק %(step)s %(inlaw)s",
_FATHER_LEVEL = [
+
        "נכד רחוק %(step)s %(inlaw)s",
    "",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב גדול %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה שלישית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה רביעית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה חמישית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה שישית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה שביעית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה שמינית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה תשיעית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה עשירית %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
    "סב מרמה אחת עשרה %(step)s %(inlaw)s",
+
        "נכד רחוק %(step)s %(inlaw)s",
 +
    ]
  
]
+
    _DAUGHTER_LEVEL = [
 +
        "",
 +
        "בת %(step)s %(inlaw)s",
 +
        "נכדה %(step)s %(inlaw)s",
 +
        "נינה %(step)s %(inlaw)s",
 +
        "חימשה %(step)s %(inlaw)s",
 +
        "נכדה מרמה חמישית %(step)s %(inlaw)s",
 +
        "נכדה מרמה שישית %(step)s %(inlaw)s",
 +
        "נכדה מרמה שביעית %(step)s %(inlaw)s",
 +
        "נכדה מרמה שמינית %(step)s %(inlaw)s",
 +
        "נכדה מרמה תשיעית %(step)s %(inlaw)s",
 +
        "נכדה מרמה עשירית %(step)s %(inlaw)s",
 +
        "נכדה מרמה אחת עשרה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
        "נכדה רחוקה %(step)s %(inlaw)s",
 +
    ]
  
_MOTHER_LEVEL = [
+
    _SISTER_LEVEL = [
    "",
+
        "",
    "סבה %(step)s %(inlaw)s",
+
        "אחות %(step)s %(inlaw)s",
    "סבה גדולה %(step)s %(inlaw)s",
+
        "דודה %(step)s %(inlaw)s",
    "סבה מרמה שלישית %(step)s %(inlaw)s",
+
        "דודה גדולה %(step)s %(inlaw)s",
    "סבה מרמה רביעית %(step)s %(inlaw)s",
+
        "דודה מרמה שלישית %(step)s %(inlaw)s",
    "סבה מרמה חמישית %(step)s %(inlaw)s",
+
        "דודה מרמה רביעית %(step)s %(inlaw)s",
    "סבה מרמה שישית %(step)s %(inlaw)s",
+
        "דודה מרמה חמישית %(step)s %(inlaw)s",
    "סבה מרמה שביעית %(step)s %(inlaw)s",
+
        "דודה מרמה שישית %(step)s %(inlaw)s",
    "סבה מרמה שמינית %(step)s %(inlaw)s",
+
        "דודה מרמה שביעית %(step)s %(inlaw)s",
    "סבה מרמה תשיעית %(step)s %(inlaw)s",
+
        "דודה מרמה שמינית %(step)s %(inlaw)s",
    "סבה מרמה עשירית %(step)s %(inlaw)s",
+
        "דודה מרמה תשיעיתית %(step)s %(inlaw)s",
    "סבה מרמה אחת עשרה %(step)s %(inlaw)s",
+
        "דודה מרמה עשירית %(step)s %(inlaw)s",
]
+
        "דודה מרמה אחת עשרה%(step)s %(inlaw)s",
 +
    ]
  
_SON_LEVEL = [
+
    _BROTHER_LEVEL = [
    "",
+
        "",
    "בן %(step)s %(inlaw)s",
+
        "אח %(step)s %(inlaw)s",
    "נכד %(step)s %(inlaw)s",
+
        "דוד %(step)s %(inlaw)s",
    "נין %(step)s %(inlaw)s",
+
        "דוד גדול %(step)s %(inlaw)s",
    "חימש %(step)s %(inlaw)s",
+
        "דוד מרמה שלישית %(step)s %(inlaw)s",
    "נכד מרמה חמישית %(step)s %(inlaw)s",
+
        "דוד מרמה רביעית %(step)s %(inlaw)s",
    "נכד מרמה שישית %(step)s %(inlaw)s",
+
        "דוד מרמה חמישית %(step)s %(inlaw)s",
    "נכד מרמה שביעית %(step)s %(inlaw)s",
+
        "דוד מרמה שישית %(step)s %(inlaw)s",
    "נכד מרמה שמינית %(step)s %(inlaw)s",
+
        "דוד מרמה שביעית %(step)s %(inlaw)s",
    "נכד מרמה תשיעית %(step)s %(inlaw)s",
+
        "דוד מרמה שמינית %(step)s %(inlaw)s",
    "נכד מרמה עשירית %(step)s %(inlaw)s",
+
        "דוד מרמה תשיעית %(step)s %(inlaw)s",
    "נכד מרמה אחת עשרה %(step)s %(inlaw)s",
+
        "דוד מרמה עשירית %(step)s %(inlaw)s",
    "נכד רחוק %(step)s %(inlaw)s",
+
        "דוד מרמה אחת עשרה %(step)s %(inlaw)s",
    "נכד רחוק %(step)s %(inlaw)s",
+
     ]
    "נכד רחוק %(step)s %(inlaw)s",
 
     "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
    "נכד רחוק %(step)s %(inlaw)s",
 
  
 +
    _NEPHEW_LEVEL = [
 +
        "",
 +
        "אחיין %(step)s %(inlaw)s",
 +
        "נכדן %(step)s %(inlaw)s",
 +
        "אחיין גדול %(step)s %(inlaw)s",
 +
        "אחיין מרמה שלישית %(step)s %(inlaw)s",
 +
        "אחיין מרמה רביעית %(step)s %(inlaw)s",
 +
        "אחיין מרמה חמישית %(step)s %(inlaw)s",
 +
        "אחיין מרמה שישית %(step)s %(inlaw)s",
 +
        "אחיין מרמה שביעית %(step)s %(inlaw)s",
 +
        "אחיין מרמה שמינית %(step)s %(inlaw)s",
 +
        "אחיין מרמה תשיעית %(step)s %(inlaw)s",
 +
        "אחיין מרמה עשירית %(step)s %(inlaw)s",
 +
        "אחיין מרמה אחת עשרה %(step)s %(inlaw)s",
 +
    ]
  
 +
    _NIECE_LEVEL = [
 +
        "",
 +
        "אחיינית %(step)s %(inlaw)s",
 +
        "נכדנית %(step)s %(inlaw)s",
 +
        "אחיינית גדולה %(step)s %(inlaw)s",
 +
        "אחיינית מרמה שלישית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה רביעית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה חמישית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה שישית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה שביעית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה שמינית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה תשיעית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה עשירית %(step)s %(inlaw)s",
 +
        "אחיינית מרמה אחת עשרה %(step)s %(inlaw)s",
 +
    ]
  
]
+
    _CHILDREN_LEVEL = [
 +
        "",
 +
        "ילדים",
 +
        "נכדים",
 +
        "נינים",
 +
        "חימשים",
 +
        "נכדים מרמה חמישית",
 +
        "נכדים מרמה שישית",
 +
        "נכדים מרמה שביעית",
 +
        "נכדים מרמה שמינית",
 +
        "נכדים מרמה תשיעית",
 +
        "נכדים מרמה עשירית",
 +
        "נכדים מרמה אחת עשרה",
 +
    ]
  
_DAUGHTER_LEVEL = [
+
    _SIBLINGS_LEVEL = [
    "",
+
        "",
    "בת %(step)s %(inlaw)s",
+
        "אחאים",
    "נכדה %(step)s %(inlaw)s",
+
        "דוד/דודה",
    "נינה %(step)s %(inlaw)s",
+
        "דוד/דודה גדולים",
    "חימשה %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה שלישית",
    "נכדה מרמה חמישית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה רביעית",
    "נכדה מרמה שישית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה חמישית",
    "נכדה מרמה שביעית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה שישית",
    "נכדה מרמה שמינית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה שביעית",
    "נכדה מרמה תשיעית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה שמינית",
    "נכדה מרמה עשירית %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה תשיעית",
    "נכדה מרמה אחת עשרה %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה עשירית",
    "נכדה רחוקה %(step)s %(inlaw)s",
+
        "דוד/דודה מרמה אחת עשרה",
    "נכדה רחוקה %(step)s %(inlaw)s",
+
     ]
    "נכדה רחוקה %(step)s %(inlaw)s",
 
     "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
    "נכדה רחוקה %(step)s %(inlaw)s",
 
  
]
+
    _SIBLING_LEVEL = [
 +
        "",
 +
        "אחאים %(step)s %(inlaw)s",
 +
        "דוד/דודה %(step)s %(inlaw)s",
 +
        "דוד/דודה גדולים %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה שלישית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה רביעית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה חמישית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה שישית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה שביעית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה שמינית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה תשיעית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה עשירית %(step)s %(inlaw)s",
 +
        "דוד/דודה מרמה אחת עשרה %(step)s %(inlaw)s",
 +
    ]
  
_SISTER_LEVEL = [
+
    _NEPHEWS_NIECES_LEVEL = [
    "",
+
        "",
    "אחות %(step)s %(inlaw)s",
+
        "אחאים",
    "דודה %(step)s %(inlaw)s",
+
        "אחיין/אחיינית",
    "דודה גדולה %(step)s %(inlaw)s",
+
        "נכדן/נכדנית",
    "דודה מרמה שלישית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית גדולים",
    "דודה מרמה רביעית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית שלישית",
    "דודה מרמה חמישית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית רביעית",
    "דודה מרמה שישית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית חמישית",
    "דודה מרמה שביעית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית מרמה שישית",
    "דודה מרמה שמינית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית מרמה שביעית",
    "דודה מרמה תשיעיתית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית מרמה שמינית",
    "דודה מרמה עשירית %(step)s %(inlaw)s",
+
        "אחיין/אחיינית מרמה תשיעית",
    "דודה מרמה אחת עשרה%(step)s %(inlaw)s",
+
        "אחיין/אחיינית מרמה עשירית",
 +
        "אחיין/אחיינית מרמה אחת עשרה",
 +
    ]
  
]
+
    # -------------------------------------------------------------------------
 +
    #
 +
    # RelationshipCalculator
 +
    #
 +
    # -------------------------------------------------------------------------
 +
    class RelationshipCalculator:
 +
        """
 +
        The relationship calculator helps to determine the relationship between
 +
        two people.
 +
        """
  
_BROTHER_LEVEL = [
+
        REL_MOTHER = "m" # going up to mother
    "",
+
        REL_FATHER = "f" # going up to father
    "אח %(step)s %(inlaw)s",
+
        REL_MOTHER_NOTBIRTH = "M" # going up to mother, not birth relation
    "דוד %(step)s %(inlaw)s",
+
        REL_FATHER_NOTBIRTH = "F" # going up to father, not birth relation
    "דוד גדול %(step)s %(inlaw)s",
+
        REL_SIBLING = "s" # going sideways to sibling (no parents)
    "דוד מרמה שלישית %(step)s %(inlaw)s",
+
        REL_FAM_BIRTH = "a" # going up to family (mother and father)
    "דוד מרמה רביעית %(step)s %(inlaw)s",
+
        REL_FAM_NONBIRTH = "A" # going up to family, not birth relation
    "דוד מרמה חמישית %(step)s %(inlaw)s",
+
        REL_FAM_BIRTH_MOTH_ONLY = "b" # going up to fam, only birth rel to mother
    "דוד מרמה שישית %(step)s %(inlaw)s",
+
        REL_FAM_BIRTH_FATH_ONLY = "c" # going up to fam, only birth rel to father
    "דוד מרמה שביעית %(step)s %(inlaw)s",
+
        REL_FAM_INLAW_PREFIX = "L" # going to the partner.
    "דוד מרמה שמינית %(step)s %(inlaw)s",
 
    "דוד מרמה תשיעית %(step)s %(inlaw)s",
 
    "דוד מרמה עשירית %(step)s %(inlaw)s",
 
    "דוד מרמה אחת עשרה %(step)s %(inlaw)s",
 
  
]
+
        # sibling types
 +
        NORM_SIB = 0  # same birth parents
 +
        HALF_SIB_MOTHER = 1  # same mother, father known to be different
 +
        HALF_SIB_FATHER = 2  # same father, mother known to be different
 +
        STEP_SIB = 3  # birth parents known to be different
 +
        UNKNOWN_SIB = 4  # insufficient data to draw conclusion
  
_NEPHEW_LEVEL = [
+
        # sibling strings  for Hebrew we need four "step": male sing/plur, female sing/plur
    "",
+
        STEP = "שלוב"
    "אחיין %(step)s %(inlaw)s",
+
        STEP_F = "שלובה"
    "נכדן %(step)s %(inlaw)s",
+
        STEP_M = "שלוב" # this is actually redundant if Can't make it "plural form".
    "אחיין גדול %(step)s %(inlaw)s",
+
        HALF = "למחצה"
    "אחיין מרמה שלישית %(step)s %(inlaw)s",
+
        INLAW = "מחיתון"
    "אחיין מרמה רביעית %(step)s %(inlaw)s",
 
    "אחיין מרמה חמישית %(step)s %(inlaw)s",
 
    "אחיין מרמה שישית %(step)s %(inlaw)s",
 
    "אחיין מרמה שביעית %(step)s %(inlaw)s",
 
    "אחיין מרמה שמינית %(step)s %(inlaw)s",
 
    "אחיין מרמה תשיעית %(step)s %(inlaw)s",
 
    "אחיין מרמה עשירית %(step)s %(inlaw)s",
 
    "אחיין מרמה אחת עשרה %(step)s %(inlaw)s",
 
  
]
+
        # 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
  
_NIECE_LEVEL = [
+
        def __init__(self):
    "",
+
            self.signal_keys = []
    "אחיינית %(step)s %(inlaw)s",
+
            self.state_signal_key = None
    "נכדנית %(step)s %(inlaw)s",
+
            self.storemap = False
    "אחיינית גדולה %(step)s %(inlaw)s",
+
            self.dirtymap = True
    "אחיינית מרמה שלישית %(step)s %(inlaw)s",
+
            self.stored_map = None
    "אחיינית מרמה רביעית %(step)s %(inlaw)s",
+
            self.map_handle = None
    "אחיינית מרמה חמישית %(step)s %(inlaw)s",
+
            self.map_meta = None
    "אחיינית מרמה שישית %(step)s %(inlaw)s",
+
            self.__db_connected = False
    "אחיינית מרמה שביעית %(step)s %(inlaw)s",
+
            self.depth = 15
    "אחיינית מרמה שמינית %(step)s %(inlaw)s",
+
            try:
    "אחיינית מרמה תשיעית %(step)s %(inlaw)s",
+
                from .config import config
    "אחיינית מרמה עשירית %(step)s %(inlaw)s",
+
                self.set_depth(config.get("behavior.generation-depth"))
    "אחיינית מרמה אחת עשרה %(step)s %(inlaw)s",
+
            except ImportError:
 +
                pass
  
]
+
            # data storage to communicate with recursive functions
 +
            self.__max_depth_reached = False
 +
            self.__loop_detected = False
 +
            self.__max_depth = 12
 +
            self.__all_families = False
 +
            self.__all_dist = False
 +
            self.__only_birth = False
 +
            self.__crosslinks = False
 +
            self.__msg = []
  
_CHILDREN_LEVEL = [
+
        def set_depth(self, depth):
    "",
+
            """
    "ילדים",
+
            Set how deep relationships must be searched. Input must be an
    "נכדים",
+
            integer > 0
    "נינים",
+
            """
    "חימשים",
+
            if depth != self.depth:
    "נכדים מרמה חמישית",
+
                self.depth = depth
    "נכדים מרמה שישית",
+
                self.dirtymap = True
    "נכדים מרמה שביעית",
 
    "נכדים מרמה שמינית",
 
    "נכדים מרמה תשיעית",
 
    "נכדים מרמה עשירית",
 
    "נכדים מרמה אחת עשרה",
 
  
]
+
        def get_depth(self):
 +
            """
 +
            Obtain depth of relationship search
 +
            """
 +
            return self.depth
  
_SIBLINGS_LEVEL = [
+
        DIST_FATHER = "אב־קדמון רחוק %(step)s %(inlaw)s (%(level)d דורות)"
    "",
 
    "אחאים",
 
    "דוד/דודה",
 
    "דוד/דודה גדולים",
 
    "דוד/דודה מרמה שלישית",
 
    "דוד/דודה מרמה רביעית",
 
    "דוד/דודה מרמה חמישית",
 
    "דוד/דודה מרמה שישית",
 
    "דוד/דודה מרמה שביעית",
 
    "דוד/דודה מרמה שמינית",
 
    "דוד/דודה מרמה תשיעית",
 
    "דוד/דודה מרמה עשירית",
 
    "דוד/דודה מרמה אחת עשרה",
 
  
]
+
        def _get_father(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level > len(_FATHER_LEVEL) - 1:
 +
                return self.DIST_FATHER % {"step": step, "inlaw": inlaw, "level": level}
 +
            else:
 +
                return _FATHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
_SIBLING_LEVEL = [
+
       
    "",
+
        DIST_SON = "בן רחוק %(step) %(inlaw)s (%(level)d דורות)"
    "אחאים %(step)s %(inlaw)s",
 
    "דוד/דודה %(step)s %(inlaw)s",
 
    "דוד/דודה גדולים %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה שלישית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה רביעית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה חמישית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה שישית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה שביעית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה שמינית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה תשיעית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה עשירית %(step)s %(inlaw)s",
 
    "דוד/דודה מרמה אחת עשרה %(step)s %(inlaw)s",
 
  
]
+
        def _get_son(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level > len(_SON_LEVEL) - 1:
 +
                return self.DIST_SON % {"step": step, "inlaw": inlaw, "level": level}
 +
            else:
 +
                return _SON_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
_NEPHEWS_NIECES_LEVEL = [
 
    "",
 
    "אחאים",
 
    "אחיין/אחיינית",
 
    "נכדן/נכדנית",
 
    "אחיין/אחיינית גדולים",
 
    "אחיין/אחיינית שלישית",
 
    "אחיין/אחיינית רביעית",
 
    "אחיין/אחיינית חמישית",
 
    "אחיין/אחיינית מרמה שישית",
 
    "אחיין/אחיינית מרמה שביעית",
 
    "אחיין/אחיינית מרמה שמינית",
 
    "אחיין/אחיינית מרמה תשיעית",
 
    "אחיין/אחיינית מרמה עשירית",
 
    "אחיין/אחיינית מרמה אחת עשרה",
 
  
]
+
        DIST_MOTHER = "אם־קדמונית רחוקה %(step)s %(inlaw) s(%(level)d דורות)"
  
 +
        def _get_mother(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level > len(_MOTHER_LEVEL) - 1:
 +
                return self.DIST_MOTHER % {"step": step, "inlaw": inlaw, "level": level}
 +
            else:
 +
                return _MOTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
# -------------------------------------------------------------------------
 
#
 
# RelationshipCalculator
 
#
 
# -------------------------------------------------------------------------
 
class RelationshipCalculator:
 
    """
 
    The relationship calculator helps to determine the relationship between
 
    two people.
 
    """
 
  
    REL_MOTHER = "m"  # going up to mother
+
        DIST_DAUGHTER = "בת רחוקה %(step) %(inlaw)s(%(level)d דורות)"
    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.
+
        def _get_daughter(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level > len(_DAUGHTER_LEVEL) - 1:
 +
                return self.DIST_DAUGHTER % {"step": step, "inlaw": inlaw, "level": level}
 +
            else:
 +
                return _DAUGHTER_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
    # sibling types
+
        def _get_parent_unknown(self, level, step="", inlaw=""):
    NORM_SIB = 0  # same birth parents
+
            """
    HALF_SIB_MOTHER = 1  # same mother, father known to be different
+
            Internal english method to create relation string
    HALF_SIB_FATHER = 2  # same father, mother known to be different
+
            """
    STEP_SIB = 3  # birth parents known to be different
+
            if level < len(_LEVEL_NAME):
    UNKNOWN_SIB = 4  # insufficient data to draw conclusion
+
                return ("אב־קדמון %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
 +
            else:
 +
                return "אב־קדמון רחוק %s %s (%d דורות)" % (step, inlaw, level)
  
    # sibling strings  for Hebrew we need four "step": male sing/plur, female sing/plur
 
    STEP = "שלוב"
 
    STEP_F = "שלובה"
 
    STEP_M = "שלוב"
 
  
    HALF = "למחצה"
+
        DIST_CHILD = "צאצא רחוק %(step)s (%(level)d דורות)"
  
    INLAW = "מחיתון"
+
        def _get_child_unknown(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level < len(_LEVEL_NAME):
 +
                return ("צאצא %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
 +
            else:
 +
                return self.DIST_CHILD % {"step": step, "level": level}
  
    # 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):
+
        DIST_AUNT = "דודה רחוקה %(step)s %(inlaw)s"
        self.signal_keys = []
 
        self.state_signal_key = None
 
        self.storemap = False
 
        self.dirtymap = True
 
        self.stored_map = None
 
        self.map_handle = None
 
        self.map_meta = None
 
        self.__db_connected = False
 
        self.depth = 15
 
        try:
 
            from .config import config
 
  
            self.set_depth(config.get("behavior.generation-depth"))
+
        def _get_aunt(self, level, step="", inlaw=""):
        except ImportError:
+
            """
             pass
+
            Internal english method to create relation string
 +
            """
 +
            if level > len(_SISTER_LEVEL) - 1:
 +
                return self.DIST_AUNT % {"step": step, "inlaw": inlaw}
 +
             else:
 +
                return _SISTER_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
        # data storage to communicate with recursive functions
 
        self.__max_depth_reached = False
 
        self.__loop_detected = False
 
        self.__max_depth = 12
 
        self.__all_families = False
 
        self.__all_dist = False
 
        self.__only_birth = False
 
        self.__crosslinks = False
 
        self.__msg = []
 
  
    def set_depth(self, depth):
+
        DIST_UNCLE = "דוד רחוק %(step)s %(inlaw)s"
        """
 
        Set how deep relationships must be searched. Input must be an
 
        integer > 0
 
        """
 
        if depth != self.depth:
 
            self.depth = depth
 
            self.dirtymap = True
 
  
    def get_depth(self):
+
        def _get_uncle(self, level, step="", inlaw=""):
        """
+
            """
        Obtain depth of relationship search
+
            Internal english method to create relation string
        """
+
            """
        return self.depth
+
            if level > len(_BROTHER_LEVEL) - 1:
 +
                return self.DIST_UNCLE % {"step": step, "inlaw": inlaw}
 +
            else:
 +
                return _BROTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
    DIST_FATHER = "אב־קדמון רחוק %(step)s %(inlaw)s (%(level)d דורות)"
 
  
    def _get_father(self, level, step="", inlaw=""):
+
        DIST_NEPHEW = "אחיין רחוק %(step)s %(inlaw)s"
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_FATHER_LEVEL) - 1:
 
            return self.DIST_FATHER % {"step": step, "inlaw": inlaw, "level": level}
 
        else:
 
            return _FATHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
  
    DIST_SON = "בן רחוק %(step) %(inlaw)s (%(level)d דורות)"
+
        def _get_nephew(self, level, step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if level > len(_NEPHEW_LEVEL) - 1:
 +
                return self.DIST_NEPHEW % {"step": step, "inlaw": inlaw}
 +
            else:
 +
                return _NEPHEW_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
    def _get_son(self, level, step="", inlaw=""):
 
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_SON_LEVEL) - 1:
 
            return self.DIST_SON % {"step": step, "inlaw": inlaw, "level": level}
 
        else:
 
            return _SON_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
  
    DIST_MOTHER = "אם־קדמונית רחוקה %(step)s %(inlaw) s(%(level)d דורות)"
+
        DIST_NIECE = "אחיינית רחוקה %(step)s %(inlaw)s"
  
    def _get_mother(self, level, step="", inlaw=""):
+
        def _get_niece(self, level, step="", inlaw=""):
        """
+
            """
        Internal english method to create relation string
+
            Internal english method to create relation string
        """
+
            """
        if level > len(_MOTHER_LEVEL) - 1:
+
            if level > len(_NIECE_LEVEL) - 1:
            return self.DIST_MOTHER % {"step": step, "inlaw": inlaw, "level": level}
+
                return self.DIST_NIECE % {"step": step, "inlaw": inlaw}
        else:
+
            else:
            return _MOTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
+
                return _NIECE_LEVEL[level] % {"step": step, "inlaw": inlaw}
  
    DIST_DAUGHTER = "בת רחוקה %(step) %(inlaw)s(%(level)d דורות)"
+
        def _get_cousin(self, level, removed, dir="", step="", inlaw=""):
 +
            """
 +
            Internal english method to create relation string
 +
            """
 +
            if removed == 0 and level < len(_LEVEL_NAME):
 +
                return "בן־דוד %s %s %s" % (step, inlaw, _LEVEL_NAME[level])
 +
            elif removed > len(_REMOVED_LEVEL) - 1 or level > len(_LEVEL_NAME) - 1:
 +
                return "קרוב־משפחה רחוק %s %s" % (step, inlaw)
 +
            else:
 +
                return "בן־דוד/בת־דודה %s %s %s %s %s" % (step, inlaw, _LEVEL_NAME[level], _REMOVED_LEVEL[removed], dir,)
  
    def _get_daughter(self, level, step="", inlaw=""):
 
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_DAUGHTER_LEVEL) - 1:
 
            return self.DIST_DAUGHTER % {"step": step, "inlaw": inlaw, "level": level}
 
        else:
 
            return _DAUGHTER_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
  
    def _get_parent_unknown(self, level, step="", inlaw=""):
+
        DIST_SIB = "דוד/דודה רחוקים %(step)s %(inlaw)s"
        """
 
        Internal english method to create relation string
 
        """
 
        if level < len(_LEVEL_NAME):
 
            return ("אב־קדמון %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
 
        else:
 
            return "אב־קדמון רחוק %s %s (%d דורות)" % (step, inlaw, level)
 
  
    DIST_CHILD = "צאצא רחוק %(step)s (%(level)d דורות)"
+
        def _get_sibling(self, level, step="", inlaw=""):
 
+
            """
    def _get_child_unknown(self, level, step="", inlaw=""):
+
            Internal english method to create relation string
        """
+
            """
        Internal english method to create relation string
+
            if level < len(_SIBLING_LEVEL):
        """
+
                return _SIBLING_LEVEL[level] % {"step": step, "inlaw": inlaw}
        if level < len(_LEVEL_NAME):
+
            else:
            return ("צאצא %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
+
                return self.DIST_SIB % {"step": step, "inlaw": inlaw}
        else:
 
            return self.DIST_CHILD % {"step": step, "level": level}
 
  
    DIST_AUNT = "דודה רחוקה %(step)s %(inlaw)s"
+
         def get_sibling_type(self, db, orig, other):
 
+
            """
    def _get_aunt(self, level, step="", inlaw=""):
+
            Translation free determination of type of orig and other as siblings
        """
+
            The procedure returns sibling types, these can be passed to
        Internal english method to create relation string
+
            get_sibling_relationship_string.
        """
+
            Only call this method if known that orig and other are siblings
        if level > len(_SISTER_LEVEL) - 1:
+
            """
            return self.DIST_AUNT % {"step": step, "inlaw": inlaw}
+
            fatherorig, motherorig = self.get_birth_parents(db, orig)
        else:
+
            fatherother, motherother = self.get_birth_parents(db, other)
            return _SISTER_LEVEL[level] % {"step": step, "inlaw": inlaw}
+
            if fatherorig and motherorig and fatherother and motherother:
 
+
                if fatherother == fatherorig and motherother == motherorig:
    DIST_UNCLE = "דוד רחוק %(step)s %(inlaw)s"
+
                    return self.NORM_SIB
 
+
                elif fatherother == fatherorig:
    def _get_uncle(self, level, step="", inlaw=""):
+
                    # all birth parents are known, one
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_BROTHER_LEVEL) - 1:
 
            return self.DIST_UNCLE % {"step": step, "inlaw": inlaw}
 
        else:
 
            return _BROTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
 
 
    DIST_NEPHEW = "אחיין רחוק %(step)s %(inlaw)s"
 
 
 
    def _get_nephew(self, level, step="", inlaw=""):
 
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_NEPHEW_LEVEL) - 1:
 
            return self.DIST_NEPHEW % {"step": step, "inlaw": inlaw}
 
        else:
 
            return _NEPHEW_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
 
 
    DIST_NIECE = "אחיינית רחוקה %(step)s %(inlaw)s"
 
 
 
    def _get_niece(self, level, step="", inlaw=""):
 
        """
 
        Internal english method to create relation string
 
        """
 
        if level > len(_NIECE_LEVEL) - 1:
 
            return self.DIST_NIECE % {"step": step, "inlaw": inlaw}
 
        else:
 
            return _NIECE_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
 
 
    def _get_cousin(self, level, removed, dir="", step="", inlaw=""):
 
         """
 
        Internal english method to create relation string
 
        """
 
        if removed == 0 and level < len(_LEVEL_NAME):
 
            return "בן־דוד %s %s %s" % (step, inlaw, _LEVEL_NAME[level])
 
        elif removed > len(_REMOVED_LEVEL) - 1 or level > len(_LEVEL_NAME) - 1:
 
            return "קרוב־משפחה רחוק %s %s" % (step, inlaw)
 
        else:
 
            return "בן־דוד/בת־דודה %s %s %s %s %s" % (step, inlaw, _LEVEL_NAME[level], _REMOVED_LEVEL[removed], dir,)
 
 
 
    DIST_SIB = "דוד/דודה רחוקים %(step)s %(inlaw)s"
 
 
 
    def _get_sibling(self, level, step="", inlaw=""):
 
        """
 
        Internal english method to create relation string
 
        """
 
        if level < len(_SIBLING_LEVEL):
 
            return _SIBLING_LEVEL[level] % {"step": step, "inlaw": inlaw}
 
        else:
 
            return self.DIST_SIB % {"step": step, "inlaw": 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:
 
                # all birth parents are known, one
 
                return self.HALF_SIB_FATHER
 
            elif motherother == motherorig:
 
                return self.HALF_SIB_MOTHER
 
            else:
 
                return self.STEP_SIB
 
        else:
 
            # some birth parents are not known, hence we or cannot know if
 
            # half siblings. step siblings might be possible, otherwise give up
 
            orig_nb_par = self._get_nonbirth_parent_list(db, orig)
 
            if fatherother and fatherother in orig_nb_par:
 
                # the birth parent of other is non-birth of orig
 
                if motherother and motherother == motherorig:
 
                    return self.HALF_SIB_MOTHER
 
                else:
 
                    return self.STEP_SIB
 
            if motherother and motherother in orig_nb_par:
 
                # the birth parent of other is non-birth of orig
 
                if fatherother and fatherother == fatherorig:
 
 
                     return self.HALF_SIB_FATHER
 
                     return self.HALF_SIB_FATHER
                 else:
+
                 elif motherother == motherorig:
                    return self.STEP_SIB
 
            other_nb_par = self._get_nonbirth_parent_list(db, other)
 
            if fatherorig and fatherorig in other_nb_par:
 
                # the one birth parent of other is non-birth of orig
 
                if motherorig and motherother == motherorig:
 
 
                     return self.HALF_SIB_MOTHER
 
                     return self.HALF_SIB_MOTHER
 
                 else:
 
                 else:
 
                     return self.STEP_SIB
 
                     return self.STEP_SIB
             if motherorig and motherorig in other_nb_par:
+
             else:
                # the one birth parent of other is non-birth of orig
+
                # some birth parents are not known, hence we or cannot know if
                if fatherother and fatherother == fatherorig:
+
                # half siblings. step siblings might be possible, otherwise give up
                    return self.HALF_SIB_FATHER
+
                orig_nb_par = self._get_nonbirth_parent_list(db, orig)
                else:
+
                if fatherother and fatherother in orig_nb_par:
                    return self.STEP_SIB
+
                    # the birth parent of other is non-birth of orig
            # there is an unknown birth parent, it could be that this is the
+
                    if motherother and motherother == motherorig:
            # birth parent of the other person
+
                        return self.HALF_SIB_MOTHER
            return self.UNKNOWN_SIB
+
                    else:
 +
                        return self.STEP_SIB
 +
                if motherother and motherother in orig_nb_par:
 +
                    # the birth parent of other is non-birth of orig
 +
                    if fatherother and fatherother == fatherorig:
 +
                        return self.HALF_SIB_FATHER
 +
                    else:
 +
                        return self.STEP_SIB
 +
                other_nb_par = self._get_nonbirth_parent_list(db, other)
 +
                if fatherorig and fatherorig in other_nb_par:
 +
                    # the one birth parent of other is non-birth of orig
 +
                    if motherorig and motherother == motherorig:
 +
                        return self.HALF_SIB_MOTHER
 +
                    else:
 +
                        return self.STEP_SIB
 +
                if motherorig and motherorig in other_nb_par:
 +
                    # the one birth parent of other is non-birth of orig
 +
                    if fatherother and fatherother == fatherorig:
 +
                        return self.HALF_SIB_FATHER
 +
                    else:
 +
                        return self.STEP_SIB
 +
                # there is an unknown birth parent, it could be that this is the
 +
                # birth parent of the other person
 +
                return self.UNKNOWN_SIB
  
    def get_birth_parents(self, db, person):
+
        def get_birth_parents(self, db, person):
        """
+
            """
        Method that returns the birthparents of a person as tuple
+
            Method that returns the birthparents of a person as tuple
        (mother handle, father handle), if no known birthparent, the
+
            (mother handle, father handle), if no known birthparent, the
        handle is replaced by None
+
            handle is replaced by None
        """
+
            """
        birthfather = None
+
            birthfather = None
        birthmother = None
+
            birthmother = None
        for fam in person.get_parent_family_handle_list():
+
            for fam in person.get_parent_family_handle_list():
            family = db.get_family_from_handle(fam)
+
                family = db.get_family_from_handle(fam)
            if not family:
+
                if not family:
                continue
+
                    continue
            childrel = [
+
                childrel = [
                (ref.get_mother_relation(), ref.get_father_relation())
+
                    (ref.get_mother_relation(), ref.get_father_relation())
                for ref in family.get_child_ref_list()
+
                    for ref in family.get_child_ref_list()
                if ref.ref == person.handle
+
                    if ref.ref == person.handle
            ]
+
                ]
            if not birthmother and childrel[0][0] == ChildRefType.BIRTH:
+
                if not birthmother and childrel[0][0] == ChildRefType.BIRTH:
                birthmother = family.get_mother_handle()
+
                    birthmother = family.get_mother_handle()
            if not birthfather and childrel[0][1] == ChildRefType.BIRTH:
+
                if not birthfather and childrel[0][1] == ChildRefType.BIRTH:
                birthfather = family.get_father_handle()
+
                    birthfather = family.get_father_handle()
            if birthmother and birthfather:
+
                if birthmother and birthfather:
                break
+
                    break
        return (birthmother, birthfather)
+
            return (birthmother, birthfather)
  
    def _get_nonbirth_parent_list(self, db, person):
+
        def _get_nonbirth_parent_list(self, db, person):
        """
+
            """
        Returns a list of handles of parents of which it is known
+
            Returns a list of handles of parents of which it is known
        they are not birth parents.
+
            they are not birth parents.
        So all parents which do not have relation BIRTH or UNKNOWN
+
            So all parents which do not have relation BIRTH or UNKNOWN
        are returned.
+
            are returned.
        """
+
            """
        nb_parents = []
+
            nb_parents = []
        for fam in person.get_parent_family_handle_list():
+
            for fam in person.get_parent_family_handle_list():
            family = db.get_family_from_handle(fam)
+
                family = db.get_family_from_handle(fam)
            if not family:
+
                if not family:
                continue
+
                    continue
            childrel = [
+
                childrel = [
                (ref.get_mother_relation(), ref.get_father_relation())
+
                    (ref.get_mother_relation(), ref.get_father_relation())
                for ref in family.get_child_ref_list()
+
                    for ref in family.get_child_ref_list()
                if ref.ref == person.handle
+
                    if ref.ref == person.handle
            ]
+
                ]
            if (
+
                if (
                childrel[0][0] != ChildRefType.BIRTH
+
                    childrel[0][0] != ChildRefType.BIRTH
                and childrel[0][0] != ChildRefType.UNKNOWN
+
                    and childrel[0][0] != ChildRefType.UNKNOWN
            ):
+
                ):
                nb_parents.append(family.get_mother_handle())
+
                    nb_parents.append(family.get_mother_handle())
            if (
+
                if (
                childrel[0][1] != ChildRefType.BIRTH
+
                    childrel[0][1] != ChildRefType.BIRTH
                and childrel[0][1] != ChildRefType.UNKNOWN
+
                    and childrel[0][1] != ChildRefType.UNKNOWN
            ):
+
                ):
                nb_parents.append(family.get_father_handle())
+
                    nb_parents.append(family.get_father_handle())
        # make every person appear only once:
+
            # make every person appear only once:
        return list(set(nb_parents))
+
            return list(set(nb_parents))
  
    def _get_spouse_type(self, db, orig, other, all_rel=False):
+
        def _get_spouse_type(self, db, orig, other, all_rel=False):
        """
+
            """
        Translation free determination if orig and other are partners.
+
            Translation free determination if orig and other are partners.
        The procedure returns partner types, these can be passed to
+
            The procedure returns partner types, these can be passed to
        get_partner_relationship_string.
+
            get_partner_relationship_string.
        If all_rel=False, returns None or a partner type.
+
            If all_rel=False, returns None or a partner type.
        If all_rel=True, returns a list, empty if no partner
+
            If all_rel=True, returns a list, empty if no partner
        """
+
            """
        val = []
+
            val = []
        for family_handle in orig.get_family_handle_list():
+
            for family_handle in orig.get_family_handle_list():
            family = db.get_family_from_handle(family_handle)
+
                family = db.get_family_from_handle(family_handle)
            # return first found spouse type
+
                # return first found spouse type
            if family and other.get_handle() in [
+
                if family and other.get_handle() in [
                family.get_father_handle(),
+
                    family.get_father_handle(),
                family.get_mother_handle(),
+
                    family.get_mother_handle(),
            ]:
+
                ]:
                family_rel = family.get_relationship()
+
                    family_rel = family.get_relationship()
                # check for divorce event:
+
                    # check for divorce event:
                ex = False
+
                    ex = False
                for eventref in family.get_event_ref_list():
+
                    for eventref in family.get_event_ref_list():
                    event = db.get_event_from_handle(eventref.ref)
+
                        event = db.get_event_from_handle(eventref.ref)
                    if event and (
+
                        if event and (
                        event.get_type() == EventType.DIVORCE
+
                            event.get_type() == EventType.DIVORCE
                        or event.get_type() == EventType.ANNULMENT
+
                            or event.get_type() == EventType.ANNULMENT
                    ):
+
                        ):
                        ex = True
+
                            ex = True
                        break
+
                            break
                if family_rel == FamilyRelType.MARRIED:
+
                    if family_rel == FamilyRelType.MARRIED:
                     if ex:
+
                        if ex:
                         val.append(self.PARTNER_EX_MARRIED)
+
                            val.append(self.PARTNER_EX_MARRIED)
 +
                        else:
 +
                            val.append(self.PARTNER_MARRIED)
 +
                     elif family_rel == FamilyRelType.UNMARRIED:
 +
                        if ex:
 +
                            val.append(self.PARTNER_EX_UNMARRIED)
 +
                         else:
 +
                            val.append(self.PARTNER_UNMARRIED)
 +
                    elif family_rel == FamilyRelType.CIVIL_UNION:
 +
                        if ex:
 +
                            val.append(self.PARTNER_EX_CIVIL_UNION)
 +
                        else:
 +
                            val.append(self.PARTNER_CIVIL_UNION)
 
                     else:
 
                     else:
                         val.append(self.PARTNER_MARRIED)
+
                         if ex:
                elif family_rel == FamilyRelType.UNMARRIED:
+
                            val.append(self.PARTNER_EX_UNKNOWN_REL)
                    if ex:
+
                        else:
                        val.append(self.PARTNER_EX_UNMARRIED)
+
                            val.append(self.PARTNER_UNKNOWN_REL)
                    else:
+
 
                        val.append(self.PARTNER_UNMARRIED)
+
            if all_rel:
                elif family_rel == FamilyRelType.CIVIL_UNION:
+
                return val
                    if ex:
+
            else:
                        val.append(self.PARTNER_EX_CIVIL_UNION)
+
                # last relation is normally the defenitive relation
                    else:
+
                if val:
                        val.append(self.PARTNER_CIVIL_UNION)
+
                    return val[-1]
 
                 else:
 
                 else:
                     if ex:
+
                     return None
                        val.append(self.PARTNER_EX_UNKNOWN_REL)
 
                    else:
 
                        val.append(self.PARTNER_UNKNOWN_REL)
 
  
         if all_rel:
+
         def is_spouse(self, db, orig, other, all_rel=False):
             return val
+
             """
        else:
+
             Determine the spouse relation
             # last relation is normally the defenitive relation
+
            """
             if val:
+
            spouse_type = self._get_spouse_type(db, orig, other, all_rel)
                 return val[-1]
+
             if spouse_type:
 +
                 return self.get_partner_relationship_string(
 +
                    spouse_type, orig.get_gender(), other.get_gender()
 +
                )
 
             else:
 
             else:
 
                 return None
 
                 return None
  
    def is_spouse(self, db, orig, other, all_rel=False):
+
        def get_relationship_distance_new(
        """
+
            self,
         Determine the spouse relation
+
            db,
        """
+
            orig_person,
        spouse_type = self._get_spouse_type(db, orig, other, all_rel)
+
            other_person,
        if spouse_type:
+
            all_families=False,
             return self.get_partner_relationship_string(
+
            all_dist=False,
                spouse_type, orig.get_gender(), other.get_gender()
+
            only_birth=True,
            )
+
         ):
        else:
+
            """
            return None
+
            Return 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:
  
    def get_relationship_distance_new(
+
            .. note:: _new can be removed once all rel_xx modules no longer
        self,
+
                      overwrite get_relationship_distance
        db,
 
        orig_person,
 
        other_person,
 
        all_families=False,
 
        all_dist=False,
 
        only_birth=True,
 
    ):
 
        """
 
        Return 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:
 
  
        .. note:: _new can be removed once all rel_xx modules no longer
+
            The tuple or list of tuples consists of:
                  overwrite get_relationship_distance
 
  
         The tuple or list of tuples consists of:
+
            ==============  =====================================================
 +
            Element         Description
 +
            ==============  =====================================================
 +
            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
        Element        Description
+
            common ancestor is mother of the second family of the father of the
        ==============  =====================================================
+
             first family of the father of the third family.
        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
+
            Note that the same person might be present twice if the person is
        common ancestor is mother of the second family of the father of the
+
            reached via a different branch too. Path (firstRel_str and
        first family of the father of the third family.
+
            secondRel_str) will of course be different.
  
        Note that the same person might be present twice if the person is
+
            :param db: database to work on
        reached via a different branch too. Path (firstRel_str and
+
            :param orig_person: first person
        secondRel_str) will of course be different.
+
            :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
 +
            """
 +
            # data storage to communicate with recursive functions
 +
            self.__max_depth_reached = False
 +
            self.__loop_detected = False
 +
            self.__max_depth = self.get_depth()
 +
            self.__all_families = all_families
 +
            self.__all_dist = all_dist
 +
            self.__only_birth = only_birth
 +
            self.__crosslinks = False  # no crosslinks
  
        :param db: database to work on
+
            first_rel = -1
        :param orig_person: first person
+
            second_rel = -1
        :type orig_person: Person Obj
+
            self.__msg = []
        :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
 
        """
 
        # data storage to communicate with recursive functions
 
        self.__max_depth_reached = False
 
        self.__loop_detected = False
 
        self.__max_depth = self.get_depth()
 
        self.__all_families = all_families
 
        self.__all_dist = all_dist
 
        self.__only_birth = only_birth
 
        self.__crosslinks = False  # no crosslinks
 
  
        first_rel = -1
+
            common = []
        second_rel = -1
+
            first_map = {}
        self.__msg = []
+
            second_map = {}
 +
            rank = 9999999
  
        common = []
+
            try:
        first_map = {}
+
                if (
        second_map = {}
+
                    self.storemap
        rank = 9999999
+
                    and self.stored_map is not None
 
+
                    and self.map_handle == orig_person.handle
        try:
+
                    and not self.dirtymap
            if (
+
                ):
                self.storemap
+
                    first_map = self.stored_map
                and self.stored_map is not None
+
                    (
                and self.map_handle == orig_person.handle
+
                        self.__max_depth_reached,
                and not self.dirtymap
+
                        self.__loop_detected,
            ):
+
                        self.__all_families,
                first_map = self.stored_map
+
                        self.__all_dist,
                (
+
                        self.__only_birth,
                    self.__max_depth_reached,
+
                        self.__crosslinks,
                    self.__loop_detected,
+
                        self.__msg,
                    self.__all_families,
+
                    ) = self.map_meta
                    self.__all_dist,
+
                    self.__msg = list(self.__msg)
                    self.__only_birth,
+
                else:
                    self.__crosslinks,
+
                    self.__apply_filter(db, orig_person, "", [], first_map)
                    self.__msg,
+
                    self.map_meta = (
                ) = self.map_meta
+
                        self.__max_depth_reached,
                self.__msg = list(self.__msg)
+
                        self.__loop_detected,
            else:
+
                        self.__all_families,
                self.__apply_filter(db, orig_person, "", [], first_map)
+
                        self.__all_dist,
                self.map_meta = (
+
                        self.__only_birth,
                    self.__max_depth_reached,
+
                        self.__crosslinks,
                    self.__loop_detected,
+
                        list(self.__msg),
                    self.__all_families,
+
                    )
                    self.__all_dist,
+
                self.__apply_filter(
                    self.__only_birth,
+
                    db, other_person, "", [], second_map, stoprecursemap=first_map
                    self.__crosslinks,
 
                    list(self.__msg),
 
 
                 )
 
                 )
             self.__apply_filter(
+
             except RuntimeError:
                db, other_person, "", [], second_map, stoprecursemap=first_map
+
                return (-1, None, -1, [], -1, []), [
            )
+
                    _(
        except RuntimeError:
+
                        "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
            return (-1, None, -1, [], -1, []), [
+
                        "נסרקו %d דורות .\nלא מן הנמנע "
                _(
+
                        "שהתפספסו קשרי קירבת משפחה."
                    "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
+
                    )
                    "נסרקו %d דורות .\nלא מן הנמנע "
+
                ] + self.__msg
                    "שהתפספסו קשרי קירבת משפחה."
 
                )
 
            ] + self.__msg
 
  
        if self.storemap:
+
            if self.storemap:
            self.stored_map = first_map
+
                self.stored_map = first_map
            self.dirtymap = False
+
                self.dirtymap = False
            self.map_handle = orig_person.handle
+
                self.map_handle = orig_person.handle
  
        for person_handle in second_map:
+
            for person_handle in second_map:
            if person_handle in first_map:
+
                if person_handle in first_map:
                com = []
+
                    com = []
                # a common ancestor
+
                    # a common ancestor
                for rel1, fam1 in zip(
+
                    for rel1, fam1 in zip(
                    first_map[person_handle][0], first_map[person_handle][1]
+
                        first_map[person_handle][0], first_map[person_handle][1]
                ):
 
                    len1 = len(rel1)
 
                    for rel2, fam2 in zip(
 
                        second_map[person_handle][0], second_map[person_handle][1]
 
 
                     ):
 
                     ):
                         len2 = len(rel2)
+
                         len1 = len(rel1)
                        # collect paths to arrive at common ancestor
+
                        for rel2, fam2 in zip(
                        com.append((len1 + len2, person_handle, rel1, fam1, rel2, fam2))
+
                            second_map[person_handle][0], second_map[person_handle][1]
                # insert common ancestor in correct position,
+
                        ):
                #  if shorter links, check if not subset
+
                            len2 = len(rel2)
                #  if longer links, check if not superset
+
                            # collect paths to arrive at common ancestor
                pos = 0
+
                            com.append((len1 + len2, person_handle, rel1, fam1, rel2, fam2))
                for ranknew, handlenew, rel1new, fam1new, rel2new, fam2new in com:
+
                    # insert common ancestor in correct position,
                    insert = True
+
                    #  if shorter links, check if not subset
                    for rank, handle, rel1, fam1, rel2, fam2 in common:
+
                    #  if longer links, check if not superset
                        if ranknew < rank:
+
                    pos = 0
                            break
+
                    for ranknew, handlenew, rel1new, fam1new, rel2new, fam2new in com:
                        elif ranknew >= rank:
+
                        insert = True
                            # check subset
+
                        for rank, handle, rel1, fam1, rel2, fam2 in common:
                            if (
+
                            if ranknew < rank:
                                rel1 == rel1new[: len(rel1)]
 
                                and rel2 == rel2new[: len(rel2)]
 
                            ):
 
                                # subset relation exists already
 
                                insert = False
 
 
                                 break
 
                                 break
                        pos += 1
+
                            elif ranknew >= rank:
                    if insert:
+
                                # check subset
                        if common:
+
                                if (
                            common.insert(
+
                                    rel1 == rel1new[: len(rel1)]
                                pos,
+
                                    and rel2 == rel2new[: len(rel2)]
                                (
+
                                ):
                                    ranknew,
+
                                    # subset relation exists already
                                    handlenew,
+
                                    insert = False
                                    rel1new,
+
                                    break
                                    fam1new,
+
                            pos += 1
                                    rel2new,
+
                        if insert:
                                    fam2new,
+
                            if common:
                                ),
+
                                common.insert(
                            )
+
                                    pos,
                        else:
+
                                    (
                            common = [
+
                                        ranknew,
                                (ranknew, handlenew, rel1new, fam1new, rel2new, fam2new)
+
                                        handlenew,
                            ]
+
                                        rel1new,
                        # now check if superset must be deleted from common
+
                                        fam1new,
                        deletelist = []
+
                                        rel2new,
                        index = pos + 1
+
                                        fam2new,
                        for rank, handle, rel1, fam1, rel2, fam2 in common[pos + 1 :]:
+
                                    ),
                            if (
+
                                )
                                rel1new == rel1[: len(rel1new)]
+
                            else:
                                and rel2new == rel2[: len(rel2new)]
+
                                common = [
                            ):
+
                                    (ranknew, handlenew, rel1new, fam1new, rel2new, fam2new)
                                deletelist.append(index)
+
                                ]
                            index += 1
+
                            # now check if superset must be deleted from common
                        deletelist.reverse()
+
                            deletelist = []
                        for index in deletelist:
+
                            index = pos + 1
                            del common[index]
+
                            for rank, handle, rel1, fam1, rel2, fam2 in common[pos + 1 :]:
        # check for extra messages
+
                                if (
        if self.__max_depth_reached:
+
                                    rel1new == rel1[: len(rel1new)]
            self.__msg += [
+
                                    and rel2new == rel2[: len(rel2new)]
                _(
+
                                ):
                    "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
+
                                    deletelist.append(index)
                    "נסרקו %d דורות .\nלא מן הנמנע "
+
                                index += 1
                    "שהתפספסו קשרי קירבת משפחה."
+
                            deletelist.reverse()
                )
+
                            for index in deletelist:
                % (self.__max_depth)
+
                                del common[index]
            ]
+
            # check for extra messages
 +
            if self.__max_depth_reached:
 +
                self.__msg += [
 +
                    _(
 +
                        "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
 +
                        "נסרקו %d דורות .\nלא מן הנמנע "
 +
                        "שהתפספסו קשרי קירבת משפחה."
 +
                    )
 +
                    % (self.__max_depth)
 +
                ]
  
        if common and not self.__all_dist:
+
            if common and not self.__all_dist:
            rank = common[0][0]
+
                rank = common[0][0]
            person_handle = common[0][1]
+
                person_handle = common[0][1]
            first_rel = common[0][2]
+
                first_rel = common[0][2]
            first_fam = common[0][3]
+
                first_fam = common[0][3]
            second_rel = common[0][4]
+
                second_rel = common[0][4]
            second_fam = common[0][5]
+
                second_fam = common[0][5]
            return (
+
                return (
                rank,
+
                    rank,
                person_handle,
+
                    person_handle,
                first_rel,
+
                    first_rel,
                first_fam,
+
                    first_fam,
                second_rel,
+
                    second_rel,
                second_fam,
+
                    second_fam,
            ), self.__msg
+
                ), self.__msg
        if common:
+
            if common:
            # list with tuples (rank, handle person,rel_str_orig,rel_fam_orig,
+
                # list with tuples (rank, handle person,rel_str_orig,rel_fam_orig,
            #      rel_str_other,rel_fam_str) and messages
+
                #      rel_str_other,rel_fam_str) and messages
            return common, self.__msg
+
                return common, self.__msg
        if not self.__all_dist:
+
            if not self.__all_dist:
            return (-1, None, "", [], "", []), self.__msg
+
                return (-1, None, "", [], "", []), self.__msg
        else:
+
            else:
            return [(-1, None, "", [], "", [])], self.__msg
+
                return [(-1, None, "", [], "", [])], self.__msg
  
    def __apply_filter(
+
        def __apply_filter(
        self, db, person, rel_str, rel_fam, pmap, depth=1, stoprecursemap=None
+
            self, db, person, rel_str, rel_fam, pmap, depth=1, stoprecursemap=None
    ):
+
        ):
        """
+
            """
        Typically this method is called recursively in two ways:
+
            Typically this method is called recursively in two ways:
        First method is stoprecursemap= None
+
            First method is stoprecursemap= None
        In this case a recursemap is builded by storing all data.
+
            In this case a recursemap is builded by storing all data.
  
        Second method is with a stoprecursemap given
+
            Second method is with a stoprecursemap given
        In this case parents are recursively looked up. If present in
+
            In this case parents are recursively looked up. If present in
        stoprecursemap, a common ancestor is found, and the method can
+
            stoprecursemap, a common ancestor is found, and the method can
        stop looking further. If however self.__crosslinks == True, the data
+
            stop looking further. If however self.__crosslinks == True, the data
        of first contains loops, and parents
+
            of first contains loops, and parents
        will be looked up anyway an stored if common. At end the doubles
+
            will be looked up anyway an stored if common. At end the doubles
        are filtered out
+
            are filtered out
        """
+
            """
        if person is None or not person.handle:
+
            if person is None or not person.handle:
            return
+
                return
  
        if depth > self.__max_depth:
+
            if depth > self.__max_depth:
            self.__max_depth_reached = True
+
                self.__max_depth_reached = True
            # print('Maximum ancestor generations ('+str(depth)+') reached', \
+
                # print('Maximum ancestor generations ('+str(depth)+') reached', \
            #            '(' + rel_str + ').',\
+
                #            '(' + rel_str + ').',\
            #            'Stopping relation algorithm.')
+
                #            'Stopping relation algorithm.')
            return
+
                return
        depth += 1
+
            depth += 1
  
        commonancestor = False
+
            commonancestor = False
        store = True  # normally we store all parents
+
            store = True  # normally we store all parents
        if stoprecursemap:
+
            if stoprecursemap:
            store = False  # but not if a stop map given
+
                store = False  # but not if a stop map given
            if person.handle in stoprecursemap:
+
                if person.handle in stoprecursemap:
                commonancestor = True
+
                    commonancestor = True
                store = True
+
                    store = True
  
        # add person to the map, take into account that person can be obtained
+
            # add person to the map, take into account that person can be obtained
        # from different sides
+
            # from different sides
        if person.handle in pmap:
+
            if person.handle in pmap:
            # person is already a grandparent in another branch, we already have
+
                # person is already a grandparent in another branch, we already have
            # had lookup of all parents, we call that a crosslink
+
                # had lookup of all parents, we call that a crosslink
            if not stoprecursemap:
+
                if not stoprecursemap:
                self.__crosslinks = True
+
                    self.__crosslinks = True
            pmap[person.handle][0] += [rel_str]
+
                pmap[person.handle][0] += [rel_str]
            pmap[person.handle][1] += [rel_fam]
+
                pmap[person.handle][1] += [rel_fam]
            # check if there is no loop father son of his son, ...
+
                # check if there is no loop father son of his son, ...
            # loop means person is twice reached, same rel_str in begin
+
                # loop means person is twice reached, same rel_str in begin
            for rel1 in pmap[person.handle][0]:
+
                for rel1 in pmap[person.handle][0]:
                for rel2 in pmap[person.handle][0]:
+
                    for rel2 in pmap[person.handle][0]:
                    if len(rel1) < len(rel2) and rel1 == rel2[: len(rel1)]:
+
                        if len(rel1) < len(rel2) and rel1 == rel2[: len(rel1)]:
                        # loop, keep one message in storage!
+
                            # loop, keep one message in storage!
                        self.__loop_detected = True
+
                            self.__loop_detected = True
                        self.__msg += [
+
                            self.__msg += [
                            _("Relationship loop detected:")
+
                                _("Relationship loop detected:")
                            + " "
+
                                + " "
                            + _(
+
                                + _(
                                "Person %(person)s connects to himself via %(relation)s"
+
                                    "Person %(person)s connects to himself via %(relation)s"
                            )
+
                                )
                            % {
+
                                % {
                                "person": person.get_primary_name().get_name(),
+
                                    "person": person.get_primary_name().get_name(),
                                "relation": rel2[len(rel1) :],
+
                                    "relation": rel2[len(rel1) :],
                            }
+
                                }
                        ]
+
                            ]
                        return
+
                            return
        elif store:
+
            elif store:
            pmap[person.handle] = [[rel_str], [rel_fam]]
+
                pmap[person.handle] = [[rel_str], [rel_fam]]
  
        # having added person to the pmap, we only look up recursively to
+
            # having added person to the pmap, we only look up recursively to
        # parents if this person is not common relative
+
            # parents if this person is not common relative
        # if however the first map has crosslinks, we need to continue reduced
+
            # if however the first map has crosslinks, we need to continue reduced
        if commonancestor and not self.__crosslinks:
+
            if commonancestor and not self.__crosslinks:
            # don't continue search, great speedup!
+
                # don't continue search, great speedup!
            return
+
                return
  
        family_handles = []
+
            family_handles = []
        main = person.get_main_parents_family_handle()
+
            main = person.get_main_parents_family_handle()
        if main:
+
            if main:
            family_handles = [main]
+
                family_handles = [main]
        if self.__all_families:
+
            if self.__all_families:
            family_handles = person.get_parent_family_handle_list()
+
                family_handles = person.get_parent_family_handle_list()
  
        try:
+
            try:
            parentstodo = {}
+
                parentstodo = {}
            fam = 0
+
                fam = 0
            for family_handle in family_handles:
+
                for family_handle in family_handles:
                rel_fam_new = rel_fam + [fam]
+
                    rel_fam_new = rel_fam + [fam]
                family = db.get_family_from_handle(family_handle)
+
                    family = db.get_family_from_handle(family_handle)
                if not family:
+
                    if not family:
                    continue
+
                        continue
                # obtain childref for this person
+
                    # obtain childref for this person
                childrel = [
+
                    childrel = [
                    (ref.get_mother_relation(), ref.get_father_relation())
+
                        (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 data[0] not in parentstodo:
 
                        persontodo = db.get_person_from_handle(data[0])
 
                        if data[3] == 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:
 
                        # this person is already scheduled to research
 
                        # update family list
 
                        famlist = parentstodo[data[0]][2]
 
                        if not isinstance(famlist[-1], list) and 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()
 
                         for ref in family.get_child_ref_list()
                         if ref.ref != person.handle
+
                         if ref.ref == person.handle
 
                     ]
 
                     ]
                     addstr = self.REL_SIBLING
+
                     fhandle = family.father_handle
                    for chandle in child_list:
+
                    mhandle = family.mother_handle
                        if chandle in pmap:
+
                    for data in [
                            pmap[chandle][0] += [rel_str + addstr]
+
                        (
                            pmap[chandle][1] += [rel_fam_new]
+
                            fhandle,
                            # person is already a grandparent in another branch
+
                            self.REL_FATHER,
                        else:
+
                            self.REL_FATHER_NOTBIRTH,
                            pmap[chandle] = [[rel_str + addstr], [rel_fam_new]]
+
                            childrel[0][1],
                fam += 1
+
                        ),
 +
                        (
 +
                            mhandle,
 +
                            self.REL_MOTHER,
 +
                            self.REL_MOTHER_NOTBIRTH,
 +
                            childrel[0][0],
 +
                        ),
 +
                    ]:
 +
                        if data[0] and data[0] not in parentstodo:
 +
                            persontodo = db.get_person_from_handle(data[0])
 +
                            if data[3] == 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:
 +
                            # this person is already scheduled to research
 +
                            # update family list
 +
                            famlist = parentstodo[data[0]][2]
 +
                            if not isinstance(famlist[-1], list) and 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 chandle in pmap:
 +
                                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, data in parentstodo.items():
+
                for handle, data in parentstodo.items():
                self.__apply_filter(
+
                    self.__apply_filter(
                    db, data[0], data[1], data[2], pmap, depth, stoprecursemap
+
                        db, data[0], data[1], data[2], pmap, depth, stoprecursemap
                )
+
                    )
        except:
+
            except:
            import traceback
+
                import traceback
  
            traceback.print_exc()
+
                traceback.print_exc()
            return
+
                return
  
    def collapse_relations(self, relations):
+
        def collapse_relations(self, relations):
        """
+
            """
        Internal method to condense the relationships as returned by
+
            Internal method to condense the relationships as returned by
        get_relationship_distance_new.
+
            get_relationship_distance_new.
        Common ancestors in the same family are collapsed to one entry,
+
            Common ancestors in the same family are collapsed to one entry,
        changing the person paths to family paths, eg 'mf' and 'mm' become 'ma'
+
            changing the person paths to family paths, eg 'mf' and 'mm' become 'ma'
  
        relations : list of relations as returned by
+
            relations : list of relations as returned by
                    get_relationship_distance_new with all_dist = True
+
                        get_relationship_distance_new with all_dist = True
  
        returns : the same data as relations, but collapsed, hence the
+
            returns : the same data as relations, but collapsed, hence the
                  handle entry is now a list of handles, and the
+
                      handle entry is now a list of handles, and the
                  path to common ancestors can now contain family
+
                      path to common ancestors can now contain family
                  identifiers (eg 'a', ...)
+
                      identifiers (eg 'a', ...)
                  In the case of sibling, this is replaced by family
+
                      In the case of sibling, this is replaced by family
                  with common ancestor handles empty list []!
+
                      with common ancestor handles empty list []!
        """
+
            """
        if relations[0][0] == -1:
+
            if relations[0][0] == -1:
            return relations
+
                return relations
        commonnew = []
+
            commonnew = []
        existing_path = []
+
            existing_path = []
        for relation in relations:
+
            for relation in relations:
            relstrfirst = None
+
                relstrfirst = None
            commonhandle = [relation[1]]
+
                commonhandle = [relation[1]]
            if relation[2]:
+
                if relation[2]:
                relstrfirst = relation[2][:-1]
+
                    relstrfirst = relation[2][:-1]
            relstrsec = None
+
                relstrsec = None
            if relation[4]:
+
                if relation[4]:
                relstrsec = relation[4][:-1]
+
                    relstrsec = relation[4][:-1]
            relfamfirst = relation[3][:]
+
                relfamfirst = relation[3][:]
            relfamsec = relation[5][:]
+
                relfamsec = relation[5][:]
            # handle pure sibling:
+
                # handle pure sibling:
            rela2 = relation[2]
+
                rela2 = relation[2]
            rela4 = relation[4]
+
                rela4 = relation[4]
            if relation[2] and relation[2][-1] == self.REL_SIBLING:
+
                if relation[2] and relation[2][-1] == self.REL_SIBLING:
                # sibling will be the unique common ancestor,
+
                    # sibling will be the unique common ancestor,
                # change to a family with unknown handle for common ancestor
+
                    # change to a family with unknown handle for common ancestor
                rela2 = relation[2][:-1] + self.REL_FAM_BIRTH
+
                    rela2 = relation[2][:-1] + self.REL_FAM_BIRTH
                rela4 = relation[4] + self.REL_FAM_BIRTH
+
                    rela4 = relation[4] + self.REL_FAM_BIRTH
                relfamsec = relfamsec + [relfamfirst[-1]]
+
                    relfamsec = relfamsec + [relfamfirst[-1]]
                relstrsec = relation[4][:-1]
+
                    relstrsec = relation[4][:-1]
                commonhandle = []
+
                    commonhandle = []
  
            # a unique path to family of common person:
+
                # a unique path to family of common person:
            familypaths = []
+
                familypaths = []
            if relfamfirst and isinstance(relfamfirst[-1], list):
+
                if relfamfirst and isinstance(relfamfirst[-1], list):
                if relfamsec and isinstance(relfamsec[-1], list):
+
                    if relfamsec and isinstance(relfamsec[-1], list):
                    for val1 in relfamfirst[-1]:
+
                        for val1 in relfamfirst[-1]:
                        for val2 in relfamsec[-1]:
+
                            for val2 in relfamsec[-1]:
 +
                                familypaths.append(
 +
                                    (
 +
                                        relstrfirst,
 +
                                        relstrsec,
 +
                                        relfamfirst[:-1] + [val1],
 +
                                        relfamsec[:-1] + [val2],
 +
                                    )
 +
                                )
 +
                    else:
 +
                        for val1 in relfamfirst[-1]:
 
                             familypaths.append(
 
                             familypaths.append(
 
                                 (
 
                                 (
Line 1,184: Line 1,184:
 
                                     relstrsec,
 
                                     relstrsec,
 
                                     relfamfirst[:-1] + [val1],
 
                                     relfamfirst[:-1] + [val1],
                                     relfamsec[:-1] + [val2],
+
                                     relfamsec,
 
                                 )
 
                                 )
 
                             )
 
                             )
 +
                elif relfamsec and isinstance(relfamsec[-1], list):
 +
                    for val2 in relfamsec[-1]:
 +
                        familypaths.append(
 +
                            (relstrfirst, relstrsec, relfamfirst, relfamsec[:-1] + [val2])
 +
                        )
 
                 else:
 
                 else:
                     for val1 in relfamfirst[-1]:
+
                     familypaths.append((relstrfirst, relstrsec, relfamfirst, relfamsec))
                         familypaths.append(
+
                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 = rela2[-1]
 +
                        scndcomstr = tmp[2][-1]
 +
                        newcomstra = self._famrel_from_persrel(frstcomstr, scndcomstr)
 +
                        frstcomstr = rela4[-1]
 +
                        scndcomstr = tmp[4][-1]
 +
                        newcomstrb = self._famrel_from_persrel(frstcomstr, scndcomstr)
 +
 
 +
                        commonnew[posfam] = (
 +
                            tmp[0],
 +
                            tmp[1] + commonhandle,
 +
                            rela2[:-1] + newcomstra,
 +
                            tmp[3],
 +
                            rela4[:-1] + newcomstrb,
 +
                            tmp[5],
 +
                        )
 +
                    else:
 +
                         existing_path.append(familypath)
 +
                        commonnew.append(
 
                             (
 
                             (
                                 relstrfirst,
+
                                 relation[0],
                                 relstrsec,
+
                                commonhandle,
                                 relfamfirst[:-1] + [val1],
+
                                 rela2,
                                 relfamsec,
+
                                 familypath[2],
 +
                                 rela4,
 +
                                familypath[3],
 
                             )
 
                             )
 
                         )
 
                         )
             elif relfamsec and isinstance(relfamsec[-1], list):
+
             # we now have multiple person handles, single families, now collapse
                for val2 in relfamsec[-1]:
+
            #  families again if all else equal
                     familypaths.append(
+
            collapsed = commonnew[:1]
                         (relstrfirst, relstrsec, relfamfirst, relfamsec[:-1] + [val2])
+
            for rel in commonnew[1:]:
                    )
+
                found = False
            else:
+
                for newrel in collapsed:
                familypaths.append((relstrfirst, relstrsec, relfamfirst, relfamsec))
+
                     if newrel[0:3] == rel[0:3] and newrel[4] == rel[4]:
            for familypath in familypaths:
+
                         # another familypath to arrive at same result, merge
                # familypath = (relstrfirst, relstrsec, relfamfirst, relfamsec)
+
                        path1 = []
                try:
+
                        path2 = []
                    posfam = existing_path.index(familypath)
+
                        for a, b in zip(newrel[3], rel[3]):
                except ValueError:
+
                            if a == b:
                    posfam = None
+
                                path1.append(a)
                # if relstr is '', the ancestor is unique, if posfam None,
+
                            elif isinstance(a, list):
                # first time we see this family path
+
                                path1.append(a.append(b))
                if (
+
                            else:
                    posfam is not None
+
                                path1.append([a, b])
                    and relstrfirst is not None
+
                        for a, b in zip(newrel[5], rel[5]):
                    and relstrsec is not None
+
                            if a == b:
                ):
+
                                path2.append(a)
                    # We already have a common ancestor of this family, just
+
                            elif isinstance(a, list):
                    # add the other, setting correct family relation.
+
                                path2.append(a.append(b))
                    tmp = commonnew[posfam]
+
                            else:
                    frstcomstr = rela2[-1]
+
                                path2.append([a, b])
                    scndcomstr = tmp[2][-1]
+
                        newrel[3][:] = path1[:]
                    newcomstra = self._famrel_from_persrel(frstcomstr, scndcomstr)
+
                        newrel[5][:] = path2[:]
                    frstcomstr = rela4[-1]
+
                        found = True
                    scndcomstr = tmp[4][-1]
+
                        break
                     newcomstrb = self._famrel_from_persrel(frstcomstr, scndcomstr)
+
                if not found:
 +
                     collapsed.append(rel)
 +
 
 +
            return collapsed
  
                    commonnew[posfam] = (
+
        def _famrel_from_persrel(self, persrela, persrelb):
                        tmp[0],
+
            """
                        tmp[1] + commonhandle,
+
            Conversion from eg 'f' and 'm' to 'a', so relation to the two
                        rela2[:-1] + newcomstra,
+
            persons of a common family is converted to a family relation
                        tmp[3],
+
            """
                        rela4[:-1] + newcomstrb,
+
            if persrela == persrelb:
                        tmp[5],
+
                # should not happen, procedure called in error, just return value
                    )
+
                return persrela
                else:
+
            if (persrela == self.REL_MOTHER and persrelb == self.REL_FATHER) or (
                    existing_path.append(familypath)
+
                persrelb == self.REL_MOTHER and persrela == self.REL_FATHER
                    commonnew.append(
+
             ):
                        (
+
                 return self.REL_FAM_BIRTH
                            relation[0],
+
            if (persrela == self.REL_MOTHER and persrelb == self.REL_FATHER_NOTBIRTH) or (
                            commonhandle,
+
                persrelb == self.REL_MOTHER and persrela == self.REL_FATHER_NOTBIRTH
                            rela2,
+
            ):
                            familypath[2],
+
                return self.REL_FAM_BIRTH_MOTH_ONLY
                            rela4,
+
            if (persrela == self.REL_FATHER and persrelb == self.REL_MOTHER_NOTBIRTH) or (
                            familypath[3],
+
                persrelb == self.REL_FATHER and persrela == self.REL_MOTHER_NOTBIRTH
                        )
+
            ):
                    )
+
                return self.REL_FAM_BIRTH_FATH_ONLY
        # we now have multiple person handles, single families, now collapse
+
            # catch calling with family relations already, return val
        #  families again if all else equal
+
            if (
        collapsed = commonnew[:1]
+
                persrela == self.REL_FAM_BIRTH
        for rel in commonnew[1:]:
+
                or persrela == self.REL_FAM_BIRTH_FATH_ONLY
            found = False
+
                or persrela == self.REL_FAM_BIRTH_MOTH_ONLY
             for newrel in collapsed:
+
                or persrela == self.REL_FAM_NONBIRTH
                 if newrel[0:3] == rel[0:3] and newrel[4] == rel[4]:
+
            ):
                    # another familypath to arrive at same result, merge
+
                return persrela
                    path1 = []
+
            if (
                    path2 = []
+
                persrelb == self.REL_FAM_BIRTH
                    for a, b in zip(newrel[3], rel[3]):
+
                or persrelb == self.REL_FAM_BIRTH_FATH_ONLY
                        if a == b:
+
                or persrelb == self.REL_FAM_BIRTH_MOTH_ONLY
                            path1.append(a)
+
                or persrelb == self.REL_FAM_NONBIRTH
                        elif isinstance(a, list):
+
             ):
                            path1.append(a.append(b))
+
                 return persrelb
                        else:
+
            return self.REL_FAM_NONBIRTH
                            path1.append([a, b])
 
                    for a, b in zip(newrel[5], rel[5]):
 
                        if a == b:
 
                            path2.append(a)
 
                        elif isinstance(a, list):
 
                            path2.append(a.append(b))
 
                        else:
 
                            path2.append([a, b])
 
                    newrel[3][:] = path1[:]
 
                    newrel[5][:] = path2[:]
 
                    found = True
 
                    break
 
             if not found:
 
                 collapsed.append(rel)
 
  
         return collapsed
+
         def only_birth(self, path):
 +
            """
 +
            Given a path to common ancestor. Return True if only birth
 +
            relations, False otherwise
 +
            """
 +
            for value in path:
 +
                if value in [
 +
                    self.REL_FAM_NONBIRTH,
 +
                    self.REL_FATHER_NOTBIRTH,
 +
                    self.REL_MOTHER_NOTBIRTH,
 +
                ]:
 +
                    return False
 +
            return True
  
    def _famrel_from_persrel(self, persrela, persrelb):
+
        def get_one_relationship(
        """
+
            self, db, orig_person, other_person, extra_info=False, olocale=glocale
        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 (
+
             Returns a string representing the most relevant relationship between
             persrelb == self.REL_MOTHER and persrela == self.REL_FATHER_NOTBIRTH
+
             the two people. If extra_info = True, extra information is returned:
        ):
+
             (relation_string, distance_common_orig, distance_common_other)
             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):
+
            If olocale is passed in (a GrampsLocale) that language will be used.
        """
 
        Given a path to common ancestor. Return True if only birth
 
        relations, False otherwise
 
        """
 
        for value in path:
 
            if value in [
 
                self.REL_FAM_NONBIRTH,
 
                self.REL_FATHER_NOTBIRTH,
 
                self.REL_MOTHER_NOTBIRTH,
 
            ]:
 
                return False
 
        return True
 
  
    def get_one_relationship(
+
            :param olocale: allow selection of the relationship language
        self, db, orig_person, other_person, extra_info=False, olocale=glocale
+
            :type olocale: a GrampsLocale instance
    ):
+
            """
        """
+
            self._locale = olocale
        Returns a string representing the most relevant relationship between
+
            stop = False
        the two people. If extra_info = True, extra information is returned:
+
            if orig_person is None:
        (relation_string, distance_common_orig, distance_common_other)
+
                rel_str = _("undefined")
 +
                stop = True
  
        If olocale is passed in (a GrampsLocale) that language will be used.
+
            if not stop and orig_person.get_handle() == other_person.get_handle():
 +
                rel_str = ""
 +
                stop = True
  
        :param olocale: allow selection of the relationship language
+
            if not stop:
        :type olocale: a GrampsLocale instance
+
                is_spouse = self.is_spouse(db, orig_person, other_person)
        """
+
                if is_spouse:
        self._locale = olocale
+
                    rel_str = is_spouse
        stop = False
+
                    stop = True
        if orig_person is None:
 
            rel_str = _("undefined")
 
            stop = True
 
  
        if not stop and orig_person.get_handle() == other_person.get_handle():
+
            if stop:
            rel_str = ""
+
                if extra_info:
            stop = True
+
                    return (rel_str, -1, -1)
 +
                else:
 +
                    return rel_str
  
        if not stop:
+
             data, msg = self.get_relationship_distance_new(
             is_spouse = self.is_spouse(db, orig_person, other_person)
+
                db,
             if is_spouse:
+
                orig_person,
                 rel_str = is_spouse
+
                other_person,
                 stop = True
+
                all_dist=True,
 +
                all_families=True,
 +
                only_birth=False,
 +
            )
 +
             if data[0][0] == -1:
 +
                 if extra_info:
 +
                    return ("", -1, -1)
 +
                 else:
 +
                    return ""
  
        if stop:
+
             data = self.collapse_relations(data)
             if extra_info:
 
                return (rel_str, -1, -1)
 
            else:
 
                return rel_str
 
  
        data, msg = self.get_relationship_distance_new(
+
             # most relevant relationship is a birth family relation of lowest rank
            db,
+
            databest = [data[0]]
            orig_person,
+
            rankbest = data[0][0]
            other_person,
+
            for rel in data:
            all_dist=True,
+
                # data is sorted on rank
             all_families=True,
+
                if rel[0] == rankbest:
            only_birth=False,
+
                    databest.append(rel)
        )
+
            rel = databest[0]
        if data[0][0] == -1:
 
            if extra_info:
 
                return ("", -1, -1)
 
            else:
 
                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:
 
            # data is sorted on rank
 
            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 == dist_other == 1:
 
                rel_str = self.get_sibling_relationship_string(
 
                    self.get_sibling_type(db, orig_person, other_person),
 
                    orig_person.get_gender(),
 
                    other_person.get_gender(),
 
                )
 
            else:
 
                rel_str = 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_orig = len(rel[2])
 
             dist_other = len(rel[4])
 
             dist_other = len(rel[4])
             birth = self.only_birth(rel[2]) and self.only_birth(rel[4])
+
             if len(databest) == 1:
            if dist_orig == dist_other == 1:
+
                birth = self.only_birth(rel[2]) and self.only_birth(rel[4])
                rel_str = self.get_sibling_relationship_string(
+
                if dist_orig == dist_other == 1:
                    self.get_sibling_type(db, orig_person, other_person),
+
                    rel_str = self.get_sibling_relationship_string(
                     orig_person.get_gender(),
+
                        self.get_sibling_type(db, orig_person, other_person),
                    other_person.get_gender(),
+
                        orig_person.get_gender(),
                )
+
                        other_person.get_gender(),
 +
                     )
 +
                else:
 +
                    rel_str = 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:
 
             else:
                 rel_str = self.get_single_relationship_string(
+
                 order = [
                    dist_orig,
+
                    self.REL_FAM_BIRTH,
                     dist_other,
+
                     self.REL_FAM_BIRTH_MOTH_ONLY,
                     orig_person.get_gender(),
+
                     self.REL_FAM_BIRTH_FATH_ONLY,
                     other_person.get_gender(),
+
                     self.REL_MOTHER,
                     rel[2],
+
                     self.REL_FATHER,
                     rel[4],
+
                     self.REL_SIBLING,
                     only_birth=birth,
+
                     self.REL_FAM_NONBIRTH,
                     in_law_a=False,
+
                     self.REL_MOTHER_NOTBIRTH,
                     in_law_b=False,
+
                     self.REL_FATHER_NOTBIRTH,
                 )
+
                 ]
        if extra_info:
+
                orderbest = order.index(self.REL_MOTHER)
            return (rel_str, dist_orig, dist_other)
+
                for relother in databest:
        else:
+
                    relbirth = self.only_birth(rel[2]) and self.only_birth(rel[4])
            return rel_str
+
                    if relother[2] == "" or relother[4] == "":
 
+
                        # direct relation, take that
    def get_all_relationships(self, db, orig_person, other_person):
+
                        rel = relother
        """
+
                        break
        Return a tuple, of which the first entry is a list with all
+
                    if (
        relationships in text, and the second a list of lists of all common
+
                        not relbirth
        ancestors that have that text as relationship
+
                        and self.only_birth(relother[2])
        """
+
                        and self.only_birth(relother[4])
        relstrings = []
+
                    ):
        commons = {}
+
                        # birth takes precedence
        if orig_person is None:
+
                        rel = relother
            return ([], [])
+
                        continue
 
+
                    if (
        if orig_person.get_handle() == other_person.get_handle():
+
                        order.index(relother[2][-1]) < order.index(rel[2][-1])
            return ([], [])
+
                        and order.index(relother[2][-1]) < orderbest
 
+
                    ):
        is_spouse = self.is_spouse(db, orig_person, other_person)
+
                        rel = relother
        if is_spouse:
+
                        continue
            relstrings.append(is_spouse)
+
                    if (
            commons[is_spouse] = []
+
                        order.index(relother[4][-1]) < order.index(rel[4][-1])
 
+
                        and order.index(relother[4][-1]) < orderbest
        data, msg = self.get_relationship_distance_new(
+
                    ):
            db,
+
                        rel = relother
            orig_person,
+
                        continue
            other_person,
+
                    if (
            all_dist=True,
+
                        order.index(rel[2][-1]) < orderbest
            all_families=True,
+
                        or order.index(rel[4][-1]) < orderbest
            only_birth=False,
+
                    ):
        )
+
                        # keep the good one
        if data[0][0] != -1:
+
                        continue
            data = self.collapse_relations(data)
+
                    if order.index(relother[2][-1]) < order.index(rel[2][-1]):
            for rel in data:
+
                        rel = relother
                rel2 = rel[2]
+
                        continue
                rel4 = rel[4]
+
                    if order.index(relother[2][-1]) == order.index(
                rel1 = rel[1]
+
                        rel[2][-1]
 +
                    ) and order.index(relother[4][-1]) < order.index(rel[4][-1]):
 +
                        rel = relother
 +
                        continue
 
                 dist_orig = len(rel[2])
 
                 dist_orig = len(rel[2])
 
                 dist_other = len(rel[4])
 
                 dist_other = len(rel[4])
                if rel[2] and rel[2][-1] == self.REL_SIBLING:
+
                 birth = self.only_birth(rel[2]) and self.only_birth(rel[4])
                    rel2 = rel2[:-1] + self.REL_FAM_BIRTH
 
                    dist_other += 1
 
                    rel4 = rel4 + self.REL_FAM_BIRTH
 
                    rel1 = None
 
                 birth = self.only_birth(rel2) and self.only_birth(rel4)
 
 
                 if dist_orig == dist_other == 1:
 
                 if dist_orig == dist_other == 1:
 
                     rel_str = self.get_sibling_relationship_string(
 
                     rel_str = self.get_sibling_relationship_string(
Line 1,544: Line 1,470:
 
                         orig_person.get_gender(),
 
                         orig_person.get_gender(),
 
                         other_person.get_gender(),
 
                         other_person.get_gender(),
                         rel2,
+
                         rel[2],
                         rel4,
+
                         rel[4],
 
                         only_birth=birth,
 
                         only_birth=birth,
 
                         in_law_a=False,
 
                         in_law_a=False,
 
                         in_law_b=False,
 
                         in_law_b=False,
 
                     )
 
                     )
                 if rel_str not in relstrings:
+
            if extra_info:
                    relstrings.append(rel_str)
+
                return (rel_str, dist_orig, dist_other)
                     if rel1:
+
            else:
                         commons[rel_str] = rel1
+
                 return rel_str
 +
 
 +
        def get_all_relationships(self, db, orig_person, other_person):
 +
            """
 +
            Return a tuple, of which the first entry is a list with all
 +
            relationships in text, and the second a list of lists of all common
 +
            ancestors that have that text as relationship
 +
            """
 +
            relstrings = []
 +
            commons = {}
 +
            if orig_person is None:
 +
                return ([], [])
 +
 
 +
            if orig_person.get_handle() == other_person.get_handle():
 +
                return ([], [])
 +
 
 +
            is_spouse = self.is_spouse(db, orig_person, other_person)
 +
            if is_spouse:
 +
                relstrings.append(is_spouse)
 +
                commons[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:
 +
                data = self.collapse_relations(data)
 +
                for rel in data:
 +
                    rel2 = rel[2]
 +
                    rel4 = rel[4]
 +
                    rel1 = rel[1]
 +
                    dist_orig = len(rel[2])
 +
                    dist_other = len(rel[4])
 +
                     if rel[2] and rel[2][-1] == self.REL_SIBLING:
 +
                         rel2 = rel2[:-1] + self.REL_FAM_BIRTH
 +
                        dist_other += 1
 +
                        rel4 = rel4 + self.REL_FAM_BIRTH
 +
                        rel1 = None
 +
                    birth = self.only_birth(rel2) and self.only_birth(rel4)
 +
                    if dist_orig == dist_other == 1:
 +
                        rel_str = self.get_sibling_relationship_string(
 +
                            self.get_sibling_type(db, orig_person, other_person),
 +
                            orig_person.get_gender(),
 +
                            other_person.get_gender(),
 +
                        )
 
                     else:
 
                     else:
                         # unknown parent eg
+
                         rel_str = self.get_single_relationship_string(
                        commons[rel_str] = []
+
                            dist_orig,
                else:
+
                            dist_other,
                    if rel1:
+
                            orig_person.get_gender(),
                        commons[rel_str].extend(rel1)
+
                            other_person.get_gender(),
        # construct the return tupply, relstrings is ordered on rank automatic
+
                            rel2,
        common_list = []
+
                            rel4,
        for rel_str in relstrings:
+
                            only_birth=birth,
            common_list.append(commons[rel_str])
+
                            in_law_a=False,
        return (relstrings, common_list)
+
                            in_law_b=False,
 +
                        )
 +
                    if rel_str not in relstrings:
 +
                        relstrings.append(rel_str)
 +
                        if rel1:
 +
                            commons[rel_str] = rel1
 +
                        else:
 +
                            # unknown parent eg
 +
                            commons[rel_str] = []
 +
                    else:
 +
                        if rel1:
 +
                            commons[rel_str].extend(rel1)
 +
            # construct the return tupply, relstrings is ordered on rank automatic
 +
            common_list = []
 +
            for rel_str in relstrings:
 +
                common_list.append(commons[rel_str])
 +
            return (relstrings, common_list)
  
    def get_plural_relationship_string(
+
        def get_plural_relationship_string(
        self,
+
            self,
        Ga,
+
            Ga,
        Gb,
+
            Gb,
        reltocommon_a="",
+
            reltocommon_a="",
        reltocommon_b="",
+
            reltocommon_b="",
        only_birth=True,
+
            only_birth=True,
        in_law_a=False,
+
            in_law_a=False,
        in_law_b=False,
+
            in_law_b=False,
    ):
+
        ):
        """
+
            """
        Provide a string that describes the relationsip between a person, and
+
            Provide a string that describes the relationsip between a person, and
        a group of people with the same relationship. E.g. "grandparents" or
+
            a group of people with the same relationship. E.g. "grandparents" or
        "children".
+
            "children".
  
        Ga and Gb can be used to mathematically calculate the relationship.
+
            Ga and Gb can be used to mathematically calculate the relationship.
  
        .. seealso::
+
            .. seealso::
            http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
+
                http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
  
        :param Ga: The number of generations between the main person and the
+
            :param Ga: The number of generations between the main person and the
                  common ancestor.
+
                      common ancestor.
        :type Ga: int
+
            :type Ga: int
        :param Gb: The number of generations between the group of people and the
+
            :param Gb: The number of generations between the group of people and the
                  common ancestor
+
                      common ancestor
        :type Gb: int
+
            :type Gb: int
        :param reltocommon_a: relation path to common ancestor or common
+
            :param reltocommon_a: relation path to common ancestor or common
                              Family for person a.
+
                                  Family for person a.
                              Note that length = Ga
+
                                  Note that length = Ga
        :type reltocommon_a: str
+
            :type reltocommon_a: str
        :param reltocommon_b: relation path to common ancestor or common
+
            :param reltocommon_b: relation path to common ancestor or common
                              Family for person b.
+
                                  Family for person b.
                              Note that length = Gb
+
                                  Note that length = Gb
        :type reltocommon_b: str
+
            :type reltocommon_b: str
        :param only_birth: True if relation between a and b is by birth only
+
            :param only_birth: True if relation between a and b is by birth only
                          False otherwise
+
                              False otherwise
        :type only_birth: bool
+
            :type only_birth: bool
        :param in_law_a: True if path to common ancestors is via the partner
+
            :param in_law_a: True if path to common ancestors is via the partner
                        of person a
+
                            of person a
        :type in_law_a: bool
+
            :type in_law_a: bool
        :param in_law_b: True if path to common ancestors is via the partner
+
            :param in_law_b: True if path to common ancestors is via the partner
                        of person b
+
                            of person b
        :type in_law_b: bool
+
            :type in_law_b: bool
        :returns: A string describing the relationship between the person and
+
            :returns: A string describing the relationship between the person and
                  the group.
+
                      the group.
        :rtype: str
+
            :rtype: str
        """
+
            """
        rel_str = "קרובי־משפחה רחוקים"
+
            rel_str = "קרובי־משפחה רחוקים"
        if Ga == 0:
+
            if Ga == 0:
            # These are descendants
+
                # These are descendants
            if Gb < len(_CHILDREN_LEVEL):
+
                if Gb < len(_CHILDREN_LEVEL):
                rel_str = _CHILDREN_LEVEL[Gb]
+
                    rel_str = _CHILDREN_LEVEL[Gb]
            else:
+
                else:
                rel_str = "צאצאים רחוקים"
+
                    rel_str = "צאצאים רחוקים"
        elif Gb == 0:
+
            elif Gb == 0:
            # These are parents/grand parents
+
                # These are parents/grand parents
            if Ga < len(_PARENTS_LEVEL):
+
                if Ga < len(_PARENTS_LEVEL):
                rel_str = _PARENTS_LEVEL[Ga]
+
                    rel_str = _PARENTS_LEVEL[Ga]
            else:
+
                else:
                rel_str = "אבות־קדמונים רחוקים"
+
                    rel_str = "אבות־קדמונים רחוקים"
        elif Gb == 1:
+
            elif Gb == 1:
            # These are siblings/aunts/uncles
+
                # These are siblings/aunts/uncles
            if Ga < len(_SIBLINGS_LEVEL):
+
                if Ga < len(_SIBLINGS_LEVEL):
                rel_str = _SIBLINGS_LEVEL[Ga]
+
                    rel_str = _SIBLINGS_LEVEL[Ga]
            else:
+
                else:
                rel_str = "דודים/דודות רחוקים"
+
                    rel_str = "דודים/דודות רחוקים"
        elif Ga == 1:
+
            elif Ga == 1:
            # These are nieces/nephews
+
                # These are nieces/nephews
            if Gb < len(_NEPHEWS_NIECES_LEVEL):
+
                if Gb < len(_NEPHEWS_NIECES_LEVEL):
                rel_str = _NEPHEWS_NIECES_LEVEL[Gb]
+
                    rel_str = _NEPHEWS_NIECES_LEVEL[Gb]
            else:
+
                else:
                rel_str = "אחיינים/אחייניות רחוקים"
+
                    rel_str = "אחיינים/אחייניות רחוקים"
        elif Ga > 1 and Ga == Gb:
+
            elif Ga > 1 and Ga == Gb:
            # These are cousins in the same generation
+
                # These are cousins in the same generation
            if Ga <= len(_LEVEL_NAME):
+
                if Ga <= len(_LEVEL_NAME):
                rel_str = "בני דודים %s  " % _LEVEL_NAME[Ga - 1]
+
                    rel_str = "בני דודים %s  " % _LEVEL_NAME[Ga - 1]
            else:
+
                else:
                rel_str = "בני דודים רחוקים"
+
                    rel_str = "בני דודים רחוקים"
        elif Ga > 1 and Ga > Gb:
+
            elif Ga > 1 and Ga > Gb:
            # These are cousins in different generations with the second person
+
                # These are cousins in different generations with the second person
            # being in a higher generation from the common ancestor than the
+
                # being in a higher generation from the common ancestor than the
            # first person.
+
                # first person.
            if Gb <= len(_LEVEL_NAME) and (Ga - Gb) < len(_REMOVED_LEVEL):
+
                if Gb <= len(_LEVEL_NAME) and (Ga - Gb) < len(_REMOVED_LEVEL):
                rel_str = " %s %s (עולה)" % (
+
                    rel_str = " %s %s (עולה)" % (
                    _LEVEL_NAME[Gb - 1],
+
                        _LEVEL_NAME[Gb - 1],
                    _REMOVED_LEVEL[Ga - Gb],
+
                        _REMOVED_LEVEL[Ga - Gb],
                )
+
                    )
            else:
+
                else:
                rel_str = "בני דודים רחוקים"
+
                    rel_str = "בני דודים רחוקים"
        elif Gb > 1 and Gb > Ga:
+
            elif Gb > 1 and Gb > Ga:
            # These are cousins in different generations with the second person
+
                # These are cousins in different generations with the second person
            # being in a lower generation from the common ancestor than the
+
                # being in a lower generation from the common ancestor than the
            # first person.
+
                # first person.
            if Ga <= len(_LEVEL_NAME) and (Gb - Ga) < len(_REMOVED_LEVEL):
+
                if Ga <= len(_LEVEL_NAME) and (Gb - Ga) < len(_REMOVED_LEVEL):
                rel_str = " בני דודים%s %s (יורד)" % (
+
                    rel_str = " בני דודים%s %s (יורד)" % (
                    _LEVEL_NAME[Ga - 1],
+
                        _LEVEL_NAME[Ga - 1],
                    _REMOVED_LEVEL[Gb - Ga],
+
                        _REMOVED_LEVEL[Gb - Ga],
                )
+
                    )
            else:
+
                else:
                rel_str = "בני דודים רחוקים"
+
                    rel_str = "בני דודים רחוקים"
  
        if in_law_b is True:
+
            if in_law_b is True:
            rel_str = "זוג של %s" % rel_str
+
                rel_str = "זוג של %s" % rel_str
  
        return rel_str
+
            return rel_str
  
    def get_single_relationship_string(
+
        def get_single_relationship_string(
        self,
+
            self,
        Ga,
+
            Ga,
        Gb,
+
            Gb,
        gender_a,
+
            gender_a,
        gender_b,
+
            gender_b,
        reltocommon_a,
+
            reltocommon_a,
        reltocommon_b,
+
            reltocommon_b,
        only_birth=True,
+
            only_birth=True,
        in_law_a=False,
+
            in_law_a=False,
        in_law_b=False,
+
            in_law_b=False,
    ):
+
        ):
        """
+
            """
        Provide a string that describes the relationsip between a person, and
+
            Provide a string that describes the relationsip between a person, and
        another person. E.g. "grandparent" or "child".
+
            another person. E.g. "grandparent" or "child".
  
        To be used as: 'person b is the grandparent of a', this will be in
+
            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'
+
            translation string:  'person b is the %(relation)s of a'
  
        Note that languages with gender should add 'the' inside the
+
            Note that languages with gender should add 'the' inside the
        translation, so eg in french:  'person b est %(relation)s de a'
+
            translation, so eg in french:  'person b est %(relation)s de a'
        where relation will be here: le grandparent
+
            where relation will be here: le grandparent
  
        Ga and Gb can be used to mathematically calculate the relationship.
+
            Ga and Gb can be used to mathematically calculate the relationship.
  
        .. seealso::
+
            .. seealso::
            http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
+
                http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
  
        Some languages need to know the specific path to the common ancestor.
+
            Some languages need to know the specific path to the common ancestor.
        Those languages should use reltocommon_a and reltocommon_b which is
+
            Those languages should use reltocommon_a and reltocommon_b which is
        a string like 'mfmf'.
+
            a string like 'mfmf'.
  
        The possible string codes are:
+
            The possible string codes are:
  
        =======================  ===========================================
+
            =======================  ===========================================
        Code                    Description
+
            Code                    Description
        =======================  ===========================================
+
            =======================  ===========================================
        REL_MOTHER              # going up to mother
+
            REL_MOTHER              # going up to mother
        REL_FATHER              # going up to father
+
            REL_FATHER              # going up to father
        REL_MOTHER_NOTBIRTH      # going up to mother, not birth relation
+
            REL_MOTHER_NOTBIRTH      # going up to mother, not birth relation
        REL_FATHER_NOTBIRTH      # going up to father, 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_BIRTH            # going up to family (mother and father)
        REL_FAM_NONBIRTH        # going up to family, not birth relation
+
            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_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
+
            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.
+
            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'
+
            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
+
            is True, if it starts with the inlaw of person b, then 'in_law_b' is
        True.
+
            True.
  
        Also REL_SIBLING (# going sideways to sibling (no parents)) is not
+
            Also REL_SIBLING (# going sideways to sibling (no parents)) is not
        passed to this routine. The collapse_relations changes this to a
+
            passed to this routine. The collapse_relations changes this to a
        family relation.
+
            family relation.
  
        Hence, calling routines should always strip REL_SIBLING and
+
            Hence, calling routines should always strip REL_SIBLING and
        REL_FAM_INLAW_PREFIX before calling get_single_relationship_string()
+
            REL_FAM_INLAW_PREFIX before calling get_single_relationship_string()
        Note that only_birth=False, means that in the reltocommon one of the
+
            Note that only_birth=False, means that in the reltocommon one of the
        NOTBIRTH specifiers is present.
+
            NOTBIRTH specifiers is present.
  
        The REL_FAM identifiers mean that the relation is not via a common
+
            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
+
            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
+
            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
+
            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.
+
            set to False. The only_birth() method is normally used for this.
  
        :param Ga: The number of generations between the main person and the
+
            :param Ga: The number of generations between the main person and the
                  common ancestor.
+
                      common ancestor.
        :type Ga: int
+
            :type Ga: int
        :param Gb: The number of generations between the other person and the
+
            :param Gb: The number of generations between the other person and the
                  common ancestor.
+
                      common ancestor.
        :type Gb: int
+
            :type Gb: int
        :param gender_a: gender of person a
+
            :param gender_a: gender of person a
        :type gender_a: int gender
+
            :type gender_a: int gender
        :param gender_b: gender of person b
+
            :param gender_b: gender of person b
        :type gender_b: int gender
+
            :type gender_b: int gender
        :param reltocommon_a: relation path to common ancestor or common
+
            :param reltocommon_a: relation path to common ancestor or common
                              Family for person a.
+
                                  Family for person a.
                              Note that length = Ga
+
                                  Note that length = Ga
        :type reltocommon_a: str
+
            :type reltocommon_a: str
        :param reltocommon_b: relation path to common ancestor or common
+
            :param reltocommon_b: relation path to common ancestor or common
                              Family for person b.
+
                                  Family for person b.
                              Note that length = Gb
+
                                  Note that length = Gb
        :type reltocommon_b: str
+
            :type reltocommon_b: str
        :param in_law_a:  True if path to common ancestors is via the partner
+
            :param in_law_a:  True if path to common ancestors is via the partner
                          of person a
+
                              of person a
        :type in_law_a: bool
+
            :type in_law_a: bool
        :param in_law_b: True if path to common ancestors is via the partner
+
            :param in_law_b: True if path to common ancestors is via the partner
                        of person b
+
                            of person b
        :type in_law_b: bool
+
            :type in_law_b: bool
        :param only_birth: True if relation between a and b is by birth only
+
            :param only_birth: True if relation between a and b is by birth only
                          False otherwise
+
                              False otherwise
        :type only_birth: bool
+
            :type only_birth: bool
        :returns: A string describing the relationship between the two people
+
            :returns: A string describing the relationship between the two people
        :rtype: str
+
            :rtype: str
  
        .. note:: 1. the self.REL_SIBLING should not be passed to this routine,
+
            .. note:: 1. the self.REL_SIBLING should not be passed to this routine,
                    so we should not check on it. All other self.
+
                        so we should not check on it. All other self.
                  2. for better determination of siblings, use if Ga=1=Gb
+
                      2. for better determination of siblings, use if Ga=1=Gb
                    get_sibling_relationship_string
+
                        get_sibling_relationship_string
        """
+
            """
        if only_birth:
+
            if only_birth:
            step = ""
+
                step = ""
        else:
+
            else:
            if gender_b == MALE:
+
                if gender_b == MALE:
                step = self.STEP
+
                    step = self.STEP
            elif gender_b == FEMALE:
+
                elif gender_b == FEMALE:
                 step = self.STEP_F
+
                    step = self.STEP_F
 +
                 else:
 +
                    step = self.STEP # Change this as appropriate for other and unknown gender
 +
 
 +
            if in_law_a or in_law_b:
 +
                inlaw = self.INLAW
 
             else:
 
             else:
                 step = self.STEP # Change this as appropriate for other and unknown gender
+
                 inlaw = ""
 +
 
 +
            rel_str = "קרוב־משפחה רחוק %s%s" % (step, inlaw)
 +
 
 +
            if Ga == 0:
 +
                # b is descendant of a
 +
                if Gb == 0:
 +
                    rel_str = "האדם"
 +
                elif gender_b == MALE:
 +
                    rel_str = self._get_son(Gb, step, inlaw)
 +
                elif gender_b == FEMALE:
 +
                    rel_str = self._get_daughter(Gb, step, inlaw)
 +
                else:
 +
                    rel_str = self._get_child_unknown(Gb, step, inlaw)
 +
            elif Gb == 0:
 +
                # b is parents/grand parent of a
 +
                if gender_b == MALE:
 +
                    rel_str = self._get_father(Ga, step, inlaw)
 +
                elif gender_b == 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 == MALE:
 +
                    rel_str = self._get_uncle(Ga, step, inlaw)
 +
                elif gender_b == 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 == MALE:
 +
                    rel_str = self._get_nephew(Gb - 1, step, inlaw)
 +
                elif gender_b == FEMALE:
 +
                    rel_str = self._get_niece(Gb - 1, step, inlaw)
 +
                elif Gb < len(_NIECE_LEVEL) and Gb < len(_NEPHEW_LEVEL):
 +
                    rel_str = "%sאו %s" % (
 +
                        self._get_nephew(Gb - 1, step, inlaw),
 +
                        self._get_niece(Gb - 1, step, inlaw),
 +
                    )
 +
                else:
 +
                    rel_str = "אחיין/אחיינית רחוקים %s %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=" (עולה)", 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=" (יורד)", step=step, inlaw=inlaw
 +
                )
 +
            return rel_str
  
         if in_law_a or in_law_b:
+
         def get_sibling_relationship_string(
             inlaw = self.INLAW
+
            self, sib_type, gender_a, gender_b, in_law_a=False, in_law_b=False
        else:
+
        ):
             inlaw = ""
+
             """
 +
            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.
  
        rel_str = "קרוב־משפחה רחוק %s%s" % (step, inlaw)
+
            .. warning:: 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_MOTHER or sib_type == self.HALF_SIB_FATHER:
 +
                typestr = self.HALF
 +
            elif sib_type == self.STEP_SIB:
 +
                typestr = self.STEP
  
        if Ga == 0:
+
             if in_law_a or in_law_b:
            # b is descendant of a
+
                 inlaw = self.INLAW
             if Gb == 0:
 
                rel_str = "האדם"
 
            elif gender_b == MALE:
 
                 rel_str = self._get_son(Gb, step, inlaw)
 
            elif gender_b == FEMALE:
 
                rel_str = self._get_daughter(Gb, step, inlaw)
 
 
             else:
 
             else:
                 rel_str = self._get_child_unknown(Gb, step, inlaw)
+
                 inlaw = ""
        elif Gb == 0:
+
 
            # b is parents/grand parent of a
 
 
             if gender_b == MALE:
 
             if gender_b == MALE:
                 rel_str = self._get_father(Ga, step, inlaw)
+
                 rel_str = self._get_uncle(1, typestr, inlaw)
 
             elif gender_b == FEMALE:
 
             elif gender_b == FEMALE:
                 rel_str = self._get_mother(Ga, step, inlaw)
+
                 rel_str = self._get_aunt(1, typestr, inlaw)
 
             else:
 
             else:
                 rel_str = self._get_parent_unknown(Ga, step, inlaw)
+
                 rel_str = self._get_sibling(1, typestr, inlaw)
        elif Gb == 1:
+
             return rel_str
             # b is sibling/aunt/uncle of a
+
 
            if gender_b == MALE:
+
        def get_partner_relationship_string(self, spouse_type, gender_a, gender_b):
                rel_str = self._get_uncle(Ga, step, inlaw)
+
             """
            elif gender_b == FEMALE:
+
             Determine the string giving the relation between two partners of
                rel_str = self._get_aunt(Ga, step, inlaw)
+
             type spouse_type.
            else:
+
             Eg: b is the spouse of a
                rel_str = self._get_sibling(Ga, step, inlaw)
+
             Here 'spouse' is the string we need to determine
        elif Ga == 1:
 
            # b is niece/nephew of a
 
            if gender_b == MALE:
 
                rel_str = self._get_nephew(Gb - 1, step, inlaw)
 
            elif gender_b == FEMALE:
 
                rel_str = self._get_niece(Gb - 1, step, inlaw)
 
            elif Gb < len(_NIECE_LEVEL) and Gb < len(_NEPHEW_LEVEL):
 
                rel_str = "%sאו %s" % (
 
                    self._get_nephew(Gb - 1, step, inlaw),
 
                    self._get_niece(Gb - 1, step, inlaw),
 
                )
 
             else:
 
                rel_str = "אחיין/אחיינית רחוקים %s %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=" (עולה)", 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=" (יורד)", step=step, inlaw=inlaw
 
            )
 
        return rel_str
 
  
    def get_sibling_relationship_string(
+
            .. warning:: DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR
        self, sib_type, gender_a, gender_b, in_law_a=False, in_law_b=False
+
                        LANGUAGE, AS GETTEXT IS ALREADY USED !
    ):
+
            """
        """
+
            # english only needs gender of b, we don't guess if unknown like in old
        Determine the string giving the relation between two siblings of
+
            # procedure as that is stupid in present day cases!
        type sib_type.
+
            gender = gender_b
        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.
 
  
        .. warning:: DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR
+
            if not spouse_type:
                    LANGUAGE, AND SAME METHODS EXIST (get_uncle, get_aunt,
+
                return ""
                    get_sibling)
 
        """
 
        if sib_type == self.NORM_SIB or sib_type == self.UNKNOWN_SIB:
 
            typestr = ""
 
        elif sib_type == self.HALF_SIB_MOTHER or sib_type == self.HALF_SIB_FATHER:
 
            typestr = self.HALF
 
        elif sib_type == self.STEP_SIB:
 
            typestr = self.STEP
 
  
        if in_law_a or in_law_b:
+
            trans_text = _
             inlaw = self.INLAW
+
            # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh)
        else:
+
             if hasattr(self, "_locale") and self._locale != glocale:
            inlaw = ""
+
                trans_text = self._locale.translation.sgettext
  
        if gender_b == MALE:
+
            if spouse_type == self.PARTNER_MARRIED:
             rel_str = self._get_uncle(1, typestr, inlaw)
+
                if gender == MALE:
        elif gender_b == FEMALE:
+
                    return trans_text("בעל")
             rel_str = self._get_aunt(1, typestr, inlaw)
+
                elif gender == FEMALE:
        else:
+
                    return trans_text("רעיה")
             rel_str = self._get_sibling(1, typestr, inlaw)
+
                else:
        return rel_str
+
                    return trans_text("זוג", "מגדר לא ידוע")
 +
            elif spouse_type == self.PARTNER_EX_MARRIED:
 +
                if gender == MALE:
 +
                    return trans_text("בעל לשעבר")
 +
                elif gender == FEMALE:
 +
                    return trans_text("רעיה לשעבר")
 +
                else:
 +
                    return trans_text("זוג לשעבר", "מגדר לא ידוע")
 +
            elif spouse_type == self.PARTNER_UNMARRIED:
 +
                if gender == MALE:
 +
                    return trans_text("שותף", "זכר, לא נשוי")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה", "נקבה, לא נשואה")
 +
                else:
 +
                    return trans_text("שותף", "מגדר לא ידוע, לא נשוי")
 +
            elif spouse_type == self.PARTNER_EX_UNMARRIED:
 +
                if gender == MALE:
 +
                    return trans_text("שותף לשעבר", "זכר, לא נשוי")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה לשעבר", "נקבה, לא נשואה")
 +
                else:
 +
                    return trans_text("שותף לשעבר", "מגדר לא ידוע, לא נשוי")
 +
            elif spouse_type == self.PARTNER_CIVIL_UNION:
 +
                if gender == MALE:
 +
                    return trans_text("שותף", "זכר, נישואים אזרחיים")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה", "נקבה, נישואים אזרחיים")
 +
                else:
 +
                    return trans_text("שותף", "מגדר לא ידוע, נישואים אזרחיים")
 +
             elif spouse_type == self.PARTNER_EX_CIVIL_UNION:
 +
                if gender == MALE:
 +
                    return trans_text("שותף קודם", "זכר, נישואים אזרחיים")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה קודמת", "נקבה, נישואים אזרחיים")
 +
                else:
 +
                    return trans_text("שותף קודם", "מגדר לא ידוע ,נישואים אזרחיים")
 +
             elif spouse_type == self.PARTNER_UNKNOWN_REL:
 +
                if gender == MALE:
 +
                    return trans_text("שותף", "זכר, טיב יחסים לא ידוע")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה", "נקבה, טיב יחסים לא ידוע")
 +
                else:
 +
                    return trans_text("שותף", "מגדר לא ידוע, טיב יחסים לא ידוע")
 +
             else:
 +
                # here we have spouse_type == self.PARTNER_EX_UNKNOWN_REL
 +
                #  or other not catched types
 +
                if gender == MALE:
 +
                    return trans_text("שותף קודם", "זכר, טיב יחסים לא ידוע")
 +
                elif gender == FEMALE:
 +
                    return trans_text("שותפה קודמת", "נקבה, טיב יחסים לא ידוע")
 +
                else:
 +
                    return trans_text("שותף קודם", "מגדר לא ידוע ,טיב יחסים לא ידוע")
  
    def get_partner_relationship_string(self, spouse_type, gender_a, gender_b):
+
        def connect_db_signals(self, dbstate):
        """
+
            """
        Determine the string giving the relation between two partners of
+
            We can save work by storing a map, however, if database changes
        type spouse_type.
+
            this map must be regenerated.
        Eg: b is the spouse of a
+
            Before close, the calling app must call disconnect_db_signals
        Here 'spouse' is the string we need to determine
+
            """
 +
            if self.__db_connected:
 +
                return
 +
            assert len(self.signal_keys) == 0
 +
            self.state_signal_key = dbstate.connect(
 +
                "database-changed", self._dbchange_callback
 +
            )
 +
            self.__connect_db_signals(dbstate.db)
  
         .. warning:: DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR
+
         def __connect_db_signals(self, db):
                    LANGUAGE, AS GETTEXT IS ALREADY USED !
+
            signals = [
        """
+
                "person-add",
        # english only needs gender of b, we don't guess if unknown like in old
+
                "person-update",
        # procedure as that is stupid in present day cases!
+
                "person-delete",
        gender = gender_b
+
                "person-rebuild",
 +
                "family-add",
 +
                "family-update",
 +
                "family-delete",
 +
                "family-rebuild",
 +
                "database-changed",
 +
            ]
 +
            for name in signals:
 +
                self.signal_keys.append(db.connect(name, self._datachange_callback))
 +
            self.storemap = True
 +
            self.__db_connected = True
  
         if not spouse_type:
+
         def disconnect_db_signals(self, dbstate):
             return ""
+
             """
 +
            Method to disconnect to all signals the relationship calculator is
 +
            subscribed
 +
            """
 +
            dbstate.disconnect(self.state_signal_key)
 +
            list(map(dbstate.db.disconnect, self.signal_keys))
 +
            self.storemap = False
 +
            self.stored_map = None
  
         trans_text = _
+
         def _dbchange_callback(self, db):
        # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh)
+
            """
        if hasattr(self, "_locale") and self._locale != glocale:
+
            When database changes, the map can no longer be used.
             trans_text = self._locale.translation.sgettext
+
            Connects must be remade
 +
            """
 +
            self.dirtymap = True
 +
            # signals are disconnected on close of old database, connect to new
 +
             self.__connect_db_signals(db)
  
         if spouse_type == self.PARTNER_MARRIED:
+
         def _datachange_callback(self, handle_list=None):
            if gender == MALE:
+
             """
                return trans_text("בעל")
+
             When data in database changes, the map can no  longer be used.
            elif gender == FEMALE:
+
             As the map might be in use or might be generated at the moment,
                return trans_text("רעיה")
+
             this method sets a dirty flag. Before reusing the map, this flag
            else:
+
             will be checked
                return trans_text("זוג", "מגדר לא ידוע")
+
             """
        elif spouse_type == self.PARTNER_EX_MARRIED:
+
             self.dirtymap = True
            if gender == MALE:
 
                return trans_text("בעל לשעבר")
 
            elif gender == FEMALE:
 
                return trans_text("רעיה לשעבר")
 
            else:
 
                return trans_text("זוג לשעבר", "מגדר לא ידוע")
 
        elif spouse_type == self.PARTNER_UNMARRIED:
 
            if gender == MALE:
 
                return trans_text("שותף", "זכר, לא נשוי")
 
            elif gender == FEMALE:
 
                return trans_text("שותפה", "נקבה, לא נשואה")
 
            else:
 
                return trans_text("שותף", "מגדר לא ידוע, לא נשוי")
 
        elif spouse_type == self.PARTNER_EX_UNMARRIED:
 
            if gender == MALE:
 
                return trans_text("שותף לשעבר", "זכר, לא נשוי")
 
             elif gender == FEMALE:
 
                return trans_text("שותפה לשעבר", "נקבה, לא נשואה")
 
             else:
 
                return trans_text("שותף לשעבר", "מגדר לא ידוע, לא נשוי")
 
        elif spouse_type == self.PARTNER_CIVIL_UNION:
 
             if gender == MALE:
 
                return trans_text("שותף", "זכר, נישואים אזרחיים")
 
            elif gender == FEMALE:
 
                return trans_text("שותפה", "נקבה, נישואים אזרחיים")
 
            else:
 
                return trans_text("שותף", "מגדר לא ידוע, נישואים אזרחיים")
 
        elif spouse_type == self.PARTNER_EX_CIVIL_UNION:
 
            if gender == MALE:
 
                return trans_text("שותף קודם", "זכר, נישואים אזרחיים")
 
            elif gender == FEMALE:
 
                return trans_text("שותפה קודמת", "נקבה, נישואים אזרחיים")
 
             else:
 
                return trans_text("שותף קודם", "מגדר לא ידוע ,נישואים אזרחיים")
 
        elif spouse_type == self.PARTNER_UNKNOWN_REL:
 
            if gender == MALE:
 
                return trans_text("שותף", "זכר, טיב יחסים לא ידוע")
 
             elif gender == FEMALE:
 
                return trans_text("שותפה", "נקבה, טיב יחסים לא ידוע")
 
             else:
 
                return trans_text("שותף", "מגדר לא ידוע, טיב יחסים לא ידוע")
 
        else:
 
             # here we have spouse_type == self.PARTNER_EX_UNKNOWN_REL
 
            #  or other not catched types
 
            if gender == MALE:
 
                return trans_text("שותף קודם", "זכר, טיב יחסים לא ידוע")
 
            elif gender == FEMALE:
 
                return trans_text("שותפה קודמת", "נקבה, טיב יחסים לא ידוע")
 
            else:
 
                return trans_text("שותף קודם", "מגדר לא ידוע ,טיב יחסים לא ידוע")
 
  
    def connect_db_signals(self, dbstate):
 
        """
 
        We can save work by storing a map, however, if database changes
 
        this map must be regenerated.
 
        Before close, the calling app must call disconnect_db_signals
 
        """
 
        if self.__db_connected:
 
            return
 
        assert len(self.signal_keys) == 0
 
        self.state_signal_key = dbstate.connect(
 
            "database-changed", self._dbchange_callback
 
        )
 
        self.__connect_db_signals(dbstate.db)
 
  
     def __connect_db_signals(self, db):
+
     # -------------------------------------------------------------------------
        signals = [
+
    #
            "person-add",
+
    # define the default relationshipcalculator
            "person-update",
+
    #
            "person-delete",
+
    # -------------------------------------------------------------------------
            "person-rebuild",
 
            "family-add",
 
            "family-update",
 
            "family-delete",
 
            "family-rebuild",
 
            "database-changed",
 
        ]
 
        for name in signals:
 
            self.signal_keys.append(db.connect(name, self._datachange_callback))
 
        self.storemap = True
 
        self.__db_connected = True
 
  
     def disconnect_db_signals(self, dbstate):
+
     __RELCALC_CLASS = None
        """
 
        Method to disconnect to all signals the relationship calculator is
 
        subscribed
 
        """
 
        dbstate.disconnect(self.state_signal_key)
 
        list(map(dbstate.db.disconnect, self.signal_keys))
 
        self.storemap = False
 
        self.stored_map = None
 
  
    def _dbchange_callback(self, db):
 
        """
 
        When database changes, the map can no longer be used.
 
        Connects must be remade
 
        """
 
        self.dirtymap = True
 
        # signals are disconnected on close of old database, connect to new
 
        self.__connect_db_signals(db)
 
  
     def _datachange_callback(self, handle_list=None):
+
     def get_relationship_calculator(reinit=False, clocale=glocale):
 
         """
 
         """
         When data in database changes, the map can no  longer be used.
+
         Return the relationship calculator for the current language.
        As the map might be in use or might be generated at the moment,
 
        this method sets a dirty flag. Before reusing the map, this flag
 
        will be checked
 
        """
 
        self.dirtymap = True
 
  
 +
        If clocale is passed in (a GrampsLocale) then that language will be used.
  
# -------------------------------------------------------------------------
+
        :param clocale: allow selection of the relationship language
#
+
        :type clocale: a GrampsLocale instance
# define the default relationshipcalculator
 
#
 
# -------------------------------------------------------------------------
 
  
__RELCALC_CLASS = None
+
        """
 +
        global __RELCALC_CLASS
  
 +
        if __RELCALC_CLASS is None or reinit:
 +
            lang = clocale.language[0]
 +
            __RELCALC_CLASS = RelationshipCalculator
 +
            # If lang not set default to English relationship calulator
 +
            # See if lang begins with en_, English_ or english_
 +
            # If so return standard relationship calculator.
 +
            if lang.startswith("en") or lang == "C":
 +
                return __RELCALC_CLASS()
 +
            # set correct non English relationship calculator based on lang
 +
            relation_translation_found = False
 +
            for plugin in PluginRegister.get_instance().relcalc_plugins():
 +
                if lang in plugin.lang_list:
 +
                    pmgr = BasePluginManager.get_instance()
 +
                    # the loaded module is put in variable mod
 +
                    mod = pmgr.load_plugin(plugin)
 +
                    if mod:
 +
                        __RELCALC_CLASS = getattr(mod, plugin.relcalcclass)
 +
                        relation_translation_found = True
 +
                        break
 +
            if not relation_translation_found and len(
 +
                PluginRegister.get_instance().relcalc_plugins()
 +
            ):
 +
                LOG.warning(
 +
                    _(
 +
                        "תרגומון קירבת משפחה לא זמין "
 +
                        "לקוד השפה '%s'. במקום זאת גרמפס יאתחל בשפה ה'אנגלית'."
 +
                    ),
 +
                    lang,
 +
                )
 +
        return __RELCALC_CLASS()
  
def get_relationship_calculator(reinit=False, clocale=glocale):
 
    """
 
    Return the relationship calculator for the current language.
 
  
    If clocale is passed in (a GrampsLocale) then that language will be used.
 
  
     :param clocale: allow selection of the relationship language
+
     if __name__ == "__main__":
    :type clocale: a GrampsLocale instance
+
        """
 +
        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
    global __RELCALC_CLASS
+
        """
 +
        from gramps.gen.relationship import test
 +
        REL_CALC = RelationshipCalculator()
 +
        test(REL_CALC, True)
 +
</pre>
 +
{{-}}
 +
<noinclude>
  
    if __RELCALC_CLASS is None or reinit:
+
==See also==
        lang = clocale.language[0]
+
* [[Handler List]]
        __RELCALC_CLASS = RelationshipCalculator
 
        # If lang not set default to English relationship calulator
 
        # See if lang begins with en_, English_ or english_
 
        # If so return standard relationship calculator.
 
        if lang.startswith("en") or lang == "C":
 
            return __RELCALC_CLASS()
 
        # set correct non English relationship calculator based on lang
 
        relation_translation_found = False
 
        for plugin in PluginRegister.get_instance().relcalc_plugins():
 
            if lang in plugin.lang_list:
 
                pmgr = BasePluginManager.get_instance()
 
                # the loaded module is put in variable mod
 
                mod = pmgr.load_plugin(plugin)
 
                if mod:
 
                    __RELCALC_CLASS = getattr(mod, plugin.relcalcclass)
 
                    relation_translation_found = True
 
                    break
 
        if not relation_translation_found and len(
 
            PluginRegister.get_instance().relcalc_plugins()
 
        ):
 
            LOG.warning(
 
                _(
 
                    "תרגומון קירבת משפחה לא זמין "
 
                    "לקוד השפה '%s'. במקום זאת גרמפס יאתחל בשפה ה'אנגלית'."
 
                ),
 
                lang,
 
            )
 
    return __RELCALC_CLASS()
 
 
 
 
 
 
 
if __name__ == "__main__":
 
    """
 
    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
+
[[Category:Translators/Categories]][[Category:Developers/General]]
    """
+
</noinclude>
    from gramps.gen.relationship import test
 
    REL_CALC = RelationshipCalculator()
 
    test(REL_CALC, True)
 

Latest revision as of 02:09, 23 September 2025

Gramps-notes.png
Hebrew Relationship Calculator - added to Gramps 5.2

    # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    #
    """
    Classes for relationships.
    """
    # -------------------------------------------------------------------------
    #
    # Python modules
    #
    # -------------------------------------------------------------------------
    import logging
    # -------------------------------------------------------------------------
    #
    # Gramps modules
    #
    # -------------------------------------------------------------------------
    from gramps.gen.lib import Person, ChildRefType, EventType, FamilyRelType
    import gramps.gen.relationship
    from gramps.gen.plug import PluginRegister, BasePluginManager
    from gramps.gen.const import GRAMPS_LOCALE as glocale

    _ = glocale.translation.sgettext

    MALE = Person.MALE
    FEMALE = Person.FEMALE
    UNKNOWN = Person.UNKNOWN

    LOG = logging.getLogger("gen.relationship")
    LOG.addHandler(logging.StreamHandler())

    # -------------------------------------------------------------------------
    #
    #
    #
    # -------------------------------------------------------------------------

    _LEVEL_NAME = [
        "",
        "מרמה ראשונה",
        "מרמה שניה",
        "מרמה שלישית",
        "מרמה רביעית",
        "מרמה חמישית",
        "מרמה שישית",
        "מרמה שביעית",
        "מרמה שמינית",
        "מרמה תשיעית",
        "מרמה עשירית",
        "מרמה אחת עשרה",
    ]

    _REMOVED_LEVEL = [
        "",
         "מדרגה שניה",
         "פעמיים מדרגה שניה",
         "שלוש פעמים מדרגה שניה",
         "ארבע פעמים מדרגה שניה",
         "חמש פעמים מדרגה שניה",
         "שש פעמים מדרגה שניה",
         "שבע פעמים מדרגה שניה",
         "שמונה פעמים מדרגה שניה",
         "תשע פעמים מדרגה שניה",
         "עשר פעמים מדרגה שניה",
         "אחת עשרה פעמים מדרגה שניה",
    ]

    _PARENTS_LEVEL = [
        "",
        "הורים",
        "סבים",
        "סבות",
        "סב גדול",
        "סבה גדולה",
        "סב וסבה גדולים",
        "סב וסבה מרמה שלישית",
        "סב וסבה מרמה רביעית",
        "סב וסבה מרמה חמישית",
        "סב וסבה מרמה שישית",
        "סב וסבה מרמה שביעית",
        "סב וסבה מרמה שמינית",
        "סב וסבה מרמה תשיעית",
        "סב וסבה מרמה עשירית",
        "סב וסבה מרמה אחת עשרה",
    ]

    _FATHER_LEVEL = [
        "",
        "סב %(step)s %(inlaw)s",
        "סב גדול %(step)s %(inlaw)s",
        "סב מרמה שלישית %(step)s %(inlaw)s",
        "סב מרמה רביעית %(step)s %(inlaw)s",
        "סב מרמה חמישית %(step)s %(inlaw)s",
        "סב מרמה שישית %(step)s %(inlaw)s",
        "סב מרמה שביעית %(step)s %(inlaw)s",
        "סב מרמה שמינית %(step)s %(inlaw)s",
        "סב מרמה תשיעית %(step)s %(inlaw)s",
        "סב מרמה עשירית %(step)s %(inlaw)s",
        "סב מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _MOTHER_LEVEL = [
        "",
        "סבה %(step)s %(inlaw)s",
        "סבה גדולה %(step)s %(inlaw)s",
        "סבה מרמה שלישית %(step)s %(inlaw)s",
        "סבה מרמה רביעית %(step)s %(inlaw)s",
        "סבה מרמה חמישית %(step)s %(inlaw)s",
        "סבה מרמה שישית %(step)s %(inlaw)s",
        "סבה מרמה שביעית %(step)s %(inlaw)s",
        "סבה מרמה שמינית %(step)s %(inlaw)s",
        "סבה מרמה תשיעית %(step)s %(inlaw)s",
        "סבה מרמה עשירית %(step)s %(inlaw)s",
        "סבה מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _SON_LEVEL = [
        "",
        "בן %(step)s %(inlaw)s",
        "נכד %(step)s %(inlaw)s",
        "נין %(step)s %(inlaw)s",
        "חימש %(step)s %(inlaw)s",
        "נכד מרמה חמישית %(step)s %(inlaw)s",
        "נכד מרמה שישית %(step)s %(inlaw)s",
        "נכד מרמה שביעית %(step)s %(inlaw)s",
        "נכד מרמה שמינית %(step)s %(inlaw)s",
        "נכד מרמה תשיעית %(step)s %(inlaw)s",
        "נכד מרמה עשירית %(step)s %(inlaw)s",
        "נכד מרמה אחת עשרה %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
        "נכד רחוק %(step)s %(inlaw)s",
    ]

    _DAUGHTER_LEVEL = [
        "",
        "בת %(step)s %(inlaw)s",
        "נכדה %(step)s %(inlaw)s",
        "נינה %(step)s %(inlaw)s",
        "חימשה %(step)s %(inlaw)s",
        "נכדה מרמה חמישית %(step)s %(inlaw)s",
        "נכדה מרמה שישית %(step)s %(inlaw)s",
        "נכדה מרמה שביעית %(step)s %(inlaw)s",
        "נכדה מרמה שמינית %(step)s %(inlaw)s",
        "נכדה מרמה תשיעית %(step)s %(inlaw)s",
        "נכדה מרמה עשירית %(step)s %(inlaw)s",
        "נכדה מרמה אחת עשרה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
        "נכדה רחוקה %(step)s %(inlaw)s",
    ]

    _SISTER_LEVEL = [
        "",
        "אחות %(step)s %(inlaw)s",
        "דודה %(step)s %(inlaw)s",
        "דודה גדולה %(step)s %(inlaw)s",
        "דודה מרמה שלישית %(step)s %(inlaw)s",
        "דודה מרמה רביעית %(step)s %(inlaw)s",
        "דודה מרמה חמישית %(step)s %(inlaw)s",
        "דודה מרמה שישית %(step)s %(inlaw)s",
        "דודה מרמה שביעית %(step)s %(inlaw)s",
        "דודה מרמה שמינית %(step)s %(inlaw)s",
        "דודה מרמה תשיעיתית %(step)s %(inlaw)s",
        "דודה מרמה עשירית %(step)s %(inlaw)s",
        "דודה מרמה אחת עשרה%(step)s %(inlaw)s",
    ]

    _BROTHER_LEVEL = [
        "",
        "אח %(step)s %(inlaw)s",
        "דוד %(step)s %(inlaw)s",
        "דוד גדול %(step)s %(inlaw)s",
        "דוד מרמה שלישית %(step)s %(inlaw)s",
        "דוד מרמה רביעית %(step)s %(inlaw)s",
        "דוד מרמה חמישית %(step)s %(inlaw)s",
        "דוד מרמה שישית %(step)s %(inlaw)s",
        "דוד מרמה שביעית %(step)s %(inlaw)s",
        "דוד מרמה שמינית %(step)s %(inlaw)s",
        "דוד מרמה תשיעית %(step)s %(inlaw)s",
        "דוד מרמה עשירית %(step)s %(inlaw)s",
        "דוד מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _NEPHEW_LEVEL = [
        "",
        "אחיין %(step)s %(inlaw)s",
        "נכדן %(step)s %(inlaw)s",
        "אחיין גדול %(step)s %(inlaw)s",
        "אחיין מרמה שלישית %(step)s %(inlaw)s",
        "אחיין מרמה רביעית %(step)s %(inlaw)s",
        "אחיין מרמה חמישית %(step)s %(inlaw)s",
        "אחיין מרמה שישית %(step)s %(inlaw)s",
        "אחיין מרמה שביעית %(step)s %(inlaw)s",
        "אחיין מרמה שמינית %(step)s %(inlaw)s",
        "אחיין מרמה תשיעית %(step)s %(inlaw)s",
        "אחיין מרמה עשירית %(step)s %(inlaw)s",
        "אחיין מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _NIECE_LEVEL = [
        "",
        "אחיינית %(step)s %(inlaw)s",
        "נכדנית %(step)s %(inlaw)s",
        "אחיינית גדולה %(step)s %(inlaw)s",
        "אחיינית מרמה שלישית %(step)s %(inlaw)s",
        "אחיינית מרמה רביעית %(step)s %(inlaw)s",
        "אחיינית מרמה חמישית %(step)s %(inlaw)s",
        "אחיינית מרמה שישית %(step)s %(inlaw)s",
        "אחיינית מרמה שביעית %(step)s %(inlaw)s",
        "אחיינית מרמה שמינית %(step)s %(inlaw)s",
        "אחיינית מרמה תשיעית %(step)s %(inlaw)s",
        "אחיינית מרמה עשירית %(step)s %(inlaw)s",
        "אחיינית מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _CHILDREN_LEVEL = [
        "",
        "ילדים",
        "נכדים",
        "נינים",
        "חימשים",
        "נכדים מרמה חמישית",
        "נכדים מרמה שישית",
        "נכדים מרמה שביעית",
        "נכדים מרמה שמינית",
        "נכדים מרמה תשיעית",
        "נכדים מרמה עשירית",
        "נכדים מרמה אחת עשרה",
    ]

    _SIBLINGS_LEVEL = [
        "",
        "אחאים",
        "דוד/דודה",
        "דוד/דודה גדולים",
        "דוד/דודה מרמה שלישית",
        "דוד/דודה מרמה רביעית",
        "דוד/דודה מרמה חמישית",
        "דוד/דודה מרמה שישית",
        "דוד/דודה מרמה שביעית",
        "דוד/דודה מרמה שמינית",
        "דוד/דודה מרמה תשיעית",
        "דוד/דודה מרמה עשירית",
        "דוד/דודה מרמה אחת עשרה",
    ]

    _SIBLING_LEVEL = [
        "",
        "אחאים %(step)s %(inlaw)s",
        "דוד/דודה %(step)s %(inlaw)s",
        "דוד/דודה גדולים %(step)s %(inlaw)s",
        "דוד/דודה מרמה שלישית %(step)s %(inlaw)s",
        "דוד/דודה מרמה רביעית %(step)s %(inlaw)s",
        "דוד/דודה מרמה חמישית %(step)s %(inlaw)s",
        "דוד/דודה מרמה שישית %(step)s %(inlaw)s",
        "דוד/דודה מרמה שביעית %(step)s %(inlaw)s",
        "דוד/דודה מרמה שמינית %(step)s %(inlaw)s",
        "דוד/דודה מרמה תשיעית %(step)s %(inlaw)s",
        "דוד/דודה מרמה עשירית %(step)s %(inlaw)s",
        "דוד/דודה מרמה אחת עשרה %(step)s %(inlaw)s",
    ]

    _NEPHEWS_NIECES_LEVEL = [
        "",
        "אחאים",
        "אחיין/אחיינית",
        "נכדן/נכדנית",
        "אחיין/אחיינית גדולים",
        "אחיין/אחיינית שלישית",
        "אחיין/אחיינית רביעית",
        "אחיין/אחיינית חמישית",
        "אחיין/אחיינית מרמה שישית",
        "אחיין/אחיינית מרמה שביעית",
        "אחיין/אחיינית מרמה שמינית",
        "אחיין/אחיינית מרמה תשיעית",
        "אחיין/אחיינית מרמה עשירית",
        "אחיין/אחיינית מרמה אחת עשרה",
    ]

    # -------------------------------------------------------------------------
    #
    # RelationshipCalculator
    #
    # -------------------------------------------------------------------------
    class RelationshipCalculator:
        """
        The relationship calculator helps to determine the relationship between
        two people.
        """

        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  # same birth parents
        HALF_SIB_MOTHER = 1  # same mother, father known to be different
        HALF_SIB_FATHER = 2  # same father, mother known to be different
        STEP_SIB = 3  # birth parents known to be different
        UNKNOWN_SIB = 4  # insufficient data to draw conclusion

        # sibling strings  for Hebrew we need four "step": male sing/plur, female sing/plur
        STEP = "שלוב"
        STEP_F = "שלובה"
        STEP_M = "שלוב"  # this is actually redundant if Can't make it "plural form".
        HALF = "למחצה"
        INLAW = "מחיתון"

        # 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):
            self.signal_keys = []
            self.state_signal_key = None
            self.storemap = False
            self.dirtymap = True
            self.stored_map = None
            self.map_handle = None
            self.map_meta = None
            self.__db_connected = False
            self.depth = 15
            try:
                from .config import config
                self.set_depth(config.get("behavior.generation-depth"))
            except ImportError:
                pass

            # data storage to communicate with recursive functions
            self.__max_depth_reached = False
            self.__loop_detected = False
            self.__max_depth = 12
            self.__all_families = False
            self.__all_dist = False
            self.__only_birth = False
            self.__crosslinks = False
            self.__msg = []

        def set_depth(self, depth):
            """
            Set how deep relationships must be searched. Input must be an
            integer > 0
            """
            if depth != self.depth:
                self.depth = depth
                self.dirtymap = True

        def get_depth(self):
            """
            Obtain depth of relationship search
            """
            return self.depth

        DIST_FATHER = "אב־קדמון רחוק %(step)s %(inlaw)s (%(level)d דורות)"

        def _get_father(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_FATHER_LEVEL) - 1:
                return self.DIST_FATHER % {"step": step, "inlaw": inlaw, "level": level}
            else:
                return _FATHER_LEVEL[level] % {"step": step, "inlaw": inlaw}

        
        DIST_SON = "בן רחוק %(step) %(inlaw)s (%(level)d דורות)"

        def _get_son(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_SON_LEVEL) - 1:
                return self.DIST_SON % {"step": step, "inlaw": inlaw, "level": level}
            else:
                return _SON_LEVEL[level] % {"step": step, "inlaw": inlaw}


        DIST_MOTHER = "אם־קדמונית רחוקה %(step)s %(inlaw) s(%(level)d דורות)"

        def _get_mother(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_MOTHER_LEVEL) - 1:
                return self.DIST_MOTHER % {"step": step, "inlaw": inlaw, "level": level}
            else:
                return _MOTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}


        DIST_DAUGHTER = "בת רחוקה %(step) %(inlaw)s(%(level)d דורות)"

        def _get_daughter(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_DAUGHTER_LEVEL) - 1:
                return self.DIST_DAUGHTER % {"step": step, "inlaw": inlaw, "level": level}
            else:
                return _DAUGHTER_LEVEL[level] % {"step": step, "inlaw": inlaw}

        def _get_parent_unknown(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level < len(_LEVEL_NAME):
                return ("אב־קדמון %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
            else:
                 return "אב־קדמון רחוק %s %s (%d דורות)" % (step, inlaw, level)


        DIST_CHILD = "צאצא רחוק %(step)s (%(level)d דורות)"

        def _get_child_unknown(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level < len(_LEVEL_NAME):
                return ("צאצא %(step)s %(inlaw)s" % {"step": step, "inlaw": inlaw} + _LEVEL_NAME[level])
            else:
                return self.DIST_CHILD % {"step": step, "level": level}


        DIST_AUNT = "דודה רחוקה %(step)s %(inlaw)s"

        def _get_aunt(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_SISTER_LEVEL) - 1:
                return self.DIST_AUNT % {"step": step, "inlaw": inlaw}
            else:
                return _SISTER_LEVEL[level] % {"step": step, "inlaw": inlaw}


        DIST_UNCLE = "דוד רחוק %(step)s %(inlaw)s"

        def _get_uncle(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_BROTHER_LEVEL) - 1:
                return self.DIST_UNCLE % {"step": step, "inlaw": inlaw}
            else:
                return _BROTHER_LEVEL[level] % {"step": step, "inlaw": inlaw}


        DIST_NEPHEW = "אחיין רחוק %(step)s %(inlaw)s"

        def _get_nephew(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_NEPHEW_LEVEL) - 1:
                return self.DIST_NEPHEW % {"step": step, "inlaw": inlaw}
            else:
                return _NEPHEW_LEVEL[level] % {"step": step, "inlaw": inlaw}


        DIST_NIECE = "אחיינית רחוקה %(step)s %(inlaw)s"

        def _get_niece(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level > len(_NIECE_LEVEL) - 1:
                return self.DIST_NIECE % {"step": step, "inlaw": inlaw}
            else:
                return _NIECE_LEVEL[level] % {"step": step, "inlaw": inlaw}

        def _get_cousin(self, level, removed, dir="", step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if removed == 0 and level < len(_LEVEL_NAME):
                return "בן־דוד %s %s %s" % (step, inlaw, _LEVEL_NAME[level])
            elif removed > len(_REMOVED_LEVEL) - 1 or level > len(_LEVEL_NAME) - 1:
                return "קרוב־משפחה רחוק %s %s" % (step, inlaw)
            else:
                return "בן־דוד/בת־דודה %s %s %s %s %s" % (step, inlaw, _LEVEL_NAME[level], _REMOVED_LEVEL[removed], dir,)


        DIST_SIB = "דוד/דודה רחוקים %(step)s %(inlaw)s"

        def _get_sibling(self, level, step="", inlaw=""):
            """
            Internal english method to create relation string
            """
            if level < len(_SIBLING_LEVEL):
                return _SIBLING_LEVEL[level] % {"step": step, "inlaw": inlaw}
            else:
                return self.DIST_SIB % {"step": step, "inlaw": 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:
                    # all birth parents are known, one
                    return self.HALF_SIB_FATHER
                elif motherother == motherorig:
                    return self.HALF_SIB_MOTHER
                else:
                    return self.STEP_SIB
            else:
                # some birth parents are not known, hence we or cannot know if
                # half siblings. step siblings might be possible, otherwise give up
                orig_nb_par = self._get_nonbirth_parent_list(db, orig)
                if fatherother and fatherother in orig_nb_par:
                    # the birth parent of other is non-birth of orig
                    if motherother and motherother == motherorig:
                        return self.HALF_SIB_MOTHER
                    else:
                        return self.STEP_SIB
                if motherother and motherother in orig_nb_par:
                    # the birth parent of other is non-birth of orig
                    if fatherother and fatherother == fatherorig:
                        return self.HALF_SIB_FATHER
                    else:
                        return self.STEP_SIB
                other_nb_par = self._get_nonbirth_parent_list(db, other)
                if fatherorig and fatherorig in other_nb_par:
                    # the one birth parent of other is non-birth of orig
                    if motherorig and motherother == motherorig:
                        return self.HALF_SIB_MOTHER
                    else:
                        return self.STEP_SIB
                if motherorig and motherorig in other_nb_par:
                    # the one birth parent of other is non-birth of orig
                    if fatherother and fatherother == fatherorig:
                        return self.HALF_SIB_FATHER
                    else:
                        return self.STEP_SIB
                # there is an unknown birth parent, it could be that this is the
                # birth parent of the other person
                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 fam in person.get_parent_family_handle_list():
                family = db.get_family_from_handle(fam)
                if not family:
                    continue
                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] == ChildRefType.BIRTH:
                    birthmother = family.get_mother_handle()
                if not birthfather and childrel[0][1] == ChildRefType.BIRTH:
                    birthfather = family.get_father_handle()
                if birthmother and birthfather:
                    break
            return (birthmother, birthfather)

        def _get_nonbirth_parent_list(self, db, person):
            """
            Returns a list of handles of parents of which it is known
            they are not birth parents.
            So all parents which do not have relation BIRTH or UNKNOWN
            are returned.
            """
            nb_parents = []
            for fam in person.get_parent_family_handle_list():
                family = db.get_family_from_handle(fam)
                if not family:
                    continue
                childrel = [
                    (ref.get_mother_relation(), ref.get_father_relation())
                    for ref in family.get_child_ref_list()
                    if ref.ref == person.handle
                ]
                if (
                    childrel[0][0] != ChildRefType.BIRTH
                    and childrel[0][0] != ChildRefType.UNKNOWN
                ):
                    nb_parents.append(family.get_mother_handle())
                if (
                    childrel[0][1] != ChildRefType.BIRTH
                    and childrel[0][1] != ChildRefType.UNKNOWN
                ):
                    nb_parents.append(family.get_father_handle())
            # make every person appear only once:
            return list(set(nb_parents))

        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 family_handle in orig.get_family_handle_list():
                family = db.get_family_from_handle(family_handle)
                # 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() == EventType.DIVORCE
                            or event.get_type() == EventType.ANNULMENT
                        ):
                            ex = True
                            break
                    if family_rel == FamilyRelType.MARRIED:
                        if ex:
                            val.append(self.PARTNER_EX_MARRIED)
                        else:
                            val.append(self.PARTNER_MARRIED)
                    elif family_rel == FamilyRelType.UNMARRIED:
                        if ex:
                            val.append(self.PARTNER_EX_UNMARRIED)
                        else:
                            val.append(self.PARTNER_UNMARRIED)
                    elif family_rel == 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
            """
            spouse_type = self._get_spouse_type(db, orig, other, all_rel)
            if spouse_type:
                return self.get_partner_relationship_string(
                    spouse_type, orig.get_gender(), other.get_gender()
                )
            else:
                return None

        def get_relationship_distance_new(
            self,
            db,
            orig_person,
            other_person,
            all_families=False,
            all_dist=False,
            only_birth=True,
        ):
            """
            Return 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:

            .. note:: _new can be removed once all rel_xx modules no longer
                      overwrite get_relationship_distance

            The tuple or list of tuples consists of:

            ==============  =====================================================
            Element         Description
            ==============  =====================================================
            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
            """
            # data storage to communicate with recursive functions
            self.__max_depth_reached = False
            self.__loop_detected = False
            self.__max_depth = self.get_depth()
            self.__all_families = all_families
            self.__all_dist = all_dist
            self.__only_birth = only_birth
            self.__crosslinks = False  # no crosslinks

            first_rel = -1
            second_rel = -1
            self.__msg = []

            common = []
            first_map = {}
            second_map = {}
            rank = 9999999

            try:
                if (
                    self.storemap
                    and self.stored_map is not None
                    and self.map_handle == orig_person.handle
                    and not self.dirtymap
                ):
                    first_map = self.stored_map
                    (
                        self.__max_depth_reached,
                        self.__loop_detected,
                        self.__all_families,
                        self.__all_dist,
                        self.__only_birth,
                        self.__crosslinks,
                        self.__msg,
                    ) = self.map_meta
                    self.__msg = list(self.__msg)
                else:
                    self.__apply_filter(db, orig_person, "", [], first_map)
                    self.map_meta = (
                        self.__max_depth_reached,
                        self.__loop_detected,
                        self.__all_families,
                        self.__all_dist,
                        self.__only_birth,
                        self.__crosslinks,
                        list(self.__msg),
                    )
                self.__apply_filter(
                    db, other_person, "", [], second_map, stoprecursemap=first_map
                )
            except RuntimeError:
                return (-1, None, -1, [], -1, []), [
                    _(
                        "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
                        "נסרקו %d דורות .\nלא מן הנמנע "
                        "שהתפספסו קשרי קירבת משפחה."
                    )
                ] + self.__msg

            if self.storemap:
                self.stored_map = first_map
                self.dirtymap = False
                self.map_handle = orig_person.handle

            for person_handle in second_map:
                if person_handle in first_map:
                    com = []
                    # a common ancestor
                    for rel1, fam1 in zip(
                        first_map[person_handle][0], first_map[person_handle][1]
                    ):
                        len1 = len(rel1)
                        for rel2, fam2 in zip(
                            second_map[person_handle][0], second_map[person_handle][1]
                        ):
                            len2 = len(rel2)
                            # collect paths to arrive at common ancestor
                            com.append((len1 + len2, 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.__max_depth_reached:
                self.__msg += [
                    _(
                        "מספר הדורות באילן היוחסין גבוהה ממספר הדורות המירבי "
                        "נסרקו %d דורות .\nלא מן הנמנע "
                        "שהתפספסו קשרי קירבת משפחה."
                    )
                    % (self.__max_depth)
                ]

            if common and not self.__all_dist:
                rank = common[0][0]
                person_handle = common[0][1]
                first_rel = common[0][2]
                first_fam = common[0][3]
                second_rel = common[0][4]
                second_fam = common[0][5]
                return (
                    rank,
                    person_handle,
                    first_rel,
                    first_fam,
                    second_rel,
                    second_fam,
                ), 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(
            self, db, person, rel_str, rel_fam, pmap, depth=1, stoprecursemap=None
        ):
            """
            Typically this method is called recursively in two ways:
            First method is stoprecursemap= None
            In this case a recursemap is builded by storing all data.

            Second method is with a stoprecursemap given
            In this case parents are recursively looked up. If present in
            stoprecursemap, a common ancestor is found, and the method can
            stop looking further. If however self.__crosslinks == True, the data
            of first contains loops, and parents
            will be looked up anyway an stored if common. At end the doubles
            are filtered out
            """
            if person is None or not person.handle:
                return

            if depth > self.__max_depth:
                self.__max_depth_reached = True
                # print('Maximum ancestor generations ('+str(depth)+') reached', \
                #            '(' + rel_str + ').',\
                #            'Stopping relation algorithm.')
                return
            depth += 1

            commonancestor = False
            store = True  # normally we store all parents
            if stoprecursemap:
                store = False  # but not if a stop map given
                if person.handle in stoprecursemap:
                    commonancestor = True
                    store = True

            # add person to the map, take into account that person can be obtained
            # from different sides
            if person.handle in pmap:
                # person is already a grandparent in another branch, we already have
                # had lookup of all parents, we call that a crosslink
                if not stoprecursemap:
                    self.__crosslinks = True
                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.__loop_detected = True
                            self.__msg += [
                                _("Relationship loop detected:")
                                + " "
                                + _(
                                    "Person %(person)s connects to himself via %(relation)s"
                                )
                                % {
                                    "person": person.get_primary_name().get_name(),
                                    "relation": rel2[len(rel1) :],
                                }
                            ]
                            return
            elif store:
                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 however the first map has crosslinks, we need to continue reduced
            if commonancestor and not self.__crosslinks:
                # don't continue search, great speedup!
                return

            family_handles = []
            main = person.get_main_parents_family_handle()
            if main:
                family_handles = [main]
            if self.__all_families:
                family_handles = person.get_parent_family_handle_list()

            try:
                parentstodo = {}
                fam = 0
                for family_handle in family_handles:
                    rel_fam_new = rel_fam + [fam]
                    family = db.get_family_from_handle(family_handle)
                    if not family:
                        continue
                    # 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 data[0] not in parentstodo:
                            persontodo = db.get_person_from_handle(data[0])
                            if data[3] == 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:
                            # this person is already scheduled to research
                            # update family list
                            famlist = parentstodo[data[0]][2]
                            if not isinstance(famlist[-1], list) and 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 chandle in pmap:
                                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, data in parentstodo.items():
                    self.__apply_filter(
                        db, data[0], data[1], data[2], pmap, depth, stoprecursemap
                    )
            except:
                import traceback

                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:
                rela2 = relation[2]
                rela4 = relation[4]
                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
                    rela2 = relation[2][:-1] + self.REL_FAM_BIRTH
                    rela4 = 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))
                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 = rela2[-1]
                        scndcomstr = tmp[2][-1]
                        newcomstra = self._famrel_from_persrel(frstcomstr, scndcomstr)
                        frstcomstr = rela4[-1]
                        scndcomstr = tmp[4][-1]
                        newcomstrb = self._famrel_from_persrel(frstcomstr, scndcomstr)

                        commonnew[posfam] = (
                            tmp[0],
                            tmp[1] + commonhandle,
                            rela2[:-1] + newcomstra,
                            tmp[3],
                            rela4[:-1] + newcomstrb,
                            tmp[5],
                        )
                    else:
                        existing_path.append(familypath)
                        commonnew.append(
                            (
                                relation[0],
                                commonhandle,
                                rela2,
                                familypath[2],
                                rela4,
                                familypath[3],
                            )
                        )
            # we now have multiple person handles, single families, now collapse
            #  families again if all else equal
            collapsed = commonnew[:1]
            for rel in commonnew[1:]:
                found = False
                for newrel in collapsed:
                    if newrel[0:3] == rel[0:3] and newrel[4] == rel[4]:
                        # another familypath to arrive at same result, merge
                        path1 = []
                        path2 = []
                        for a, b in zip(newrel[3], rel[3]):
                            if a == b:
                                path1.append(a)
                            elif isinstance(a, list):
                                path1.append(a.append(b))
                            else:
                                path1.append([a, b])
                        for a, b in zip(newrel[5], rel[5]):
                            if a == b:
                                path2.append(a)
                            elif isinstance(a, list):
                                path2.append(a.append(b))
                            else:
                                path2.append([a, b])
                        newrel[3][:] = path1[:]
                        newrel[5][:] = path2[:]
                        found = True
                        break
                if not found:
                    collapsed.append(rel)

            return collapsed

        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
            """
            for value in path:
                if value in [
                    self.REL_FAM_NONBIRTH,
                    self.REL_FATHER_NOTBIRTH,
                    self.REL_MOTHER_NOTBIRTH,
                ]:
                    return False
            return True

        def get_one_relationship(
            self, db, orig_person, other_person, extra_info=False, olocale=glocale
        ):
            """
            Returns a string representing the most relevant relationship between
            the two people. If extra_info = True, extra information is returned:
            (relation_string, distance_common_orig, distance_common_other)

            If olocale is passed in (a GrampsLocale) that language will be used.

            :param olocale: allow selection of the relationship language
            :type olocale: a GrampsLocale instance
            """
            self._locale = olocale
            stop = False
            if orig_person is None:
                rel_str = _("undefined")
                stop = True

            if not stop and orig_person.get_handle() == other_person.get_handle():
                rel_str = ""
                stop = True

            if not stop:
                is_spouse = self.is_spouse(db, orig_person, other_person)
                if is_spouse:
                    rel_str = is_spouse
                    stop = True

            if stop:
                if extra_info:
                    return (rel_str, -1, -1)
                else:
                    return rel_str

            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:
                if extra_info:
                    return ("", -1, -1)
                else:
                    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:
                # data is sorted on rank
                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 == dist_other == 1:
                    rel_str = self.get_sibling_relationship_string(
                        self.get_sibling_type(db, orig_person, other_person),
                        orig_person.get_gender(),
                        other_person.get_gender(),
                    )
                else:
                    rel_str = 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 == dist_other == 1:
                    rel_str = self.get_sibling_relationship_string(
                        self.get_sibling_type(db, orig_person, other_person),
                        orig_person.get_gender(),
                        other_person.get_gender(),
                    )
                else:
                    rel_str = 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,
                    )
            if extra_info:
                return (rel_str, dist_orig, dist_other)
            else:
                return rel_str

        def get_all_relationships(self, db, orig_person, other_person):
            """
            Return a tuple, of which the first entry is a list with all
            relationships in text, and the second a list of lists of all common
            ancestors that have that text as relationship
            """
            relstrings = []
            commons = {}
            if orig_person is None:
                return ([], [])

            if orig_person.get_handle() == other_person.get_handle():
                return ([], [])

            is_spouse = self.is_spouse(db, orig_person, other_person)
            if is_spouse:
                relstrings.append(is_spouse)
                commons[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:
                data = self.collapse_relations(data)
                for rel in data:
                    rel2 = rel[2]
                    rel4 = rel[4]
                    rel1 = rel[1]
                    dist_orig = len(rel[2])
                    dist_other = len(rel[4])
                    if rel[2] and rel[2][-1] == self.REL_SIBLING:
                        rel2 = rel2[:-1] + self.REL_FAM_BIRTH
                        dist_other += 1
                        rel4 = rel4 + self.REL_FAM_BIRTH
                        rel1 = None
                    birth = self.only_birth(rel2) and self.only_birth(rel4)
                    if dist_orig == dist_other == 1:
                        rel_str = self.get_sibling_relationship_string(
                            self.get_sibling_type(db, orig_person, other_person),
                            orig_person.get_gender(),
                            other_person.get_gender(),
                        )
                    else:
                        rel_str = self.get_single_relationship_string(
                            dist_orig,
                            dist_other,
                            orig_person.get_gender(),
                            other_person.get_gender(),
                            rel2,
                            rel4,
                            only_birth=birth,
                            in_law_a=False,
                            in_law_b=False,
                        )
                    if rel_str not in relstrings:
                        relstrings.append(rel_str)
                        if rel1:
                            commons[rel_str] = rel1
                        else:
                            # unknown parent eg
                            commons[rel_str] = []
                    else:
                        if rel1:
                            commons[rel_str].extend(rel1)
            # construct the return tupply, relstrings is ordered on rank automatic
            common_list = []
            for rel_str in relstrings:
                common_list.append(commons[rel_str])
            return (relstrings, common_list)

        def get_plural_relationship_string(
            self,
            Ga,
            Gb,
            reltocommon_a="",
            reltocommon_b="",
            only_birth=True,
            in_law_a=False,
            in_law_b=False,
        ):
            """
            Provide 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.

            .. seealso::
                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
            :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 only_birth: True if relation between a and b is by birth only
                               False otherwise
            :type only_birth: bool
            :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
            :returns: A string describing the relationship between the person and
                      the group.
            :rtype: str
            """
            rel_str = "קרובי־משפחה רחוקים"
            if Ga == 0:
                # These are descendants
                if Gb < len(_CHILDREN_LEVEL):
                    rel_str = _CHILDREN_LEVEL[Gb]
                else:
                    rel_str = "צאצאים רחוקים"
            elif Gb == 0:
                # These are parents/grand parents
                if Ga < len(_PARENTS_LEVEL):
                    rel_str = _PARENTS_LEVEL[Ga]
                else:
                    rel_str = "אבות־קדמונים רחוקים"
            elif Gb == 1:
                # These are siblings/aunts/uncles
                if Ga < len(_SIBLINGS_LEVEL):
                    rel_str = _SIBLINGS_LEVEL[Ga]
                else:
                    rel_str = "דודים/דודות רחוקים"
            elif Ga == 1:
                # These are nieces/nephews
                if Gb < len(_NEPHEWS_NIECES_LEVEL):
                    rel_str = _NEPHEWS_NIECES_LEVEL[Gb]
                else:
                    rel_str = "אחיינים/אחייניות רחוקים"
            elif Ga > 1 and Ga == Gb:
                # These are cousins in the same generation
                if Ga <= len(_LEVEL_NAME):
                    rel_str = "בני דודים %s  " % _LEVEL_NAME[Ga - 1]
                else:
                    rel_str = "בני דודים רחוקים"
            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 %s (עולה)" % (
                        _LEVEL_NAME[Gb - 1],
                        _REMOVED_LEVEL[Ga - Gb],
                    )
                else:
                    rel_str = "בני דודים רחוקים"
            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 %s (יורד)" % (
                        _LEVEL_NAME[Ga - 1],
                        _REMOVED_LEVEL[Gb - Ga],
                    )
                else:
                    rel_str = "בני דודים רחוקים"

            if in_law_b is True:
                rel_str = "זוג של %s" % rel_str

            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,
        ):
            """
            Provide 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.

            .. seealso::
                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:

            =======================  ===========================================
            Code                     Description
            =======================  ===========================================
            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
            """
            if only_birth:
                step = ""
            else:
                if gender_b == MALE:
                    step = self.STEP
                elif gender_b == FEMALE:
                    step = self.STEP_F
                else:
                    step = self.STEP # Change this as appropriate for other and unknown gender

            if in_law_a or in_law_b:
                inlaw = self.INLAW
            else:
                inlaw = ""

            rel_str = "קרוב־משפחה רחוק %s%s" % (step, inlaw)

            if Ga == 0:
                # b is descendant of a
                if Gb == 0:
                    rel_str = "האדם"
                elif gender_b == MALE:
                    rel_str = self._get_son(Gb, step, inlaw)
                elif gender_b == FEMALE:
                    rel_str = self._get_daughter(Gb, step, inlaw)
                else:
                    rel_str = self._get_child_unknown(Gb, step, inlaw)
            elif Gb == 0:
                # b is parents/grand parent of a
                if gender_b == MALE:
                    rel_str = self._get_father(Ga, step, inlaw)
                elif gender_b == 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 == MALE:
                    rel_str = self._get_uncle(Ga, step, inlaw)
                elif gender_b == 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 == MALE:
                    rel_str = self._get_nephew(Gb - 1, step, inlaw)
                elif gender_b == FEMALE:
                    rel_str = self._get_niece(Gb - 1, step, inlaw)
                elif Gb < len(_NIECE_LEVEL) and Gb < len(_NEPHEW_LEVEL):
                    rel_str = "%sאו %s" % (
                        self._get_nephew(Gb - 1, step, inlaw),
                        self._get_niece(Gb - 1, step, inlaw),
                    )
                else:
                    rel_str = "אחיין/אחיינית רחוקים %s %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=" (עולה)", 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=" (יורד)", 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.

            .. warning:: 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_MOTHER or sib_type == self.HALF_SIB_FATHER:
                typestr = self.HALF
            elif sib_type == self.STEP_SIB:
                typestr = self.STEP

            if in_law_a or in_law_b:
                inlaw = self.INLAW
            else:
                inlaw = ""

            if gender_b == MALE:
                rel_str = self._get_uncle(1, typestr, inlaw)
            elif gender_b == 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 partners of
            type spouse_type.
            Eg: b is the spouse of a
            Here 'spouse' is the string we need to determine

            .. warning:: 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 ""

            trans_text = _
            # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh)
            if hasattr(self, "_locale") and self._locale != glocale:
                trans_text = self._locale.translation.sgettext

            if spouse_type == self.PARTNER_MARRIED:
                if gender == MALE:
                    return trans_text("בעל")
                elif gender == FEMALE:
                    return trans_text("רעיה")
                else:
                    return trans_text("זוג", "מגדר לא ידוע")
            elif spouse_type == self.PARTNER_EX_MARRIED:
                if gender == MALE:
                    return trans_text("בעל לשעבר")
                elif gender == FEMALE:
                    return trans_text("רעיה לשעבר")
                else:
                    return trans_text("זוג לשעבר", "מגדר לא ידוע")
            elif spouse_type == self.PARTNER_UNMARRIED:
                if gender == MALE:
                    return trans_text("שותף", "זכר, לא נשוי")
                elif gender == FEMALE:
                    return trans_text("שותפה", "נקבה, לא נשואה")
                else:
                    return trans_text("שותף", "מגדר לא ידוע, לא נשוי")
            elif spouse_type == self.PARTNER_EX_UNMARRIED:
                if gender == MALE:
                    return trans_text("שותף לשעבר", "זכר, לא נשוי")
                elif gender == FEMALE:
                    return trans_text("שותפה לשעבר", "נקבה, לא נשואה")
                else:
                    return trans_text("שותף לשעבר", "מגדר לא ידוע, לא נשוי")
            elif spouse_type == self.PARTNER_CIVIL_UNION:
                if gender == MALE:
                    return trans_text("שותף", "זכר, נישואים אזרחיים")
                elif gender == FEMALE:
                    return trans_text("שותפה", "נקבה, נישואים אזרחיים")
                else:
                    return trans_text("שותף", "מגדר לא ידוע, נישואים אזרחיים")
            elif spouse_type == self.PARTNER_EX_CIVIL_UNION:
                if gender == MALE:
                    return trans_text("שותף קודם", "זכר, נישואים אזרחיים")
                elif gender == FEMALE:
                    return trans_text("שותפה קודמת", "נקבה, נישואים אזרחיים")
                else:
                    return trans_text("שותף קודם", "מגדר לא ידוע ,נישואים אזרחיים")
            elif spouse_type == self.PARTNER_UNKNOWN_REL:
                if gender == MALE:
                    return trans_text("שותף", "זכר, טיב יחסים לא ידוע")
                elif gender == FEMALE:
                    return trans_text("שותפה", "נקבה, טיב יחסים לא ידוע")
                else:
                    return trans_text("שותף", "מגדר לא ידוע, טיב יחסים לא ידוע")
            else:
                # here we have spouse_type == self.PARTNER_EX_UNKNOWN_REL
                #   or other not catched types
                if gender == MALE:
                    return trans_text("שותף קודם", "זכר, טיב יחסים לא ידוע")
                elif gender == FEMALE:
                    return trans_text("שותפה קודמת", "נקבה, טיב יחסים לא ידוע")
                else:
                    return trans_text("שותף קודם", "מגדר לא ידוע ,טיב יחסים לא ידוע")

        def connect_db_signals(self, dbstate):
            """
            We can save work by storing a map, however, if database changes
            this map must be regenerated.
            Before close, the calling app must call disconnect_db_signals
            """
            if self.__db_connected:
                return
            assert len(self.signal_keys) == 0
            self.state_signal_key = dbstate.connect(
                "database-changed", self._dbchange_callback
            )
            self.__connect_db_signals(dbstate.db)

        def __connect_db_signals(self, db):
            signals = [
                "person-add",
                "person-update",
                "person-delete",
                "person-rebuild",
                "family-add",
                "family-update",
                "family-delete",
                "family-rebuild",
                "database-changed",
            ]
            for name in signals:
                self.signal_keys.append(db.connect(name, self._datachange_callback))
            self.storemap = True
            self.__db_connected = True

        def disconnect_db_signals(self, dbstate):
            """
            Method to disconnect to all signals the relationship calculator is
            subscribed
            """
            dbstate.disconnect(self.state_signal_key)
            list(map(dbstate.db.disconnect, self.signal_keys))
            self.storemap = False
            self.stored_map = None

        def _dbchange_callback(self, db):
            """
            When database changes, the map can no longer be used.
            Connects must be remade
            """
            self.dirtymap = True
            # signals are disconnected on close of old database, connect to new
            self.__connect_db_signals(db)

        def _datachange_callback(self, handle_list=None):
            """
            When data in database changes, the map can no  longer be used.
            As the map might be in use or might be generated at the moment,
            this method sets a dirty flag. Before reusing the map, this flag
            will be checked
            """
            self.dirtymap = True


    # -------------------------------------------------------------------------
    #
    # define the default relationshipcalculator
    #
    # -------------------------------------------------------------------------

    __RELCALC_CLASS = None


    def get_relationship_calculator(reinit=False, clocale=glocale):
        """
        Return the relationship calculator for the current language.

        If clocale is passed in (a GrampsLocale) then that language will be used.

        :param clocale: allow selection of the relationship language
        :type clocale: a GrampsLocale instance

        """
        global __RELCALC_CLASS

        if __RELCALC_CLASS is None or reinit:
            lang = clocale.language[0]
            __RELCALC_CLASS = RelationshipCalculator
            # If lang not set default to English relationship calulator
            # See if lang begins with en_, English_ or english_
            # If so return standard relationship calculator.
            if lang.startswith("en") or lang == "C":
                return __RELCALC_CLASS()
            # set correct non English relationship calculator based on lang
            relation_translation_found = False
            for plugin in PluginRegister.get_instance().relcalc_plugins():
                if lang in plugin.lang_list:
                    pmgr = BasePluginManager.get_instance()
                    # the loaded module is put in variable mod
                    mod = pmgr.load_plugin(plugin)
                    if mod:
                        __RELCALC_CLASS = getattr(mod, plugin.relcalcclass)
                        relation_translation_found = True
                        break
            if not relation_translation_found and len(
                PluginRegister.get_instance().relcalc_plugins()
            ):
                LOG.warning(
                    _(
                        "תרגומון קירבת משפחה לא זמין "
                        "לקוד השפה '%s'. במקום זאת גרמפס יאתחל בשפה ה'אנגלית'."
                    ),
                    lang,
                )
        return __RELCALC_CLASS()



    if __name__ == "__main__":
        """
        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
        """
        from gramps.gen.relationship import test
        REL_CALC = RelationshipCalculator()
        test(REL_CALC, True)



See also