From 647c6abf2545e90c8a537daff68cccad29478156 Mon Sep 17 00:00:00 2001 From: Martin Ryan Date: Wed, 16 Oct 2019 15:54:09 +1100 Subject: [PATCH] wip: node job info --- package.json | 14 + src/components/core/CyElement.vue | 47 + src/components/core/Cytoscape.vue | 211 +++ src/components/cylc/Graph.vue | 1462 +++++++++++++++++++++ src/router/paths.js | 9 + src/services/cytoscape.service.js | 27 + src/styles/cytoscape/cytoscape-custom.css | 223 ++++ src/styles/cytoscape/html-label.scss | 57 + src/styles/cytoscape/panzoom.css | 221 ++++ src/views/GScan.vue | 13 +- src/views/Graph.vue | 20 + 11 files changed, 2297 insertions(+), 7 deletions(-) create mode 100644 src/components/core/CyElement.vue create mode 100644 src/components/core/Cytoscape.vue create mode 100644 src/components/cylc/Graph.vue create mode 100644 src/services/cytoscape.service.js create mode 100644 src/styles/cytoscape/cytoscape-custom.css create mode 100644 src/styles/cytoscape/html-label.scss create mode 100644 src/styles/cytoscape/panzoom.css create mode 100644 src/views/Graph.vue diff --git a/package.json b/package.json index 98e24e1b7b..aa8fed468f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,17 @@ "dependencies": { "apollo-boost": "^0.4.4", "axios-fetch": "^1.1.0", + "cytoscape": "^3.9.0", + "cytoscape-cola": "^2.3.0", + "cytoscape-cose-bilkent": "^4.0.0", + "cytoscape-dagre": "^2.2.2", + "cytoscape-expand-collapse": "^3.1.2", + "cytoscape-klay": "^3.1.2", + "cytoscape-navigator": "^1.3.3", + "cytoscape-node-html-label": "^1.1.5", + "cytoscape-panzoom": "^2.5.3", + "cytoscape-popper": "^1.0.4", + "cytoscape-undo-redo": "^1.3.1", "dotparser": "^0.4.0", "enumify": "^1.0.4", "graphql": "^14.5.3", @@ -25,13 +36,16 @@ "jshint": "^2.10.2", "nprogress": "^0.2.0", "tippy.js": "^4.3.5", + "uuid-by-string": "^2.1.5", "vee-validate": "^3.0.2", "vue": "^2.6.10", "vue-apollo": "^3.0.0-beta.30", + "vue-cytoscape": "^0.2.10", "vue-i18n": "^8.14.0", "vue-meta": "^2.2.1", "vue-router": "^3.1.2", "vue-spinner": "^1.0.3", + "vue-tippy": "^4.0.3", "vuetify": "^2.0.10", "vuex": "^3.1.1", "vuex-router-sync": "^5.0.0" diff --git a/src/components/core/CyElement.vue b/src/components/core/CyElement.vue new file mode 100644 index 0000000000..46d5f24bc1 --- /dev/null +++ b/src/components/core/CyElement.vue @@ -0,0 +1,47 @@ + + diff --git a/src/components/core/Cytoscape.vue b/src/components/core/Cytoscape.vue new file mode 100644 index 0000000000..a9f3d7c5b9 --- /dev/null +++ b/src/components/core/Cytoscape.vue @@ -0,0 +1,211 @@ + + + diff --git a/src/components/cylc/Graph.vue b/src/components/cylc/Graph.vue new file mode 100644 index 0000000000..657c3384b7 --- /dev/null +++ b/src/components/cylc/Graph.vue @@ -0,0 +1,1462 @@ + + + + + diff --git a/src/router/paths.js b/src/router/paths.js index c8c5dc9f84..e8cb6eb5a3 100644 --- a/src/router/paths.js +++ b/src/router/paths.js @@ -37,6 +37,15 @@ export default [ layout: 'default' } }, + { + path: '/graph/:workflowid', + view: 'Graph', + name: i18n.t('App.graph'), + meta: { + layout: 'default' + }, + props: true + }, { path: '/guide', name: 'Guide', diff --git a/src/services/cytoscape.service.js b/src/services/cytoscape.service.js new file mode 100644 index 0000000000..f3f7b399a4 --- /dev/null +++ b/src/services/cytoscape.service.js @@ -0,0 +1,27 @@ +/* jshint esversion: 6, asi: true */ +import { GQuery } from '@/services/gquery' + +class CytoscapeGQueryService extends GQuery { + constructor () { + super() + this.polling = null + } + + destructor () { + clearInterval(this.polling) + } + + subscribe (view, query) { + const ret = super.subscribe(view, query) + if (!this.polling) { + this.polling = setInterval(() => { + this.request() + }, 10000) + this.request() + } + return ret + } +} + +const cytoscapeService = new CytoscapeGQueryService() +export default cytoscapeService diff --git a/src/styles/cytoscape/cytoscape-custom.css b/src/styles/cytoscape/cytoscape-custom.css new file mode 100644 index 0000000000..9dba1ff10e --- /dev/null +++ b/src/styles/cytoscape/cytoscape-custom.css @@ -0,0 +1,223 @@ +#holder { + width: 100%; + height: 800px; + background-color: #fff; + z-index: 1; +} + +#cytoscape { + width: 100%; + height: 800px; + background-color: #fff; + z-index: 2; +} + +#app { + text-align: left; + color: #2c3e50; + margin-top: 0; + width: 100%; +} + +.cytoscape-navigator-overlay { + position: absolute; + border: 1px solid #222; + background: #fff; + z-index: 5; + width: 200px; + height: 150px; + bottom: 83px; + right: -1px; + overflow: hidden; +} + +.cytoscape-navigatorView { + border: 2px solid yellow; + border-radius: 1px; + background-color: #0c78ff; + opacity: 0.4; + /* cursor: move; */ +} + +.spinner { + position: fixed; + top: 50%; + left: 54%; + z-index: 99; +} + +.collapsed-child { + opacity: 0; + display: none + } + +.switchlayout { + position: absolute; + bottom: 83px; + left: 1px; +} + +.dagre-button { + text-align: centre; + color: #f2f2f2; + z-index: 4; + height: 40px; +} + +.cosebilkent-button { + text-align: centre; + color: #f2f2f2; + z-index: 4; + height: 40px; + margin-left: 4px; +} + +.klay-button { + text-align: centre; + color: #f2f2f2; + z-index: 4; + height: 40px; + width: 40px; + margin-left: 4px; +} + +.hierarchical-button { + text-align: centre; + color: #f2f2f2; + z-index: 4; + height: 40px; + margin-left: 4px; +} + +.cola-button { + text-align: centre; + color: #f2f2f2; + z-index: 4; + height: 40px; + width: 40px; + margin-left: 4px; +} + +.collapse-all-button { + text-align: centre; + color: #f2f2f2; + font-size: .6em; + /* color: #666; */ + z-index: 4; + height: 10px; + margin: 1em; +} + +.layout-title { + text-align: left; + font-size: .8em; + color: #2b2b2b; + /* background: #f2f2f2; */ + text-transform: lowercase; + position: absolute; + bottom: 44px; + left: 1px; + z-index: 4; +} + +.suite-title { + text-align: left; + font-size: .8em; + color: #2b2b2b; + /* background: #f2f2f2; */ + text-transform: lowercase; + position: absolute; + bottom: 60px; + left: 1px; + z-index: 4; +} + +.node-tooltip { + font-family: arial, helvetica, sans-serif; +} + +.edge-tooltip { + font-family: arial, helvetica, sans-serif; + width: 100%; +} + +.box { + float: left; + height: 20px; + width: 20px; + /* margin-top: 1em; */ + margin-right: 1em; + margin-bottom: 1px; + /* margin-bottom: 15px; */ + border: none; + clear: both; + border-radius: 4px; +} + +.expired { + background-color: #fefaff; +} + +.failed { + background-color: #ff3a2b; +} + +.queued { + background-color: #fff138; +} + +.ready { + background-color: #7093FF; +} + +.retrying { + background-color: #ff3a2b; +} + +.running { + background-color: #4ab7ff; +} + +.subfailed { + background-color: #d453ff; +} + +.submitted { + background-color: #9ef9ff; +} + +.succeeded { + background-color: #31ff53; +} + +.waiting { + background-color: #666; +} + +.default { + background-color: #555; +} + +.UNDEFINED { + background-color: #444; +} + +hr { + width: 100%; + margin-top: 0.5em; + margin-bottom: 0.5em; + margin-left: auto; + margin-right: auto; + height: 2px; + background-color:#666; + opacity: 0.5; + } + +.header { + width: 100%; + height: 1em; + overflow: hidden; + background-color: #e1e1e1; + padding: 20px 10px; +} + \ No newline at end of file diff --git a/src/styles/cytoscape/html-label.scss b/src/styles/cytoscape/html-label.scss new file mode 100644 index 0000000000..4250956de2 --- /dev/null +++ b/src/styles/cytoscape/html-label.scss @@ -0,0 +1,57 @@ + .cy-title { + text-align: left; + font-size: 1em; + width: 130px; + color: #2b2b2b; + text-transform: capitalize; + background: rgba(255, 255, 255, 1) + } + + .cy-title__cyclepoint { + text-align: left; + font-size: 1em; + width: 130px; + color: #2b2b2b; + text-transform: capitalize; + background: rgba(255, 255, 255, 1) + } + + .cy-title__label { + font-size: 1em; + overflow-wrap: break-word; + color: '#333'; + background: rgba(255, 255, 255, 1) + } + + .cy-title__state { + font-size: 0.9em; + background: rgba(255, 255, 255, 1) + } + + .cy-title__submittedtime { + font-size: 1em; + color: '#333'; + background: rgba(255, 255, 255, 1) + } + + .cy-title__status { + font-size: .8em; + color: '#333'; + background: rgba(255, 255, 255, 1) + } + + .cy-title__jobs { + font-size: .8em; + color: '#333'; + background: rgba(255, 255, 255, 1) + } + + .cy-title__name { + font-size: 1.5em; + background: rgba(255, 255, 255, 1) + } + + .cy-title__info { + font-size: 0.9em; + background: rgba(255, 255, 255, 1) + } diff --git a/src/styles/cytoscape/panzoom.css b/src/styles/cytoscape/panzoom.css new file mode 100644 index 0000000000..e3cdb98eba --- /dev/null +++ b/src/styles/cytoscape/panzoom.css @@ -0,0 +1,221 @@ +.cy-panzoom { + position: absolute; + font-size: 12px; + color: #fff; + font-family: arial, helvetica, sans-serif; + line-height: 1; + color: #666; + font-size: 11px; + z-index: 99999; + box-sizing: content-box; + margin: 1em; +} + +.cy-panzoom-zoom-button { + cursor: pointer; + padding: 3px; + text-align: center; + position: absolute; + border-radius: 3px; + width: 10px; + height: 10px; + left: 16px; + background: #fff; + border: 1px solid #999; + margin-left: -1px; + margin-top: -1px; + z-index: 1; + box-sizing: content-box; +} + +.cy-panzoom-zoom-button:active, +.cy-panzoom-slider-handle:active, +.cy-panzoom-slider-handle.active { + background: #ddd; + box-sizing: content-box; +} + +.cy-panzoom-pan-button { + position: absolute; + z-index: 1; + height: 16px; + width: 16px; + box-sizing: content-box; +} + +.cy-panzoom-reset { + top: 55px; + box-sizing: content-box; +} + +.cy-panzoom-zoom-in { + top: 80px; + box-sizing: content-box; +} + +.cy-panzoom-zoom-out { + top: 197px; + box-sizing: content-box; +} + +.cy-panzoom-pan-up { + top: 0; + left: 50%; + margin-left: -5px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #666; + box-sizing: content-box; +} + +.cy-panzoom-pan-down { + bottom: 0; + left: 50%; + margin-left: -5px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #666; + box-sizing: content-box; +} + +.cy-panzoom-pan-left { + top: 50%; + left: 0; + margin-top: -5px; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #666; + box-sizing: content-box; +} + +.cy-panzoom-pan-right { + top: 50%; + right: 0; + margin-top: -5px; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #666; + box-sizing: content-box; +} + +.cy-panzoom-pan-indicator { + position: absolute; + left: 0; + top: 0; + width: 8px; + height: 8px; + border-radius: 8px; + background: #000; + border-radius: 8px; + margin-left: -5px; + margin-top: -5px; + display: none; + z-index: 999; + opacity: 0.6; + box-sizing: content-box; +} + +.cy-panzoom-slider { + position: absolute; + top: 97px; + left: 17px; + height: 100px; + width: 15px; + box-sizing: content-box; +} + +.cy-panzoom-slider-background { + position: absolute; + top: 0; + width: 2px; + height: 100px; + left: 5px; + background: #fff; + border-left: 1px solid #999; + border-right: 1px solid #999; + box-sizing: content-box; +} + +.cy-panzoom-slider-handle { + position: absolute; + width: 16px; + height: 8px; + background: #fff; + border: 1px solid #999; + border-radius: 2px; + margin-left: -2px; + z-index: 999; + line-height: 8px; + cursor: default; + box-sizing: content-box; +} + +.cy-panzoom-slider-handle .icon { + margin: 0 4px; + line-height: 10px; + box-sizing: content-box; +} + +.cy-panzoom-no-zoom-tick { + position: absolute; + background: #666; + border: 1px solid #fff; + border-radius: 2px; + margin-left: -1px; + width: 8px; + height: 2px; + left: 3px; + z-index: 1; + margin-top: 3px; + box-sizing: content-box; +} + +.cy-panzoom-panner { + position: absolute; + left: 5px; + top: 5px; + height: 40px; + width: 40px; + background: #fff; + border: 1px solid #999; + border-radius: 40px; + margin-left: -1px; + box-sizing: content-box; +} + +.cy-panzoom-panner-handle { + position: absolute; + left: 0; + top: 0; + outline: none; + height: 40px; + width: 40px; + position: absolute; + z-index: 999; + box-sizing: content-box; +} + +.cy-panzoom-zoom-only .cy-panzoom-slider, +.cy-panzoom-zoom-only .cy-panzoom-panner { + display: none; +} + +.cy-panzoom-zoom-only .cy-panzoom-reset { + top: 20px; +} + +.cy-panzoom-zoom-only .cy-panzoom-zoom-in { + top: 45px; +} + +.cy-panzoom-zoom-only .cy-panzoom-zoom-out { + top: 70px; +} \ No newline at end of file diff --git a/src/views/GScan.vue b/src/views/GScan.vue index a85d9bf238..4b7e6df671 100644 --- a/src/views/GScan.vue +++ b/src/views/GScan.vue @@ -50,13 +50,8 @@ {{ item.host }} {{ item.port }} - - mdi-table-edit - + mdi-table-edit + mdi-vector-polyline @@ -152,6 +147,10 @@ export default { this.$router.push({ path: `/workflows/${workflow.name}` }) }, + viewGraph (workflow) { + this.$router.push({ path: `/graph/${workflow.id}` }) + }, + subscribe (queryName) { const id = workflowService.subscribe( this, diff --git a/src/views/Graph.vue b/src/views/Graph.vue new file mode 100644 index 0000000000..ae0b7b9770 --- /dev/null +++ b/src/views/Graph.vue @@ -0,0 +1,20 @@ + + + + +