a b/qiita_db/test/test_artifact.py
1
# -----------------------------------------------------------------------------
2
# Copyright (c) 2014--, The Qiita Development Team.
3
#
4
# Distributed under the terms of the BSD 3-clause License.
5
#
6
# The full license is in the file LICENSE, distributed with this software.
7
# -----------------------------------------------------------------------------
8
9
from unittest import TestCase, main
10
from tempfile import mkstemp, mkdtemp
11
from datetime import datetime
12
from os import close, remove
13
from os.path import exists, join, basename, dirname, abspath
14
from shutil import copyfile
15
from functools import partial
16
from json import dumps
17
18
import pandas as pd
19
import networkx as nx
20
from biom import example_table as et
21
from biom.util import biom_open
22
23
from qiita_core.util import qiita_test_checker
24
from qiita_core.testing import wait_for_processing_job
25
import qiita_db as qdb
26
27
28
class ArtifactTestsReadOnly(TestCase):
29
    def test_iter(self):
30
        obs = list(qdb.artifact.Artifact.iter_by_visibility('public'))
31
        self.assertEqual(obs, [])
32
33
        obs = list(qdb.artifact.Artifact.iter_by_visibility('private'))
34
        exp = [qdb.artifact.Artifact(1),
35
               qdb.artifact.Artifact(2),
36
               qdb.artifact.Artifact(3),
37
               qdb.artifact.Artifact(4),
38
               qdb.artifact.Artifact(5),
39
               qdb.artifact.Artifact(6),
40
               qdb.artifact.Artifact(7)]
41
        self.assertEqual(obs, exp)
42
43
        exp.extend([qdb.artifact.Artifact(8), qdb.artifact.Artifact(9)])
44
        self.assertEqual(list(qdb.artifact.Artifact.iter()), exp)
45
46
    def test_create_type(self):
47
        obs = qdb.artifact.Artifact.types()
48
        exp = [['BIOM', 'BIOM table', False, False, True],
49
               ['Demultiplexed', 'Demultiplexed and QC sequences', True, True,
50
                False],
51
               ['FASTA', None, False, False, False],
52
               ['FASTA_Sanger', None, False, False, False],
53
               ['FASTQ', None, False, False, True],
54
               ['SFF', None, False, False, False],
55
               ['per_sample_FASTQ', None, True, False, True],
56
               ['beta_div_plots', 'Qiime 1 beta diversity results', False,
57
                False, False],
58
               ['rarefaction_curves', 'Rarefaction curves', False, False,
59
                False],
60
               ['taxa_summary', 'Taxa summary plots', False, False, False]]
61
        self.assertCountEqual(obs, exp)
62
63
        qdb.artifact.Artifact.create_type(
64
            "NewType", "NewTypeDesc", False, False, False,
65
            [("log", False), ("raw_forward_seqs", True)])
66
67
        obs = qdb.artifact.Artifact.types()
68
        exp = [['BIOM', 'BIOM table', False, False, True],
69
               ['Demultiplexed', 'Demultiplexed and QC sequences', True, True,
70
                False],
71
               ['FASTA', None, False, False, False],
72
               ['FASTA_Sanger', None, False, False, False],
73
               ['FASTQ', None, False, False, True],
74
               ['SFF', None, False, False, False],
75
               ['per_sample_FASTQ', None, True, False, True],
76
               ['beta_div_plots', 'Qiime 1 beta diversity results', False,
77
                False, False],
78
               ['rarefaction_curves', 'Rarefaction curves', False, False,
79
                False],
80
               ['taxa_summary', 'Taxa summary plots', False, False, False],
81
               ['NewType', 'NewTypeDesc', False, False, False]]
82
        self.assertCountEqual(obs, exp)
83
        self.assertTrue(exists(qdb.util.get_mountpoint('NewType')[0][1]))
84
85
        with self.assertRaises(qdb.exceptions.QiitaDBDuplicateError):
86
            qdb.artifact.Artifact.create_type(
87
                "NewType", "NewTypeDesc", False, False, False,
88
                [("log", False), ("raw_forward_seqs", True)])
89
90
    def test_name(self):
91
        self.assertEqual(qdb.artifact.Artifact(1).name, "Raw data 1")
92
        self.assertEqual(qdb.artifact.Artifact(2).name, "Demultiplexed 1")
93
        self.assertEqual(qdb.artifact.Artifact(3).name, "Demultiplexed 2")
94
        self.assertEqual(qdb.artifact.Artifact(4).name, "BIOM")
95
96
    def test_timestamp(self):
97
        self.assertEqual(qdb.artifact.Artifact(1).timestamp,
98
                         datetime(2012, 10, 1, 9, 30, 27))
99
        self.assertEqual(qdb.artifact.Artifact(2).timestamp,
100
                         datetime(2012, 10, 1, 10, 30, 27))
101
        self.assertEqual(qdb.artifact.Artifact(3).timestamp,
102
                         datetime(2012, 10, 1, 11, 30, 27))
103
        self.assertEqual(qdb.artifact.Artifact(4).timestamp,
104
                         datetime(2012, 10, 2, 17, 30, 00))
105
106
    def test_processing_parameters(self):
107
        self.assertIsNone(qdb.artifact.Artifact(1).processing_parameters)
108
        obs = qdb.artifact.Artifact(2).processing_parameters
109
        exp = qdb.software.Parameters.load(
110
            qdb.software.Command(1),
111
            values_dict={'max_barcode_errors': '1.5', 'sequence_max_n': '0',
112
                         'max_bad_run_length': '3', 'rev_comp': 'False',
113
                         'phred_quality_threshold': '3', 'input_data': '1',
114
                         'rev_comp_barcode': 'False',
115
                         'rev_comp_mapping_barcodes': 'False',
116
                         'min_per_read_length_fraction': '0.75',
117
                         'barcode_type': 'golay_12',
118
                         'phred_offset': 'auto'})
119
        self.assertEqual(obs, exp)
120
        obs = qdb.artifact.Artifact(3).processing_parameters
121
        exp = qdb.software.Parameters.load(
122
            qdb.software.Command(1),
123
            values_dict={'max_barcode_errors': '1.5', 'sequence_max_n': '0',
124
                         'max_bad_run_length': '3', 'rev_comp': 'False',
125
                         'phred_quality_threshold': '3', 'input_data': '1',
126
                         'rev_comp_barcode': 'False',
127
                         'rev_comp_mapping_barcodes': 'True',
128
                         'min_per_read_length_fraction': '0.75',
129
                         'barcode_type': 'golay_12',
130
                         'phred_offset': 'auto'})
131
        self.assertEqual(obs, exp)
132
133
    def test_visibility(self):
134
        self.assertEqual(qdb.artifact.Artifact(1).visibility, "private")
135
136
    def test_artifact_type(self):
137
        self.assertEqual(qdb.artifact.Artifact(1).artifact_type, "FASTQ")
138
        self.assertEqual(qdb.artifact.Artifact(2).artifact_type,
139
                         "Demultiplexed")
140
        self.assertEqual(qdb.artifact.Artifact(3).artifact_type,
141
                         "Demultiplexed")
142
        self.assertEqual(qdb.artifact.Artifact(4).artifact_type, "BIOM")
143
144
    def test_data_type(self):
145
        self.assertEqual(qdb.artifact.Artifact(1).data_type, "18S")
146
        self.assertEqual(qdb.artifact.Artifact(2).data_type, "18S")
147
        self.assertEqual(qdb.artifact.Artifact(3).data_type, "18S")
148
        self.assertEqual(qdb.artifact.Artifact(4).data_type, "18S")
149
150
    def test_can_be_submitted_to_ebi(self):
151
        self.assertFalse(qdb.artifact.Artifact(1).can_be_submitted_to_ebi)
152
        self.assertTrue(qdb.artifact.Artifact(2).can_be_submitted_to_ebi)
153
        self.assertTrue(qdb.artifact.Artifact(3).can_be_submitted_to_ebi)
154
        self.assertFalse(qdb.artifact.Artifact(4).can_be_submitted_to_ebi)
155
156
    def test_is_submitted_to_ebi(self):
157
        self.assertTrue(qdb.artifact.Artifact(2).is_submitted_to_ebi)
158
        self.assertFalse(qdb.artifact.Artifact(3).is_submitted_to_ebi)
159
160
        with self.assertRaises(
161
                qdb.exceptions.QiitaDBOperationNotPermittedError):
162
            qdb.artifact.Artifact(1).is_submitted_to_ebi
163
        with self.assertRaises(
164
                qdb.exceptions.QiitaDBOperationNotPermittedError):
165
            qdb.artifact.Artifact(4).is_submitted_to_ebi
166
167
    def test_ebi_run_accessions(self):
168
        exp = {'1.SKB1.640202': 'ERR0000001',
169
               '1.SKB2.640194': 'ERR0000002',
170
               '1.SKB3.640195': 'ERR0000003',
171
               '1.SKB4.640189': 'ERR0000004',
172
               '1.SKB5.640181': 'ERR0000005',
173
               '1.SKB6.640176': 'ERR0000006',
174
               '1.SKB7.640196': 'ERR0000007',
175
               '1.SKB8.640193': 'ERR0000008',
176
               '1.SKB9.640200': 'ERR0000009',
177
               '1.SKD1.640179': 'ERR0000010',
178
               '1.SKD2.640178': 'ERR0000011',
179
               '1.SKD3.640198': 'ERR0000012',
180
               '1.SKD4.640185': 'ERR0000013',
181
               '1.SKD5.640186': 'ERR0000014',
182
               '1.SKD6.640190': 'ERR0000015',
183
               '1.SKD7.640191': 'ERR0000016',
184
               '1.SKD8.640184': 'ERR0000017',
185
               '1.SKD9.640182': 'ERR0000018',
186
               '1.SKM1.640183': 'ERR0000019',
187
               '1.SKM2.640199': 'ERR0000020',
188
               '1.SKM3.640197': 'ERR0000021',
189
               '1.SKM4.640180': 'ERR0000022',
190
               '1.SKM5.640177': 'ERR0000023',
191
               '1.SKM6.640187': 'ERR0000024',
192
               '1.SKM7.640188': 'ERR0000025',
193
               '1.SKM8.640201': 'ERR0000026',
194
               '1.SKM9.640192': 'ERR0000027'}
195
        self.assertEqual(qdb.artifact.Artifact(2).ebi_run_accessions, exp)
196
        self.assertEqual(qdb.artifact.Artifact(3).ebi_run_accessions, dict())
197
198
        with self.assertRaises(
199
                qdb.exceptions.QiitaDBOperationNotPermittedError):
200
            qdb.artifact.Artifact(1).ebi_run_accessions
201
202
        with self.assertRaises(
203
                qdb.exceptions.QiitaDBOperationNotPermittedError):
204
            qdb.artifact.Artifact(4).ebi_run_accessions
205
206
    def test_can_be_submitted_to_vamps(self):
207
        self.assertFalse(qdb.artifact.Artifact(1).can_be_submitted_to_vamps)
208
        self.assertTrue(qdb.artifact.Artifact(2).can_be_submitted_to_vamps)
209
        self.assertTrue(qdb.artifact.Artifact(3).can_be_submitted_to_vamps)
210
        self.assertFalse(qdb.artifact.Artifact(4).can_be_submitted_to_vamps)
211
212
    def test_is_submitted_to_vamps(self):
213
        with self.assertRaises(
214
                qdb.exceptions.QiitaDBOperationNotPermittedError):
215
            self.assertFalse(qdb.artifact.Artifact(1).is_submitted_to_vamps)
216
        self.assertFalse(qdb.artifact.Artifact(2).is_submitted_to_vamps)
217
        self.assertFalse(qdb.artifact.Artifact(3).is_submitted_to_vamps)
218
        with self.assertRaises(
219
                qdb.exceptions.QiitaDBOperationNotPermittedError):
220
            self.assertFalse(qdb.artifact.Artifact(4).is_submitted_to_vamps)
221
222
    def test_filepaths(self):
223
        db_test_raw_dir = qdb.util.get_mountpoint('raw_data')[0][1]
224
        path_builder = partial(join, db_test_raw_dir)
225
        exp_fps = [{'fp_id': 1,
226
                    'fp': path_builder("1_s_G1_L001_sequences.fastq.gz"),
227
                    'fp_type': "raw_forward_seqs",
228
                    'checksum': '2125826711',
229
                    'fp_size': 58},
230
                   {'fp_id': 2,
231
                    'fp': path_builder(
232
                        "1_s_G1_L001_sequences_barcodes.fastq.gz"),
233
                    'fp_type': "raw_barcodes",
234
                    'checksum': '2125826711',
235
                    'fp_size': 58}]
236
        self.assertEqual(qdb.artifact.Artifact(1).filepaths, exp_fps)
237
238
    def test_parents(self):
239
        self.assertEqual(qdb.artifact.Artifact(1).parents, [])
240
241
        exp_parents = [qdb.artifact.Artifact(1)]
242
        self.assertEqual(qdb.artifact.Artifact(2).parents, exp_parents)
243
        self.assertEqual(qdb.artifact.Artifact(3).parents, exp_parents)
244
245
        exp_parents = [qdb.artifact.Artifact(2)]
246
        self.assertEqual(qdb.artifact.Artifact(4).parents, exp_parents)
247
248
    def test_create_lineage_graph_from_edge_list_empty(self):
249
        tester = qdb.artifact.Artifact(1)
250
        obs = tester._create_lineage_graph_from_edge_list([])
251
        self.assertTrue(isinstance(obs, nx.DiGraph))
252
        self.assertCountEqual(obs.nodes(), [tester])
253
        self.assertCountEqual(obs.edges(), [])
254
255
    def test_create_lineage_graph_from_edge_list(self):
256
        tester = qdb.artifact.Artifact(1)
257
        obs = tester._create_lineage_graph_from_edge_list(
258
            [(1, 2), (2, 4), (1, 3), (3, 4)])
259
        self.assertTrue(isinstance(obs, nx.DiGraph))
260
        exp = [qdb.artifact.Artifact(1), qdb.artifact.Artifact(2),
261
               qdb.artifact.Artifact(3), qdb.artifact.Artifact(4)]
262
        self.assertCountEqual(obs.nodes(), exp)
263
        exp = [(qdb.artifact.Artifact(1), qdb.artifact.Artifact(2)),
264
               (qdb.artifact.Artifact(2), qdb.artifact.Artifact(4)),
265
               (qdb.artifact.Artifact(1), qdb.artifact.Artifact(3)),
266
               (qdb.artifact.Artifact(3), qdb.artifact.Artifact(4))]
267
        self.assertCountEqual(obs.edges(), exp)
268
269
    def test_ancestors(self):
270
        obs = qdb.artifact.Artifact(1).ancestors
271
        self.assertTrue(isinstance(obs, nx.DiGraph))
272
        obs_nodes = obs.nodes()
273
        self.assertCountEqual(obs_nodes, [qdb.artifact.Artifact(1)])
274
        obs_edges = obs.edges()
275
        self.assertCountEqual(obs_edges, [])
276
277
        obs = qdb.artifact.Artifact(2).ancestors
278
        self.assertTrue(isinstance(obs, nx.DiGraph))
279
        obs_nodes = obs.nodes()
280
        exp_nodes = [qdb.artifact.Artifact(1), qdb.artifact.Artifact(2)]
281
        self.assertCountEqual(obs_nodes, exp_nodes)
282
        obs_edges = obs.edges()
283
        exp_edges = [(qdb.artifact.Artifact(1), qdb.artifact.Artifact(2))]
284
        self.assertCountEqual(obs_edges, exp_edges)
285
286
        obs = qdb.artifact.Artifact(3).ancestors
287
        self.assertTrue(isinstance(obs, nx.DiGraph))
288
        obs_nodes = obs.nodes()
289
        exp_nodes = [qdb.artifact.Artifact(1), qdb.artifact.Artifact(3)]
290
        self.assertCountEqual(obs_nodes, exp_nodes)
291
        obs_edges = obs.edges()
292
        exp_edges = [(qdb.artifact.Artifact(1), qdb.artifact.Artifact(3))]
293
        self.assertCountEqual(obs_edges, exp_edges)
294
295
        obs = qdb.artifact.Artifact(4).ancestors
296
        self.assertTrue(isinstance(obs, nx.DiGraph))
297
        obs_nodes = obs.nodes()
298
        exp_nodes = [qdb.artifact.Artifact(1), qdb.artifact.Artifact(2),
299
                     qdb.artifact.Artifact(4)]
300
        self.assertCountEqual(obs_nodes, exp_nodes)
301
        obs_edges = obs.edges()
302
        exp_edges = [(qdb.artifact.Artifact(1), qdb.artifact.Artifact(2)),
303
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(4))]
304
        self.assertCountEqual(obs_edges, exp_edges)
305
306
    def test_descendants(self):
307
        obs = qdb.artifact.Artifact(1).descendants
308
        self.assertTrue(isinstance(obs, nx.DiGraph))
309
        obs_nodes = obs.nodes()
310
        exp_nodes = [qdb.artifact.Artifact(1), qdb.artifact.Artifact(2),
311
                     qdb.artifact.Artifact(3), qdb.artifact.Artifact(4),
312
                     qdb.artifact.Artifact(5), qdb.artifact.Artifact(6)]
313
        self.assertCountEqual(obs_nodes, exp_nodes)
314
        obs_edges = obs.edges()
315
        exp_edges = [(qdb.artifact.Artifact(1), qdb.artifact.Artifact(2)),
316
                     (qdb.artifact.Artifact(1), qdb.artifact.Artifact(3)),
317
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(4)),
318
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(5)),
319
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(6))]
320
        self.assertCountEqual(obs_edges, exp_edges)
321
322
        obs = qdb.artifact.Artifact(2).descendants
323
        self.assertTrue(isinstance(obs, nx.DiGraph))
324
        obs_nodes = obs.nodes()
325
        exp_nodes = [qdb.artifact.Artifact(2), qdb.artifact.Artifact(4),
326
                     qdb.artifact.Artifact(5), qdb.artifact.Artifact(6)]
327
        self.assertCountEqual(obs_nodes, exp_nodes)
328
        obs_edges = obs.edges()
329
        exp_edges = [(qdb.artifact.Artifact(2), qdb.artifact.Artifact(4)),
330
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(5)),
331
                     (qdb.artifact.Artifact(2), qdb.artifact.Artifact(6))]
332
        self.assertCountEqual(obs_edges, exp_edges)
333
334
        obs = qdb.artifact.Artifact(3).descendants
335
        self.assertTrue(isinstance(obs, nx.DiGraph))
336
        obs_nodes = obs.nodes()
337
        self.assertCountEqual(obs_nodes, [qdb.artifact.Artifact(3)])
338
        obs_edges = obs.edges()
339
        self.assertCountEqual(obs_edges, [])
340
341
        obs = qdb.artifact.Artifact(4).descendants
342
        self.assertTrue(isinstance(obs, nx.DiGraph))
343
        obs_nodes = obs.nodes()
344
        self.assertCountEqual(obs_nodes, [qdb.artifact.Artifact(4)])
345
        obs_edges = obs.edges()
346
        self.assertCountEqual(obs_edges, [])
347
348
    def test_descendants_with_jobs(self):
349
        A = qdb.artifact.Artifact
350
        obs = A(1).descendants_with_jobs
351
        self.assertTrue(isinstance(obs, nx.DiGraph))
352
        obs_nodes = obs.nodes()
353
354
        # Add an HTML summary job in one artifact in a non-success statuts, to
355
        # make sure that it doesn't get returned in the graph
356
        html_job = qdb.processing_job.ProcessingJob.create(
357
            qdb.user.User('test@foo.bar'),
358
            qdb.software.Parameters.load(
359
                qdb.software.Command.get_html_generator(A(6).artifact_type),
360
                values_dict={'input_data': 6}))
361
        html_job._set_status('running')
362
        # as jobs are created at random we will only check that the artifacts
363
        # are there and that the number of jobs matches
364
        exp_nodes = [('artifact', A(1)), ('artifact', A(2)),
365
                     ('artifact', A(3)), ('artifact', A(4)),
366
                     ('artifact', A(5)), ('artifact', A(6))]
367
        for e in exp_nodes:
368
            self.assertIn(e, obs_nodes)
369
        self.assertEqual(5, len([e for dt, e in obs_nodes if dt == 'job']))
370
        obs_edges = obs.edges()
371
        # as jobs are created at random we will only check the number of pairs
372
        # matches and they are instances of what we expect
373
        self.assertEqual(10, len(obs_edges))
374
        self.assertEqual(2, len([x for x, y in obs_edges
375
                                 if x[1] == A(1) and y[0] == 'job']))
376
        self.assertEqual(3, len([x for x, y in obs_edges
377
                                 if x[1] == A(2) and y[0] == 'job']))
378
        self.assertEqual(1, len([y for x, y in obs_edges
379
                                 if y[1] == A(2) and x[0] == 'job']))
380
        self.assertEqual(1, len([y for x, y in obs_edges
381
                                 if y[1] == A(3) and x[0] == 'job']))
382
        self.assertEqual(1, len([y for x, y in obs_edges
383
                                 if y[1] == A(4) and x[0] == 'job']))
384
        self.assertEqual(1, len([y for x, y in obs_edges
385
                                 if y[1] == A(5) and x[0] == 'job']))
386
        self.assertEqual(1, len([y for x, y in obs_edges
387
                                 if y[1] == A(6) and x[0] == 'job']))
388
389
        obs = A(3).descendants
390
        self.assertTrue(isinstance(obs, nx.DiGraph))
391
        obs_nodes = obs.nodes()
392
        self.assertCountEqual(obs_nodes, [A(3)])
393
        obs_edges = obs.edges()
394
        self.assertCountEqual(obs_edges, [])
395
396
        # Create a workflow starting in the artifact 1, so we can test that
397
        # "in construction" jobs also show up correctly
398
        json_str = (
399
            '{"input_data": 1, "max_barcode_errors": 1.5, '
400
            '"barcode_type": "8", "max_bad_run_length": 3, '
401
            '"rev_comp": false, "phred_quality_threshold": 3, '
402
            '"rev_comp_barcode": false, "rev_comp_mapping_barcodes": false, '
403
            '"min_per_read_length_fraction": 0.75, "sequence_max_n": 0, '
404
            '"phred_offset": "auto"}')
405
        params = qdb.software.Parameters.load(qdb.software.Command(1),
406
                                              json_str=json_str)
407
        wf = qdb.processing_job.ProcessingWorkflow.from_scratch(
408
            qdb.user.User('test@foo.bar'), params, name='Test WF')
409
        parent = list(wf.graph.nodes())[0]
410
        wf.add(qdb.software.DefaultParameters(10),
411
               connections={parent: {'demultiplexed': 'input_data'}})
412
        obs = A(1).descendants_with_jobs
413
        obs_edges = obs.edges()
414
        # We have 4 more edges than before. From artifact 1 to parent job,
415
        # from parent job to output, from output to child job, and from child
416
        # job to child output
417
        self.assertEqual(len(obs_edges), 14)
418
        # We will check that the edges related with the "type" nodes (i.e.
419
        # the outputs of the jobs in construction) are present
420
        self.assertEqual(1, len([y for x, y in obs_edges if x[0] == 'type']))
421
        self.assertEqual(2, len([y for x, y in obs_edges if y[0] == 'type']))
422
423
    def test_children(self):
424
        exp = [qdb.artifact.Artifact(2), qdb.artifact.Artifact(3)]
425
        self.assertEqual(qdb.artifact.Artifact(1).children, exp)
426
        exp = [qdb.artifact.Artifact(4), qdb.artifact.Artifact(5),
427
               qdb.artifact.Artifact(6)]
428
        self.assertEqual(qdb.artifact.Artifact(2).children, exp)
429
        self.assertEqual(qdb.artifact.Artifact(3).children, [])
430
        self.assertEqual(qdb.artifact.Artifact(4).children, [])
431
432
    def test_youngest_artifact(self):
433
        exp = qdb.artifact.Artifact(6)
434
        self.assertEqual(qdb.artifact.Artifact(1).youngest_artifact, exp)
435
        self.assertEqual(qdb.artifact.Artifact(2).youngest_artifact, exp)
436
        self.assertEqual(qdb.artifact.Artifact(3).youngest_artifact,
437
                         qdb.artifact.Artifact(3))
438
        self.assertEqual(qdb.artifact.Artifact(6).youngest_artifact, exp)
439
440
    def test_prep_templates(self):
441
        self.assertEqual(
442
            qdb.artifact.Artifact(1).prep_templates,
443
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
444
        self.assertEqual(
445
            qdb.artifact.Artifact(2).prep_templates,
446
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
447
        self.assertEqual(
448
            qdb.artifact.Artifact(3).prep_templates,
449
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
450
        self.assertEqual(
451
            qdb.artifact.Artifact(4).prep_templates,
452
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
453
454
    def test_study(self):
455
        self.assertEqual(qdb.artifact.Artifact(1).study, qdb.study.Study(1))
456
        self.assertIsNone(qdb.artifact.Artifact(9).study)
457
458
    def test_analysis(self):
459
        self.assertEqual(qdb.artifact.Artifact(9).analysis,
460
                         qdb.analysis.Analysis(1))
461
        self.assertIsNone(qdb.artifact.Artifact(1).analysis)
462
463
    def test_merging_scheme(self):
464
        self.assertEqual(qdb.artifact.Artifact(1).merging_scheme, ('', ''))
465
        self.assertEqual(qdb.artifact.Artifact(2).merging_scheme,
466
                         ('Split libraries FASTQ | N/A', 'N/A'))
467
        self.assertEqual(qdb.artifact.Artifact(3).merging_scheme,
468
                         ('Split libraries FASTQ | N/A', 'N/A'))
469
        self.assertEqual(qdb.artifact.Artifact(4).merging_scheme,
470
                         ('Pick closed-reference OTUs | Split libraries FASTQ',
471
                          'QIIMEq2 v1.9.1'))
472
        self.assertEqual(qdb.artifact.Artifact(5).merging_scheme,
473
                         ('Pick closed-reference OTUs | Split libraries FASTQ',
474
                          'QIIMEq2 v1.9.1'))
475
476
    def test_jobs(self):
477
        # Returning all jobs
478
        obs = qdb.artifact.Artifact(1).jobs(show_hidden=True)
479
        exp = [
480
            qdb.processing_job.ProcessingJob(
481
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
482
            qdb.processing_job.ProcessingJob(
483
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
484
            qdb.processing_job.ProcessingJob(
485
                '063e553b-327c-4818-ab4a-adfe58e49860'),
486
            qdb.processing_job.ProcessingJob(
487
                'bcc7ebcd-39c1-43e4-af2d-822e3589f14d'),
488
            qdb.processing_job.ProcessingJob(
489
                'b72369f9-a886-4193-8d3d-f7b504168e75')]
490
491
        # there are some extra jobs randomly generated, not testing those
492
        for e in exp:
493
            self.assertIn(e, obs)
494
495
        # Returning only jobs visible by the user
496
        obs = qdb.artifact.Artifact(1).jobs()
497
        exp = [
498
            qdb.processing_job.ProcessingJob(
499
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
500
            qdb.processing_job.ProcessingJob(
501
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
502
            qdb.processing_job.ProcessingJob(
503
                'b72369f9-a886-4193-8d3d-f7b504168e75')]
504
505
        for e in exp:
506
            self.assertIn(e, obs)
507
508
    def test_jobs_cmd(self):
509
        cmd = qdb.software.Command(1)
510
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, show_hidden=True)
511
        exp = [
512
            qdb.processing_job.ProcessingJob(
513
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
514
            qdb.processing_job.ProcessingJob(
515
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
516
            qdb.processing_job.ProcessingJob(
517
                '063e553b-327c-4818-ab4a-adfe58e49860'),
518
            qdb.processing_job.ProcessingJob(
519
                'b72369f9-a886-4193-8d3d-f7b504168e75')
520
            ]
521
        # there are some extra jobs randomly generated, not testing those
522
        for e in exp:
523
            self.assertIn(e, obs)
524
525
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd)
526
        exp = [
527
            qdb.processing_job.ProcessingJob(
528
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
529
            qdb.processing_job.ProcessingJob(
530
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
531
            qdb.processing_job.ProcessingJob(
532
                'b72369f9-a886-4193-8d3d-f7b504168e75')
533
            ]
534
535
        cmd = qdb.software.Command(2)
536
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, show_hidden=True)
537
        exp = [qdb.processing_job.ProcessingJob(
538
            'bcc7ebcd-39c1-43e4-af2d-822e3589f14d')]
539
        self.assertEqual(obs, exp)
540
541
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd)
542
        self.assertEqual(obs, [])
543
544
    def test_jobs_status(self):
545
        obs = qdb.artifact.Artifact(1).jobs(status='success')
546
        exp = [
547
            qdb.processing_job.ProcessingJob(
548
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
549
            qdb.processing_job.ProcessingJob(
550
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
551
            qdb.processing_job.ProcessingJob(
552
                'b72369f9-a886-4193-8d3d-f7b504168e75')
553
            ]
554
        # there are some extra jobs randomly generated, not testing those
555
        for e in exp:
556
            self.assertIn(e, obs)
557
558
        obs = qdb.artifact.Artifact(1).jobs(status='running', show_hidden=True)
559
        exp = [qdb.processing_job.ProcessingJob(
560
            'bcc7ebcd-39c1-43e4-af2d-822e3589f14d')]
561
        self.assertEqual(obs, exp)
562
563
        obs = qdb.artifact.Artifact(1).jobs(status='running')
564
        self.assertEqual(obs, [])
565
566
        obs = qdb.artifact.Artifact(1).jobs(status='queued', show_hidden=True)
567
        exp = [qdb.processing_job.ProcessingJob(
568
            '063e553b-327c-4818-ab4a-adfe58e49860')]
569
        self.assertEqual(obs, exp)
570
571
        obs = qdb.artifact.Artifact(1).jobs(status='queued')
572
        self.assertEqual(obs, [])
573
574
    def test_jobs_cmd_and_status(self):
575
        cmd = qdb.software.Command(1)
576
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, status='success')
577
        exp = [
578
            qdb.processing_job.ProcessingJob(
579
                '6d368e16-2242-4cf8-87b4-a5dc40bb890b'),
580
            qdb.processing_job.ProcessingJob(
581
                '4c7115e8-4c8e-424c-bf25-96c292ca1931'),
582
            qdb.processing_job.ProcessingJob(
583
                'b72369f9-a886-4193-8d3d-f7b504168e75')
584
            ]
585
        # there are some extra jobs randomly generated, not testing those
586
        for e in exp:
587
            self.assertIn(e, obs)
588
589
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, status='queued',
590
                                            show_hidden=True)
591
        exp = [qdb.processing_job.ProcessingJob(
592
            '063e553b-327c-4818-ab4a-adfe58e49860')]
593
        self.assertEqual(obs, exp)
594
595
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, status='queued')
596
        self.assertEqual(obs, [])
597
598
        cmd = qdb.software.Command(2)
599
        obs = qdb.artifact.Artifact(1).jobs(cmd=cmd, status='queued')
600
        exp = []
601
        self.assertEqual(obs, exp)
602
603
    def test_get_commands(self):
604
        # we will check only ids for simplicity
605
        # checking processing artifacts
606
        obs = [c.id for c in qdb.artifact.Artifact(1).get_commands]
607
        self.assertEqual(obs, [1])
608
        obs = [c.id for c in qdb.artifact.Artifact(2).get_commands]
609
        self.assertEqual(obs, [3])
610
        # this is a biom in processing, so no commands should be available
611
        obs = [c.id for c in qdb.artifact.Artifact(6).get_commands]
612
        self.assertEqual(obs, [])
613
614
        # checking analysis object - this is a biom in analysis, several
615
        # commands should be available
616
        obs = [c.id for c in qdb.artifact.Artifact(8).get_commands]
617
        self.assertEqual(obs, [9, 10, 11, 12])
618
619
620
@qiita_test_checker()
621
class ArtifactTests(TestCase):
622
    def setUp(self):
623
        # Generate some files for a root artifact
624
        fd, self.fp1 = mkstemp(suffix='_seqs.fastq')
625
        close(fd)
626
        with open(self.fp1, 'w') as f:
627
            f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 1:N:0:\n"
628
                    "NACGTAGGGTGCAAGCGTTGTCCGGAATNA\n"
629
                    "+\n"
630
                    "#1=DDFFFHHHHHJJJJJJJJJJJJGII#0\n")
631
632
        fd, self.fp2 = mkstemp(suffix='_barcodes.fastq')
633
        close(fd)
634
        with open(self.fp2, 'w') as f:
635
            f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 2:N:0:\n"
636
                    "NNNCNNNNNNNNN\n"
637
                    "+\n"
638
                    "#############\n")
639
        self.filepaths_root = [(self.fp1, 1), (self.fp2, 3)]
640
641
        # Generate some files for a processed artifact
642
        fd, self.fp3 = mkstemp(suffix='_seqs.fna')
643
        close(fd)
644
        with open(self.fp3, 'w') as f:
645
            f.write(">1.sid_r4_0 M02034:17:000000000-A5U18:1:1101:15370:1394 "
646
                    "1:N:0:1 orig_bc=CATGAGCT new_bc=CATGAGCT bc_diffs=0\n"
647
                    "GTGTGCCAGCAGCCGCGGTAATACGTAGGG\n")
648
        self.filepaths_processed = [(self.fp3, 4)]
649
650
        # Generate some file for a BIOM
651
        fd, self.fp4 = mkstemp(suffix='_table.biom')
652
        with biom_open(self.fp4, 'w') as f:
653
            et.to_hdf5(f, "test")
654
        self.filepaths_biom = [(self.fp4, 7)]
655
656
        # Create a new prep template
657
        metadata_dict = {
658
            'SKB8.640193': {'center_name': 'ANL',
659
                            'primer': 'GTGCCAGCMGCCGCGGTAA',
660
                            'barcode': 'GTCCGCAAGTTA',
661
                            'run_prefix': "s_G1_L001_sequences",
662
                            'platform': 'Illumina',
663
                            'instrument_model': 'Illumina MiSeq',
664
                            'library_construction_protocol': 'AAAA',
665
                            'target_subfragment': 'V4',
666
                            'target_gene': '16S rRNA',
667
                            'experiment_design_description': 'BBBB'}}
668
        metadata = pd.DataFrame.from_dict(metadata_dict, orient='index',
669
                                          dtype=str)
670
        self.prep_template = \
671
            qdb.metadata_template.prep_template.PrepTemplate.create(
672
                metadata, qdb.study.Study(1), "16S")
673
        self.prep_template_2 = \
674
            qdb.metadata_template.prep_template.PrepTemplate.create(
675
                metadata, qdb.study.Study(1), "16S")
676
677
        self._clean_up_files = [self.fp1, self.fp2, self.fp3, self.fp4]
678
679
        # per_sample_FASTQ Metagenomic example
680
681
        self.prep_template_per_sample_fastq = \
682
            qdb.metadata_template.prep_template.PrepTemplate.create(
683
                metadata, qdb.study.Study(1), "Metagenomic")
684
        fd, self.fwd = mkstemp(prefix='SKB8.640193', suffix='_R1.fastq')
685
        close(fd)
686
        with open(self.fwd, 'w') as f:
687
            f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 1:N:0:\n"
688
                    "NACGTAGGGTGCAAGCGTTGTCCGGAATNA\n"
689
                    "+\n"
690
                    "#1=DDFFFHHHHHJJJJJJJJJJJJGII#0\n")
691
        fd, self.rev = mkstemp(prefix='SKB8.640193', suffix='_R2.fastq')
692
        close(fd)
693
        with open(self.rev, 'w') as f:
694
            f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 1:N:0:\n"
695
                    "NACGTAGGGTGCAAGCGTTGTCCGGAATNA\n"
696
                    "+\n"
697
                    "#1=DDFFFHHHHHJJJJJJJJJJJJGII#0\n")
698
699
        self._clean_up_files.extend([self.fwd, self.rev])
700
701
        self.user = qdb.user.User('test@foo.bar')
702
703
    def tearDown(self):
704
        for f in self._clean_up_files:
705
            if exists(f):
706
                remove(f)
707
708
    def test_copy(self):
709
        src = qdb.artifact.Artifact.create(
710
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
711
        before = datetime.now()
712
        obs = qdb.artifact.Artifact.copy(src, self.prep_template_2)
713
714
        self.assertTrue(before < obs.timestamp < datetime.now())
715
        self.assertIsNone(obs.processing_parameters)
716
        self.assertEqual(obs.visibility, 'sandbox')
717
        self.assertEqual(obs.artifact_type, src.artifact_type)
718
        self.assertEqual(obs.data_type, self.prep_template.data_type())
719
        self.assertEqual(obs.can_be_submitted_to_ebi,
720
                         src.can_be_submitted_to_ebi)
721
        self.assertEqual(obs.can_be_submitted_to_vamps,
722
                         src.can_be_submitted_to_vamps)
723
724
        db_dir = qdb.util.get_mountpoint(src.artifact_type)[0][1]
725
        path_builder = partial(join, db_dir, str(obs.id))
726
        exp_fps = []
727
        for x in src.filepaths:
728
            new_fp = path_builder(basename(x['fp']))
729
            exp_fps.append((new_fp, x['fp_type']))
730
            self._clean_up_files.append(new_fp)
731
732
        self.assertEqual([(x['fp'], x['fp_type'])
733
                          for x in obs.filepaths], exp_fps)
734
        self.assertEqual(obs.parents, [])
735
        self.assertEqual(obs.prep_templates, [self.prep_template_2])
736
737
        self.assertEqual(obs.study, qdb.study.Study(1))
738
739
    def test_create_error(self):
740
        # no filepaths
741
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
742
            qdb.artifact.Artifact.create(
743
                [], "FASTQ", prep_template=self.prep_template)
744
745
        # prep template and parents
746
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
747
            qdb.artifact.Artifact.create(
748
                self.filepaths_root, "FASTQ", prep_template=self.prep_template,
749
                parents=[qdb.artifact.Artifact(1)])
750
751
        # analysis and prep_template
752
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
753
            qdb.artifact.Artifact.create(
754
                self.filepaths_root, "BIOM", prep_template=self.prep_template,
755
                analysis=qdb.analysis.Analysis(1))
756
757
        # Analysis and parents
758
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
759
            qdb.artifact.Artifact.create(
760
                self.filepaths_root, "BIOM",
761
                parents=[qdb.artifact.Artifact(1)],
762
                analysis=qdb.analysis.Analysis(1))
763
764
        # no prep template no parents no analysis
765
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
766
            qdb.artifact.Artifact.create(self.filepaths_root, "FASTQ")
767
768
        # parents no processing parameters
769
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
770
            qdb.artifact.Artifact.create(
771
                self.filepaths_root, "FASTQ",
772
                parents=[qdb.artifact.Artifact(1)])
773
774
        # analysis no data type
775
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
776
            qdb.artifact.Artifact.create(
777
                self.filepaths_root, "BIOM", analysis=qdb.analysis.Analysis(1))
778
779
        # prep template and processing parameters
780
        parameters = qdb.software.Parameters.from_default_params(
781
            qdb.software.DefaultParameters(1), {'input_data': 1})
782
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
783
            qdb.artifact.Artifact.create(
784
                self.filepaths_root, "FASTQ", prep_template=self.prep_template,
785
                processing_parameters=parameters)
786
787
        # prep template and data type
788
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
789
            qdb.artifact.Artifact.create(
790
                self.filepaths_root, "FASTQ", prep_template=self.prep_template,
791
                data_type="Multiomic")
792
793
        # different data types
794
        new = qdb.artifact.Artifact.create(
795
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
796
        parameters = qdb.software.Parameters.from_default_params(
797
            qdb.software.DefaultParameters(1), {'input_data': 1})
798
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactCreationError):
799
            qdb.artifact.Artifact.create(
800
                self.filepaths_processed, "Demultiplexed",
801
                parents=[qdb.artifact.Artifact(1), new],
802
                processing_parameters=parameters)
803
804
    def test_create_root(self):
805
        before = datetime.now()
806
        obs = qdb.artifact.Artifact.create(
807
            self.filepaths_root, "FASTQ", prep_template=self.prep_template,
808
            name='Test artifact')
809
        self.assertEqual(obs.name, 'Test artifact')
810
        self.assertTrue(before < obs.timestamp < datetime.now())
811
        self.assertIsNone(obs.processing_parameters)
812
        self.assertEqual(obs.visibility, 'sandbox')
813
        self.assertEqual(obs.artifact_type, "FASTQ")
814
        self.assertEqual(obs.data_type, self.prep_template.data_type())
815
        self.assertFalse(obs.can_be_submitted_to_ebi)
816
        self.assertFalse(obs.can_be_submitted_to_vamps)
817
818
        db_fastq_dir = qdb.util.get_mountpoint('FASTQ')[0][1]
819
        path_builder = partial(join, db_fastq_dir, str(obs.id))
820
        exp_fps = [
821
            (path_builder(basename(self.fp1)), "raw_forward_seqs"),
822
            (path_builder(basename(self.fp2)), "raw_barcodes")]
823
        self.assertEqual([(x['fp'], x['fp_type'])
824
                          for x in obs.filepaths], exp_fps)
825
        self.assertEqual(obs.parents, [])
826
        self.assertEqual(obs.prep_templates, [self.prep_template])
827
828
        with self.assertRaises(
829
                qdb.exceptions.QiitaDBOperationNotPermittedError):
830
            obs.ebi_run_accessions
831
832
        with self.assertRaises(
833
                qdb.exceptions.QiitaDBOperationNotPermittedError):
834
            obs.is_submitted_to_vamps
835
836
        self.assertEqual(obs.study, qdb.study.Study(1))
837
        self.assertIsNone(obs.analysis)
838
839
    def test_create_root_analysis(self):
840
        before = datetime.now()
841
        obs = qdb.artifact.Artifact.create(
842
            self.filepaths_biom, "BIOM", name='Test artifact analysis',
843
            analysis=qdb.analysis.Analysis(1), data_type="16S")
844
        self.assertEqual(obs.name, 'Test artifact analysis')
845
        self.assertTrue(before < obs.timestamp < datetime.now())
846
        self.assertIsNone(obs.processing_parameters)
847
        self.assertEqual(obs.visibility, 'sandbox')
848
        self.assertEqual(obs.artifact_type, "BIOM")
849
        self.assertEqual(obs.data_type, "16S")
850
        self.assertFalse(obs.can_be_submitted_to_ebi)
851
        self.assertFalse(obs.can_be_submitted_to_vamps)
852
853
        db_fastq_dir = qdb.util.get_mountpoint('BIOM')[0][1]
854
        path_builder = partial(join, db_fastq_dir, str(obs.id))
855
        exp_fps = [(path_builder(basename(self.fp4)), "biom")]
856
        self.assertEqual([(x['fp'], x['fp_type'])
857
                          for x in obs.filepaths], exp_fps)
858
        self.assertEqual(obs.parents, [])
859
        self.assertEqual(obs.prep_templates, [])
860
861
        with self.assertRaises(
862
                qdb.exceptions.QiitaDBOperationNotPermittedError):
863
            obs.ebi_run_accessions
864
865
        with self.assertRaises(
866
                qdb.exceptions.QiitaDBOperationNotPermittedError):
867
            obs.is_submitted_to_vamps
868
869
        self.assertIsNone(obs.study)
870
        self.assertEqual(obs.analysis, qdb.analysis.Analysis(1))
871
872
        # testing that it can be deleted
873
        qdb.artifact.Artifact.delete(obs.id)
874
875
    def test_create_processed(self):
876
        # make a copy of files for the can_be_submitted_to_ebi tests
877
        lcopy = self.fp3 + '.fna'
878
        self._clean_up_files.append(lcopy)
879
        copyfile(self.fp3, lcopy)
880
881
        exp_params = qdb.software.Parameters.from_default_params(
882
            qdb.software.DefaultParameters(1), {'input_data': 1})
883
        before = datetime.now()
884
        obs = qdb.artifact.Artifact.create(
885
            self.filepaths_processed, "Demultiplexed",
886
            parents=[qdb.artifact.Artifact(1)],
887
            processing_parameters=exp_params)
888
        self.assertEqual(obs.name, 'noname')
889
        self.assertTrue(before < obs.timestamp < datetime.now())
890
        self.assertEqual(obs.processing_parameters, exp_params)
891
        self.assertEqual(obs.visibility, 'private')
892
        self.assertEqual(obs.artifact_type, "Demultiplexed")
893
        self.assertEqual(obs.data_type, qdb.artifact.Artifact(1).data_type)
894
        self.assertTrue(obs.can_be_submitted_to_ebi)
895
        self.assertTrue(obs.can_be_submitted_to_vamps)
896
        self.assertFalse(obs.is_submitted_to_vamps)
897
898
        db_demultiplexed_dir = qdb.util.get_mountpoint('Demultiplexed')[0][1]
899
        path_builder = partial(join, db_demultiplexed_dir, str(obs.id))
900
        exp_fps = [(path_builder(basename(self.fp3)),
901
                    "preprocessed_fasta")]
902
        self.assertEqual([(x['fp'], x['fp_type'])
903
                          for x in obs.filepaths], exp_fps)
904
        self.assertEqual(obs.parents, [qdb.artifact.Artifact(1)])
905
        self.assertEqual(
906
            obs.prep_templates,
907
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
908
        self.assertEqual(obs.ebi_run_accessions, dict())
909
        self.assertEqual(obs.study, qdb.study.Study(1))
910
        self.assertFalse(exists(self.filepaths_processed[0][0]))
911
        self.assertIsNone(obs.analysis)
912
913
        # let's create another demultiplexed on top of the previous one to
914
        # test can_be_submitted_to_ebi
915
        exp_params = qdb.software.Parameters.from_default_params(
916
            qdb.software.DefaultParameters(1), {'input_data': obs.id})
917
        new = qdb.artifact.Artifact.create(
918
            [(lcopy, 4)], "Demultiplexed", parents=[obs],
919
            processing_parameters=exp_params)
920
        self.assertFalse(new.can_be_submitted_to_ebi)
921
922
    def test_create_copy_files(self):
923
        exp_params = qdb.software.Parameters.from_default_params(
924
            qdb.software.DefaultParameters(1), {'input_data': 1})
925
        before = datetime.now()
926
        obs = qdb.artifact.Artifact.create(
927
            self.filepaths_processed, "Demultiplexed",
928
            parents=[qdb.artifact.Artifact(1)],
929
            processing_parameters=exp_params, move_files=False)
930
        self.assertEqual(obs.name, 'noname')
931
        self.assertTrue(before < obs.timestamp < datetime.now())
932
        self.assertEqual(obs.processing_parameters, exp_params)
933
        self.assertEqual(obs.visibility, 'private')
934
        self.assertEqual(obs.artifact_type, "Demultiplexed")
935
        self.assertEqual(obs.data_type, qdb.artifact.Artifact(1).data_type)
936
        self.assertTrue(obs.can_be_submitted_to_ebi)
937
        self.assertTrue(obs.can_be_submitted_to_vamps)
938
        self.assertFalse(obs.is_submitted_to_vamps)
939
940
        db_demultiplexed_dir = qdb.util.get_mountpoint('Demultiplexed')[0][1]
941
        path_builder = partial(join, db_demultiplexed_dir, str(obs.id))
942
        exp_fps = [(path_builder(basename(self.fp3)),
943
                    "preprocessed_fasta")]
944
        self.assertEqual([(x['fp'], x['fp_type'])
945
                          for x in obs.filepaths], exp_fps)
946
        self.assertEqual(obs.parents, [qdb.artifact.Artifact(1)])
947
        self.assertEqual(
948
            obs.prep_templates,
949
            [qdb.metadata_template.prep_template.PrepTemplate(1)])
950
        self.assertEqual(obs.ebi_run_accessions, dict())
951
        self.assertEqual(obs.study, qdb.study.Study(1))
952
        self.assertTrue(exists(self.filepaths_processed[0][0]))
953
        self.assertIsNone(obs.analysis)
954
955
    def test_create_biom(self):
956
        before = datetime.now()
957
        cmd = qdb.software.Command(3)
958
        exp_params = qdb.software.Parameters.from_default_params(
959
            next(cmd.default_parameter_sets), {'input_data': 1})
960
        obs = qdb.artifact.Artifact.create(
961
            self.filepaths_biom, "BIOM", parents=[qdb.artifact.Artifact(2)],
962
            processing_parameters=exp_params)
963
        self.assertEqual(obs.name, 'noname')
964
        self.assertTrue(before < obs.timestamp < datetime.now())
965
        self.assertEqual(obs.processing_parameters, exp_params)
966
        self.assertEqual(obs.visibility, 'private')
967
        self.assertEqual(obs.artifact_type, 'BIOM')
968
        self.assertEqual(obs.data_type, qdb.artifact.Artifact(2).data_type)
969
        self.assertFalse(obs.can_be_submitted_to_ebi)
970
        self.assertFalse(obs.can_be_submitted_to_vamps)
971
        with self.assertRaises(
972
                qdb.exceptions.QiitaDBOperationNotPermittedError):
973
            obs.ebi_run_accessions
974
975
        with self.assertRaises(
976
                qdb.exceptions.QiitaDBOperationNotPermittedError):
977
            obs.is_submitted_to_vamps
978
979
        db_biom_dir = qdb.util.get_mountpoint('BIOM')[0][1]
980
        path_builder = partial(join, db_biom_dir, str(obs.id))
981
        exp_fps = [(path_builder(basename(self.fp4)), 'biom')]
982
        self.assertEqual([(x['fp'], x['fp_type'])
983
                          for x in obs.filepaths], exp_fps)
984
        self.assertEqual(obs.parents, [qdb.artifact.Artifact(2)])
985
        self.assertEqual(obs.prep_templates,
986
                         [qdb.metadata_template.prep_template.PrepTemplate(1)])
987
        self.assertEqual(obs.study, qdb.study.Study(1))
988
        self.assertIsNone(obs.analysis)
989
990
    def test_delete_error_public(self):
991
        test = qdb.artifact.Artifact.create(
992
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
993
        test.visibility = "public"
994
        self._clean_up_files.extend([x['fp'] for x in test.filepaths])
995
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
996
            qdb.artifact.Artifact.delete(test.id)
997
998
    def test_delete_error_has_children(self):
999
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
1000
            qdb.artifact.Artifact.delete(1)
1001
1002
    def test_delete_error_analyzed(self):
1003
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
1004
            qdb.artifact.Artifact.delete(4)
1005
1006
    def test_delete_error_ebi(self):
1007
        parameters = qdb.software.Parameters.from_default_params(
1008
            qdb.software.DefaultParameters(1), {'input_data': 1})
1009
        obs = qdb.artifact.Artifact.create(
1010
            self.filepaths_processed, "Demultiplexed",
1011
            parents=[qdb.artifact.Artifact(1)],
1012
            processing_parameters=parameters)
1013
        obs.ebi_run_accessions = {'1.SKB1.640202': 'ERR1000001',
1014
                                  '1.SKB2.640194': 'ERR1000002'}
1015
        self._clean_up_files.extend([x['fp'] for x in obs.filepaths])
1016
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
1017
            qdb.artifact.Artifact.delete(obs.id)
1018
1019
    def test_delete_error_vamps(self):
1020
        parameters = qdb.software.Parameters.from_default_params(
1021
            qdb.software.DefaultParameters(1), {'input_data': 1})
1022
        obs = qdb.artifact.Artifact.create(
1023
            self.filepaths_processed, "Demultiplexed",
1024
            parents=[qdb.artifact.Artifact(1)],
1025
            processing_parameters=parameters)
1026
        obs.is_submitted_to_vamps = True
1027
        self._clean_up_files.extend([x['fp'] for x in obs.filepaths])
1028
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
1029
            qdb.artifact.Artifact.delete(obs.id)
1030
1031
    def test_delete_in_construction_job(self):
1032
        test = qdb.artifact.Artifact.create(
1033
            self.filepaths_root, 'FASTQ', prep_template=self.prep_template)
1034
        self._clean_up_files.extend([x['fp'] for x in test.filepaths])
1035
        json_str = (
1036
            '{"input_data": %d, "max_barcode_errors": 1.5, '
1037
            '"barcode_type": "golay_12", "max_bad_run_length": 3, '
1038
            '"rev_comp": false, "phred_quality_threshold": 3, '
1039
            '"rev_comp_barcode": false, "rev_comp_mapping_barcodes": false, '
1040
            '"min_per_read_length_fraction": 0.75, "sequence_max_n": 0, '
1041
            '"phred_offset": ""}' % test.id)
1042
        qdb.processing_job.ProcessingJob.create(
1043
            self.user,
1044
            qdb.software.Parameters.load(qdb.software.Command(1),
1045
                                         json_str=json_str))
1046
        uploads_fp = join(qdb.util.get_mountpoint("uploads")[0][1],
1047
                          str(test.study.id))
1048
        self._clean_up_files.extend(
1049
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1050
1051
        qdb.artifact.Artifact.delete(test.id)
1052
1053
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1054
            qdb.artifact.Artifact(test.id)
1055
1056
    def test_delete_error_running_job(self):
1057
        test = qdb.artifact.Artifact.create(
1058
            self.filepaths_root, 'FASTQ', prep_template=self.prep_template)
1059
        self._clean_up_files.extend([x['fp'] for x in test.filepaths])
1060
        json_str = (
1061
            '{"input_data": %d, "max_barcode_errors": 1.5, '
1062
            '"barcode_type": "golay_12", "max_bad_run_length": 3, '
1063
            '"rev_comp": false, "phred_quality_threshold": 3, '
1064
            '"rev_comp_barcode": false, "rev_comp_mapping_barcodes": false, '
1065
            '"min_per_read_length_fraction": 0.75, "sequence_max_n": 0, '
1066
            '"phred_offset": ""}' % test.id)
1067
        job = qdb.processing_job.ProcessingJob.create(
1068
            self.user,
1069
            qdb.software.Parameters.load(qdb.software.Command(1),
1070
                                         json_str=json_str))
1071
        job._set_status('running')
1072
        with self.assertRaises(qdb.exceptions.QiitaDBArtifactDeletionError):
1073
            qdb.artifact.Artifact.delete(test.id)
1074
1075
    def test_delete(self):
1076
        test = qdb.artifact.Artifact.create(
1077
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
1078
1079
        uploads_fp = join(qdb.util.get_mountpoint("uploads")[0][1],
1080
                          str(test.study.id))
1081
        self._clean_up_files.extend(
1082
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1083
1084
        qdb.artifact.Artifact.delete(test.id)
1085
1086
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1087
            qdb.artifact.Artifact(test.id)
1088
1089
        # Analysis artifact
1090
        parameters = qdb.software.Parameters.from_default_params(
1091
            qdb.software.DefaultParameters(1), {'input_data': 1})
1092
        test = qdb.artifact.Artifact.create(
1093
            self.filepaths_processed, "Demultiplexed",
1094
            parents=[qdb.artifact.Artifact(9)],
1095
            processing_parameters=parameters)
1096
1097
        self._clean_up_files.extend(
1098
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1099
        qdb.artifact.Artifact.delete(test.id)
1100
1101
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1102
            qdb.artifact.Artifact(test.id)
1103
1104
    def test_delete_with_html(self):
1105
1106
        # creating a single file html_summary
1107
        fd, html_fp = mkstemp(suffix=".html")
1108
        close(fd)
1109
        self.filepaths_root.append((html_fp, 'html_summary'))
1110
        self._clean_up_files.append(html_fp)
1111
1112
        # creating a folder with a file for html_summary_dir
1113
        summary_dir = mkdtemp()
1114
        open(join(summary_dir, 'index.html'), 'w').write('this is a test')
1115
        self.filepaths_root.append((summary_dir, 'html_summary_dir'))
1116
        self._clean_up_files.append(summary_dir)
1117
1118
        test = qdb.artifact.Artifact.create(
1119
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
1120
1121
        uploads_fp = join(qdb.util.get_mountpoint("uploads")[0][1],
1122
                          str(test.study.id))
1123
1124
        self._clean_up_files.extend(
1125
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1126
1127
        qdb.artifact.Artifact.delete(test.id)
1128
1129
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1130
            qdb.artifact.Artifact(test.id)
1131
1132
        self.assertFalse(exists(join(uploads_fp, basename(html_fp))))
1133
        self.assertFalse(exists(join(uploads_fp, basename(summary_dir))))
1134
1135
    def test_delete_with_jobs(self):
1136
        test = qdb.artifact.Artifact.create(
1137
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
1138
        uploads_fp = join(qdb.util.get_mountpoint("uploads")[0][1],
1139
                          str(test.study.id))
1140
        self._clean_up_files.extend(
1141
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1142
1143
        json_str = (
1144
            '{"input_data": %d, "max_barcode_errors": 1.5, '
1145
            '"barcode_type": "golay_12", "max_bad_run_length": 3, '
1146
            '"rev_comp": false, "phred_quality_threshold": 3, '
1147
            '"rev_comp_barcode": false, "rev_comp_mapping_barcodes": false, '
1148
            '"min_per_read_length_fraction": 0.75, "sequence_max_n": 0, '
1149
            '"phred_offset": ""}' % test.id)
1150
        job = qdb.processing_job.ProcessingJob.create(
1151
            self.user,
1152
            qdb.software.Parameters.load(qdb.software.Command(1),
1153
                                         json_str=json_str))
1154
        job._set_status('success')
1155
1156
        qdb.artifact.Artifact.delete(test.id)
1157
1158
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1159
            qdb.artifact.Artifact(test.id)
1160
1161
        # Check that the job still exists, so we cap keep track of system usage
1162
        qdb.processing_job.ProcessingJob(job.id)
1163
1164
    def test_being_deleted_by(self):
1165
        test = qdb.artifact.Artifact.create(
1166
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
1167
        uploads_fp = join(qdb.util.get_mountpoint("uploads")[0][1],
1168
                          str(test.study.id))
1169
        self._clean_up_files.extend(
1170
            [join(uploads_fp, basename(x['fp'])) for x in test.filepaths])
1171
1172
        # verifying that there are no jobs in the list
1173
        self.assertIsNone(test.being_deleted_by)
1174
1175
        # creating new deleting job
1176
        qiita_plugin = qdb.software.Software.from_name_and_version(
1177
            'Qiita', 'alpha')
1178
        cmd = qiita_plugin.get_command('delete_artifact')
1179
        params = qdb.software.Parameters.load(
1180
            cmd, values_dict={'artifact': test.id})
1181
        job = qdb.processing_job.ProcessingJob.create(self.user, params, True)
1182
        job._set_status('running')
1183
1184
        # verifying that there is a job and is the same than above
1185
        self.assertEqual(job, test.being_deleted_by)
1186
1187
        # let's set it as error and now we should not have it anymore
1188
        job._set_error('Killed by admin')
1189
        self.assertIsNone(test.being_deleted_by)
1190
1191
        # now, let's actually remove
1192
        job = qdb.processing_job.ProcessingJob.create(self.user, params, True)
1193
        job.submit()
1194
        # let's wait for job
1195
        wait_for_processing_job(job.id)
1196
1197
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1198
            qdb.artifact.Artifact(test.id)
1199
1200
    def test_delete_as_output_job(self):
1201
        fd, fp = mkstemp(suffix='_table.biom')
1202
        self._clean_up_files.append(fp)
1203
        close(fd)
1204
        with open(fp, 'w') as f:
1205
            f.write('\n')
1206
        data = {'OTU table': {'filepaths': [(fp, 'biom')],
1207
                              'artifact_type': 'BIOM'}}
1208
        job = qdb.processing_job.ProcessingJob.create(
1209
            self.user,
1210
            qdb.software.Parameters.load(
1211
                qdb.software.Command.get_validator('BIOM'),
1212
                values_dict={'files': dumps({'biom': [fp]}),
1213
                             'artifact_type': 'BIOM',
1214
                             'template': 1,
1215
                             'provenance': dumps(
1216
                                {'job': "bcc7ebcd-39c1-43e4-af2d-822e3589f14d",
1217
                                 'cmd_out_id': 3, 'name': 'test-delete'})}
1218
            )
1219
        )
1220
        parent = qdb.processing_job.ProcessingJob(
1221
            "bcc7ebcd-39c1-43e4-af2d-822e3589f14d")
1222
        parent._set_validator_jobs([job])
1223
        job._set_status('running')
1224
        job.complete(True, artifacts_data=data)
1225
        job = qdb.processing_job.ProcessingJob(
1226
            "bcc7ebcd-39c1-43e4-af2d-822e3589f14d")
1227
        job.release_validators()
1228
        artifact = job.outputs['OTU table']
1229
        self._clean_up_files.extend([x['fp'] for x in artifact.filepaths])
1230
1231
        qdb.artifact.Artifact.delete(artifact.id)
1232
1233
        with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
1234
            qdb.artifact.Artifact(artifact.id)
1235
1236
    def test_name_setter(self):
1237
        a = qdb.artifact.Artifact(1)
1238
        self.assertEqual(a.name, "Raw data 1")
1239
        a.name = "new name"
1240
        self.assertEqual(a.name, "new name")
1241
1242
    def test_visibility_setter(self):
1243
        a = qdb.artifact.Artifact.create(
1244
            self.filepaths_root, "FASTQ", prep_template=self.prep_template)
1245
1246
        self.assertEqual(a.visibility, "sandbox")
1247
        a.visibility = "awaiting_approval"
1248
        self.assertEqual(a.visibility, "awaiting_approval")
1249
        a.visibility = "private"
1250
        self.assertEqual(a.visibility, "private")
1251
        a.visibility = "public"
1252
        self.assertEqual(a.visibility, "public")
1253
1254
        # Testing that the visibility inference works as expected
1255
        # The current artifact network that we have in the db looks as follows:
1256
        #                              /- 4 (private)
1257
        #              /- 2 (private) -|- 5 (private)
1258
        # 1 (private) -|               \- 6 (private)
1259
        #              \- 3 (private)
1260
        # By changing the visibility of 4 to public, the visibility of all
1261
        # should change
1262
        a1 = qdb.artifact.Artifact(1)
1263
        a2 = qdb.artifact.Artifact(2)
1264
        a3 = qdb.artifact.Artifact(3)
1265
        a4 = qdb.artifact.Artifact(4)
1266
        a5 = qdb.artifact.Artifact(5)
1267
        a6 = qdb.artifact.Artifact(6)
1268
1269
        a4.visibility = 'public'
1270
1271
        self.assertEqual(a1.visibility, "public")
1272
        self.assertEqual(a2.visibility, "public")
1273
        self.assertEqual(a3.visibility, "public")
1274
        self.assertEqual(a4.visibility, "public")
1275
        self.assertEqual(a5.visibility, "public")
1276
        self.assertEqual(a6.visibility, "public")
1277
1278
        # Same if we go back
1279
        a4.visibility = 'private'
1280
1281
        self.assertEqual(a1.visibility, "private")
1282
        self.assertEqual(a2.visibility, "private")
1283
        self.assertEqual(a3.visibility, "private")
1284
        self.assertEqual(a4.visibility, "private")
1285
        self.assertEqual(a5.visibility, "private")
1286
        self.assertEqual(a6.visibility, "private")
1287
1288
        # testing human_reads_filter_method here as in the future we might
1289
        # want to check that this property is inherited as visibility is;
1290
        # however, for the time being we don't need to do that and there is
1291
        # no downside on adding it here.
1292
        mtd = 'The greatest human filtering method'
1293
        self.assertEqual(mtd, a1.human_reads_filter_method)
1294
        self.assertIsNone(a2.human_reads_filter_method)
1295
        self.assertIsNone(a3.human_reads_filter_method)
1296
1297
        # let's change some values
1298
        with self.assertRaisesRegex(ValueError, '"This should fail" is not a '
1299
                                    'valid human_reads_filter_method'):
1300
            a2.human_reads_filter_method = 'This should fail'
1301
        self.assertIsNone(a2.human_reads_filter_method)
1302
        a2.human_reads_filter_method = mtd
1303
        self.assertEqual(mtd, a2.human_reads_filter_method)
1304
        self.assertIsNone(a3.human_reads_filter_method)
1305
1306
    def test_ebi_run_accessions_setter(self):
1307
        a = qdb.artifact.Artifact(3)
1308
        self.assertEqual(a.ebi_run_accessions, dict())
1309
        new_vals = {
1310
            '1.SKB1.640202': 'ERR1000001',
1311
            '1.SKB2.640194': 'ERR1000002',
1312
            '1.SKB3.640195': 'ERR1000003',
1313
            '1.SKB4.640189': 'ERR1000004',
1314
            '1.SKB5.640181': 'ERR1000005',
1315
            '1.SKB6.640176': 'ERR1000006',
1316
            '1.SKB7.640196': 'ERR1000007',
1317
            '1.SKB8.640193': 'ERR1000008',
1318
            '1.SKB9.640200': 'ERR1000009',
1319
            '1.SKD1.640179': 'ERR1000010',
1320
            '1.SKD2.640178': 'ERR1000011',
1321
            '1.SKD3.640198': 'ERR1000012',
1322
            '1.SKD4.640185': 'ERR1000013',
1323
            '1.SKD5.640186': 'ERR1000014',
1324
            '1.SKD6.640190': 'ERR1000015',
1325
            '1.SKD7.640191': 'ERR1000016',
1326
            '1.SKD8.640184': 'ERR1000017',
1327
            '1.SKD9.640182': 'ERR1000018',
1328
            '1.SKM1.640183': 'ERR1000019',
1329
            '1.SKM2.640199': 'ERR1000020',
1330
            '1.SKM3.640197': 'ERR1000021',
1331
            '1.SKM4.640180': 'ERR1000022',
1332
            '1.SKM5.640177': 'ERR1000023',
1333
            '1.SKM6.640187': 'ERR1000024',
1334
            '1.SKM7.640188': 'ERR1000025',
1335
            '1.SKM8.640201': 'ERR1000026',
1336
            '1.SKM9.640192': 'ERR1000027'}
1337
        a.ebi_run_accessions = new_vals
1338
        self.assertEqual(a.ebi_run_accessions, new_vals)
1339
1340
    def test_is_submitted_to_vamps_setter(self):
1341
        a = qdb.artifact.Artifact(2)
1342
        self.assertFalse(a.is_submitted_to_vamps)
1343
        a.is_submitted_to_vamps = True
1344
        self.assertTrue(a.is_submitted_to_vamps)
1345
1346
    def test_html_summary_setter(self):
1347
        a = qdb.artifact.Artifact(1)
1348
1349
        # Check that returns None when it doesn't exist
1350
        self.assertIsNone(a.html_summary_fp)
1351
1352
        fd, fp = mkstemp(suffix=".html")
1353
        close(fd)
1354
        self._clean_up_files.append(fp)
1355
1356
        db_fastq_dir = qdb.util.get_mountpoint('FASTQ')[0][1]
1357
        path_builder = partial(join, db_fastq_dir, str(a.id))
1358
1359
        # Check the setter works when the artifact does not have the summary
1360
        a.set_html_summary(fp)
1361
        exp1 = path_builder(basename(fp))
1362
        self.assertEqual(a.html_summary_fp[1], exp1)
1363
1364
        fd, fp = mkstemp(suffix=".html")
1365
        close(fd)
1366
        self._clean_up_files.append(fp)
1367
1368
        dp = mkdtemp()
1369
        self._clean_up_files.append(dp)
1370
1371
        # Check the setter works when the artifact already has a summary
1372
        # and with a directory
1373
        a.set_html_summary(fp, support_dir=dp)
1374
        exp2 = path_builder(basename(fp))
1375
        self.assertEqual(a.html_summary_fp[1], exp2)
1376
        self.assertFalse(exists(exp1))
1377
1378
        # Check that the setter correctly removes the directory if a new
1379
        # summary is added. Magic number 0. There is only one html_summary_dir
1380
        # added on the previous test
1381
        old_dir_fp = [x['fp'] for x in a.filepaths
1382
                      if x['fp_type'] == 'html_summary_dir'][0]
1383
        fd, fp = mkstemp(suffix='.html')
1384
        close(fd)
1385
        self._clean_up_files.append(fp)
1386
        a.set_html_summary(fp)
1387
        exp3 = path_builder(basename(fp))
1388
        self.assertEqual(a.html_summary_fp[1], exp3)
1389
        self.assertFalse(exists(exp2))
1390
        self.assertFalse(exists(old_dir_fp))
1391
        summary_dir = [x['fp'] for x in a.filepaths
1392
                       if x['fp_type'] == 'html_summary_dir']
1393
        self.assertEqual(summary_dir, [])
1394
1395
        # let's check if we update, we do _not_ remove the files
1396
        a.set_html_summary(exp3)
1397
        self.assertTrue(exists(a.html_summary_fp[1]))
1398
1399
    def test_descendants_with_jobs_one_element(self):
1400
        artifact = qdb.artifact.Artifact.create(
1401
            self.filepaths_root, 'FASTQ', prep_template=self.prep_template)
1402
1403
        obs = self.prep_template.artifact.descendants_with_jobs.nodes()
1404
        exp = [('artifact', artifact)]
1405
        self.assertCountEqual(obs, exp)
1406
1407
    def test_has_human(self):
1408
        # testing a FASTQ artifact (1), should be False
1409
        self.assertFalse(qdb.artifact.Artifact(1).has_human)
1410
1411
        # create a per_sample_FASTQ
1412
        artifact = qdb.artifact.Artifact.create(
1413
            [(self.fwd, 1), (self.rev, 2)], "per_sample_FASTQ",
1414
            prep_template=self.prep_template_per_sample_fastq)
1415
1416
        # this should be False as there are no human samples
1417
        self.assertFalse(artifact.has_human)
1418
1419
        # let's make it True by making the samle human-*
1420
        df = pd.DataFrame.from_dict(
1421
            {'1.SKB8.640193': {'env_package': 'human-oral'}},
1422
            orient='index', dtype=str)
1423
        artifact.study.sample_template.update(df)
1424
1425
        self.assertTrue(artifact.has_human)
1426
1427
        # now if we change the pt data_type to 16S
1428
        pt = artifact.prep_templates[0]
1429
        with qdb.sql_connection.TRN:
1430
            qdb.sql_connection.TRN.add(
1431
                f"""UPDATE qiita.prep_template
1432
                    SET data_type_id = 1
1433
                    WHERE prep_template_id = {pt.id}""")
1434
            qdb.sql_connection.TRN.execute()
1435
        self.assertFalse(artifact.has_human)
1436
1437
    def test_descendants_with_jobs(self):
1438
        # let's tests that we can connect two artifacts with different root
1439
        # in the same analysis
1440
        # 1. make sure there are 3 nodes
1441
        a = qdb.artifact.Artifact(8)
1442
        self.assertEqual(len(a.descendants_with_jobs.nodes), 3)
1443
        self.assertEqual(len(a.analysis.artifacts), 2)
1444
        # 2. add a new root and make sure we see it
1445
        c = qdb.artifact.Artifact.create(
1446
            self.filepaths_root, "BIOM", analysis=a.analysis,
1447
            data_type="16S")
1448
        self.assertEqual(len(a.analysis.artifacts), 3)
1449
        # 3. add jobs conencting the new artifact to the other root
1450
        #    - currently:
1451
        #    a -> job -> b
1452
        #    c
1453
        #    - expected:
1454
        #    a --> job  -> b
1455
        #                  |-> job2 -> out
1456
        #                        ^
1457
        #                  |-----|---> job1 -> out
1458
        #    c ------------|
1459
        cmd = qdb.software.Command.create(
1460
            qdb.software.Software(1),
1461
            "CommandWithMultipleInputs", "", {
1462
                'input_x': ['artifact:["BIOM"]', None],
1463
                'input_y': ['artifact:["BIOM"]', None]}, {'out': 'BIOM'})
1464
        params = qdb.software.Parameters.load(
1465
            cmd, values_dict={'input_x': a.children[0].id, 'input_y': c.id})
1466
        wf = qdb.processing_job.ProcessingWorkflow.from_scratch(
1467
            self.user, params, name='Test WF')
1468
        job1 = list(wf.graph.nodes())[0]
1469
1470
        cmd_dp = qdb.software.DefaultParameters.create("", cmd)
1471
        wf.add(cmd_dp, req_params={'input_x': a.id, 'input_y': c.id})
1472
        job2 = list(wf.graph.nodes())[1]
1473
        jobs = [j[1] for e in a.descendants_with_jobs.edges
1474
                for j in e if j[0] == 'job']
1475
        self.assertIn(job1, jobs)
1476
        self.assertIn(job2, jobs)
1477
1478
        # 4. add job3 connecting job2 output with c as inputs
1479
        #    - expected:
1480
        #    a --> job  -> b
1481
        #                  |-> job2 -> out -> job3 -> out
1482
        #                        ^             ^
1483
        #                        |             |
1484
        #                        |             |
1485
        #                  |-----|---> job1 -> out
1486
        #    c ------------|
1487
        wf.add(cmd_dp, connections={
1488
            job1: {'out': 'input_x'}, job2: {'out': 'input_y'}})
1489
        job3 = list(wf.graph.nodes())[2]
1490
        jobs = [j[1] for e in a.descendants_with_jobs.edges
1491
                for j in e if j[0] == 'job']
1492
        self.assertIn(job3, jobs)
1493
1494
1495
@qiita_test_checker()
1496
class ArtifactArchiveTests(TestCase):
1497
    def test_archive(self):
1498
        A = qdb.artifact.Artifact
1499
        QE = qdb.exceptions.QiitaDBOperationNotPermittedError
1500
1501
        # check nodes, without any change
1502
        exp_nodes = [A(1), A(2), A(3), A(4), A(5), A(6)]
1503
        self.assertCountEqual(A(1).descendants.nodes(), exp_nodes)
1504
        obs_artifacts = len(qdb.util.get_artifacts_information([4, 5, 6, 8]))
1505
        self.assertEqual(4, obs_artifacts)
1506
1507
        # check errors
1508
        with self.assertRaisesRegex(QE, 'Only public artifacts can be '
1509
                                    'archived'):
1510
            A.archive(1)
1511
        A(1).visibility = 'public'
1512
1513
        with self.assertRaisesRegex(QE, 'Only BIOM artifacts can be archived'):
1514
            A.archive(1)
1515
1516
        A(8).visibility = 'public'
1517
        with self.assertRaisesRegex(QE, 'Only non analysis artifacts can '
1518
                                    'be archived'):
1519
            A.archive(8)
1520
1521
        for aid in range(5, 7):
1522
            ms = A(aid).merging_scheme
1523
            A.archive(aid)
1524
            self.assertEqual(ms, A(aid).merging_scheme)
1525
            exp_nodes.remove(A(aid))
1526
            self.assertCountEqual(A(1).descendants.nodes(), exp_nodes)
1527
1528
        obs_artifacts = len(qdb.util.get_artifacts_information([4, 5, 6, 8]))
1529
        self.assertEqual(2, obs_artifacts)
1530
1531
        # in the tests above we generated and validated archived artifacts
1532
        # so this allows us to add tests to delete a prep-info with archived
1533
        # artifacts. The first bottleneck to do this is that this tests will
1534
        # actually remove files, which we will need for other tests so lets
1535
        # make a copy and then restore them
1536
        mfolder = dirname(dirname(abspath(__file__)))
1537
        mpath = join(mfolder, 'support_files', 'test_data')
1538
        mp = partial(join, mpath)
1539
        fps = [
1540
            mp('processed_data/1_study_1001_closed_reference_otu_table.biom'),
1541
            mp('processed_data/'
1542
               '1_study_1001_closed_reference_otu_table_Silva.biom'),
1543
            mp('raw_data/1_s_G1_L001_sequences.fastq.gz'),
1544
            mp('raw_data/1_s_G1_L001_sequences_barcodes.fastq.gz')]
1545
        for fp in fps:
1546
            copyfile(fp, f'{fp}.bk')
1547
1548
        PT = qdb.metadata_template.prep_template.PrepTemplate
1549
        QEE = qdb.exceptions.QiitaDBExecutionError
1550
        pt = A(1).prep_templates[0]
1551
        # it should fail as this prep is public and have been submitted to ENA
1552
        with self.assertRaisesRegex(QEE, 'Cannot remove prep template 1'):
1553
            PT.delete(pt.id)
1554
        # now, remove those restrictions + analysis + linked artifacts
1555
        sql = "DELETE FROM qiita.artifact_processing_job"
1556
        qdb.sql_connection.perform_as_transaction(sql)
1557
        sql = "DELETE FROM qiita.ebi_run_accession"
1558
        qdb.sql_connection.perform_as_transaction(sql)
1559
        sql = "UPDATE qiita.artifact SET visibility_id = 1"
1560
        qdb.sql_connection.perform_as_transaction(sql)
1561
        qdb.analysis.Analysis.delete_analysis_artifacts(1)
1562
        qdb.analysis.Analysis.delete_analysis_artifacts(2)
1563
        qdb.analysis.Analysis.delete_analysis_artifacts(3)
1564
        for aid in [3, 2, 1]:
1565
            A.delete(aid)
1566
1567
        PT.delete(pt.id)
1568
1569
        # bringing back the filepaths
1570
        for fp in fps:
1571
            copyfile(f'{fp}.bk', fp)
1572
1573
1574
if __name__ == '__main__':
1575
    main()