Skip to content

Commit e263a14

Browse files
authored
Merge pull request #2016 from cylc/flow-nums
Show flow numbers
2 parents b6c2d08 + b5d310f commit e263a14

File tree

26 files changed

+476
-134
lines changed

26 files changed

+476
-134
lines changed

changes.d/2016.feat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

cypress/component/treeItem.cy.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import TreeItem from '@/components/cylc/tree/TreeItem.vue'
19+
import {
20+
simpleCyclepointNode,
21+
} from '$tests/unit/components/cylc/tree/tree.data'
22+
23+
// Get lists of: a) all tree item nodes; b) only visible ones.
24+
Cypress.Commands.add('getNodeTypes', () => {
25+
cy.get('.c-treeitem')
26+
.then(($els) => {
27+
const all = Array.from($els, ({ dataset }) => dataset.nodeType)
28+
const visible = all.filter((val, i) => $els[i].checkVisibility())
29+
return { all, visible }
30+
})
31+
})
32+
33+
// Expand/collapse the first node of this type.
34+
Cypress.Commands.add('toggleNode', (nodeType) => {
35+
cy.get(`[data-node-type=${nodeType}] .node-expand-collapse-button:first`).click()
36+
})
37+
38+
describe('TreeItem component', () => {
39+
it('expands/collapses children', () => {
40+
cy.vmount(TreeItem, {
41+
props: {
42+
node: simpleCyclepointNode,
43+
filteredOutNodesCache: new WeakMap(),
44+
},
45+
})
46+
cy.addVuetifyStyles(cy)
47+
48+
cy.getNodeTypes()
49+
.should('deep.equal', {
50+
// Auto expand everything down to task nodes by default
51+
all: ['cycle', 'task'],
52+
visible: ['cycle', 'task']
53+
})
54+
55+
cy.toggleNode('task')
56+
cy.getNodeTypes()
57+
.should('deep.equal', {
58+
all: ['cycle', 'task', 'job'],
59+
visible: ['cycle', 'task', 'job']
60+
})
61+
62+
cy.toggleNode('cycle')
63+
cy.getNodeTypes()
64+
.should('deep.equal', {
65+
// All previously expanded nodes under cycle should be hidden but remain rendered
66+
all: ['cycle', 'task', 'job'],
67+
visible: ['cycle']
68+
})
69+
70+
cy.toggleNode('cycle')
71+
cy.toggleNode('job')
72+
cy.getNodeTypes()
73+
.should('deep.equal', {
74+
// Job node does not use a child TreeItem
75+
all: ['cycle', 'task', 'job'],
76+
visible: ['cycle', 'task', 'job']
77+
})
78+
})
79+
})

src/components/cylc/commandMenu/Menu.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ import { mapGetters, mapState } from 'vuex'
126126
import WorkflowState from '@/model/WorkflowState.model'
127127
import { eventBus } from '@/services/eventBus'
128128
import CopyBtn from '@/components/core/CopyBtn.vue'
129+
import { upperFirst } from 'lodash-es'
130+
import { formatFlowNums } from '@/utils/tasks'
129131
130132
export default {
131133
name: 'CommandMenu',
@@ -199,14 +201,14 @@ export default {
199201
// can happen briefly when switching workflows
200202
return
201203
}
202-
let ret = this.node.type
204+
let ret = upperFirst(this.node.type)
203205
if (this.node.type !== 'cycle') {
204206
// NOTE: cycle point nodes don't have associated node data at present
205-
ret += ' - '
207+
ret += ' '
206208
if (this.node.type === 'workflow') {
207-
ret += this.node.node.statusMsg || this.node.node.status || 'state unknown'
209+
ret += upperFirst(this.node.node.statusMsg || this.node.node.status || 'state unknown')
208210
} else {
209-
ret += this.node.node.state || 'state unknown'
211+
ret += upperFirst(this.node.node.state || 'state unknown')
210212
if (this.node.node.isHeld) {
211213
ret += ' (held)'
212214
}
@@ -216,6 +218,9 @@ export default {
216218
if (this.node.node.isRunahead) {
217219
ret += ' (runahead)'
218220
}
221+
if (this.node.node.flowNums) {
222+
ret += ` • Flows: ${formatFlowNums(this.node.node.flowNums)}`
223+
}
219224
}
220225
}
221226
return ret
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<!--
2+
Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
-->
17+
18+
<template>
19+
<v-chip
20+
v-if="showFlowNums"
21+
label
22+
density="compact"
23+
size="small"
24+
class="ml-1 cursor-default"
25+
:prepend-icon="mdiLabelOutline"
26+
data-cy="flow-num-chip"
27+
>
28+
{{ flowNumsStr }}
29+
<v-tooltip location="right">
30+
Flows: {{ flowNumsStr }}
31+
</v-tooltip>
32+
</v-chip>
33+
</template>
34+
35+
<script setup>
36+
import { formatFlowNums } from '@/utils/tasks'
37+
import { mdiLabelOutline } from '@mdi/js'
38+
import { computed } from 'vue'
39+
40+
const props = defineProps({
41+
flowNums: {
42+
type: String,
43+
required: false,
44+
},
45+
})
46+
47+
const flowNumsStr = computed(
48+
() => props.flowNums && formatFlowNums(props.flowNums)
49+
)
50+
51+
/** Hide flow=1 and flow=None by default */
52+
const showFlowNums = computed(
53+
() => flowNumsStr.value && !['1', 'None'].includes(flowNumsStr.value)
54+
)
55+
56+
</script>

src/components/cylc/table/Table.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2828
v-model:items-per-page="itemsPerPage"
2929
>
3030
<template v-slot:item.task.name="{ item }">
31-
<div class="d-flex align-content-center flex-nowrap">
31+
<div
32+
class="d-flex align-center flex-nowrap"
33+
:class="{ 'flow-none': isFlowNone(item.task.node.flowNums) }"
34+
:data-cy-task-name="item.task.name"
35+
>
3236
<div style="width: 2em;">
3337
<Task
3438
v-command-menu="item.task"
@@ -44,7 +48,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
4448
:previous-state="item.previousJob?.node?.state"
4549
/>
4650
</div>
47-
<div>{{ item.task.name }}</div>
51+
{{ item.task.name }}
52+
<FlowNumsChip
53+
:flowNums="item.task.node.flowNums"
54+
class="ml-2"
55+
/>
4856
</div>
4957
</template>
5058
<template v-slot:item.task.node.task.meanElapsedTime="{ item }">
@@ -110,13 +118,17 @@ import {
110118
datetimeComparator,
111119
numberComparator,
112120
} from '@/components/cylc/table/sort'
113-
import { dtMean } from '@/utils/tasks'
121+
import {
122+
dtMean,
123+
isFlowNone,
124+
} from '@/utils/tasks'
114125
import { useCyclePointsOrderDesc } from '@/composables/localStorage'
115126
import {
116127
initialOptions,
117128
updateInitialOptionsEvent,
118129
useInitialOptions
119130
} from '@/utils/initialOptions'
131+
import FlowNumsChip from '@/components/cylc/common/FlowNumsChip.vue'
120132
121133
export default {
122134
name: 'TableComponent',
@@ -132,6 +144,7 @@ export default {
132144
},
133145
134146
components: {
147+
FlowNumsChip,
135148
Task,
136149
Job,
137150
},
@@ -243,6 +256,7 @@ export default {
243256
icons: {
244257
mdiChevronDown
245258
},
259+
isFlowNone,
246260
itemsPerPageOptions: [
247261
{ value: 10, title: '10' },
248262
{ value: 20, title: '20' },

src/components/cylc/tree/TreeItem.vue

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
<div
2020
v-show="!filteredOutNodesCache.get(node)"
2121
class="c-treeitem"
22+
:data-node-type="node.type"
23+
:data-node-name="node.name"
2224
>
2325
<div
2426
class="node d-flex align-center"
@@ -30,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
3032
v-if="renderExpandCollapseBtn"
3133
aria-label="Expand/collapse"
3234
class="node-expand-collapse-button flex-shrink-0"
33-
@click="toggleExpandCollapse"
35+
@click="toggleExpandCollapse()"
3436
:style="expandCollapseBtnStyle"
3537
xmlns="http://www.w3.org/2000/svg"
3638
viewBox="0 0 24 24"
@@ -63,7 +65,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6365
/>
6466
<span class="mx-1">{{ node.name }}</span>
6567
</template>
66-
<template v-else-if="node.type === 'task'">
68+
<div
69+
v-else-if="node.type === 'task'"
70+
class="d-flex align-center"
71+
:class="{ 'flow-none': isFlowNone(node.node.flowNums) }"
72+
>
6773
<!-- Task summary -->
6874
<Task
6975
v-command-menu="node"
@@ -85,7 +91,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8591
/>
8692
</div>
8793
<span class="mx-1">{{ node.name }}</span>
88-
</template>
94+
<FlowNumsChip :flowNums="node.node.flowNums"/>
95+
</div>
8996
<template v-else-if="node.type === 'job'">
9097
<Job
9198
v-command-menu="node"
@@ -134,7 +141,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
134141
class="ml-2 bg-grey text-white"
135142
size="small"
136143
link
137-
@click="toggleExpandCollapse"
144+
@click="toggleExpandCollapse()"
138145
>
139146
+{{ jobMessageOutputs.length - 5 }}
140147
</v-chip>
@@ -175,20 +182,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
175182
</template>
176183

177184
<script>
178-
import { mdiChevronRight } from '@mdi/js'
185+
import {
186+
mdiChevronRight,
187+
} from '@mdi/js'
179188
import Task from '@/components/cylc/Task.vue'
180189
import Job from '@/components/cylc/Job.vue'
181190
import JobDetails from '@/components/cylc/tree/JobDetails.vue'
182191
import {
192+
jobMessageOutputs,
183193
latestJob,
184-
jobMessageOutputs
194+
isFlowNone,
185195
} from '@/utils/tasks'
186196
import { getIndent, getNodeChildren } from '@/components/cylc/tree/util'
197+
import { once } from '@/utils'
198+
import { useToggle } from '@vueuse/core'
199+
import FlowNumsChip from '@/components/cylc/common/FlowNumsChip.vue'
187200
188201
export default {
189202
name: 'TreeItem',
190203
191204
components: {
205+
FlowNumsChip,
192206
Task,
193207
Job,
194208
JobDetails,
@@ -240,27 +254,23 @@ export default {
240254
},
241255
},
242256
243-
data () {
257+
setup (props) {
258+
const [isExpanded, toggleExpandCollapse] = useToggle(
259+
props.autoExpandTypes.includes(props.node.type)
260+
)
261+
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
262+
const renderChildren = once(isExpanded)
263+
244264
return {
245-
manuallyExpanded: null,
265+
isExpanded,
266+
isFlowNone,
267+
latestJob,
268+
renderChildren,
269+
toggleExpandCollapse,
246270
}
247271
},
248272
249273
computed: {
250-
isExpanded: {
251-
get () {
252-
return this.manuallyExpanded ?? this.autoExpandTypes.includes(this.node.type)
253-
},
254-
set (value) {
255-
this.manuallyExpanded = value
256-
}
257-
},
258-
259-
renderChildren () {
260-
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
261-
return this.renderChildren || this.isExpanded
262-
},
263-
264274
hasChildren () {
265275
return (
266276
// "job" nodes have auto-generated "job-detail" nodes
@@ -310,13 +320,6 @@ export default {
310320
}
311321
},
312322
313-
methods: {
314-
toggleExpandCollapse () {
315-
this.isExpanded = !this.isExpanded
316-
},
317-
latestJob
318-
},
319-
320323
icons: {
321324
mdiChevronRight,
322325
},

src/services/mock/checkpoint/get_checkpoint.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
meanElapsedTime
8383
name
8484
}
85+
flowNums
8586
}
8687
8788
fragment JobData on Job {

src/services/mock/json/workflows/multi.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
"isQueued": true,
146146
"isRunahead": false,
147147
"name": "foo",
148+
"flowNums": "[1]",
148149
"task": {
149150
"meanElapsedTime": 0,
150151
"__typename": "Task"
@@ -162,6 +163,7 @@
162163
"isQueued": false,
163164
"isRunahead": false,
164165
"name": "foo",
166+
"flowNums": "[1]",
165167
"task": {
166168
"meanElapsedTime": 0,
167169
"__typename": "Task"

0 commit comments

Comments
 (0)