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 in tree view & command menu #2016

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
12 changes: 8 additions & 4 deletions src/components/cylc/commandMenu/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ 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'

export default {
name: 'CommandMenu',
Expand Down Expand Up @@ -199,14 +200,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 +217,9 @@ export default {
if (this.node.node.isRunahead) {
ret += ' (runahead)'
}
if (this.node.node.flowNums) {
ret += ` • Flows: ${this.node.node.flowNums.replace(/\[|\]/g, '')}`
}
}
}
return ret
Expand Down
28 changes: 24 additions & 4 deletions src/components/cylc/tree/TreeItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</div>
<span class="mx-1">{{ node.name }}</span>
<v-chip
label
density="compact"
size="small"
class="ml-1 cursor-default"
:prepend-icon="$options.icons.flow"
:disabled="node.node.state === 'waiting' && !node.node.isQueued"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

N.B. this is for developer purposes only (showing dimmed flow nums for tasks in the data store that have not spawned yet, which could have different flow nums when they actually spawn). We should make this condition a v-if instead to hide data store flow nums.

>
{{ formatFlowNums(node.node.flowNums) }}
<v-tooltip location="right">
Flows: {{ formatFlowNums(node.node.flowNums) }}
</v-tooltip>
</v-chip>
</template>
<template v-else-if="node.type === 'job'">
<Job
Expand Down Expand Up @@ -175,13 +188,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>

<script>
import { mdiChevronRight } from '@mdi/js'
import { ref } from 'vue'
import {
mdiChevronRight,
mdiLabelOutline,
} 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 {
formatFlowNums,
jobMessageOutputs,
latestJob,
jobMessageOutputs
} from '@/utils/tasks'
import { getIndent, getNodeChildren } from '@/components/cylc/tree/util'

Expand Down Expand Up @@ -240,9 +258,10 @@ export default {
},
},

data () {
setup () {
return {
manuallyExpanded: null,
manuallyExpanded: ref(null),
formatFlowNums,
}
},

Expand Down Expand Up @@ -319,6 +338,7 @@ export default {

icons: {
mdiChevronRight,
flow: mdiLabelOutline
},
}
</script>
7 changes: 7 additions & 0 deletions src/services/mock/json/workflows/one.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/root",
"name": "root",
Expand All @@ -133,6 +134,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/SUCCEEDED",
"name": "SUCCEEDED",
Expand All @@ -152,6 +154,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/BAD",
"name": "BAD",
Expand All @@ -170,6 +173,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/BAD",
"name": "BAD",
Expand All @@ -188,6 +192,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/root",
"name": "root",
Expand All @@ -206,6 +211,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/SUCCEEDED",
"name": "SUCCEEDED",
Expand All @@ -225,6 +231,7 @@
"isQueued": false,
"isRunahead": false,
"cyclePoint": "20000102T0000Z",
"flowNums": "[1]",
"firstParent": {
"id": "~user/one//20000102T0000Z/root",
"name": "root",
Expand Down
1 change: 1 addition & 0 deletions src/styles/cylc/_tree.scss
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ $icon-width: 1.5rem;
.node-data {
display: flex;
flex-wrap: nowrap;
align-items: center;
.node-summary {
display: flex;
flex-wrap: nowrap;
Expand Down
22 changes: 11 additions & 11 deletions src/utils/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @returns {string} a valid Task State name, or empty string if not found
* @link @see https://github.com/cylc/cylc-flow/blob/d66ae5c3ce8c749c8178d1cd53cb8c81d1560346/lib/cylc/task_state_prop.py
*/
function extractGroupState (childStates, isStopped = false) {
export function extractGroupState (childStates, isStopped = false) {
const states = isStopped ? isStoppedOrderedStates : TaskState.enumValues
for (const state of states) {
if (childStates.includes(state.name)) {
Expand All @@ -50,7 +50,7 @@
return ''
}

function latestJob (taskProxy) {
export function latestJob (taskProxy) {
return taskProxy?.children?.[0]?.node
}

Expand All @@ -67,7 +67,7 @@
* }
* }
*/
function jobMessageOutputs (jobNode) {
export function jobMessageOutputs (jobNode) {
const ret = []

for (const message of jobNode.node.messages || []) {
Expand Down Expand Up @@ -96,7 +96,7 @@
* 00:00:00, rather than undefined
* @return {string=} Formatted duration
*/
function formatDuration (dur, allowZeros = false) {
export function formatDuration (dur, allowZeros = false) {
if (dur || (dur === 0 && allowZeros === true)) {
const seconds = dur % 60
const minutes = ((dur - seconds) / 60) % 60
Expand All @@ -118,16 +118,16 @@
return undefined
}

function dtMean (taskNode) {
export function dtMean (taskNode) {
// Convert to an easily read duration format:
const dur = taskNode.node?.task?.meanElapsedTime
return formatDuration(dur)
}

export {
extractGroupState,
latestJob,
jobMessageOutputs,
formatDuration,
dtMean
/**
* @param {string} flowNums - Flow numbers in DB format
* @returns {string} - Flow numbers in pretty format
*/
export function formatFlowNums (flowNums) {
return JSON.parse(flowNums).join(', ') || 'None'

Check failure on line 132 in src/utils/tasks.js

View workflow job for this annotation

GitHub Actions / unit-test-and-build

tests/unit/components/cylc/tree/treeitem.vue.spec.js

SyntaxError: "undefined" is not valid JSON ❯ Proxy.formatFlowNums src/utils/tasks.js:132:15 ❯ src/components/cylc/tree/TreeItem.vue:285:18 ❯ Proxy.renderFnWithContext node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:814:13 ❯ default node_modules/vuetify/src/components/VChip/VChip.tsx:319:22 ❯ normalizeChildren node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7293:34 ❯ createBaseVNode node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7104:5 ❯ _createVNode node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7181:10 ❯ createVNodeWithArgsTransform node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7058:10 ❯ Proxy.<anonymous> node_modules/vuetify/src/components/VChip/VChip.tsx:229:49
}
1 change: 1 addition & 0 deletions src/views/Tree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ fragment TaskProxyData on TaskProxy {
firstParent {
id
}
flowNums
}

fragment JobData on Job {
Expand Down
11 changes: 2 additions & 9 deletions tests/unit/components/cylc/tree/tree.vue.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// we mount the tree to include the TreeItem component and other vuetify children components
import { mount } from '@vue/test-utils'
import { vi } from 'vitest'
import { createVuetify } from 'vuetify'
import { cloneDeep } from 'lodash'
import Tree from '@/components/cylc/tree/Tree.vue'
import { simpleWorkflowTree4Nodes } from './tree.data'
import CommandMenuPlugin from '@/components/cylc/commandMenu/plugin'

const vuetify = createVuetify()

describe('Tree component', () => {
const mountFunction = (props) => mount(Tree, {
global: {
plugins: [vuetify, CommandMenuPlugin],
},
props: {
workflows: cloneDeep(simpleWorkflowTree4Nodes),
autoStripTypes: ['workflow'],
filterState: null,
...props,
}
},
shallow: true,
})

it.each([
Expand Down
19 changes: 18 additions & 1 deletion tests/unit/utils/tasks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@
*/

import TaskState from '@/model/TaskState.model'
import { dtMean, extractGroupState, latestJob, formatDuration, jobMessageOutputs } from '@/utils/tasks'
import {
dtMean,
extractGroupState,
latestJob,
formatDuration,
jobMessageOutputs,
formatFlowNums,
} from '@/utils/tasks'

describe('tasks', () => {
describe('extractGroupState', () => {
Expand Down Expand Up @@ -207,4 +214,14 @@ describe('tasks', () => {
])
})
})

describe('formatFlowNums', () => {
it.each([
['[1]', '1'],
['[1, 4, 8]', '1, 4, 8'],
['[]', 'None'],
])('formatFlowNums(%s) -> %s', (input, expected) => {
expect(formatFlowNums(input)).toEqual(expected)
})
})
})
Loading