Просмотр исходного кода

SingleStatPanel: Added graph to single stat panel, #951

Torkel Ödegaard 11 лет назад
Родитель
Сommit
31a4d9204c

+ 1 - 0
src/app/components/settings.js

@@ -17,6 +17,7 @@ function (_, crypto) {
       window_title_prefix           : 'Grafana - ',
       panels                        : {
         'graph': { path: 'panels/graph' },
+        'stats': { path: 'panels/stats' },
         'text': { path: 'panels/text' }
       },
       plugins                       : {},

+ 2 - 2
src/app/directives/panelMenu.js

@@ -9,7 +9,7 @@ function (angular, $, _) {
   angular
     .module('grafana.directives')
     .directive('panelMenu', function($compile) {
-      var linkTemplate = '<a class="panel-title drag-handle">{{panel.title | interpolateTemplateVars}}</a>';
+      var linkTemplate = '<span class="panel-title drag-handle pointer">{{panel.title | interpolateTemplateVars}}</span>';
 
       function createMenuTemplate($scope) {
         var template = '<div class="panel-menu small">';
@@ -113,7 +113,7 @@ function (angular, $, _) {
             dismiss(2500);
           };
 
-          if ($scope.panelMeta.titlePos) {
+          if ($scope.panelMeta.titlePos && $scope.panel.title) {
             elem.css('text-align', 'left');
             $link.css('padding-left', '10px');
           }

+ 3 - 29
src/app/panels/stats/module.html

@@ -1,40 +1,14 @@
 <div ng-controller='StatsCtrl'>
-	<div stats-panel></div>
 
-	<!-- <div class="stats&#45;panel&#45;value&#45;container"> -->
-	<!-- 	<span class="stats&#45;panel&#45;value">{{mainstat.value}}</span> -->
-	<!-- 	<span class="stats&#45;panel&#45;func">({{mainstat.func}})</span> -->
-	<!-- </div> -->
-  <!--  -->
-	<!-- <table class="stats&#45;panel&#45;table"> -->
-	<!-- 	<tr> -->
-	<!-- 		<th></th> -->
-	<!-- 		<th>avg</th> -->
-	<!-- 		<th>min</th> -->
-	<!-- 		<th>max</th> -->
-	<!-- 		<th>current</th> -->
-	<!-- 		<th>total</th> -->
-	<!-- 	</tr> -->
-	<!-- 	<tr class="stats&#45;series&#45;item" ng&#45;repeat="series in series"> -->
-	<!-- 		<td> -->
-	<!-- 			<i class='icon&#45;minus pointer' ng&#45;style="{color: series.color}"></i> -->
-	<!-- 			{{series.info.alias}} -->
-	<!-- 		</td> -->
-	<!-- 		<td>{{series.info.avg}}</td> -->
-	<!-- 		<td>{{series.info.min}}</td> -->
-	<!-- 		<td>{{series.info.max}}</td> -->
-	<!-- 		<td>{{series.info.current}}</td> -->
-	<!-- 		<td>{{series.info.total}}</td> -->
-	<!-- 	</tr> -->
-	<!-- </table> -->
+	<div class="stats-panel" stats-panel></div>
 
   <div class="clearfix"></div>
 
 	<div style="margin-top: 30px" ng-if="editMode">
 		<div class="dashboard-editor-header">
 			<div class="dashboard-editor-title">
-				<i class="icon icon-bar-chart"></i>
-			  Panel settings
+				<i class="icon icon-dashboard"></i>
+			  Singlestat
 			</div>
 
 			<div ng-model="editor.index" bs-tabs>

+ 27 - 149
src/app/panels/stats/module.js

@@ -5,14 +5,15 @@ define([
   'components/timeSeries',
   'kbn',
   'services/panelSrv',
+  './statsDirective',
 ],
 function (angular, app, _, TimeSeries, kbn) {
   'use strict';
 
-  var module = angular.module('grafana.panels.stats', []);
+  var module = angular.module('grafana.panels.stats');
   app.useModule(module);
 
-  module.controller('StatsCtrl', function($scope, panelSrv, timeSrv, $rootScope) {
+  module.controller('StatsCtrl', function($scope, panelSrv, timeSrv) {
 
     $scope.panelMeta = {
       titlePos: 'left',
@@ -27,7 +28,7 @@ function (angular, app, _, TimeSeries, kbn) {
           src:'app/partials/metrics.html'
         },
         {
-          title: 'Display Styles',
+          title: 'Options',
           src:'app/panels/stats/statsEditor.html'
         }
       ],
@@ -39,25 +40,20 @@ function (angular, app, _, TimeSeries, kbn) {
       targets: [{}],
       cacheTimeout: null,
       format: 'none',
-      stats: {
-        show: true,
-        avg: true,
-        template: '{{value}} {{func}}'
-      },
-      coloring: {
-        thresholds: '',
-        background: false,
-        value: false,
-        colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"]
-      },
-      table: {
-        show: true,
+      template: '{{avg}} !(avg)',
+      thresholds: '',
+      colorBackground: false,
+      colorValue: false,
+      colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
+      sparkline: {
+        show: false,
+        full: false,
+        lineColor: 'rgb(31, 120, 193)',
+        fillColor: 'rgb(31, 120, 193)',
       }
     };
 
     _.defaults($scope.panel, _d);
-    _.defaults($scope.panel.stats, _d.stats);
-    _.defaults($scope.panel.coloring, _d.coloring);
 
     $scope.init = function() {
       panelSrv.init($scope);
@@ -101,43 +97,33 @@ function (angular, app, _, TimeSeries, kbn) {
       $scope.render();
     };
 
-    $scope.seriesHandler = function(seriesData, index) {
-      var datapoints = seriesData.datapoints;
-      var alias = seriesData.target;
-      var color = $rootScope.colors[index];
-
-      var seriesInfo = {
-        alias: alias,
-        enable: true,
-        color: color
-      };
-
+    $scope.seriesHandler = function(seriesData) {
       var series = new TimeSeries({
-        datapoints: datapoints,
-        info: seriesInfo,
+        datapoints: seriesData.datapoints,
+        info: { alias: seriesData.target },
       });
 
-      series.points = series.getFlotPairs('connected');
+      series.data = series.getFlotPairs('connected');
 
       return series;
     };
 
     $scope.setColoring = function(options) {
       if (options.background) {
-        $scope.panel.coloring.value = false;
-        $scope.panel.coloring.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
+        $scope.panel.colorValue = false;
+        $scope.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
       }
       else {
-        $scope.panel.coloring.background = false;
-        $scope.panel.coloring.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
+        $scope.panel.colorBackground = false;
+        $scope.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
       }
       $scope.render();
     };
 
     $scope.invertColorOrder = function() {
-      var tmp = $scope.panel.coloring.colors[0];
-      $scope.panel.coloring.colors[0] = $scope.panel.coloring.colors[2];
-      $scope.panel.coloring.colors[2] = tmp;
+      var tmp = $scope.panel.colors[0];
+      $scope.panel.colors[0] = $scope.panel.colors[2];
+      $scope.panel.colors[2] = tmp;
       $scope.render();
     };
 
@@ -153,11 +139,11 @@ function (angular, app, _, TimeSeries, kbn) {
         series.updateLegendValues(kbn.valueFormats[$scope.panel.format], 2, -7);
       }
 
-      data.thresholds = $scope.panel.coloring.thresholds.split(',').map(function(strVale) {
+      data.thresholds = $scope.panel.thresholds.split(',').map(function(strVale) {
         return Number(strVale.trim());
       });
 
-      data.colorMap = $scope.panel.coloring.colors;
+      data.colorMap = $scope.panel.colors;
 
       $scope.data = data;
       $scope.$emit('render');
@@ -165,112 +151,4 @@ function (angular, app, _, TimeSeries, kbn) {
 
     $scope.init();
   });
-
-  module.directive('statsPanel', function() {
-
-    return {
-      link: function(scope, elem) {
-        var data;
-        var valueRegex = /\{\{([a-zA-Z]+)\}\}/g;
-        var smallValueTextRegex = /!(\S+)/g;
-
-        scope.$on('render', function() {
-          data = scope.data;
-          data.mainValue = null;
-
-          if (!data || data.series.length === 0) {
-            elem.html('no data');
-            return;
-          }
-
-          render();
-        });
-
-        function applyColoringThresholds(value, valueString) {
-          if (!scope.panel.coloring.value) {
-            return valueString;
-          }
-
-          var color = getColorForValue(value);
-          if (color) {
-            return '<span style="color:' + color + '">'+ valueString + '</span>';
-          }
-
-          return valueString;
-        }
-
-        function getColorForValue(value) {
-          for (var i = data.thresholds.length - 1; i >= 0 ; i--) {
-            if (value > data.thresholds[i]) {
-              return data.colorMap[i];
-            }
-          }
-          return null;
-        }
-
-        function valueTemplateReplaceFunc(match, statType) {
-          var stats = data.series[0].stats;
-          data.mainValue = stats[statType];
-          var valueFormated = scope.formatValue(data.mainValue);
-          return applyColoringThresholds(data.mainValue, valueFormated);
-        }
-
-        function smallValueTextReplaceFunc(match, text) {
-          return '<span class="stats-panel-value-small">' + text + '</span>';
-        }
-
-        function render() {
-          var panel = scope.panel;
-          var body = '';
-          var i, series;
-
-          if (panel.stats.show) {
-            body += '<div class="stats-panel-value-container">';
-            body += '<span class="stats-panel-value">';
-            var valueHtml = panel.stats.template.replace(valueRegex, valueTemplateReplaceFunc);
-            body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc);
-            body += '</div>';
-            body += '</div>';
-          }
-
-          if (panel.coloring.background && data.mainValue) {
-            var color = getColorForValue(data.mainValue);
-            if (color) {
-              elem.parents('.panel-container').css('background-color', color);
-              if (scope.fullscreen) {
-                elem.css('background-color', color);
-              } else {
-                elem.css('background-color', '');
-              }
-            }
-          } else {
-            elem.parents('.panel-container').css('background-color', '');
-            elem.css('background-color', '');
-          }
-
-          if (panel.table.show) {
-            body += '<table class="stats-panel-table">';
-            body += '<tr>';
-            body += '<th></th><th>avg</th><th>min</th><th>max</th><th>current</th><th>total</th>';
-            body += '</tr>';
-            for (i = 0; i < data.series.length; i++) {
-              series = data.series[i];
-              body += '<tr>';
-              body += '<td><i class="icon-minus pointer" style="color:' + series.color + '"></i> ';
-              body += series.info.alias + ' </td>';
-              body += '<td>' + series.info.avg + '</td>';
-              body += '<td>' + series.info.min + '</td>';
-              body += '<td>' + series.info.max + '</td>';
-              body += '<td>' + series.info.total + '</td>';
-              body += '<td>' + series.info.current + '</td>';
-            }
-            body += '</table>';
-          }
-
-          elem.html(body);
-        }
-      }
-    };
-  });
-
 });

+ 170 - 0
src/app/panels/stats/statsDirective.js

@@ -0,0 +1,170 @@
+define([
+  'angular',
+  'app',
+  'lodash',
+  'kbn',
+  'jquery',
+  'jquery.flot',
+  'jquery.flot.time',
+],
+function (angular, app, _, kbn, $) {
+  'use strict';
+
+  var module = angular.module('grafana.panels.stats', []);
+  app.useModule(module);
+
+  module.directive('statsPanel', function() {
+
+    return {
+      link: function(scope, elem) {
+        var data;
+        var valueRegex = /\{\{([a-zA-Z]+)\}\}/g;
+        var smallValueTextRegex = /!(\S+)/g;
+        var $panelContainer = elem.parents('.panel-container');
+
+        scope.$on('render', function() {
+          data = scope.data;
+          data.mainValue = null;
+
+          if (!data || data.series.length === 0) {
+            elem.html('no data');
+            return;
+          }
+
+          render();
+        });
+
+        function setElementHeight() {
+          try {
+            var height = scope.height || scope.panel.height || scope.row.height;
+            if (_.isString(height)) {
+              height = parseInt(height.replace('px', ''), 10);
+            }
+
+            height -= scope.panel.title ? 24 : 9; // subtract panel title bar
+
+            elem.css('height', height + 'px');
+
+            return true;
+          } catch(e) { // IE throws errors sometimes
+            return false;
+          }
+        }
+
+        function applyColoringThresholds(value, valueString) {
+          if (!scope.panel.colorValue) {
+            return valueString;
+          }
+
+          var color = getColorForValue(value);
+          if (color) {
+            return '<span style="color:' + color + '">'+ valueString + '</span>';
+          }
+
+          return valueString;
+        }
+
+        function getColorForValue(value) {
+          for (var i = data.thresholds.length - 1; i >= 0 ; i--) {
+            if (value > data.thresholds[i]) {
+              return data.colorMap[i];
+            }
+          }
+          return null;
+        }
+
+        function valueTemplateReplaceFunc(match, statType) {
+          var stats = data.series[0].stats;
+          data.mainValue = stats[statType];
+          var valueFormated = scope.formatValue(data.mainValue);
+          return applyColoringThresholds(data.mainValue, valueFormated);
+        }
+
+        function smallValueTextReplaceFunc(match, text) {
+          return '<span class="stats-panel-value-small">' + text + '</span>';
+        }
+
+        function render() {
+          setElementHeight();
+
+          var panel = scope.panel;
+          var body = '';
+
+          body += '<div class="stats-panel-value-container">';
+          body += '<span class="stats-panel-value">';
+          var valueHtml = panel.template.replace(valueRegex, valueTemplateReplaceFunc);
+          body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc);
+          body += '</div>';
+          body += '</div>';
+
+          if (panel.colorBackground && data.mainValue) {
+            var color = getColorForValue(data.mainValue);
+            if (color) {
+              $panelContainer.css('background-color', color);
+              if (scope.fullscreen) {
+                elem.css('background-color', color);
+              } else {
+                elem.css('background-color', '');
+              }
+            }
+          } else {
+            $panelContainer.css('background-color', '');
+            elem.css('background-color', '');
+          }
+
+          var width = elem.width() + 20;
+          var height = elem.height() || 100;
+
+          var plotCanvas = $('<div></div>');
+          var plotCss = {};
+          plotCss.position = 'absolute';
+
+          if (panel.sparkline.full) {
+            plotCss.bottom = '5px';
+            plotCss.left = '-5px';
+            plotCss.width = (width - 10) + 'px';
+            plotCss.height = (height - 45) + 'px';
+          }
+          else {
+            plotCss.bottom = "0px";
+            plotCss.left = "-5px";
+            plotCss.width = (width - 10) + 'px';
+            plotCss.height = Math.floor(height * 0.3) + "px";
+          }
+
+          plotCanvas.css(plotCss);
+
+          var options = {
+            legend: { show: false },
+            series: {
+              lines:  {
+                show: true,
+                fill: 1,
+                lineWidth: 1,
+                fillColor: panel.sparkline.fillColor,
+              },
+            },
+            yaxes: { show: false },
+            xaxis: {
+              show: false,
+              mode: "time",
+              min: scope.range.from.getTime(),
+              max: scope.range.to.getTime(),
+            },
+            grid: { hoverable: false, show: false },
+          };
+
+          elem.html(body);
+          elem.append(plotCanvas);
+
+          data.series[0].color = panel.sparkline.lineColor;
+
+          setTimeout(function() {
+            $.plot(plotCanvas, [data.series[0]], options);
+          }, 200);
+        }
+      }
+    };
+  });
+
+});

+ 21 - 13
src/app/panels/stats/statsEditor.html

@@ -1,14 +1,9 @@
 <div class="editor-row">
-  <div class="section">
-    <h5>Main options</h5>
-		<editor-opt-bool text="Show table" model="panel.table.show" change="render()"></editor-opt-bool>
-		<editor-opt-bool text="Show big values" model="panel.stats.show" change="render()"></editor-opt-bool>
-  </div>
-  <div class="section" ng-if="panel.stats">
+	<div class="section">
     <h5>Big values</h5>
 		<div class="editor-option">
 			<label class="small">Template</label>
-			<input type="text" class="input-large" ng-model="panel.stats.template" ng-blur="render()"></input>
+			<input type="text" class="input-xlarge" ng-model="panel.template" ng-blur="render()"></input>
 		</div>
 	</div>
 	<div class="section">
@@ -20,22 +15,35 @@
 	</div>
 	<div class="section">
     <h5>Coloring</h5>
-		<editor-opt-bool text="Background" model="panel.coloring.background" change="setColoring({background: true})"></editor-opt-bool>
-		<editor-opt-bool text="Value" model="panel.coloring.value" change="setColoring({value: true})"></editor-opt-bool>
+		<editor-opt-bool text="Background" model="panel.colorBackground" change="setColoring({background: true})"></editor-opt-bool>
+		<editor-opt-bool text="Value" model="panel.colorValue" change="setColoring({value: true})"></editor-opt-bool>
 		<div class="editor-option">
 			<label class="small">Thresholds</label>
-			<input type="text" class="input-large" ng-model="panel.coloring.thresholds" ng-blur="render()"></input>
+			<input type="text" class="input-large" ng-model="panel.thresholds" ng-blur="render()"></input>
 		</div>
 		<div class="editor-option">
       <label class="small">Color</label>
-      <spectrum-picker ng-model="panel.coloring.colors[0]" ng-change="render()" ></spectrum-picker>
-      <spectrum-picker ng-model="panel.coloring.colors[1]" ng-change="render()" ></spectrum-picker>
-			<spectrum-picker ng-model="panel.coloring.colors[2]" ng-change="render()" ></spectrum-picker>
+      <spectrum-picker ng-model="panel.colors[0]" ng-change="render()" ></spectrum-picker>
+      <spectrum-picker ng-model="panel.colors[1]" ng-change="render()" ></spectrum-picker>
+			<spectrum-picker ng-model="panel.colors[2]" ng-change="render()" ></spectrum-picker>
 			<a class="pointer" ng-click="invertColorOrder()">invert order</a>
 		</div>
 	</div>
 </div>
 
+<div class="editor-row">
+	<div class="section">
+		<h5>Spark lines</h5>
+	</div>
+	<editor-opt-bool text="Spark line" model="panel.sparkline.show" change="render()"></editor-opt-bool>
+	<editor-opt-bool text="Background mode" model="panel.sparkline.full" change="render()"></editor-opt-bool>
+	<div class="editor-option">
+    <label class="small">Line color</label>
+		<spectrum-picker ng-model="panel.sparkline.lineColor" ng-change="render()" ></spectrum-picker>
+		<spectrum-picker ng-model="panel.sparkline.fillColor" ng-change="render()" ></spectrum-picker>
+  </div>
+</div>
+
 <div class="editor-row">
 	<div class="section">
 		<h5>Series options</h5>

+ 10 - 0
src/css/less/stats-panel.less

@@ -1,6 +1,16 @@
+.stats-panel {
+  position: relative;
+  display: table;
+  width: 100%;
+}
+
 .stats-panel-value-container {
   padding: 20px;
+  display: table-cell;
+  vertical-align: middle;
   text-align: center;
+  position: relative;
+  z-index: 1;
 }
 
 .stats-panel-value {