Passed
Pull Request — master (#39)
by Paolo
02:41
created

image_app.mixins.BioSampleMixin.data_source_id()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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