-
-
Save petterik/5f6d95fdd3138188ae16 to your computer and use it in GitHub Desktop.
A Splunk dashboard for an Image Search demo
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> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>ma new image search | Splunk</title> | |
<link rel="shortcut icon" href="{{SPLUNKWEB_URL_PREFIX}}/static/img/favicon.ico" /> | |
<link rel="stylesheet" type="text/css" href="{{SPLUNKWEB_URL_PREFIX}}/static/css/bootstrap.min.css" /> | |
<link rel="stylesheet" type="text/css" media="all" href="{{SPLUNKWEB_URL_PREFIX}}/static/css/pages/dashboard-simple-bootstrap.min.css" /> | |
<link rel="stylesheet" type="text/css" media="all" href="{{SPLUNKWEB_URL_PREFIX}}/static/app/search/dashboard.css" /> | |
<!--[if IE 7]><link rel="stylesheet" href="{{SPLUNKWEB_URL_PREFIX}}/static/css/sprites-ie7.css" /><![endif]--> | |
</head> | |
<body class="simplexml preload"> | |
<!-- | |
BEGIN LAYOUT | |
This section contains the layout for the dashboard. Splunk uses proprietary | |
styles in <div> tags, similar to Bootstrap's grid system. | |
--> | |
<div class="header"> | |
<div id="placeholder-splunk-bar"> | |
<a href="{{SPLUNKWEB_URL_PREFIX}}/app/launcher/home" class="brand" title="splunk > listen to your data">splunk<strong>></strong></a> | |
</div> | |
<div id="placeholder-app-bar"></div> | |
</div> | |
<div class="dashboard-body container-fluid main-section-body" data-role="main"> | |
<div class="dashboard-header clearfix"> | |
<h2>ma new image search</h2> | |
</div> | |
<div class="dashboard-row dashboard-row1"> | |
<div class="dashboard-cell" style="width: 100%;"> | |
<div class="dashboard-panel clearfix"> | |
<div class="panel-element-row"> | |
<div class="dashboard-element event" id="element1" style="width: 100%"> | |
<div class="panel-body"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="footer"></div> | |
<!-- | |
END LAYOUT | |
--> | |
<script src="{{SPLUNKWEB_URL_PREFIX}}/config?autoload=1"></script> | |
<script src="{{SPLUNKWEB_URL_PREFIX}}/static/js/i18n.js"></script> | |
<script src="{{SPLUNKWEB_URL_PREFIX}}/static/js/build/simplexml.min/config.js"></script> | |
<script type="text/javascript"> | |
require.config({ | |
baseUrl: "{{SPLUNKWEB_URL_PREFIX}}/static/js", | |
waitSeconds: 0 // Disable require.js load timeout | |
}); | |
// | |
// LIBRARY REQUIREMENTS | |
// | |
// In the require function, we include the necessary libraries and modules for | |
// the HTML dashboard. Then, we pass variable names for these libraries and | |
// modules as function parameters, in order. | |
// | |
// When you add libraries or modules, remember to retain this mapping order | |
// between the library or module and its function parameter. You can do this by | |
// adding to the end of these lists, as shown in the commented examples below. | |
require([ | |
"splunkjs/mvc", | |
"splunkjs/mvc/utils", | |
"splunkjs/mvc/tokenutils", | |
"underscore", | |
"jquery", | |
"splunkjs/mvc/simplexml", | |
"splunkjs/mvc/headerview", | |
"splunkjs/mvc/footerview", | |
"splunkjs/mvc/simplexml/dashboard", | |
"splunkjs/mvc/simplexml/element/chart", | |
"splunkjs/mvc/simplexml/element/event", | |
"splunkjs/mvc/simplexml/element/html", | |
"splunkjs/mvc/simplexml/element/list", | |
"splunkjs/mvc/simplexml/element/map", | |
"splunkjs/mvc/simplexml/element/single", | |
"splunkjs/mvc/simplexml/element/table", | |
"splunkjs/mvc/simpleform/input/dropdown", | |
"splunkjs/mvc/simpleform/input/radiogroup", | |
"splunkjs/mvc/simpleform/input/text", | |
"splunkjs/mvc/simpleform/input/timerange", | |
"splunkjs/mvc/simpleform/input/submit", | |
"splunkjs/mvc/searchmanager", | |
"splunkjs/mvc/savedsearchmanager", | |
"splunkjs/mvc/postprocessmanager", | |
"splunkjs/mvc/simplexml/urltokenmodel" | |
// Add comma-separated libraries and modules manually here, for example: | |
// ..."splunkjs/mvc/simplexml/urltokenmodel", | |
// "splunkjs/mvc/checkboxview" | |
], | |
function( | |
mvc, | |
utils, | |
TokenUtils, | |
_, | |
$, | |
DashboardController, | |
HeaderView, | |
FooterView, | |
Dashboard, | |
ChartElement, | |
EventElement, | |
HtmlElement, | |
ListElement, | |
MapElement, | |
SingleElement, | |
TableElement, | |
DropdownInput, | |
RadioGroupInput, | |
TextInput, | |
TimeRangeInput, | |
SubmitButton, | |
SearchManager, | |
SavedSearchManager, | |
PostProcessManager, | |
UrlTokenModel | |
// Add comma-separated parameter names here, for example: | |
// ...UrlTokenModel, | |
// CheckboxView | |
) { | |
// AUTO-COMPILED JAVASCRIPT | |
// | |
// Import required dependencies | |
// | |
var DashboardController = require("splunkjs/mvc/simplexml/controller"); | |
var HeaderView = require("splunkjs/mvc/headerview"); | |
var FooterView = require("splunkjs/mvc/footerview"); | |
var Dashboard = require("splunkjs/mvc/simplexml/dashboard"); | |
var ChartElement = require("splunkjs/mvc/simplexml/element/chart"); | |
var EventElement = require("splunkjs/mvc/simplexml/element/event"); | |
var HtmlElement = require("splunkjs/mvc/simplexml/element/html"); | |
var ListElement = require("splunkjs/mvc/simplexml/element/list"); | |
var MapElement = require("splunkjs/mvc/simplexml/element/map"); | |
var SingleElement = require("splunkjs/mvc/simplexml/element/single"); | |
var TableElement = require("splunkjs/mvc/simplexml/element/table"); | |
var SearchManager = require("splunkjs/mvc/searchmanager"); | |
var SavedSearchManager = require("splunkjs/mvc/savedsearchmanager"); | |
var UrlTokenModel = require("splunkjs/mvc/simplexml/urltokenmodel"); | |
var SimpleSplunkView = require("splunkjs/mvc/simplesplunkview"); | |
// | |
// Create form token namespaces | |
// | |
var defaultTokenModel = mvc.Components.getInstance('default', {create: true}); | |
var submittedTokenModel = new UrlTokenModel(); | |
mvc.Components.registerInstance('submitted', submittedTokenModel); | |
submittedTokenModel.on('url:navigate', function() { | |
submittedTokenModel.disableAutosave(); | |
defaultTokenModel.set(submittedTokenModel.toJSON()); | |
submittedTokenModel.enableAutosave(); | |
}); | |
// Initialize non-input tokens | |
defaultTokenModel.set(submittedTokenModel.toJSON()); | |
var submitTokens = _.debounce(function() { | |
// Submit form, even if no values have changed since the last submit. | |
submittedTokenModel.set(defaultTokenModel.toJSON()); | |
}); | |
// | |
// Create searches | |
// | |
function getKey(e, k) { | |
return e.context.attributes.getNamedItem(k).nodeValue; | |
} | |
function createSearch(id) { | |
var search = "index=images AND colors"; | |
var spath = ""; | |
var sumString = "score="; | |
var checkedBuckets = 0; | |
$(".bucket").each(function(i) { | |
var self = $(this); | |
if (self.attr("checked")) { | |
checkedBuckets += 1; | |
var colorId = "colors{" + getKey(self, "b") + "}{" + getKey(self,"s") + "}{" + getKey(self, "h") + "}"; | |
spath += " | spath "; | |
var color = "color" + i; | |
spath += "path=" + colorId + " output=" + color; | |
sumString += "(1 + " + color + ")*"; | |
} | |
}); | |
sumString += "1"; | |
var n = checkedBuckets; | |
var scorePctEval = "score_pct=if(" + n + "<2, score, 100*(log(score," + n + "))/log(pow((floor(100/" + n + "))+1," + n + ")," + n + "))"; | |
search += spath; | |
search += " | eval " + sumString; | |
search += " | eval " + scorePctEval; | |
search += " | stats sum(score_pct) as relevance by image, source"; | |
search += " | sort -relevance" | |
console.log("da search: " + search); | |
return new SearchManager({ | |
"id": "search" + id, | |
"status_buckets": 0, | |
"earliest_time": "$earliest$", | |
"search": search, | |
"latest_time": "$latest$", | |
"cancelOnUnload": true, | |
"app": utils.getCurrentApp(), | |
"auto_cancel": 90, | |
"autostart":false, | |
"preview": true | |
}, {tokens: true, tokenNamespace: "submitted"}); | |
} | |
// | |
// Create header and footer Splunk UI | |
// | |
new HeaderView({ | |
id: 'header', | |
section: 'dashboards', | |
el: $('.header'), | |
acceleratedAppNav: true | |
}, {tokens: true}).render(); | |
new FooterView({ | |
id: 'footer', | |
el: $('.footer') | |
}, {tokens: true}).render(); | |
new Dashboard({ | |
id: 'dashboard', | |
el: $('.dashboard-body') | |
}, {tokens: true}).render(); | |
// | |
// Create components | |
// | |
function createElement(id) { | |
return new MySplunkView({ | |
"id": "element" + id, | |
"resizable": true, | |
"managerid": "search" + id, | |
"el": $("#element" + id) | |
}, {tokens: true}); | |
} | |
DashboardController.ready(); | |
submitTokens(); | |
// END OF AUTO-COMPILED JAVASCRIPT | |
// HSB -> RGB conversion | |
function hsv2rgb(h,s,v) { | |
// Adapted from http://www.easyrgb.com/math.html | |
// hsv values = 0 - 1, rgb values = 0 - 255 | |
var r, g, b; | |
var RGB = new Array(); | |
if(s==0){ | |
RGB['red']=RGB['green']=RGB['blue']=Math.round(v*255); | |
}else{ | |
// h must be < 1 | |
var var_h = h * 6; | |
if (var_h==6) var_h = 0; | |
//Or ... var_i = floor( var_h ) | |
var var_i = Math.floor( var_h ); | |
var var_1 = v*(1-s); | |
var var_2 = v*(1-s*(var_h-var_i)); | |
var var_3 = v*(1-s*(1-(var_h-var_i))); | |
if(var_i==0){ | |
var_r = v; | |
var_g = var_3; | |
var_b = var_1; | |
}else if(var_i==1){ | |
var_r = var_2; | |
var_g = v; | |
var_b = var_1; | |
}else if(var_i==2){ | |
var_r = var_1; | |
var_g = v; | |
var_b = var_3 | |
}else if(var_i==3){ | |
var_r = var_1; | |
var_g = var_2; | |
var_b = v; | |
}else if (var_i==4){ | |
var_r = var_3; | |
var_g = var_1; | |
var_b = v; | |
}else{ | |
var_r = v; | |
var_g = var_1; | |
var_b = var_2 | |
} | |
//rgb results = 0 ÷ 255 | |
RGB['red']=Math.round(var_r * 255); | |
RGB['green']=Math.round(var_g * 255); | |
RGB['blue']=Math.round(var_b * 255); | |
} | |
return RGB; | |
}; | |
// START OWN STUFF | |
var hBuckets = 10; | |
var sBuckets = 3; | |
var bBuckets = 2; | |
var checkBoxesDiv = $("<div/>", { | |
class: 'row span11', | |
id: 'checkBoxes' | |
}); | |
var loop = 0; | |
for (var i = bBuckets-1; i >= 0; i--) { | |
loop++; | |
for (var j = (loop == 1? 0 : sBuckets-1); | |
(loop == 1 && j < sBuckets) || (loop == 2 && j >= 0); | |
loop == 1 ? j++ : j--) { | |
for (var k = 0; k < hBuckets; k++) { | |
var hue = (1/(1.1*hBuckets)) + (k == 0? 0 : (1/hBuckets)*k); | |
var sat = (1/(1.1*sBuckets)) + (j == 0? 0 : (1/sBuckets)*j); | |
var bri = (1/(1.1*bBuckets)) + (i == 0? 0 : (1/bBuckets)*i); | |
var rgb = hsv2rgb(hue, sat, bri); | |
var checkBoxWrap = $("<div/>", { | |
class: "span1", | |
style: "background-color: rgb(" + rgb['red'] + "," + rgb['green'] + "," + rgb['blue'] + ")" | |
}); | |
var checkBox = $("<input/>", { | |
type: "checkbox", | |
class: "bucket", | |
h: k, | |
s: j, | |
b: i | |
}); | |
checkBoxWrap.append(checkBox); | |
checkBoxesDiv.append(checkBoxWrap); | |
} | |
} | |
} | |
var dashBody = $('.dashboard-row'); | |
dashBody.prepend(checkBoxesDiv); | |
// search | |
var searchButton = $("<button/>", { | |
class: "btn btn-large btn-primary", | |
type: "button", | |
text: "Search!", | |
}); | |
var currentSearchId = 0; | |
var search = null; | |
var element = null; | |
$(searchButton).click(function() { | |
if (element != null) { | |
element.off(); | |
} | |
if (search != null) { | |
search.cancel(); | |
search.finalize(); | |
search.off(); | |
} | |
$("#element" + currentSearchId).remove(); | |
currentSearchId++; | |
var searchId = currentSearchId; | |
search = createSearch(searchId); | |
attachNewElement(searchId); | |
element = createElement(searchId); | |
element.render(); | |
search.startSearch(); | |
}); | |
var resetButton = $("<button/>", { | |
class: "btn btn-small btn-default", | |
type: "button", | |
text: "Reset selection", | |
}); | |
$(resetButton).click(function() { | |
$('#checkBoxes').find('input[type=checkbox]:checked').removeAttr('checked'); | |
}); | |
dashBody.append(searchButton); | |
dashBody.append(resetButton); | |
function attachNewElement(id) { | |
var elementDiv = $("<div/>", { | |
class: "dashboard-element chart", | |
id: "element" + id, | |
style: "width: 100%" | |
}); | |
$("<div/>", { | |
class: "panel-body" | |
}).appendTo(elementDiv); | |
$(".panel-element-row").append(elementDiv); | |
} | |
// My own view | |
var MySplunkView = SimpleSplunkView.extend({ | |
className: "my-splunk-view", | |
outputMode: "json", | |
n_images: 6, | |
height: 800, | |
visible: false, | |
cache: {}, | |
createView: function() { | |
console.log("In createView:"); | |
$(".splunk-message-container").remove(); | |
this.setSize(); | |
for (var i = 0; i < this.n_images; i++) { | |
this.createImageContainer(); | |
} | |
return this; | |
}, | |
createImageContainer: function() { | |
$("<div/>", { | |
style: "position: relative; width: 400px; height: 400px; float: left;" | |
}).appendTo(this.$el.selector); | |
}, | |
formatData: function(data) { | |
return data; | |
}, | |
updateView: function(viz, data) { | |
this.visible = true; | |
this.setSize(); | |
if (this._isJobDone) | |
console.log("Job is done!"); | |
console.log("In updateView:"); | |
console.log(data); | |
var currentImages = {}; | |
for (var i = 0; i < this.n_images && i < data.length; i++) { | |
fetchImage(i, data, viz, this.cache); | |
currentImages[getImageKey(data, i)] = true; | |
} | |
for (var key in this.cache) { | |
if (!(key in currentImages)) { | |
delete this.cache[key]; | |
} | |
} | |
}, | |
setSize: function() { | |
$(this.$el.selector).css({ | |
width: "1200px", | |
height: this.height + "px" | |
}); | |
}, | |
grow: function() { | |
if(this.visible && this._data.length > this.n_images) { | |
this.height += 400; | |
var increment = Math.min(3, this._data.length - this.n_images); | |
this.n_images += increment; | |
for (var i = 0; i < increment; i++) { | |
this.createImageContainer(); | |
} | |
this.render(); | |
} | |
} | |
}); | |
function getImageKey(data, i) { | |
return data[i].source + data[i].image; | |
} | |
function fetchImage(i, data, viz, cache) { | |
var imageKey = getImageKey(data, i); | |
var itsDiv = $('.my-splunk-view').children()[i]; | |
if ($(itsDiv).attr("imageKey") === imageKey) { | |
return; | |
} else { | |
$(itsDiv).attr("imageKey", imageKey); | |
} | |
var matchSplit = data[i].relevance.split("."); | |
var match = ""; | |
if (matchSplit.length == 1) { | |
match = matchSplit[0]; | |
} else { | |
match = matchSplit[0] + "." + matchSplit[1].substring(0, 1); | |
} | |
if (imageKey in cache) { | |
populateImageDiv(cache[imageKey], match, itsDiv); | |
} else { | |
$.ajax({ | |
url: "http://localhost:5123/image", | |
cache: false, | |
type: "GET", | |
data: { | |
path: data[i].source, | |
filename: data[i].image | |
}, | |
success: function(image) { | |
if ($(itsDiv).attr("imageKey") === imageKey) { | |
cache[imageKey] = image; | |
populateImageDiv(image, match, itsDiv); | |
} | |
}, | |
error: function(x) { | |
alert("Error: " + x); | |
}}); | |
} | |
} | |
function populateImageDiv(image, match, div) { | |
var theDiv = $(div); | |
theDiv.empty(); | |
var img = $("<img/>", { | |
width: "400", | |
height: "400", | |
style: "float: left; width: 400px; height: 400px;", | |
src: "data:image/jpg;base64," + image | |
}); | |
img.appendTo(theDiv); | |
img.load(function() { | |
}); | |
var h2style = "position: absolute; top: 280px; left: 0; width: 380px;"; | |
h2style += " color: white; font: bold 24px/45px Helvetica, Sans-Serif;"; | |
h2style += " letter-spacing: -1px; background: rgb(0, 0, 0);"; | |
h2style += " background: rgba(0, 0, 0, 0.7); padding: 10px;"; | |
$("<h2/>", { | |
style: h2style, | |
text: "Relevance: " + match + "" | |
}).appendTo(theDiv); | |
} | |
// Scrolling stuff | |
function isAtTheBottom() { | |
return $(window).scrollTop() == $(document).height() - $(window).height(); | |
} | |
$(window).scroll(function() { | |
if(isAtTheBottom() && element != null) { | |
element.grow(); | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment