Skip to content

Commit

Permalink
Visualizations
Browse files Browse the repository at this point in the history
  • Loading branch information
smicallef committed Oct 20, 2013
1 parent 828a1d5 commit 21c0ab4
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 34 deletions.
72 changes: 61 additions & 11 deletions dyn/scaninfo.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<h2>${name} &nbsp;<img id=loader src=/static/img/loader.gif></h2>
<div class="btn-toolbar">
<div class="btn-group">
<!--<a id='btn-live' class="btn active" href="#" onClick='liveView("${id}")'><i class='icon-eye-open'></i>&nbsp;Live View</a> -->
<a id='btn-browse' class="btn" onClick='browseEventList("${id}")'><i class='icon-th-list'></i>&nbsp;Browse</a>
<a id='btn-info' class="btn" onClick='viewScanConfig("${id}")'><i class='icon-cog'></i>&nbsp;Scan Settings</a>
<a id='btn-log' class="btn" onClick='viewScanLog("${id}")'><i class='icon-book'></i>&nbsp;Log</a>
Expand All @@ -12,8 +11,17 @@
<a id='btn-export' rel="tooltip" title="Export Data to CSV" class="btn-info btn" onClick='exportEventData("${id}", currentType)'><i class='icon-download-alt icon-white'></i></a>
</div>
<div id='customview' class="btn-group pull-right">
<a id='btn-fullview' rel="tooltip" title="Full Data View" class="btn-inverse btn active" onClick='browseUpdate("full")'><i class='icon-th icon-white'></i></a>
<a id='btn-uniqueview' rel="tooltip" title="Unique Data View" class="btn-inverse btn" onClick='browseUpdate("unique")'><i class='icon-align-justify icon-white'></i></a>
<a id='btn-fullview' rel="tooltip" data-title="Full Data View" class="btn-inverse btn active" onClick='browseUpdate("full")'><i class='icon-th icon-white'></i></a>
<a id='btn-uniqueview' rel="tooltip" data-title="Unique Data View" class="btn-inverse btn" onClick='browseUpdate("unique")'><i class='icon-align-justify icon-white'></i></a>
<a id='btn-vizview' rel="tooltip" data-title="Visualize" class="btn-inverse btn dropdown-toggle" data-toggle="dropdown"><i class='icon-picture icon-white'></i></a>
<ul class="dropdown-menu">
<li><a class='link' onClick='browseUpdate("viz-bubble-data")'>Bubble (Data Element)</a></li>
<li><a class='link' onClick='browseUpdate("viz-bubble-source")'>Bubble (Source Data Element)</a></li>
<li><a class='link' onClick='browseUpdate("viz-dendro")'>Discovery Path</a></li>
<li><a class='link' onClick='browseUpdate("viz-discovery")'>Relationship Tree (One Level)</a></li>
<li><a class='link' onClick='browseUpdate("viz-discovery")'>Relationship Tree (Two Levels)</a></li>
<li><a class='link' onClick='browseUpdate("viz-discovery")'>Relationship Tree (Three Levels)</a></li>
</ul>
</div>
</div>

Expand All @@ -22,7 +30,7 @@
var refresh = function() { browseEventList("${id}"); }

function navTo(target) {
var targets = [ "btn-live", "btn-browse", "btn-info", "btn-log", "btn-export" ]
var targets = [ "btn-browse", "btn-info", "btn-log", "btn-export", "btn-viz" ]
for (var i = 0; i < targets.length; i++) {
if (targets[i] == target) {
$("#" + targets[i]).addClass("active");
Expand Down Expand Up @@ -110,6 +118,7 @@
if (format == 'full') {
$("#btn-fullview").addClass("active");
$("#btn-uniqueview").removeClass("active");
$("#btn-vizview").removeClass("active");
sf.fetchData('/scaneventresults', {'id': instanceId, 'eventType': eventType }, function(data) {
var crumbs = " <ul class='breadcrumb' id='breadcrumbs'> <li><a class='link' onClick='browseEventList(\"" + instanceId + "\");'>Browse</a>";
crumbs += " <span class='divider'>&gt;</span></li> <li><a class='link' onClick=";
Expand All @@ -122,9 +131,9 @@
for (var i = 0; i < data.length; i++) {
table += "<tr><td><pre style='border: 0px; background-color: inherit'>" + data[i][1];
// for debug
//table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][7];
//table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][5];
//table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][6];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][7];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][5];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][6];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][2];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][3];
table += "</pre></td><td><pre style='border: 0px; background-color: inherit'>" + data[i][0];
Expand All @@ -140,24 +149,65 @@
if (format == 'unique') {
$("#btn-uniqueview").addClass("active");
$("#btn-fullview").removeClass("active");
$("#btn-vizview").removeClass("active");
sf.fetchData('/scaneventresultsunique', {'id': instanceId, 'eventType': eventType }, function(data) {
var crumbs = " <ul class='breadcrumb' id='breadcrumbs'> <li><a onClick='browseEventList(\"" + instanceId + "\");'>Browse</a>";
crumbs += " <span class='divider'>&gt;</span></li> <li><a onClick=";
var crumbs = " <ul class='breadcrumb' id='breadcrumbs'> <li><a class='link' onClick='browseEventList(\"" + instanceId + "\");'>Browse</a>";
crumbs += " <span class='divider'>&gt;</span></li> <li><a class='link' onClick=";
crumbs += "'browseEventData(\"" + instanceId + "\",\"" + eventTypeLabel + "\",\"" + eventType + "\",\"" + format + "\");'>";
crumbs += eventTypeLabel + "</a></li></ul>";

var table = "<table id='scansummary-content' class='table table-bordered table-striped small'>";
table += "<thead><tr> <th>Unique Data Element</th></tr></thead><tbody>";
table += "<thead><tr> <th>Unique Data Element</th><th>Occurrences</th></tr></thead><tbody>";
for (var i = 0; i < data.length; i++) {
table += "<tr><td><pre style='border: 0px; background-color: inherit'>" + data[i][0];
table += "</pre></td>";
table += "</pre></td><td>" + data[i][2] + "</td>";
table += "</tr>";
}
table += "</tbody></table>"
$("#loader").fadeOut(500);
$("#mainbody").append(crumbs + table);
});
}

if (format.indexOf('viz') == 0) {
$("#btn-vizview").addClass("active");
$("#btn-fullview").removeClass("active");
$("#btn-uniqueview").removeClass("active");

if (format.indexOf("viz-bubble") == 0) {
sf.fetchData('/scaneventresults', {'id': instanceId, 'eventType': eventType }, function(data) {
var crumbs = " <ul class='breadcrumb' id='breadcrumbs'> <li><a class='link' onClick='browseEventList(\"" + instanceId + "\");'>Browse</a>";
crumbs += " <span class='divider'>&gt;</span></li> <li><a class='link' onClick=";
crumbs += "'browseEventData(\"" + instanceId + "\",\"" + eventTypeLabel + "\",\"" + eventType + "\",\"" + format + "\");'>";
crumbs += eventTypeLabel + "</a></li></ul>";
itemList = []
for (var i = 0; i < data.length; i++) {
if (format == "viz-bubble-source") {
itemList.push(data[i][2]);
}
if (format == "viz-bubble-data") {
itemList.push(data[i][1]);
}
}
$("#loader").fadeOut(500);
$("#mainbody").append(crumbs + "<div id='scansummary-content' style='text-align: center'></div>");
sf_viz_bubble("#scansummary-content", itemList)
});
}

if (format == "viz-dendro") {
sf.fetchData("/scanelementtypediscovery", {'id': instanceId, 'eventType': eventType }, function(data) {
var crumbs = " <ul class='breadcrumb' id='breadcrumbs'> <li><a class='link' onClick='browseEventList(\"" + instanceId + "\");'>Browse</a>";
crumbs += " <span class='divider'>&gt;</span></li> <li><a class='link' onClick=";
crumbs += "'browseEventData(\"" + instanceId + "\",\"" + eventTypeLabel + "\",\"" + eventType + "\",\"" + format + "\");'>";
crumbs += eventTypeLabel + "</a></li></ul>";

$("#loader").fadeOut(500);
$("#mainbody").append(crumbs + "<div id='scansummary-content' style='text-align: center'></div>");
sf_viz_dendrogram("#scansummary-content", data);
});
}
}
}

// Download the data currently being viewed
Expand Down
20 changes: 20 additions & 0 deletions static/css/spiderfoot.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,23 @@ margin-left: -1px;
a.link:hover {
cursor: pointer;
}



.dend-node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}

.dend-node {
font: 10px sans-serif;
}

.dend-link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}


182 changes: 159 additions & 23 deletions static/js/viz.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,160 @@
// data should be an array of the items you want to plot
// Takes an object in the form of:
// { name: "blah", children: [ { name: "blah 2", children: [ ... ] } ] }
// and counts the number of objects without children
function sf_viz_countTailNodes(arg) {
var data = arg;
var count = 0;

for (var i = 0; i < data.length; i++) {
for (var p in data[i]) {
if (p == "children" && data[i].children == null) {
count++;
continue;
}
if (p == "children" && data[i].children != null) {
count += sf_viz_countTailNodes(data[i].children);
}
}
}

return count;
}

// As above but counts the total number of objects
function sf_viz_countTotalNodes(arg) {
var data = arg;
var count = 0;

for (var i = 0; i < data.length; i++) {
for (var p in data[i]) {
if (p == "name") {
count++;
continue;
}
if (p == "children" && data[i].children != null) {
count += sf_viz_countTotalNodes(data[i].children);
}
}
}

return count;
}

// As above but counts the highest number of levels
function sf_viz_countLevels(arg, levelsDeep, maxLevels) {
var data = arg;
var levels = levelsDeep;
var max = maxLevels;

for (var i = 0; i < data.length; i++) {
for (var p in data[i]) {
// We've hit a member with children..
if (p == "children" && data[i].children != null) {
levels++;
[ levels, max ] = sf_viz_countLevels(data[i].children, levels, max);
}

if (p == "children" && data[i].children == null) {
if (levels > max) {
//alert("max = " + levels);
max = levels;
}
}
}

// Reset to the level we're at as we iterate through the next child.
levels = levelsDeep;
}

return [ levels, max ];
}

function sf_viz_dendrogram(targetId, plotData) {
var width = sf_viz_countLevels([plotData], 0, 0)[1] * 150;
var height = sf_viz_countTailNodes([plotData]) * 20;

if (width < 600) {
width = 600;
}
if (height < 600) {
height = 600;
}

var cluster = d3.layout.cluster()
.size([height, width - 160]);

var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });

var svg = d3.select(targetId).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");

var nodes = cluster.nodes(plotData),
links = cluster.links(nodes);

var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "dend-link")
.attr("d", diagonal);

var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "dend-node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
.on("mouseover", function(d, i) {
d3.select(this).style("fill", "silver");
showToolTip(d.name, d3.event.pageX+10, d3.event.pageY+10,true);
})
.on("mouseout", function() {
d3.select(this).style("fill", "black");
showToolTip(" ",0,0,false);
});

node.append("circle")
.attr("r", 4.5);

node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return 'something'; });

d3.select(targetId).style("height", height + "px");

function showToolTip(pMessage,pX,pY,pShow) {
if (typeof(tooltipDivID)=="undefined") {
tooltipDivID =$('<div id="messageToolTipDiv" style="position:absolute;display:block;z-index:10000;border:2px solid black;background-color:rgba(0,0,0,0.8);margin:auto;padding:3px 5px 3px 5px;color:white;font-size:12px;font-family:arial;border-radius: 5px;vertical-align: middle;text-align: center;min-width:50px;overflow:auto;"></div>');
$('body').append(tooltipDivID);
}
if (!pShow) { tooltipDivID.hide(); return;}
tooltipDivID.html(pMessage);
tooltipDivID.css({top:pY,left:pX});
tooltipDivID.show();
}
}


// Produces a bubble diagram enabling visually comparing size of
// data points.
// plotData should be an array of the items you want to plot
function sf_viz_bubble(targetId, plotData) {
var diameter = 700 - 30,
limit = 5000,
var diameter = 900,
format = d3.format(",d"),
color = d3.scale.category20c();

var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(2);
.padding(1.5);

var svg = d3.select(targetId).append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");

var wordList = []; //each word one entry and contains the total count [ {cnt:30,title_list:[3,5,9],
var wordCount = [];
Expand All @@ -27,20 +168,18 @@ function sf_viz_bubble(targetId, plotData) {
for (var i = 0; i < plotData.length; i++) {
wordStr = plotData[i];
try {
{
if (typeof(wordStr) != "undefined" && wordStr.length > 2) {
wordStr = wordStr.toLowerCase();
if (typeof(wordMap[wordStr]) == "undefined") {
wordList.push(wordStr);
wordCount.push(1);
wordMap[wordStr] = wordId;
wordIdList.push(wordId);
wordId++;
} else {
wordCount[wordMap[wordStr]]++;
}
}
}
if (typeof(wordStr) != "undefined" && wordStr.length > 0) {
wordStr = wordStr.toLowerCase();
if (typeof(wordMap[wordStr]) == "undefined") {
wordList.push(wordStr);
wordCount.push(1);
wordMap[wordStr] = wordId;
wordIdList.push(wordId);
wordId++;
} else {
wordCount[wordMap[wordStr]]++;
}
}
} catch (err) {
alert("Error encountered parsing supplied words.")
}
Expand Down Expand Up @@ -77,11 +216,8 @@ function sf_viz_bubble(targetId, plotData) {
.style("fill", function(d) { return color(data[0][d.key]); })
.on("mouseover", function(d,i) {
d3.select(this).style("fill", "gold");
showToolTip(" "+data[0][i]+"<br>"+data[1][i]+" ",d.x+d3.mouse(this)[0]+50,d.y+d3.mouse(this)[1],true);
showToolTip(" "+data[0][i]+"<br>"+data[1][i]+" ",d3.event.pageX+10, d3.event.pageY+10,true);
})
.on("mousemove", function(d,i) {
tooltipDivID.css({top:d.y+d3.mouse(this)[1],left:d.x+d3.mouse(this)[0]+50});
})
.on("mouseout", function() {
d3.select(this).style("fill", function(d) { return color(data[0][d.key]); });
showToolTip(" ",0,0,false);
Expand Down

0 comments on commit 21c0ab4

Please sign in to comment.