Passed
Pull Request — master (#40)
by Paolo
01:48
created

excel.helpers.fill_uid.fill_uid_animals()   B

Complexity

Conditions 5

Size

Total Lines 102
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 62
dl 0
loc 102
rs 7.7769
c 0
b 0
f 0
cc 5
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Fri Jul  5 16:37:48 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import logging
10
11
from common.constants import (
12
    ERROR, LOADED, ACCURACIES, SAMPLE_STORAGE, SAMPLE_STORAGE_PROCESSING)
13
from common.helpers import image_timedelta, parse_image_timedelta
14
from image_app.helpers import get_or_create_obj, update_or_create_obj
15
from image_app.models import (
16
    DictBreed, DictCountry, DictSpecie, DictSex, DictUberon, Name, Animal,
17
    Sample, DictDevelStage, DictPhysioStage)
18
from submissions.helpers import send_message
19
from validation.helpers import construct_validation_message
20
from validation.models import ValidationSummary
21
22
from .exceptions import ExcelImportError
23
from .exceltemplate import ExcelTemplateReader
24
25
# Get an instance of a logger
26
logger = logging.getLogger(__name__)
27
28
29
def fill_uid_breeds(submission_obj, template):
30
    """Fill DictBreed from a excel record"""
31
32
    logger.info("fill_uid_breeds() started")
33
34
    # ok get languages from submission (useful for translation)
35
    language = submission_obj.gene_bank_country.label
36
37
    # iterate among excel template
38
    for record in template.get_breed_records():
39
        # get a DictSpecie object. Species are in latin names, but I can
40
        # find also a common name in translation tables
41
        specie = DictSpecie.get_specie_check_synonyms(
42
            species_label=record.species,
43
            language=language)
44
45
        # get country for breeds. Ideally will be the same of submission,
46
        # however, it could be possible to store data from other contries
47
        country = get_or_create_obj(
48
            DictCountry,
49
            label=record.efabis_breed_country)
50
51
        get_or_create_obj(
52
            DictBreed,
53
            supplied_breed=record.supplied_breed,
54
            specie=specie,
55
            country=country)
56
57
    logger.info("fill_uid_breeds() completed")
58
59
60
def fill_uid_names(submission_obj, template):
61
    """fill Names table from crbanim record"""
62
63
    # debug
64
    logger.info("called fill_uid_names()")
65
66
    # iterate among excel template
67
    for record in template.get_animal_records():
68
        # in the same record I have the sample identifier and animal identifier
69
        # a name record for animal
70
        get_or_create_obj(
71
            Name,
72
            name=record.animal_id_in_data_source,
73
            submission=submission_obj,
74
            owner=submission_obj.owner)
75
76
    # iterate among excel template
77
    for record in template.get_sample_records():
78
        # name record for sample
79
        get_or_create_obj(
80
            Name,
81
            name=record.sample_id_in_data_source,
82
            submission=submission_obj,
83
            owner=submission_obj.owner)
84
85
    logger.info("fill_uid_names() completed")
86
87
88
def fill_uid_animals(submission_obj, template):
89
    # debug
90
    logger.info("called fill_uid_animals()")
91
92
    # iterate among excel template
93
    for record in template.get_animal_records():
94
        # determine sex. Check for values
95
        sex = DictSex.objects.get(label__iexact=record.sex)
96
97
        # get specie
98
        specie = DictSpecie.objects.get(label=record.species)
99
100
        # how I can get breed from my data?
101
        breeds = [breed for breed in template.get_breed_records()
102
                  if breed.supplied_breed == record.breed and
103
                  breed.species == record.species]
104
105
        # breed is supposed to be unique, from UID constraints. However
106
        # I could place the same breed name for two countries. In that case,
107
        # I cant derive a unique breed from users data
108
        if len(breeds) != 1:
109
            raise ExcelImportError(
110
                "Can't determine a unique breed for '%s:%s' from user data" %
111
                (record.breed, record.species))
112
113
        # get a country for this breed
114
        country = DictCountry.objects.get(
115
            label=breeds[0].efabis_breed_country)
116
117
        # ok get a real dictbreed object
118
        breed = DictBreed.objects.get(
119
            supplied_breed=record.breed,
120
            specie=specie,
121
            country=country)
122
123
        logger.debug("Selected breed is %s" % (breed))
124
125
        # define names
126
        name, mother, father = None, None, None
127
128
        # get name for this animal and for mother and father
129
        logger.debug("Getting %s as my name" % (
130
            record.animal_id_in_data_source))
131
132
        name = Name.objects.get(
133
            name=record.animal_id_in_data_source,
134
            submission=submission_obj)
135
136
        if record.father_id_in_data_source:
137
            logger.debug("Getting %s as father" % (
138
                record.father_id_in_data_source))
139
140
            father = Name.objects.get(
141
                name=record.father_id_in_data_source,
142
                submission=submission_obj)
143
144
        if record.mother_id_in_data_source:
145
            logger.debug("Getting %s as mother" % (
146
                record.mother_id_in_data_source))
147
148
            mother = Name.objects.get(
149
                name=record.mother_id_in_data_source,
150
                submission=submission_obj)
151
152
        # now get accuracy
153
        accuracy = ACCURACIES.get_value_by_desc(
154
            record.birth_location_accuracy)
155
156
        # create a new object. Using defaults to avoid collisions when
157
        # updating data
158
        defaults = {
159
            'alternative_id': record.alternative_animal_id,
160
            'description': record.animal_description,
161
            'breed': breed,
162
            'sex': sex,
163
            'father': father,
164
            'mother': mother,
165
            'birth_date': record.birth_date,
166
            'birth_location': record.birth_location,
167
            'birth_location_latitude': record.birth_location_latitude,
168
            'birth_location_longitude': record.birth_location_longitude,
169
            'birth_location_accuracy': accuracy,
170
            'owner': submission_obj.owner
171
        }
172
173
        # creating or updating an object
174
        update_or_create_obj(
175
            Animal,
176
            name=name,
177
            defaults=defaults)
178
179
    # create a validation summary object and set all_count
180
    validation_summary = get_or_create_obj(
181
        ValidationSummary,
182
        submission=submission_obj,
183
        type="animal")
184
185
    # reset counts
186
    validation_summary.reset_all_count()
187
188
    # debug
189
    logger.info("fill_uid_animals() completed")
190
191
192
def fill_uid_samples(submission_obj, template):
193
    # debug
194
    logger.info("called fill_uid_samples()")
195
196
    # iterate among excel template
197
    for record in template.get_sample_records():
198
        # get name for this sample
199
        name = Name.objects.get(
200
            name=record.sample_id_in_data_source,
201
            submission=submission_obj,
202
            owner=submission_obj.owner)
203
204
        # get animal by reading record
205
        animal = Animal.objects.get(
206
            name__name=record.animal_id_in_data_source,
207
            name__submission=submission_obj)
208
209
        # get a organism part. Organism parts need to be in lowercases
210
        organism_part = get_or_create_obj(
211
            DictUberon,
212
            label=record.organism_part
213
        )
214
215
        # get developmental_stage and physiological_stage terms
216
        # they are not mandatory
217
        devel_stage, physio_stage = None, None
218
219
        if record.developmental_stage:
220
            devel_stage = get_or_create_obj(
221
                DictDevelStage,
222
                label=record.developmental_stage
223
            )
224
225
        if record.physiological_stage:
226
            physio_stage = get_or_create_obj(
227
                DictPhysioStage,
228
                label=record.physiological_stage
229
            )
230
231
        # animal age could be present or not
232
        if record.animal_age_at_collection:
233
            animal_age_at_collection, time_units = parse_image_timedelta(
234
                record.animal_age_at_collection)
235
236
        else:
237
            # derive animal age at collection
238
            animal_age_at_collection, time_units = image_timedelta(
239
                record.collection_date, animal.birth_date)
240
241
        # another time column
242
        preparation_interval, preparation_interval_units = None, None
243
244
        if record.sampling_to_preparation_interval:
245
            preparation_interval, preparation_interval_units = \
246
                parse_image_timedelta(record.sampling_to_preparation_interval)
247
248
        # now get accuracy
249
        accuracy = ACCURACIES.get_value_by_desc(
250
            record.collection_place_accuracy)
251
252
        # now get storage and storage processing
253
        # TODO; check those values in excel columns
254
        storage = SAMPLE_STORAGE.get_value_by_desc(
255
            record.sample_storage)
256
257
        storage_processing = SAMPLE_STORAGE_PROCESSING.get_value_by_desc(
258
            record.sample_storage_processing)
259
260
        # create a new object. Using defaults to avoid collisions when
261
        # updating data
262
        defaults = {
263
            'alternative_id': record.alternative_sample_id,
264
            'description': record.sample_description,
265
            'animal': animal,
266
            'protocol': record.specimen_collection_protocol,
267
            'collection_date': record.collection_date,
268
            'collection_place_latitude': record.collection_place_latitude,
269
            'collection_place_longitude': record.collection_place_longitude,
270
            'collection_place': record.collection_place,
271
            'collection_place_accuracy': accuracy,
272
            'organism_part': organism_part,
273
            'developmental_stage': devel_stage,
274
            'physiological_stage': physio_stage,
275
            'animal_age_at_collection': animal_age_at_collection,
276
            'animal_age_at_collection_units': time_units,
277
            'availability': record.availability,
278
            'storage': storage,
279
            'storage_processing': storage_processing,
280
            'preparation_interval': preparation_interval,
281
            'preparation_interval_units': preparation_interval_units,
282
            'owner': submission_obj.owner,
283
        }
284
285
        update_or_create_obj(
286
            Sample,
287
            name=name,
288
            defaults=defaults)
289
290
    # create a validation summary object and set all_count
291
    validation_summary = get_or_create_obj(
292
        ValidationSummary,
293
        submission=submission_obj,
294
        type="sample")
295
296
    # reset counts
297
    validation_summary.reset_all_count()
298
299
    # debug
300
    logger.info("fill_uid_samples() completed")
301
302
303
def upload_template(submission_obj):
304
    # debug
305
    logger.info("Importing from Excel template file")
306
307
    # this is the full path in docker container
308
    fullpath = submission_obj.get_uploaded_file_path()
309
310
    # read submission data
311
    reader = ExcelTemplateReader()
312
    reader.read_file(fullpath)
313
314
    # start data loading
315
    try:
316
        # check for species and sex in a similar way as cryoweb does
317
        # TODO: identical to CRBanim. Move to a mixin
318
        check, not_found = reader.check_sex()
319
320
        if not check:
321
            message = (
322
                "Not all Sex terms are loaded into database: "
323
                "check for %s in your dataset" % (not_found))
324
325
            raise ExcelImportError(message)
326
327
        check, not_found = reader.check_species(
328
            submission_obj.gene_bank_country)
329
330
        if not check:
331
            raise ExcelImportError(
332
                "Some species are not loaded in UID database: "
333
                "%s" % (not_found))
334
335
        check, not_found = reader.check_accuracies()
336
337
        if not check:
338
            message = (
339
                "Not all accuracy levels are defined in database: "
340
                "check for %s in your dataset" % (not_found))
341
342
            raise ExcelImportError(message)
343
344
        # BREEDS
345
        fill_uid_breeds(submission_obj, reader)
346
347
        # NAME
348
        fill_uid_names(submission_obj, reader)
349
350
        # ANIMALS
351
        fill_uid_animals(submission_obj, reader)
352
353
        # SAMPLES
354
        fill_uid_samples(submission_obj, reader)
355
356
    except Exception as exc:
357
        # set message:
358
        message = "Error in importing data: %s" % (str(exc))
359
360
        # save a message in database
361
        submission_obj.status = ERROR
362
        submission_obj.message = message
363
        submission_obj.save()
364
365
        # send async message
366
        send_message(submission_obj)
367
368
        # debug
369
        logger.error("Error in importing from Template: %s" % (exc))
370
        logger.exception(exc)
371
372
        return False
373
374
    else:
375
        message = "Template import completed for submission: %s" % (
376
            submission_obj.id)
377
378
        submission_obj.message = message
379
        submission_obj.status = LOADED
380
        submission_obj.save()
381
382
        # send async message
383
        send_message(
384
            submission_obj,
385
            validation_message=construct_validation_message(submission_obj))
386
387
    logger.info("Import from Template is complete")
388
389
    return True
390