From 23246605b0b3a934821d0e0f99ea9d89c50c1cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 22 Sep 2016 16:05:20 +0200 Subject: [PATCH] feat(graph panel): working on adding non time series support to graph panel --- public/app/core/core.ts | 2 + public/app/core/directives/metric_segment.js | 14 +- public/app/core/utils/colors.ts | 12 ++ .../app/plugins/panel/graph/axes_edit_tab.ts | 25 +++ .../app/plugins/panel/graph/data_processor.ts | 164 ++++++++++++++++++ public/app/plugins/panel/graph/module.ts | 161 ++++------------- public/app/plugins/panel/graph/tab_axes.html | 12 +- 7 files changed, 250 insertions(+), 140 deletions(-) create mode 100644 public/app/core/utils/colors.ts create mode 100644 public/app/plugins/panel/graph/axes_edit_tab.ts create mode 100644 public/app/plugins/panel/graph/data_processor.ts diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 1174e267f4e..d44cbf4dbfb 100644 --- a/public/app/core/core.ts +++ b/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, }; diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index d51260395de..98921753997 100644 --- a/public/app/core/directives/metric_segment.js +++ b/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') { diff --git a/public/app/core/utils/colors.ts b/public/app/core/utils/colors.ts new file mode 100644 index 00000000000..bd774ea02ea --- /dev/null +++ b/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" +]; + diff --git a/public/app/plugins/panel/graph/axes_edit_tab.ts b/public/app/plugins/panel/graph/axes_edit_tab.ts new file mode 100644 index 00000000000..119e0a0aff6 --- /dev/null +++ b/public/app/plugins/panel/graph/axes_edit_tab.ts @@ -0,0 +1,25 @@ +/// + +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, + }; +} diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts new file mode 100644 index 00000000000..71a6875c858 --- /dev/null +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -0,0 +1,164 @@ +/// + +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; +// } + + diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 712ca04c423..90c323d4c87 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/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'} - ]); + return this.$q.when(this.processor.getXAxisValueOptions({dataList: this.dataList})); } } -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; -} - export {GraphCtrl, GraphCtrl as PanelCtrl} diff --git a/public/app/plugins/panel/graph/tab_axes.html b/public/app/plugins/panel/graph/tab_axes.html index d0915f7e17c..f139e88de5a 100644 --- a/public/app/plugins/panel/graph/tab_axes.html +++ b/public/app/plugins/panel/graph/tab_axes.html @@ -44,24 +44,20 @@
- +
- +
-
+
- +