module.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. ## Pie
  3. ### Parameters
  4. * query :: An object with 2 possible parameters depends on the mode:
  5. ** field: Fields to run a terms facet on. Only does anything in terms mode
  6. ** goal: How many to shoot for, only does anything in goal mode
  7. * exclude :: In terms mode, ignore these terms
  8. * donut :: Drill a big hole in the pie
  9. * tilt :: A janky 3D representation of the pie. Looks terrible 90% of the time.
  10. * legend :: Show the legend?
  11. * labels :: Label the slices of the pie?
  12. * mode :: 'terms' or 'goal'
  13. * default_field :: LOL wat? A dumb fail over field if for some reason the query object
  14. doesn't have a field
  15. * spyable :: Show the 'eye' icon that displays the last ES query for this panel
  16. */
  17. define([
  18. 'angular',
  19. 'app',
  20. 'underscore',
  21. 'jquery',
  22. 'kbn',
  23. 'config'
  24. ], function (angular, app, _, $, kbn) {
  25. 'use strict';
  26. var module = angular.module('kibana.panels.pie', []);
  27. app.useModule(module);
  28. module.controller('pie', function($scope, $rootScope, querySrv, dashboard, filterSrv) {
  29. $scope.panelMeta = {
  30. editorTabs : [
  31. {title:'Queries', src:'app/partials/querySelect.html'}
  32. ],
  33. modals : [
  34. {
  35. description: "Inspect",
  36. icon: "icon-info-sign",
  37. partial: "app/partials/inspector.html",
  38. show: $scope.panel.spyable
  39. }
  40. ],
  41. status : "Deprecated",
  42. description : "Uses an Elasticsearch terms facet to create a pie chart. You should really only"+
  43. " point this at not_analyzed fields for that reason. This panel is going away soon, it has"+
  44. " <strong>been replaced by the terms panel</strong>. Please use that one instead."
  45. };
  46. // Set and populate defaults
  47. var _d = {
  48. query : { field:"_type", goal: 100},
  49. queries : {
  50. mode : 'all',
  51. ids : []
  52. },
  53. size : 10,
  54. exclude : [],
  55. donut : false,
  56. tilt : false,
  57. legend : "above",
  58. labels : true,
  59. mode : "terms",
  60. default_field : 'DEFAULT',
  61. spyable : true,
  62. };
  63. _.defaults($scope.panel,_d);
  64. $scope.init = function() {
  65. $scope.$on('refresh',function(){$scope.get_data();});
  66. $scope.get_data();
  67. };
  68. $scope.set_mode = function(mode) {
  69. switch(mode)
  70. {
  71. case 'terms':
  72. $scope.panel.query = {field:"_all"};
  73. break;
  74. case 'goal':
  75. $scope.panel.query = {goal:100};
  76. break;
  77. }
  78. };
  79. $scope.set_refresh = function (state) {
  80. $scope.refresh = state;
  81. };
  82. $scope.close_edit = function() {
  83. if($scope.refresh) {
  84. $scope.get_data();
  85. }
  86. $scope.refresh = false;
  87. $scope.$emit('render');
  88. };
  89. $scope.get_data = function() {
  90. // Make sure we have everything for the request to complete
  91. if(dashboard.indices.length === 0) {
  92. return;
  93. }
  94. $scope.panelMeta.loading = true;
  95. var request = $scope.ejs.Request().indices(dashboard.indices);
  96. $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
  97. var queries = querySrv.getQueryObjs($scope.panel.queries.ids);
  98. // This could probably be changed to a BoolFilter
  99. var boolQuery = $scope.ejs.BoolQuery();
  100. _.each(queries,function(q) {
  101. boolQuery = boolQuery.should(querySrv.toEjsObj(q));
  102. });
  103. var results;
  104. // Terms mode
  105. if ($scope.panel.mode === "terms") {
  106. request = request
  107. .facet($scope.ejs.TermsFacet('pie')
  108. .field($scope.panel.query.field || $scope.panel.default_field)
  109. .size($scope.panel.size)
  110. .exclude($scope.panel.exclude)
  111. .facetFilter($scope.ejs.QueryFilter(
  112. $scope.ejs.FilteredQuery(
  113. boolQuery,
  114. filterSrv.getBoolFilter(filterSrv.ids)
  115. )))).size(0);
  116. $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
  117. results = request.doSearch();
  118. // Populate scope when we have results
  119. results.then(function(results) {
  120. $scope.panelMeta.loading = false;
  121. $scope.hits = results.hits.total;
  122. $scope.data = [];
  123. var k = 0;
  124. _.each(results.facets.pie.terms, function(v) {
  125. var slice = { label : v.term, data : v.count };
  126. $scope.data.push();
  127. $scope.data.push(slice);
  128. k = k + 1;
  129. });
  130. $scope.$emit('render');
  131. });
  132. // Goal mode
  133. } else {
  134. request = request
  135. .query(boolQuery)
  136. .filter(filterSrv.getBoolFilter(filterSrv.ids))
  137. .size(0);
  138. $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
  139. results = request.doSearch();
  140. results.then(function(results) {
  141. $scope.panelMeta.loading = false;
  142. var complete = results.hits.total;
  143. var remaining = $scope.panel.query.goal - complete;
  144. $scope.data = [
  145. { label : 'Complete', data : complete, color: '#BF6730' },
  146. { data : remaining, color: '#e2d0c4' }
  147. ];
  148. $scope.$emit('render');
  149. });
  150. }
  151. };
  152. });
  153. module.directive('pie', function(querySrv, filterSrv) {
  154. return {
  155. restrict: 'A',
  156. link: function(scope, elem) {
  157. elem.html('<center><img src="img/load_big.gif"></center>');
  158. // Receive render events
  159. scope.$on('render',function(){
  160. render_panel();
  161. });
  162. // Or if the window is resized
  163. angular.element(window).bind('resize', function(){
  164. render_panel();
  165. });
  166. // Function for rendering panel
  167. function render_panel() {
  168. // IE doesn't work without this
  169. elem.css({height:scope.panel.height||scope.row.height});
  170. var label;
  171. if(scope.panel.mode === 'goal') {
  172. label = {
  173. show: scope.panel.labels,
  174. radius: 0,
  175. formatter: function(label, series){
  176. var font = parseInt(scope.row.height.replace('px',''),10)/8 + String('px');
  177. if(!(_.isUndefined(label))) {
  178. return '<div style="font-size:'+font+';font-weight:bold;text-align:center;padding:2px;color:#fff;">'+
  179. Math.round(series.percent)+'%</div>';
  180. } else {
  181. return '';
  182. }
  183. },
  184. };
  185. } else {
  186. label = {
  187. show: scope.panel.labels,
  188. radius: 2/3,
  189. formatter: function(label, series){
  190. return '<div "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
  191. label+'<br/>'+Math.round(series.percent)+'%</div>';
  192. },
  193. threshold: 0.1
  194. };
  195. }
  196. var pie = {
  197. series: {
  198. pie: {
  199. innerRadius: scope.panel.donut ? 0.45 : 0,
  200. tilt: scope.panel.tilt ? 0.45 : 1,
  201. radius: 1,
  202. show: true,
  203. combine: {
  204. color: '#999',
  205. label: 'The Rest'
  206. },
  207. label: label,
  208. stroke: {
  209. width: 0
  210. }
  211. }
  212. },
  213. //grid: { hoverable: true, clickable: true },
  214. grid: {
  215. backgroundColor: null,
  216. hoverable: true,
  217. clickable: true
  218. },
  219. legend: { show: false },
  220. colors: querySrv.colors
  221. };
  222. // Populate legend
  223. if(elem.is(":visible")){
  224. require(['jquery.flot.pie'], function(){
  225. scope.legend = $.plot(elem, scope.data, pie).getData();
  226. if(!scope.$$phase) {
  227. scope.$apply();
  228. }
  229. });
  230. }
  231. }
  232. elem.bind('plotclick', function (event, pos, object) {
  233. if (!object) {
  234. return;
  235. }
  236. if(scope.panel.mode === 'terms') {
  237. filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label});
  238. }
  239. });
  240. var $tooltip = $('<div>');
  241. elem.bind('plothover', function (event, pos, item) {
  242. if (item) {
  243. $tooltip
  244. .html([
  245. kbn.query_color_dot(item.series.color, 15),
  246. (item.series.label || ''),
  247. parseFloat(item.series.percent).toFixed(1) + '%'
  248. ].join(' '))
  249. .place_tt(pos.pageX, pos.pageY, {
  250. offset: 10
  251. });
  252. } else {
  253. $tooltip.remove();
  254. }
  255. });
  256. }
  257. };
  258. });
  259. });