Skip to content

Commit

Permalink
Merge pull request #1176 from rashidkpc/master
Browse files Browse the repository at this point in the history
Stats panel and stats adhoc facet for table
  • Loading branch information
Rashid Khan committed Apr 23, 2014
2 parents fd553f5 + 3924178 commit da661d7
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 40 deletions.
3 changes: 2 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"globals": {
"define": true,
"require": true,
"Chromath": false
"Chromath": false,
"jsonPath": false
}
}
7 changes: 6 additions & 1 deletion src/app/components/require.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ require.config({


modernizr: '../vendor/modernizr-2.6.1',
numeral: '../vendor/numeral',
numeral: '../vendor/numeral',
jsonpath: '../vendor/jsonpath',
elasticjs: '../vendor/elasticjs/elastic-angular-client',
},
shim: {
Expand All @@ -62,6 +63,10 @@ require.config({
exports: 'Modernizr'
},

jsonpath: {
exports: 'jsonPath'
},

jquery: {
exports: 'jQuery'
},
Expand Down
16 changes: 9 additions & 7 deletions src/app/panels/stats/editor.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div class="row-fluid">
<h5>Details</h5>
<div class="editor-option">
<label class="small">Function</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
<label class="small">Featured Stat</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in modes"></select>
</div>
<div class="editor-option">
<label class="small">Field <tip>This field must contain a numeric value</tip></label>
Expand All @@ -12,6 +12,12 @@ <h5>Details</h5>
<label class="small">Unit</label>
<input type="text" class="input-large" ng-model="panel.unit">
</div>

<h5>Columns</h5>
<div class="editor-option" ng-repeat="stat in modes">
<label class="small">{{stat}}</label><input type="checkbox" ng-model="panel.show[stat]" ng-checked="panel.show[stat]">
</div>

<h5>Formating</h5>
<div class="editor-option">
<label class="small">Format</label>
Expand All @@ -26,11 +32,7 @@ <h5>Formating</h5>
<select class="input-mini" ng-model="panel.display_breakdown" ng-options="f for f in ['yes', 'no']"></select></span>
</div>
<div class="editor-option">
<label class="small">Label column name</label>
<label class="small">Query column name</label>
<input type="text" class="input-large" ng-model="panel.label_name">
</div>
<div class="editor-option">
<label class="small">Value column name</label>
<input type="text" class="input-large" ng-model="panel.value_name">
</div>
</div>
38 changes: 30 additions & 8 deletions src/app/panels/stats/module.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
<div ng-controller="stats" ng-init="init()">
<h1 ng-style="panel.style" style="text-align: center; line-height: .6em">{{data.value|formatstats:panel.format}} <small style="font-size: .5em; line-height: 0;">{{panel.unit}}</small></h1>
<table ng-show="panel.display_breakdown == 'yes'" cellspacing="0" class="table-hover table table-condensed" style="margin-top: 38px;">
<style>
table.stats-table th, table.stats-table td {
text-align: right;
}

table.stats-table th:first-child, table.stats-table td:first-child {
text-align: left !important;
}


</style>

<h1 ng-style="panel.style" style="text-align: center; line-height: .6em">{{data.value|formatstats:panel.format}} <small style="font-size: .5em; line-height: 0;">{{panel.unit}} ({{panel.mode}})</small></h1>
<table ng-show="panel.display_breakdown == 'yes'" cellspacing="0" class="table-hover table table-condensed stats-table" style="margin-top: 38px;">
<tbody>
<tr>
<th><a href="" ng-click="set_sort('label')" ng-class="{'icon-chevron-down': panel.sort_field == 'label' && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == 'label' && panel.sort_reverse == false}"> {{panel.label_name}} </a></th>
<th style="text-align: right;"><a href="" ng-click="set_sort('value')" ng-class="{'icon-chevron-down': panel.sort_field == 'value' && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == 'value' && panel.sort_reverse == false}"> {{panel.value_name}} </a></th>
</tr>
<tr ng-repeat="item in data.rows | orderBy:panel.sort_field:panel.sort_reverse">
<thead>
<tr>
<th><a href="" ng-click="set_sort('label')" ng-class="{'icon-chevron-down': panel.sort_field == 'label' && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == 'label' && panel.sort_reverse == false}"> {{panel.label_name}} </a></th>
<th ng-repeat="stat in modes" ng-show="panel.show[stat]">
<a href=""
ng-click="set_sort(stat)"
ng-class="{'icon-chevron-down': panel.sort_field == stat && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == stat && panel.sort_reverse == false}">
{{stat}}
</a>
</th>
</tr>
</thead>
<tr ng-repeat="item in data.rows | orderBy:(panel.sort_field == 'label' ? 'label' : 'value.'+panel.sort_field):panel.sort_reverse">
<td><i class="icon-circle" ng-style="{color:item.color}"></i> {{item.label}}</td>
<td style="text-align: right;">{{item.value|formatstats:panel.format}} {{panel.unit}}</td>
<td ng-repeat="stat in modes" ng-show="panel.show[stat]">{{item.value[stat]|formatstats:panel.format}} {{panel.unit}}</td>

<!--<td style="text-align: right;">{{item.value|formatstats:panel.format}} {{panel.unit}}</td>-->
</tr>
</tbody>
</table>
Expand Down
20 changes: 17 additions & 3 deletions src/app/panels/stats/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ define([
description: 'A statistical panel for displaying aggregations using the Elastic Search statistical facet query.'
};

$scope.modes = ['count','min','max','mean','total','variance','std_deviation','sum_of_squares'];

var defaults = {
queries : {
Expand All @@ -62,7 +63,17 @@ define([
sort_reverse: false,
label_name: 'Query',
value_name: 'Value',
spyable : true
spyable : true,
show: {
count: true,
min: true,
max: true,
mean: true,
std_deviation: true,
sum_of_squares: true,
total: true,
variance: true
}
};

_.defaults($scope.panel, defaults);
Expand All @@ -76,6 +87,7 @@ define([
};

$scope.set_sort = function(field) {
console.log(field);
if($scope.panel.sort_field === field && $scope.panel.sort_reverse === false) {
$scope.panel.sort_reverse = true;
} else if($scope.panel.sort_field === field && $scope.panel.sort_reverse === true) {
Expand Down Expand Up @@ -149,8 +161,8 @@ define([
var obj = _.clone(q);
obj.label = alias;
obj.Label = alias.toLowerCase(); //sort field
obj.value = results.facets['stats_'+alias][$scope.panel.mode];
obj.Value = results.facets['stats_'+alias][$scope.panel.mode]; //sort field
obj.value = results.facets['stats_'+alias];
obj.Value = results.facets['stats_'+alias]; //sort field
return obj;
});

Expand All @@ -159,6 +171,8 @@ define([
rows: rows
};

console.log($scope.data);

$scope.$emit('render');
});
};
Expand Down
7 changes: 6 additions & 1 deletion src/app/panels/table/micropanel.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<span>
<i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'must');dismiss();" bs-tooltip="'Find events with this field'"></i>
<i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'mustNot');dismiss();" bs-tooltip="'Find events without this field'"></i>
<strong>Micro Analysis of {{micropanel.field}}</strong>
<strong>Micro Analysis of {{micropanel.field}} <span ng-if='micropanel.type'>({{micropanel.type}})</span></strong>
<span ng-show="micropanel.hasArrays">
as
<a class="link" ng-class="{'strong':micropanel.grouped}" ng-click="toggle_micropanel(micropanel.field,true)">Groups</a> /
Expand Down Expand Up @@ -59,6 +59,11 @@
<li><a ng-click="termsModal(field,'table');dismiss();">Table</a></li>
</ul>
</div>
<div class="btn-group" ng-show="micropanel.hasStats">
<a class="btn dropdown-toggle pointer" ng-click="statsModal(field);dismiss();">
<i class="icon-list-ol"></i> Stats
</a>
</div>

</div>
</div>
2 changes: 1 addition & 1 deletion src/app/panels/table/module.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@

<h4>
<button class="btn btn-mini btn-danger" ng-click="closeFacet();">close</button>
Top 10 terms in field {{modalField}}
{{adhocOpts.title}}
<span class="pointer ng-scope ng-pristine ng-valid ui-draggable" bs-tooltip="'Drag to add to dashboard'"
data-drag="true"
data-jqyoui-options="kbnJqUiDraggableOptions"
Expand Down
53 changes: 35 additions & 18 deletions src/app/panels/table/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ define([
'lodash',
'kbn',
'moment',
'jsonpath'
],
function (angular, app, _, kbn, moment) {
'use strict';
Expand Down Expand Up @@ -181,31 +182,27 @@ function (angular, app, _, kbn, moment) {
};

$scope.statsModal = function(field) {
$scope.modalField = field;
showModal(
'{"field":"'+field+'"}','statistics');
$scope.closeFacet();
$timeout(function() {
$scope.modalField = field;
$scope.adhocOpts = {
height: "200px",
field: field,
mode: 'mean',
span: $scope.panel.span,
type: 'stats',
title: 'Statistics for ' + field
};
showModal(
angular.toJson($scope.adhocOpts),'stats');
},0);
};

var showModal = function(panel,type) {
$scope.facetPanel = panel;
$scope.facetType = type;

// create a new modal. Can't reuse one modal unforunately as the directive will not
// re-render on show.
/*
$modal({
template: './app/panels/table/modal.html',
persist: false,
show: true,
scope: $scope.$new(),
keyboard: false
});
*/

};



$scope.toggle_micropanel = function(field,groups) {
var docs = _.map($scope.data,function(_d){return _d.kibana._source;});
var topFieldValues = kbn.top_field_values(docs,field,10,groups);
Expand All @@ -218,6 +215,26 @@ function (angular, app, _, kbn, moment) {
limit: 10,
count: _.countBy(docs,function(doc){return _.contains(_.keys(doc),field);})['true']
};


var nodeInfo = $scope.ejs.client.get('/' + dashboard.indices + '/_mapping/field/' + field,
undefined, undefined, function(data, status) {
console.log(status);
return;
});

return nodeInfo.then(function(p) {
var types = _.uniq(jsonPath(p, '*.*.*.*.mapping.*.type'));
if (_.isArray(types)) {
$scope.micropanel.type = types.join(', ');
}


if(_.intersection(types, ['long','float','integer','double']).length > 0) {
$scope.micropanel.hasStats = true;
}
});

};

$scope.micropanelColor = function(index) {
Expand Down
87 changes: 87 additions & 0 deletions src/vendor/jsonpath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* JSONPath 0.8.0 - XPath for JSON
*
* Copyright (c) 2007 Stefan Goessner (goessner.net)
* Licensed under the MIT (MIT-LICENSE.txt) licence.
*/
function jsonPath(obj, expr, arg) {
var P = {
resultType: arg && arg.resultType || "VALUE",
result: [],
normalize: function(expr) {
var subx = [];
return expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0,$1){return "[#"+(subx.push($1)-1)+"]";})
.replace(/'?\.'?|\['?/g, ";")
.replace(/;;;|;;/g, ";..;")
.replace(/;$|'?\]|'$/g, "")
.replace(/#([0-9]+)/g, function($0,$1){return subx[$1];});
},
asPath: function(path) {
var x = path.split(";"), p = "$";
for (var i=1,n=x.length; i<n; i++)
p += /^[0-9*]+$/.test(x[i]) ? ("["+x[i]+"]") : ("['"+x[i]+"']");
return p;
},
store: function(p, v) {
if (p) P.result[P.result.length] = P.resultType == "PATH" ? P.asPath(p) : v;
return !!p;
},
trace: function(expr, val, path) {
if (expr) {
var x = expr.split(";"), loc = x.shift();
x = x.join(";");
if (val && val.hasOwnProperty(loc))
P.trace(x, val[loc], path + ";" + loc);
else if (loc === "*")
P.walk(loc, x, val, path, function(m,l,x,v,p) { P.trace(m+";"+x,v,p); });
else if (loc === "..") {
P.trace(x, val, path);
P.walk(loc, x, val, path, function(m,l,x,v,p) { typeof v[m] === "object" && P.trace("..;"+x,v[m],p+";"+m); });
}
else if (/,/.test(loc)) { // [name1,name2,...]
for (var s=loc.split(/'?,'?/),i=0,n=s.length; i<n; i++)
P.trace(s[i]+";"+x, val, path);
}
else if (/^\(.*?\)$/.test(loc)) // [(expr)]
P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(";")+1))+";"+x, val, path);
else if (/^\?\(.*?\)$/.test(loc)) // [?(expr)]
P.walk(loc, x, val, path, function(m,l,x,v,p) { if (P.eval(l.replace(/^\?\((.*?)\)$/,"$1"),v[m],m)) P.trace(m+";"+x,v,p); });
else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step] phyton slice syntax
P.slice(loc, x, val, path);
}
else
P.store(path, val);
},
walk: function(loc, expr, val, path, f) {
if (val instanceof Array) {
for (var i=0,n=val.length; i<n; i++)
if (i in val)
f(i,loc,expr,val,path);
}
else if (typeof val === "object") {
for (var m in val)
if (val.hasOwnProperty(m))
f(m,loc,expr,val,path);
}
},
slice: function(loc, expr, val, path) {
if (val instanceof Array) {
var len=val.length, start=0, end=len, step=1;
loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function($0,$1,$2,$3){start=parseInt($1||start);end=parseInt($2||end);step=parseInt($3||step);});
start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
for (var i=start; i<end; i+=step)
P.trace(i+";"+expr, val, path);
}
},
eval: function(x, _v, _vname) {
try { return $ && _v && eval(x.replace(/@/g, "_v")); }
catch(e) { throw new SyntaxError("jsonPath: " + e.message + ": " + x.replace(/@/g, "_v").replace(/\^/g, "_a")); }
}
};

var $ = obj;
if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) {
P.trace(P.normalize(expr).replace(/^\$;/,""), obj, "$");
return P.result.length ? P.result : false;
}
}

0 comments on commit da661d7

Please sign in to comment.