Completed
Push — master ( 8738db...eecf08 )
by Paolo
06:03
created

SanitizeAccessionTest.test_sanitize_accession()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 2
nop 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Fri Feb 22 15:49:18 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import os
10
import logging
11
12
from unittest.mock import patch
13
from collections import namedtuple
14
15
from django.test import TestCase
16
17
from common.tests import WebSocketMixin
18
from image_app.models import (
19
    DictSex, DictBreed, Name, Animal,
20
    Sample, DictUberon, DictCountry)
21
from image_app.tests.mixins import (
22
    DataSourceMixinTestCase, FileReaderMixinTestCase)
23
24
from ..helpers import (
25
    logger, CRBAnimReader, upload_crbanim, fill_uid_breed, fill_uid_names,
26
    fill_uid_animal, fill_uid_sample, find_storage_type, sanitize_url)
27
from .common import BaseTestCase
28
29
30
class CRBAnimMixin(DataSourceMixinTestCase, WebSocketMixin):
31
    """Common test for CRBanim classes"""
32
33
    # define the method to upload data from. Since the function is now inside
34
    # a class it becomes a method, specifically a bound method and is supposed
35
    # to receive the self attribute by default. If we don't want to get the
36
    # self attribute, we have to declare function as a staticmetho
37
    # https://stackoverflow.com/a/35322635/4385116
38
    upload_method = staticmethod(upload_crbanim)
39
40
    def test_upload_crbanim(self):
41
        """Testing uploading and importing data from crbanim to UID"""
42
43
        # test data loaded
44
        message = "CRBAnim import completed for submission"
45
        self.upload_datasource(message)
46
47
        # check async message called
48
        notification_message = (
49
            'CRBAnim import completed for submission: 1')
50
        validation_message = {
51
            'animals': 1, 'samples': 2,
52
            'animal_unkn': 1, 'sample_unkn': 2,
53
            'animal_issues': 0, 'sample_issues': 0}
54
55
        # check async message called using WebSocketMixin.check_message
56
        self.check_message('Loaded', notification_message, validation_message)
57
58
    def check_errors(self, my_check, message, notification_message):
59
        """Common stuff for error in crbanim loading"""
60
61
        super().check_errors(my_check, message)
62
63
        # check async message called using WebSocketMixin.check_message
64
        self.check_message('Error', notification_message)
65
66
67
class CRBAnimReaderTestCase(
68
        FileReaderMixinTestCase, DataSourceMixinTestCase, BaseTestCase,
69
        TestCase):
70
    def setUp(self):
71
        # calling my base class setup
72
        super().setUp()
73
74
        # crate a CRBAnimReade object
75
        self.reader = CRBAnimReader()
76
77
        # get filenames for DataSourceMixinTestCase.dst_path
78
        self.reader.read_file(self.dst_path)
79
80
    def test_has_columns(self):
81
        """Test if class has columns"""
82
83
        reference = [
84
            'BRC_identifier',
85
            'BRC_name',
86
            'associated_research_project_name',
87
            'BRC_responsible_last_name',
88
            'BRC_responsible_first_name',
89
            'BRC_contact_email',
90
            'organization_name',
91
            'organisation_adress',
92
            'organization_url',
93
            'organization_country',
94
            'BRC_address',
95
            'BRC_url',
96
            'BRC_country',
97
            'sample_identifier',
98
            'EBI_Biosample_identifier',
99
            'sample_availability',
100
            'sample_bibliographic_references',
101
            'animal_ID',
102
            'sex',
103
            'species_latin_name',
104
            'NCBI_taxo_id',
105
            'breed_name',
106
            'country_of_origin',
107
            'sampling_protocol_url',
108
            'sampling_date',
109
            'sample_type_name',
110
            'sample_type_identifier',
111
            'sample_type_ontology',
112
            'body_part_name',
113
            'body_part_identifier',
114
            'body_part_ontology',
115
            'animal_birth_date',
116
            'sample_storage_temperature',
117
            'sample_container_type',
118
            'sample_fixer_nature']
119
120
        self.assertEqual(reference, self.reader.header)
121
122
    def test_debug_row(self):
123
        """Assert a function is callable"""
124
125
        self.reader.print_line(0)
126
        self.assertLogs(logger=logger, level=logging.DEBUG)
127
128
    def test_filter_by_column(self):
129
        """Filter records by column value"""
130
131
        # filter out biosample records from mydata
132
        data = self.reader.filter_by_column_values(
133
            "EBI_Biosample_identifier",
134
            [None])
135
        data = list(data)
136
137
        self.assertEqual(len(data), 2)
138
139
    def test_filter_by_column_case(self):
140
        """Filter records by column value case insensitive"""
141
142
        # filter out biosample records from mydata
143
        # record have capital letter. No record after filtering case sensitive
144
        data = self.reader.filter_by_column_values(
145
            "sex",
146
            ['female'],
147
            ignorecase=False)
148
        data = list(data)
149
150
        self.assertEqual(len(data), 0)
151
152
        # filtering female case insensitive
153
        data = self.reader.filter_by_column_values(
154
            "sex",
155
            ['female'],
156
            ignorecase=True)
157
        data = list(data)
158
159
        self.assertEqual(len(data), 1)
160
161
        # No record after filtering foo case insensitive
162
        data = self.reader.filter_by_column_values(
163
            "sex",
164
            ['foo'],
165
            ignorecase=True)
166
        data = list(data)
167
168
        self.assertEqual(len(data), 0)
169
170
    def test_is_valid(self):
171
        """Test recognizing a good CRBanim file"""
172
173
        with open(self.dst_path) as handle:
174
            chunk = handle.read(2048)
175
176
        check, not_found = self.reader.is_valid(chunk)
177
178
        # a good crbanim file returns True and an empty list
179
        self.assertTrue(check)
180
        self.assertEqual(not_found, [])
181
182
        # assert a not good file. self.base_dir comes from
183
        # common.tests.mixins.DataSourceMixinTestCase
184
        filename = os.path.join(
185
            self.base_dir,
186
            "Mapping_rules_CRB-Anim_InjectTool_v1.csv")
187
188
        with open(filename) as handle:
189
            chunk = handle.read(2048)
190
191
        check, not_found = self.reader.is_valid(chunk)
192
193
        # a invalid crbanim file returns False and a list
194
        self.assertFalse(check)
195
        self.assertGreater(len(not_found), 0)
196
197
198
class ProcessRecordTestCase(DataSourceMixinTestCase, BaseTestCase, TestCase):
199
    """A class to test function which process record"""
200
201
    def setUp(self):
202
        # calling my base class setup
203
        super().setUp()
204
205
        # crate a CRBAnimReade object
206
        self.reader = CRBAnimReader()
207
208
        # get filenames for DataSourceMixinTestCase.dst_path
209
        self.reader.read_file(self.dst_path)
210
211
        # filter out biosample records from mydata
212
        data = self.reader.filter_by_column_values(
213
            "EBI_Biosample_identifier",
214
            [None])
215
        self.data = list(data)
216
217
        # track the sample record for test
218
        self.record = self.data[0]
219
220
        # set a country
221
        self.country = DictCountry.objects.get(label="United Kingdom")
222
223
        # fill object to test inserts. Fill breed
224
        self.breed = fill_uid_breed(self.record, self.country)
225
226
        # fill names
227
        self.animal_name, self.sample_name = fill_uid_names(
228
            self.record, self.submission)
229
230
        # filling animal and samples
231
        self.animal = fill_uid_animal(
232
            self.record,
233
            self.animal_name,
234
            self.breed,
235
            self.submission,
236
            {})
237
238
        # testing samples
239
        self.sample = fill_uid_sample(
240
            self.record,
241
            self.sample_name,
242
            self.animal,
243
            self.submission)
244
245
    def test_fill_uid_breed(self):
246
        """testing fill_uid_breed"""
247
248
        # testing output
249
        self.assertIsInstance(self.breed, DictBreed)
250
        self.assertEqual(self.breed.supplied_breed, self.record.breed_name)
251
        self.assertEqual(
252
            self.breed.specie.label, self.record.species_latin_name)
253
254
    def test_fill_uid_names(self):
255
        # testing output
256
        self.assertIsInstance(self.animal_name, Name)
257
        self.assertIsInstance(self.sample_name, Name)
258
259
        self.assertEqual(self.animal_name.name, self.record.animal_ID)
260
        self.assertEqual(self.sample_name.name, self.record.sample_identifier)
261
262
    def test_fill_uid_animals(self):
263
        # testing animal
264
        self.assertIsInstance(self.animal, Animal)
265
266
        # testing animal attributes
267
        sex = DictSex.objects.get(label__iexact=self.record.sex)
268
        self.assertEqual(self.animal.sex, sex)
269
270
    def test_fill_uid_samples(self):
271
        # testing sample
272
        self.assertIsInstance(self.sample, Sample)
273
274
        # testing sample attributes
275
        organism_part = DictUberon.objects.get(
276
            label__iexact="bone marrow")
277
        self.assertEqual(self.sample.organism_part, organism_part)
278
279
    def test_organism_part(self):
280
        """Check that an 'unknown' organims_part generate a DictUberon
281
        relying on sample_type_name (see crbanim_test_data.csv file)"""
282
283
        # get a new record
284
        record = self.data[1]
285
286
        # fill breeds
287
        breed = fill_uid_breed(record, self.country)
288
289
        # creating name
290
        animal_name, sample_name = fill_uid_names(
291
            record, self.submission)
292
293
        # filling animal and samples
294
        animal = fill_uid_animal(
295
            record,
296
            animal_name,
297
            breed,
298
            self.submission,
299
            {})
300
301
        # testing samples
302
        sample = fill_uid_sample(
303
            record,
304
            sample_name,
305
            animal,
306
            self.submission)
307
308
        # testing sample attributes
309
        organism_part = DictUberon.objects.get(
310
            label__iexact="uterus")
311
        self.assertEqual(sample.organism_part, organism_part)
312
313
    def test_find_storage_type(self):
314
        """Asserting storage type conversion"""
315
316
        result = find_storage_type(self.record)
317
        self.assertEqual(result, 2)
318
319
        # create a fare record object
320
        Data = namedtuple("Data", ["sample_storage_temperature"])
321
        record = Data._make(["meow"])
322
323
        result = find_storage_type(record)
324
        self.assertIsNone(result)
325
326
327
class UploadCRBAnimTestCase(CRBAnimMixin, BaseTestCase, TestCase):
328
329
    @patch("crbanim.helpers.CRBAnimReader.check_species",
330
           return_value=[False, 'Rainbow trout'])
331
    def test_upload_crbanim_errors_with_species(self, my_check):
332
        """Testing importing with data into UID with errors"""
333
334
        message = "Some species are not loaded in UID database"
335
        notification_message = (
336
            'Error in importing data: Some species '
337
            'are not loaded in UID database: Rainbow '
338
            'trout')
339
340
        # check crbanim import fails
341
        self.check_errors(my_check, message, notification_message)
342
343
    @patch("crbanim.helpers.CRBAnimReader.check_sex",
344
           return_value=[False, 'unknown'])
345
    def test_upload_crbanim_errors_with_sex(self, my_check):
346
        """Testing importing with data into UID with errors"""
347
348
        message = "Not all Sex terms are loaded into database"
349
        notification_message = (
350
            'Error in importing data: Not all Sex '
351
            'terms are loaded into database: check '
352
            'for unknown in your dataset')
353
354
        # check crbanim import fails
355
        self.check_errors(my_check, message, notification_message)
356
357
358
class ReloadCRBAnimTestCase(CRBAnimMixin, BaseTestCase, TestCase):
359
360
    """Simulate a crbanim reload case. Load data as in
361
    UploadCRBAnimTestCase, then call test which reload the same data"""
362
363
    # override used fixtures
364
    fixtures = [
365
        'crbanim/auth',
366
        'crbanim/dictspecie',
367
        'crbanim/image_app',
368
        'crbanim/submission',
369
        'language/speciesynonym'
370
    ]
371
372
373
class SanitizeAccessionTest(TestCase):
374
375
    def setUp(self):
376
        self.queries = [
377
            (
378
                ("https://crb-anim.fr/access-to-collection/#/simplesearch?"
379
                 "SAMPLEIDENTIFIER=Ovar-Mér-3796-1"),
380
                ("https://crb-anim.fr/access-to-collection/#/simplesearch?"
381
                 "SAMPLEIDENTIFIER=Ovar-M%C3%A9r-3796-1")
382
            ),
383
        ]
384
385
    def test_sanitize_accession(self):
386
        for query in self.queries:
387
            url, reference = query
388
            test = sanitize_url(url)
389
            self.assertEqual(reference, test)
390