예제 #1
0
    def test_add_children(self):
        """test adding children
        """
        fam = Family("@F11@")
        child_id_1 = "@I08@"
        fam.add_child(child_id_1)

        self.assertEqual(1, len(fam.get_children()))
        self.assertEqual(child_id_1, fam.get_children()[0])

        child_id_2 = "@I09@"
        fam.add_child(child_id_2)

        self.assertEqual(2, len(fam.get_children()))
        self.assertEqual(child_id_2, fam.get_children()[1])
예제 #2
0
class Families(object):
    """Families class
    Contains logic for processing family tags

    Attributes:
        families (:list:Family): dict of families
        _curr_family (Family): family of the current processing line
    Args:
        people (:list:People): people used for looking up individuals
    """

    CLASS_IDENTIFIER = "FAMILY"
    DAYS_IN_YEAR = 365.2425

    def __init__(self, people, validation_messages):
        self.families = {}
        self._curr_family = None
        self._current_level_1 = None
        self._people = people
        self._msgs = validation_messages
        self._current_time = datetime.now()
        # parses names in the format Bob /Hope/ where the last name has slashes around it
        self._last_name_regex = re.compile(r'\/(.*)\/')
        self._first_name_regex = re.compile(r'(.*) \/')

    def process_line_data(self, data):
        """line data is a dict of the format:
        {
            "level": int,
            "tag": string,
            "args": string,
            "valid": "Y" or "N"
        }
        """

        if data["valid"] == "N":
            raise ValueError

        if data["level"] < 2:
            self._current_level_1 = None

        if data["tag"] == "FAM":
            self._curr_family = Family(data["args"])
            # US22
            if data["args"] in self.families:
                self._msgs.add_message(self.CLASS_IDENTIFIER, "US22", data["args"], "NA",
                                       "Not unique family ID " + data["args"] + " ")

            self.families[data["args"]] = self._curr_family

        if data["tag"] == "MARR":
            self._current_level_1 = "MARR"

        if data["tag"] == "DIV":
            self._current_level_1 = "DIV"

        if data["tag"] == "HUSB":
            self._curr_family.set_husband_id(data["args"])
            self._current_level_1 = "HUSB"

        if data["tag"] == "WIFE":
            self._curr_family.set_wife_id(data["args"])
            self._current_level_1 = "WIFE"

        if data["tag"] == "CHIL":
            self._curr_family.add_child(data["args"])
            self._current_level_1 = "CHIL"

        if data["tag"] == "DATE":
            if self._current_level_1 == "MARR":
                self._curr_family.set_date(data["args"], "married")
            if self._current_level_1 == "DIV":
                self._curr_family.set_date(data["args"], "divorced")

    def print_all(self):
        """print all families information
        """
        fam_keys = sorted(self.families.keys())

        p_table = PrettyTable(["ID", "Married", "Divorced", "Husband ID",
                               "Husband Name", "Wife ID", "Wife Name", "Children"])
        for idx in fam_keys:
            family = self.families[idx]
            married_date = "NA"
            if family.get_married_date() is not None:
                married_date = family.get_married_date().date().isoformat()
            divorced_date = "NA"
            if family.get_divorced_date() is not None:
                divorced_date = family.get_divorced_date().date().isoformat()
            husband_id = "NA"
            husband_name = "NA"
            if family.get_husband_id() is not None:
                husband_id = family.get_husband_id()
                husband_name = self._people.individuals[husband_id].get_name()
            wife_id = "NA"
            wife_name = "NA"
            if family.get_wife_id() is not None:
                wife_id = family.get_wife_id()
                wife_name = self._people.individuals[wife_id].get_name()
            children_order = "NA"
            if family.get_children() is not None:
                children = family.get_children()
                child_age = []
            for child_id in children:
                child = self._people.individuals[child_id]
                if child.get_age is None:
                    child_age = 1
                child_age.append(child.get_age())
            keys = children
            values = child_age
            childid_age = dict(zip(keys, values))
            order_childid_age = sorted(
                childid_age.items(), key=lambda t: t[1], reverse=True)
            order_childid_id = [idx for idx, val in order_childid_age]
            children_order = order_childid_id

            p_table.add_row([
                family.get_family_id(),
                married_date,
                divorced_date,
                husband_id,
                husband_name,
                wife_id,
                wife_name,
                children_order])
        print(p_table)

    def us30_print_married(self):
        """"
        Prints all married individuals
        """
        families = self.families
        individuals = self._people.individuals
        married_ind = dict()
        table = PrettyTable(["ID", "Name", "Married"])

        for family_id in families:
            # type: Family
            family = self.families[family_id]
            if family.get_married_date() and family.get_divorced_date() is None:
                husband = individuals[family.get_husband_id()]
                if family.get_husband_id() not in married_ind:
                    married_ind[family.get_husband_id()] = husband

                wife = individuals[family.get_wife_id()]
                if family.get_wife_id() not in married_ind:
                    married_ind[family.get_wife_id()] = wife

        for individual_id, individual in married_ind.items():
            table.add_row([individual_id, individual.get_name(), True])

        print("Married Individuals")
        print(table)

    def us32_print_multiple_births(self):
        """"US32
        Prints all multiple births
        """
        families = self.families
        individuals = self._people.individuals
        table = PrettyTable(["ID", "Name", "Family ID", "Birthday"])

        for family_id in families:
            child_bd = []
            children = []
            # type: Family
            family = self.families[family_id]
            if family.get_children() is not None and len(family.get_children()) > 1:
                for child_id in family.get_children():
                    child = individuals[child_id]
                    if child.get_birth_date is not None:
                        children.append(child_id)
                        child_bd.append(child.get_birth_date())
                for x in range(0, len(child_bd)):
                    if child_bd.count(child_bd[x]) > 1:
                        table.add_row([children[x], individuals[children[x]].get_name(), family_id, child_bd[x]])

        print("Multiple Births")
        print(table)

    def us33_print_orphans(self):
        """"
        Prints all orphaned individuals
        """
        families = self.families
        individuals = self._people.individuals
        table = PrettyTable(["ID", "Name", "Age"])

        for family_id in families:
            # type: Family
            family = self.families[family_id]
            if not family.get_children():
                continue
            hus_id = family.get_husband_id()
            husband = None
            wife_id = family.get_wife_id()
            wife = None
            if wife_id is not None:
                wife = individuals[wife_id]
            if hus_id is not None:
                husband = individuals[hus_id]
            if husband is None or wife is None:
                continue
            if wife.get_death_date() is not None and husband.get_death_date() is not None:
                for child_id in family.get_children():
                    child = individuals[child_id]
                    age = child.get_age()
                    if age is not None and age < 18:
                        table.add_row([child_id, child.get_name(), child.get_age()])

        print("Orphans")
        print(table)

    def us_39_print_upcoming_anniversaries(self):
        """"US39
        Prints Anniversaries within the next 30 days
        """
        families = self.families
        individuals = self._people.individuals
        table = PrettyTable(["ID", "Husband", "Wife", "Anniversary"])

        for familiy_id in families:
            # type: Family
            family = self.families[familiy_id]
            today = datetime.today()
            anniversary = family.get_married_date()
            husband = individuals[family.get_husband_id()]
            wife = individuals[family.get_wife_id()]
            if anniversary is not None and husband.get_is_alive() and wife.get_is_alive():
                family_current_anniversary = datetime(today.year, anniversary.month, anniversary.day)
                if 0 <= (family_current_anniversary - today).days <= 30:
                    table.add_row([family.get_family_id(), husband.get_name(), wife.get_name(), anniversary])

        print("Upcoming Anniversaries")
        print(table)

    def us34_print_big_age_diff(self):
        """
        prints all couples that when they married they were twice as old as the other spouse.
        It doesn't matter if they're alive or dead as this is when they got married.
        Must have birth and married date.
        """
        families = self.families
        individuals = self._people.individuals
        table = PrettyTable(["ID", "Names", "Age Difference"])

        for family_id in families:
            # type: Family
            family = self.families[family_id]
            if family.get_married_date() is None:
                continue
            marr_date = family.get_married_date()
            hus_id = family.get_husband_id()
            husband = None
            wife_id = family.get_wife_id()
            wife = None
            if wife_id is not None:
                wife = individuals[wife_id]
            if hus_id is not None:
                husband = individuals[hus_id]
            if husband is None or wife is None:
                continue
            wife_age = wife.get_age_at_date(marr_date)
            husb_age = husband.get_age_at_date(marr_date)
            if wife_age is not None and husb_age is not None:
                age_diff = None
                if wife_age > (husb_age * 2):
                    age_diff = wife_age - husb_age
                if husb_age > (wife_age * 2):
                    age_diff = husb_age - wife_age
                if age_diff is not None and age_diff > 0:
                    table.add_row([family.get_family_id(), wife.get_name() + ', ' + husband.get_name(), age_diff])
        print("Married Couples Large Age Differences")
        print(table)

    def validate(self):
        """run through all the validation rules around families
        """
        # ensure the order of the results doesn't change between runs
        fam_keys = sorted(self.families.keys())
        fam_hashs = {}
        for idx in fam_keys:
            family = self.families[idx]
            self._us02_us04_us05_us06_us10_validate_dates(family)
            self._us01_validate_marr_div_dates(family)
            self._us09_validate_death_of_parents_before_child_birth(family)
            self._us16_validate_males_in_family_same_last_name(family)
            self._us25_validate_children_names_and_birthdays_are_different(family)
            self._us15_validate_fewer_than_15_siblings(family)
            self._us11_validate_no_bigamy(family)
            self._us26_validate_corresponding_entries(family)
            self._us17_validate_no_marriage_to_decendants(family)
            self._us14_validate_less_than_5_multi_births(family)
            self._us12_validate_parents_not_too_old(family)
            self._us21_validate_correct_gender_roles(family)
            self._us24_hash_family(family, fam_hashs)

        self._us24_validate_duplicate_families(fam_hashs)

    def _us24_validate_duplicate_families(self, fam_hashs):
        """US24: Go through the hashes of the families and find the ones
        with more than one in the hash
        US24 No more than one family with the same spouses by name
        and the same marriage date should appear in a GEDCOM file.
        """
        fam_keys = sorted(fam_hashs.keys())
        for idx in fam_keys:
            key = fam_hashs[idx]
            dups = key["duplicate_families"]
            if dups:
                self._msgs.add_message(self.CLASS_IDENTIFIER,
                                       "US24",
                                       key["first_family_id"],
                                       "NA",
                                       "Duplicate families by spouse names and married date: " + ", ".join(dups))

    def _us24_hash_family(self, family, fam_hashs):
        """ hash family values to allow for detecting redundant family setups
        Used for US24 validation.  Must have both spouses and a married date set to get a hash else we have incomplete data to detect a redundant family
        """
        if family.get_husband_id() is None or family.get_wife_id() is None or family.get_married_date() is None:
            return
        fam_hash = self._people.individuals[family.get_husband_id()].get_name() + "|" + self._people.individuals[family.get_wife_id()].get_name() + "|" + family.get_married_date().isoformat()
        if fam_hash in fam_hashs:
            fam_hashs[fam_hash]["duplicate_families"].append(family.get_family_id())
        else:
            fam_hashs[fam_hash] = {
                "first_family_id": family.get_family_id(),
                "duplicate_families": []
            }

    def _us02_us04_us05_us06_us10_validate_dates(self, family):
        """US02, US04-US06, US10 validating dates
        """
        # US04
        if family.get_family_id() is not None and family.get_married_date() is not None:
            fam_id = family.get_family_id()
            mar_date = family.get_married_date()
            if family.get_divorced_date() is not None:
                div_date = family.get_divorced_date()
                if mar_date > div_date:
                    self._msgs.add_message("FAMILY",
                                           "US04",
                                           fam_id,
                                           "NA",
                                           "Marriage date should occur before divorce date of a family")
            # Husband Dates
            # US02 HUSBAND
            if family.get_husband_id() is not None and family.get_wife_id() is not None:
                hus_id = family.get_husband_id()
                wife_id = family.get_wife_id()
                if self._people.individuals[family.get_husband_id()].get_birth_date() is not None and self._people.individuals[family.get_husband_id()].get_name() is not None:
                    hus_bd = self._people.individuals[family.get_husband_id(
                    )].get_birth_date()
                    hus_name = self._people.individuals[family.get_husband_id(
                    )].get_name()
                    if mar_date < hus_bd:
                        self._msgs.add_message(People.CLASS_IDENTIFIER,
                                               "US02",
                                               hus_id,
                                               hus_name,
                                               "Birth date should occur before marriage of an individual")
                    # US10 HUSBAND
                    hus_mar_age = int(
                        (mar_date - hus_bd).days / self.DAYS_IN_YEAR)
                    if hus_mar_age < 14:
                        self._msgs.add_message("FAMILY",
                                               "US10",
                                               fam_id,
                                               "NA",
                                               "marriage before age 14 for " + hus_id + " " + hus_name)
                    # US05 HUSBAND
                    if self._people.individuals[family.get_husband_id()].get_death_date() is not None:
                        hus_dd = self._people.individuals[family.get_husband_id(
                        )].get_death_date()
                        if mar_date > hus_dd:
                            self._msgs.add_message("FAMILY",
                                                   "US05",
                                                   fam_id,
                                                   "NA",
                                                   "marriage after death for " + hus_id + " " + hus_name)
                        # US06 HUSBAND
                        if family.get_divorced_date() is not None:
                            div_date = family.get_divorced_date()
                            if div_date > hus_dd:
                                self._msgs.add_message("FAMILY",
                                                       "US06",
                                                       fam_id,
                                                       "NA",
                                                       "divorce after death for " + hus_id + " " + hus_name)
                # Wife Dates
                # US02 WIFE
                if self._people.individuals[family.get_wife_id()].get_birth_date() is not None and self._people.individuals[family.get_wife_id()].get_name() is not None:
                    wife_bd = self._people.individuals[family.get_wife_id(
                    )].get_birth_date()
                    wife_name = self._people.individuals[family.get_wife_id(
                    )].get_name()
                    if mar_date < wife_bd:
                        self._msgs.add_message(People.CLASS_IDENTIFIER,
                                               "US02",
                                               wife_id,
                                               wife_name,
                                               "Birth date should occur before marriage of an individual")
                    # US10 WIFE
                    wif_mar_age = int(
                        (mar_date - wife_bd).days / self.DAYS_IN_YEAR)
                    if wif_mar_age < 14:
                        self._msgs.add_message("FAMILY",
                                               "US10",
                                               fam_id,
                                               "NA",
                                               "marriage before age 14 for " + wife_id + " " + wife_name)
                    # US05 WIFE
                    if self._people.individuals[family.get_wife_id()].get_death_date() is not None:
                        wife_dd = self._people.individuals[family.get_wife_id(
                        )].get_death_date()
                        if mar_date > wife_dd:
                            self._msgs.add_message("FAMILY",
                                                   "US05",
                                                   fam_id,
                                                   "NA",
                                                   "marriage after death for " + wife_id + " " + wife_name)
                        # US06 WIFE
                        if family.get_divorced_date() is not None:
                            div_date = family.get_divorced_date()
                            if div_date > wife_dd:
                                self._msgs.add_message("FAMILY",
                                                       "US06",
                                                       fam_id,
                                                       "NA",
                                                       "divorce after death for " + wife_id + " " + wife_name)

    def _us01_validate_marr_div_dates(self, family):
        """US01 Validate that family marriage and divorce dates occurs before current date
        """
        if family.get_divorced_date() is not None:
            if family.get_divorced_date() > self._current_time:
                self._msgs.add_message("FAMILY",
                                       "US01",
                                       family.get_family_id(),
                                       "NA",
                                       "Divorced date should occur before current date for a family")
                return False

        if family.get_married_date() is not None:
            if family.get_married_date() > self._current_time:
                self._msgs.add_message("FAMILY",
                                       "US01",
                                       family.get_family_id(),
                                       "NA",
                                       "Married date should occur before current date for a family")
            return False
        return True

    def _us09_validate_death_of_parents_before_child_birth(self, family):
        """US09: validate death of parents before child birth
        """
        key = "US09"
        msg = "parent death before child birth for "
        children = family.get_children()

        for child_id in children:
            chil = self._people.individuals[child_id]
            if family.get_husband_id() is not None:
                # check the husband died after conception of child
                husb = self._people.individuals[family.get_husband_id()]
                if husb.get_death_date() is not None:
                    hub9_date = husb.get_death_date()
                    # Calculate 9 Months Back
                    for _ in range(0, 9):
                        hub9_date = hub9_date.replace(day=1)
                        hub9_date = hub9_date - timedelta(days=1)
                        # Calculate Day
                    if hub9_date.day > husb.get_death_date().day:
                        hub9_date = hub9_date.replace(
                            day=husb.get_death_date().day)
                    if hub9_date < chil.get_birth_date():
                        # error husband died at least 9 months before child birth
                        self._msgs.add_message(
                            "FAMILY",
                            key,
                            family.get_family_id(),
                            "NA",
                            msg + husb.get_person_id() + " " + husb.get_name())
            if family.get_wife_id() is not None:
                # check the wife died before the child birth
                wife = self._people.individuals[family.get_wife_id()]
                if wife.get_death_date() is not None and chil.get_birth_date() > wife.get_death_date():
                    # error wife died before child birth
                    self._msgs.add_message(
                        self.CLASS_IDENTIFIER,
                        key,
                        family.get_family_id(),
                        "NA",
                        msg + wife.get_person_id() + " " + wife.get_name())

    def _us11_person_check_bigamy(self, person_id, marriage_start, marriage_end):
        """Part of US11 check bigamy for person
        """
        spouses_h = self._people.individuals[person_id].get_spouse_of_families(
        )
        if spouses_h is not None and len(spouses_h) > 1:
            for item in spouses_h:
                ft = self.families[item]
                ft_hus = ft.get_husband_id()
                ft_wif = ft.get_wife_id()
                ft_som = ft.get_married_date()
                if ft_som is None:
                    return True
                if (marriage_start is None or ft_som > marriage_start) and ft_som < marriage_end:
                    return True
                ft_eom = ft.get_divorced_date()
                if ft_eom is None:
                    ft_eom = self._people.individuals[ft_hus].get_death_date()
                    ft_w_d = self._people.individuals[ft_wif].get_death_date()
                    if ft_w_d is not None:
                        if ft_eom is None or ft_w_d < ft_eom:
                            ft_eom = ft_w_d
                if ft_eom is None:
                    ft_eom = self._current_time
                if ft_eom > marriage_start and ft_eom < marriage_end:
                    return True
        return False

    def _us11_validate_no_bigamy(self, family):
        """US11 No bigamy
        """
        key = "US11"
        msg = "Bigamy for "

        hus = family.get_husband_id()
        wif = family.get_wife_id()

        if hus is None or wif is None:
            return

        som = family.get_married_date()
        eom = family.get_divorced_date()
        if eom is None:
            eom = self._people.individuals[hus].get_death_date()
            w_d = self._people.individuals[wif].get_death_date()
            if w_d is not None:
                if eom is None or w_d < eom:
                    eom = w_d
        if eom is None:
            eom = self._current_time

        hus_result = self._us11_person_check_bigamy(hus, som, eom)

        wif_result = self._us11_person_check_bigamy(wif, som, eom)

        if hus_result is True:
            self._msgs.add_message(
                self.CLASS_IDENTIFIER,
                key,
                family.get_family_id(),
                "NA",
                msg + self._people.individuals[family.get_husband_id()].get_person_id() + " " + self._people.individuals[family.get_husband_id()].get_name())

        if wif_result is True:
            self._msgs.add_message(
                self.CLASS_IDENTIFIER,
                key,
                family.get_family_id(),
                "NA",
                msg + self._people.individuals[family.get_wife_id()].get_person_id() + " " + self._people.individuals[family.get_wife_id()].get_name())

    def _us17_validate_no_marriage_to_decendants(self, family):
        """US17 No marriage to decendants
        """
        if family.get_children() is None:
            return

        if family.get_husband_id() is not None:
            self._us17_check_family_for_decendant_marriage(
                family, family.get_husband_id(), True)

        if family.get_wife_id() is not None:
            self._us17_check_family_for_decendant_marriage(
                family, family.get_wife_id(), True)

    def _us17_check_family_for_decendant_marriage(self, family, person_id, ignore_current_family):
        """recursive function to go through families to see if a particular person_id was reused
        Is a part of US17
        """
        if ignore_current_family is False:
            if family.get_wife_id() == person_id or family.get_husband_id() == person_id:
                person = self._people.individuals[person_id]
                self._msgs.add_message(self.CLASS_IDENTIFIER,
                                       "US17",
                                       family.get_family_id(),
                                       "NA",
                                       "No marriage to decendants. " + person_id + " " + person.get_name())

        children = family.get_children()
        if children is None:
            return

        for child_id in family.get_children():
            child = self._people.individuals[child_id]
            fams = child.get_spouse_of_families()
            if fams is None:
                return
            for fam_id in fams:
                fam = self.families[fam_id]
                self._us17_check_family_for_decendant_marriage(
                    fam, person_id, False)

    def _us25_validate_children_names_and_birthdays_are_different(self, family):
        """US25 Child in a family must have unique first name and birthday
        otherwise it will be considered a misentry of the same child
        """
        children = family.get_children()
        if children is None:
            return

        children_first_names = dict()
        people_ids = []
        people_ids.extend(children)

        for person_id in people_ids:
            peep = self._people.individuals[person_id]

            name = self._first_name_regex.search(peep.get_name())
            if peep.get_birth_date() is None:
                return
            if name is not None and peep.get_birth_date() is not None:
                children_first_names[name.group(
                    0), peep.get_birth_date()] = True

        if len(children_first_names) < len(people_ids):
            self._msgs.add_message(self.CLASS_IDENTIFIER,
                                   "US25",
                                   family.get_family_id(),
                                   "NA",
                                   "Children in a family must have unique first name and birthday")

    def _us16_validate_males_in_family_same_last_name(self, family):
        """US16 All males in a family have the same last name
        """
        children = family.get_children()
        if children is None:
            return

        male_last_names = dict()
        people_ids = []
        people_ids.extend(children)

        if family.get_husband_id() is not None:
            people_ids.append(family.get_husband_id())

        for person_id in people_ids:
            peep = self._people.individuals[person_id]

            if peep.get_gender() != "M":
                continue

            result = self._last_name_regex.search(peep.get_name())
            if result is not None:
                male_last_names[result.group(1)] = True

        if len(male_last_names) > 1:
            self._msgs.add_message(self.CLASS_IDENTIFIER,
                                   "US16",
                                   family.get_family_id(),
                                   "NA",
                                   "All males in a family must have the same last name")

    def _us15_validate_fewer_than_15_siblings(self, family):
        """US15 There should be fewer than 15 siblings in a family
        """
        children = family.get_children()
        if children is not None:
            if len(children) >= 15:
                self._msgs.add_message(self.CLASS_IDENTIFIER,
                                       "US15",
                                       family.get_family_id(),
                                       "NA",
                                       "There should be fewer than 15 siblings in a family")

    def _us26_validate_corresponding_entries(self, family):
        """US26 Corresponding entries(families)
        See US26 in people class for rest of US26 functionality
        """
        us_name = "US26"
        fam_id = family.get_family_id()
        husband_id = family.get_husband_id()
        wife_id = family.get_wife_id()
        child_ids = family.get_children()
        if husband_id is not None:
            husband = self._people.individuals[husband_id]
            if fam_id not in husband.get_spouse_of_families():
                self._msgs.add_message(self.CLASS_IDENTIFIER, us_name, fam_id, "NA",
                                       "corresponding spouse link missing for " + husband_id + " " + husband.get_name())
        if wife_id is not None:
            wife = self._people.individuals[wife_id]
            if fam_id not in wife.get_spouse_of_families():
                self._msgs.add_message(self.CLASS_IDENTIFIER, us_name, fam_id, "NA",
                                       "corresponding spouse link missing for " + wife_id + " " + wife.get_name())
        if child_ids is not None:
            for child_id in child_ids:
                child = self._people.individuals[child_id]
                if fam_id not in child.get_children_of_families():
                    self._msgs.add_message(self.CLASS_IDENTIFIER, us_name, fam_id, "NA",
                                           "corresponding child link missing for " + child_id + " " + child.get_name())

    def _us14_validate_less_than_5_multi_births(self, family):
        """US14 No more than 5 siblings born in a multiple birth in a family"""
        children = family.get_children()
        childbdays = {}

        for child_id in children:
            child = self._people.individuals[child_id]

            if child.get_birth_date() is None:
                continue

            value = childbdays.get(
                child.get_birth_date().date().isoformat(), None)
            if value is not None:
                childbdays[child.get_birth_date().date().isoformat()] = value + 1
            else:
                childbdays[child.get_birth_date().date().isoformat()] = 1
            for _, value in childbdays.items():
                if value > 5:
                    self._msgs.add_message(self.CLASS_IDENTIFIER,
                                           "US14",
                                           family.get_family_id(),
                                           "NA",
                                           "No more than five siblings should be born at the same time")
                    return

    def _us12_validate_parents_not_too_old(self, family):
        """US12 Validate that husband and wife are not too much older than children
        Returns:
            bool
        """

        children = set(family.get_children())

        if children:
            for child in children:
                # type: Person
                child = self._people.individuals[child]
                if family.get_husband_id() is not None:
                    # type: Person
                    husband = self._people.individuals[family.get_husband_id()]
                    if husband.get_is_alive() and (husband.get_age() - child.get_age() >= 80):
                        self._msgs.add_message(self.CLASS_IDENTIFIER,
                                               "US12",
                                               family.get_family_id(),
                                               "NA",
                                               "Father %s should be less than 80 years older than his child %s" %
                                               (family.get_husband_id(), child.get_person_id()))
                        return False
                if family.get_wife_id() is not None:
                    # type: Person
                    wife = self._people.individuals[family.get_wife_id()]

                    if wife.get_is_alive() and (wife.get_age() - child.get_age() >= 60):
                        self._msgs.add_message(self.CLASS_IDENTIFIER,
                                               "US12",
                                               family.get_family_id(),
                                               "NA",
                                               "Mother %s should be less than 60 years older than her child %s" %
                                               (family.get_wife_id(), child.get_person_id()))
                        return False

        return True

    def _us21_validate_correct_gender_roles(self, family):
        "US21 Validate that the husband and wife roles in family are assigned the correct gender"
        husband_ids = []
        wife_ids = []
        if family.get_husband_id() is not None:
            husband_ids.append(family.get_husband_id())
        for person_id in husband_ids:
            hus_spouse = self._people.individuals[person_id]

            if hus_spouse.get_gender() != "M":
                self._msgs.add_message(self.CLASS_IDENTIFIER,
                                       "US21",
                                       family.get_family_id(),
                                       "NA",
                                       "Father %s should be a Male" %
                                       (family.get_husband_id()))
        if family.get_wife_id() is not None:
            wife_ids.append(family.get_wife_id())
        for person_id in wife_ids:
            wif_spouse = self._people.individuals[person_id]

            if wif_spouse.get_gender() != "F":
                self._msgs.add_message(self.CLASS_IDENTIFIER,
                                       "US21",
                                       family.get_family_id(),
                                       "NA",
                                       "Mother %s should be a Female" %
                                       (family.get_wife_id()))
                return False
            return True