From 1a8ef91e23ca0c7e071ebcf4487d02ad531a247c Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Sat, 13 Apr 2019 16:11:35 -0400 Subject: [PATCH] callback dag visualization --- package.json | 1 + .../CallbackGraph/CallbackGraphContainer.css | 8 ++ .../CallbackGraphContainer.react.js | 73 +++++++++++++++++++ .../error/GlobalErrorContainer.react.js | 5 +- src/components/error/menu/DebugMenu.react.js | 26 ++++++- 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/components/error/CallbackGraph/CallbackGraphContainer.css create mode 100644 src/components/error/CallbackGraph/CallbackGraphContainer.react.js diff --git a/package.json b/package.json index c62e918..e73a043 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "redux-actions": "^0.9.1", "redux-thunk": "^2.0.1", "uniqid": "^5.0.3", + "viz.js": "1.8.0", "webpack": "^4.20.2", "webpack-cli": "^3.1.2", "webpack-partial": "^1.2.0", diff --git a/src/components/error/CallbackGraph/CallbackGraphContainer.css b/src/components/error/CallbackGraph/CallbackGraphContainer.css new file mode 100644 index 0000000..cb616f8 --- /dev/null +++ b/src/components/error/CallbackGraph/CallbackGraphContainer.css @@ -0,0 +1,8 @@ +.dash-callback-dag--container { + position: absolute; + bottom: 110px; + right: 110px; + max-width: 80vw; + max-height: 80vh; + overflow: scroll; +} diff --git a/src/components/error/CallbackGraph/CallbackGraphContainer.react.js b/src/components/error/CallbackGraph/CallbackGraphContainer.react.js new file mode 100644 index 0000000..f2a0dac --- /dev/null +++ b/src/components/error/CallbackGraph/CallbackGraphContainer.react.js @@ -0,0 +1,73 @@ +import React, {Component} from 'react'; +import './CallbackGraphContainer.css'; + +import viz from 'viz.js'; + +import PropTypes from 'prop-types'; + +class CallbackGraphContainer extends Component { + constructor(props) { + super(props); + } + render() { + const {dependenciesRequest} = this.props; + const elements = {}; + const callbacks = []; + const links = dependenciesRequest.content.map(({inputs, output}, i) => { + callbacks.push(`cb${i};`); + function recordAndReturn([id, property]) { + elements[id] = elements[id] || {}; + elements[id][property] = true; + return `"${id}.${property}"`; + } + const out_nodes = output + .replace(/^\.\./, '') + .replace(/\.\.$/, '') + .split('...') + .map(o => recordAndReturn(o.split('.'))) + .join(', '); + const in_nodes = inputs + .map(({id, property}) => recordAndReturn([id, property])) + .join(', '); + return `{${in_nodes}} -> cb${i} -> {${out_nodes}};`; + }); + + const dot = `digraph G { + overlap = false; fontname="Arial"; fontcolor="#333333"; + edge [color="#888888"]; + node [shape=box, fontname="Arial", style=filled, color="#109DFF", fontcolor=white]; + graph [penwidth=0]; + subgraph callbacks { + node [shape=circle, width=0.3, label="", color="#00CC96"]; + ${callbacks.join('\n')} } + + ${Object.entries(elements) + .map( + ([id, props], i) => ` + subgraph cluster_${i} { + bgcolor="#B9C2CE"; + ${Object.keys(props) + .map(p => `"${id}.${p}" [label="${p}"];`) + .join('\n')} + label = "${id}"; }` + ) + .join('\n')} + + ${links.join('\n')} }`; + + return ( +
+ ); + } +} + +CallbackGraphContainer.propTypes = { + dependenciesRequest: PropTypes.object, +}; + +export {CallbackGraphContainer}; diff --git a/src/components/error/GlobalErrorContainer.react.js b/src/components/error/GlobalErrorContainer.react.js index f7d38ee..da8415e 100644 --- a/src/components/error/GlobalErrorContainer.react.js +++ b/src/components/error/GlobalErrorContainer.react.js @@ -20,11 +20,12 @@ class UnconnectedGlobalErrorContainer extends Component { } render() { - const {error, dispatch} = this.props; + const {error, dispatch, dependenciesRequest} = this.props; return (
@@ -38,12 +39,14 @@ class UnconnectedGlobalErrorContainer extends Component { UnconnectedGlobalErrorContainer.propTypes = { children: PropTypes.object, error: PropTypes.object, + dependenciesRequest: PropTypes.object, dispatch: PropTypes.func, }; const GlobalErrorContainer = connect( state => ({ error: state.error, + dependenciesRequest: state.dependenciesRequest, }), dispatch => ({dispatch}) )(Radium(UnconnectedGlobalErrorContainer)); diff --git a/src/components/error/menu/DebugMenu.react.js b/src/components/error/menu/DebugMenu.react.js index eda8c13..7cd9242 100644 --- a/src/components/error/menu/DebugMenu.react.js +++ b/src/components/error/menu/DebugMenu.react.js @@ -10,6 +10,7 @@ import GraphIcon from '../icons/GraphIcon.svg'; import PropTypes from 'prop-types'; import {DebugAlertContainer} from './DebugAlertContainer.react'; import GlobalErrorOverlay from '../GlobalErrorOverlay.react'; +import {CallbackGraphContainer} from '../CallbackGraph/CallbackGraphContainer.react'; class DebugMenu extends Component { constructor(props) { @@ -18,12 +19,18 @@ class DebugMenu extends Component { this.state = { opened: false, alertsOpened: false, + callbackGraphOpened: false, toastsEnabled: true, }; } render() { - const {opened, alertsOpened, toastsEnabled} = this.state; - const {error, resolveError, dispatch} = this.props; + const { + opened, + alertsOpened, + toastsEnabled, + callbackGraphOpened, + } = this.state; + const {error, resolveError, dispatch, dependenciesRequest} = this.props; const menuClasses = opened ? 'dash-debug-menu dash-debug-menu--opened' @@ -31,6 +38,11 @@ class DebugMenu extends Component { const menuContent = opened ? (
+ {callbackGraphOpened ? ( + + ) : null} {error.frontEnd.length > 0 || error.backEnd.length > 0 ? (
) : null}
-
+
+ this.setState({ + callbackGraphOpened: !callbackGraphOpened, + }) + } + >