Completed
Push — master ( 8a2447...e2760f )
by Paolo
06:42
created

uid.mixins.BioSampleMixin.__can_I()   A

Complexity

Conditions 2

Size

Total Lines 18
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 18
rs 10
c 0
b 0
f 0
cc 2
nop 2
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Fri Jun 28 14:46:39 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
8
Define mixins classes for uid.models
9
10
"""
11
12
import logging
13
14
from django.db import connections
15
from django.utils import timezone
16
17
from common.constants import STATUSES
18
from common.helpers import format_attribute
19
20
# Get an instance of a logger
21
logger = logging.getLogger(__name__)
22
23
24
# Adding a classmethod to Category if you want to enable truncate
25
# https://books.agiliq.com/projects/django-orm-cookbook/en/latest/truncate.html
26
class BaseMixin(object):
27
    """Base class for UID tables. It implement common stuff for all UID
28
    tables::
29
30
        from uid.models import BaseMixin
31
32
        class Submission(BaseMixin):
33
            pass
34
35
    """
36
37
    @classmethod
38
    def truncate(cls):
39
        """
40
        Truncate table data and restart indexes from 0::
41
42
            from uid.models import Submission
43
44
            Submission.truncate()
45
        """
46
47
        # Django.db.connections is a dictionary-like object that allows you
48
        # to retrieve a specific connection using its alias
49
        with connections["default"].cursor() as cursor:
50
            statement = "TRUNCATE TABLE {0} RESTART IDENTITY CASCADE".format(
51
                cls._meta.db_table)
52
            logger.debug(statement)
53
            cursor.execute(statement)
54
55
56
class BioSampleMixin(BaseMixin):
57
    """
58
    Common methods for animal and samples useful in biosample generation
59
    Need to called with data into biosample or animals::
60
61
        from uid.models import Animal
62
63
        animal = Animal.objects.get(pk=1)
64
        biosample_data = animal.to_biosample()
65
66
    """
67
68
    def __str__(self):
69
        return str(self.name)
70
71
    @property
72
    def person(self):
73
        """Retrieve :py:class:`Person` information from owner relationship"""
74
75
        return self.owner.person
76
77
    @property
78
    def organization(self):
79
        """Return :py:class:`Organization` relationship from related
80
        :py:class:`Submission` object"""
81
82
        return self.submission.organization
83
84
    @property
85
    def gene_bank_country(self):
86
        """Return :py:class:`DictCountry` relationship from related
87
        :py:class:`Submission` object"""
88
89
        return self.submission.gene_bank_country
90
91
    @property
92
    def gene_bank_name(self):
93
        """Return gene bank name from related :py:class:`Submission` object"""
94
95
        return self.submission.gene_bank_name
96
97
    @property
98
    def data_source_id(self):
99
        """Get Data source id (original animal/sample name)"""
100
101
        return self.name
102
103
    @property
104
    def specie(self):
105
        raise NotImplementedError(
106
            "You need to define this method in your class")
107
108
    def get_attributes(self):
109
        """Common attribute definition required from Animal and samples. Need
110
        to be called inside Animal/sample get_atribute method. Keys
111
        is the name in metadata rules
112
113
        Returns:
114
            dict: a dictionary object
115
        """
116
117
        attributes = {}
118
119
        attributes['Data source ID'] = format_attribute(
120
            value=self.data_source_id)
121
122
        attributes['Alternative id'] = format_attribute(
123
            value=self.alternative_id)
124
125
        # HINT: this is a mandatory biosample field: could be removed from
126
        # attributes?
127
        attributes['Description'] = format_attribute(
128
            value=self.description)
129
130
        attributes["Project"] = format_attribute(
131
            value="IMAGE")
132
133
        # to retrieve where this sample belongs
134
        attributes["IMAGE submission id"] = format_attribute(
135
            value=self.submission.id)
136
137
        attributes['Submission title'] = format_attribute(
138
            value=self.submission.title)
139
140
        attributes['Submission description'] = format_attribute(
141
            value=self.submission.description)
142
143
        attributes['Person last name'] = format_attribute(
144
            value=self.owner.last_name)
145
146
        attributes['Person initial'] = format_attribute(
147
            value=self.person.initials)
148
149
        attributes['Person first name'] = format_attribute(
150
            value=self.owner.first_name)
151
152
        attributes['Person email'] = format_attribute(
153
            value="mailto:%s" % (self.owner.email))
154
155
        attributes['Person affiliation'] = format_attribute(
156
            value=self.person.affiliation.name)
157
158
        attributes['Person role'] = self.person.role.format_attribute()
159
160
        attributes['Organization name'] = format_attribute(
161
            value=self.organization.name)
162
163
        attributes['Organization address'] = format_attribute(
164
            value=self.organization.address)
165
166
        attributes['Organization uri'] = format_attribute(
167
            value=self.organization.URI)
168
169
        attributes['Organization country'] = \
170
            self.organization.country.format_attribute()
171
172
        attributes[
173
            'Organization role'] = self.organization.role.format_attribute()
174
175
        # this could be present or not
176
        if self.publication:
177
            attributes['Publication DOI'] = format_attribute(
178
                value=self.publication.doi)
179
180
        attributes['Gene bank name'] = format_attribute(
181
            value=self.gene_bank_name)
182
183
        attributes[
184
            'Gene bank country'] = self.gene_bank_country.format_attribute()
185
186
        attributes['Data source type'] = format_attribute(
187
            value=self.submission.get_datasource_type_display())
188
189
        attributes['Data source version'] = format_attribute(
190
            value=self.submission.datasource_version)
191
192
        attributes['Species'] = self.specie.format_attribute()
193
194
        return attributes
195
196
    def to_biosample(self, release_date=None):
197
        """
198
        Common stuff to generate a biosample object. Need to be called
199
        inside Animal/Sample to_biosample method
200
201
        Args:
202
            release_date (str): data will no be published before this day
203
                (YYYY-MM-DD)
204
205
        Returns:
206
            dict: a dictionary object
207
        """
208
209
        result = {}
210
211
        # define mandatory fields
212
        result['alias'] = self.biosample_alias
213
        result['title'] = self.name
214
215
        # in case of update, I need to provide the old accession in payload
216
        if self.biosample_id and self.biosample_id != '':
217
            result['accession'] = self.biosample_id
218
219
        if release_date:
220
            result['releaseDate'] = release_date
221
222
        else:
223
            now = timezone.now()
224
            result['releaseDate'] = str(now.date())
225
226
        result['taxonId'] = self.specie.taxon_id
227
228
        result['taxon'] = self.specie.label
229
230
        # define optinal fields
231
        if self.description:
232
            result['description'] = self.description
233
234
        # define attributes that will be customized in Animal and sample
235
        result['attributes'] = self.get_attributes()
236
237
        return result
238
239
    def __status_not_in(self, statuses):
240
        """
241
        Return True id self.status not in statuses
242
243
        Args:
244
            statuses (list): a list of :py:class:`common.constants.STATUSES`
245
246
        Returns:
247
            bool
248
        """
249
250
        statuses = [x.value[0] for x in STATUSES if x.name in statuses]
251
252
        if self.submission.status not in statuses:
253
            return True
254
255
        else:
256
            return False
257
258
    def can_edit(self):
259
        """Returns True if I can edit a sample/animal according to submission
260
        status
261
262
        Returns:
263
            bool
264
        """
265
266
        statuses = ['waiting', 'submitted']
267
268
        return self.__status_not_in(statuses)
269
270
    def can_delete(self):
271
        """Returns True if I can delete a sample/animal according to submission
272
        status
273
274
        Returns:
275
            bool
276
        """
277
278
        statuses = ['waiting', 'submitted']
279
280
        return self.__status_not_in(statuses)
281