importabcimporttypingfromhpotk.modelimportIdentified,TermIdfromhpotk.ontologyimportMinimalOntologyfromhpotk.constants.hpo.baseimportPHENOTYPIC_ABNORMALITYfromhpotk.utilimportvalidate_instancefrom._modelimportValidationResult,ValidationResults,ValidationLevel,RuleValidatorfrom._utilimportSimpleFeatureT=typing.TypeVar('T',SimpleFeature,TermId)classBaseOntologyRuleValidator(RuleValidator,metaclass=abc.ABCMeta):def__init__(self,hpo:MinimalOntology):self._hpo=validate_instance(hpo,MinimalOntology,'hpo')def_primary_term_id(self,feature:T)->typing.Optional[T]:""" Update the term ID of the `feature` to the primary term ID in case the current term ID is obsolete. """ifisinstance(feature,SimpleFeature):current_id=self._hpo.get_term(feature.identifier)ifcurrent_idisNone:returnNoneifcurrent_id.identifier!=feature.identifier:# Update to the primary term ID.feature.identifier=current_id.identifierreturnfeatureelifisinstance(feature,TermId):current=self._hpo.get_term(feature)returnNoneifcurrentisNoneelsecurrent.identifierelse:raiseValueError(f'feature must be either a `TermId` or `SimpleFeature` but was {type(feature)}')
[docs]classAnnotationPropagationValidator(BaseOntologyRuleValidator):""" Validator to check that a sequence of terms, when interpreted together, does not violate the annotation propagation rule. More formally, the terms must not satisfy the following: * terms must not contain a present term and its present or excluded ancestor * terms must not contain an excluded term and its excluded ancestor Violation of annotation propagation rule produces an :class:`ValidationLevel.ERROR`. The validator replaces obsolete term IDs with the current term IDs before performing the validation. :param ontology: HPO represented as :class:`hpotk.ontology.MinimalOntology`. """def__init__(self,ontology:MinimalOntology):super().__init__(ontology)
[docs]defvalidate(self,items:typing.Sequence[typing.Union[Identified,TermId]])->ValidationResults:stateful_features:typing.Collection[SimpleFeature]={self._primary_term_id(self._extract_stateful_feature(item))foriteminitemsifitemisnotNone}results=[]forfeatureinstateful_features:iffeature.is_present:# A present feature cannot coexist with a present or excluded ancestor.forancinself._hpo.graph.get_ancestors(feature):ifany(anc==sf.identifierforsfinstateful_features):current_term=self._hpo.get_term(feature.identifier)term=self._hpo.get_term(anc)results.append(ValidationResult(level=ValidationLevel.ERROR,category='annotation_propagation',message=f'Terms should not contain both present 'f'{current_term.name} [{current_term.identifier.value}] 'f'and its present or excluded ancestor 'f'{term.name} [{term.identifier.value}]'))else:# An excluded feature cannot coexist with an excluded ancestor.forancinself._hpo.graph.get_ancestors(feature):# We only check excluded ancestors here since presence of an ancestor is allowed# for an excluded feature.ifany(anc==sf.identifierforsfinstateful_featuresifsf.is_excluded):current_term=self._hpo.get_term(feature.identifier)term=self._hpo.get_term(anc)results.append(ValidationResult(level=ValidationLevel.ERROR,category='annotation_propagation',message=f'Terms should not contain both excluded 'f'{current_term.name} [{current_term.identifier.value}] 'f'and its present or excluded ancestor 'f'{term.name} [{term.identifier.value}]'))returnValidationResults(results)
[docs]classPhenotypicAbnormalityValidator(BaseOntologyRuleValidator):""" Validator for checking that the term is a phenotypic abnormality (a descendant of `Phenotypic abnormality <https://hpo.jax.org/app/browse/term/HP:0000118>`_ [HP:0000118]). Presence of a term that is not a descendant of Phenotypic abnormality is a :class:`ValidationLevel.WARNING`. The validator replaces obsolete term IDs with the current term IDs before performing the validation. :param ontology: HPO represented as :class:`hpotk.ontology.MinimalOntology`. """def__init__(self,ontology:MinimalOntology):super().__init__(ontology)
[docs]defvalidate(self,items:typing.Sequence[typing.Union[Identified,TermId]])->ValidationResults:results=[]foriteminitems:primary=self._primary_term_id(self._extract_stateful_feature(item))ifprimaryisNone:# Unable to get the primary term ID. Handling items with obsolete IDs is not the responsibility# of this validatorcontinueifnotany(PHENOTYPIC_ABNORMALITY==ancforancinself._hpo.graph.get_ancestors(primary)):item=self._hpo.get_term(primary.identifier)results.append(ValidationResult(level=ValidationLevel.WARNING,category='phenotypic_abnormality_descendant',message=f'{item.name} [{item.identifier.value}] 'f'is not a descendant of Phenotypic abnormality [{PHENOTYPIC_ABNORMALITY.value}]'))returnValidationResults(results)
[docs]classObsoleteTermIdsValidator(BaseOntologyRuleValidator):""" `ObsoleteTermIdsValidator` points out usage of obsolete term ids in `items`. Presence of an obsolete term ID is a :class:`ValidationLevel.WARNING`. :param ontology: HPO represented as :class:`hpotk.ontology.MinimalOntology`. """def__init__(self,ontology:MinimalOntology):super().__init__(ontology)
[docs]defvalidate(self,items:typing.Sequence[typing.Union[Identified,TermId]])->ValidationResults:results=[]foriteminitems:sf=self._extract_stateful_feature(item)current=sf.identifier# cache the ID since _primary_term_id can update in place.primary=self._primary_term_id(sf)ifprimary.identifier!=current:current_term=self._hpo.get_term(sf.identifier)results.append(ValidationResult(level=ValidationLevel.WARNING,category='obsolete_term_id_is_used',message=f'Using the obsolete {current.value} instead of {primary.identifier.value} 'f'for {current_term.name}'))returnValidationResults(results)