mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
More work on annotations (#44, #8) It is working for annotations from graphite metrics, basic annotation editor and everything is working. Needs some final touches and support for more datasources
This commit is contained in:
@@ -10,8 +10,6 @@ function (angular, app, _) {
|
||||
|
||||
module.controller('SubmenuCtrl', function($scope) {
|
||||
var _d = {
|
||||
collapse: false,
|
||||
notice: false,
|
||||
enable: true
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ define([
|
||||
'angular',
|
||||
'underscore'
|
||||
],
|
||||
function (angular,_) {
|
||||
function (angular, _) {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
@@ -10,30 +10,29 @@ function (angular,_) {
|
||||
.directive('configModal', function($modal,$q) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem) {
|
||||
link: function(scope, elem, attrs) {
|
||||
var
|
||||
model = attrs.kbnModel,
|
||||
partial = attrs.configModal;
|
||||
|
||||
|
||||
// create a new modal. Can't reuse one modal unforunately as the directive will not
|
||||
// re-render on show.
|
||||
elem.bind('click',function(){
|
||||
if (scope.openConfigureModal) {
|
||||
scope.openConfigureModal();
|
||||
scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a temp scope so we can discard changes to it if needed
|
||||
var tmpScope = scope.$new();
|
||||
tmpScope.panel = angular.copy(scope.panel);
|
||||
tmpScope[model] = angular.copy(scope[model]);
|
||||
|
||||
tmpScope.editSave = function(panel) {
|
||||
// Correctly set the top level properties of the panel object
|
||||
_.each(panel,function(v,k) {
|
||||
scope.panel[k] = panel[k];
|
||||
scope[model][k] = panel[k];
|
||||
});
|
||||
};
|
||||
|
||||
var panelModal = $modal({
|
||||
template: './app/partials/paneleditor.html',
|
||||
template: partial,
|
||||
persist: true,
|
||||
show: false,
|
||||
scope: tmpScope,
|
||||
|
||||
@@ -44,7 +44,8 @@ function (angular, $) {
|
||||
'</span>' +
|
||||
'</span>'+
|
||||
|
||||
'<span ng-if="!panelMeta.menuItems" config-modal class="panel-text panel-title pointer" ng-show="panel.title">' +
|
||||
'<span ng-if="!panelMeta.menuItems" config-modal="./app/partials/paneleditor.html" ' +
|
||||
' kbn-model="panel" class="panel-text panel-title pointer" >' +
|
||||
'{{panel.title}}' +
|
||||
'</span>'+
|
||||
|
||||
|
||||
60
src/app/panels/annotations/editor.html
Normal file
60
src/app/panels/annotations/editor.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<div bindonce class="modal-body">
|
||||
<div class="pull-right editor-title">Annotations</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<table class="table table-striped annotation-editor-table">
|
||||
<thead>
|
||||
<th width="1%">Type</th>
|
||||
<th width="90%">Name</th>
|
||||
<th width="1%"></th>
|
||||
<th width="1%"></th>
|
||||
<th width="1%"></th>
|
||||
<th width="1%"></th>
|
||||
</thead>
|
||||
<tr ng-repeat="annotation in panel.annotations">
|
||||
<td>{{annotation.type}}</td>
|
||||
<td>{{annotation.name}}</td>
|
||||
<td><i ng-click="_.move(panel.annotations,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
|
||||
<td><i ng-click="_.move(panel.annotations,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
|
||||
<td><i ng-click="panel.annotations = _.without(panel.annotations, annotation)" class="pointer icon-remove"></i></td>
|
||||
<td><a ng-click="edit(annotation)"><i class="icon-pencil" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<h4 ng-show="currentIsNew">Add Annotation</h4>
|
||||
<h4 ng-show="!currentIsNew">Edit Annotation</h4>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Name</label>
|
||||
<input type="text" class="input-medium" ng-model='currentAnnnotation.name' placeholder="name"></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Type</label>
|
||||
<select ng-model="currentAnnnotation.type" ng-options="f for f in ['graphite metric']"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="currentAnnnotation.type === 'graphite metric'">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite target expression</label>
|
||||
<input type="text" class="span10" ng-model='currentAnnnotation.target' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="currentAnnnotation.type === 'graphite events'">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite event tags</label>
|
||||
<input type="text" ng-model='currentAnnnotation.tags' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<!-- close_edit() is provided here to allow for a scope to perform action on dismiss -->
|
||||
<button ng-show="currentIsNew" type="button" class="btn btn-success" ng-click="add()">Add annotation</button>
|
||||
<button ng-show="!currentIsNew" type="button" class="btn btn-success" ng-click="update()">Update</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="editSave(panel);close_edit();dismiss()">Close</button>
|
||||
</div>
|
||||
@@ -1,13 +1,12 @@
|
||||
<div ng-controller='AnnotationsCtrl' ng-init="init()">
|
||||
|
||||
<!-- <div class="submenu-toggle" ng-class="{'annotation-disabled': panel.hideAll }">
|
||||
<a ng-click="hideAll();" class="small">Hide All</a>
|
||||
<i class="icon-ok"></i>
|
||||
</div>
|
||||
-->
|
||||
<div class="submenu-toggle" ng-repeat="annotation in annotationList" ng-class="{'annotation-disabled': !annotation.enabled }">
|
||||
<div class="submenu-toggle" ng-repeat="annotation in panel.annotations" ng-class="{'annotation-disabled': !annotation.enable }">
|
||||
<i class="annotation-color-icon icon-minus"></i>
|
||||
<a ng-click="hide(annotation)" class="small">{{annotation.name}}</a>
|
||||
<i class="icon-ok"></i>
|
||||
</div>
|
||||
|
||||
<div class="submenu-control-edit">
|
||||
<i class="icon-cog pointer" bs-modal="'app/panels/annotations/editor.html'" bs-tooltip="'Edit annotations'" ></i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -14,7 +14,7 @@ function (angular, app, _) {
|
||||
var module = angular.module('kibana.panels.annotations', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('AnnotationsCtrl', function($scope, dashboard, annotationsSrv, $rootScope) {
|
||||
module.controller('AnnotationsCtrl', function($scope, dashboard, $rootScope) {
|
||||
|
||||
$scope.panelMeta = {
|
||||
status : "Stable",
|
||||
@@ -23,26 +23,46 @@ function (angular, app, _) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
annotations: []
|
||||
};
|
||||
|
||||
var annotationDefaults = {
|
||||
name: '',
|
||||
type: 'graphite metric'
|
||||
};
|
||||
|
||||
_.defaults($scope.panel,_d);
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.annotationList = annotationsSrv.annotationList;
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.hideAll = function () {
|
||||
$scope.panel.hideAll = !$scope.panel.hideAll;
|
||||
$scope.getAnnotationInfo = function(annotation) {
|
||||
return annotation.target;
|
||||
};
|
||||
|
||||
_.each($scope.annotationList, function(annotation) {
|
||||
annotation.enabled = !$scope.panel.hideAll;
|
||||
});
|
||||
$scope.edit = function(annotation) {
|
||||
$scope.currentAnnnotation = annotation;
|
||||
$scope.currentIsNew = false;
|
||||
};
|
||||
|
||||
$scope.getInfo = function(annotation) {
|
||||
return annotation.target;
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.add = function() {
|
||||
$scope.panel.annotations.push($scope.currentAnnnotation);
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
};
|
||||
|
||||
$scope.hide = function (annotation) {
|
||||
annotation.enabled = !annotation.enabled;
|
||||
$scope.panel.hideAll = !annotation.enabled;
|
||||
|
||||
annotation.enable = !annotation.enable;
|
||||
$rootScope.$broadcast('refresh');
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 1">
|
||||
<div class="row-fluid">
|
||||
<div class="editor-row">
|
||||
<div class="span8">
|
||||
<h4>Rows</h4>
|
||||
<table class="table table-striped">
|
||||
@@ -69,9 +69,6 @@
|
||||
<input type="text" class="input-mini" ng-model='row.height'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 2" ng-controller="dashLoader">
|
||||
@@ -124,7 +121,7 @@
|
||||
<div ng-if="editor.index == 2">
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Pulldowns</h5>
|
||||
<h5>Feature toggles</h5>
|
||||
<div class="editor-option" ng-repeat="pulldown in dashboard.current.pulldowns">
|
||||
<label class="small" style="text-transform:capitalize;">{{pulldown.type}}</label><input type="checkbox" ng-model="pulldown.enable" ng-checked="pulldown.enable">
|
||||
</div>
|
||||
|
||||
@@ -7,27 +7,35 @@ define([
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('annotationsSrv', function(dashboard, datasourceSrv, $q, alertSrv) {
|
||||
module.service('annotationsSrv', function(dashboard, datasourceSrv, $q, alertSrv, $rootScope) {
|
||||
var promiseCached;
|
||||
var annotationPanel;
|
||||
|
||||
this.init = function() {
|
||||
this.annotationList = [
|
||||
/* {
|
||||
type: 'graphite-target',
|
||||
enabled: false,
|
||||
target: 'metrics_data.mysite.dolph.counters.payment.cart_klarna_payment_completed.count',
|
||||
name: 'deploys',
|
||||
},
|
||||
{
|
||||
type: 'graphite-target',
|
||||
enabled: true,
|
||||
target: 'metrics_data.mysite.dolph.counters.payment.cart_paypal_payment_completed.count',
|
||||
name: 'restarts',
|
||||
}*/
|
||||
];
|
||||
$rootScope.$on('refresh', this.clearCache);
|
||||
$rootScope.$on('dashboard-loaded', this.dashboardLoaded);
|
||||
|
||||
this.dashboardLoaded();
|
||||
};
|
||||
|
||||
this.dashboardLoaded = function () {
|
||||
annotationPanel = _.findWhere(dashboard.current.pulldowns, { type: 'annotations' });
|
||||
};
|
||||
|
||||
this.clearCache = function() {
|
||||
promiseCached = null;
|
||||
};
|
||||
|
||||
this.getAnnotations = function(rangeUnparsed) {
|
||||
var graphiteAnnotations = _.where(this.annotationList, { type: 'graphite-target', enabled: true });
|
||||
if (!annotationPanel.enable) {
|
||||
return $q.when(null);
|
||||
}
|
||||
|
||||
if (promiseCached) {
|
||||
return promiseCached;
|
||||
}
|
||||
|
||||
var graphiteAnnotations = _.where(annotationPanel.annotations, { type: 'graphite metric', enable: true });
|
||||
var graphiteTargets = _.map(graphiteAnnotations, function(annotation) {
|
||||
return { target: annotation.target };
|
||||
});
|
||||
@@ -43,7 +51,7 @@ define([
|
||||
maxDataPoints: 100
|
||||
};
|
||||
|
||||
return datasourceSrv.default.query(graphiteQuery)
|
||||
promiseCached = datasourceSrv.default.query(graphiteQuery)
|
||||
.then(function(results) {
|
||||
return _.reduce(results.data, function(list, target) {
|
||||
_.each(target.datapoints, function (values) {
|
||||
@@ -51,12 +59,13 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
list.push({
|
||||
min: values[1] * 1000,
|
||||
max: values[1] * 1000,
|
||||
eventType: "annotation",
|
||||
title: null,
|
||||
description: "<small><i class='icon-tag icon-flip-vertical'></i>test</small><br>"+
|
||||
description: "<small>" + target.target + "</small><br>"+
|
||||
moment(values[1] * 1000).format('YYYY-MM-DD HH:mm:ss'),
|
||||
score: 1
|
||||
});
|
||||
@@ -68,6 +77,8 @@ define([
|
||||
.then(null, function() {
|
||||
alertSrv.set('Annotations','Could not fetch annotations','error');
|
||||
});
|
||||
|
||||
return promiseCached;
|
||||
};
|
||||
|
||||
// Now init
|
||||
|
||||
@@ -28,7 +28,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
|
||||
failover: false,
|
||||
panel_hints: true,
|
||||
rows: [],
|
||||
pulldowns: [ { type: 'filtering' }, /*{ type: 'annotations' }*/ ],
|
||||
pulldowns: [ { type: 'filtering' }, { type: 'annotations' } ],
|
||||
nav: [ { type: 'timepicker' } ],
|
||||
services: {},
|
||||
loader: {
|
||||
@@ -131,13 +131,13 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
|
||||
});
|
||||
}
|
||||
|
||||
/*var annotations = _.findWhere(dashboard.pulldowns, {type: 'annotations'});
|
||||
var annotations = _.findWhere(dashboard.pulldowns, {type: 'annotations'});
|
||||
if (!annotations) {
|
||||
dashboard.pulldowns.push({
|
||||
type: 'annotations',
|
||||
enable: false
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
return dashboard;
|
||||
};
|
||||
@@ -177,6 +177,8 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
|
||||
// Take out any that we're not allowed to add from the gui.
|
||||
self.availablePanels = _.difference(self.availablePanels,config.hidden_panels);
|
||||
|
||||
$rootScope.$emit('dashboard-loaded');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
2
src/css/bootstrap.dark.min.css
vendored
2
src/css/bootstrap.dark.min.css
vendored
File diff suppressed because one or more lines are too long
2
src/css/bootstrap.light.min.css
vendored
2
src/css/bootstrap.light.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -142,7 +142,7 @@
|
||||
padding: 0 4px;
|
||||
i {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -565,57 +565,8 @@ div.flot-text {
|
||||
color: @white;
|
||||
}
|
||||
|
||||
// Filter submenu
|
||||
.filtering-container {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.filtering-container label {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.filtering-container input[type=checkbox] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.filter-panel-filter {
|
||||
display:inline-block;
|
||||
vertical-align: top;
|
||||
padding: 4px 10px 3px 10px;
|
||||
border-right: 1px solid @submenuBorder;
|
||||
}
|
||||
|
||||
.filter-panel-filter:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.filter-panel-filter ul {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.filter-deselected {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.filter-action {
|
||||
float:right;
|
||||
padding-right: 2px;
|
||||
margin-bottom: 0px !important;
|
||||
margin-left: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.add-filter-action {
|
||||
padding: 3px 10px 0px 5px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.filter-mandate {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-apply {
|
||||
float:right;
|
||||
.annotation-editor-table {
|
||||
td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,15 @@
|
||||
}
|
||||
|
||||
.submenu-panel {
|
||||
padding: 0 10px 0 17px;
|
||||
padding: 0 4px 0 8px;
|
||||
border-right: 1px solid @submenuBorder;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.submenu-panel:first-child {
|
||||
padding-left: 17px;
|
||||
}
|
||||
|
||||
.submenu-panel-title {
|
||||
float: left;
|
||||
text-transform: uppercase;
|
||||
@@ -30,12 +34,79 @@
|
||||
.submenu-toggle {
|
||||
padding: 4px 0 3px 8px;
|
||||
float: left;
|
||||
.annotation-color-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-toggle:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.submenu-control-edit {
|
||||
padding: 4px 4px 3px 8px;
|
||||
float: right;
|
||||
border-left: 1px solid @submenuBorder;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.annotation-disabled, .annotation-disabled a {
|
||||
color: darken(@textColor, 25%);
|
||||
}
|
||||
|
||||
|
||||
// Filter submenu
|
||||
.filtering-container {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.filtering-container label {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.filtering-container input[type=checkbox] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.filter-panel-filter {
|
||||
display:inline-block;
|
||||
vertical-align: top;
|
||||
padding: 4px 10px 3px 10px;
|
||||
border-right: 1px solid @submenuBorder;
|
||||
}
|
||||
|
||||
.filter-panel-filter:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.filter-panel-filter ul {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.filter-deselected {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.filtering-container .filter-action {
|
||||
float:right;
|
||||
padding-right: 2px;
|
||||
margin-bottom: 0px !important;
|
||||
margin-left: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.add-filter-action {
|
||||
padding: 3px 5px 0px 5px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.filter-mandate {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-apply {
|
||||
float:right;
|
||||
}
|
||||
Reference in New Issue
Block a user