Skip to content

Commit

Permalink
Merge pull request #9151 from jmchilton/simplified_run
Browse files Browse the repository at this point in the history
Simplified Workflow Run Form
  • Loading branch information
dannon committed Aug 14, 2020
2 parents 3cab834 + 4626f71 commit 246603a
Show file tree
Hide file tree
Showing 13 changed files with 388 additions and 47 deletions.
60 changes: 60 additions & 0 deletions client/src/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,22 @@
<workflow-run-form
ref="runform"
:model="model"
v-if="!simpleForm"
:set-run-button-status="setRunButtonStatus"
@submissionSuccess="handleInvocations"
/>
<div v-else>
<workflow-run-form-simple
ref="runform"
:model="model"
:set-run-button-status="setRunButtonStatus"
:target-history="simpleFormTargetHistory"
:use-job-cache="simpleFormUseJobCache"
@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 +71,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 +81,22 @@ export default {
WaitButton,
WorkflowRunSuccess,
WorkflowRunForm,
WorkflowRunFormSimple,
},
props: {
workflowId: { type: String },
preferSimpleForm: {
type: Boolean,
default: false,
},
simpleFormTargetHistory: {
type: String,
default: "current",
},
simpleFormUseJobCache: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand All @@ -83,20 +110,50 @@ 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"
);
simpleForm = false;
}
// 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 +169,9 @@ export default {
handleInvocations(invocations) {
this.invocations = invocations;
},
showAdvanced() {
this.simpleForm = false;
},
},
};
</script>
114 changes: 114 additions & 0 deletions client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<template>
<div ref="form"></div>
</template>

<script>
import Form from "mvc/form/form-view";
import { invokeWorkflow } from "./services";
export default {
props: {
model: {
type: Object,
required: true,
},
setRunButtonStatus: {
type: Function,
required: true,
},
targetHistory: {
type: String,
default: "current",
},
useJobCache: {
type: Boolean,
default: false,
},
},
data() {
return {
form: null,
inputTypes: {},
};
},
computed: {},
created() {
this.form = new Form(this.formDefinition());
this.$nextTick(() => {
const el = this.$refs["form"];
el.appendChild(this.form.$el[0]);
});
},
methods: {
formDefinition() {
const inputs = [];
// Add workflow parameters.
Object.values(this.model.wpInputs).forEach((input) => {
const inputCopy = Object.assign({}, input);
// do we want to keep the color if we're not showing steps?
inputCopy.color = undefined;
inputs.push(inputCopy);
this.inputTypes[inputCopy.name] = "replacement_parameter";
});
// Add actual input modules.
this.model.steps.forEach((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 || new String(step.step_index + 1);
const help = step.annotation;
const longFormInput = step.inputs[0];
const stepAsInput = Object.assign({}, longFormInput, { name: stepName, help: help, label: stepLabel });
// disable collection mapping...
stepAsInput.flavor = "module";
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") {
inputs[inputName] = value;
}
}
const data = {
replacement_dict: replacementParams,
inputs: inputs,
inputs_by: "step_index",
batch: true,
use_cached_job: this.useJobCache,
};
if (this.targetHistory == "current") {
data.history_id = this.model.historyId;
} else {
data.new_history_name = this.model.name;
}
invokeWorkflow(this.model.workflowId, data)
.then((invocations) => {
this.$emit("submissionSuccess", invocations);
})
.catch((error) => {
console.log(error);
});
},
},
};
</script>
9 changes: 8 additions & 1 deletion client/src/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
16 changes: 15 additions & 1 deletion client/src/entry/analysis/AnalysisRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,20 @@ 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 Galaxy = getGalaxyInstance();
let preferSimpleForm = Galaxy.config.simplified_workflow_run_ui == "prefer";
const preferSimpleFormOverride = QueryStringParsing.get("simplified_workflow_run_ui");
if (preferSimpleFormOverride == "prefer") {
preferSimpleForm = true;
}
const simpleFormTargetHistory = Galaxy.config.simplified_workflow_run_ui_target_history;
const simpleFormUseJobCache = Galaxy.config.simplified_workflow_run_ui_job_cache == "on";
const props = {
workflowId,
preferSimpleForm,
simpleFormTargetHistory,
simpleFormUseJobCache,
};
this._display_vue_helper(WorkflowRun, props, "workflow");
},
});
3 changes: 3 additions & 0 deletions lib/galaxy/managers/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ 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,
'simplified_workflow_run_ui_target_history': _use_config,
'simplified_workflow_run_ui_job_cache': _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
Loading

0 comments on commit 246603a

Please sign in to comment.