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 @@
-
+
-