Last active
August 20, 2016 00:24
-
-
Save belablotski/190be806cb26316b3185a891075380e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta charset="utf-8"> | |
<!-- Based on https://github.com/cpettitt/dagre-d3/blob/master/demo/etl-status.html --> | |
<!-- Sample data are here: https://gist.github.com/beloblotskiy/06583a59c3005d6225835084be35641b --> | |
<title>Order-Customer-Inventory Jobs Graph</title> | |
<script src="./js/d3/d3.min.js" charset="utf-8"></script> | |
<script src="./js/dagre-d3/dagre-d3.min.js" charset="utf-8"></script> | |
<style> | |
body { | |
position: fixed; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
margin: 0; | |
padding: 0; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serf; | |
background: #333; | |
} | |
@-webkit-keyframes flash { | |
0%, 50%, 100% { | |
opacity: 1; | |
} | |
25%, 75% { | |
opacity: 0.2; | |
} | |
} | |
@keyframes flash { | |
0%, 50%, 100% { | |
opacity: 1; | |
} | |
25%, 75% { | |
opacity: 0.2; | |
} | |
} | |
.warn { | |
-webkit-animation-duration: 5s; | |
animation-duration: 5s; | |
-webkit-animation-fill-mode: both; | |
animation-fill-mode: both; | |
-webkit-animation-iteration-count: 1; | |
animation-iteration-count: 1; | |
} | |
.live.map { | |
width: 100%; | |
height: 100%; | |
} | |
svg { | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
} | |
.live.map text { | |
font-weight: 300; | |
font-size: 14px; | |
} | |
.live.map .node rect { | |
stroke-width: 1.5px; | |
stroke: #bbb; | |
fill: #666; | |
} | |
.live.map .status { | |
height: 100%; | |
width: 15px; | |
display: block; | |
float: left; | |
border-top-left-radius: 5px; | |
border-bottom-left-radius: 5px; | |
margin-right: 4px; | |
} | |
.live.map .running .status { | |
background-color: #7f7; | |
} | |
.live.map .running.warn .status { | |
background-color: #ffed68; | |
} | |
.live.map .stopped .status { | |
background-color: #f77; | |
} | |
.live.map .warn .queue { | |
color: #FEE;/*#f77;*/ | |
} | |
.warn { | |
-webkit-animation-name: flash; | |
animation-name: flash; | |
} | |
.live.map .consumers { | |
margin-right: 2px; | |
} | |
.live.map .consumers, | |
.live.map .name { | |
margin-top: 4px; | |
} | |
.live.map .consumers:after { | |
content: "x"; | |
} | |
.live.map .queue { | |
display: block; | |
float: left; | |
width: 130px; | |
height: 20px; | |
font-size: 14px; /* 12px */ | |
margin-top: 2px; | |
} | |
.live.map .node g div { | |
width: 500px; | |
height: 40px; | |
color: #fff; | |
} | |
.live.map .node g div span.consumers { | |
display: inline-block; | |
width: 20px; | |
} | |
.live.map .edgeLabel text { | |
width: 50px; | |
fill: #fff; | |
} | |
.live.map .edgePath path { | |
stroke: #999; | |
stroke-width: 1.5px; | |
fill: #999; | |
} | |
</style> | |
<body> | |
<div class="live map"> | |
<svg id="JobGraphVis"><g/></svg> | |
</div> | |
<script> | |
var JobGraph_workers = {}; | |
function JobGraph_Visualize(tblsJsDataFile) { | |
// Set up zoom support | |
var svg = d3.select("svg"), | |
inner = svg.select("g"), | |
zoom = d3.behavior.zoom().on("zoom", function() { | |
inner.attr("transform", "translate(" + d3.event.translate + ")" + | |
"scale(" + d3.event.scale + ")"); | |
}); | |
svg.call(zoom); | |
var render = new dagreD3.render(); | |
// Left-to-right layout | |
var g = new dagreD3.graphlib.Graph(); | |
g.setGraph({ | |
nodesep: 70, | |
ranksep: 50, | |
rankdir: "LR", | |
marginx: 20, | |
marginy: 20 | |
}); | |
function draw(isUpdate) { | |
for (var id in JobGraph_workers) { | |
var worker = JobGraph_workers[id]; | |
var className = worker.consumers ? "running" : "stopped"; | |
if (worker.isWarn) { | |
className += " warn"; | |
} | |
var html = "<div>"; | |
html += "<span class='status'></span>"; | |
//html += "<span class='consumers'>"+worker.consumers+"</span>"; | |
html += "<span class='name'>"+id+"</span><br />"; | |
html += "<span class='queue'><span class='job-desc'>Module: <b>" + (worker.execModule ? worker.execModule : "N/A") + "</b> - " + worker.jobDesc + "</span></span>"; | |
html += "</div>"; | |
g.setNode(id, { | |
labelType: "html", | |
label: html, | |
rx: 5, | |
ry: 5, | |
padding: 0, | |
class: className | |
}); | |
if (worker.parentJobs) { | |
for (var k = 0; k < worker.parentJobs.length; k++) { | |
g.setEdge(worker.parentJobs[k], id, { | |
label: "", // worker.inputThroughput + "/s", | |
width: 50 | |
}); | |
} | |
} | |
} | |
inner.call(render, g); | |
// Zoom and scale to fit | |
var graphWidth = g.graph().width + 80; | |
var graphHeight = g.graph().height + 40; | |
var width = parseInt(svg.style("width").replace(/px/, "")); | |
var height = parseInt(svg.style("height").replace(/px/, "")); | |
var zoomScale = Math.min(width / graphWidth, height / graphHeight); | |
var translate = [(width/2) - ((graphWidth*zoomScale)/2), (height/2) - ((graphHeight*zoomScale)/2)]; | |
zoom.translate(translate); | |
zoom.scale(zoomScale); | |
zoom.event(isUpdate ? svg.transition().duration(500) : d3.select("svg")); | |
} | |
// Load JS with data | |
function createScriptDomElem(jsFile) { | |
var elm = document.createElement("script"); | |
elm.type = "application/javascript"; | |
elm.src = jsFile; | |
document.body.appendChild(elm); | |
} | |
JobGraph_workers = {}; | |
d3.select("body").insert("p", ":first-child").attr("style", "color:gray").text("Run-up for loading " + tblsJsDataFile + " --- " + new Date()) | |
createScriptDomElem(tblsJsDataFile) | |
var checkJsLoad = setInterval(function() { | |
try { | |
if (JobGraph_workers) { | |
var c = 0 | |
for (var prop in JobGraph_workers) c++ | |
if (c > 0) { | |
clearInterval(checkJsLoad) | |
d3.select("body").selectAll("p").remove() | |
draw() | |
} | |
} | |
} | |
catch (err) { | |
d3.select("body").insert("p", ":first-child").attr("style", "color:gray").text("Loading " + tblsJsDataFile + " --- " + new Date()) | |
} | |
}, 1000); | |
} | |
try { | |
function displayCtmTablesList() { | |
function databasesSelectOnChange() { | |
d3.select("#JobGraphVis").selectAll("*").remove() | |
d3.select("#JobGraphVis").append("g") | |
var selectValue = d3.select("#databasesSelect").property("value") | |
JobGraph_Visualize("./data/job_graph/" + selectValue + ".js") | |
} | |
var databasesSelect = d3.select("body").insert("select", ":first-child") | |
.attr("id", "databasesSelect") | |
.on("change", databasesSelectOnChange) | |
databasesSelect | |
.selectAll("option") | |
.data(["ORDER_CUSTOMER_INVENTORY", "ORDER_MANAGEMENT", "TRANSACTIONS_ONLINE"]) | |
.enter() | |
.append("option") | |
.text(function(d) {return d}) | |
.attr("value", function(d) {return d}) | |
d3.select("body").append("p") | |
} | |
displayCtmTablesList() | |
JobGraph_Visualize("./data/job_graph/ORDER_CUSTOMER_INVENTORY.js") | |
} | |
catch(e) { | |
if (e instanceof Error) { | |
alert("ERROR: " + e.name + '\n' + e.message) | |
} else { | |
alert("ERROR:\n" + err) | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment