Merge branch 'issue-5812-x-axis' of https://github.com/alexanderzobnin/grafana into graph-panel-non-timeseries

This commit is contained in:
Torkel Ödegaard
2016-08-27 09:53:10 +02:00
3 changed files with 276 additions and 10 deletions
+79 -3
View File
@@ -237,6 +237,23 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
var series = data[i];
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
if (panel.xaxis.mode === 'series') {
series.data = [
[i + 1, series.stats[panel.xaxis.seriesValue]]
];
} else if (panel.xaxis.mode === 'table' ||
panel.xaxis.mode === 'elastic') {
series.data = [];
for (var j = 0; j < series.datapoints.length; j++) {
var dataIndex = i * series.datapoints.length + j;
series.datapoints[j];
series.data.push([
dataIndex + 1,
series.datapoints[j][0]
]);
}
}
// if hidden remove points and disable stack
if (ctrl.hiddenSeries[series.alias]) {
series.data = [];
@@ -244,11 +261,34 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
}
}
if (data.length && data[0].stats.timeStep) {
options.series.bars.barWidth = data[0].stats.timeStep / 1.5;
if (panel.xaxis.mode === 'series') {
if (data.length) {
options.series.bars.barWidth = 0.7;
options.series.bars.align = 'center';
options.series.bars.show = true;
options.series.points.show = false;
options.series.lines.show = false;
}
addXSeriesAxis(options);
} else if (panel.xaxis.mode === 'table' ||
panel.xaxis.mode === 'elastic') {
if (data.length) {
options.series.bars.barWidth = 0.7;
options.series.bars.align = 'center';
}
addXTableAxis(options);
} else {
if (data.length && data[0].stats.timeStep) {
options.series.bars.barWidth = data[0].stats.timeStep / 1.5;
}
addTimeAxis(options);
}
addTimeAxis(options);
thresholdManager.addPlotOptions(options, panel);
addAnnotations(options);
configureAxisOptions(data, options);
@@ -315,6 +355,42 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
};
}
function addXSeriesAxis(options) {
var ticks = _.map(data, function(series, index) {
return [index + 1, series.alias];
});
options.xaxis = {
timezone: dashboard.getTimezone(),
show: panel.xaxis.show,
mode: null,
min: 0,
max: ticks.length + 1,
label: "Datetime",
ticks: ticks
};
}
function addXTableAxis(options) {
var ticks = _.map(data, function(series, seriesIndex) {
return _.map(series.datapoints, function(point, pointIndex) {
var tickIndex = seriesIndex * series.datapoints.length + pointIndex;
return [tickIndex + 1, point[1]];
});
});
ticks = _.flatten(ticks, true);
options.xaxis = {
timezone: dashboard.getTimezone(),
show: panel.xaxis.show,
mode: null,
min: 0,
max: ticks.length + 1,
label: "Datetime",
ticks: ticks
};
}
function addAnnotations(options) {
if(!annotations || annotations.length === 0) {
return;
+128 -7
View File
@@ -22,6 +22,9 @@ class GraphCtrl extends MetricsPanelCtrl {
seriesList: any = [];
logScales: any;
unitFormats: any;
xAxisModes: any;
xAxisSeriesValues: any;
xAxisColumns: any = [];
annotationsPromise: any;
datapointsCount: number;
datapointsOutside: boolean;
@@ -53,7 +56,9 @@ class GraphCtrl extends MetricsPanelCtrl {
}
],
xaxis: {
show: true
show: true,
mode: 'time',
seriesValue: 'avg'
},
alert: {
warn: {op: '>', value: undefined},
@@ -117,6 +122,7 @@ class GraphCtrl extends MetricsPanelCtrl {
_.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
_.defaults(this.panel.alert, this.panelDefaults.alert);
_.defaults(this.panel.legend, this.panelDefaults.legend);
_.defaults(this.panel.xaxis, this.panelDefaults.xaxis);
this.colors = $scope.$root.colors;
@@ -144,8 +150,16 @@ class GraphCtrl extends MetricsPanelCtrl {
'log (base 32)': 32,
'log (base 1024)': 1024
};
this.unitFormats = kbn.getUnitFormats();
this.xAxisModes = {
'Time': 'time',
'Series': 'series',
'Table': 'table',
'Elastic Raw Doc': 'elastic'
};
this.xAxisSeriesValues = ['min', 'max', 'avg', 'current', 'total'];
this.subTabIndex = 0;
}
@@ -183,7 +197,40 @@ class GraphCtrl extends MetricsPanelCtrl {
this.datapointsWarning = false;
this.datapointsCount = 0;
this.datapointsOutside = false;
this.seriesList = dataList.map(this.seriesHandler.bind(this));
let dataHandler: (seriesData, index)=>any;
if (this.panel.xaxis.mode === 'table') {
if (dataList.length) {
// Table panel uses only first enabled tagret, so we can use dataList[0]
// for table data representation
dataList.splice(1, dataList.length - 1);
this.xAxisColumns = _.map(dataList[0].columns, (column, index) => {
return {
text: column.text,
index: index
};
});
// Set last column as default value
if (!this.panel.xaxis.valueColumnIndex) {
this.panel.xaxis.valueColumnIndex = this.xAxisColumns.length - 1;
}
}
dataHandler = this.tableHandler;
} else if (this.panel.xaxis.mode === 'elastic') {
if (dataList.length) {
dataList.splice(1, dataList.length - 1);
var point = _.first(dataList[0].datapoints);
this.xAxisColumns = getFieldsFromESDoc(point);
}
dataHandler = this.esRawDocHandler;
} else {
dataHandler = this.timeSeriesHandler;
}
this.seriesList = dataList.map(dataHandler.bind(this));
this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
this.annotationsPromise.then(annotations => {
@@ -196,9 +243,7 @@ class GraphCtrl extends MetricsPanelCtrl {
});
}
seriesHandler(seriesData, index) {
var datapoints = seriesData.datapoints;
var alias = seriesData.target;
seriesHandler(seriesData, index, datapoints, alias) {
var colorIndex = index % this.colors.length;
var color = this.panel.aliasColors[alias] || this.colors[colorIndex];
@@ -220,10 +265,52 @@ class GraphCtrl extends MetricsPanelCtrl {
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; }
@@ -330,4 +417,38 @@ class GraphCtrl extends MetricsPanelCtrl {
}
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}
@@ -40,6 +40,75 @@
<div class="section gf-form-group">
<h5 class="section-heading">X-Axis</h5>
<gf-form-switch class="gf-form" label="Show" label-class="width-5" checked="ctrl.panel.xaxis.show" on-change="ctrl.render()"></gf-form-switch>
<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.refresh()">
</select>
</div>
</div>
<!-- Series mode -->
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'series'">
<label class="gf-form-label width-5">Value</label>
<div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input"
ng-model="ctrl.panel.xaxis.seriesValue"
ng-options="v for v in ctrl.xAxisSeriesValues"
ng-change="ctrl.render()">
</select>
</div>
</div>
<!-- Table mode -->
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'table'">
<label class="gf-form-label width-5">Column</label>
<div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input"
ng-model="ctrl.panel.xaxis.columnIndex"
ng-options="col.index as col.text for col in ctrl.xAxisColumns"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'table'">
<label class="gf-form-label width-5">Value</label>
<div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input"
ng-model="ctrl.panel.xaxis.valueColumnIndex"
ng-options="col.index as col.text for col in ctrl.xAxisColumns"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<!-- Elastic Raw Document mode -->
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'elastic'">
<label class="gf-form-label width-5">Column</label>
<div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input"
ng-model="ctrl.panel.xaxis.esField"
ng-options="field for field in ctrl.xAxisColumns"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'elastic'">
<label class="gf-form-label width-5">Value</label>
<div class="gf-form-select-wrapper max-width-15">
<select class="gf-form-input"
ng-model="ctrl.panel.xaxis.esValueField"
ng-options="field for field in ctrl.xAxisColumns"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
</div>
</div>