From 150289e97180ed43bd2f5cbe9c87ce2bef95743a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20Jer=C5=A1e?= Date: Mon, 18 Mar 2024 10:40:17 +0100 Subject: [PATCH] Add variants related models --- docs/CHANGELOG.rst | 1 + src/resdk/query.py | 4 +- src/resdk/resolwe.py | 9 ++++ src/resdk/resources/__init__.py | 9 ++++ src/resdk/resources/sample.py | 12 ++++- src/resdk/resources/variants.py | 94 +++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/resdk/resources/variants.py diff --git a/docs/CHANGELOG.rst b/docs/CHANGELOG.rst index cf8b2f16..7c32ecf4 100644 --- a/docs/CHANGELOG.rst +++ b/docs/CHANGELOG.rst @@ -16,6 +16,7 @@ Changed Added ----- - Add ``restart`` method to the ``Data`` resource +- Add variants related models Fixed ----- diff --git a/src/resdk/query.py b/src/resdk/query.py index 3a0ceb97..4d86a831 100644 --- a/src/resdk/query.py +++ b/src/resdk/query.py @@ -175,7 +175,9 @@ def _add_filter(self, filter_): """Add filtering parameters.""" for key, value in filter_.items(): # 'sample' is called 'entity' in the backend. - key = key.replace("sample", "entity") + if not key.startswith("variant_calls__"): + key = key.replace("sample", "entity") + print("Adding filter", key, value) value = self._dehydrate_resources(value) if self._non_string_iterable(value): value = ",".join(map(str, value)) diff --git a/src/resdk/resolwe.py b/src/resdk/resolwe.py index 63268dc1..4a897fb3 100644 --- a/src/resdk/resolwe.py +++ b/src/resdk/resolwe.py @@ -47,6 +47,10 @@ Relation, Sample, User, + Variant, + VariantAnnotation, + VariantCall, + VariantExperiment, ) from .resources.base import BaseResource from .resources.kb import Feature, Mapping @@ -114,6 +118,10 @@ class Resolwe: resource_query_mapping = { AnnotationField: "annotation_field", AnnotationValue: "annotation_value", + Variant: "variant", + VariantAnnotation: "variant_annotation", + VariantExperiment: "variant_experiment", + VariantCall: "variant_calls", Data: "data", Collection: "collection", Sample: "sample", @@ -126,6 +134,7 @@ class Resolwe: Mapping: "mapping", Geneset: "geneset", Metadata: "metadata", + Variant: "variant", } # Map ResolweQuery name to it's slug_field slug_field_mapping = { diff --git a/src/resdk/resources/__init__.py b/src/resdk/resources/__init__.py index 061a7964..d05d38cb 100644 --- a/src/resdk/resources/__init__.py +++ b/src/resdk/resources/__init__.py @@ -54,6 +54,10 @@ :members: :inherited-members: +.. autoclass:: resdk.resources.Variants + :members: + :inherited-members: + .. autoclass:: resdk.resources.User :members: :inherited-members: @@ -102,6 +106,7 @@ from .relation import Relation from .sample import Sample from .user import Group, User +from .variants import Variant, VariantAnnotation, VariantCall, VariantExperiment __all__ = ( "AnnotationField", @@ -117,4 +122,8 @@ "Process", "Relation", "User", + "Variant", + "VariantAnnotation", + "VariantCall", + "VariantExperiment", ) diff --git a/src/resdk/resources/sample.py b/src/resdk/resources/sample.py index 25cf7c14..cfdf5f05 100644 --- a/src/resdk/resources/sample.py +++ b/src/resdk/resources/sample.py @@ -1,7 +1,7 @@ """Sample resource.""" import logging -from typing import TYPE_CHECKING, Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional from resdk.exceptions import ResolweServerError from resdk.shortcuts.sample import SampleUtilsMixin @@ -9,6 +9,7 @@ from ..utils.decorators import assert_object_exists from .background_task import BackgroundTask from .collection import BaseCollection, Collection +from .variants import Variant if TYPE_CHECKING: from .annotations import AnnotationValue @@ -39,6 +40,8 @@ def __init__(self, resolwe, **model_data): self._background = None #: is this sample background to any other sample? self._is_background = None + #: list of ``Variant`` objects attached to the sample + self._variants = None super().__init__(resolwe, **model_data) @@ -60,6 +63,13 @@ def data(self): return self._data + @property + def variants(self): + """Get variants.""" + if self._variants is None: + self._variants = self.resolwe.variant.filter(variant_calls__sample=self.id) + return self._variants + @property def collection(self): """Get collection.""" diff --git a/src/resdk/resources/variants.py b/src/resdk/resources/variants.py new file mode 100644 index 00000000..75e9607a --- /dev/null +++ b/src/resdk/resources/variants.py @@ -0,0 +1,94 @@ +"""Variant resources.""" + +from .base import BaseResource + + +class Variant(BaseResource): + """ResolweBio Variant resource.""" + + endpoint = "variant" + + READ_ONLY_FIELDS = BaseResource.READ_ONLY_FIELDS + ( + "species", + "genome_assembly", + "chromosome", + "position", + "reference", + "alternative", + ) + + def __init__(self, resolwe, **model_data): + """Initialize object.""" + super().__init__(resolwe, **model_data) + self._annotations = None + + @property + def annotations(self): + """Get the annotations for this variant.""" + if self._annotations is None: + self._annotations = self.resolwe.variant_annotation.filter(variant_id=self.id) + return self._annotations[0] + + def __repr__(self) -> str: + """Return string representation.""" + return ( + f"Variant " + ) + + +class VariantAnnotation(BaseResource): + """VariantAnnotation resource.""" + + endpoint = "variant_annotations" + + READ_ONLY_FIELDS = BaseResource.READ_ONLY_FIELDS + ( + "variant_id", + "type", + "clinical_diagnosis", + "clinical_significance", + "dbsnp_id", + "clinvar_id", + "data", + "transcripts", + ) + + def __repr__(self) -> str: + """Return string representation.""" + return f"VariantAnnotation " + + +class VariantExperiment(BaseResource): + """Variant experiment resource.""" + + endpoint = "variant_experiment" + + READ_ONLY_FIELDS = BaseResource.READ_ONLY_FIELDS + ( + "variant_data_source", + "timestamp", + "contributor", + ) + + +class VariantCall(BaseResource): + """VariantCall resource.""" + + endpoint = "variant_calls" + + READ_ONLY_FIELDS = BaseResource.READ_ONLY_FIELDS + ( + "sample", + "variant", + "experiment", + "quality", + "depth_norm_quality", + "alternative_allele_depth", + "depth", + "genotype", + "genotype_quality", + "filter", + "data", + ) + + def __repr__(self) -> str: + """Return string representation.""" + return f"VariantCall "