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

Show flow numbers #2016

Merged
merged 3 commits into from
Jan 8, 2025
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
1 change: 1 addition & 0 deletions changes.d/2016.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show [flow numbers](https://cylc.github.io/cylc-doc/stable/html/glossary.html#term-flow) when applicable. Removed tasks (flow=None) are now dimmed.
79 changes: 79 additions & 0 deletions cypress/component/treeItem.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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/>.
*/

import TreeItem from '@/components/cylc/tree/TreeItem.vue'
import {
simpleCyclepointNode,
} from '$tests/unit/components/cylc/tree/tree.data'

// Get lists of: a) all tree item nodes; b) only visible ones.
Cypress.Commands.add('getNodeTypes', () => {
cy.get('.c-treeitem')
.then(($els) => {
const all = Array.from($els, ({ dataset }) => dataset.nodeType)
const visible = all.filter((val, i) => $els[i].checkVisibility())
return { all, visible }
})
})

// Expand/collapse the first node of this type.
Cypress.Commands.add('toggleNode', (nodeType) => {
cy.get(`[data-node-type=${nodeType}] .node-expand-collapse-button:first`).click()
})

describe('TreeItem component', () => {
it('expands/collapses children', () => {
cy.vmount(TreeItem, {
props: {
node: simpleCyclepointNode,
filteredOutNodesCache: new WeakMap(),
},
})
cy.addVuetifyStyles(cy)

cy.getNodeTypes()
.should('deep.equal', {
// Auto expand everything down to task nodes by default
all: ['cycle', 'task'],
visible: ['cycle', 'task']
})

cy.toggleNode('task')
cy.getNodeTypes()
.should('deep.equal', {
all: ['cycle', 'task', 'job'],
visible: ['cycle', 'task', 'job']
})

cy.toggleNode('cycle')
cy.getNodeTypes()
.should('deep.equal', {
// All previously expanded nodes under cycle should be hidden but remain rendered
all: ['cycle', 'task', 'job'],
visible: ['cycle']
})

cy.toggleNode('cycle')
cy.toggleNode('job')
cy.getNodeTypes()
.should('deep.equal', {
// Job node does not use a child TreeItem
all: ['cycle', 'task', 'job'],
visible: ['cycle', 'task', 'job']
})
})
})
13 changes: 9 additions & 4 deletions src/components/cylc/commandMenu/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ import { mapGetters, mapState } from 'vuex'
import WorkflowState from '@/model/WorkflowState.model'
import { eventBus } from '@/services/eventBus'
import CopyBtn from '@/components/core/CopyBtn.vue'
import { upperFirst } from 'lodash-es'
import { formatFlowNums } from '@/utils/tasks'

export default {
name: 'CommandMenu',
Expand Down Expand Up @@ -199,14 +201,14 @@ export default {
// can happen briefly when switching workflows
return
}
let ret = this.node.type
let ret = upperFirst(this.node.type)
if (this.node.type !== 'cycle') {
// NOTE: cycle point nodes don't have associated node data at present
ret += ' - '
ret += ' '
if (this.node.type === 'workflow') {
ret += this.node.node.statusMsg || this.node.node.status || 'state unknown'
ret += upperFirst(this.node.node.statusMsg || this.node.node.status || 'state unknown')
} else {
ret += this.node.node.state || 'state unknown'
ret += upperFirst(this.node.node.state || 'state unknown')
if (this.node.node.isHeld) {
ret += ' (held)'
}
Expand All @@ -216,6 +218,9 @@ export default {
if (this.node.node.isRunahead) {
ret += ' (runahead)'
}
if (this.node.node.flowNums) {
ret += ` • Flows: ${formatFlowNums(this.node.node.flowNums)}`
}
}
}
return ret
Expand Down
56 changes: 56 additions & 0 deletions src/components/cylc/common/FlowNumsChip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!--
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/>.
-->

<template>
<v-chip
v-if="showFlowNums"
label
density="compact"
size="small"
class="ml-1 cursor-default"
:prepend-icon="mdiLabelOutline"
data-cy="flow-num-chip"
>
{{ flowNumsStr }}
<v-tooltip location="right">
Flows: {{ flowNumsStr }}
</v-tooltip>
</v-chip>
</template>

<script setup>
import { formatFlowNums } from '@/utils/tasks'
import { mdiLabelOutline } from '@mdi/js'
import { computed } from 'vue'

const props = defineProps({
flowNums: {
type: String,
required: false,
},
})

const flowNumsStr = computed(
() => props.flowNums && formatFlowNums(props.flowNums)
)

/** Hide flow=1 and flow=None by default */
const showFlowNums = computed(
() => flowNumsStr.value && !['1', 'None'].includes(flowNumsStr.value)
)

</script>
20 changes: 17 additions & 3 deletions src/components/cylc/table/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model:items-per-page="itemsPerPage"
>
<template v-slot:item.task.name="{ item }">
<div class="d-flex align-content-center flex-nowrap">
<div
class="d-flex align-center flex-nowrap"
:class="{ 'flow-none': isFlowNone(item.task.node.flowNums) }"
:data-cy-task-name="item.task.name"
>
<div style="width: 2em;">
<Task
v-command-menu="item.task"
Expand All @@ -44,7 +48,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:previous-state="item.previousJob?.node?.state"
/>
</div>
<div>{{ item.task.name }}</div>
{{ item.task.name }}
<FlowNumsChip
:flowNums="item.task.node.flowNums"
class="ml-2"
/>
</div>
</template>
<template v-slot:item.task.node.task.meanElapsedTime="{ item }">
Expand Down Expand Up @@ -110,13 +118,17 @@ import {
datetimeComparator,
numberComparator,
} from '@/components/cylc/table/sort'
import { dtMean } from '@/utils/tasks'
import {
dtMean,
isFlowNone,
} from '@/utils/tasks'
import { useCyclePointsOrderDesc } from '@/composables/localStorage'
import {
initialOptions,
updateInitialOptionsEvent,
useInitialOptions
} from '@/utils/initialOptions'
import FlowNumsChip from '@/components/cylc/common/FlowNumsChip.vue'

export default {
name: 'TableComponent',
Expand All @@ -132,6 +144,7 @@ export default {
},

components: {
FlowNumsChip,
Task,
Job,
},
Expand Down Expand Up @@ -243,6 +256,7 @@ export default {
icons: {
mdiChevronDown
},
isFlowNone,
itemsPerPageOptions: [
{ value: 10, title: '10' },
{ value: 20, title: '20' },
Expand Down
61 changes: 32 additions & 29 deletions src/components/cylc/tree/TreeItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div
v-show="!filteredOutNodesCache.get(node)"
class="c-treeitem"
:data-node-type="node.type"
:data-node-name="node.name"
>
<div
class="node d-flex align-center"
Expand All @@ -30,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-if="renderExpandCollapseBtn"
aria-label="Expand/collapse"
class="node-expand-collapse-button flex-shrink-0"
@click="toggleExpandCollapse"
@click="toggleExpandCollapse()"
:style="expandCollapseBtnStyle"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
Expand Down Expand Up @@ -63,7 +65,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<span class="mx-1">{{ node.name }}</span>
</template>
<template v-else-if="node.type === 'task'">
<div
v-else-if="node.type === 'task'"
class="d-flex align-center"
:class="{ 'flow-none': isFlowNone(node.node.flowNums) }"
>
<!-- Task summary -->
<Task
v-command-menu="node"
Expand All @@ -85,7 +91,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</div>
<span class="mx-1">{{ node.name }}</span>
</template>
<FlowNumsChip :flowNums="node.node.flowNums"/>
</div>
<template v-else-if="node.type === 'job'">
<Job
v-command-menu="node"
Expand Down Expand Up @@ -134,7 +141,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class="ml-2 bg-grey text-white"
size="small"
link
@click="toggleExpandCollapse"
@click="toggleExpandCollapse()"
>
+{{ jobMessageOutputs.length - 5 }}
</v-chip>
Expand Down Expand Up @@ -175,20 +182,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>

<script>
import { mdiChevronRight } from '@mdi/js'
import {
mdiChevronRight,
} from '@mdi/js'
import Task from '@/components/cylc/Task.vue'
import Job from '@/components/cylc/Job.vue'
import JobDetails from '@/components/cylc/tree/JobDetails.vue'
import {
jobMessageOutputs,
latestJob,
jobMessageOutputs
isFlowNone,
} from '@/utils/tasks'
import { getIndent, getNodeChildren } from '@/components/cylc/tree/util'
import { once } from '@/utils'
import { useToggle } from '@vueuse/core'
import FlowNumsChip from '@/components/cylc/common/FlowNumsChip.vue'

export default {
name: 'TreeItem',

components: {
FlowNumsChip,
Task,
Job,
JobDetails,
Expand Down Expand Up @@ -240,27 +254,23 @@ export default {
},
},

data () {
setup (props) {
const [isExpanded, toggleExpandCollapse] = useToggle(
props.autoExpandTypes.includes(props.node.type)
)
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
const renderChildren = once(isExpanded)

return {
manuallyExpanded: null,
isExpanded,
isFlowNone,
latestJob,
renderChildren,
toggleExpandCollapse,
}
},

computed: {
isExpanded: {
get () {
return this.manuallyExpanded ?? this.autoExpandTypes.includes(this.node.type)
},
set (value) {
this.manuallyExpanded = value
}
},

renderChildren () {
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
return this.renderChildren || this.isExpanded
},

hasChildren () {
return (
// "job" nodes have auto-generated "job-detail" nodes
Expand Down Expand Up @@ -310,13 +320,6 @@ export default {
}
},

methods: {
toggleExpandCollapse () {
this.isExpanded = !this.isExpanded
},
latestJob
},

icons: {
mdiChevronRight,
},
Expand Down
1 change: 1 addition & 0 deletions src/services/mock/checkpoint/get_checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
meanElapsedTime
name
}
flowNums
}

fragment JobData on Job {
Expand Down
2 changes: 2 additions & 0 deletions src/services/mock/json/workflows/multi.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"isQueued": true,
"isRunahead": false,
"name": "foo",
"flowNums": "[1]",
"task": {
"meanElapsedTime": 0,
"__typename": "Task"
Expand All @@ -162,6 +163,7 @@
"isQueued": false,
"isRunahead": false,
"name": "foo",
"flowNums": "[1]",
"task": {
"meanElapsedTime": 0,
"__typename": "Task"
Expand Down
Loading
Loading