Commit 23246605 by Torkel Ödegaard

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

parent f3d4a97f
...@@ -41,6 +41,7 @@ import 'app/core/routes/routes'; ...@@ -41,6 +41,7 @@ import 'app/core/routes/routes';
import './filters/filters'; import './filters/filters';
import coreModule from './core_module'; import coreModule from './core_module';
import appEvents from './app_events'; import appEvents from './app_events';
import colors from './utils/colors';
export { export {
...@@ -60,4 +61,5 @@ export { ...@@ -60,4 +61,5 @@ export {
dashboardSelector, dashboardSelector,
queryPartEditorDirective, queryPartEditorDirective,
WizardFlow, WizardFlow,
colors,
}; };
...@@ -170,6 +170,7 @@ function (_, $, coreModule) { ...@@ -170,6 +170,7 @@ function (_, $, coreModule) {
}, },
link: { link: {
pre: function postLink($scope, elem, attrs) { pre: function postLink($scope, elem, attrs) {
var cachedOptions;
$scope.valueToSegment = function(value) { $scope.valueToSegment = function(value) {
var option = _.find($scope.options, {value: value}); var option = _.find($scope.options, {value: value});
...@@ -189,13 +190,20 @@ function (_, $, coreModule) { ...@@ -189,13 +190,20 @@ function (_, $, coreModule) {
}); });
return $q.when(optionSegments); return $q.when(optionSegments);
} else { } 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() { $scope.onSegmentChange = function() {
if ($scope.options) { var options = $scope.options || cachedOptions;
var option = _.find($scope.options, {text: $scope.segment.value});
if (options) {
var option = _.find(options, {text: $scope.segment.value});
if (option && option.value !== $scope.property) { if (option && option.value !== $scope.property) {
$scope.property = option.value; $scope.property = option.value;
} else if (attrs.custom !== 'false') { } else if (attrs.custom !== 'false') {
......
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"
];
///<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,
};
}
///<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;
// }
...@@ -14,15 +14,18 @@ import TimeSeries from 'app/core/time_series2'; ...@@ -14,15 +14,18 @@ import TimeSeries from 'app/core/time_series2';
import config from 'app/core/config'; import config from 'app/core/config';
import * as fileExport from 'app/core/utils/file_export'; import * as fileExport from 'app/core/utils/file_export';
import {MetricsPanelCtrl, alertTab} from 'app/plugins/sdk'; import {MetricsPanelCtrl, alertTab} from 'app/plugins/sdk';
import {DataProcessor} from './data_processor';
class GraphCtrl extends MetricsPanelCtrl { class GraphCtrl extends MetricsPanelCtrl {
static template = template; static template = template;
hiddenSeries: any = {}; hiddenSeries: any = {};
seriesList: any = []; seriesList: any = [];
dataList: any = [];
logScales: any; logScales: any;
unitFormats: any; unitFormats: any;
xAxisModes: any; xAxisModes: any;
xAxisStatOptions: any;
xNameSegment: any; xNameSegment: any;
annotationsPromise: any; annotationsPromise: any;
datapointsCount: number; datapointsCount: number;
...@@ -30,6 +33,7 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -30,6 +33,7 @@ class GraphCtrl extends MetricsPanelCtrl {
datapointsWarning: boolean; datapointsWarning: boolean;
colors: any = []; colors: any = [];
subTabIndex: number; subTabIndex: number;
processor: DataProcessor;
panelDefaults = { panelDefaults = {
// datasource name, null = default datasource // datasource name, null = default datasource
...@@ -118,7 +122,7 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -118,7 +122,7 @@ class GraphCtrl extends MetricsPanelCtrl {
_.defaults(this.panel.legend, this.panelDefaults.legend); _.defaults(this.panel.legend, this.panelDefaults.legend);
_.defaults(this.panel.xaxis, this.panelDefaults.xaxis); _.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('render', this.onRender.bind(this));
this.events.on('data-received', this.onDataReceived.bind(this)); this.events.on('data-received', this.onDataReceived.bind(this));
...@@ -144,6 +148,7 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -144,6 +148,7 @@ class GraphCtrl extends MetricsPanelCtrl {
'log (base 32)': 32, 'log (base 32)': 32,
'log (base 1024)': 1024 'log (base 1024)': 1024
}; };
this.unitFormats = kbn.getUnitFormats(); this.unitFormats = kbn.getUnitFormats();
this.xAxisModes = { this.xAxisModes = {
...@@ -153,6 +158,14 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -153,6 +158,14 @@ class GraphCtrl extends MetricsPanelCtrl {
'Json': 'json' '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; this.subTabIndex = 0;
} }
...@@ -199,25 +212,8 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -199,25 +212,8 @@ class GraphCtrl extends MetricsPanelCtrl {
this.datapointsCount = 0; this.datapointsCount = 0;
this.datapointsOutside = false; this.datapointsOutside = false;
let dataHandler: (seriesData, index)=>any; this.dataList = dataList;
switch (this.panel.xaxis.mode) { this.seriesList = this.processor.getSeriesList({dataList: dataList, range: this.range});
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.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside; this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
this.annotationsPromise.then(annotations => { this.annotationsPromise.then(annotations => {
...@@ -230,73 +226,6 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -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() { onRender() {
if (!this.seriesList) { return; } if (!this.seriesList) { return; }
...@@ -380,13 +309,11 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -380,13 +309,11 @@ class GraphCtrl extends MetricsPanelCtrl {
this.render(); this.render();
} }
// Called from panel menu
toggleLegend() { toggleLegend() {
this.panel.legend.show = !this.panel.legend.show; this.panel.legend.show = !this.panel.legend.show;
this.refresh(); this.refresh();
} }
legendValuesOptionChanged() { legendValuesOptionChanged() {
var legend = this.panel.legend; var legend = this.panel.legend;
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total; legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
...@@ -401,9 +328,21 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -401,9 +328,21 @@ class GraphCtrl extends MetricsPanelCtrl {
fileExport.exportSeriesListToCsvColumns(this.seriesList); fileExport.exportSeriesListToCsvColumns(this.seriesList);
} }
xAxisModeChanged() { xAxisOptionChanged() {
// set defaults switch (this.panel.xaxis.mode) {
this.refresh(); 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() { getXAxisNameOptions() {
...@@ -413,44 +352,8 @@ class GraphCtrl extends MetricsPanelCtrl { ...@@ -413,44 +352,8 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
getXAxisValueOptions() { getXAxisValueOptions() {
return this.$q.when([ return this.$q.when(this.processor.getXAxisValueOptions({dataList: this.dataList}));
{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 value;
} }
export {GraphCtrl, GraphCtrl as PanelCtrl} export {GraphCtrl, GraphCtrl as PanelCtrl}
...@@ -44,24 +44,20 @@ ...@@ -44,24 +44,20 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-5">Mode</label> <label class="gf-form-label width-5">Mode</label>
<div class="gf-form-select-wrapper max-width-15"> <div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input" <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>
ng-model="ctrl.panel.xaxis.mode"
ng-options="v as k for (k, v) in ctrl.xAxisModes"
ng-change="ctrl.xAxisModeChanged()">
</select>
</div> </div>
</div> </div>
<!-- Table mode --> <!-- Table mode -->
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'table' || ctrl.panel.xaxis.mode === 'json'"> <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> <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> </div>
<!-- Series mode --> <!-- 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> <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>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment