@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818<template >
1919 <TreeItem
2020 v-bind =" { node, depth, filteredOutNodesCache, hoverable }"
21- :auto-expand-types =" $options. nodeTypes"
21+ :auto-expand-types =" nodeTypes"
2222 :render-expand-collapse-btn =" node.type !== 'workflow'"
2323 ref =" treeItem"
2424 >
@@ -47,16 +47,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
4747 </span >
4848 </div >
4949 <div class =" d-flex c-gscan-workflow-states flex-grow-0" >
50- <template
51- v-for =" state in Object .keys (descendantTaskInfo .latestTasks )"
52- :key =" ` ${node .id }-${state } ` "
53- >
54- <TaskStateBadge
55- v-if =" descendantTaskInfo.stateTotals[state]"
56- :state =" state"
57- :value =" descendantTaskInfo.stateTotals[state]"
58- />
59- </template >
50+ <TaskStateBadge
51+ v-for =" (value, state) in statesInfo.stateTotals"
52+ :key =" state"
53+ v-bind =" { state, value }"
54+ :latest-tasks =" statesInfo.latestTasks[state]"
55+ />
6056 <WarningIcon
6157 v-if =" workflowWarnings"
6258 :workflow =" node"
@@ -80,117 +76,97 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8076 </TreeItem >
8177</template >
8278
83- <script >
79+ <script setup>
80+ import { computed } from ' vue'
8481import TaskStateBadge from ' @/components/cylc/TaskStateBadge.vue'
8582import WorkflowIcon from ' @/components/cylc/gscan/WorkflowIcon.vue'
8683import TreeItem from ' @/components/cylc/tree/TreeItem.vue'
8784import WarningIcon from ' @/components/cylc/WarningIcon.vue'
88- import { JobStateNames } from ' @/model/JobState .model'
85+ import TaskState from ' @/model/TaskState .model'
8986import { WorkflowState } from ' @/model/WorkflowState.model'
9087import { useWorkflowWarnings } from ' @/composables/localStorage'
9188
89+ const nodeTypes = [' workflow-part' , ' workflow' ]
90+
91+ /** Display order in sidebar */
92+ const taskStatesOrdered = [
93+ TaskState .FAILED .name ,
94+ TaskState .SUBMIT_FAILED .name ,
95+ TaskState .SUBMITTED .name ,
96+ TaskState .RUNNING .name ,
97+ ]
98+
9299/**
93- * Get aggregated task state totals and latest task states for all descendents of a node.
100+ * Get aggregated task state totals for all descendents of a node.
94101 *
95102 * @param {Object} node
96- * @param {Record<string, number>} stateTotals
97- * @param {Record<string, string[]>} latestTasks
98- * @param {Boolean} topLevel - true if the traversal depth is 0, else false.
103+ * @param {Record<string, number>} stateTotals - Accumulator for state totals.
104+ * @param {Record<string, string[]>} latestTasks - Accumulator for latest tasks.
99105 */
100- function traverseChildren (node , stateTotals = {}, latestTasks = {}, topLevel = true ) {
106+ function getStatesInfo (node , stateTotals = {}, latestTasks = {}) {
101107 // if we aren't at the end of the node tree, continue recurse until we hit something other then a workflow part
102108 if (node .type === ' workflow-part' && node .children ) {
103- // at every branch, recurse all child nodes
109+ // at every branch, recurse all child nodes except stopped workflows
104110 for (const child of node .children ) {
105- traverseChildren (child, stateTotals, latestTasks, false )
111+ if (child .node .status !== WorkflowState .STOPPED .name ) {
112+ getStatesInfo (child, stateTotals, latestTasks)
113+ }
106114 }
107115 } else if (node .type === ' workflow' && node .node .stateTotals ) {
108- // if we are at the end of a node (or at least, hit a workflow node), stop and merge state
109-
110- // the latest state tasks from this node with all the others from the tree
111- for (const [state , totals ] of Object .entries (node .node .stateTotals )) {
112- if (
113- // filter only valid states
114- JobStateNames .includes (state) &&
115- // omit state totals from stopped workflows
116- (topLevel || node .node .status !== ' stopped' )
117- ) {
118- // (cast as numbers so they dont get concatenated as strings)
119- stateTotals[state] = (stateTotals[state] ?? 0 ) + parseInt (totals)
116+ // if we hit a workflow node, stop and merge state
117+
118+ // the non-zero state totals from this node with all the others from the tree
119+ for (const state of taskStatesOrdered) {
120+ const nodeTotal = node .node .stateTotals [state]
121+ if (nodeTotal) {
122+ stateTotals[state] = (stateTotals[state] ?? 0 ) + nodeTotal
120123 }
121- }
122- for (const [state , taskNames ] of Object .entries (node .node .latestStateTasks )) {
123- if (JobStateNames .includes (state)) {
124- // concat the new tasks in where they don't already exist
124+ const nodeLatestTasks = node .node .latestStateTasks ? .[state]
125+ if (nodeLatestTasks? .length ) {
125126 latestTasks[state] = [
126127 ... (latestTasks[state] ?? []),
127- ... taskNames ,
128+ ... nodeLatestTasks ,
128129 ].sort ().reverse () // cycle point descending order
129130 }
130131 }
131132 }
132133 return { stateTotals, latestTasks }
133134}
134135
135- export default {
136- name: ' GScanTreeItem' ,
136+ const workflowWarnings = useWorkflowWarnings ()
137137
138- components: {
139- TaskStateBadge,
140- TreeItem,
141- WarningIcon,
142- WorkflowIcon,
138+ const props = defineProps ({
139+ node: {
140+ type: Object ,
141+ required: true
143142 },
144-
145- data : () => ({
146- workflowWarnings: useWorkflowWarnings ()
147- }),
148-
149- props: {
150- node: {
151- type: Object ,
152- required: true
153- },
154- depth: {
155- type: Number ,
156- default: 0
157- },
158- filteredOutNodesCache: {
159- type: WeakMap ,
160- required: true ,
161- },
162- hoverable: {
163- type: Boolean ,
164- },
143+ depth: {
144+ type: Number ,
145+ default: 0
165146 },
166-
167- computed: {
168- workflowLink () {
169- return this .node .type === ' workflow'
170- ? ` /workspace/${ this .node .tokens .workflow } `
171- : ' '
172- },
173-
174- /** Task state totals and latest states for all descendents of this node. */
175- descendantTaskInfo () {
176- return traverseChildren (this .node )
177- },
178-
179- nodeChildren () {
180- return this .node .type === ' workflow'
181- ? []
182- : this .node .children
183- },
184-
185- nodeClass () {
186- return {
187- ' c-workflow-stopped' : this .node .node ? .status === WorkflowState .STOPPED .name ,
188- }
189- }
147+ filteredOutNodesCache: {
148+ type: WeakMap ,
149+ required: true ,
190150 },
151+ hoverable: {
152+ type: Boolean ,
153+ },
154+ })
191155
192- nodeTypes: [' workflow-part' , ' workflow' ],
193- maxTasksDisplayed: 5 ,
194- WorkflowState,
195- }
156+ const workflowLink = computed (
157+ () => props .node .type === ' workflow'
158+ ? ` /workspace/${ props .node .tokens .workflow } `
159+ : ' '
160+ )
161+
162+ /** Task state totals for all descendents of this node. */
163+ const statesInfo = computed (() => getStatesInfo (props .node ))
164+
165+ const nodeChildren = computed (
166+ () => props .node .type === ' workflow' ? [] : props .node .children
167+ )
168+
169+ const nodeClass = computed (() => ({
170+ ' c-workflow-stopped' : props .node .node ? .status === WorkflowState .STOPPED .name ,
171+ }))
196172< / script>
0 commit comments