Skip to content

Commit 35d9335

Browse files
authored
Support parameters in workflows (#3333)
* adding support of sample/prep parameters in workflows * workflows.html changes * better text in 00~workflows.html01~ * fix qiita_pet test * change db files * some cleanup + sample_info PrepTemplateDataHandler
1 parent aa0d462 commit 35d9335

File tree

15 files changed

+246
-54
lines changed

15 files changed

+246
-54
lines changed

qiita_db/handlers/prep_template.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,16 @@ def get(self, prep_id):
108108
dict
109109
The contents of the prep information keyed by sample id
110110
"""
111+
sample_info = self.get_argument('sample_information', False)
112+
111113
with qdb.sql_connection.TRN:
112114
pt = _get_prep_template(prep_id)
113-
response = {'data': pt.to_dataframe().to_dict(orient='index')}
115+
if not sample_info:
116+
response = {'data': pt.to_dataframe().to_dict(orient='index')}
117+
else:
118+
ST = qdb.metadata_template.sample_template.SampleTemplate
119+
response = {'data': ST(pt.study_id).to_dataframe(
120+
samples=list(pt)).to_dict(orient='index')}
114121

115122
self.write(response)
116123

qiita_db/handlers/tests/test_prep_template.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ def test_get(self):
130130
'qiita_prep_id': '1'}
131131
self.assertEqual(obs, exp)
132132

133+
def test_get_sample_information(self):
134+
obs = self.get(
135+
'/qiita_db/prep_template/1/data/?sample_information=true',
136+
headers=self.header)
137+
self.assertEqual(obs.code, 200)
138+
139+
obs = loads(obs.body)
140+
self.assertCountEqual(obs.keys(), ['data'])
141+
142+
# let's just check that the samples and the keys from the first element
143+
# match - this assures us that is the sample_info, the rest is
144+
# basically the same as the regular prep_info
145+
obs = obs['data']
146+
exp = ['1.SKB2.640194', '1.SKM4.640180', '1.SKB3.640195',
147+
'1.SKB6.640176', '1.SKD6.640190', '1.SKM6.640187',
148+
'1.SKD9.640182', '1.SKM8.640201', '1.SKM2.640199',
149+
'1.SKD2.640178', '1.SKB7.640196', '1.SKD4.640185',
150+
'1.SKB8.640193', '1.SKM3.640197', '1.SKD5.640186',
151+
'1.SKB1.640202', '1.SKM1.640183', '1.SKD1.640179',
152+
'1.SKD3.640198', '1.SKB5.640181', '1.SKB4.640189',
153+
'1.SKB9.640200', '1.SKM9.640192', '1.SKD8.640184',
154+
'1.SKM5.640177', '1.SKM7.640188', '1.SKD7.640191']
155+
self.assertCountEqual(list(obs.keys()), exp)
156+
exp = ['ph', 'temp', 'depth', 'country', 'texture', 'altitude',
157+
'latitude', 'taxon_id', 'elevation', 'env_biome', 'longitude',
158+
'tot_nitro', 'host_taxid', 'common_name', 'description',
159+
'env_feature', 'env_package', 'sample_type', 'tot_org_carb',
160+
'dna_extracted', 'samp_salinity', 'anonymized_name',
161+
'host_subject_id', 'scientific_name', 'assigned_from_geo',
162+
'season_environment', 'water_content_soil',
163+
'collection_timestamp', 'description_duplicate',
164+
'physical_specimen_location', 'physical_specimen_remaining',
165+
'qiita_study_id']
166+
self.assertCountEqual(obs['1.SKB2.640194'].keys(), exp)
167+
133168

134169
class PrepTemplateAPItestHandlerTests(OauthTestingBase):
135170
def test_post(self):

qiita_db/metadata_template/prep_template.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def columns_restrictions(self):
328328
Returns
329329
-------
330330
dict
331-
The dict of restictions based on the data_type
331+
The dict of restrictions based on the data_type
332332
"""
333333
pt_cols = deepcopy(PREP_TEMPLATE_COLUMNS)
334334
if self.data_type() in TARGET_GENE_DATA_TYPES:
@@ -850,13 +850,35 @@ def _get_predecessors(workflow, node):
850850
else:
851851
starting_job = None
852852
pt_artifact = self.artifact.artifact_type
853-
workflows = [wk for wk in qdb.software.DefaultWorkflow.iter()
854-
if wk.artifact_type == pt_artifact and
855-
pt_dt in wk.data_type]
853+
854+
workflows = []
855+
ST = qdb.metadata_template.sample_template.SampleTemplate
856+
for wk in qdb.software.DefaultWorkflow.iter():
857+
if wk.artifact_type == pt_artifact and pt_dt in wk.data_type:
858+
wk_params = wk.parameters
859+
reqs_satisfied = True
860+
861+
if wk_params['sample']:
862+
df = ST(self.study_id).to_dataframe(samples=list(self))
863+
for k, v in wk_params['sample'].items():
864+
if k not in df.columns or v not in df[k].unique():
865+
reqs_satisfied = False
866+
867+
if wk_params['prep']:
868+
df = self.to_dataframe()
869+
for k, v in wk_params['prep'].items():
870+
if k not in df.columns or v not in df[k].unique():
871+
reqs_satisfied = False
872+
873+
if reqs_satisfied:
874+
workflows.append(wk)
875+
856876
if not workflows:
857877
# raises option a.
858878
msg = (f'This preparation data type: "{pt_dt}" and/or artifact '
859-
f'type "{pt_artifact}" does not have valid workflows')
879+
f'type "{pt_artifact}" does not have valid workflows; this '
880+
'could be due to required parameters, please check the '
881+
'available workflows.')
860882
raise ValueError(msg)
861883
missing_artifacts = dict()
862884
for wk in workflows:

qiita_db/metadata_template/sample_template.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def columns_restrictions(self):
168168
Returns
169169
-------
170170
dict
171-
The dict of restictions
171+
The dict of restrictions
172172
"""
173173
return qdb.metadata_template.constants.SAMPLE_TEMPLATE_COLUMNS
174174

qiita_db/metadata_template/test/test_prep_template.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,8 +1369,8 @@ def test_artifact_setter(self):
13691369

13701370
# here we can test that we can properly create a workflow but we are
13711371
# going to add lot more steps to make it more complex by adding a
1372-
# couple of new scenarios
1373-
# 1/2. adds a new path that should be kept separate all the way; this
1372+
# few more scenarios
1373+
# 1/4. adds a new path that should be kept separate all the way; this
13741374
# is to emulate what happens with different trimming (different
13751375
# default parameter) and deblur (same for each of the previous
13761376
# steps)
@@ -1385,7 +1385,7 @@ def test_artifact_setter(self):
13851385
default_workflow_edge_id, parent_output_id, child_input_id)
13861386
VALUES (4, 1, 3)"""
13871387
qdb.sql_connection.perform_as_transaction(sql)
1388-
# 2/2. adds a new path that should be kept together and then separate;
1388+
# 2/4. adds a new path that should be kept together and then separate;
13891389
# this is to simulate what happens with MTX/WGS processing, one
13901390
# single QC step (together) and 2 separete profilers
13911391
sql = """
@@ -1407,15 +1407,36 @@ def test_artifact_setter(self):
14071407
VALUES (5, 1, 3)
14081408
"""
14091409
qdb.sql_connection.perform_as_transaction(sql)
1410-
# Finally, we need to "activate" the merging scheme values of the
1410+
# 3/4. we need to "activate" the merging scheme values of the
14111411
# commands so they are actually different:
14121412
# 31->'Pick closed-reference OTUs', 6->'Split libraries FASTQ'
14131413
sql = """
14141414
UPDATE qiita.command_parameter
14151415
SET check_biom_merge = true
14161416
WHERE command_parameter_id IN (31, 6)"""
14171417
qdb.sql_connection.perform_as_transaction(sql)
1418+
# 4/4. update so we check the prep info file for a valid value
1419+
sql = """
1420+
UPDATE qiita.default_workflow
1421+
SET parameters = '
1422+
{"sample": {"scientific_name": "1118232"},
1423+
"prep": {"center_name": "ANL - 1"}}'::JSONB
1424+
WHERE default_workflow_id = 1"""
1425+
qdb.sql_connection.perform_as_transaction(sql)
1426+
1427+
# let's check that nothing is found due to the parameters, in specific
1428+
# "prep": {"center_name": "ANL - 1"}
1429+
with self.assertRaisesRegex(ValueError, 'This preparation data type: '
1430+
'"16S" and/or artifact type "FASTQ" does '
1431+
'not have valid workflows; this could be '
1432+
'due to required parameters, please check '
1433+
'the available workflows.'):
1434+
pt.add_default_workflow(qdb.user.User('[email protected]'))
14181435

1436+
# now, let's replace the parameters for something fine
1437+
qdb.software.DefaultWorkflow(1).parameters = {
1438+
"sample": {"scientific_name": "1118232"},
1439+
"prep": {"center_name": "ANL"}}
14191440
wk = pt.add_default_workflow(qdb.user.User('[email protected]'))
14201441
self.assertEqual(len(wk.graph.nodes), 5)
14211442
self.assertEqual(len(wk.graph.edges), 3)
@@ -1427,7 +1448,7 @@ def test_artifact_setter(self):
14271448
'Pick closed-reference OTUs'])
14281449

14291450
# now let's try to generate again and it should fail cause the jobs
1430-
# are alrady created
1451+
# are already created
14311452
with self.assertRaisesRegex(ValueError, "Cannot create job because "
14321453
"the parameters are the same as jobs"):
14331454
pt.add_default_workflow(qdb.user.User('[email protected]'))

qiita_db/software.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,3 +2000,46 @@ def graph(self):
20002000
e = DefaultWorkflowEdge(edge_id)
20012001
g.add_edge(nodes[p_id], nodes[c_id], connections=e)
20022002
return g
2003+
2004+
@property
2005+
def parameters(self):
2006+
"""Retrieves the parameters that the workflow can be applied to
2007+
2008+
Returns
2009+
----------
2010+
dict, dict
2011+
The dictionary of valid key: value pairs given by the sample or
2012+
the preparation info file
2013+
"""
2014+
with qdb.sql_connection.TRN:
2015+
sql = """SELECT parameters
2016+
FROM qiita.default_workflow
2017+
WHERE default_workflow_id = %s"""
2018+
qdb.sql_connection.TRN.add(sql, [self.id])
2019+
return qdb.sql_connection.TRN.execute_fetchflatten()[0]
2020+
2021+
@parameters.setter
2022+
def parameters(self, values):
2023+
"""Sets the parameters that the workflow can be applied to
2024+
2025+
Parameters
2026+
----------
2027+
dict : {'sample': dict, 'prep': dict}
2028+
dict of dict with the key: value pairs for the 'sample' and 'prep'
2029+
info files
2030+
2031+
Raises
2032+
------
2033+
ValueError
2034+
if the passed parameter is not a properly formated dict
2035+
"""
2036+
if not isinstance(values, dict) or \
2037+
set(values.keys()) != set(['prep', 'sample']):
2038+
raise ValueError("Improper format for values, should be "
2039+
"{'sample': dict, 'prep': dict} ")
2040+
with qdb.sql_connection.TRN:
2041+
sql = """UPDATE qiita.default_workflow
2042+
SET parameters = %s
2043+
WHERE default_workflow_id = %s"""
2044+
qdb.sql_connection.perform_as_transaction(
2045+
sql, [dumps(values), self._id])

qiita_db/support_files/patches/89.sql

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
-- Nov 1, 2023
2-
-- add creation_job_id to qiita.prep_template
2+
-- add slurm/queues changes to support per user_level and analysis parameters
33
ALTER TABLE qiita.analysis ADD slurm_reservation VARCHAR DEFAULT '' NOT NULL;
44
ALTER TABLE qiita.user_level ADD slurm_parameters VARCHAR DEFAULT '--nice=10000' NOT NULL;
55

66
UPDATE qiita.user_level SET slurm_parameters = '--nice=5000' WHERE name = 'admin';
77

88
UPDATE qiita.user_level SET slurm_parameters = '' WHERE name = 'wet-lab admin';
9+
10+
-- Nov 22, 2023
11+
-- add changes to support workflow per sample/prep info specific parameters values
12+
ALTER TABLE qiita.default_workflow ADD parameters JSONB DEFAULT '{"sample": {}, "prep": {}}'::JSONB NOT NULL;

qiita_db/support_files/qiita-db.dbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,9 @@
555555
<column name="artifact_type_id" type="bigint" length="19" decimal="0" jt="-5" mandatory="y" >
556556
<defo><![CDATA[3]]></defo>
557557
</column>
558+
<column name="parameters" type="jsonb" jt="2000" mandatory="y" >
559+
<defo><![CDATA[{"sample": {}, "prep": {}}]]></defo>
560+
</column>
558561
<index name="pk_default_workflow" unique="PRIMARY_KEY" >
559562
<column name="default_workflow_id" />
560563
</index>

0 commit comments

Comments
 (0)