Source code for dae.pedigrees.family_role_builder

"""Family members' roles with respect to the proband."""

import logging
from collections import defaultdict
from typing import Optional

from dae.pedigrees.family import Family, Person
from dae.variants.attributes import Role, Sex, Status

logger = logging.getLogger(__name__)


[docs]class Mating: """Class to represent a mating unit.""" def __init__(self, mom_id: str, dad_id: Optional[str]) -> None: self.mom_id = mom_id self.dad_id = dad_id self.mating_id = Mating.build_id(mom_id, dad_id) self.children: set[str] = set()
[docs] @staticmethod def build_id(mom_id: Optional[str], dad_id: Optional[str]) -> str: return f"{mom_id},{dad_id}"
[docs] @staticmethod def parents_id(person: Person) -> str: assert person.mom_id is None or isinstance(person.mom_id, str), person assert person.dad_id is None or isinstance(person.dad_id, str), person return Mating.build_id(person.mom_id, person.dad_id)
def __repr__(self) -> str: return f"({self.mating_id}> Mom: {self.mom_id}, Dad: {self.dad_id})"
[docs]class FamilyRoleBuilder: # pylint: disable=too-few-public-methods """Build roles of family members.""" def __init__(self, family: Family) -> None: self.family = family self.family_matings = self._build_family_matings() self.members_matings = self._build_members_matings()
[docs] def build_roles(self) -> None: """Build roles of all family members.""" proband = self._get_family_proband() if proband is None: self._assign_unknown_roles() return assert proband is not None self._set_person_role(proband, Role.prb) self._assign_roles_children(proband) self._assign_roles_mates(proband) self._assign_roles_parents(proband) self._assign_roles_siblings(proband) self._assign_roles_paternal(proband) self._assign_roles_maternal(proband) self._assign_roles_step_parents_and_half_siblings(proband) self._assign_unknown_roles()
@classmethod def _set_person_role(cls, person: Person, role: Role) -> None: assert isinstance(person, Person) assert isinstance(role, Role) if person.role is None or person.role == Role.unknown: if role != person.role: logger.info( "changing role for %s from %s to %s", person, person.role, role) # pylint: disable=protected-access person._role = role person._attributes["role"] = role def _get_family_proband(self) -> Optional[Person]: probands = self.family.get_members_with_roles([Role.prb]) if len(probands) > 0: return probands[0] for person in self.family.full_members: is_proband = person.get_attr("proband", False) # assert isinstance(is_proband, bool), is_proband if is_proband: return person affected = self.family.get_members_with_statuses([Status.affected]) affected = [p for p in affected if p.has_parent()] if len(affected) > 0: return affected[0] return None def _build_family_matings(self) -> dict[str, Mating]: matings = {} for person_id, person in self.family.persons.items(): if person.has_parent(): parents_mating_id = Mating.parents_id(person) if parents_mating_id not in matings: parents = Mating(person.mom_id, person.dad_id) matings[parents_mating_id] = parents parents_mating = matings.get(parents_mating_id) assert parents_mating is not None parents_mating.children.add(person_id) return matings def _build_members_matings(self) -> dict[str, set[str]]: members_matings = defaultdict(set) for mating_id, mating in self.family_matings.items(): if mating.mom_id is not None: members_matings[mating.mom_id].add(mating_id) if mating.dad_id is not None: members_matings[mating.dad_id].add(mating_id) return members_matings def _assign_roles_children(self, proband: Person) -> None: for mating_id in self.members_matings[proband.person_id]: mating = self.family_matings[mating_id] for child_id in mating.children: child = self.family.persons[child_id] self._set_person_role(child, Role.child) def _assign_roles_mates(self, proband: Person) -> None: for mating_id in self.members_matings[proband.person_id]: mating = self.family_matings[mating_id] if ( mating.dad_id is not None and mating.dad_id != proband.person_id ): person = self.family.persons[mating.dad_id] self._set_person_role(person, Role.spouse) elif mating.mom_id is not None and \ mating.mom_id != proband.person_id: person = self.family.persons[mating.mom_id] self._set_person_role(person, Role.spouse) def _assign_roles_parents(self, proband: Person) -> None: if not proband.has_parent(): return if proband.mom is not None: self._set_person_role(proband.mom, Role.mom) if proband.dad is not None: self._set_person_role(proband.dad, Role.dad) def _assign_roles_siblings(self, proband: Person) -> None: if not proband.has_parent(): return parents_mating = self.family_matings[Mating.parents_id(proband)] for person_id in parents_mating.children: if person_id != proband.person_id: person = self.family.persons[person_id] self._set_person_role(person, Role.sib) def _assign_roles_paternal(self, proband: Person) -> None: if proband.dad is None or not proband.dad.has_parent(): return dad = proband.dad if dad.dad is not None: self._set_person_role(dad.dad, Role.paternal_grandfather) if dad.mom is not None: self._set_person_role(dad.mom, Role.paternal_grandmother) grandparents_mating_id = Mating.parents_id(dad) grandparents_mating = self.family_matings[grandparents_mating_id] for person_id in grandparents_mating.children: person = self.family.persons[person_id] if person.role is not None and person.role != Role.unknown: continue if person.sex == Sex.M: self._set_person_role(person, Role.paternal_uncle) if person.sex == Sex.F: self._set_person_role(person, Role.paternal_aunt) for person_mating_id in self.members_matings[person.person_id]: person_mating = self.family_matings[person_mating_id] for cousin_id in person_mating.children: cousin = self.family.persons[cousin_id] self._set_person_role(cousin, Role.paternal_cousin) def _assign_roles_maternal(self, proband: Person) -> None: if proband.mom is None or not proband.mom.has_parent(): return mom = proband.mom if mom.dad is not None: self._set_person_role(mom.dad, Role.maternal_grandfather) if mom.mom is not None: self._set_person_role(mom.mom, Role.maternal_grandmother) grandparents_mating_id = Mating.parents_id(mom) grandparents_mating = self.family_matings[grandparents_mating_id] for person_id in grandparents_mating.children: person = self.family.persons[person_id] if person.role is not None and person.role != Role.unknown: continue if person.sex == Sex.M: self._set_person_role(person, Role.maternal_uncle) if person.sex == Sex.F: self._set_person_role(person, Role.maternal_aunt) for person_mating_id in self.members_matings[person.person_id]: person_mating = self.family_matings[person_mating_id] for cousin_id in person_mating.children: cousin = self.family.persons[cousin_id] self._set_person_role(cousin, Role.maternal_cousin) def _find_parent_matings(self, parent: Person) -> list[Mating]: matings = [] for mating in self.family_matings.values(): if parent.sex == Sex.male: if mating.dad_id == parent.person_id: matings.append(mating) else: if mating.mom_id == parent.person_id: matings.append(mating) return matings def _assign_roles_step_parents_and_half_siblings( self, proband: Person, ) -> None: if proband.mom is None or proband.dad is None: return if proband.mom is not None: mom_mates = filter( lambda x: x.dad_id != proband.dad.person_id, # type: ignore self._find_parent_matings(proband.mom), ) for mating in mom_mates: if mating.dad_id is None: continue step_dad = self.family.persons[mating.dad_id] self._set_person_role(step_dad, Role.step_dad) maternal_halfsiblings_ids = mating.children for halfsibling_id in maternal_halfsiblings_ids: halfsibling = self.family.persons[halfsibling_id] self._set_person_role( halfsibling, Role.maternal_half_sibling, ) if proband.dad is not None: dad_mates = filter( lambda x: x.mom_id != proband.mom.person_id, # type: ignore self._find_parent_matings(proband.dad), ) for mating in dad_mates: if mating.mom_id is None: continue step_mom = self.family.persons[mating.mom_id] self._set_person_role(step_mom, Role.step_mom) paternal_halfsiblings_ids = mating.children for halfsibling_id in paternal_halfsiblings_ids: halfsibling = self.family.persons[halfsibling_id] self._set_person_role( halfsibling, Role.paternal_half_sibling, ) def _assign_unknown_roles(self) -> None: for person in self.family.persons.values(): if person.role is None: self._set_person_role(person, Role.unknown)