Source code for ppsc.v202._base

import typing

import phenopackets as pp202
from google.protobuf.message import Message

from .._api import MessageMixin
from .._timestamp import Timestamp
from ..parse import extract_message_scalar, extract_pb_message_scalar, extract_oneof_scalar, extract_pb_oneof_scalar


[docs] class OntologyClass(MessageMixin): """ `OntologyClass` represents classes (terms) from ontologies, and is used in many places throughout the Phenopacket standard. It is a simple, two element message that represents the `identifier` and the `label` of an ontology class. >>> from ppsc.v202 import OntologyClass >>> oc = OntologyClass(id='HP:0001250', label='Seizure') >>> oc.id 'HP:0001250' >>> oc.label 'Seizure' :param id: a `str` with a CURIE-style identifier (e.g. `HP:0001250`). :param label: a `str` with a human-readable class name (e.g. `Seizure`). """ def __init__( self, id: str, label: str, ): self._id = id self._label = label @property def id(self) -> str: """ Get a `str` with the ontology class identifier. """ return self._id @property def label(self) -> str: """ Get a `str` with the human-readable class name. """ return self._label
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'id', 'label'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'id', 'label'
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return OntologyClass( id=values['id'], label=values['label'], ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.OntologyClass( id=self._id, label=self._label, )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.OntologyClass
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.OntologyClass): return OntologyClass( id=msg.id, label=msg.label, ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, OntologyClass) \ and self._id == other._id \ and self._label == other._label def __repr__(self): return f'OntologyClass(id={self._id}, label={self._label})'
[docs] class ExternalReference(MessageMixin): def __init__( self, id: typing.Optional[str] = None, reference: typing.Optional[str] = None, description: typing.Optional[str] = None, ): self._id = id self._reference = reference self._description = description @property def id(self) -> str: return self._id @id.setter def id(self, value: str): self._id = value @property def reference(self) -> str: return self._reference @reference.setter def reference(self, value: str): self._reference = value @reference.deleter def reference(self): self._reference = None @property def description(self) -> str: return self._description @description.setter def description(self, value: str): self._description = value @description.deleter def description(self): self._description = None
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'id', 'reference', 'description'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return ()
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return ExternalReference( id=MessageMixin._extract_optional_field('id', values), reference=MessageMixin._extract_optional_field('reference', values), description=MessageMixin._extract_optional_field('description', values), ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.ExternalReference( id=None if self._id is None else self._id, reference=None if self._reference is None else self._reference, description=None if self._description is None else self._description, )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.ExternalReference
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.ExternalReference): return ExternalReference( id=None if msg.id == '' else msg.id, reference=None if msg.reference == '' else msg.reference, description=None if msg.description == '' else msg.description, ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, ExternalReference) \ and self._id == other._id \ and self._reference == other._reference \ and self._description == other._description def __repr__(self): return f'ExternalReference(id={self._id}, reference={self._reference}, description={self._description})'
[docs] class Evidence(MessageMixin): def __init__( self, evidence_code: OntologyClass, reference: typing.Optional[ExternalReference] = None, ): self._evidence_code = evidence_code self._reference = reference @property def evidence_code(self) -> OntologyClass: return self._evidence_code @evidence_code.setter def evidence_code(self, value: OntologyClass): self._evidence_code = value @property def reference(self) -> typing.Optional[ExternalReference]: return self._reference @reference.setter def reference(self, value: typing.Optional[ExternalReference]): self._reference = value @reference.deleter def reference(self): self._reference = None
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'evidence_code', 'reference'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'evidence_code',
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return Evidence( evidence_code=extract_message_scalar('evidence_code', OntologyClass, values), reference=extract_message_scalar('reference', ExternalReference, values), ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.Evidence( evidence_code=self._evidence_code.to_message(), reference=self._reference.to_message(), )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.Evidence
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.Evidence): return Evidence( evidence_code=extract_pb_message_scalar('evidence_code', OntologyClass, msg), reference=extract_pb_message_scalar('reference', ExternalReference, msg), ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, Evidence) \ and self._evidence_code == other._evidence_code \ and self._reference == other._reference def __repr__(self): return f'Evidence(evidence_code={self._evidence_code}, reference={self._reference})'
[docs] class GestationalAge(MessageMixin): def __init__( self, weeks: int, days: typing.Optional[int] = None, ): # TODO: validate self._weeks = weeks self._days = days @property def weeks(self) -> int: return self._weeks @weeks.setter def weeks(self, value: int): self._weeks = value @property def days(self) -> typing.Optional[int]: return self._days @days.setter def days(self, value: typing.Optional[int]): self._days = value @days.deleter def days(self): self._days = None
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'weeks', 'days',
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'weeks',
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): weeks = values['weeks'] days = values['days'] if 'days' in values else None return GestationalAge( weeks=weeks, days=days, ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.GestationalAge( weeks=self._weeks, days=self._days, )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.GestationalAge
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.GestationalAge): return GestationalAge( weeks=msg.weeks, days=msg.days, ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, GestationalAge) \ and self._weeks == other._weeks \ and self._days == other._days def __repr__(self): return f'GestationalAge(weeks={self._weeks}, days={self._days})'
[docs] class Age(MessageMixin): def __init__( self, iso8601duration: str, ): self._iso8601duration = iso8601duration @property def iso8601duration(self) -> str: return self._iso8601duration @iso8601duration.setter def iso8601duration(self, value: str): self._iso8601duration = value
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'iso8601duration',
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'iso8601duration',
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return Age(iso8601duration=values['iso8601duration']) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.Age( iso8601duration=self._iso8601duration, )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.Age
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.Age): return Age( iso8601duration=msg.iso8601duration, ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, Age) \ and self._iso8601duration == other._iso8601duration def __repr__(self): return f'Age(iso8601duration={self._iso8601duration})'
[docs] class AgeRange(MessageMixin): def __init__( self, start: Age, end: Age, ): self._start = start self._end = end @property def start(self): return self._start @start.setter def start(self, value: Age): self._start = value @property def end(self) -> Age: return self._end @end.setter def end(self, value: Age): self._end = value
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'start', 'end'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'start', 'end'
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return AgeRange( start=Age.from_dict(values['start']), end=Age.from_dict(values['end']), ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.AgeRange( start=self._start.to_message(), end=self._end.to_message(), )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.AgeRange
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.AgeRange): return AgeRange( start=extract_pb_message_scalar('start', Age, msg), end=extract_pb_message_scalar('end', Age, msg), ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, AgeRange) \ and self._start == other._start \ and self._end == other._end def __repr__(self): return f'AgeRange(start={self._start}, end={self._end})'
[docs] class TimeInterval(MessageMixin): def __init__( self, start: Timestamp, end: Timestamp, ): self._start = start self._end = end @property def start(self) -> Timestamp: return self._start @start.setter def start(self, value: Timestamp): self._start = value @property def end(self) -> Timestamp: return self._end @end.setter def end(self, value: Timestamp): self._end = value
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'start', 'end'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'start', 'end'
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return TimeInterval( start=Timestamp.from_str(values['start']), end=Timestamp.from_str(values['end']), ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: return pp202.TimeInterval( start=self._start.to_message(), end=self._end.to_message(), )
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.TimeInterval
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.TimeInterval): return TimeInterval( start=extract_pb_message_scalar('start', Timestamp, msg), end=extract_pb_message_scalar('end', Timestamp, msg), ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, TimeInterval) \ and self._start == other._start \ and self._end == other._end def __repr__(self): return f'TimeInterval(start={self._start}, end={self._end})'
[docs] class TimeElement(MessageMixin): """ TODO: better description """ _ONEOF_ELEMENT = { 'gestational_age': GestationalAge, 'age': Age, 'age_range': AgeRange, 'ontology_class': OntologyClass, 'timestamp': Timestamp, 'interval': TimeInterval, } def __init__( self, element: typing.Union[GestationalAge, Age, AgeRange, OntologyClass, Timestamp, TimeInterval] ): self._element = element @property def element(self) -> typing.Union[GestationalAge, Age, AgeRange, OntologyClass, Timestamp, TimeInterval]: return self._element @property def age(self) -> typing.Optional[Age]: return self._element if isinstance(self._element, Age) else None @age.setter def age(self, value: Age): self._element = value @property def age_range(self) -> typing.Optional[AgeRange]: return self._element if isinstance(self._element, AgeRange) else None @age_range.setter def age_range(self, value: AgeRange): self._element = value @property def ontology_class(self) -> OntologyClass: return self._element if isinstance(self._element, OntologyClass) else None @ontology_class.setter def ontology_class(self, value: OntologyClass): self._element = value @property def timestamp(self) -> Timestamp: return self._element if isinstance(self._element, Timestamp) else None @timestamp.setter def timestamp(self, value: Timestamp): self._element = value @property def interval(self) -> TimeInterval: return self._element if isinstance(self._element, TimeInterval) else None @interval.setter def interval(self, value: TimeInterval): self._element = value @property def gestational_age(self) -> typing.Optional[GestationalAge]: return self._element if isinstance(self._element, GestationalAge) else None @gestational_age.setter def gestational_age(self, value: GestationalAge): self._element = value
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'gestational_age', 'age', 'age_range', 'ontology_class', 'timestamp', 'interval'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: raise NotImplementedError('Should not be called!')
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if any(field in values for field in cls._ONEOF_ELEMENT): return TimeElement( element=extract_oneof_scalar(cls._ONEOF_ELEMENT, values) ) else: raise ValueError( f'Missing one of required fields: ' f'`{"|".join(cls._ONEOF_ELEMENT)}`: {values}' )
[docs] def to_message(self) -> Message: te = pp202.TimeElement() val = self._element.to_message() if isinstance(self._element, Age): te.age.CopyFrom(val) elif isinstance(self._element, AgeRange): te.age_range.CopyFrom(val) elif isinstance(self._element, OntologyClass): te.ontology_class.CopyFrom(val) elif isinstance(self._element, Timestamp): te.timestamp.CopyFrom(val) elif isinstance(self._element, TimeInterval): te.interval.CopyFrom(val) elif isinstance(self._element, GestationalAge): te.gestational_age.CopyFrom(val) else: raise ValueError('Bug') return te
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.TimeElement
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, cls.message_type()): return TimeElement( element=extract_pb_oneof_scalar( 'element', { 'gestational_age': GestationalAge, 'age': Age, 'age_range': AgeRange, 'ontology_class': OntologyClass, 'timestamp': Timestamp, 'interval': TimeInterval, }, msg) ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, TimeElement) and self._element == other._element def __repr__(self): return f'TimeElement(element={self._element})'
[docs] class Procedure(MessageMixin): def __init__( self, code: OntologyClass, body_site: typing.Optional[OntologyClass] = None, performed: typing.Optional[TimeElement] = None, ): self._code = code self._body_site = body_site self._performed = performed @property def code(self) -> OntologyClass: return self._code @code.setter def code(self, value: OntologyClass): self._code = value @property def body_site(self) -> typing.Optional[OntologyClass]: return self._body_site @body_site.setter def body_site(self, value: typing.Optional[OntologyClass]): self._body_site = value @body_site.deleter def body_site(self): self._body_site = None @property def performed(self) -> typing.Optional[TimeElement]: return self._performed @performed.setter def performed(self, value: typing.Optional[TimeElement]): self._performed = value @performed.deleter def performed(self): self._performed = None
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'code', 'body_site', 'performed'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'code',
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return Procedure( code=extract_message_scalar('code', OntologyClass, values), body_site=extract_message_scalar('body_site', OntologyClass, values), performed=extract_message_scalar('performed', TimeElement, values), ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: procedure = pp202.Procedure( code=self._code.to_message(), ) if self._body_site is not None: procedure.body_site.CopyFrom(self._body_site.to_message()) if self._performed is not None: procedure.performed.CopyFrom(self._performed.to_message()) return procedure
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.Procedure
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.Procedure): return Procedure( code=extract_pb_message_scalar('code', OntologyClass, msg), body_site=extract_pb_message_scalar('body_site', OntologyClass, msg), performed=extract_pb_message_scalar('performed', TimeElement, msg), ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, Procedure) \ and self._code == other._code \ and self._body_site == other._body_site \ and self._performed == other._performed def __repr__(self): return f'Procedure(code={self._code}, body_site={self._body_site}, performed={self._performed})'
[docs] class File(MessageMixin): def __init__( self, uri: str, individual_to_file_identifiers: typing.Optional[typing.Mapping[str, str]] = None, file_attributes: typing.Optional[typing.Mapping[str, str]] = None, ): self._uri = uri self._individual_to_file_identifiers = dict() \ if individual_to_file_identifiers is None \ else dict(individual_to_file_identifiers) self._file_attributes = dict() if file_attributes is None else dict(file_attributes) @property def uri(self) -> str: return self._uri @uri.setter def uri(self, value: str): self._uri = value @property def individual_to_file_identifiers(self) -> typing.MutableMapping[str, str]: return self._individual_to_file_identifiers @property def file_attributes(self) -> typing.MutableMapping[str, str]: return self._file_attributes
[docs] @staticmethod def field_names() -> typing.Iterable[str]: return 'uri', 'individual_to_file_identifiers', 'file_attributes'
[docs] @classmethod def required_fields(cls) -> typing.Sequence[str]: return 'uri',
[docs] @classmethod def from_dict(cls, values: typing.Mapping[str, typing.Any]): if cls._all_required_fields_are_present(values): return File( uri=values['uri'], individual_to_file_identifiers=values['individual_to_file_identifiers'], file_attributes=values['file_attributes'], ) else: cls._complain_about_missing_field(values)
[docs] def to_message(self) -> Message: file = pp202.File(uri=self._uri) for k, v in self._individual_to_file_identifiers.items(): file.individual_to_file_identifiers[k] = v for k, v in self._file_attributes.items(): file.file_attributes[k] = v return file
[docs] @classmethod def message_type(cls) -> typing.Type[Message]: return pp202.File
[docs] @classmethod def from_message(cls, msg: Message): if isinstance(msg, pp202.File): return File( uri=msg.uri, individual_to_file_identifiers=msg.individual_to_file_identifiers, file_attributes=msg.file_attributes, ) else: cls.complain_about_incompatible_msg_type(msg)
def __eq__(self, other): return isinstance(other, File) and self._uri == other._uri \ and self._individual_to_file_identifiers == other._individual_to_file_identifiers \ and self._file_attributes == other._file_attributes def __repr__(self): return 'File(' \ f'uri={self._uri},' \ f' individual_to_file_identifiers={self._individual_to_file_identifiers},' \ f' file_attributes={self._file_attributes})'