mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(plugins): made panels loaded via plugin-componet directive
This commit is contained in:
parent
2a8b96b680
commit
14cc771cbe
@ -5,6 +5,7 @@ import _ from 'lodash';
|
|||||||
|
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
import {UnknownPanelCtrl} from 'app/plugins/panel/unknown/module';
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
|
function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
|
||||||
@ -45,20 +46,23 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadPanelComponentInfo(scope, attrs) {
|
function loadPanelComponentInfo(scope, attrs) {
|
||||||
|
var componentInfo: any = {
|
||||||
|
name: 'panel-plugin-' + scope.panel.type,
|
||||||
|
bindings: {dashboard: "=", panel: "=", row: "="},
|
||||||
|
attrs: {dashboard: "dashboard", panel: "panel", row: "row"},
|
||||||
|
};
|
||||||
|
|
||||||
var panelElemName = 'panel-' + scope.panel.type;
|
var panelElemName = 'panel-' + scope.panel.type;
|
||||||
let panelInfo = config.panels[scope.panel.type];
|
let panelInfo = config.panels[scope.panel.type];
|
||||||
if (!panelInfo) {
|
var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
|
||||||
// unknown
|
if (panelInfo) {
|
||||||
|
panelCtrlPromise = System.import(panelInfo.module).then(function(panelModule) {
|
||||||
|
return panelModule.PanelCtrl;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return System.import(panelInfo.module).then(function(panelModule): any {
|
return panelCtrlPromise.then(function(PanelCtrl: any) {
|
||||||
var PanelCtrl = panelModule.PanelCtrl;
|
componentInfo.Component = PanelCtrl;
|
||||||
var componentInfo = {
|
|
||||||
name: 'panel-plugin-' + panelInfo.id,
|
|
||||||
bindings: {dashboard: "=", panel: "=", row: "="},
|
|
||||||
attrs: {dashboard: "dashboard", panel: "panel", row: "row"},
|
|
||||||
Component: PanelCtrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!PanelCtrl || PanelCtrl.registered) {
|
if (!PanelCtrl || PanelCtrl.registered) {
|
||||||
return componentInfo;
|
return componentInfo;
|
||||||
|
@ -2,7 +2,6 @@ define([
|
|||||||
'./panel_menu',
|
'./panel_menu',
|
||||||
'./panel_directive',
|
'./panel_directive',
|
||||||
'./solo_panel_ctrl',
|
'./solo_panel_ctrl',
|
||||||
'./panel_loader',
|
|
||||||
'./query_ctrl',
|
'./query_ctrl',
|
||||||
'./panel_editor_tab',
|
'./panel_editor_tab',
|
||||||
'./query_editor_row',
|
'./query_editor_row',
|
||||||
|
@ -4,12 +4,18 @@ import config from 'app/core/config';
|
|||||||
|
|
||||||
import {PanelCtrl} from './panel_ctrl';
|
import {PanelCtrl} from './panel_ctrl';
|
||||||
import {MetricsPanelCtrl} from './metrics_panel_ctrl';
|
import {MetricsPanelCtrl} from './metrics_panel_ctrl';
|
||||||
import {PanelDirective} from './panel_directive';
|
|
||||||
import {QueryCtrl} from './query_ctrl';
|
import {QueryCtrl} from './query_ctrl';
|
||||||
|
|
||||||
|
class DefaultPanelCtrl extends PanelCtrl {
|
||||||
|
/** @ngInject */
|
||||||
|
constructor($scope, $injector) {
|
||||||
|
super($scope, $injector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
PanelCtrl,
|
PanelCtrl,
|
||||||
|
DefaultPanelCtrl,
|
||||||
MetricsPanelCtrl,
|
MetricsPanelCtrl,
|
||||||
PanelDirective,
|
|
||||||
QueryCtrl,
|
QueryCtrl,
|
||||||
}
|
}
|
||||||
|
@ -3,48 +3,6 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
import {PanelCtrl} from './panel_ctrl';
|
|
||||||
|
|
||||||
export class DefaultPanelCtrl extends PanelCtrl {
|
|
||||||
/** @ngInject */
|
|
||||||
constructor($scope, $injector) {
|
|
||||||
super($scope, $injector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PanelDirective {
|
|
||||||
template: string;
|
|
||||||
templateUrl: string;
|
|
||||||
bindToController: boolean;
|
|
||||||
scope: any;
|
|
||||||
controller: any;
|
|
||||||
controllerAs: string;
|
|
||||||
|
|
||||||
getDirective() {
|
|
||||||
if (!this.controller) {
|
|
||||||
this.controller = DefaultPanelCtrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
template: this.template,
|
|
||||||
templateUrl: this.templateUrl,
|
|
||||||
controller: this.controller,
|
|
||||||
controllerAs: 'ctrl',
|
|
||||||
bindToController: true,
|
|
||||||
scope: {dashboard: "=", panel: "=", row: "="},
|
|
||||||
link: (scope, elem, attrs, ctrl) => {
|
|
||||||
ctrl.init();
|
|
||||||
this.link(scope, elem, attrs, ctrl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
link(scope, elem, attrs, ctrl) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var module = angular.module('grafana.directives');
|
var module = angular.module('grafana.directives');
|
||||||
|
|
||||||
module.directive('grafanaPanel', function() {
|
module.directive('grafanaPanel', function() {
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
import config from 'app/core/config';
|
|
||||||
|
|
||||||
import {UnknownPanel} from '../../plugins/panel/unknown/module';
|
|
||||||
|
|
||||||
var directiveModule = angular.module('grafana.directives');
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
function panelLoader($compile, $http, $q, $injector, $templateCache) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
dashboard: "=",
|
|
||||||
row: "=",
|
|
||||||
panel: "="
|
|
||||||
},
|
|
||||||
link: function(scope, elem, attrs) {
|
|
||||||
|
|
||||||
function getTemplate(directive) {
|
|
||||||
if (directive.template) {
|
|
||||||
return $q.when(directive.template);
|
|
||||||
}
|
|
||||||
var cached = $templateCache.get(directive.templateUrl);
|
|
||||||
if (cached) {
|
|
||||||
return $q.when(cached);
|
|
||||||
}
|
|
||||||
return $http.get(directive.templateUrl).then(res => {
|
|
||||||
return res.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPanelAndCompile(name) {
|
|
||||||
var child = angular.element(document.createElement(name));
|
|
||||||
child.attr('dashboard', 'dashboard');
|
|
||||||
child.attr('panel', 'panel');
|
|
||||||
child.attr('row', 'row');
|
|
||||||
$compile(child)(scope);
|
|
||||||
|
|
||||||
elem.empty();
|
|
||||||
elem.append(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPanel(name, Panel) {
|
|
||||||
if (Panel.registered) {
|
|
||||||
addPanelAndCompile(name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Panel.promise) {
|
|
||||||
Panel.promise.then(() => {
|
|
||||||
addPanelAndCompile(name);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var panelInstance = $injector.instantiate(Panel);
|
|
||||||
var directive = panelInstance.getDirective();
|
|
||||||
|
|
||||||
Panel.promise = getTemplate(directive).then(template => {
|
|
||||||
directive.templateUrl = null;
|
|
||||||
directive.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
|
|
||||||
directiveModule.directive(attrs.$normalize(name), function() {
|
|
||||||
return directive;
|
|
||||||
});
|
|
||||||
Panel.registered = true;
|
|
||||||
addPanelAndCompile(name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var panelElemName = 'panel-directive-' + scope.panel.type;
|
|
||||||
let panelInfo = config.panels[scope.panel.type];
|
|
||||||
if (!panelInfo) {
|
|
||||||
addPanel(panelElemName, UnknownPanel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.import(panelInfo.module).then(function(panelModule) {
|
|
||||||
addPanel(panelElemName, panelModule.Panel);
|
|
||||||
}).catch(err => {
|
|
||||||
console.log('Panel err: ', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
directiveModule.directive('panelLoader', panelLoader);
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel';
|
import {PanelCtrl} from '../../../features/panel/panel';
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
||||||
var panelDefaults = {
|
var panelDefaults = {
|
||||||
@ -55,11 +55,4 @@ class DashListCtrl extends PanelCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DashListPanel extends PanelDirective {
|
export {DashListCtrl, DashListCtrl as PanelCtrl}
|
||||||
controller = DashListCtrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
DashListCtrl as DashListCtrl,
|
|
||||||
DashListCtrl as PanelCtrl,
|
|
||||||
}
|
|
||||||
|
@ -1,232 +0,0 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import kbn from 'app/core/utils/kbn';
|
|
||||||
import TimeSeries from '../../../core/time_series2';
|
|
||||||
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
|
||||||
|
|
||||||
// Set and populate defaults
|
|
||||||
var panelDefaults = {
|
|
||||||
links: [],
|
|
||||||
datasource: null,
|
|
||||||
maxDataPoints: 100,
|
|
||||||
interval: null,
|
|
||||||
targets: [{}],
|
|
||||||
cacheTimeout: null,
|
|
||||||
format: 'none',
|
|
||||||
prefix: '',
|
|
||||||
postfix: '',
|
|
||||||
nullText: null,
|
|
||||||
valueMaps: [
|
|
||||||
{ value: 'null', op: '=', text: 'N/A' }
|
|
||||||
],
|
|
||||||
nullPointMode: 'connected',
|
|
||||||
valueName: 'avg',
|
|
||||||
prefixFontSize: '50%',
|
|
||||||
valueFontSize: '80%',
|
|
||||||
postfixFontSize: '50%',
|
|
||||||
thresholds: '',
|
|
||||||
colorBackground: false,
|
|
||||||
colorValue: false,
|
|
||||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
|
||||||
sparkline: {
|
|
||||||
show: false,
|
|
||||||
full: false,
|
|
||||||
lineColor: 'rgb(31, 120, 193)',
|
|
||||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SingleStatCtrl extends MetricsPanelCtrl {
|
|
||||||
series: any[];
|
|
||||||
data: any[];
|
|
||||||
fontSizes: any[];
|
|
||||||
unitFormats: any[];
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor($scope, $injector) {
|
|
||||||
super($scope, $injector);
|
|
||||||
_.defaults(this.panel, panelDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
initEditMode() {
|
|
||||||
super.initEditMode();
|
|
||||||
this.icon = "fa fa-dashboard";
|
|
||||||
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
|
||||||
this.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html', 2);
|
|
||||||
this.unitFormats = kbn.getUnitFormats();
|
|
||||||
}
|
|
||||||
|
|
||||||
setUnitFormat(subItem) {
|
|
||||||
this.panel.format = subItem.value;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshData(datasource) {
|
|
||||||
return this.issueQueries(datasource)
|
|
||||||
.then(this.dataHandler.bind(this))
|
|
||||||
.catch(err => {
|
|
||||||
this.series = [];
|
|
||||||
this.render();
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSnapshot(snapshotData) {
|
|
||||||
this.updateTimeRange();
|
|
||||||
this.dataHandler(snapshotData);
|
|
||||||
}
|
|
||||||
|
|
||||||
dataHandler(results) {
|
|
||||||
this.series = _.map(results.data, this.seriesHandler.bind(this));
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
seriesHandler(seriesData) {
|
|
||||||
var series = new TimeSeries({
|
|
||||||
datapoints: seriesData.datapoints,
|
|
||||||
alias: seriesData.target,
|
|
||||||
});
|
|
||||||
|
|
||||||
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
|
|
||||||
return series;
|
|
||||||
}
|
|
||||||
|
|
||||||
setColoring(options) {
|
|
||||||
if (options.background) {
|
|
||||||
this.panel.colorValue = false;
|
|
||||||
this.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
|
||||||
} else {
|
|
||||||
this.panel.colorBackground = false;
|
|
||||||
this.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
|
||||||
}
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
invertColorOrder() {
|
|
||||||
var tmp = this.panel.colors[0];
|
|
||||||
this.panel.colors[0] = this.panel.colors[2];
|
|
||||||
this.panel.colors[2] = tmp;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDecimalsForValue(value) {
|
|
||||||
if (_.isNumber(this.panel.decimals)) {
|
|
||||||
return {decimals: this.panel.decimals, scaledDecimals: null};
|
|
||||||
}
|
|
||||||
|
|
||||||
var delta = value / 2;
|
|
||||||
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
|
||||||
|
|
||||||
var magn = Math.pow(10, -dec),
|
|
||||||
norm = delta / magn, // norm is between 1.0 and 10.0
|
|
||||||
size;
|
|
||||||
|
|
||||||
if (norm < 1.5) {
|
|
||||||
size = 1;
|
|
||||||
} else if (norm < 3) {
|
|
||||||
size = 2;
|
|
||||||
// special case for 2.5, requires an extra decimal
|
|
||||||
if (norm > 2.25) {
|
|
||||||
size = 2.5;
|
|
||||||
++dec;
|
|
||||||
}
|
|
||||||
} else if (norm < 7.5) {
|
|
||||||
size = 5;
|
|
||||||
} else {
|
|
||||||
size = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
size *= magn;
|
|
||||||
|
|
||||||
// reduce starting decimals if not needed
|
|
||||||
if (Math.floor(value) === value) { dec = 0; }
|
|
||||||
|
|
||||||
var result: any = {};
|
|
||||||
result.decimals = Math.max(0, dec);
|
|
||||||
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
var data: any = {};
|
|
||||||
this.setValues(data);
|
|
||||||
|
|
||||||
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
|
|
||||||
return Number(strVale.trim());
|
|
||||||
});
|
|
||||||
|
|
||||||
data.colorMap = this.panel.colors;
|
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
this.broadcastRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
setValues(data) {
|
|
||||||
data.flotpairs = [];
|
|
||||||
|
|
||||||
if (this.series.length > 1) {
|
|
||||||
this.inspector.error = new Error();
|
|
||||||
this.inspector.error.message = 'Multiple Series Error';
|
|
||||||
this.inspector.error.data = 'Metric query returns ' + this.series.length +
|
|
||||||
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify(this.series);
|
|
||||||
throw this.inspector.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series && this.series.length > 0) {
|
|
||||||
var lastPoint = _.last(this.series[0].datapoints);
|
|
||||||
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
|
||||||
|
|
||||||
if (_.isString(lastValue)) {
|
|
||||||
data.value = 0;
|
|
||||||
data.valueFormated = lastValue;
|
|
||||||
data.valueRounded = 0;
|
|
||||||
} else {
|
|
||||||
data.value = this.series[0].stats[this.panel.valueName];
|
|
||||||
data.flotpairs = this.series[0].flotpairs;
|
|
||||||
|
|
||||||
var decimalInfo = this.getDecimalsForValue(data.value);
|
|
||||||
var formatFunc = kbn.valueFormats[this.panel.format];
|
|
||||||
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
|
|
||||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check value to text mappings
|
|
||||||
for (var i = 0; i < this.panel.valueMaps.length; i++) {
|
|
||||||
var map = this.panel.valueMaps[i];
|
|
||||||
// special null case
|
|
||||||
if (map.value === 'null') {
|
|
||||||
if (data.value === null || data.value === void 0) {
|
|
||||||
data.valueFormated = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// value/number to text mapping
|
|
||||||
var value = parseFloat(map.value);
|
|
||||||
if (value === data.value) {
|
|
||||||
data.valueFormated = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.value === null || data.value === void 0) {
|
|
||||||
data.valueFormated = "no value";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
removeValueMap(map) {
|
|
||||||
var index = _.indexOf(this.panel.valueMaps, map);
|
|
||||||
this.panel.valueMaps.splice(index, 1);
|
|
||||||
this.render();
|
|
||||||
};
|
|
||||||
|
|
||||||
addValueMap() {
|
|
||||||
this.panel.valueMaps.push({value: '', op: '=', text: '' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +1,237 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import 'jquery.flot';
|
import 'jquery.flot';
|
||||||
import {SingleStatCtrl} from './controller';
|
|
||||||
import {PanelDirective} from '../../../features/panel/panel';
|
|
||||||
|
|
||||||
class SingleStatPanel extends PanelDirective {
|
import kbn from 'app/core/utils/kbn';
|
||||||
templateUrl = 'public/app/plugins/panel/singlestat/module.html';
|
import TimeSeries from '../../../core/time_series2';
|
||||||
controller = SingleStatCtrl;
|
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
||||||
|
|
||||||
|
// Set and populate defaults
|
||||||
|
var panelDefaults = {
|
||||||
|
links: [],
|
||||||
|
datasource: null,
|
||||||
|
maxDataPoints: 100,
|
||||||
|
interval: null,
|
||||||
|
targets: [{}],
|
||||||
|
cacheTimeout: null,
|
||||||
|
format: 'none',
|
||||||
|
prefix: '',
|
||||||
|
postfix: '',
|
||||||
|
nullText: null,
|
||||||
|
valueMaps: [
|
||||||
|
{ value: 'null', op: '=', text: 'N/A' }
|
||||||
|
],
|
||||||
|
nullPointMode: 'connected',
|
||||||
|
valueName: 'avg',
|
||||||
|
prefixFontSize: '50%',
|
||||||
|
valueFontSize: '80%',
|
||||||
|
postfixFontSize: '50%',
|
||||||
|
thresholds: '',
|
||||||
|
colorBackground: false,
|
||||||
|
colorValue: false,
|
||||||
|
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||||
|
sparkline: {
|
||||||
|
show: false,
|
||||||
|
full: false,
|
||||||
|
lineColor: 'rgb(31, 120, 193)',
|
||||||
|
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SingleStatCtrl extends MetricsPanelCtrl {
|
||||||
|
static templateUrl = 'public/app/plugins/panel/singlestat/module.html';
|
||||||
|
|
||||||
|
series: any[];
|
||||||
|
data: any[];
|
||||||
|
fontSizes: any[];
|
||||||
|
unitFormats: any[];
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $location, private linkSrv, private $timeout, private templateSrv) {
|
constructor($scope, $injector, private $location, private linkSrv, private templateSrv) {
|
||||||
super();
|
super($scope, $injector);
|
||||||
|
_.defaults(this.panel, panelDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
initEditMode() {
|
||||||
|
super.initEditMode();
|
||||||
|
this.icon = "fa fa-dashboard";
|
||||||
|
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
||||||
|
this.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html', 2);
|
||||||
|
this.unitFormats = kbn.getUnitFormats();
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnitFormat(subItem) {
|
||||||
|
this.panel.format = subItem.value;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(datasource) {
|
||||||
|
return this.issueQueries(datasource)
|
||||||
|
.then(this.dataHandler.bind(this))
|
||||||
|
.catch(err => {
|
||||||
|
this.series = [];
|
||||||
|
this.render();
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSnapshot(snapshotData) {
|
||||||
|
this.updateTimeRange();
|
||||||
|
this.dataHandler(snapshotData);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataHandler(results) {
|
||||||
|
this.series = _.map(results.data, this.seriesHandler.bind(this));
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesHandler(seriesData) {
|
||||||
|
var series = new TimeSeries({
|
||||||
|
datapoints: seriesData.datapoints,
|
||||||
|
alias: seriesData.target,
|
||||||
|
});
|
||||||
|
|
||||||
|
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
|
||||||
|
return series;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColoring(options) {
|
||||||
|
if (options.background) {
|
||||||
|
this.panel.colorValue = false;
|
||||||
|
this.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
||||||
|
} else {
|
||||||
|
this.panel.colorBackground = false;
|
||||||
|
this.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||||
|
}
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
invertColorOrder() {
|
||||||
|
var tmp = this.panel.colors[0];
|
||||||
|
this.panel.colors[0] = this.panel.colors[2];
|
||||||
|
this.panel.colors[2] = tmp;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
getDecimalsForValue(value) {
|
||||||
|
if (_.isNumber(this.panel.decimals)) {
|
||||||
|
return {decimals: this.panel.decimals, scaledDecimals: null};
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = value / 2;
|
||||||
|
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||||
|
|
||||||
|
var magn = Math.pow(10, -dec),
|
||||||
|
norm = delta / magn, // norm is between 1.0 and 10.0
|
||||||
|
size;
|
||||||
|
|
||||||
|
if (norm < 1.5) {
|
||||||
|
size = 1;
|
||||||
|
} else if (norm < 3) {
|
||||||
|
size = 2;
|
||||||
|
// special case for 2.5, requires an extra decimal
|
||||||
|
if (norm > 2.25) {
|
||||||
|
size = 2.5;
|
||||||
|
++dec;
|
||||||
|
}
|
||||||
|
} else if (norm < 7.5) {
|
||||||
|
size = 5;
|
||||||
|
} else {
|
||||||
|
size = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= magn;
|
||||||
|
|
||||||
|
// reduce starting decimals if not needed
|
||||||
|
if (Math.floor(value) === value) { dec = 0; }
|
||||||
|
|
||||||
|
var result: any = {};
|
||||||
|
result.decimals = Math.max(0, dec);
|
||||||
|
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var data: any = {};
|
||||||
|
this.setValues(data);
|
||||||
|
|
||||||
|
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
|
||||||
|
return Number(strVale.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
data.colorMap = this.panel.colors;
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.broadcastRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
setValues(data) {
|
||||||
|
data.flotpairs = [];
|
||||||
|
|
||||||
|
if (this.series.length > 1) {
|
||||||
|
this.inspector.error = new Error();
|
||||||
|
this.inspector.error.message = 'Multiple Series Error';
|
||||||
|
this.inspector.error.data = 'Metric query returns ' + this.series.length +
|
||||||
|
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify(this.series);
|
||||||
|
throw this.inspector.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.series && this.series.length > 0) {
|
||||||
|
var lastPoint = _.last(this.series[0].datapoints);
|
||||||
|
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
||||||
|
|
||||||
|
if (_.isString(lastValue)) {
|
||||||
|
data.value = 0;
|
||||||
|
data.valueFormated = lastValue;
|
||||||
|
data.valueRounded = 0;
|
||||||
|
} else {
|
||||||
|
data.value = this.series[0].stats[this.panel.valueName];
|
||||||
|
data.flotpairs = this.series[0].flotpairs;
|
||||||
|
|
||||||
|
var decimalInfo = this.getDecimalsForValue(data.value);
|
||||||
|
var formatFunc = kbn.valueFormats[this.panel.format];
|
||||||
|
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
|
||||||
|
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check value to text mappings
|
||||||
|
for (var i = 0; i < this.panel.valueMaps.length; i++) {
|
||||||
|
var map = this.panel.valueMaps[i];
|
||||||
|
// special null case
|
||||||
|
if (map.value === 'null') {
|
||||||
|
if (data.value === null || data.value === void 0) {
|
||||||
|
data.valueFormated = map.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// value/number to text mapping
|
||||||
|
var value = parseFloat(map.value);
|
||||||
|
if (value === data.value) {
|
||||||
|
data.valueFormated = map.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.value === null || data.value === void 0) {
|
||||||
|
data.valueFormated = "no value";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeValueMap(map) {
|
||||||
|
var index = _.indexOf(this.panel.valueMaps, map);
|
||||||
|
this.panel.valueMaps.splice(index, 1);
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
addValueMap() {
|
||||||
|
this.panel.valueMaps.push({value: '', op: '=', text: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
link(scope, elem, attrs, ctrl) {
|
link(scope, elem, attrs, ctrl) {
|
||||||
@ -235,7 +454,7 @@ function getColorForValue(data, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SingleStatPanel,
|
SingleStatCtrl,
|
||||||
SingleStatPanel as Panel,
|
SingleStatCtrl as PanelCtrl,
|
||||||
getColorForValue
|
getColorForValue
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../..
|
|||||||
|
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import helpers from '../../../../../test/specs/helpers';
|
import helpers from '../../../../../test/specs/helpers';
|
||||||
import {SingleStatCtrl} from '../controller';
|
import {SingleStatCtrl} from '../module';
|
||||||
|
|
||||||
describe('SingleStatCtrl', function() {
|
describe('SingleStatCtrl', function() {
|
||||||
var ctx = new helpers.ControllerTestContext();
|
var ctx = new helpers.ControllerTestContext();
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import moment from 'moment';
|
|
||||||
import * as FileExport from 'app/core/utils/file_export';
|
|
||||||
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
|
||||||
import {transformDataToTable} from './transformers';
|
|
||||||
import {tablePanelEditor} from './editor';
|
|
||||||
|
|
||||||
var panelDefaults = {
|
|
||||||
targets: [{}],
|
|
||||||
transform: 'timeseries_to_columns',
|
|
||||||
pageSize: null,
|
|
||||||
showHeader: true,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
type: 'date',
|
|
||||||
pattern: 'Time',
|
|
||||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
unit: 'short',
|
|
||||||
type: 'number',
|
|
||||||
decimals: 2,
|
|
||||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
|
||||||
colorMode: null,
|
|
||||||
pattern: '/.*/',
|
|
||||||
thresholds: [],
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [],
|
|
||||||
scroll: true,
|
|
||||||
fontSize: '100%',
|
|
||||||
sort: {col: 0, desc: true},
|
|
||||||
};
|
|
||||||
|
|
||||||
export class TablePanelCtrl extends MetricsPanelCtrl {
|
|
||||||
pageIndex: number;
|
|
||||||
dataRaw: any;
|
|
||||||
table: any;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor($scope, $injector, private annotationsSrv) {
|
|
||||||
super($scope, $injector);
|
|
||||||
this.pageIndex = 0;
|
|
||||||
|
|
||||||
if (this.panel.styles === void 0) {
|
|
||||||
this.panel.styles = this.panel.columns;
|
|
||||||
this.panel.columns = this.panel.fields;
|
|
||||||
delete this.panel.columns;
|
|
||||||
delete this.panel.fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
_.defaults(this.panel, panelDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
initEditMode() {
|
|
||||||
super.initEditMode();
|
|
||||||
this.addEditorTab('Options', tablePanelEditor, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
getExtendedMenu() {
|
|
||||||
var menu = super.getExtendedMenu();
|
|
||||||
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshData(datasource) {
|
|
||||||
this.pageIndex = 0;
|
|
||||||
|
|
||||||
if (this.panel.transform === 'annotations') {
|
|
||||||
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
|
|
||||||
this.dataRaw = annotations;
|
|
||||||
this.render();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.issueQueries(datasource)
|
|
||||||
.then(this.dataHandler.bind(this))
|
|
||||||
.catch(err => {
|
|
||||||
this.render();
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleColumnSort(col, colIndex) {
|
|
||||||
if (this.panel.sort.col === colIndex) {
|
|
||||||
if (this.panel.sort.desc) {
|
|
||||||
this.panel.sort.desc = false;
|
|
||||||
} else {
|
|
||||||
this.panel.sort.col = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.panel.sort.col = colIndex;
|
|
||||||
this.panel.sort.desc = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
dataHandler(results) {
|
|
||||||
this.dataRaw = results.data;
|
|
||||||
this.pageIndex = 0;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// automatically correct transform mode
|
|
||||||
// based on data
|
|
||||||
if (this.dataRaw && this.dataRaw.length) {
|
|
||||||
if (this.dataRaw[0].type === 'table') {
|
|
||||||
this.panel.transform = 'table';
|
|
||||||
} else {
|
|
||||||
if (this.dataRaw[0].type === 'docs') {
|
|
||||||
this.panel.transform = 'json';
|
|
||||||
} else {
|
|
||||||
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
|
|
||||||
this.panel.transform = 'timeseries_to_rows';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.table = transformDataToTable(this.dataRaw, this.panel);
|
|
||||||
this.table.sort(this.panel.sort);
|
|
||||||
this.broadcastRender(this.table);
|
|
||||||
}
|
|
||||||
|
|
||||||
exportCsv() {
|
|
||||||
FileExport.exportTableDataToCsv(this.table);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,139 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import kbn = require('app/core/utils/kbn');
|
import angular from 'angular';
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {PanelDirective} from '../../../features/panel/panel';
|
import * as FileExport from 'app/core/utils/file_export';
|
||||||
import {TablePanelCtrl} from './controller';
|
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
||||||
|
import {transformDataToTable} from './transformers';
|
||||||
|
import {tablePanelEditor} from './editor';
|
||||||
import {TableRenderer} from './renderer';
|
import {TableRenderer} from './renderer';
|
||||||
|
|
||||||
class TablePanel extends PanelDirective {
|
var panelDefaults = {
|
||||||
templateUrl = 'public/app/plugins/panel/table/module.html';
|
targets: [{}],
|
||||||
controller = TablePanelCtrl;
|
transform: 'timeseries_to_columns',
|
||||||
|
pageSize: null,
|
||||||
|
showHeader: true,
|
||||||
|
styles: [
|
||||||
|
{
|
||||||
|
type: 'date',
|
||||||
|
pattern: 'Time',
|
||||||
|
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unit: 'short',
|
||||||
|
type: 'number',
|
||||||
|
decimals: 2,
|
||||||
|
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||||
|
colorMode: null,
|
||||||
|
pattern: '/.*/',
|
||||||
|
thresholds: [],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [],
|
||||||
|
scroll: true,
|
||||||
|
fontSize: '100%',
|
||||||
|
sort: {col: 0, desc: true},
|
||||||
|
};
|
||||||
|
|
||||||
|
class TablePanelCtrl extends MetricsPanelCtrl {
|
||||||
|
static templateUrl = 'public/app/plugins/panel/table/module.html';
|
||||||
|
|
||||||
|
pageIndex: number;
|
||||||
|
dataRaw: any;
|
||||||
|
table: any;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor($scope, $injector, private annotationsSrv) {
|
||||||
|
super($scope, $injector);
|
||||||
|
this.pageIndex = 0;
|
||||||
|
|
||||||
|
if (this.panel.styles === void 0) {
|
||||||
|
this.panel.styles = this.panel.columns;
|
||||||
|
this.panel.columns = this.panel.fields;
|
||||||
|
delete this.panel.columns;
|
||||||
|
delete this.panel.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
_.defaults(this.panel, panelDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
initEditMode() {
|
||||||
|
super.initEditMode();
|
||||||
|
this.addEditorTab('Options', tablePanelEditor, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtendedMenu() {
|
||||||
|
var menu = super.getExtendedMenu();
|
||||||
|
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(datasource) {
|
||||||
|
this.pageIndex = 0;
|
||||||
|
|
||||||
|
if (this.panel.transform === 'annotations') {
|
||||||
|
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
|
||||||
|
this.dataRaw = annotations;
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.issueQueries(datasource)
|
||||||
|
.then(this.dataHandler.bind(this))
|
||||||
|
.catch(err => {
|
||||||
|
this.render();
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleColumnSort(col, colIndex) {
|
||||||
|
if (this.panel.sort.col === colIndex) {
|
||||||
|
if (this.panel.sort.desc) {
|
||||||
|
this.panel.sort.desc = false;
|
||||||
|
} else {
|
||||||
|
this.panel.sort.col = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.panel.sort.col = colIndex;
|
||||||
|
this.panel.sort.desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
dataHandler(results) {
|
||||||
|
this.dataRaw = results.data;
|
||||||
|
this.pageIndex = 0;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// automatically correct transform mode
|
||||||
|
// based on data
|
||||||
|
if (this.dataRaw && this.dataRaw.length) {
|
||||||
|
if (this.dataRaw[0].type === 'table') {
|
||||||
|
this.panel.transform = 'table';
|
||||||
|
} else {
|
||||||
|
if (this.dataRaw[0].type === 'docs') {
|
||||||
|
this.panel.transform = 'json';
|
||||||
|
} else {
|
||||||
|
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
|
||||||
|
this.panel.transform = 'timeseries_to_rows';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.table = transformDataToTable(this.dataRaw, this.panel);
|
||||||
|
this.table.sort(this.panel.sort);
|
||||||
|
this.broadcastRender(this.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportCsv() {
|
||||||
|
FileExport.exportTableDataToCsv(this.table);
|
||||||
|
}
|
||||||
|
|
||||||
link(scope, elem, attrs, ctrl) {
|
link(scope, elem, attrs, ctrl) {
|
||||||
var data;
|
var data;
|
||||||
@ -97,6 +219,6 @@ class TablePanel extends PanelDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TablePanel,
|
TablePanelCtrl,
|
||||||
TablePanel as Panel
|
TablePanelCtrl as PanelCtrl
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel';
|
import {PanelCtrl} from '../../../features/panel/panel';
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
||||||
var panelDefaults = {
|
var panelDefaults = {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import {PanelDirective} from '../../../features/panel/panel';
|
import {PanelCtrl} from '../../../features/panel/panel';
|
||||||
|
|
||||||
class UnknownPanel extends PanelDirective {
|
export class UnknownPanelCtrl extends PanelCtrl {
|
||||||
templateUrl = 'public/app/plugins/panel/unknown/module.html';
|
static templateUrl = 'public/app/plugins/panel/unknown/module.html';
|
||||||
|
|
||||||
|
constructor($scope, $injector) {
|
||||||
|
super($scope, $injector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export {
|
|
||||||
UnknownPanel,
|
|
||||||
UnknownPanel as Panel
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user