From fecb3078b813a4e4a483ba3378f6211ac63fe0ec Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Wed, 9 Sep 2020 13:59:17 +1200 Subject: [PATCH 1/3] Async Scan updates for the UI --- src/components/cylc/gscan/GScan.vue | 9 +++++++-- src/components/cylc/gscan/index.js | 31 ++++++++++++++++------------- src/components/cylc/tree/index.js | 5 ++++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/components/cylc/gscan/GScan.vue b/src/components/cylc/gscan/GScan.vue index 1bcbe056d..0bc6f5b0c 100644 --- a/src/components/cylc/gscan/GScan.vue +++ b/src/components/cylc/gscan/GScan.vue @@ -92,6 +92,7 @@ export default { props: { /** * Vanilla workflows object from GraphQL query + * @type {{}|null} */ workflows: { type: Array, @@ -113,8 +114,12 @@ export default { */ workflowsSummaries () { const workflowSummaries = new Map() - for (const workflow of this.workflows) { - workflowSummaries.set(workflow.name, getWorkflowSummary(workflow)) + // with async scan, the workflows list may be null or undefined + // see cylc-uiserver PR#150 + if (this.workflows) { + for (const workflow of this.workflows) { + workflowSummaries.set(workflow.name, getWorkflowSummary(workflow)) + } } return workflowSummaries } diff --git a/src/components/cylc/gscan/index.js b/src/components/cylc/gscan/index.js index a4201152a..fbadfdb0a 100644 --- a/src/components/cylc/gscan/index.js +++ b/src/components/cylc/gscan/index.js @@ -25,23 +25,26 @@ */ function getWorkflowSummary (workflow) { const states = new Map() - for (const taskProxy of workflow.taskProxies) { - // a task in waiting, may not have any jobs - if (taskProxy.jobs) { - for (const job of taskProxy.jobs) { - // TODO: temporary fix, as the backend is sending ready jobs, but they will change in cylc flow&uiserver in the future - if (job.state === 'ready') { - continue + // a stopped workflow, may not have any tasks + if (workflow.taskProxies) { + for (const taskProxy of workflow.taskProxies) { + // a task in waiting, may not have any jobs + if (taskProxy.jobs) { + for (const job of taskProxy.jobs) { + // TODO: temporary fix, as the backend is sending ready jobs, but they will change in cylc flow&uiserver in the future + if (job.state === 'ready') { + continue + } + if (!states.has(job.state)) { + states.set(job.state, new Set()) + } + states.get(job.state).add(`${taskProxy.name}.${taskProxy.cyclePoint}`) } - if (!states.has(job.state)) { - states.set(job.state, new Set()) - } - states.get(job.state).add(`${taskProxy.name}.${taskProxy.cyclePoint}`) } } - } - for (const [stateName, tasksSet] of states.entries()) { - states.set(stateName, [...tasksSet].sort()) + for (const [stateName, tasksSet] of states.entries()) { + states.set(stateName, [...tasksSet].sort()) + } } return new Map([...states.entries()].sort()) } diff --git a/src/components/cylc/tree/index.js b/src/components/cylc/tree/index.js index d8f3648b1..e5a34bd25 100644 --- a/src/components/cylc/tree/index.js +++ b/src/components/cylc/tree/index.js @@ -132,7 +132,10 @@ function containsTreeData (workflow) { */ function populateTreeFromGraphQLData (tree, workflow) { if (!tree || !workflow || !containsTreeData(workflow)) { - throw new Error('You must provide valid data to populate the tree!') + // throw new Error('You must provide valid data to populate the tree!') + // a stopped workflow is valid, but won't have anything that we can use + // to populate the tree, only workflow data and empty families + return } // the workflow object gets augmented to become a valid node for the tree const rootNode = createWorkflowNode(workflow) From e1493c4a17484170a58ea36526b3bc0866e8d784 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 14 Sep 2020 10:48:34 +1200 Subject: [PATCH 2/3] Make stopped workflows opaque (0.5) in GScan --- src/components/cylc/gscan/GScan.vue | 46 +++++++++++++++++++++-------- src/styles/cylc/_gscan.scss | 26 ++++++++++++++++ src/styles/index.scss | 1 + 3 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 src/styles/cylc/_gscan.scss diff --git a/src/components/cylc/gscan/GScan.vue b/src/components/cylc/gscan/GScan.vue index 0bc6f5b0c..dafcefb4b 100644 --- a/src/components/cylc/gscan/GScan.vue +++ b/src/components/cylc/gscan/GScan.vue @@ -16,10 +16,22 @@ along with this program. If not, see . --> @@ -180,6 +193,13 @@ export default { default: return 'mdi-help-circle' } + }, + + getWorkflowClass (status) { + return { + // TODO: replace by constant or enum later (not TaskState as that doesn't have stopped, maybe WorkflowState?) + 'c-workflow-stopped': status === 'stopped' + } } } } diff --git a/src/styles/cylc/_gscan.scss b/src/styles/cylc/_gscan.scss new file mode 100644 index 000000000..eb75e8300 --- /dev/null +++ b/src/styles/cylc/_gscan.scss @@ -0,0 +1,26 @@ +/** + * Copyright (C) NIWA & British Crown (Met Office) & Contributors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.c-gscan { + .c-gscan-workflows { + .c-gscan-workflow { + a.c-workflow-stopped { + opacity: 0.5; + } + } + } +} diff --git a/src/styles/index.scss b/src/styles/index.scss index 3af35f347..45f949188 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -21,6 +21,7 @@ @import "cylc/header"; @import "cylc/toolbar"; @import "cylc/drawer"; +@import "cylc/gscan"; @import "cylc/job"; @import "cylc/task"; @import "cylc/dashboard"; From 812f7721773b4886451935991638e9bd493f8542 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Tue, 15 Sep 2020 10:04:26 +1200 Subject: [PATCH 3/3] Update unit tests to support stopped workflows (i.e. do not throw errors when a workflow does not have tree data) --- tests/unit/components/cylc/tree/deltas.spec.js | 6 +++--- tests/unit/components/cylc/tree/index.spec.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/components/cylc/tree/deltas.spec.js b/tests/unit/components/cylc/tree/deltas.spec.js index 223d91591..b928c01fa 100644 --- a/tests/unit/components/cylc/tree/deltas.spec.js +++ b/tests/unit/components/cylc/tree/deltas.spec.js @@ -108,7 +108,7 @@ describe('Deltas', () => { expect(cylcTree.root.id).to.equal(WORKFLOW_ID) expect(fakeTree.tallyCyclePointStates.called).to.equal(true) }) - it('Should log to console and throw an error if it fails to handle the initial data burst', () => { + it('Should not log to console nor throw an error if it fails to handle the initial data burst', () => { const sandbox = sinon.createSandbox() sandbox.stub(console, 'error') const deltasWithInitialDataBurst = { @@ -123,8 +123,8 @@ describe('Deltas', () => { } } } - expect(() => applyDeltas(deltasWithInitialDataBurst, cylcTree)).to.throw(Error) - expect(console.error.calledOnce).to.equal(true) + expect(() => applyDeltas(deltasWithInitialDataBurst, cylcTree)).to.not.throw(Error) + expect(console.error.calledOnce).to.equal(false) sandbox.restore() }) }) diff --git a/tests/unit/components/cylc/tree/index.spec.js b/tests/unit/components/cylc/tree/index.spec.js index 90b0aeb98..cfcd8955f 100644 --- a/tests/unit/components/cylc/tree/index.spec.js +++ b/tests/unit/components/cylc/tree/index.spec.js @@ -120,11 +120,11 @@ describe('Tree component functions', () => { const taskProxyNode = createTaskProxyNode(taskProxy) expect(taskProxyNode.node.state).to.equal('') }) - it('should throw an error when the workflow to be populated is invalid', () => { + it('should not throw an error when the workflow to be populated is invalid', () => { const tree = {} const workflow = {} - expect(populateTreeFromGraphQLData, null, workflow).to.throw(Error) - expect(populateTreeFromGraphQLData, tree, null).to.throw(Error) - expect(populateTreeFromGraphQLData, tree, workflow).to.throw(Error) + expect(populateTreeFromGraphQLData, null, workflow).to.not.throw(Error) + expect(populateTreeFromGraphQLData, tree, null).to.not.throw(Error) + expect(populateTreeFromGraphQLData, tree, workflow).to.not.throw(Error) }) })