Source code for uid.mixins
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 28 14:46:39 2019
@author: Paolo Cozzi <cozzi@ibba.cnr.it>
Define mixins classes for uid.models
"""
import logging
from django.db import connections
from django.utils import timezone
from common.constants import STATUSES
from common.helpers import format_attribute
# Get an instance of a logger
logger = logging.getLogger(__name__)
# Adding a classmethod to Category if you want to enable truncate
# https://books.agiliq.com/projects/django-orm-cookbook/en/latest/truncate.html
[docs]class BaseMixin(object):
"""Base class for UID tables. It implement common stuff for all UID
tables::
from uid.models import BaseMixin
class Submission(BaseMixin):
pass
"""
[docs] @classmethod
def truncate(cls):
"""
Truncate table data and restart indexes from 0::
from uid.models import Submission
Submission.truncate()
"""
# Django.db.connections is a dictionary-like object that allows you
# to retrieve a specific connection using its alias
with connections["default"].cursor() as cursor:
statement = "TRUNCATE TABLE {0} RESTART IDENTITY CASCADE".format(
cls._meta.db_table)
logger.debug(statement)
cursor.execute(statement)
[docs]class BioSampleMixin(BaseMixin):
"""
Common methods for animal and samples useful in biosample generation
Need to called with data into biosample or animals::
from uid.models import Animal
animal = Animal.objects.get(pk=1)
biosample_data = animal.to_biosample()
"""
def __str__(self):
return str(self.name)
@property
def person(self):
"""Retrieve :py:class:`Person` information from owner relationship"""
return self.owner.person
@property
def organization(self):
"""Return :py:class:`Organization` relationship from related
:py:class:`Submission` object"""
return self.submission.organization
@property
def gene_bank_country(self):
"""Return :py:class:`DictCountry` relationship from related
:py:class:`Submission` object"""
return self.submission.gene_bank_country
@property
def gene_bank_name(self):
"""Return gene bank name from related :py:class:`Submission` object"""
return self.submission.gene_bank_name
@property
def data_source_id(self):
"""Get Data source id (original animal/sample name)"""
return self.name
@property
def specie(self):
raise NotImplementedError(
"You need to define this method in your class")
[docs] def get_attributes(self):
"""Common attribute definition required from Animal and samples. Need
to be called inside Animal/sample get_atribute method. Keys
is the name in metadata rules
Returns:
dict: a dictionary object
"""
attributes = {}
attributes['Data source ID'] = format_attribute(
value=self.data_source_id)
attributes['Alternative id'] = format_attribute(
value=self.alternative_id)
# HINT: this is a mandatory biosample field: could be removed from
# attributes?
attributes['Description'] = format_attribute(
value=self.description)
attributes["Project"] = format_attribute(
value="IMAGE")
# to retrieve where this sample belongs
attributes["IMAGE submission id"] = format_attribute(
value=self.submission.id)
attributes['Submission title'] = format_attribute(
value=self.submission.title)
attributes['Submission description'] = format_attribute(
value=self.submission.description)
attributes['Person last name'] = format_attribute(
value=self.owner.last_name)
attributes['Person initial'] = format_attribute(
value=self.person.initials)
attributes['Person first name'] = format_attribute(
value=self.owner.first_name)
attributes['Person email'] = format_attribute(
value="mailto:%s" % (self.owner.email))
attributes['Person affiliation'] = format_attribute(
value=self.person.affiliation.name)
attributes['Person role'] = self.person.role.format_attribute()
attributes['Organization name'] = format_attribute(
value=self.organization.name)
attributes['Organization address'] = format_attribute(
value=self.organization.address)
attributes['Organization uri'] = format_attribute(
value=self.organization.URI)
attributes['Organization country'] = \
self.organization.country.format_attribute()
attributes[
'Organization role'] = self.organization.role.format_attribute()
# this could be present or not
if self.publication:
attributes['Publication DOI'] = format_attribute(
value=self.publication.doi)
attributes['Gene bank name'] = format_attribute(
value=self.gene_bank_name)
attributes[
'Gene bank country'] = self.gene_bank_country.format_attribute()
attributes['Data source type'] = format_attribute(
value=self.submission.get_datasource_type_display())
attributes['Data source version'] = format_attribute(
value=self.submission.datasource_version)
attributes['Species'] = self.specie.format_attribute()
return attributes
[docs] def to_biosample(self, release_date=None):
"""
Common stuff to generate a biosample object. Need to be called
inside Animal/Sample to_biosample method
Args:
release_date (str): data will no be published before this day
(YYYY-MM-DD)
Returns:
dict: a dictionary object
"""
result = {}
# define mandatory fields
result['alias'] = self.biosample_alias
result['title'] = self.name
# in case of update, I need to provide the old accession in payload
if self.biosample_id and self.biosample_id != '':
result['accession'] = self.biosample_id
if release_date:
result['releaseDate'] = release_date
else:
now = timezone.now()
result['releaseDate'] = str(now.date())
result['taxonId'] = self.specie.taxon_id
result['taxon'] = self.specie.label
# define optinal fields
if self.description:
result['description'] = self.description
# define attributes that will be customized in Animal and sample
result['attributes'] = self.get_attributes()
return result
def __status_not_in(self, statuses):
"""
Return True id self.status not in statuses
Args:
statuses (list): a list of :py:class:`common.constants.STATUSES`
Returns:
bool
"""
statuses = [x.value[0] for x in STATUSES if x.name in statuses]
if self.submission.status not in statuses:
return True
else:
return False
[docs] def can_edit(self):
"""Returns True if I can edit a sample/animal according to submission
status
Returns:
bool
"""
statuses = ['waiting', 'submitted']
return self.__status_not_in(statuses)
[docs] def can_delete(self):
"""Returns True if I can delete a sample/animal according to submission
status
Returns:
bool
"""
statuses = ['waiting', 'submitted']
return self.__status_not_in(statuses)