Workflow replacement parameters polish.

- Selenium test case demonstrating their use in simple workflows.
- API test demonstrating they work with nested workflows from the run perspective.
- Update run variant of workflow to_dict to parse out replacement parameters to display on the backend and tweak the client to just accept these.
- Parse out replacement parameters for subworkflows recursively, include in new run output attribute - nested workflow replacement parameters work now.
- Selenium test case demosntrating subworkflow replacement parameters are rendered and used when submitted.
This commit is contained in:
John Chilton
2018-09-07 11:22:20 -04:00
parent 3824a8948a
commit 8f979b4da1
6 changed files with 172 additions and 33 deletions
@@ -127,21 +127,24 @@ var View = Backbone.View.extend({
// identify and configure workflow parameters
var wp_count = 0;
this.wp_inputs = {};
function _ensureWorkflowParameter(wp_name) {
return self.wp_inputs[wp_name] = self.wp_inputs[wp_name] || {
label: wp_name,
name: wp_name,
type: "text",
color: `hsl( ${++wp_count * 100}, 70%, 30% )`,
style: "ui-form-wp-source",
links: []
};
}
function _handleWorkflowParameter(value, callback) {
var re = /\$\{(.+?)\}/g;
var match;
while ((match = re.exec(String(value)))) {
var wp_name = match[1];
callback(
(self.wp_inputs[wp_name] = self.wp_inputs[wp_name] || {
label: wp_name,
name: wp_name,
type: "text",
color: `hsl( ${++wp_count * 100}, 70%, 30% )`,
style: "ui-form-wp-source",
links: []
})
);
callback(_ensureWorkflowParameter(wp_name));
}
}
_.each(this.steps, (step, i) => {
@@ -154,10 +157,8 @@ var View = Backbone.View.extend({
input.style = "ui-form-wp-target";
});
});
_.each(step.post_job_actions, pja => {
_.each(pja.action_arguments, arg => {
_handleWorkflowParameter(arg, () => {});
});
_.each(step.replacement_parameters, wp_name => {
_ensureWorkflowParameter(wp_name);
});
});
+1
View File
@@ -444,6 +444,7 @@ class WorkflowContentsManager(UsesAnnotations):
step_model = {
'inputs' : [input.to_dict(trans) for input in inputs.values()]
}
step_model['replacement_parameters'] = step.module.get_replacement_parameters(step)
step_model['step_type'] = step.type
step_model['step_label'] = step.label
step_model['step_name'] = step.module.get_name()
+26
View File
@@ -2,6 +2,7 @@
Modules used in building workflows
"""
import logging
import re
from json import loads
from xml.etree.ElementTree import (
Element,
@@ -259,6 +260,11 @@ class WorkflowModule(object):
progress.set_step_outputs(invocation_step, outputs, already_persisted=True)
def get_replacement_parameters(self, step):
"""Return a list of replacement parameters."""
return []
class SubWorkflowModule(WorkflowModule):
# Two step improvements to build runtime inputs for subworkflow modules
@@ -403,6 +409,16 @@ class SubWorkflowModule(WorkflowModule):
return inputs
def get_replacement_parameters(self, step):
"""Return a list of replacement parameters."""
replacement_parameters = set()
for subworkflow_step in self.subworkflow.steps:
module = subworkflow_step.module
for replacement_parameter in module.get_replacement_parameters(subworkflow_step):
replacement_parameters.add(replacement_parameter)
return list(replacement_parameters)
class InputProxy(object):
"""Provide InputParameter-interfaces over inputs but renamed for workflow context."""
@@ -1088,6 +1104,16 @@ class ToolModule(WorkflowModule):
action_arguments = None
return PostJobAction(value['action_type'], step, output_name, action_arguments)
def get_replacement_parameters(self, step):
"""Return a list of replacement parameters."""
replacement_parameters = set()
for pja in step.post_job_actions:
for argument in pja.action_arguments.values():
for match in re.findall(r'\$\{(.+?)\}', argument):
replacement_parameters.add(match)
return list(replacement_parameters)
class WorkflowModuleFactory(object):
+23 -19
View File
@@ -20,8 +20,10 @@ from base.populators import ( # noqa: I100
WorkflowPopulator
)
from base.workflow_fixtures import ( # noqa: I100
WORKFLOW_NESTED_REPLACEMENT_PARAMETER,
WORKFLOW_NESTED_RUNTIME_PARAMETER,
WORKFLOW_NESTED_SIMPLE,
WORKFLOW_RENAME_ON_INPUT,
WORKFLOW_RUNTIME_PARAMETER_AFTER_PAUSE,
WORKFLOW_WITH_DYNAMIC_OUTPUT_COLLECTION,
WORKFLOW_WITH_OUTPUT_COLLECTION,
@@ -224,6 +226,7 @@ class BaseWorkflowsApiTestCase(api.ApiTestCase):
test_data = jobs_descriptions.get("test_data", {})
parameters = test_data.pop('step_parameters', {})
replacement_parameters = test_data.pop("replacement_parameters", {})
inputs, label_map, has_uploads = load_data_dict(history_id, test_data, self.dataset_populator, self.dataset_collection_populator)
workflow_request = dict(
history="hist_id=%s" % history_id,
@@ -234,6 +237,8 @@ class BaseWorkflowsApiTestCase(api.ApiTestCase):
if parameters:
workflow_request["parameters"] = dumps(parameters)
workflow_request["parameters_normalized"] = True
if replacement_parameters:
workflow_request["replacement_params"] = dumps(replacement_parameters)
if has_uploads:
self.dataset_populator.wait_for_history(history_id, assert_ok=True)
url = "workflows/%s/usage" % (workflow_id)
@@ -980,6 +985,23 @@ test_data:
content = self.dataset_populator.get_history_dataset_content(history_id)
assert len([x for x in content.split("\n") if x]) == 2
@skip_without_tool("cat")
def test_run_subworkflow_replacment_parameters(self):
with self.dataset_populator.test_history() as history_id:
workflow_run_description = """%s
test_data:
replacement_parameters:
replaceme: moocow
outer_input:
value: 1.bed
type: File
""" % WORKFLOW_NESTED_REPLACEMENT_PARAMETER
self._run_jobs(workflow_run_description, history_id=history_id)
details = self.dataset_populator.get_history_dataset_details(history_id)
assert details["name"] == "moocow suffix"
@skip_without_tool("random_lines1")
def test_run_runtime_parameters_after_pause(self):
with self.dataset_populator.test_history() as history_id:
@@ -2247,25 +2269,7 @@ test_data: {}
@skip_without_tool("cat")
def test_run_rename_based_on_input(self):
with self.dataset_populator.test_history() as history_id:
self._run_jobs("""
class: GalaxyWorkflow
inputs:
- id: input1
steps:
- tool_id: cat
label: first_cat
state:
input1:
$link: input1
outputs:
out_file1:
rename: "#{input1 | basename} suffix"
test_data:
input1:
value: 1.fasta
type: File
name: fasta1
""", history_id=history_id)
self._run_jobs(WORKFLOW_RENAME_ON_INPUT, history_id=history_id)
content = self.dataset_populator.get_history_dataset_details(history_id, wait=True, assert_ok=True)
name = content["name"]
assert name == "fasta1 suffix", name
+64
View File
@@ -319,3 +319,67 @@ steps:
seed_source_selector: set_seed
seed: asdf
"""
WORKFLOW_RENAME_ON_INPUT = """
class: GalaxyWorkflow
inputs:
- id: input1
steps:
- tool_id: cat
label: first_cat
state:
input1:
$link: input1
outputs:
out_file1:
rename: "#{input1 | basename} suffix"
test_data:
input1:
value: 1.fasta
type: File
name: fasta1
"""
WORKFLOW_RENAME_ON_REPLACEMENT_PARAM = """
class: GalaxyWorkflow
inputs:
- id: input1
steps:
- tool_id: cat
label: first_cat
state:
input1:
$link: input1
outputs:
out_file1:
rename: "${replaceme} suffix"
"""
WORKFLOW_NESTED_REPLACEMENT_PARAMETER = """
class: GalaxyWorkflow
inputs:
- id: outer_input
outputs:
- id: outer_output
source: nested_workflow#workflow_output
steps:
- run:
class: GalaxyWorkflow
inputs:
- id: inner_input
outputs:
- id: workflow_output
source: first_cat#out_file1
steps:
- tool_id: cat
label: first_cat
state:
input1:
$link: inner_input
outputs:
out_file1:
rename: "${replaceme} suffix"
label: nested_workflow
connect:
inner_input: outer_input
"""
+43
View File
@@ -2,8 +2,10 @@ import yaml
from base import rules_test_data
from base.populators import load_data_dict
from base.workflow_fixtures import (
WORKFLOW_NESTED_REPLACEMENT_PARAMETER,
WORKFLOW_NESTED_RUNTIME_PARAMETER,
WORKFLOW_NESTED_SIMPLE,
WORKFLOW_RENAME_ON_REPLACEMENT_PARAM,
WORKFLOW_RUNTIME_PARAMETER_SIMPLE,
WORKFLOW_SIMPLE_CAT_TWICE,
WORKFLOW_WITH_DYNAMIC_OUTPUT_COLLECTION,
@@ -78,6 +80,38 @@ class WorkflowRunTestCase(SeleniumTestCase, UsesHistoryItemAssertions):
self._assert_has_3_lines_after_run(hid=2)
@selenium_test
@managed_history
def test_replacement_parameters(self):
self.perform_upload(self.get_filename("1.txt"))
self.wait_for_history()
self.open_in_workflow_run(WORKFLOW_RENAME_ON_REPLACEMENT_PARAM)
self.screenshot("workflow_run_rename_simple_empty")
self._set_replacement_parameter("replaceme", "moocow")
self.screenshot("workflow_run_rename_simple_input")
self.workflow_run_submit()
output_hid = 2
self.history_panel_wait_for_hid_ok(output_hid, allowed_force_refreshes=1)
history_id = self.current_history_id()
details = self.dataset_populator.get_history_dataset_details(history_id, hid=output_hid)
assert details["name"] == "moocow suffix", details
@selenium_test
@managed_history
def test_replacement_parameters_on_subworkflows(self):
self.perform_upload(self.get_filename("1.txt"))
self.wait_for_history()
self.open_in_workflow_run(WORKFLOW_NESTED_REPLACEMENT_PARAMETER)
self.screenshot("workflow_run_rename_subworkflow_empty")
self._set_replacement_parameter("replaceme", "moocow")
self.screenshot("workflow_run_rename_subworkflow_input")
self.workflow_run_submit()
output_hid = 2
self.history_panel_wait_for_hid_ok(output_hid, allowed_force_refreshes=1)
history_id = self.current_history_id()
details = self.dataset_populator.get_history_dataset_details(history_id, hid=output_hid)
assert details["name"] == "moocow suffix", details
@selenium_test
def test_execution_with_tool_upgrade(self):
name = self.workflow_upload_yaml_with_random_name(WORKFLOW_WITH_OLD_TOOL_VERSION, exact_tools=True)
@@ -148,3 +182,12 @@ class WorkflowRunTestCase(SeleniumTestCase, UsesHistoryItemAssertions):
assert initial_value == "1", initial_value
input_element.clear()
input_element.send_keys("3")
def _set_replacement_parameter(self, tour_id, value):
# for random_lines num_lines parameter as runtime parameter in workflow form.
div = self.tool_parameter_div(tour_id)
input_element = div.find_element_by_css_selector("input")
initial_value = input_element.get_attribute("value")
assert initial_value == "", initial_value
input_element.clear()
input_element.send_keys(value)