Commit 700d6005 by Torkel Ödegaard

More work on annotations (#44, #8) It is working for annotations from graphite…

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
parent bda5ae5b
......@@ -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>'+
......
<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>
<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>
\ No newline at end of file
......@@ -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.hide = function (annotation) {
annotation.enabled = !annotation.enabled;
$scope.panel.hideAll = !annotation.enabled;
$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.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;
};
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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;
}
}
\ No newline at end of file
......@@ -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;
}
\ No newline at end of file
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