Source code for resdk.resources.relation

"""Relation resource."""

import logging

from resdk.exceptions import ValidationError

from .base import BaseResolweResource
from .collection import Collection
from .descriptor import DescriptorSchema
from .utils import get_sample_id


[docs]class Relation(BaseResolweResource): """Resolwe Relation resource. :param resolwe: Resolwe instance :type resolwe: Resolwe object :param model_data: Resource model data """ endpoint = "relation" READ_ONLY_FIELDS = BaseResolweResource.READ_ONLY_FIELDS + ("descriptor_dirty",) UPDATE_PROTECTED_FIELDS = BaseResolweResource.UPDATE_PROTECTED_FIELDS + ("type",) WRITABLE_FIELDS = BaseResolweResource.WRITABLE_FIELDS + ( "collection", "category", "descriptor", "descriptor_schema", "partitions", "unit", ) def __init__(self, resolwe, **model_data): """Initialize attributes.""" self.logger = logging.getLogger(__name__) #: Collection in which relation is self._collection = None #: ``DescriptorSchema`` of ``Relation`` object self._descriptor_schema = None #: List of samples in the relation self._samples = None #: indicate whether `descriptor` doesn't match `descriptor_schema` (is dirty) self.descriptor_dirty = None #: annotation data, with the form defined in descriptor_schema self.descriptor = None #: list of ``RelationPartition`` objects in the ``Relation`` self.partitions = None #: type of the relation self.type = None #: category of the relation self.category = None #: unit (where applicable, e.g. for serieses) self.unit = None super().__init__(resolwe, **model_data) @property def samples(self): """Return list of sample objects in the relation.""" if not self._samples: if not self.partitions: self._samples = [] else: sample_ids = [partition["entity"] for partition in self.partitions] self._samples = self.resolwe.sample.filter(id__in=sample_ids) # Samples should be sorted, so they have same order as positions # XXX: This may be slow for many samples in single collection self._samples = sorted( self._samples, key=lambda sample: sample_ids.index(sample.id) ) return self._samples @property def collection(self): """Return collection object to which relation belongs.""" if not self._collection: self._collection = self.resolwe.collection.get( self._original_values.get("colection", None) ) return self._collection @collection.setter def collection(self, payload): """Set collection to which relation belongs.""" self._resource_setter(payload, Collection, "_collection") @property def descriptor_schema(self): """Get descriptor schema.""" return self._descriptor_schema @descriptor_schema.setter def descriptor_schema(self, payload): """Set descriptor schema.""" self._resource_setter(payload, DescriptorSchema, "_descriptor_schema")
[docs] def update(self): """Clear cache and update resource fields from the server.""" self._samples = None self._descriptor_schema = None super().update()
[docs] def add_sample(self, sample, label=None, position=None): """Add ``sample`` object to relation.""" self.partitions.append( { "entity": sample.id, "position": position, "label": label, } ) self.save() self._samples = None
[docs] def remove_samples(self, *samples): """Remove ``sample`` objects from relation.""" sample_ids = [get_sample_id(sample) for sample in samples] self.partitions = [ partition for partition in self.partitions if partition["entity"] not in sample_ids ] self.save() self._samples = None
[docs] def save(self): """Check that collection is saved and save instance.""" if self._collection is None: # `save` fails in an ugly way if collection is not set raise ValidationError("`collection` attribute is required.") super().save()
def __repr__(self): """Format relation name.""" sample_info = [] for sample, partition in zip(self.samples, self.partitions): name = sample.name label = partition.get("label", None) position = partition.get("position", None) if label and position: sample_info.append( "{} ({} {}): {}".format(label, position, self.unit, name) ) elif partition["label"]: sample_info.append("{}: {}".format(label, name)) elif partition["position"]: sample_info.append("{} {}: {}".format(position, self.unit, name)) else: sample_info.append(name) return "{} id: {}, type: '{}', category: '{}', samples: {{{}}}".format( self.__class__.__name__, self.id, self.type, self.category, ", ".join(sample_info), )