Skip to content

Commit 39a03e3

Browse files
josenavasantgonza
authored andcommitted
Analysis refactor gui part7 (#2117)
* fix #1505 * improving some GUI stuff * improving some GUI stuff - missing lines * addressing all comments * ready for review * fix #1987 * initial commit * requested changes * fix filter job list * Fixing server cert (#2051) * fix get_studies * flake8 * fix #503 * fix #2010 * fix #1913 * fix errors * addressing @josenavas comment * flake8 * fix #1010 * fix #1066 (#2058) * addressing @josenavas comments * fix #1961 * fix #1837 * Automatic jobs & new stats (#2057) * fix #814, fix #1636 * fixing error in test-env * fixing stats.html call * adding img * addressing @josenavas comments * rm for loops * addresssing @ElDeveloper comments * generalizing this functionality * fix #1816 * fix #1959 * addressing @josenavas comments * addressing @josenavas comments * fixing error * fixed? * addressing @josenavas comments * addressing @wasade comments * fix flake8 * generate biom and metadata release (#2066) * initial commit * adding portal * addressing @josenavas comments * pid -> qiita_artifact_id * addressing @josenavas comments * addressing @ElDeveloper comments * rm 50.sql * database changes to fix 969 * adding delete * addressing @josenavas comments * addressing @ElDeveloper comments * duh! * fix generate_biom_and_metadata_release (#2072) * fix generate_biom_and_metadata_release * addressing @ElDeveloper comment * Removing qiita ware code that will not be used anymore * Organizing the handlers and new analysis description page * fixing timestamp * rm formats * st -> pt * Connecting the analysis creation and making interface responsive * Addressing @antgonza's comments * Initial artifact GUI refactor * Removing unused code * moving to ISO 8601 - wow :'( * fix errors * addressing @wasade comments * Adding can_edit call to the analysis * Fixing artifact rest API since not all artifacts have study * Adding can_be_publicized call to analysis * Adding QiitaHTTPError to handle errors gracefully * Adding safe_execution contextmanager * Fixing typo * Adding qiita test checker * Adapting some artifact handlers * Abstracting the graph reloading and adding some documentation * Fixing typo * Fixing changing artifact visibility * Fixing delete * Fixing artifact deletion * Adding default parameters to the commands * Fixing processing page * Fixing variable name * fixing private/public studies * Changing bdiv metrics to single choice * sanbox-to-sandbox * flake8 * Fixing patch * fixing other issues * adding share documentation * psycopg2 <= 2.7 * psycopg2 < 2.7 * Various small fixes to be able to run tests on the plugins * Adding private module * Fixing processing job completion * Fixing patch 52 * Fixing call * Fixing complete * small fixes * Adding processing handlers * Fixing url and bug on processing job workflow * Adding the private script runner * Adding is_analysis column to the command * Adding retrieval of commands excluding analysis commands * Addressing bug on retrieving information from redis * Enabling the command register endpoint to provide if the command is analysis only * Addressing @antgonza's comments * Addressing @wasade's comments
1 parent 7cf4559 commit 39a03e3

File tree

18 files changed

+809
-122
lines changed

18 files changed

+809
-122
lines changed

qiita_db/handlers/plugin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,14 @@ def post(self, name, version):
104104
if outputs:
105105
outputs = loads(outputs)
106106
dflt_param_set = loads(self.get_argument('default_parameter_sets'))
107+
analysis_only = self.get_argument('analysis_only', False)
107108

108109
parameters = req_params
109110
parameters.update(opt_params)
110111

111112
cmd = qdb.software.Command.create(
112-
plugin, cmd_name, cmd_desc, parameters, outputs)
113+
plugin, cmd_name, cmd_desc, parameters, outputs,
114+
analysis_only=analysis_only)
113115

114116
if dflt_param_set is not None:
115117
for name, vals in dflt_param_set.items():

qiita_db/handlers/tests/test_plugin.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,25 @@ def test_post(self):
8888
self.assertEqual(obs.code, 200)
8989
obs = _get_command('QIIME', '1.9.1', 'New Command')
9090
self.assertEqual(obs.name, 'New Command')
91+
self.assertFalse(obs.analysis_only)
92+
93+
# Create a new command that is analysis only
94+
data = {
95+
'name': 'New analysis command',
96+
'description': 'Analysis command added for testing',
97+
'required_parameters': dumps(
98+
{'in_data': ['artifact:["BIOM"]', None]}),
99+
'optional_parameters': dumps({'param1': ['string', 'default']}),
100+
'outputs': dumps({'outtable': 'BIOM'}),
101+
'default_parameter_sets': dumps({'dflt1': {'param1': 'test'}}),
102+
'analysis_only': True
103+
}
104+
obs = self.post('/qiita_db/plugins/QIIME/1.9.1/commands/', data=data,
105+
headers=self.header)
106+
self.assertEqual(obs.code, 200)
107+
obs = _get_command('QIIME', '1.9.1', 'New analysis command')
108+
self.assertEqual(obs.name, 'New analysis command')
109+
self.assertTrue(obs.analysis_only)
91110

92111

93112
class CommandHandlerTests(OauthTestingBase):

qiita_db/private.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 json import dumps
10+
from sys import exc_info
11+
from time import sleep
12+
import traceback
13+
14+
import qiita_db as qdb
15+
16+
17+
def build_analysis_files(job):
18+
"""Builds the files for an analysis
19+
20+
Parameters
21+
----------
22+
job : qiita_db.processing_job.ProcessingJob
23+
The processing job with the information for building the files
24+
"""
25+
with qdb.sql_connection.TRN:
26+
params = job.parameters.values
27+
analysis_id = params['analysis']
28+
merge_duplicated_sample_ids = params['merge_dup_sample_ids']
29+
analysis = qdb.analysis.Analysis(analysis_id)
30+
biom_files = analysis.build_files(merge_duplicated_sample_ids)
31+
32+
cmd = qdb.software.Command.get_validator('BIOM')
33+
val_jobs = []
34+
for dtype, biom_fp in biom_files:
35+
validate_params = qdb.software.Parameters.load(
36+
cmd, values_dict={'files': dumps({'biom': [biom_fp]}),
37+
'artifact_type': 'BIOM',
38+
'provenance': dumps({'job': job.id,
39+
'data_type': dtype}),
40+
'analysis': analysis_id})
41+
val_jobs.append(qdb.processing_job.ProcessingJob.create(
42+
analysis.owner, validate_params))
43+
44+
job._set_validator_jobs(val_jobs)
45+
46+
for j in val_jobs:
47+
j.submit()
48+
sleep(1)
49+
50+
51+
TASK_DICT = {'build_analysis_files': build_analysis_files}
52+
53+
54+
def private_task(job_id):
55+
"""Complets a Qiita private task
56+
57+
Parameters
58+
----------
59+
job_id : str
60+
The job id
61+
"""
62+
if job_id == 'register':
63+
# We don't need to do anything here if Qiita is registering plugins
64+
return
65+
66+
job = qdb.processing_job.ProcessingJob(job_id)
67+
job.update_heartbeat_state()
68+
task_name = job.command.name
69+
70+
try:
71+
TASK_DICT[task_name](job)
72+
except Exception:
73+
job.complete(False, error="Error executing private task: %s"
74+
% traceback.format_exception(*exc_info()))

qiita_db/processing_job.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ def _complete_artifact_definition(self, artifact_data):
504504
else:
505505
# The artifact is uploaded by the user or is the initial
506506
# artifact of an analysis
507-
if job_params['analysis'] is not None:
507+
if ('analysis' in job_params and
508+
job_params['analysis'] is not None):
508509
pt = None
509510
an = qdb.analysis.Analysis(job_params['analysis'])
510511
sql = """SELECT data_type
@@ -567,11 +568,21 @@ def _complete_artifact_transformation(self, artifacts_data):
567568
templates = set()
568569
for artifact in self.input_artifacts:
569570
templates.update(pt.id for pt in artifact.prep_templates)
571+
template = None
572+
analysis = None
570573
if len(templates) > 1:
571574
raise qdb.exceptions.QiitaDBError(
572575
"Currently only single prep template "
573576
"is allowed, found %d" % len(templates))
574-
template = templates.pop()
577+
elif len(templates) == 1:
578+
template = templates.pop()
579+
else:
580+
# In this case we have 0 templates. What this means is that
581+
# this artifact is being generated in the analysis pipeline
582+
# All the artifacts included in the analysis pipeline
583+
# belong to the same analysis, so we can just ask the
584+
# first artifact for the analysis that it belongs to
585+
analysis = self.input_artifacts[0].analysis.id
575586

576587
# Once the validate job completes, it needs to know if it has
577588
# been generated from a command (and how) or if it has been
@@ -592,6 +603,7 @@ def _complete_artifact_transformation(self, artifacts_data):
592603
cmd, values_dict={'files': dumps(filepaths),
593604
'artifact_type': atype,
594605
'template': template,
606+
'analysis': analysis,
595607
'provenance': dumps(provenance)})
596608
validator_jobs.append(
597609
ProcessingJob.create(self.user, validate_params))
@@ -1196,7 +1208,16 @@ def _raise_if_not_in_construction(self):
11961208
WHERE processing_job_workflow_id = %s"""
11971209
qdb.sql_connection.TRN.add(sql, [self.id])
11981210
res = qdb.sql_connection.TRN.execute_fetchflatten()
1199-
if len(res) != 1 or res[0] != 'in_construction':
1211+
# If the above SQL query returns a single element and the value
1212+
# is different from in construction, it means that all the jobs
1213+
# in the workflow are in the same status and it is not
1214+
# 'in_construction', hence raise the error. If the above SQL query
1215+
# returns more than value (len(res) > 1) it means that the workflow
1216+
# is no longer in construction cause some jobs have been submited
1217+
# for processing. Note that if the above query doesn't retrun any
1218+
# value, it means that no jobs are in the workflow and that means
1219+
# that the workflow is in construction.
1220+
if (len(res) == 1 and res[0] != 'in_construction') or len(res) > 1:
12001221
# The workflow is no longer in construction, raise an error
12011222
raise qdb.exceptions.QiitaDBOperationNotPermittedError(
12021223
"Workflow not in construction")

qiita_db/software.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ class Command(qdb.base.QiitaObject):
4444
_table = "software_command"
4545

4646
@classmethod
47-
def get_commands_by_input_type(cls, artifact_types, active_only=True):
47+
def get_commands_by_input_type(cls, artifact_types, active_only=True,
48+
exclude_analysis=True):
4849
"""Returns the commands that can process the given artifact types
4950
5051
Parameters
@@ -70,6 +71,8 @@ def get_commands_by_input_type(cls, artifact_types, active_only=True):
7071
WHERE artifact_type IN %s"""
7172
if active_only:
7273
sql += " AND active = True"
74+
if exclude_analysis:
75+
sql += " AND is_analysis = False"
7376
qdb.sql_connection.TRN.add(sql, [tuple(artifact_types)])
7477
for c_id in qdb.sql_connection.TRN.execute_fetchflatten():
7578
yield cls(c_id)
@@ -191,7 +194,8 @@ def exists(cls, software, name):
191194
return qdb.sql_connection.TRN.execute_fetchlast()
192195

193196
@classmethod
194-
def create(cls, software, name, description, parameters, outputs=None):
197+
def create(cls, software, name, description, parameters, outputs=None,
198+
analysis_only=False):
195199
r"""Creates a new command in the system
196200
197201
The supported types for the parameters are:
@@ -222,6 +226,9 @@ def create(cls, software, name, description, parameters, outputs=None):
222226
outputs : dict, optional
223227
The description of the outputs that this command generated. The
224228
format is: {output_name: artifact_type}
229+
analysis_only : bool, optional
230+
If true, then the command will only be available on the analysis
231+
pipeline. Default: False.
225232
226233
Returns
227234
-------
@@ -297,10 +304,10 @@ def create(cls, software, name, description, parameters, outputs=None):
297304
% (software.id, name))
298305
# Add the command to the DB
299306
sql = """INSERT INTO qiita.software_command
300-
(name, software_id, description)
301-
VALUES (%s, %s, %s)
307+
(name, software_id, description, is_analysis)
308+
VALUES (%s, %s, %s, %s)
302309
RETURNING command_id"""
303-
sql_params = [name, software.id, description]
310+
sql_params = [name, software.id, description, analysis_only]
304311
qdb.sql_connection.TRN.add(sql, sql_params)
305312
c_id = qdb.sql_connection.TRN.execute_fetchlast()
306313

@@ -508,6 +515,22 @@ def activate(self):
508515
qdb.sql_connection.TRN.add(sql, [True, self.id])
509516
return qdb.sql_connection.TRN.execute()
510517

518+
@property
519+
def analysis_only(self):
520+
"""Returns if the command is an analysis-only command
521+
522+
Returns
523+
-------
524+
bool
525+
Whether the command is analysis only or not
526+
"""
527+
with qdb.sql_connection.TRN:
528+
sql = """SELECT is_analysis
529+
FROM qiita.software_command
530+
WHERE command_id = %s"""
531+
qdb.sql_connection.TRN.add(sql, [self.id])
532+
return qdb.sql_connection.TRN.execute_fetchlast()
533+
511534

512535
class Software(qdb.base.QiitaObject):
513536
r"""A software package available in the system

qiita_db/support_files/patches/52.sql

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ ALTER TABLE qiita.analysis ADD logging_id bigint ;
4949
CREATE INDEX idx_analysis_0 ON qiita.analysis ( logging_id ) ;
5050
ALTER TABLE qiita.analysis ADD CONSTRAINT fk_analysis_logging FOREIGN KEY ( logging_id ) REFERENCES qiita.logging( logging_id ) ;
5151

52+
-- Alter the software command table to differentiate between commands that
53+
-- apply to the analysis pipeline or commands that apply on the study
54+
-- processing pipeline
55+
ALTER TABLE qiita.software_command ADD is_analysis bool DEFAULT 'False' NOT NULL;
56+
5257
-- We can handle some of the special cases here, so we simplify the work in the
5358
-- python patch
5459

@@ -102,7 +107,7 @@ DECLARE
102107
baf_cmd_id bigint;
103108
BEGIN
104109
INSERT INTO qiita.software (name, version, description, environment_script, start_script, software_type_id, active)
105-
VALUES ('Qiita', 'alpha', 'Internal Qiita jobs', 'source activate qiita', 'qiita-private-2', 3, True)
110+
VALUES ('Qiita', 'alpha', 'Internal Qiita jobs', 'source activate qiita', 'qiita-private-plugin', 3, True)
106111
RETURNING software_id INTO qiita_sw_id;
107112

108113
INSERT INTO qiita.software_command (software_id, name, description)

qiita_db/support_files/patches/python_patches/52.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def create_non_rarefied_biom_artifact(analysis, biom_data, rarefied_table):
9494
# Note that we are sure that the biom table exists for sure, so
9595
# no need to check if biom_fp is undefined
9696
biom_table = load_table(biom_fp)
97+
samples = set(samples).intersection(biom_table.ids())
9798
biom_table.filter(samples, axis='sample', inplace=True)
9899
new_table = new_table.merge(biom_table)
99100
ids_map.update({sid: "%d.%s" % (a_id, sid)
@@ -498,8 +499,9 @@ def transfer_job(analysis, command_id, params, input_artifact_id, job_data,
498499
qiime_id = TRN.execute_fetchlast()
499500

500501
# Step 2: Insert the new commands in the software_command table
501-
sql = """INSERT INTO qiita.software_command (software_id, name, description)
502-
VALUES (%s, %s, %s)
502+
sql = """INSERT INTO qiita.software_command
503+
(software_id, name, description, is_analysis)
504+
VALUES (%s, %s, %s, TRUE)
503505
RETURNING command_id"""
504506
TRN.add(sql, [qiime_id, 'Summarize Taxa', 'Plots taxonomy summaries at '
505507
'different taxonomy levels'])
@@ -606,7 +608,7 @@ def transfer_job(analysis, command_id, params, input_artifact_id, job_data,
606608
[sum_taxa_cmd_id, 'Defaults',
607609
'{"sort": false, "metadata_category": ""}'],
608610
[bdiv_cmd_id, 'Unweighted UniFrac',
609-
'{"metrics": "unweighted_unifrac", "tree": ""}'],
611+
'{"metric": "unweighted_unifrac", "tree": ""}'],
610612
[arare_cmd_id, 'Defaults',
611613
'{"max_rare_depth": "Default", "tree": "", "num_steps": 10, '
612614
'"min_rare_depth": 10, "metrics": ["chao1", "observed_otus"]}'],
@@ -669,7 +671,10 @@ def transfer_job(analysis, command_id, params, input_artifact_id, job_data,
669671
srare_cmd_out_id)
670672
else:
671673
# The BIOM table was not rarefied, use current table as initial
672-
initial_biom_id = transfer_file_to_artifact()
674+
initial_biom_id = transfer_file_to_artifact(
675+
analysis['analysis_id'], analysis['timestamp'], None,
676+
biom_data['data_type_id'], None, 7,
677+
biom_data['filepath_id'])
673678

674679
# Loop through all the jobs that used this biom table as input
675680
sql = """SELECT *

0 commit comments

Comments
 (0)