From 828c37c6ff84115b4ae033a719aea66d894637ae Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 11 May 2020 11:53:40 -0400 Subject: [PATCH] [WIP] Simplified workflow run form. 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']. --- .../components/Workflow/Run/WorkflowRun.vue | 49 ++++++++ .../Workflow/Run/WorkflowRunFormSimple.vue | 109 ++++++++++++++++++ .../scripts/components/Workflow/Run/model.js | 9 +- .../scripts/entry/analysis/AnalysisRouter.js | 5 +- lib/galaxy/managers/configuration.py | 1 + lib/galaxy/webapps/galaxy/config_schema.yml | 13 +++ test/functional/tools/samples_tool_conf.xml | 1 + 7 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 client/galaxy/scripts/components/Workflow/Run/WorkflowRunFormSimple.vue diff --git a/client/galaxy/scripts/components/Workflow/Run/WorkflowRun.vue b/client/galaxy/scripts/components/Workflow/Run/WorkflowRun.vue index a7d509ef30dd..850bc95a56ac 100644 --- a/client/galaxy/scripts/components/Workflow/Run/WorkflowRun.vue +++ b/client/galaxy/scripts/components/Workflow/Run/WorkflowRun.vue @@ -44,9 +44,20 @@ +
+ + + Expand to full workflow form. +
@@ -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"; @@ -67,9 +79,14 @@ export default { WaitButton, WorkflowRunSuccess, WorkflowRunForm, + WorkflowRunFormSimple, }, props: { workflowId: { type: String }, + preferSimpleForm: { + type: Boolean, + default: false, + }, }, data() { return { @@ -83,6 +100,7 @@ export default { runButtonWaitText: "", runButtonPercentage: -1, invocations: null, + simpleForm: null, model: null, }; }, @@ -90,6 +108,33 @@ export default { 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; @@ -97,6 +142,7 @@ export default { this.loading = false; }) .catch((response) => { + console.log(response); this.error = errorMessageAsString(response); }); }, @@ -112,6 +158,9 @@ export default { handleInvocations(invocations) { this.invocations = invocations; }, + showAdvanced() { + this.simpleForm = false; + }, }, }; diff --git a/client/galaxy/scripts/components/Workflow/Run/WorkflowRunFormSimple.vue b/client/galaxy/scripts/components/Workflow/Run/WorkflowRunFormSimple.vue new file mode 100644 index 000000000000..1e70c2cc4eec --- /dev/null +++ b/client/galaxy/scripts/components/Workflow/Run/WorkflowRunFormSimple.vue @@ -0,0 +1,109 @@ + + + diff --git a/client/galaxy/scripts/components/Workflow/Run/model.js b/client/galaxy/scripts/components/Workflow/Run/model.js index 1e5f2bdba14e..d1bfa3eb5bf5 100644 --- a/client/galaxy/scripts/components/Workflow/Run/model.js +++ b/client/galaxy/scripts/components/Workflow/Run/model.js @@ -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]; @@ -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"; @@ -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; @@ -180,6 +185,8 @@ export class WorkflowRunModel { }); } }); + this.hasOpenToolSteps = hasOpenToolSteps; + this.hasReplacementParametersInToolForm = hasReplacementParametersInToolForm; } } diff --git a/client/galaxy/scripts/entry/analysis/AnalysisRouter.js b/client/galaxy/scripts/entry/analysis/AnalysisRouter.js index c4a38eded3a7..0c3abddab952 100644 --- a/client/galaxy/scripts/entry/analysis/AnalysisRouter.js +++ b/client/galaxy/scripts/entry/analysis/AnalysisRouter.js @@ -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"); }, }); diff --git a/lib/galaxy/managers/configuration.py b/lib/galaxy/managers/configuration.py index a34c21ccd518..48f3a22b9d51 100644 --- a/lib/galaxy/managers/configuration.py +++ b/lib/galaxy/managers/configuration.py @@ -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 diff --git a/lib/galaxy/webapps/galaxy/config_schema.yml b/lib/galaxy/webapps/galaxy/config_schema.yml index 204dd45b84d5..a66c59e3a77c 100644 --- a/lib/galaxy/webapps/galaxy/config_schema.yml +++ b/lib/galaxy/webapps/galaxy/config_schema.yml @@ -2412,6 +2412,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 diff --git a/test/functional/tools/samples_tool_conf.xml b/test/functional/tools/samples_tool_conf.xml index a7ae1aabffaf..bd42b438f091 100644 --- a/test/functional/tools/samples_tool_conf.xml +++ b/test/functional/tools/samples_tool_conf.xml @@ -191,6 +191,7 @@ +