Skip to content

Commit a412b45

Browse files
authored
add default-workflow to future artifact - SPP (#3307)
* add default-workflow to future artifact - SPP * fix error * current_job -> starting_job * fix test * addressing @charles-cowart comments
1 parent 62511cc commit a412b45

File tree

3 files changed

+102
-11
lines changed

3 files changed

+102
-11
lines changed

qiita_db/handlers/artifact.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ def post(self):
267267
atype = self.get_argument('artifact_type')
268268
aname = self.get_argument('command_artifact_name', 'Name')
269269
files = self.get_argument('files')
270+
add_default_workflow = self.get_argument('add_default_workflow', False)
270271

271272
if job_id is None and prep_id is None:
272273
raise HTTPError(
@@ -314,8 +315,18 @@ def post(self):
314315
values['template'] = prep_id
315316
cmd = qdb.software.Command.get_validator(atype)
316317
params = qdb.software.Parameters.load(cmd, values_dict=values)
317-
new_job = PJ.create(user, params, True)
318-
new_job.submit()
318+
if add_default_workflow:
319+
pwk = qdb.processing_job.ProcessingWorkflow.from_scratch(
320+
user, params, name=f'ProcessingWorkflow for {job_id}')
321+
# the new job is the first job in the workflow
322+
new_job = list(pwk.graph.nodes())[0]
323+
# adding default pipeline to the preparation
324+
pt = qdb.metadata_template.prep_template.PrepTemplate(prep_id)
325+
pt.add_default_workflow(user, pwk)
326+
pwk.submit()
327+
else:
328+
new_job = PJ.create(user, params, True)
329+
new_job.submit()
319330

320331
r_client.set('prep_template_%d' % prep_id,
321332
dumps({'job_id': new_job.id, 'is_qiita_job': True}))

qiita_db/handlers/tests/test_artifact.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,58 @@ def test_post(self):
411411
sleep(0.5)
412412
self.assertIsNotNone(new_prep.artifact)
413413

414+
def test_post_insert_artifact_and_add_default_processing(self):
415+
# now let's test adding an artifact + default processing to a new
416+
# preparation
417+
new_prep = qdb.metadata_template.prep_template.PrepTemplate.create(
418+
pd.DataFrame({'new_col': {'1.SKB1.640202': 1,
419+
'1.SKD3.640198': 2,
420+
'1.SKM4.640180': 3}}),
421+
qdb.study.Study(1), '16S')
422+
423+
# creating the fastq files to be added
424+
fd, fp1 = mkstemp(suffix='_seqs.fastq')
425+
close(fd)
426+
self._clean_up_files.append(fp1)
427+
with open(fp1, 'w') as f:
428+
f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 1:N:0:\n"
429+
"NACGTAGGGTGCAAGCGTTGTCCGGAATNA\n"
430+
"+\n"
431+
"#1=DDFFFHHHHHJJJJJJJJJJJJGII#0\n")
432+
433+
fd, fp2 = mkstemp(suffix='_barcodes.fastq')
434+
close(fd)
435+
self._clean_up_files.append(fp2)
436+
with open(fp2, 'w') as f:
437+
f.write("@HWI-ST753:189:D1385ACXX:1:1101:1214:1906 2:N:0:\n"
438+
"NNNCNNNNNNNNN\n"
439+
"+\n"
440+
"#############\n")
441+
442+
data = {'user_email': 'demo@microbio.me',
443+
'artifact_type': 'FASTQ',
444+
'prep_id': new_prep.id,
445+
'files': dumps([(fp1, 'raw_forward_seqs'),
446+
(fp2, 'raw_barcodes')]),
447+
'add_default_workflow': False}
448+
obs = self.post('/qiita_db/artifact/', headers=self.header, data=data)
449+
self.assertEqual(obs.code, 200)
450+
jid = loads(obs.body)['job_id']
451+
# if we got to this point, then we should have a job and that job
452+
# should have children jobs (generated by the default workflow)
453+
job = qdb.processing_job.ProcessingJob(jid)
454+
children = [c.command.name for c in job.children]
455+
grandchildren = [gc.command.name for c in job.children
456+
for gc in c.children]
457+
self.assertEqual('Validate', job.command.name)
458+
self.assertEqual(['Split libraries FASTQ'], children)
459+
self.assertEqual(['Pick closed-reference OTUs'], grandchildren)
460+
461+
# just to avoid any tentative issues, let's wait for the main job to
462+
# finish
463+
while job.status not in ('error', 'success'):
464+
sleep(0.5)
465+
414466

415467
if __name__ == '__main__':
416468
main()

qiita_db/metadata_template/prep_template.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -726,13 +726,15 @@ def modification_timestamp(self):
726726
def max_samples():
727727
return qdb.util.max_preparation_samples()
728728

729-
def add_default_workflow(self, user):
730-
"""The modification timestamp of the prep information
729+
def add_default_workflow(self, user, workflow=None):
730+
"""Adds the commands of the default workflow to this preparation
731731
732732
Parameters
733733
----------
734734
user : qiita_db.user.User
735735
The user that requested to add the default workflows
736+
workflow : qiita_db.processing_job.ProcessingWorkflow, optional
737+
The workflow to add the default processing
736738
737739
Returns
738740
-------
@@ -745,6 +747,13 @@ def add_default_workflow(self, user):
745747
a. If this preparation doesn't have valid workflows
746748
b. This preparation has been fully processed (no new steps needed)
747749
c. If there is no valid initial artifact to start the workflow
750+
751+
Notes
752+
-----
753+
This method adds the commands in a default workflow (definition) to
754+
the preparation, if a workflow (object) is passed it will add the
755+
commands to the last artifact in that workflow but if it's None it will
756+
create a new workflow (default)
748757
"""
749758
# helper functions to avoid duplication of code
750759

@@ -806,9 +815,14 @@ def _get_predecessors(workflow, node):
806815
# workflow
807816

808817
# 1.
809-
prep_jobs = [j for c in self.artifact.descendants.nodes()
810-
for j in c.jobs(show_hidden=True)
811-
if j.command.software.type == 'artifact transformation']
818+
# let's assume that if there is a workflow, there are no jobs
819+
if workflow is not None:
820+
prep_jobs = []
821+
else:
822+
prep_jobs = [j for c in self.artifact.descendants.nodes()
823+
for j in c.jobs(show_hidden=True)
824+
if j.command.software.type ==
825+
'artifact transformation']
812826
merging_schemes = {
813827
qdb.archive.Archive.get_merging_scheme_from_job(j): {
814828
x: y.id for x, y in j.outputs.items()}
@@ -821,7 +835,14 @@ def _get_predecessors(workflow, node):
821835

822836
# 2.
823837
pt_dt = self.data_type()
824-
pt_artifact = self.artifact.artifact_type
838+
# if there is a workflow, we would need to get the artifact_type from
839+
# the job
840+
if workflow is not None:
841+
starting_job = list(workflow.graph.nodes())[0]
842+
pt_artifact = starting_job.parameters.values['artifact_type']
843+
else:
844+
starting_job = None
845+
pt_artifact = self.artifact.artifact_type
825846
workflows = [wk for wk in qdb.software.DefaultWorkflow.iter()
826847
if wk.artifact_type == pt_artifact and
827848
pt_dt in wk.data_type]
@@ -846,7 +867,6 @@ def _get_predecessors(workflow, node):
846867
raise ValueError('This preparation is complete')
847868

848869
# 3.
849-
workflow = None
850870
for wk, wk_data in missing_artifacts.items():
851871
previous_jobs = dict()
852872
for ma, node in wk_data.items():
@@ -886,12 +906,20 @@ def _get_predecessors(workflow, node):
886906

887907
cmds_to_create.append([pdp_cmd, params, reqp])
888908

889-
init_artifacts = {wkartifact_type: self.artifact.id}
909+
if starting_job is not None:
910+
init_artifacts = {
911+
wkartifact_type: f'{starting_job.id}:'}
912+
else:
913+
init_artifacts = {wkartifact_type: self.artifact.id}
890914

891915
cmds_to_create.reverse()
892916
current_job = None
893917
for i, (cmd, params, rp) in enumerate(cmds_to_create):
894-
previous_job = current_job
918+
if starting_job is not None:
919+
previous_job = starting_job
920+
starting_job = None
921+
else:
922+
previous_job = current_job
895923
if previous_job is None:
896924
req_params = dict()
897925
for iname, dname in rp.items():

0 commit comments

Comments
 (0)