Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async scan updates for the UI #493

Merged
merged 3 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 40 additions & 15 deletions src/components/cylc/gscan/GScan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
<div>
<div v-if="workflows && workflows.length > 0">
<div v-for="workflow in workflows" :key="workflow.id">
<v-list-item :to="`/workflows/${ workflow.name }`">
<div
class="c-gscan"
>
<div
v-if="workflows && workflows.length > 0"
class="c-gscan-workflows"
>
<div
v-for="workflow in workflows"
:key="workflow.id"
class="c-gscan-workflow"
>
<v-list-item
:to="`/workflows/${ workflow.name }`"
:class="getWorkflowClass(workflow.status)"
>
<v-list-item-action>
<v-icon>{{ getWorkflowIcon(workflow.status) }}</v-icon>
</v-list-item-action>
Expand All @@ -37,15 +49,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- a v-tooltip does not work directly set on Cylc job component, so we use a dummy button to wrap it -->
<!-- NB: most of the classes/directives in these button are applied so that the user does not notice it is a button -->
<v-btn
v-on="on"
class="mt-1 pa-0"
min-width="0"
min-height="0"
style="font-size: 120%"
:ripple="false"
small
dark
text>
v-on="on"
class="mt-1 pa-0"
min-width="0"
min-height="0"
style="font-size: 120%"
:ripple="false"
small
dark
text
>
<job :status="state" />
</v-btn>
</template>
Expand Down Expand Up @@ -92,6 +105,7 @@ export default {
props: {
/**
* Vanilla workflows object from GraphQL query
* @type {{}|null}
*/
workflows: {
type: Array,
Expand All @@ -113,8 +127,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
}
Expand Down Expand Up @@ -175,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'
}
}
}
}
Expand Down
31 changes: 17 additions & 14 deletions src/components/cylc/gscan/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/cylc/tree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions src/styles/cylc/_gscan.scss
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

.c-gscan {
.c-gscan-workflows {
.c-gscan-workflow {
a.c-workflow-stopped {
opacity: 0.5;
}
}
}
}
1 change: 1 addition & 0 deletions src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/components/cylc/tree/deltas.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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()
})
})
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/components/cylc/tree/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})