Commit a151de1d by Torkel Ödegaard

progess on adding annotations

parent 0335c1f3
import {AnnotationsSrv} from './annotations_srv'; import {AnnotationsSrv} from './annotations_srv';
import {eventEditor} from './event_editor'; import {eventEditor, EventManager} from './event_editor';
export { export {
AnnotationsSrv, AnnotationsSrv,
eventEditor eventEditor,
EventManager
}; };
...@@ -17,22 +17,15 @@ export class AnnotationEvent { ...@@ -17,22 +17,15 @@ export class AnnotationEvent {
export class EventEditorCtrl { export class EventEditorCtrl {
panelCtrl: MetricsPanelCtrl; panelCtrl: MetricsPanelCtrl;
annotation: AnnotationEvent; event: AnnotationEvent;
timeRange: {from: number, to: number}; timeRange: {from: number, to: number};
form: any; form: any;
close: any; close: any;
/** @ngInject **/ /** @ngInject **/
constructor(private annotationsSrv) { constructor(private annotationsSrv) {
this.annotation = new AnnotationEvent(); this.event.panelId = this.panelCtrl.panel.id;
this.annotation.panelId = this.panelCtrl.panel.id; this.event.dashboardId = this.panelCtrl.dashboard.id;
this.annotation.dashboardId = this.panelCtrl.dashboard.id;
this.annotation.time = moment(this.timeRange.from);
if (this.timeRange.to) {
this.annotation.timeEnd = moment(this.timeRange.to);
this.annotation.isRegion = true;
}
} }
save() { save() {
...@@ -40,15 +33,17 @@ export class EventEditorCtrl { ...@@ -40,15 +33,17 @@ export class EventEditorCtrl {
return; return;
} }
let saveModel = _.cloneDeep(this.annotation); let saveModel = _.cloneDeep(this.event);
saveModel.time = saveModel.time.valueOf(); saveModel.time = saveModel.time.valueOf();
saveModel.timeEnd = 0;
if (saveModel.isRegion) { if (saveModel.isRegion) {
saveModel.timeEnd = saveModel.timeEnd.valueOf(); saveModel.timeEnd = saveModel.timeEnd.valueOf();
}
if (saveModel.timeEnd < saveModel.time) { if (saveModel.timeEnd < saveModel.time) {
console.log('invalid time'); console.log('invalid time');
return; return;
}
} }
this.annotationsSrv.saveAnnotationEvent(saveModel).then(() => { this.annotationsSrv.saveAnnotationEvent(saveModel).then(() => {
...@@ -56,6 +51,10 @@ export class EventEditorCtrl { ...@@ -56,6 +51,10 @@ export class EventEditorCtrl {
this.close(); this.close();
}); });
} }
timeChanged() {
this.panelCtrl.render();
}
} }
export function eventEditor() { export function eventEditor() {
...@@ -67,10 +66,68 @@ export function eventEditor() { ...@@ -67,10 +66,68 @@ export function eventEditor() {
templateUrl: 'public/app/features/annotations/partials/event_editor.html', templateUrl: 'public/app/features/annotations/partials/event_editor.html',
scope: { scope: {
"panelCtrl": "=", "panelCtrl": "=",
"timeRange": "=", "event": "=",
"close": "&", "close": "&",
} }
}; };
} }
coreModule.directive('eventEditor', eventEditor); coreModule.directive('eventEditor', eventEditor);
export class EventManager {
event: AnnotationEvent;
constructor(private panelCtrl: MetricsPanelCtrl,
private elem,
private popoverSrv) {
}
editorClosed() {
console.log('editorClosed');
this.event = null;
this.panelCtrl.render();
}
updateTime(range) {
let newEvent = true;
if (this.event) {
newEvent = false;
} else {
// init new event
this.event = new AnnotationEvent();
this.event.dashboardId = this.panelCtrl.dashboard.id;
this.event.panelId = this.panelCtrl.panel.id;
}
// update time
this.event.time = moment(range.from);
this.event.isRegion = false;
if (range.to) {
this.event.timeEnd = moment(range.to);
this.event.isRegion = true;
}
// newEvent means the editor is not visible
if (!newEvent) {
this.panelCtrl.render();
return;
}
this.popoverSrv.show({
element: this.elem[0],
classNames: 'drop-popover drop-popover--form',
position: 'bottom center',
openOn: null,
template: '<event-editor panel-ctrl="panelCtrl" event="event" close="dismiss()"></event-editor>',
onClose: this.editorClosed.bind(this),
model: {
event: this.event,
panelCtrl: this.panelCtrl,
},
});
this.panelCtrl.render();
}
}
...@@ -5,29 +5,29 @@ ...@@ -5,29 +5,29 @@
<div style="display: inline-block"> <div style="display: inline-block">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-7">Title</span> <span class="gf-form-label width-7">Title</span>
<input type="text" ng-model="ctrl.annotation.title" class="gf-form-input max-width-20" required> <input type="text" ng-model="ctrl.event.title" class="gf-form-input max-width-20" required>
</div> </div>
<!-- single event --> <!-- single event -->
<div ng-if="!ctrl.annotation.isRegion"> <div ng-if="!ctrl.event.isRegion">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-7">Time</span> <span class="gf-form-label width-7">Time</span>
<input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20" input-datetime required> <input type="text" ng-model="ctrl.event.time" class="gf-form-input max-width-20" input-datetime required ng-change="ctrl.timeChanged()">
</div> </div>
</div> </div>
<!-- region event --> <!-- region event -->
<div ng-if="ctrl.annotation.isRegion"> <div ng-if="ctrl.event.isRegion">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-7">Start</span> <span class="gf-form-label width-7">Start</span>
<input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20" input-datetime required> <input type="text" ng-model="ctrl.event.time" class="gf-form-input max-width-20" input-datetime required ng-change="ctrl.timeChanged()">
</div> </div>
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-7">End</span> <span class="gf-form-label width-7">End</span>
<input type="text" ng-model="ctrl.annotation.timeEnd" class="gf-form-input max-width-20" input-datetime required> <input type="text" ng-model="ctrl.event.timeEnd" class="gf-form-input max-width-20" input-datetime required ng-change="ctrl.timeChanged()">
</div> </div>
</div> </div>
<div class="gf-form gf-form--v-stretch"> <div class="gf-form gf-form--v-stretch">
<span class="gf-form-label width-7">Description</span> <span class="gf-form-label width-7">Description</span>
<textarea class="gf-form-input width-20" rows="3" ng-model="ctrl.annotation.text" placeholder="Event description"></textarea> <textarea class="gf-form-input width-20" rows="3" ng-model="ctrl.event.text" placeholder="Event description"></textarea>
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">
......
...@@ -17,6 +17,7 @@ import {tickStep} from 'app/core/utils/ticks'; ...@@ -17,6 +17,7 @@ import {tickStep} from 'app/core/utils/ticks';
import {appEvents, coreModule} from 'app/core/core'; import {appEvents, coreModule} from 'app/core/core';
import GraphTooltip from './graph_tooltip'; import GraphTooltip from './graph_tooltip';
import {ThresholdManager} from './threshold_manager'; import {ThresholdManager} from './threshold_manager';
import {EventManager} from 'app/features/annotations/all';
import {convertValuesToHistogram, getSeriesValues} from './histogram'; import {convertValuesToHistogram, getSeriesValues} from './histogram';
coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
...@@ -27,13 +28,14 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -27,13 +28,14 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
var ctrl = scope.ctrl; var ctrl = scope.ctrl;
var dashboard = ctrl.dashboard; var dashboard = ctrl.dashboard;
var panel = ctrl.panel; var panel = ctrl.panel;
var annotations = [];
var data; var data;
var annotations;
var plot; var plot;
var sortedSeries; var sortedSeries;
var legendSideLastValue = null; var legendSideLastValue = null;
var rootScope = scope.$root; var rootScope = scope.$root;
var panelWidth = 0; var panelWidth = 0;
var eventManager = new EventManager(ctrl, elem, popoverSrv);
var thresholdManager = new ThresholdManager(ctrl); var thresholdManager = new ThresholdManager(ctrl);
var tooltip = new GraphTooltip(elem, dashboard, scope, function() { var tooltip = new GraphTooltip(elem, dashboard, scope, function() {
return sortedSeries; return sortedSeries;
...@@ -54,7 +56,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -54,7 +56,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
if (!data) { if (!data) {
return; return;
} }
annotations = ctrl.annotations; annotations = ctrl.annotations || [];
render_panel(); render_panel();
}); });
...@@ -79,20 +81,6 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -79,20 +81,6 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
} }
}, scope); }, scope);
function showAddAnnotationView(timeRange) {
popoverSrv.show({
element: elem[0],
classNames: 'drop-popover drop-popover--form',
position: 'bottom center',
openOn: 'click',
template: '<event-editor panel-ctrl="panelCtrl" time-range="timeRange" close="dismiss()"></event-editor>',
model: {
timeRange: timeRange,
panelCtrl: ctrl,
},
});
}
function getLegendHeight(panelHeight) { function getLegendHeight(panelHeight) {
if (!panel.legend.show || panel.legend.rightSide) { if (!panel.legend.show || panel.legend.rightSide) {
return 0; return 0;
...@@ -343,7 +331,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -343,7 +331,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
} }
thresholdManager.addPlotOptions(options, panel); thresholdManager.addPlotOptions(options, panel);
addAnnotations(options); addAnnotationEvents(options);
configureAxisOptions(data, options); configureAxisOptions(data, options);
sortedSeries = _.sortBy(data, function(series) { return series.zindex; }); sortedSeries = _.sortBy(data, function(series) { return series.zindex; });
...@@ -475,8 +463,12 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -475,8 +463,12 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
}; };
} }
function addAnnotations(options) { function hasAnnotationEvents() {
if (!annotations || annotations.length === 0) { return eventManager.event || annotations.length > 0 ;
}
function addAnnotationEvents(options) {
if (!hasAnnotationEvents()) {
return; return;
} }
...@@ -501,26 +493,41 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -501,26 +493,41 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
types['$__execution_error'] = ['$__no_data']; types['$__execution_error'] = ['$__no_data'];
for (var i = 0; i < annotations.length; i++) { var annotationsToShow;
var item = annotations[i]; // adding/edditing event, only show that one
if (item.newState) { if (eventManager.event) {
console.log(item.newState); const event = eventManager.event;
item.eventType = '$__' + item.newState; annotationsToShow = [
continue; {
} min: event.time.valueOf(),
title: event.title,
description: event.text,
eventType: '$__alerting',
}
];
} else {
// annotations from query
for (var i = 0; i < annotations.length; i++) {
var item = annotations[i];
if (item.newState) {
item.eventType = '$__' + item.newState;
continue;
}
if (!types[item.source.name]) { if (!types[item.source.name]) {
types[item.source.name] = { types[item.source.name] = {
color: item.source.iconColor, color: item.source.iconColor,
position: 'BOTTOM', position: 'BOTTOM',
markerSize: 5, markerSize: 5,
}; };
}
} }
annotationsToShow = annotations;
} }
options.events = { options.events = {
levels: _.keys(types).length + 1, levels: _.keys(types).length + 1,
data: annotations, data: annotationsToShow,
types: types, types: types,
}; };
} }
...@@ -653,8 +660,10 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -653,8 +660,10 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
} }
elem.bind("plotselected", function (event, ranges) { elem.bind("plotselected", function (event, ranges) {
if (ranges.ctrlKey || ranges.metaKey) { if (ranges.ctrlKey || ranges.metaKey) {
showAddAnnotationView(ranges.xaxis); scope.$apply(() => {
eventManager.updateTime(ranges.xaxis);
});
} else { } else {
scope.$apply(function() { scope.$apply(function() {
timeSrv.setTime({ timeSrv.setTime({
...@@ -666,11 +675,14 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { ...@@ -666,11 +675,14 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
}); });
elem.bind("plotclick", function (event, pos, item) { elem.bind("plotclick", function (event, pos, item) {
// Skip if range selected (added in "plotselected" event handler) if (pos.ctrlKey || pos.metaKey || eventManager.event) {
let isRangeSelection = pos.x !== pos.x1; // Skip if range selected (added in "plotselected" event handler)
let createAnnotation = !isRangeSelection && (pos.ctrlKey || pos.metaKey); let isRangeSelection = pos.x !== pos.x1;
if (createAnnotation) { if (!isRangeSelection) {
showAddAnnotationView({from: pos.x, to: null}); scope.$apply(() => {
eventManager.updateTime({from: pos.x, to: null});
});
}
} }
}); });
......
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