Ver Fonte

Added terms panel, deprecated pie panel

Rashid Khan há 12 anos atrás
pai
commit
3a06457c5d
5 ficheiros alterados com 415 adições e 7 exclusões
  1. 2 1
      config.js
  2. 5 6
      panels/pie/module.js
  3. 50 0
      panels/terms/editor.html
  4. 56 0
      panels/terms/module.html
  5. 302 0
      panels/terms/module.js

+ 2 - 1
config.js

@@ -22,6 +22,7 @@ var config = new Settings(
   kibana_index:     "kibana-int", 
   modules:          ['histogram','map','pie','table','filtering',
                     'timepicker','text','fields','hits','dashcontrol',
-                    'column','derivequeries','trends','bettermap','query'],
+                    'column','derivequeries','trends','bettermap','query',
+                    'terms'],
   }
 );

+ 5 - 6
panels/pie/module.js

@@ -25,10 +25,10 @@ angular.module('kibana.pie', [])
 .controller('pie', function($scope, $rootScope, querySrv, dashboard, filterSrv) {
 
   $scope.panelMeta = {
-    status  : "Deprecating Soon",
+    status  : "Deprecated",
     description : "Uses an Elasticsearch terms facet to create a pie chart. You should really only"+
-      " point this at not_analyzed fields for that reason. This panel is going away soon, to be"+
-      " replaced with a panel that can represent a terms facet in a variety of ways."
+      " point this at not_analyzed fields for that reason. This panel is going away soon, it has"+
+      " <strong>been replaced by the terms panel</strong>. Please use that one instead."
   };
 
   // Set and populate defaults
@@ -247,11 +247,10 @@ angular.module('kibana.pie', [])
           colors: querySrv.colors
         };
 
-        // Populate element
+        // Populate legend
         if(elem.is(":visible")){
           scripts.wait(function(){
-            scope.plot = $.plot(elem, scope.data, pie);
-            scope.legend = scope.plot.getData();
+            scope.legend = $.plot(elem, scope.data, pie).getData();
             if(!scope.$$phase) {
               scope.$apply();
             }

+ 50 - 0
panels/terms/editor.html

@@ -0,0 +1,50 @@
+<div>
+  <div class="row-fluid">
+    <div class="span2">
+      <label class="small">Field</label>
+      <input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.field" ng-change="set_refresh(true)">
+    </div>
+    <div class="span2">
+      <label class="small">Length</label>
+      <input class="input-small" type="number" ng-model="panel.size" ng-change="set_refresh(true)">
+    </div>
+    <div class="span6">
+      <label class="small">Exclude Terms(s) (comma seperated)</label>
+      <input array-join type="text" ng-model='panel.exclude'></input>
+    </div>
+  </div>  
+  <div class="row-fluid">
+    <div class="span2"> 
+      <label class="small">Style</label> 
+      <select class="input-small" ng-model="panel.chart" ng-options="f for f in ['bar','pie','table']"></select></span>
+    </div>
+    <div class="span2" ng-show="panel.chart == 'table'">
+      <label class="small">Font Size</label> 
+      <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
+    </div>
+    <div class="span2" ng-show="panel.chart == 'bar' || panel.chart == 'pie'"> 
+      <label class="small">Legend</label> 
+      <select class="input-small" ng-model="panel.counter_pos" ng-options="f for f in ['above','below','none']"></select></span>
+    </div>
+    <div class="span3" ng-show="panel.chart != 'table' && panel.counter_pos != 'none'">
+      <label class="small" >Legend Format</label> 
+      <select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['horizontal','vertical']"></select></span>
+    </div>
+    <div class="span1">
+      <label class="small">Missing</label><input type="checkbox" ng-model="panel.missing" ng-checked="panel.missing">
+    </div>
+    <div class="span1">
+      <label class="small">Other</label><input type="checkbox" ng-model="panel.other" ng-checked="panel.other">
+    </div>
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Donut</label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
+    </div>
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Tilt</label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
+    </div>
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Labels</label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
+    </div>
+  </div>
+  <div class="row-fluid" ng-include="'partials/querySelect.html'"></div>
+</div>

+ 56 - 0
panels/terms/module.html

@@ -0,0 +1,56 @@
+<kibana-panel ng-controller='terms' ng-init="init()">
+
+  <!-- START Pie or bar chart -->
+  <div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+    <!-- vertical legend above -->
+    <table class="small" ng-show="panel.arrangement == 'vertical'">  
+      <tr ng-repeat="term in legend">
+        <td><i class="icon-circle" ng-style="{color:term.color}"></i></td> <td style="padding-right:10px;padding-left:10px;">{{term.label}}</td><td>{{term.data[0][1]}}</td>
+      </tr>
+    </table>
+
+    <!-- horizontal legend above -->
+    <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="term in legend" style="float:left;padding-left: 10px;">
+     <span><i class="icon-circle" ng-style="{color:term.color}"></i> {{term.label}} ({{term.data[0][1]}}) </span>
+    </div><br>
+
+  </div>
+
+  <!-- keep legend from over lapping -->
+  <div style="clear:both"></div>
+
+  <div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" terms-chart params="{{panel}}" style="position:relative"></div>
+
+  <div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+    <!-- vertical legend below -->
+    <table class="small" ng-show="panel.arrangement == 'vertical'">  
+      <tr ng-repeat="term in legend">
+        <td><i class="icon-circle" ng-style="{color:term.color}"></i></i></td> <td style="padding-right:10px;padding-left:10px;">{{term.label}}</td><td>{{term.data[0][1]}}</td>
+      </tr>
+    </table>
+
+    <!-- horizontal legend below -->
+    <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="term in legend" style="float:left;padding-left: 10px;">
+     <span><i class="icon-circle" ng-style="{color:term.color}"></i></span> {{term.label}} ({{term.data[0][1]}}) </span>
+    </div><br>
+
+  </div>
+  <!-- END Pie or Bar chart -->
+
+  <table ng-style="panel.style" class="table table-striped table-condensed" ng-show="panel.chart == 'table'">  
+    <thead>
+      <th>Term</th> <th>Count</th> <th>Action</th>
+    </thead>
+    <tr ng-repeat="term in data" ng-show="showMeta(term)">
+      <td>{{term.label}}</td>
+      <td>{{term.data[0][1]}}</td>
+      <td>
+        <span ng-hide="term.meta == 'other'">
+          <i class='icon-search pointer' ng-click="build_search(term)"></i> 
+          <i class='icon-ban-circle pointer' ng-click="build_search(term,true)"></i>
+        </span>
+      </td>
+    </tr>
+  </table>
+
+</kibana-panel>         

+ 302 - 0
panels/terms/module.js

@@ -0,0 +1,302 @@
+/*jshint globalstrict:true */
+/*global angular:true */
+
+/*
+
+  ## Terms
+
+  ### Parameters
+  * style :: A hash of css styles
+  * size :: top N
+  * arrangement :: How should I arrange the query results? 'horizontal' or 'vertical'
+  * chart :: Show a chart? 'none', 'bar', 'pie'
+  * donut :: Only applies to 'pie' charts. Punches a hole in the chart for some reason
+  * tilt :: Only 'pie' charts. Janky 3D effect. Looks terrible 90% of the time. 
+  * lables :: Only 'pie' charts. Labels on the pie?
+
+*/
+
+'use strict';
+
+angular.module('kibana.terms', [])
+.controller('terms', function($scope, querySrv, dashboard, filterSrv) {
+
+  $scope.panelMeta = {
+    status  : "Beta",
+    description : "Displays the results of an elasticsearch facet as a pie chart, bar chart, or a "+ 
+      "table"
+  };
+
+  // Set and populate defaults
+  var _d = {
+    queries     : {
+      mode        : 'all',
+      ids         : []
+    },
+    field   : '_type',
+    exclude : [],
+    missing : true,
+    other   : true,
+    size    : 10,
+    style   : { "font-size": '10pt'},
+    donut   : false,
+    tilt    : false,
+    labels  : true,
+    arrangement : 'horizontal',
+    chart       : 'bar',
+    counter_pos : 'above'
+  };
+  _.defaults($scope.panel,_d);
+
+  $scope.init = function () {
+    $scope.hits = 0;
+   
+    $scope.$on('refresh',function(){
+      $scope.get_data();
+    });
+    $scope.get_data();
+
+  };
+
+  $scope.get_data = function(segment,query_id) {
+    // Make sure we have everything for the request to complete
+    if(dashboard.indices.length === 0) {
+      return;
+    } 
+
+    $scope.panel.loading = true;
+    var request,
+      results,
+      boolQuery;
+
+    request = $scope.ejs.Request().indices(dashboard.indices);
+
+    $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
+    // This could probably be changed to a BoolFilter 
+    boolQuery = $scope.ejs.BoolQuery();
+    _.each($scope.panel.queries.ids,function(id) {
+      boolQuery = boolQuery.should(querySrv.getEjsObj(id));
+    });
+
+    // Terms mode
+    request = request
+      .facet($scope.ejs.TermsFacet('terms')
+        .field($scope.panel.field)
+        .size($scope.panel.size)
+        .exclude($scope.panel.exclude)
+        .facetFilter($scope.ejs.QueryFilter(
+          $scope.ejs.FilteredQuery(
+            boolQuery,
+            filterSrv.getBoolFilter(filterSrv.ids)
+            )))).size(0);
+
+    //$scope.populate_modal(request);
+
+    results = request.doSearch();
+
+    // Populate scope when we have results
+    results.then(function(results) {
+      var k = 0;
+      $scope.panel.loading = false;
+      $scope.hits = results.hits.total;
+      $scope.data = [];
+      _.each(results.facets.terms.terms, function(v) {
+        var slice = { label : v.term, data : [[k,v.count]], actions: true}; 
+        $scope.data.push(slice);
+        k = k + 1;
+      });
+      
+      $scope.data.push({label:'Missing field',
+        data:[[k,results.facets.terms.missing]],meta:"missing",color:'#aaa',opacity:0});
+      $scope.data.push({label:'Other values',
+        data:[[k+1,results.facets.terms.other]],meta:"other",color:'#444'});
+
+      $scope.$emit('render');
+    });
+  };
+
+  $scope.build_search = function(term,negate) {
+    if(_.isUndefined(term.meta)) {
+      filterSrv.set({type:'terms',field:$scope.panel.field,value:term.label,
+        mandate:(negate ? 'mustNot':'must')});
+    } else if(term.meta === 'missing') {
+      filterSrv.set({type:'exists',field:$scope.panel.field,
+        mandate:(negate ? 'must':'mustNot')});
+    } else {
+      return;
+    }
+    dashboard.refresh();
+  };
+
+  $scope.set_refresh = function (state) { 
+    $scope.refresh = state; 
+  };
+
+  $scope.close_edit = function() {
+    if($scope.refresh) {
+      $scope.get_data();
+    }
+    $scope.refresh =  false;
+    $scope.$emit('render');
+  };
+
+  $scope.showMeta = function(term) {
+    if(_.isUndefined(term.meta)) {
+      return true;
+    }
+    if(term.meta === 'other' && !$scope.panel.other) {
+      return false;
+    }
+    if(term.meta === 'missing' && !$scope.panel.missing) {
+      return false;
+    }
+    return true;
+  };
+
+}).directive('termsChart', function(querySrv, filterSrv, dashboard) {
+  return {
+    restrict: 'A',
+    link: function(scope, elem, attrs, ctrl) {
+
+      // Receive render events
+      scope.$on('render',function(){
+        render_panel();
+      });
+  
+      // Re-render if the window is resized
+      angular.element(window).bind('resize', function(){
+        render_panel();
+      });
+
+      // Function for rendering panel
+      function render_panel() {
+        var plot, chartData;
+        var scripts = $LAB.script("common/lib/panels/jquery.flot.js").wait()
+                          .script("common/lib/panels/jquery.flot.pie.js");
+
+        // IE doesn't work without this
+        elem.css({height:scope.panel.height||scope.row.height});
+
+        // Make a clone we can operate on.
+        chartData = _.clone(scope.data);
+        chartData = scope.panel.missing ? chartData : 
+          _.without(chartData,_.findWhere(chartData,{meta:'missing'}));
+        chartData = scope.panel.other ? chartData : 
+        _.without(chartData,_.findWhere(chartData,{meta:'other'}));
+
+        // Populate element.
+        scripts.wait(function(){
+          // Populate element
+          try {
+            // Add plot to scope so we can build out own legend 
+            if(scope.panel.chart === 'bar') {
+              plot = $.plot(elem, chartData, {
+                legend: { show: false },
+                series: {
+                  lines:  { show: false, },
+                  bars:   { show: true,  fill: 1, barWidth: 0.8, horizontal: false },
+                  shadowSize: 1
+                },
+                yaxis: { show: true, min: 0, color: "#c8c8c8" },
+                xaxis: { show: false },
+                grid: {
+                  borderWidth: 0,
+                  borderColor: '#eee',
+                  color: "#eee",
+                  hoverable: true,
+                  clickable: true
+                },
+                colors: querySrv.colors
+              });
+            }
+            if(scope.panel.chart === 'pie') {
+              var labelFormat = function(label, series){
+                return '<div ng-click="build_search(panel.field,\''+label+'\')'+
+                  ' "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+                  label+'<br/>'+Math.round(series.percent)+'%</div>';
+              };
+
+              plot = $.plot(elem, chartData, {
+                legend: { show: false },
+                series: {
+                  pie: {
+                    innerRadius: scope.panel.donut ? 0.4 : 0,
+                    tilt: scope.panel.tilt ? 0.45 : 1,
+                    radius: 1,
+                    show: true,
+                    combine: {
+                      color: '#999',
+                      label: 'The Rest'
+                    },
+                    stroke: {
+                      width: 0
+                    },
+                    label: { 
+                      show: scope.panel.labels,
+                      radius: 2/3,
+                      formatter: labelFormat,
+                      threshold: 0.1 
+                    }
+                  }
+                },
+                //grid: { hoverable: true, clickable: true },
+                grid:   { hoverable: true, clickable: true },
+                colors: querySrv.colors
+              });
+            }
+
+            // Populate legend
+            if(elem.is(":visible")){
+              scripts.wait(function(){
+                scope.legend = plot.getData();
+                if(!scope.$$phase) {
+                  scope.$apply();
+                }
+              });
+            }
+
+          } catch(e) {
+            elem.text(e);
+          }
+        });
+      }
+
+      function tt(x, y, contents) {
+        var tooltip = $('#pie-tooltip').length ? 
+          $('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
+        //var tooltip = $('#pie-tooltip')
+        tooltip.html(contents).css({
+          position: 'absolute',
+          top     : y + 5,
+          left    : x + 5,
+          color   : "#c8c8c8",
+          padding : '10px',
+          'font-size': '11pt',
+          'font-weight' : 200,
+          'background-color': '#1f1f1f',
+          'border-radius': '5px',
+        }).appendTo("body");
+      }
+
+      elem.bind("plotclick", function (event, pos, object) {
+        if(object) {
+          scope.build_search(scope.data[object.seriesIndex]);
+        }
+      });
+
+      elem.bind("plothover", function (event, pos, item) {
+        if (item) {
+          var value = scope.panel.chart === 'bar' ? 
+            item.datapoint[1] : item.datapoint[1][0][1];
+          tt(pos.pageX, pos.pageY,
+            "<div style='vertical-align:middle;border-radius:10px;display:inline-block;background:"+
+            item.series.color+";height:20px;width:20px'></div> "+item.series.label+
+            " ("+value.toFixed(0)+")");
+        } else {
+          $("#pie-tooltip").remove();
+        }
+      });
+
+    }
+  };
+});