Source code for dae.variants.attributes

from __future__ import annotations

import enum
import logging
from typing import Any, Optional, Union

logger = logging.getLogger(__name__)

_VARIANT_TYPE_DISPLAY_NAME = {
    "invalid": "inv",
    "substitution": "sub",
    "insertion": "ins",
    "deletion": "del",
    "comp": "comp",
    "cnv_p": "cnv+",
    "cnv_m": "cnv-",
}

_ROLE_DISPLAY_NAME = {
    "maternal_grandmother": "Maternal Grandmother",
    "maternal_grandfather": "Maternal Grandfather",
    "paternal_grandmother": "Paternal Grandmother",
    "paternal_grandfather": "Paternal Grandfather",
    "mom": "Mom",
    "dad": "Dad",
    "parent": "Parent",
    "prb": "Proband",
    "sib": "Sibling",
    "child": "Child",
    "maternal_half_sibling": "Maternal Half Sibling",
    "paternal_half_sibling": "Paternal Half Sibling",
    "half_sibling": "Half Sibling",
    "maternal_aunt": "Maternal Aunt",
    "maternal_uncle": "Maternal Uncle",
    "paternal_aunt": "Paternal Aunt",
    "paternal_uncle": "Paternal Uncle",
    "maternal_cousin": "Maternal Cousin",
    "paternal_cousin": "Paternal Cousin",
    "step_mom": "Step Mom",
    "step_dad": "Step Dad",
    "spouse": "Spouse",
    "unknown": "Unknown",
}

_ROLE_SYNONYMS = {
    "maternal grandmother": "maternal_grandmother",
    "maternal grandfather": "maternal_grandfather",
    "paternal grandmother": "paternal_grandmother",
    "paternal grandfather": "paternal_grandfather",
    "mother": "mom",
    "father": "dad",
    "proband": "prb",
    "initially identified proband": "prb",
    "sibling": "sib",
    "younger sibling": "sib",
    "older sibling": "sib",
    "maternal half sibling": "maternal_half_sibling",
    "paternal half sibling": "paternal_half_sibling",
    # "half sibling": "half_sibling",
    "maternal aunt": "maternal_aunt",
    "maternal uncle": "maternal_uncle",
    "paternal aunt": "paternal_aunt",
    "paternal uncle": "paternal_uncle",
    "maternal cousin": "maternal_cousin",
    "paternal cousin": "paternal_cousin",
    "step mom": "step_mom",
    "step dad": "step_dad",
    "step mother": "step_mom",
    "step father": "step_dad",
}


[docs]class Role(enum.Enum): """Enumerator for a person's role in a pedigree.""" # pylint: disable=invalid-name maternal_grandmother = 1 maternal_grandfather = 1 << 1 paternal_grandmother = 1 << 2 paternal_grandfather = 1 << 3 mom = 1 << 4 dad = 1 << 5 parent = 1 << 6 prb = 1 << 7 sib = 1 << 8 child = 1 << 9 maternal_half_sibling = 1 << 10 paternal_half_sibling = 1 << 11 half_sibling = 1 << 12 maternal_aunt = 1 << 16 maternal_uncle = 1 << 17 paternal_aunt = 1 << 18 paternal_uncle = 1 << 19 maternal_cousin = 1 << 20 paternal_cousin = 1 << 21 step_mom = 1 << 22 step_dad = 1 << 23 spouse = 1 << 24 unknown = 0 @property def display_name(self) -> str: return _ROLE_DISPLAY_NAME[self.name] def __repr__(self) -> str: return self.name # return "{}: {:023b}".format(self.name, self.value) def __str__(self) -> str: return self.name
[docs] @staticmethod def from_name(name: Optional[Union[str, int]]) -> Role: """Construct and return a Role from it's string representation.""" if isinstance(name, Role): return name if isinstance(name, int): return Role.from_value(name) if isinstance(name, str): key = name.lower() if key in Role.__members__: return Role[key] if key in _ROLE_SYNONYMS: return Role[_ROLE_SYNONYMS[key]] logger.debug("unexpected role name: <%s>", name) return Role.unknown
[docs] @staticmethod def to_value(name: Optional[Union[str, int]]) -> int: role = Role.from_name(name) return role.value
[docs] @staticmethod def to_name(value: int) -> str: return Role(value).name
[docs] @staticmethod def from_value(val: int) -> Role: return Role(int(val))
[docs] @staticmethod def not_role(value: int) -> int: return (1 << 24) - 1 - value
[docs]class Sex(enum.Enum): """Enumerator for a person's sex.""" M = 1 F = 2 U = 0 # pylint: disable=invalid-name male = M female = F unspecified = U
[docs] @staticmethod def from_name(name: Optional[Union[int, str]]) -> Sex: """Construct and return person Sex from string.""" if name is None: return Sex.U if isinstance(name, Sex): return name if isinstance(name, int): return Sex.from_value(name) assert isinstance(name, str) name = name.lower() if name in set(["male", "m", "1"]): return Sex.male if name in set(["female", "f", "2"]): return Sex.female if name in set(["unspecified", "u", "0", "unknown"]): return Sex.unspecified raise ValueError(f"unexpected sex name: {name}")
[docs] @staticmethod def to_value(name: Optional[Union[int, str]]) -> int: sex = Sex.from_name(name) return sex.value
[docs] @staticmethod def to_name(value: int) -> str: return Sex(value).name
[docs] @staticmethod def from_value(val: int) -> Sex: return Sex(int(val))
def __repr__(self) -> str: return self.name def __str__(self) -> str: return self.name
[docs] def short(self) -> str: return self.name[0].upper()
def __lt__(self, other: Sex) -> bool: return bool(self.value < other.value) def __eq__(self, other: Any) -> bool: if not isinstance(other, Sex): return False return bool(self.value == other.value) def __hash__(self) -> int: return self.value
[docs]class Status(enum.Enum): """Enumerator for a person's status.""" # pylint: disable=invalid-name unaffected = 1 affected = 2 unspecified = 0
[docs] @staticmethod def from_name(name: Optional[Union[int, str]]) -> Status: """Construct and return person status from string.""" if name is None: return Status.unspecified if isinstance(name, Status): return name if isinstance(name, int): return Status.from_value(name) assert isinstance(name, str) name = name.lower() if name in set(["unaffected", "1", "false"]): return Status.unaffected if name in set(["affected", "2", "true"]): return Status.affected if name in set(["unspecified", "-", "0", "unknown"]): return Status.unspecified raise ValueError("unexpected status type: " + name)
[docs] @staticmethod def to_value(name: Optional[Union[int, str]]) -> int: status = Status.from_name(name) return status.value
[docs] @staticmethod def to_name(value: int) -> str: return Status(value).name
[docs] @staticmethod def from_value(val: int) -> Status: return Status(int(val))
def __repr__(self) -> str: return self.name def __str__(self) -> str: return self.name
[docs] def short(self) -> str: return self.name[0].upper()
def __ge__(self, other: Status) -> bool: if self.__class__ is other.__class__: return bool(self.value >= other.value) return NotImplemented def __gt__(self, other: Status) -> bool: if self.__class__ is other.__class__: return bool(self.value > other.value) return NotImplemented def __le__(self, other: Status) -> bool: if self.__class__ is other.__class__: return bool(self.value <= other.value) return NotImplemented def __lt__(self, other: Status) -> bool: if self.__class__ is other.__class__: return bool(self.value < other.value) return NotImplemented
[docs]class Inheritance(enum.Enum): """Enumerator for variant inheritance type.""" # pylint: disable=invalid-name reference = 1 mendelian = 1 << 1 denovo = 1 << 2 possible_denovo = 1 << 3 omission = 1 << 4 possible_omission = 1 << 5 other = 1 << 6 missing = 1 << 7 unknown = 1 << 8
[docs] @staticmethod def from_name(name: str) -> Inheritance: assert ( name in Inheritance.__members__ ), f"Inheritance type {name} does not exist!" return Inheritance[name]
[docs] @staticmethod def from_value(value: int) -> Inheritance: return Inheritance(value)
def __repr__(self) -> str: return self.name def __str__(self) -> str: return self.name
[docs]def bitmask2inheritance(bitmask: int) -> set[Inheritance]: """Convert a bitmask to set of inheritance.""" all_inheritance = set([ Inheritance.reference, Inheritance.mendelian, Inheritance.denovo, Inheritance.omission, Inheritance.possible_denovo, Inheritance.possible_omission, Inheritance.other, Inheritance.missing, Inheritance.unknown, ]) result: set[Inheritance] = set() for inh in all_inheritance: if bitmask & inh.value: result.add(inh) return result
[docs]class GeneticModel(enum.Enum): # pylint: disable=invalid-name autosomal = 1 autosomal_broken = 2 pseudo_autosomal = 3 X = 4 X_broken = 5
[docs]class TransmissionType(enum.IntEnum): # pylint: disable=invalid-name unknown = 0 transmitted = 1 denovo = 2
# class VariantType(enum.Enum): # invalid = 0 # substitution = 1 # insertion = 1 << 1 # deletion = 1 << 2 # comp = 1 << 3 # indel = insertion | deletion | comp # cnv_p = 1 << 4 # cnv_m = 1 << 5 # cnv = cnv_p | cnv_m # tandem_repeat = 1 << 6 # tandem_repeat_ins = tandem_repeat | insertion # tandem_repeat_del = tandem_repeat | deletion # def __and__(self, other): # assert isinstance(other, VariantType), type(other) # return self.value & other.value # def __or__(self, other): # assert isinstance(other, VariantType) # return self.value | other.value # def __ior__(self, other): # assert isinstance(other, VariantType) # return VariantType(self.value | other.value) # @staticmethod # def from_name(name): # name = name.lower().strip() # if name == "sub" or name == "substitution": # return VariantType.substitution # elif name == "ins" or name == "insertion": # return VariantType.insertion # elif name == "del" or name == "deletion": # return VariantType.deletion # elif name == "comp" or name == "complex": # return VariantType.comp # elif name == "cnv_p" or name == "cnv+": # return VariantType.cnv_p # elif name == "cnv_m" or name == "cnv-": # return VariantType.cnv_m # elif name.lower() in set(["tr", "tandem_repeat"]): # return VariantType.tandem_repeat # raise ValueError(f"unexpected variant type: {name}") # @staticmethod # def from_cshl_variant(variant): # # FIXME: Change logic to use entire string # if variant is None: # return VariantType.invalid # vt = variant[0:2] # if vt == "su": # return VariantType.substitution # elif vt == "in": # return VariantType.insertion # elif vt == "de": # return VariantType.deletion # elif vt == "co": # return VariantType.comp # elif vt == "TR": # return VariantType.tandem_repeat # elif variant == "CNV+": # return VariantType.cnv_p # elif variant == "CNV-": # return VariantType.cnv_m # else: # raise ValueError(f"unexpected variant type: {variant}") # @staticmethod # def from_value(value): # if value is None: # return None # return VariantType(value) # @staticmethod # def is_cnv(vt): # if vt is None: # return False # assert isinstance(vt, VariantType) # return vt & VariantType.cnv # @staticmethod # def is_tr(vt): # if vt is None: # return False # assert isinstance(vt, VariantType) # return vt & VariantType.tandem_repeat # def __repr__(self) -> str: # return _VARIANT_TYPE_DISPLAY_NAME.get(self.name) or self.name # def __str__(self) -> str: # return _VARIANT_TYPE_DISPLAY_NAME.get(self.name) or self.name # def __lt__(self, other): # return self.value < other.value