Forráskód Böngészése

feat(graph panel): working on adding non time series support to graph panel

Torkel Ödegaard 9 éve
szülő
commit
23246605b0

+ 2 - 0
public/app/core/core.ts

@@ -41,6 +41,7 @@ import 'app/core/routes/routes';
 import './filters/filters';
 import coreModule from './core_module';
 import appEvents from './app_events';
+import colors from './utils/colors';
 
 
 export {
@@ -60,4 +61,5 @@ export {
   dashboardSelector,
   queryPartEditorDirective,
   WizardFlow,
+  colors,
 };

+ 11 - 3
public/app/core/directives/metric_segment.js

@@ -170,6 +170,7 @@ function (_, $, coreModule) {
       },
       link: {
         pre: function postLink($scope, elem, attrs) {
+          var cachedOptions;
 
           $scope.valueToSegment = function(value) {
             var option = _.find($scope.options, {value: value});
@@ -189,13 +190,20 @@ function (_, $, coreModule) {
               });
               return $q.when(optionSegments);
             } else {
-              return $scope.getOptions();
+              return $scope.getOptions().then(function(options) {
+                cachedOptions = options;
+                return _.map(options, function(option) {
+                  return uiSegmentSrv.newSegment({value: option.text});
+                });
+              });
             }
           };
 
           $scope.onSegmentChange = function() {
-            if ($scope.options) {
-              var option = _.find($scope.options, {text: $scope.segment.value});
+            var options = $scope.options || cachedOptions;
+
+            if (options) {
+              var option = _.find(options, {text: $scope.segment.value});
               if (option && option.value !== $scope.property) {
                 $scope.property = option.value;
               } else if (attrs.custom !== 'false') {

+ 12 - 0
public/app/core/utils/colors.ts

@@ -0,0 +1,12 @@
+
+
+export default [
+  "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
+  "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
+  "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
+  "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
+  "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
+  "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
+  "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
+];
+

+ 25 - 0
public/app/plugins/panel/graph/axes_edit_tab.ts

@@ -0,0 +1,25 @@
+///<reference path="../../../headers/common.d.ts" />
+
+export class AxesEditTabCtrl {
+  panel: any;
+  panelCtrl: any;
+
+  /** @ngInject **/
+  constructor($scope) {
+    this.panelCtrl = $scope.ctrl;
+    this.panel = this.panelCtrl.panel;
+    $scope.ctrl = this;
+  }
+
+}
+
+/** @ngInject **/
+export function axesTabCtrl() {
+  'use strict';
+  return {
+    restrict: 'E',
+    scope: true,
+    templateUrl: 'public/app/plugins/panel/graph/tab_axes.html',
+    controller: AxesEditTabCtrl,
+  };
+}

+ 164 - 0
public/app/plugins/panel/graph/data_processor.ts

@@ -0,0 +1,164 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import kbn from 'app/core/utils/kbn';
+import _ from 'lodash';
+import TimeSeries from 'app/core/time_series2';
+import {colors} from 'app/core/core';
+
+export class DataProcessor {
+
+  constructor(private panel) {
+  }
+
+  getSeriesList(options) {
+
+    switch (this.panel.xaxis.mode) {
+      case 'series':
+      case 'time': {
+        return options.dataList.map(this.timeSeriesHandler.bind(this));
+      }
+      case 'table': {
+         // Table panel uses only first enabled target, so we can use dataList[0]
+         // dataList.splice(1, dataList.length - 1);
+         // dataHandler = this.tableHandler;
+        break;
+      }
+      case 'json': {
+        break;
+      }
+    }
+  }
+
+  seriesHandler(seriesData, index, datapoints, alias) {
+    var colorIndex = index % colors.length;
+    var color = this.panel.aliasColors[alias] || colors[colorIndex];
+
+    var series = new TimeSeries({datapoints: datapoints, alias: alias, color: color, unit: seriesData.unit});
+
+    // if (datapoints && datapoints.length > 0) {
+    //   var last = moment.utc(datapoints[datapoints.length - 1][1]);
+    //   var from = moment.utc(this.range.from);
+    //   if (last - from < -10000) {
+    //     this.datapointsOutside = true;
+    //   }
+    //
+    //   this.datapointsCount += datapoints.length;
+    //   this.panel.tooltip.msResolution = this.panel.tooltip.msResolution || series.isMsResolutionNeeded();
+    // }
+
+    return series;
+  }
+
+  timeSeriesHandler(seriesData, index) {
+    var datapoints = seriesData.datapoints;
+    var alias = seriesData.target;
+
+    return this.seriesHandler(seriesData, index, datapoints, alias);
+  }
+
+  tableHandler(seriesData, index) {
+    var xColumnIndex = Number(this.panel.xaxis.columnIndex);
+    var valueColumnIndex = Number(this.panel.xaxis.valueColumnIndex);
+    var datapoints = _.map(seriesData.rows, (row) => {
+      var value = valueColumnIndex ? row[valueColumnIndex] : _.last(row);
+      return [
+        value,             // Y value
+        row[xColumnIndex]  // X value
+      ];
+    });
+
+    var alias = seriesData.columns[valueColumnIndex].text;
+
+    return this.seriesHandler(seriesData, index, datapoints, alias);
+  }
+
+  // esRawDocHandler(seriesData, index) {
+  //   let xField = this.panel.xaxis.esField;
+  //   let valueField = this.panel.xaxis.esValueField;
+  //   let datapoints = _.map(seriesData.datapoints, (doc) => {
+  //     return [
+  //       pluckDeep(doc, valueField),  // Y value
+  //       pluckDeep(doc, xField)       // X value
+  //     ];
+  //   });
+  //
+  //   // Remove empty points
+  //   datapoints = _.filter(datapoints, (point) => {
+  //     return point[0] !== undefined;
+  //   });
+  //
+  //   var alias = valueField;
+  //   return this.seriesHandler(seriesData, index, datapoints, alias);
+  // }
+  //
+  validateXAxisSeriesValue() {
+    switch (this.panel.xaxis.mode) {
+      case 'series': {
+        if (this.panel.xaxis.values.length === 0) {
+          this.panel.xaxis.values = ['total'];
+          return;
+        }
+
+        var validOptions = this.getXAxisValueOptions({});
+        var found = _.find(validOptions, {value: this.panel.xaxis.values[0]});
+        if (!found) {
+          this.panel.xaxis.values = ['total'];
+        }
+        return;
+      }
+    }
+  }
+
+  getXAxisValueOptions(options) {
+    switch (this.panel.xaxis.mode) {
+      case 'time': {
+        return [];
+      }
+      case 'series': {
+        return [
+          {text: 'Avg', value: 'avg'},
+          {text: 'Min', value: 'min'},
+          {text: 'Max', value: 'min'},
+          {text: 'Total', value: 'total'},
+          {text: 'Count', value: 'count'},
+        ];
+      }
+    }
+  }
+}
+
+// function getFieldsFromESDoc(doc) {
+//   let fields = [];
+//   let fieldNameParts = [];
+//
+//   function getFieldsRecursive(obj) {
+//     _.forEach(obj, (value, key) => {
+//       if (_.isObject(value)) {
+//         fieldNameParts.push(key);
+//         getFieldsRecursive(value);
+//       } else {
+//         let field = fieldNameParts.concat(key).join('.');
+//         fields.push(field);
+//       }
+//     });
+//     fieldNameParts.pop();
+//   }
+//
+//   getFieldsRecursive(doc);
+//   return fields;
+// }
+//
+// function pluckDeep(obj: any, property: string) {
+//   let propertyParts = property.split('.');
+//   let value = obj;
+//   for (let i = 0; i < propertyParts.length; ++i) {
+//     if (value[propertyParts[i]]) {
+//       value = value[propertyParts[i]];
+//     } else {
+//       return undefined;
+//     }
+//   }
+//   return value;
+// }
+
+

+ 32 - 129
public/app/plugins/panel/graph/module.ts

@@ -14,15 +14,18 @@ import TimeSeries from 'app/core/time_series2';
 import config from 'app/core/config';
 import * as fileExport from 'app/core/utils/file_export';
 import {MetricsPanelCtrl, alertTab} from 'app/plugins/sdk';
+import {DataProcessor} from './data_processor';
 
 class GraphCtrl extends MetricsPanelCtrl {
   static template = template;
 
   hiddenSeries: any = {};
   seriesList: any = [];
+  dataList: any = [];
   logScales: any;
   unitFormats: any;
   xAxisModes: any;
+  xAxisStatOptions: any;
   xNameSegment: any;
   annotationsPromise: any;
   datapointsCount: number;
@@ -30,6 +33,7 @@ class GraphCtrl extends MetricsPanelCtrl {
   datapointsWarning: boolean;
   colors: any = [];
   subTabIndex: number;
+  processor: DataProcessor;
 
   panelDefaults = {
     // datasource name, null = default datasource
@@ -118,7 +122,7 @@ class GraphCtrl extends MetricsPanelCtrl {
     _.defaults(this.panel.legend, this.panelDefaults.legend);
     _.defaults(this.panel.xaxis, this.panelDefaults.xaxis);
 
-    this.colors = $scope.$root.colors;
+    this.processor = new DataProcessor(this.panel);
 
     this.events.on('render', this.onRender.bind(this));
     this.events.on('data-received', this.onDataReceived.bind(this));
@@ -144,6 +148,7 @@ class GraphCtrl extends MetricsPanelCtrl {
       'log (base 32)': 32,
       'log (base 1024)': 1024
     };
+
     this.unitFormats = kbn.getUnitFormats();
 
     this.xAxisModes = {
@@ -153,6 +158,14 @@ class GraphCtrl extends MetricsPanelCtrl {
       'Json': 'json'
     };
 
+    this.xAxisStatOptions =  [
+      {text: 'Avg', value: 'avg'},
+      {text: 'Min', value: 'min'},
+      {text: 'Max', value: 'min'},
+      {text: 'Total', value: 'total'},
+      {text: 'Count', value: 'count'},
+    ];
+
     this.subTabIndex = 0;
   }
 
@@ -199,25 +212,8 @@ class GraphCtrl extends MetricsPanelCtrl {
     this.datapointsCount = 0;
     this.datapointsOutside = false;
 
-    let dataHandler: (seriesData, index)=>any;
-    switch (this.panel.xaxis.mode) {
-      case 'series':
-      case 'time': {
-        dataHandler = this.timeSeriesHandler;
-        break;
-      }
-      case 'table': {
-         // Table panel uses only first enabled target, so we can use dataList[0]
-         dataList.splice(1, dataList.length - 1);
-         dataHandler = this.tableHandler;
-        break;
-      }
-      case 'json': {
-        break;
-      }
-    }
-
-    this.seriesList = dataList.map(dataHandler.bind(this));
+    this.dataList = dataList;
+    this.seriesList = this.processor.getSeriesList({dataList: dataList, range: this.range});
     this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
 
     this.annotationsPromise.then(annotations => {
@@ -230,73 +226,6 @@ class GraphCtrl extends MetricsPanelCtrl {
     });
   }
 
-  seriesHandler(seriesData, index, datapoints, alias) {
-    var colorIndex = index % this.colors.length;
-    var color = this.panel.aliasColors[alias] || this.colors[colorIndex];
-
-    var series = new TimeSeries({
-      datapoints: datapoints,
-      alias: alias,
-      color: color,
-      unit: seriesData.unit,
-    });
-
-    if (datapoints && datapoints.length > 0) {
-      var last = moment.utc(datapoints[datapoints.length - 1][1]);
-      var from = moment.utc(this.range.from);
-      if (last - from < -10000) {
-        this.datapointsOutside = true;
-      }
-
-      this.datapointsCount += datapoints.length;
-      this.panel.tooltip.msResolution = this.panel.tooltip.msResolution || series.isMsResolutionNeeded();
-    }
-
-    return series;
-  }
-
-  timeSeriesHandler(seriesData, index) {
-    var datapoints = seriesData.datapoints;
-    var alias = seriesData.target;
-
-    return this.seriesHandler(seriesData, index, datapoints, alias);
-  }
-
-  tableHandler(seriesData, index) {
-    var xColumnIndex = Number(this.panel.xaxis.columnIndex);
-    var valueColumnIndex = Number(this.panel.xaxis.valueColumnIndex);
-    var datapoints = _.map(seriesData.rows, (row) => {
-      var value = valueColumnIndex ? row[valueColumnIndex] : _.last(row);
-      return [
-        value,             // Y value
-        row[xColumnIndex]  // X value
-      ];
-    });
-
-    var alias = seriesData.columns[valueColumnIndex].text;
-
-    return this.seriesHandler(seriesData, index, datapoints, alias);
-  }
-
-  esRawDocHandler(seriesData, index) {
-    let xField = this.panel.xaxis.esField;
-    let valueField = this.panel.xaxis.esValueField;
-    let datapoints = _.map(seriesData.datapoints, (doc) => {
-      return [
-        pluckDeep(doc, valueField),  // Y value
-        pluckDeep(doc, xField)       // X value
-      ];
-    });
-
-    // Remove empty points
-    datapoints = _.filter(datapoints, (point) => {
-      return point[0] !== undefined;
-    });
-
-    var alias = valueField;
-    return this.seriesHandler(seriesData, index, datapoints, alias);
-  }
-
   onRender() {
     if (!this.seriesList) { return; }
 
@@ -380,13 +309,11 @@ class GraphCtrl extends MetricsPanelCtrl {
     this.render();
   }
 
-  // Called from panel menu
   toggleLegend() {
     this.panel.legend.show = !this.panel.legend.show;
     this.refresh();
   }
 
-
   legendValuesOptionChanged() {
     var legend = this.panel.legend;
     legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
@@ -401,9 +328,21 @@ class GraphCtrl extends MetricsPanelCtrl {
     fileExport.exportSeriesListToCsvColumns(this.seriesList);
   }
 
-  xAxisModeChanged()  {
-    // set defaults
-    this.refresh();
+  xAxisOptionChanged()  {
+    switch (this.panel.xaxis.mode) {
+      case 'time': {
+        this.panel.tooltip.shared = true;
+        this.panel.xaxis.values = [];
+        this.onDataReceived(this.dataList);
+        break;
+      }
+      case 'series': {
+        this.panel.tooltip.shared = false;
+        this.processor.validateXAxisSeriesValue();
+        this.onDataReceived(this.dataList);
+        break;
+      }
+    }
   }
 
   getXAxisNameOptions()  {
@@ -413,44 +352,8 @@ class GraphCtrl extends MetricsPanelCtrl {
   }
 
   getXAxisValueOptions()  {
-    return this.$q.when([
-      {text: 'Avg', value: 'avg'}
-    ]);
-  }
-}
-
-function getFieldsFromESDoc(doc) {
-  let fields = [];
-  let fieldNameParts = [];
-
-  function getFieldsRecursive(obj) {
-    _.forEach(obj, (value, key) => {
-      if (_.isObject(value)) {
-        fieldNameParts.push(key);
-        getFieldsRecursive(value);
-      } else {
-        let field = fieldNameParts.concat(key).join('.');
-        fields.push(field);
-      }
-    });
-    fieldNameParts.pop();
-  }
-
-  getFieldsRecursive(doc);
-  return fields;
-}
-
-function pluckDeep(obj: any, property: string) {
-  let propertyParts = property.split('.');
-  let value = obj;
-  for (let i = 0; i < propertyParts.length; ++i) {
-    if (value[propertyParts[i]]) {
-      value = value[propertyParts[i]];
-    } else {
-      return undefined;
-    }
+    return this.$q.when(this.processor.getXAxisValueOptions({dataList: this.dataList}));
   }
-  return value;
 }
 
 export {GraphCtrl, GraphCtrl as PanelCtrl}

+ 4 - 8
public/app/plugins/panel/graph/tab_axes.html

@@ -44,24 +44,20 @@
 		<div class="gf-form">
 			<label class="gf-form-label width-5">Mode</label>
 			<div class="gf-form-select-wrapper max-width-15">
-				<select class="gf-form-input"
-					ng-model="ctrl.panel.xaxis.mode"
-					ng-options="v as k for (k, v) in ctrl.xAxisModes"
-					ng-change="ctrl.xAxisModeChanged()">
-				</select>
+				<select class="gf-form-input" ng-model="ctrl.panel.xaxis.mode" ng-options="v as k for (k, v) in ctrl.xAxisModes" ng-change="ctrl.xAxisOptionChanged()"> </select>
 			</div>
 		</div>
 
     <!-- Table mode -->
 		<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'table' || ctrl.panel.xaxis.mode === 'json'">
 			<label class="gf-form-label width-5">Name</label>
-      <metric-segment-model property="ctrl.panel.xaxis.name" get-options="ctrl.getXAxisNameOptions()" on-change="ctrl.render()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
+      <metric-segment-model property="ctrl.panel.xaxis.name" get-options="ctrl.getXAxisNameOptions()" on-change="ctrl.xAxisOptionChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
 		</div>
 
 		<!-- Series mode -->
-		<div class="gf-form" ng-if="ctrl.panel.xaxis.mode !== 'time'">
+		<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'series'">
 			<label class="gf-form-label width-5">Value</label>
-      <metric-segment-model property="ctrl.panel.xaxis.values[0]" get-options="ctrl.getXAxisValueOptions()" on-change="ctrl.render()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
+      <metric-segment-model property="ctrl.panel.xaxis.values[0]" options="ctrl.xAxisStatOptions" on-change="ctrl.xAxisOptionChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
 		</div>
 
 	</div>