Skip to content
This repository was archived by the owner on Dec 29, 2023. It is now read-only.

Commit 10cbd2d

Browse files
ebolyenthermokarst
authored andcommitted
MAINT: support optional parameters (#122)
Fixes #119
1 parent 28c0b19 commit 10cbd2d

File tree

6 files changed

+76
-31
lines changed

6 files changed

+76
-31
lines changed

app/js/components/JobHistoryData.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import React from 'react';
1111
import style from '../../css/JobHistory.css';
1212

1313
const JobHistoryData = ({ name, value }) => (
14-
name !== undefined && value !== undefined ?
14+
name !== undefined && value !== undefined && value !== null ?
1515
<tr className={name === 'stderr' && value.length > 0 ? 'danger' : ''}>
1616
<td className="col-sm-3">{name}</td>
1717
<td className={style.td} style={{ whiteSpace: 'pre' }}>

app/js/components/pages/Job.jsx

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,26 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
2020
<h4>{action.description}</h4>
2121
</div>
2222
<form onSubmit={e => submitJob(e, action.parameters)}>
23-
{ action.inputs.map(({ name }) =>
23+
{ action.inputs.map(({ name, required }) =>
2424
<fieldset
2525
className="form-group"
2626
key={`${name}-dropdown${counter++}`}
2727
>
28-
<label htmlFor={`in-${name}`}>
28+
<label htmlFor={`in-${name}${required ? '-required' : ''}`}>
2929
Input Artifact: { name }
3030
</label>
3131
<select
3232
className="form-control"
33-
name={`in-${name}`}
33+
name={`in-${name}${required ? '-required' : ''}`}
3434
>
35+
{ !required &&
36+
<option
37+
key={null}
38+
value=""
39+
>
40+
No Artifact
41+
</option>
42+
}
3543
{ inputs[name] !== undefined ?
3644
inputs[name].map(artifact =>
3745
<option
@@ -46,23 +54,32 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
4654
</fieldset>
4755
)}
4856

49-
{ action.parameters.map(({ name, type, ast, default: def }) =>
57+
{ action.parameters.map(({ name, type, ast, required, default: def }) =>
5058
<fieldset
5159
className="form-group"
5260
key={`${name}-text-input${counter++}`}
5361
>
54-
<label htmlFor={`param-${name}`}>
62+
<label htmlFor={`param-${name}${required ? '-required' : ''}`}>
5563
Input Parameter: { name }
5664
</label>
5765
{ ast.predicate.name && ast.predicate.name === 'Choices' ?
5866
(
5967
<select
6068
className="form-control"
61-
name={`param-${name}`}
69+
name={`param-${name}${required ? '-required' : ''}`}
6270
>
71+
{ !required && def === null &&
72+
<option
73+
key={null}
74+
value=""
75+
>
76+
None
77+
</option>
78+
}
6379
{
6480
_.sortBy(ast.predicate.choices).map(choice =>
6581
<option
82+
selected={choice === def}
6683
key={choice}
6784
value={choice}
6885
>
@@ -76,8 +93,16 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
7693
(
7794
<select
7895
className="form-control"
79-
name={`metadata-${name}`}
96+
name={`metadata-${name}${required ? '-required' : ''}`}
8097
>
98+
{ !required &&
99+
<option
100+
key={null}
101+
value=""
102+
>
103+
No Metadata
104+
</option>
105+
}
81106
{ metadata ?
82107
metadata.map(entry =>
83108
<option
@@ -95,8 +120,16 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
95120
<fieldset>
96121
<select
97122
className="form-control"
98-
name={`metadatacat1-${name}`}
123+
name={`metadatacat1-${name}${required ? '-required' : ''}`}
99124
>
125+
{ !required &&
126+
<option
127+
key={null}
128+
value=""
129+
>
130+
No Metadata
131+
</option>
132+
}
100133
{ metadata ?
101134
metadata.map(entry =>
102135
<option
@@ -111,18 +144,18 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
111144
<input
112145
type="text-field"
113146
className="form-control"
114-
name={`metadatacat2-${name}`}
115-
placeholder={type}
147+
name={`metadatacat2-${name}${required ? '-required' : ''}`}
148+
placeholder={`${type}${required ? '' : ' (optional)'}`}
116149
/>
117150
</fieldset>
118151
)
119152
: type === 'Bool' ?
120153
<div className="checkbox">
121-
<label htmlFor={`param-${name}`}>
154+
<label htmlFor={`param-${name}${required ? '-required' : ''}`}>
122155
<input
123156
type="checkbox"
124-
name={`param-${name}`}
125-
defaultValue={def}
157+
name={`param-${name}${required ? '-required' : ''}`}
158+
defaultChecked={def}
126159
/>{name}
127160
</label>
128161
</div>
@@ -131,8 +164,8 @@ const Job = ({ action, inputs, metadata, submitJob, cancelJob, children }) => {
131164
<input
132165
type="text-field"
133166
className="form-control"
134-
name={`param-${name}`}
135-
placeholder={type}
167+
name={`param-${name}${required ? '-required' : ''}`}
168+
placeholder={`${type}${required ? '' : ' (optional)'}`}
136169
defaultValue={def}
137170
/>
138171
)

app/js/containers/Job.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ const mapDispatchToProps = (dispatch, { router, params: { pluginId, jobId, actio
4242
};
4343
let catbuffer = [];
4444
for (const [key, value] of formData.entries()) {
45-
if (value.trim().length === 0) {
46-
alert(`${key} must not be blank.`);
45+
const [type, name, required] = key.split('-');
46+
if (value.trim().length === 0 && required) {
47+
alert(`Pleas provide a value for ${name} (must not be blank).`);
4748
return;
4849
}
49-
const [type, name] = key.split('-');
5050
switch (type) {
5151
case 'in':
5252
job.inputs[name] = value;
@@ -55,7 +55,7 @@ const mapDispatchToProps = (dispatch, { router, params: { pluginId, jobId, actio
5555
if (booleans.find(bool => bool.name === name) && value === 'on') {
5656
job.parameters[name] = 'true';
5757
} else {
58-
job.parameters[name] = value;
58+
job.parameters[name] = !required && value === '' ? null : value;
5959
}
6060
break;
6161
case 'out':

q2studio/api/jobs.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,20 @@ def create_job():
6666
json_params = {}
6767
for key, spec in action.signature.parameters.items():
6868
if spec.qiime_type == qiime2.plugin.Metadata:
69-
parameters[key] = qiime2.Metadata.load(parameters[key])
70-
json_params[key] = '<metadata>'
69+
if parameters[key] == "":
70+
parameters[key] = None
71+
json_params[key] = None
72+
else:
73+
parameters[key] = qiime2.Metadata.load(parameters[key])
74+
json_params[key] = '<metadata>'
7175
elif spec.qiime_type == qiime2.plugin.MetadataCategory:
72-
parameters[key] = qiime2.Metadata.load(
73-
parameters[key][0]).get_category(parameters[key][1])
74-
json_params[key] = '<metadata>'
76+
if parameters[key][0] == "" or parameters[key][1] == "":
77+
parameters[key] = None
78+
json_params[key] = None
79+
else:
80+
parameters[key] = qiime2.Metadata.load(
81+
parameters[key][0]).get_category(parameters[key][1])
82+
json_params[key] = '<metadata>'
7583
else:
7684
json_params[key] = parameters[key]
7785

q2studio/api/plugins.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,17 @@ def _build_data_dict(data):
4747
dict_[key]['name'] = value.name
4848
dict_[key]['description'] = value.description
4949
dict_[key]['inputs'] = [
50-
{'name': name, 'type': repr(spec.qiime_type)}
50+
{'name': name,
51+
'type': repr(spec.qiime_type),
52+
'required': not spec.has_default()}
5153
for name, spec in value.signature.inputs.items()
5254
]
5355
dict_[key]['parameters'] = [
5456
{'name': name,
5557
'type': repr(spec.qiime_type),
5658
'ast': spec.qiime_type.to_ast(),
57-
'default': value.signature.defaults.get(name)}
59+
'required': not spec.has_default(),
60+
'default': spec.default if spec.has_default() else None}
5861
for name, spec in value.signature.parameters.items()
5962
]
6063
dict_[key]['outputs'] = [

q2studio/api/workspace.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from flask import Blueprint, jsonify, request, abort, url_for
1313

14+
15+
import qiime2
1416
from qiime2.sdk import Artifact, Visualization
1517
from ..util import fail_gracefully
1618

@@ -22,7 +24,8 @@
2224

2325

2426
def load_artifacts(**kwargs):
25-
return {k: Artifact.load(ARTIFACTS[v]) for k, v in kwargs.items()}
27+
return {k: Artifact.load(ARTIFACTS[v]) for k, v in kwargs.items()
28+
if v != ''}
2629

2730

2831
@workspace.route('/', methods=['GET'])
@@ -180,9 +183,7 @@ def get_metadata():
180183
metadata = []
181184
for metadata_path in metadata_paths:
182185
try:
183-
with open(metadata_path) as fh:
184-
header = "#SampleID"
185-
assert(fh.read(len(header)) == header)
186+
qiime2.Metadata.load(metadata_path)
186187
metadata.append({
187188
"name": os.path.basename(metadata_path),
188189
"filepath": metadata_path

0 commit comments

Comments
 (0)