From 88c6639439b5e29f02795c269417a0cdcadd3749 Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Tue, 3 Sep 2024 09:58:45 -0400 Subject: [PATCH 01/26] New component for Title and Annotation detail --- .../Workflow/Run/WorkflowRunForm.vue | 8 +- .../Workflow/Run/WorkflowRunFormSimple.vue | 8 +- .../Workflow/Run/WorkflowRunName.vue | 81 +++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 client/src/components/Workflow/Run/WorkflowRunName.vue diff --git a/client/src/components/Workflow/Run/WorkflowRunForm.vue b/client/src/components/Workflow/Run/WorkflowRunForm.vue index 6b23e8895ea0..9ac415b24214 100644 --- a/client/src/components/Workflow/Run/WorkflowRunForm.vue +++ b/client/src/components/Workflow/Run/WorkflowRunForm.vue @@ -7,7 +7,11 @@
- Workflow: {{ model.name }} (version: {{ model.runData.version + 1 }}) + - Workflow: {{ model.name }} (version: {{ model.runData.version + 1 }}) + +
+ Workflow: {{ model.name }} (version: {{ model.runData.version + 1 }}) + + + + + + +
+ + + From 122b1cc6595959b03e2997172ebca094e14c6d4b Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Tue, 3 Sep 2024 10:39:30 -0400 Subject: [PATCH 02/26] Added expand/collapse for Annotation detail --- .../Workflow/Run/WorkflowRunName.vue | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/client/src/components/Workflow/Run/WorkflowRunName.vue b/client/src/components/Workflow/Run/WorkflowRunName.vue index 2fb401a16beb..e0af0ab980ca 100644 --- a/client/src/components/Workflow/Run/WorkflowRunName.vue +++ b/client/src/components/Workflow/Run/WorkflowRunName.vue @@ -1,19 +1,26 @@ @@ -32,13 +39,10 @@ export default { type: Object, required: true, }, - canMutateCurrentHistory: { - type: Boolean, - required: true, - }, }, data() { return { + expandAnnotations: true, workflowInfoData: {}, fullWorkflow: {}, }; From 829258bd3aa30646d5740334812d19b1ebb9f303 Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Tue, 10 Sep 2024 10:25:09 -0400 Subject: [PATCH 03/26] Component upgrade, minor styling changes, code cleanup --- .../Workflow/Run/WorkflowRunForm.vue | 7 +- .../Workflow/Run/WorkflowRunFormSimple.vue | 9 +- .../Workflow/Run/WorkflowRunName.vue | 138 ++++++++---------- 3 files changed, 67 insertions(+), 87 deletions(-) diff --git a/client/src/components/Workflow/Run/WorkflowRunForm.vue b/client/src/components/Workflow/Run/WorkflowRunForm.vue index 9ac415b24214..cedc38329e32 100644 --- a/client/src/components/Workflow/Run/WorkflowRunForm.vue +++ b/client/src/components/Workflow/Run/WorkflowRunForm.vue @@ -7,11 +7,7 @@
- + Workflow: {{ model.name }} (version: {{ model.runData.version + 1 }})
+ - - + + From 87b741599f709471794c5a679ec7ccff21385948 Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Tue, 10 Sep 2024 13:53:01 -0400 Subject: [PATCH 04/26] Cleaner error handling --- client/src/components/Workflow/Run/WorkflowRunName.vue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/components/Workflow/Run/WorkflowRunName.vue b/client/src/components/Workflow/Run/WorkflowRunName.vue index ce6fde56ff67..05fcd9c6cf48 100644 --- a/client/src/components/Workflow/Run/WorkflowRunName.vue +++ b/client/src/components/Workflow/Run/WorkflowRunName.vue @@ -1,11 +1,12 @@ diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 48be30372d67..64158933ec07 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -908,6 +908,23 @@ def _workflow_from_raw_description( return workflow, missing_tool_tups + def convert_to_dict_from_many(self, obj, depth=0, max_depth=5): + if depth > max_depth: + return "" + + if isinstance(obj, list): + return [self.convert_to_dict_from_many(item, depth=depth + 1, max_depth=max_depth) for item in obj] + elif isinstance(obj, dict) or hasattr(obj, "__dict__"): + if isinstance(obj, dict): + items = obj.items() # Dictionary case + else: + items = obj.__dict__.items() # Custom object case + + return {key: self.convert_to_dict_from_many(value, depth=depth + 1, max_depth=max_depth) + for key, value in items if not key.startswith('_')} + else: + return obj + def workflow_to_dict(self, trans, stored, style="export", version=None, history=None): """Export the workflow contents to a dictionary ready for JSON-ification and to be sent out via API for instance. There are three styles of export allowed 'export', 'instance', and @@ -1071,6 +1088,11 @@ def _workflow_to_dict_run(self, trans, stored, workflow, history=None): } for oc in step.output_connections ] + annotations_dict = { + "annotation": stored.annotations[0].annotation, + "update_time": (stored.annotations[0].stored_workflow.update_time).isoformat(), + "tags": self.convert_to_dict_from_many(stored.annotations[0].stored_workflow.tags), + } if step.annotations: step_model["annotation"] = step.annotations[0].annotation if step.upgrade_messages: @@ -1078,6 +1100,7 @@ def _workflow_to_dict_run(self, trans, stored, workflow, history=None): step_models.append(step_model) return { "id": trans.app.security.encode_id(stored.id), + "annotation": annotations_dict, "history_id": trans.app.security.encode_id(history.id) if history else None, "name": stored.name, "owner": stored.user.username, From a1ddc7fd6da54f72381ffcc5a1c513d2cdaef40d Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Tue, 24 Sep 2024 15:59:37 -0400 Subject: [PATCH 10/26] Backend code formatting --- lib/galaxy/managers/workflows.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 64158933ec07..1d541a8871c4 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -920,8 +920,11 @@ def convert_to_dict_from_many(self, obj, depth=0, max_depth=5): else: items = obj.__dict__.items() # Custom object case - return {key: self.convert_to_dict_from_many(value, depth=depth + 1, max_depth=max_depth) - for key, value in items if not key.startswith('_')} + return { + key: self.convert_to_dict_from_many(value, depth=depth + 1, max_depth=max_depth) + for key, value in items + if not key.startswith("_") + } else: return obj From ecb1fd1fd593c43f4d766f5ec2dcf9850fb7cbdf Mon Sep 17 00:00:00 2001 From: Michelle Savage Date: Fri, 25 Oct 2024 14:37:12 -0400 Subject: [PATCH 11/26] Update lib/galaxy/managers/workflows.py Co-authored-by: Marius van den Beek --- lib/galaxy/managers/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 1d541a8871c4..9a0213574a90 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -1093,7 +1093,7 @@ def _workflow_to_dict_run(self, trans, stored, workflow, history=None): ] annotations_dict = { "annotation": stored.annotations[0].annotation, - "update_time": (stored.annotations[0].stored_workflow.update_time).isoformat(), + "update_time": (stored.update_time).isoformat(), "tags": self.convert_to_dict_from_many(stored.annotations[0].stored_workflow.tags), } if step.annotations: From 51f5de61ae509996f472d8c9f46f662d5ba47222 Mon Sep 17 00:00:00 2001 From: Michelle Savage Date: Fri, 25 Oct 2024 14:43:27 -0400 Subject: [PATCH 12/26] Update lib/galaxy/managers/workflows.py Co-authored-by: Marius van den Beek --- lib/galaxy/managers/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 9a0213574a90..c2a4a133b7ea 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -1094,7 +1094,7 @@ def _workflow_to_dict_run(self, trans, stored, workflow, history=None): annotations_dict = { "annotation": stored.annotations[0].annotation, "update_time": (stored.update_time).isoformat(), - "tags": self.convert_to_dict_from_many(stored.annotations[0].stored_workflow.tags), + "tags": self.convert_to_dict_from_many(stored.tags), } if step.annotations: step_model["annotation"] = step.annotations[0].annotation From 625f5e301b9edf5b08988939108f1e6f7ea7cbd6 Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Fri, 25 Oct 2024 15:22:19 -0400 Subject: [PATCH 13/26] Coding convention update --- lib/galaxy/managers/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index c2a4a133b7ea..a45bb340c2d7 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -1092,7 +1092,7 @@ def _workflow_to_dict_run(self, trans, stored, workflow, history=None): for oc in step.output_connections ] annotations_dict = { - "annotation": stored.annotations[0].annotation, + "annotation": self.get_item_annotation_str(trans.sa_session, trans.user, stored), "update_time": (stored.update_time).isoformat(), "tags": self.convert_to_dict_from_many(stored.tags), } From ab5bc43d76c0834768d1752cfdc5c71465a43e71 Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Mon, 4 Nov 2024 09:49:23 -0500 Subject: [PATCH 14/26] Added reusability & consistency for Workflow headers on: Workflow Invocations Page and Workflow Run Form Page --- .../Workflow/WorkflowAnnotation.vue | 153 ++++++++++++ .../Workflow/WorkflowNavigationTitle.vue | 233 ++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 client/src/components/Workflow/WorkflowAnnotation.vue create mode 100644 client/src/components/Workflow/WorkflowNavigationTitle.vue diff --git a/client/src/components/Workflow/WorkflowAnnotation.vue b/client/src/components/Workflow/WorkflowAnnotation.vue new file mode 100644 index 000000000000..d2a558a34c05 --- /dev/null +++ b/client/src/components/Workflow/WorkflowAnnotation.vue @@ -0,0 +1,153 @@ + + + diff --git a/client/src/components/Workflow/WorkflowNavigationTitle.vue b/client/src/components/Workflow/WorkflowNavigationTitle.vue new file mode 100644 index 000000000000..f242881ea807 --- /dev/null +++ b/client/src/components/Workflow/WorkflowNavigationTitle.vue @@ -0,0 +1,233 @@ + + + From 1715e9698832960871398f0fb5edfc000d1c418f Mon Sep 17 00:00:00 2001 From: hujambo-dunia Date: Mon, 4 Nov 2024 09:50:26 -0500 Subject: [PATCH 15/26] Added reusability & consistency for Workflow headers on: Workflow Invocations Page and Workflow Run Form Page --- .../Workflow/Run/WorkflowRunFormSimple.vue | 75 ++++- .../Workflow/Run/WorkflowRunName.vue | 56 ---- .../Workflow/Run/WorkflowRunSuccess.vue | 4 +- .../Workflow/WorkflowAnnotation.vue | 153 ---------- .../WorkflowInvocationHeader.test.ts | 22 +- .../WorkflowInvocationHeader.vue | 286 ------------------ .../WorkflowInvocationState.vue | 157 ++++++++-- 7 files changed, 222 insertions(+), 531 deletions(-) delete mode 100644 client/src/components/Workflow/Run/WorkflowRunName.vue delete mode 100644 client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.vue diff --git a/client/src/components/Workflow/Run/WorkflowRunFormSimple.vue b/client/src/components/Workflow/Run/WorkflowRunFormSimple.vue index 0f0a5e4f2722..74e942f49de0 100644 --- a/client/src/components/Workflow/Run/WorkflowRunFormSimple.vue +++ b/client/src/components/Workflow/Run/WorkflowRunFormSimple.vue @@ -55,6 +55,9 @@ + + +
- + + + + +
+
+ + About This Workflow + + + + +
+
+ + + + + {{ model.runData.annotation.annotation }} +
+ +
+
+ + + + + diff --git a/client/src/components/Workflow/Run/WorkflowRunName.vue b/client/src/components/Workflow/Run/WorkflowRunName.vue deleted file mode 100644 index 43c20b311343..000000000000 --- a/client/src/components/Workflow/Run/WorkflowRunName.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git a/client/src/components/Workflow/Run/WorkflowRunSuccess.vue b/client/src/components/Workflow/Run/WorkflowRunSuccess.vue index 13d9d6cb8b55..c51868e53705 100644 --- a/client/src/components/Workflow/Run/WorkflowRunSuccess.vue +++ b/client/src/components/Workflow/Run/WorkflowRunSuccess.vue @@ -33,7 +33,7 @@ const targetHistories = props.invocations.reduce((histories, invocation) => { }, [] as string[]); const wasNewHistoryTarget = props.invocations.length > 0 && - props.invocations[0]?.history_id && + !!props.invocations[0]?.history_id && historyStore.currentHistoryId !== props.invocations[0].history_id; @@ -51,7 +51,7 @@ const wasNewHistoryTarget =
diff --git a/client/src/components/Workflow/WorkflowAnnotation.vue b/client/src/components/Workflow/WorkflowAnnotation.vue index d2a558a34c05..e69de29bb2d1 100644 --- a/client/src/components/Workflow/WorkflowAnnotation.vue +++ b/client/src/components/Workflow/WorkflowAnnotation.vue @@ -1,153 +0,0 @@ - - - diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.test.ts b/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.test.ts index eaabaad4015d..78a001efc20e 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.test.ts +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.test.ts @@ -7,7 +7,7 @@ import { getLocalVue } from "tests/jest/helpers"; import sampleInvocation from "@/components/Workflow/test/json/invocation.json"; import { useUserStore } from "@/stores/userStore"; -import WorkflowInvocationHeader from "./WorkflowInvocationHeader.vue"; +import WorkflowNavigationTitle from "../Workflow/WorkflowNavigationTitle.vue"; // Constants const WORKFLOW_OWNER = "test-user"; @@ -62,14 +62,14 @@ jest.mock("@/stores/workflowStore", () => { const localVue = getLocalVue(); /** - * Mounts the WorkflowInvocationHeader component with props/stores adjusted given the parameters + * Mounts the WorkflowNavigationTitle component with props/stores adjusted given the parameters * @param ownsWorkflow Whether the user owns the workflow associated with the invocation * @param hasReturnBtn Whether the component should have a return to invocations list button * @param unimportableWorkflow Whether the workflow import should fail * @returns The wrapper object */ -async function mountWorkflowInvocationHeader(ownsWorkflow = true, hasReturnBtn = false, unimportableWorkflow = false) { - const wrapper = shallowMount(WorkflowInvocationHeader as object, { +async function mountWorkflowNavigationTitle(ownsWorkflow = true, hasReturnBtn = false, unimportableWorkflow = false) { + const wrapper = shallowMount(WorkflowNavigationTitle as object, { propsData: { invocation: { ...sampleInvocation, @@ -94,10 +94,10 @@ async function mountWorkflowInvocationHeader(ownsWorkflow = true, hasReturnBtn = return { wrapper }; } -describe("WorkflowInvocationHeader renders", () => { +describe("WorkflowNavigationTitle renders", () => { // Included both cases in one test because these are always constant it("(always) the workflow name in header and run button in actions", async () => { - const { wrapper } = await mountWorkflowInvocationHeader(); + const { wrapper } = await mountWorkflowNavigationTitle(); const heading = wrapper.find(SELECTORS.INVOKED_WORKFLOW_HEADING); expect(heading.text()).toBe(`Invoked Workflow: "${SAMPLE_WORKFLOW.name}"`); @@ -108,7 +108,7 @@ describe("WorkflowInvocationHeader renders", () => { }); it("edit button if user owns the workflow", async () => { - const { wrapper } = await mountWorkflowInvocationHeader(); + const { wrapper } = await mountWorkflowNavigationTitle(); const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); const editButton = actionsGroup.find(SELECTORS.EDIT_WORKFLOW_BUTTON); expect(editButton.attributes("to")).toBe( @@ -117,16 +117,16 @@ describe("WorkflowInvocationHeader renders", () => { }); it("import button instead if user does not own the workflow", async () => { - const { wrapper } = await mountWorkflowInvocationHeader(false); + const { wrapper } = await mountWorkflowNavigationTitle(false); const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); expect(importButton.exists()).toBe(true); }); }); -describe("Importing a workflow in WorkflowInvocationHeader", () => { +describe("Importing a workflow in WorkflowNavigationTitle", () => { it("should show a confirmation dialog when the import is successful", async () => { - const { wrapper } = await mountWorkflowInvocationHeader(false); + const { wrapper } = await mountWorkflowNavigationTitle(false); const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); @@ -140,7 +140,7 @@ describe("Importing a workflow in WorkflowInvocationHeader", () => { }); it("should show an error dialog when the import fails", async () => { - const { wrapper } = await mountWorkflowInvocationHeader(false, false, true); + const { wrapper } = await mountWorkflowNavigationTitle(false, false, true); const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.vue deleted file mode 100644 index 9009597e72ad..000000000000 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.vue +++ /dev/null @@ -1,286 +0,0 @@ - - - - - diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index 33af070b8c88..d6b96ea7c99c 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { BAlert, BButton, BNavItem, BTab, BTabs } from "bootstrap-vue"; import { computed, onUnmounted, ref, watch } from "vue"; -import { type InvocationJobsSummary, type WorkflowInvocationElementView } from "@/api/invocations"; +import { type InvocationJobsSummary, type InvocationStep, type WorkflowInvocationElementView } from "@/api/invocations"; import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver"; import { getRootFromIndexLink } from "@/onload"; import { useInvocationStore } from "@/stores/invocationStore"; @@ -12,12 +12,20 @@ import { useWorkflowStore } from "@/stores/workflowStore"; import { errorMessageAsString } from "@/utils/simple-error"; import { cancelWorkflowScheduling } from "./services"; -import { isTerminal, jobCount, runningCount } from "./util"; +import { + errorCount as jobStatesSummaryErrorCount, + isTerminal, + jobCount as jobStatesSummaryJobCount, + numTerminal, + okCount as jobStatesSummaryOkCount, + runningCount as jobStatesSummaryRunningCount, +} from "./util"; import WorkflowInvocationSteps from "../Workflow/Invocation/Graph/WorkflowInvocationSteps.vue"; import InvocationReport from "../Workflow/InvocationReport.vue"; +import WorkflowAnnotation from "../Workflow/WorkflowAnnotation.vue"; +import WorkflowNavigationTitle from "../Workflow/WorkflowNavigationTitle.vue"; import WorkflowInvocationExportOptions from "./WorkflowInvocationExportOptions.vue"; -import WorkflowInvocationHeader from "./WorkflowInvocationHeader.vue"; import WorkflowInvocationInputOutputTabs from "./WorkflowInvocationInputOutputTabs.vue"; import WorkflowInvocationMetrics from "./WorkflowInvocationMetrics.vue"; import WorkflowInvocationOverview from "./WorkflowInvocationOverview.vue"; @@ -29,12 +37,13 @@ interface Props { isFullPage?: boolean; fromPanel?: boolean; success?: boolean; - newHistoryTarget?: string; + newHistoryTarget?: boolean; + targetHistory: string; } const props = withDefaults(defineProps(), { isSubworkflow: false, - newHistoryTarget: undefined, + targetHistory: "current", }); const emit = defineEmits<{ @@ -69,10 +78,8 @@ const disabledReportTooltip = computed(() => { const state = invocationState.value; if (state != "scheduled") { return `This workflow is not currently scheduled. The current state is ${state}. Once the workflow is fully scheduled and jobs have complete this option will become available.`; - } else if (runningCount(jobStatesSummary.value) != 0) { - return `The workflow invocation still contains ${runningCount( - jobStatesSummary.value - )} running job(s). Once these jobs have completed this option will become available.`; + } else if (runningCount.value != 0) { + return `The workflow invocation still contains ${runningCount.value} running job(s). Once these jobs have completed this option will become available.`; } else { return "Steps for this workflow are still running. A report will be available once complete."; } @@ -100,7 +107,7 @@ const invocationSchedulingTerminal = computed(() => { ); }); const jobStatesTerminal = computed(() => { - if (invocationSchedulingTerminal.value && jobCount(jobStatesSummary.value as InvocationJobsSummary) === 0) { + if (invocationSchedulingTerminal.value && jobCount.value === 0) { // no jobs for this invocation (think subworkflow or just inputs) return true; } @@ -113,11 +120,68 @@ const jobStatesSummary = computed(() => { const invocationStateSuccess = computed(() => { return ( invocationState.value == "scheduled" && - runningCount(jobStatesSummary.value) === 0 && + runningCount.value === 0 && invocationAndJobTerminal.value ); }); + +type StepStateType = { [state: string]: number }; + +const stepStates = computed(() => { + const stepStates: StepStateType = {}; + const steps: InvocationStep[] = invocation.value?.steps || []; + for (const step of steps) { + if (!step) { + continue; + } + // the API defined state here allowing null and undefined is odd... + const stepState: string = step.state || "unknown"; + if (!stepStates[stepState]) { + stepStates[stepState] = 1; + } else { + stepStates[stepState] += 1; + } + } + return stepStates; +}); + +const stepCount = computed(() => { + return invocation.value?.steps.length || 0; +}); + +const stepStatesStr = computed(() => { + return `${stepStates.value?.scheduled || 0} of ${stepCount.value} steps successfully scheduled.`; +}); + +const okCount = computed(() => { + return jobStatesSummaryOkCount(jobStatesSummary.value); +}); + +const errorCount = computed(() => { + return jobStatesSummaryErrorCount(jobStatesSummary.value); +}); + +const runningCount = computed(() => { + return jobStatesSummaryRunningCount(jobStatesSummary.value); +}); + +const jobCount = computed(() => { + return jobStatesSummaryJobCount(jobStatesSummary.value); +}); + +const newCount = computed(() => { + return jobCount.value - okCount.value - runningCount.value - errorCount.value; +}); + +const jobStatesStr = computed(() => { + let jobStr = `${numTerminal(jobStatesSummary.value) || 0} of ${jobCount.value} jobs complete`; + if (!invocationSchedulingTerminal.value) { + jobStr += " (total number of jobs will change until all steps fully scheduled)"; + } + return `${jobStr}.`; +}); + const workflowStore = useWorkflowStore(); watch( @@ -171,16 +235,55 @@ function cancelWorkflowSchedulingLocal() { + + From 09346e68dbbeeaffa518164ed91349225296b332 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Mon, 4 Nov 2024 16:08:48 -0600 Subject: [PATCH 16/26] update workflow annotation header with the following changes: - No `in_panel` prop in route anymore - In `lib/galaxy/managers`, all we need is the `workflow.id` so on the front end, for the run form, we can call `useWorkflowInstance(workflow_id)` - Adjust props and variables in the workflow header to cater for all edge cases such as whether it is rendering for run form or invocation view, workflow is owned or not, is published or not etc. - Minor style improvements --- .../src/components/Common/ButtonSpinner.vue | 4 + .../Invocation/InvocationScrollList.vue | 4 +- .../Workflow/List/WorkflowIndicators.vue | 5 +- .../Workflow/Run/WorkflowRunFormSimple.vue | 176 ++++---------- .../Workflow/WorkflowAnnotation.vue | 86 +++++++ .../Workflow/WorkflowNavigationTitle.vue | 219 ++++++------------ .../components/Workflow/WorkflowRunButton.vue | 13 +- .../WorkflowInvocationHeader.test.ts | 11 +- .../WorkflowInvocationState.vue | 17 +- client/src/entry/analysis/router.js | 1 - client/src/stores/workflowStore.ts | 3 + lib/galaxy/managers/workflows.py | 27 +-- 12 files changed, 232 insertions(+), 334 deletions(-) diff --git a/client/src/components/Common/ButtonSpinner.vue b/client/src/components/Common/ButtonSpinner.vue index e2b8def400c1..f87f34b33991 100644 --- a/client/src/components/Common/ButtonSpinner.vue +++ b/client/src/components/Common/ButtonSpinner.vue @@ -11,12 +11,14 @@ interface Props { wait?: boolean; tooltip?: string; disabled?: boolean; + size?: string; } withDefaults(defineProps(), { wait: false, tooltip: undefined, disabled: false, + size: "md", }); @@ -25,6 +27,7 @@ withDefaults(defineProps(), { v-if="wait" v-b-tooltip.hover.bottom disabled + :size="size" variant="info" title="Please Wait..." class="d-flex flex-nowrap align-items-center text-nowrap"> @@ -38,6 +41,7 @@ withDefaults(defineProps(), { class="d-flex flex-nowrap align-items-center text-nowrap" :title="tooltip" :disabled="disabled" + :size="size" @click="$emit('onClick')"> {{ title }} diff --git a/client/src/components/Workflow/Invocation/InvocationScrollList.vue b/client/src/components/Workflow/Invocation/InvocationScrollList.vue index f42dd3b858ed..bb3dca3a12c6 100644 --- a/client/src/components/Workflow/Invocation/InvocationScrollList.vue +++ b/client/src/components/Workflow/Invocation/InvocationScrollList.vue @@ -96,12 +96,10 @@ const route = useRoute(); const router = useRouter(); function cardClicked(invocation: WorkflowInvocation) { - let path = `/workflows/invocations/${invocation.id}`; if (props.inPanel) { - path += "?from_panel=true"; emit("invocation-clicked"); } - router.push(path); + router.push(`/workflows/invocations/${invocation.id}`); } function scrollToTop() { diff --git a/client/src/components/Workflow/List/WorkflowIndicators.vue b/client/src/components/Workflow/List/WorkflowIndicators.vue index 92ccac96c4ae..18ace6687a4a 100644 --- a/client/src/components/Workflow/List/WorkflowIndicators.vue +++ b/client/src/components/Workflow/List/WorkflowIndicators.vue @@ -17,6 +17,7 @@ library.add(faFileImport, faGlobe, faShieldAlt, faUsers, faUser); interface Props { workflow: any; publishedView: boolean; + noEditTime?: boolean; } const props = defineProps(); @@ -118,7 +119,7 @@ function onViewUserPublished() { - + edited @@ -136,7 +137,7 @@ function onViewUserPublished() { -
-
-
-
- - Workflow: {{ model.name }} - (version: {{ model.runData.version + 1 }}) -
-
-
- - - - - Send results to a new history - - - Attempt to re-use jobs with identical parameters? - - - Send outputs and intermediate to different storage locations? - - - - - - - - - -
-
-
+ + + - - - - -
-
- - About This Workflow - - - - -
-
- - - - - {{ model.runData.annotation.annotation }} -
- -
-
- - - - + diff --git a/client/src/components/Workflow/WorkflowAnnotation.vue b/client/src/components/Workflow/WorkflowAnnotation.vue index e69de29bb2d1..5d43404745ab 100644 --- a/client/src/components/Workflow/WorkflowAnnotation.vue +++ b/client/src/components/Workflow/WorkflowAnnotation.vue @@ -0,0 +1,86 @@ + + + diff --git a/client/src/components/Workflow/WorkflowNavigationTitle.vue b/client/src/components/Workflow/WorkflowNavigationTitle.vue index f242881ea807..9560c5df0ec8 100644 --- a/client/src/components/Workflow/WorkflowNavigationTitle.vue +++ b/client/src/components/Workflow/WorkflowNavigationTitle.vue @@ -1,7 +1,6 @@ diff --git a/client/src/components/Workflow/WorkflowRunButton.vue b/client/src/components/Workflow/WorkflowRunButton.vue index 8d23de3c65c8..f5e26c981da9 100644 --- a/client/src/components/Workflow/WorkflowRunButton.vue +++ b/client/src/components/Workflow/WorkflowRunButton.vue @@ -1,12 +1,9 @@