Skip to content

Commit

Permalink
Add variants related models
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorjerse committed Dec 12, 2024
1 parent 74ba4ec commit 1163007
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Changed
Added
-----
- Add ``restart`` method to the ``Data`` resource
- Add variants related models

Fixed
-----
Expand Down
18 changes: 16 additions & 2 deletions src/resdk/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,34 @@ def _clone(self):

def _dehydrate_resources(self, obj):
"""Iterate through object and replace all objects with their ids."""
print("Dehydrating", obj, type(obj))
if isinstance(obj, BaseResource):
print("Base")
return obj.id
if isinstance(obj, dict):
print("Dict")
return {key: self._dehydrate_resources(value) for key, value in obj.items()}
if self._non_string_iterable(obj):
print("Non string iterable")
return [self._dehydrate_resources(element) for element in obj]

print("Returning unchanged", obj, type(obj))
return obj

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__"):
print("Replacing sample with entity in", key)
key = key.replace("sample", "entity")
print("Adding filter", key, value)
value = self._dehydrate_resources(value)
print("Dehidrated value", value, type(value))
if self._non_string_iterable(value):
print("Iterable")
value = ",".join(map(str, value))
if self.resource.query_method == "GET":
print("Appending value", value)
self._filters[key].append(value)
elif self.resource.query_method == "POST":
self._filters[key] = value
Expand Down Expand Up @@ -211,6 +221,8 @@ def _fetch(self):

filters = self._compose_filters()
if self.resource.query_method == "GET":
print("Query with filters", filters)
print("My api", self.api)
items = self.api.get(**filters)
elif self.resource.query_method == "POST":
items = self.api.post(filters)
Expand Down Expand Up @@ -285,6 +297,8 @@ def get(self, *args, **kwargs):
kwargs["limit"] = kwargs.get("limit", 1)

new_query = self._clone()

print("Adding filters", kwargs)
new_query._add_filter(kwargs)

response = list(new_query)
Expand Down
9 changes: 9 additions & 0 deletions src/resdk/resolwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
Relation,
Sample,
User,
Variant,
VariantAnnotation,
VariantCall,
VariantExperiment,
)
from .resources.base import BaseResource
from .resources.kb import Feature, Mapping
Expand Down Expand Up @@ -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",
Expand All @@ -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 = {
Expand Down
9 changes: 9 additions & 0 deletions src/resdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
:members:
:inherited-members:
.. autoclass:: resdk.resources.Variants
:members:
:inherited-members:
.. autoclass:: resdk.resources.User
:members:
:inherited-members:
Expand Down Expand Up @@ -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",
Expand All @@ -117,4 +122,8 @@
"Process",
"Relation",
"User",
"Variant",
"VariantAnnotation",
"VariantCall",
"VariantExperiment",
)
21 changes: 20 additions & 1 deletion src/resdk/resources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ def fetch_object(cls, resolwe, id=None, slug=None):
if (id is None and slug is None) or (id and slug):
raise ValueError("One and only one of id or slug must be given")

print("Getting fetch object query")
query = resolwe.get_query_by_resource(cls)
# print("Got query", query)
if id:
return query.get(id=id)
print("Getting from query with slug", slug)
return query.get(slug=slug)

def fields(self):
Expand All @@ -77,6 +80,10 @@ def update(self):
response = self.api(self.id).get()
self._update_fields(response)

def __hash__(self):
"""Return hash of the object."""
return hash(self.id)

def _dehydrate_resources(self, obj):
"""Iterate through object and replace all objects with their ids."""
# Prevent circular imports:
Expand Down Expand Up @@ -130,13 +137,16 @@ def assert_fields_unchanged(field_names):
payload = {}
for field_name in self.WRITABLE_FIELDS:
if field_changed(field_name):
print("Field changed", field_name)
print("Change", getattr(self, field_name))
payload[field_name] = self._dehydrate_resources(
getattr(self, field_name)
)
if "sample" in payload:
payload["entity"] = payload.pop("sample")

if payload:
print("Sending payload 1", payload)
response = self.api(self.id).patch(payload)
self._update_fields(response)

Expand Down Expand Up @@ -216,14 +226,23 @@ def __eq__(self, obj):

def _resource_setter(self, payload, resource, field):
"""Set ``resource`` with ``payload`` on ``field``."""
print("-" * 40)
print("Resource setter", payload, type(payload))
print("Resource", resource)
if isinstance(payload, resource):
print("resource")
setattr(self, field, payload)
elif isinstance(payload, dict):
print("Dict")
setattr(self, field, resource(resolwe=self.resolwe, **payload))
elif isinstance(payload, int):
print("Int")
setattr(self, field, resource.fetch_object(self.resolwe, id=payload))
elif isinstance(payload, str):
setattr(self, field, resource.fetch_object(self.resolwe, slug=payload))
print("Str")
res = resource.fetch_object(self.resolwe, slug=payload)
print("Got result for Str", res)
setattr(self, field, res)
else:
setattr(self, field, payload)

Expand Down
3 changes: 3 additions & 0 deletions src/resdk/resources/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,19 @@ def descriptor_schema(self, payload):
def sample(self):
"""Get sample."""
if self._sample is None and self._original_values.get("entity", None):
print("Sample getter not set")
# The collection data is only serialized on the top level. Replace the
# data inside 'entity' with the actual collection data.
entity_values = self._original_values["entity"].copy()
entity_values["collection"] = self._original_values.get("collection", None)
self._sample = Sample(resolwe=self.resolwe, **entity_values)
print("Sample getter", self._sample)
return self._sample

@sample.setter
def sample(self, payload):
"""Set sample."""
print("Sample setter", payload)
self._resource_setter(payload, Sample, "_sample")

@property
Expand Down
36 changes: 35 additions & 1 deletion src/resdk/resources/sample.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""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

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
Expand Down Expand Up @@ -39,6 +40,10 @@ 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
#: list of ``VariantExperiment`` objects attached to the sample
self._experiments = None

super().__init__(resolwe, **model_data)

Expand All @@ -48,6 +53,8 @@ def update(self):
self._relations = None
self._background = None
self._is_background = None
self._variants = None
self._experiments = None

super().update()

Expand All @@ -60,6 +67,33 @@ def data(self):

return self._data

@property
def experiments(self):
"""Get experiments."""
if self._experiments is None:
self._experiments = self.resolwe.variant_experiment.filter(
variant_calls__sample=self.id
)
return self._experiments

@property
def latest_experiment(self):
"""Get latest experiment."""
return self.experiments.filter(ordering="-timestamp", limit=1)[0]

@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

def variants_by_experiment(self, experiment):
"""Get variants for sample detected by the given experiment."""
return self.resolwe.variant.filter(
variant_calls__sample=self.id, variant_calls__experiment=experiment.id
)

@property
def collection(self):
"""Get collection."""
Expand Down
Loading

0 comments on commit 1163007

Please sign in to comment.