Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: alerts on query results #502

Merged
merged 4 commits into from
Jul 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ def make_shell_context():
@manager.command
def check_settings():
"""Show the settings as re:dash sees them (useful for debugging)."""
from types import ModuleType
for name, item in settings.all_settings().iteritems():
print "{} = {}".format(name, item)

for name in dir(settings):
item = getattr(settings, name)
if not callable(item) and not name.startswith("__") and not isinstance(item, ModuleType):
print "{} = {}".format(name, item)
@manager.command
def send_test_mail():
from redash import mail
from flask_mail import Message

mail.send(Message(subject="Test Message from re:dash", recipients=[settings.MAIL_DEFAULT_SENDER], body="Test message."))


if __name__ == '__main__':
Expand Down
8 changes: 8 additions & 0 deletions migrations/0010_create_alerts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from redash.models import db, Alert, AlertSubscription

if __name__ == '__main__':
with db.database.transaction():
Alert.create_table()
AlertSubscription.create_table()

db.close_db(None)
3 changes: 2 additions & 1 deletion rd_ui/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"trailing": true,
"smarttabs": true,
"globals": {
"angular": false
"angular": false,
"_": false
}
}
4 changes: 4 additions & 0 deletions rd_ui/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
<li><a href="/queries">Queries</a></li>
</ul>
</li>
<li>
<a href="/alerts">Alerts</a>
</li>
</ul>
<form class="navbar-form navbar-left" role="search" ng-submit="searchQueries()">
<div class="form-group">
Expand Down Expand Up @@ -164,6 +167,7 @@
<script src="/scripts/directives/query_directives.js"></script>
<script src="/scripts/directives/dashboard_directives.js"></script>
<script src="/scripts/filters.js"></script>
<script src="/scripts/controllers/alerts.js"></script>
<!-- endbuild -->

<script>
Expand Down
11 changes: 8 additions & 3 deletions rd_ui/app/scripts/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,14 @@ angular.module('redash', [
templateUrl: '/views/admin_status.html',
controller: 'AdminStatusCtrl'
});
$routeProvider.when('/admin/workers', {
templateUrl: '/views/admin_workers.html',
controller: 'AdminWorkersCtrl'

$routeProvider.when('/alerts', {
templateUrl: '/views/alerts/list.html',
controller: 'AlertsCtrl'
});
$routeProvider.when('/alerts/:alertId', {
templateUrl: '/views/alerts/edit.html',
controller: 'AlertCtrl'
});

$routeProvider.when('/', {
Expand Down
171 changes: 171 additions & 0 deletions rd_ui/app/scripts/controllers/alerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
(function() {

var AlertsCtrl = function($scope, Events, Alert) {
Events.record(currentUser, "view", "page", "alerts");
$scope.$parent.pageTitle = "Alerts";

$scope.alerts = []
Alert.query(function(alerts) {
var stateClass = {
'ok': 'label label-success',
'triggered': 'label label-danger',
'unknown': 'label label-warning'
};
_.each(alerts, function(alert) {
alert.class = stateClass[alert.state];
})
$scope.alerts = alerts;

});

$scope.gridConfig = {
isPaginationEnabled: true,
itemsByPage: 50,
maxSize: 8,
};


$scope.gridColumns = [
{
"label": "Name",
"map": "name",
"cellTemplate": '<a href="/alerts/{{dataRow.id}}">{{dataRow.name}}</a> (<a href="/queries/{{dataRow.query.id}}">query</a>)'
},
{
'label': 'Created By',
'map': 'user.name'
},
{
'label': 'State',
'cellTemplate': '<span ng-class="dataRow.class">{{dataRow.state | uppercase}}</span> since <span am-time-ago="dataRow.updated_at"></span>'
},
{
'label': 'Created At',
'cellTemplate': '<span am-time-ago="dataRow.created_at"></span>'
}
];
};

var AlertCtrl = function($scope, $routeParams, growl, Query, Events, Alert) {
$scope.$parent.pageTitle = "Alerts";

$scope.alertId = $routeParams.alertId;
if ($scope.alertId === "new") {
Events.record(currentUser, 'view', 'page', 'alerts/new');
} else {
Events.record(currentUser, 'view', 'alert', $scope.alertId);
}

$scope.onQuerySelected = function(item) {
$scope.selectedQuery = item;
item.getQueryResultPromise().then(function(result) {
$scope.queryResult = result;
$scope.alert.options.column = result.getColumnNames()[0];
});
};

if ($scope.alertId === "new") {
$scope.alert = new Alert({options: {}});
} else {
$scope.alert = Alert.get({id: $scope.alertId}, function(alert) {
$scope.onQuerySelected(new Query($scope.alert.query));
});
}

$scope.ops = ['greater than', 'less than', 'equals'];
$scope.selectedQuery = null;

$scope.getDefaultName = function() {
if (!$scope.alert.query) {
return undefined;
}
return _.template("<%= query.name %>: <%= options.column %> <%= options.op %> <%= options.value %>", $scope.alert);
};

$scope.searchQueries = function (term) {
if (!term || term.length < 3) {
return;
}

Query.search({q: term}, function(results) {
$scope.queries = results;
});
};

$scope.saveChanges = function() {
if ($scope.alert.name === undefined || $scope.alert.name === '') {
$scope.alert.name = $scope.getDefaultName();
}

$scope.alert.$save(function() {
growl.addSuccessMessage("Saved.");
}, function() {
growl.addErrorMessage("Failed saving alert.");
});
};
};

angular.module('redash.directives').directive('alertSubscribers', ['AlertSubscription', function (AlertSubscription) {
return {
restrict: 'E',
replace: true,
templateUrl: '/views/alerts/subscribers.html',
scope: {
'alertId': '='
},
controller: function ($scope) {
$scope.subscribers = AlertSubscription.query({alertId: $scope.alertId});
}
}
}]);

angular.module('redash.directives').directive('subscribeButton', ['AlertSubscription', 'growl', function (AlertSubscription, growl) {
return {
restrict: 'E',
replace: true,
template: '<button class="btn btn-default btn-xs" ng-click="toggleSubscription()"><i ng-class="class"></i></button>',
controller: function ($scope) {
var updateClass = function() {
if ($scope.subscription) {
$scope.class = "fa fa-eye-slash";
} else {
$scope.class = "fa fa-eye";
}
}

$scope.subscribers.$promise.then(function() {
$scope.subscription = _.find($scope.subscribers, function(subscription) {
return (subscription.user.email == currentUser.email);
});

updateClass();
});

$scope.toggleSubscription = function() {
if ($scope.subscription) {
$scope.subscription.$delete(function() {
$scope.subscribers = _.without($scope.subscribers, $scope.subscription);
$scope.subscription = undefined;
updateClass();
}, function() {
growl.addErrorMessage("Failed saving subscription.");
});
} else {
$scope.subscription = new AlertSubscription({alert_id: $scope.alertId});
$scope.subscription.$save(function() {
$scope.subscribers.push($scope.subscription);
updateClass();
}, function() {
growl.addErrorMessage("Unsubscription failed.");
});
}
}
}
}
}]);

angular.module('redash.controllers')
.controller('AlertsCtrl', ['$scope', 'Events', 'Alert', AlertsCtrl])
.controller('AlertCtrl', ['$scope', '$routeParams', 'growl', 'Query', 'Events', 'Alert', AlertCtrl])

})();
6 changes: 2 additions & 4 deletions rd_ui/app/scripts/controllers/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
{
'label': 'Created By',
'map': 'user_name'
'map': 'user.name'
},
{
'label': 'Created At',
Expand All @@ -45,7 +45,6 @@
Query.search({q: $scope.term }, function(results) {
$scope.queries = _.map(results, function(query) {
query.created_at = moment(query.created_at);
query.user_name = query.user.name;
return query;
});
});
Expand Down Expand Up @@ -93,7 +92,6 @@
$scope.allQueries = _.map(queries, function (query) {
query.created_at = moment(query.created_at);
query.retrieved_at = moment(query.retrieved_at);
query.user_name = query.user.name;
return query;
});

Expand All @@ -108,7 +106,7 @@
},
{
'label': 'Created By',
'map': 'user_name'
'map': 'user.name'
},
{
'label': 'Created At',
Expand Down
Loading