Skip to content

Commit

Permalink
[WIP] Simplified workflow run form.
Browse files Browse the repository at this point in the history
Builds on refactoring the workflow run form landing and orchestration into Vue (#9147).

Implements #9111 - part of https://github.com/galaxyproject/galaxy/projects/15 and outlined in http://bit.ly/simplified-workflow-ui.

What is in the PR:

- Add new Galaxy configuration option - ``simplified_workflow_run_ui`` set to ``off`` by default (at least until batch parameters are hidden or implemented).
- If instead the admin has set this parameter to ``prefer``, on the workflow run page scan the workflow run request and if three conditions are met show a simplfied tool form. These conditions are:
  - No workflow "resource parameters" - pretty niche, not documented, I don't think anyone outside of Canada is using them and even them I'm not sure if they ever got to production.
  - No "open" tools (i.e. tools with disconnected runtime inputs).
  - No "replacement parameters" defined outside PJAs. I'm calling #{} inside PJA and ${} inside tool steps both "replacement parameters" because the user populates them the same way in the GUI. The API only handles them inside the context of the PJA - it seems the GUI is responsible for replacing them at runtime in the traditional form.
- The simplified workflow form:
   - Drops tool and subworkflow steps from rendering all together and instead just renders the inputs. These are not rendered as separate "steps" or forms - but as just different inputs the same clean, simple form (more the like tool GUI). Labels (or step indexes as a fallback) are used at the input name, step annotations are used as the input help text.
   - Simplify the workflow request made by the client - send only replacement parameters and inputs - do not serialize tool state for each module. I think this makes what we are tracking more structured and should be more performant as well. Prevents storing HDA IDs in unstructured JSON blobs in the database as well.
   - Drops history target option to simplify the UI (see TODO below for followup)
   - Drops job caching option to simplify the UI (see TODO below for followup)
   - Drops resource parameters - we've already verified there are none.

TODO:

- Handle batch parameters - either implement them or disable them in this context.
- Make history target in simplified mode configurable via galaxy.yml - simplified_workflow_run_ui_target_history in ['new', 'current', 'show_selection'].
- Make job caching option in simplified mode configurable via galaxy.yml - simplified_workflow_run_ui_cache in ['on', 'off'].
  • Loading branch information
jmchilton committed May 11, 2020
1 parent 157c475 commit e44ea17
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 2 deletions.
49 changes: 49 additions & 0 deletions client/galaxy/scripts/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,20 @@
<workflow-run-form
ref="runform"
:model="model"
v-if="!simpleForm"
:setRunButtonStatus="setRunButtonStatus"
@submissionSuccess="handleInvocations"
/>
<div v-else>
<workflow-run-form-simple
ref="runform"
:model="model"
:setRunButtonStatus="setRunButtonStatus"
@submissionSuccess="handleInvocations"
/>
<!-- Options to default one way or the other, disable if admins want, etc.. -->
<a href="#" @click="showAdvanced">Expand to full workflow form.</a>
</div>
</div>
</span>
</span>
Expand All @@ -58,6 +69,7 @@ import WaitButton from "components/WaitButton";
import LoadingSpan from "components/LoadingSpan";
import WorkflowRunSuccess from "./WorkflowRunSuccess";
import WorkflowRunForm from "./WorkflowRunForm";
import WorkflowRunFormSimple from "./WorkflowRunFormSimple";
import { WorkflowRunModel } from "./model.js";
import { errorMessageAsString } from "utils/simple-error";
Expand All @@ -67,9 +79,14 @@ export default {
WaitButton,
WorkflowRunSuccess,
WorkflowRunForm,
WorkflowRunFormSimple,
},
props: {
workflowId: { type: String },
preferSimpleForm: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand All @@ -83,20 +100,49 @@ export default {
runButtonWaitText: "",
runButtonPercentage: -1,
invocations: null,
simpleForm: null,
model: null,
};
},
created() {
getRunData(this.workflowId)
.then((runData) => {
const model = new WorkflowRunModel(runData);
let simpleForm = this.preferSimpleForm;
if (simpleForm) {
// These only work with PJA - the API doesn't evaluate them at
// all outside that context currently. The main workflow form renders
// these dynamically and takes care of all the validation and setup details
// on the frontend. If these are implemented on the backend at some
// point this restriction can be lifted.
if (model.hasReplacementParametersInToolForm) {
console.log("cannot render simple workflow form - has ${} values in tool steps");
simpleForm = false;
}
// If there are required parameters in a tool form (a disconnected runtime
// input), we have to render the tool form steps and cannot use the
// simplified tool form.
if (model.hasOpenToolSteps) {
console.log(
"cannot render simple workflow form - one or more tools have disconnected runtime inputs"
);
}
// Just render the whole form for resource request parameters (kind of
// niche - I'm not sure anyone is using these currently anyway).
if (model.hasWorkflowResourceParameters) {
console.log(`Cannot render simple workflow form - workflow resource parameters are configured`);
simpleForm = false;
}
}
this.simpleForm = simpleForm;
this.model = model;
this.hasUpgradeMessages = model.hasUpgradeMessages;
this.hasStepVersionChanges = model.hasStepVersionChanges;
this.workflowName = this.model.name;
this.loading = false;
})
.catch((response) => {
console.log(response);
this.error = errorMessageAsString(response);
});
},
Expand All @@ -112,6 +158,9 @@ export default {
handleInvocations(invocations) {
this.invocations = invocations;
},
showAdvanced() {
this.simpleForm = false;
},
},
};
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<div ref="form"></div>
</template>

<script>
import $ from "jquery";
import _ from "underscore";
import Form from "mvc/form/form-view";
import { invokeWorkflow } from "./services";
export default {
props: {
model: {
type: Object,
required: true,
},
setRunButtonStatus: {
type: Function,
required: true,
},
},
data() {
return {
form: null,
inputTypes: {},
};
},
computed: {},
created() {
this.form = new Form(this.formDefinition());
this.$nextTick(() => {
const el = this.$refs["form"];
$(el).append(this.form.$el);
});
},
methods: {
formDefinition() {
const inputs = [];
// Add workflow parameters.
_.each(this.model.wpInputs, (input, i) => {
// do we want to keep the color if we're not showing steps?
input["color"] = undefined;
inputs.push(input);
this.inputTypes[input.name] = "replacement_parameter";
});
// Add actual input modules.
_.each(this.model.steps, (step, i) => {
const is_input =
["data_input", "data_collection_input", "parameter_input"].indexOf(step.step_type) != -1;
if (!is_input) {
return;
}
const stepName = new String(step.step_index);
const stepLabel = step.step_label || stepName;
const help = step.annotation;
const longFormInput = step.inputs[0];
const stepAsInput = Object.assign({}, longFormInput, { name: stepName, help: help, label: stepLabel });
inputs.push(stepAsInput);
this.inputTypes[stepName] = step.step_type;
});
const def = {
inputs: inputs,
};
return def;
},
execute() {
const replacementParams = {};
const inputs = {};
const formData = this.form.data.create();
for (const inputName in formData) {
const value = formData[inputName];
const inputType = this.inputTypes[inputName];
if (inputType == "replacement_parameter") {
replacementParams[inputName] = value;
} else if (inputType == "data_input" || inputType == "data_collection_input") {
const values = value["values"];
if (values.length > 1) {
console.error("batch values not yet implemented");
} else if (values.length > 0) {
inputs[inputName] = value["values"][0];
}
} else {
inputs[inputName] = value;
}
}
const data = {
replacement_dict: replacementParams,
inputs: inputs,
inputs_by: "step_index",
};
// Consider: copy_inputs_to_history, target_history
invokeWorkflow(this.model.workflowId, data)
.then((invocation) => {
// emulate multi-invocation mode like other form for now.
const invocations = [invocation];
this.$emit("submissionSuccess", invocations);
})
.catch((error) => {
console.log(error);
});
},
},
};
</script>
9 changes: 8 additions & 1 deletion client/galaxy/scripts/components/Workflow/Run/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ export class WorkflowRunModel {
this.runData = runData;
this.name = runData.name;
this.workflowResourceParameters = runData.workflow_resource_parameters;
this.hasWorkflowResourceParameters = !_.isEmpty(this.workflowResourceParameters);
this.historyId = runData.history_id;
this.workflowId = runData.id;

this.hasUpgradeMessages = runData.has_upgrade_messages;
this.hasStepVersionChanges = runData.step_version_changes && runData.step_version_changes.length > 0;
this.hasStepVersionChanges = !_.isEmpty(runData.step_version_changes);

this.steps = [];
this.links = [];
this.parms = [];
this.wpInputs = {};
let hasOpenToolSteps = false;
let hasReplacementParametersInToolForm = false;

_.each(runData.steps, (step, i) => {
var icon = WorkflowIcons[step.step_type];
Expand Down Expand Up @@ -130,6 +133,7 @@ export class WorkflowRunModel {
_.each(this.steps, (step, i) => {
_.each(this.parms[i], (input, name) => {
_handleWorkflowParameter(input.value, (wp_input) => {
hasReplacementParametersInToolForm = true;
wp_input.links.push(step);
input.wp_linked = true;
input.type = "text";
Expand Down Expand Up @@ -166,6 +170,7 @@ export class WorkflowRunModel {
(input.value && input.value.__class__ == "RuntimeValue" && !input.step_linked)
) {
step.collapsed = false;
hasOpenToolSteps = true;
}
if (is_runtime_value) {
input.value = null;
Expand All @@ -180,6 +185,8 @@ export class WorkflowRunModel {
});
}
});
this.hasOpenToolSteps = hasOpenToolSteps;
this.hasReplacementParametersInToolForm = hasReplacementParametersInToolForm;
}
}

Expand Down
5 changes: 4 additions & 1 deletion client/galaxy/scripts/entry/analysis/AnalysisRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ export const getAnalysisRouter = (Galaxy) =>
/** load workflow by its url in run mode */
_loadWorkflow: function () {
const workflowId = QueryStringParsing.get("id");
this._display_vue_helper(WorkflowRun, { workflowId: workflowId }, "workflow");
// const preferSimpleForm = (QueryStringParsing.get("simple") || "false").toLowerCase() == "true";
const Galaxy = getGalaxyInstance();
const preferSimpleForm = Galaxy.config.simplified_workflow_run_ui == "prefer";
this._display_vue_helper(WorkflowRun, { workflowId, preferSimpleForm }, "workflow");
},
});
1 change: 1 addition & 0 deletions lib/galaxy/managers/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def _use_config(config, key, **context):
'ga_code' : _use_config,
'enable_unique_workflow_defaults' : _use_config,
'enable_beta_markdown_export' : _use_config,
'simplified_workflow_run_ui' : _use_config,
'has_user_tool_filters' : _defaults_to(False),
# TODO: is there no 'correct' way to get an api url? controller='api', action='tools' is a hack
# at any rate: the following works with path_prefix but is still brittle
Expand Down
13 changes: 13 additions & 0 deletions lib/galaxy/webapps/galaxy/config_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2408,6 +2408,19 @@ mapping:
When false, the most recently added compatible item in the history will
be used for each "Set at Runtime" input, independent of others in the workflow.
simplified_workflow_run_ui:
type: str
default: 'off'
required: false
desc: |
If set to 'off' by default, always use the traditional workflow form that renders
all steps in the GUI and serializes the tool state of all steps during
invocation. Set to 'prefer' to default to a simplified workflow UI that
only renders the inputs if possible (the workflow must have no disconnected
runtime inputs and not replacement parameters within tool steps). In the
future 'force' may be added an option for Galaskio-style servers that should
only render simplified workflows.
myexperiment_target_url:
type: str
default: www.myexperiment.org:80
Expand Down
1 change: 1 addition & 0 deletions test/functional/tools/samples_tool_conf.xml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
<tool file="for_workflows/count_list.xml" />
<tool file="for_workflows/count_multi_file.xml" />
<tool file="for_workflows/create_input_collection.xml" />
<tool file="for_workflows/randomlines.xml" />

<section id="filter" name="For Tours">
<tool file="for_tours/filtering.xml" />
Expand Down

0 comments on commit e44ea17

Please sign in to comment.