Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Merge djmitche/build-relengapi:bootstrap (PR #162)
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Dec 16, 2014
2 parents 5049edb + bffac0c commit 270ba25
Show file tree
Hide file tree
Showing 40 changed files with 9,736 additions and 324 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
include docs/conf.py
recursive-include docs *.rst
recursive-include docs/_static *
recursive-include relengapi *.html *.jpg *.css *.js *.txt *.map
recursive-include relengapi *.html *.jpg *.css *.js *.txt *.map *.eot *.svg *.ttf *.woff
include LICENSE.txt
include README.md
prune docs_build_dir
11 changes: 10 additions & 1 deletion docs/development/@relengapi/web-ui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ Javascript Support
RelengAPI includes the following Javascript libraries.
You may assume these are present in an Angular template.

* `jQuery 1.7.2 <http://jquery.com/>`_
* `jQuery 1.11.1 <http://jquery.com/>`_
* `Angular-1.2.9 <https://angularjs.org/>`_
* `Alertify <http://fabien-d.github.io/alertify.js/>`_
* `Bootstrap 3.3.1 <http://getbootstrap.com/getting-started/#download>`_
* `Moment.js 2.8.4 <http://momentjs.com/>`_ and `Angular-Moment <https://github.com/urish/angular-moment>`_ (note that your module must depend on `'angularMoment'` to get this functionality)

Rendering an Angular Template
.............................
Expand Down Expand Up @@ -207,6 +209,13 @@ To alert the user, use something as simple as
alertify.success("token issued");
Angular Directives
..................

The following directives are available in any Angular template that requires the ``relengapi`` module:

* ``<perm>foo.bar</perm>`` -- renders a permission name

Angular Services
................

Expand Down
37 changes: 21 additions & 16 deletions relengapi/blueprints/auth/static/account.html
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div ng-app='auth' ng-controller='AuthController'>
<h1>Account</h1>
<p ng-show="initial_data.user.authenticated_email">
You are authenticated as <span class="user_email">{{ initial_data.user.authenticated_email }}</span>.
</p>
<p ng-hide="initial_data.user.authenticated_email">
You are not authenticated.
</p>
<h1>Permissions</h1>
<div ng-show="initial_data.user.permissions">
<ul>
<li ng-repeat="perm in initial_data.user.permissions">
<span class="permission">{{perm.name}}</span> - <span class="action-docstring">{{perm.doc}}</span></li>
</ul>
<div ng-app='auth' ng-controller='AuthController' class="row">
<div class="col-xs-12 col-md-5">
<h1>Account</h1>
<p ng-show="initial_data.user.authenticated_email">
You are authenticated as <span class="user_email">{{ initial_data.user.authenticated_email }}</span>.
</p>
<p ng-hide="initial_data.user.authenticated_email">
You are not authenticated.
</p>
</div>
<div ng-hide="initial_data.user.permissions">
<p>You don't have permission to do anything. Sorry about that.</p>
<div class="col-xs-12 col-md-7">
<h1>Permissions</h1>
<p>You have the following permissions:</p>
<div ng-show="initial_data.user.permissions">
<ul>
<li ng-repeat="perm in initial_data.user.permissions">
<span class="permission">{{perm.name}}</span> - <span class="action-docstring">{{perm.doc}}</span></li>
</ul>
</div>
<div ng-hide="initial_data.user.permissions">
<p>You don't have permission to do anything. Sorry about that.</p>
</div>
</div>
</div>
1 change: 0 additions & 1 deletion relengapi/blueprints/badpenny/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def permitted():
def root():
return angular.template('badpenny.html',
url_for('.static', filename='badpenny.js'),
url_for('.static', filename='badpenny.css'),
tasks=api.get_data(list_tasks))


Expand Down
58 changes: 0 additions & 58 deletions relengapi/blueprints/badpenny/static/badpenny.css

This file was deleted.

75 changes: 35 additions & 40 deletions relengapi/blueprints/badpenny/static/badpenny.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,42 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div ng-app='badpenny' ng-controller="TasksController">
<style>
</style>
<h1>Active Tasks <button ng-click="refresh()">Refresh</button></h1>
<h1>Bad Penny Tasks <button class="pull-right btn btn-default" ng-click="refresh()">Refresh</button></h1>
<div class="tasklist">
<div ng-repeat="task in tasks | orderBy:'name'">
<!-- TODO: use an icon rather than colored text -->
<span ng-class="{'last-never': task.last_success == -1,
'last-fail': task.last_success == 0,
'last-success': task.last_success == 1}">
{{task.name}}</span>
<!-- TODO: some nice twirldown here -->
(<a href="#" ng-click="expandTask(task.name)">expand</a>)
<!-- TODO: maybe transplant this DOM element, rather than showing/hiding? -->
<div class="task-detail" ng-show="expandedTask == task.name">
<table class="joblist">
<thead>
<tr>
<th>Created</th><th>Started</th><th>Completed</th>
<th>Links</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="job in task.jobs | orderBy:'created_at'",
ng-class="{'job-success': job.successful, 'job-fail': !job.successful}">
<td>{{job.created_at}}</td>
<td>{{job.started_at}}</td>
<td>{{job.completed_at}}</td>
<td>
[<a ng-click="expandJob(job)" href="#">logs</a>]
[<a href="/badpenny/jobs/{{job.id}}">details</a>]
</td>
</tr>
</tbody>
</table>
<ul class="list-group">
<li ng-repeat="task in tasks | orderBy:'name'" class="list-group-item">
<div ng-click="toggleTask(task)">
<status-icon
bad-when="task.last_success == 0"
good-when="task.last_success == 1"
gray-when="task.last_success == -1"></status-icon>
{{task.name}} <small class="text-muted">{{task.schedule}}</small>
<span class="caret"></span>
</div>
</div>
</div>
<div class="logs" ng-show="expandedLogs != null">
<div class="logs-content">
<a class="close" href="#" ng-click="closeJob()">X</a>
<pre>{{expandedLogs}}</pre>
</div>
<ul class="list-group" ng-if="task.jobs != null" style="margin-bottom: 0px;">
<li ng-repeat="job in task.jobs | orderBy:'created_at'"
class="list-group-item"
ng-click="toggleJob(job)">
<small>
<status-icon good-when="job.successful" bad-when="!job.successful"></status-icon>
</small>
<small ng-if="!job.started_at">
Job created {{job.created_at | amDateFormat:'l LTS'}}, not started
</small>
<small ng-if="job.started_at && !job.completed_at">
Job started {{job.started_at | amDateFormat:'l LTS'}}, not finished yet
</small>
<small ng-if="job.completed_at">
Job started {{job.started_at | amDateFormat:'l LTS'}},
finished {{humanJobDuration(job)}}
</small>
(<a href="/badpenny/jobs/{{job.id}}">details</a>) <span class="caret"></span>
<div ng-if="job.logs != null">
<pre>{{job.logs}}</pre>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
94 changes: 70 additions & 24 deletions relengapi/blueprints/badpenny/static/badpenny.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
angular.module('badpenny', ['relengapi', 'initial_data']);
angular.module('badpenny', ['relengapi', 'initial_data', 'angularMoment']);

angular.module('badpenny').controller('TasksController',
function($scope, restapi, initial_data) {
Expand All @@ -14,50 +14,96 @@ angular.module('badpenny').controller('TasksController',
};

$scope.tasks = tasksByName(initial_data.tasks);
$scope.expandedTask = null;
$scope.expandedLogs = null;

$scope.expandTask = function(name) {
$scope.expandedTask = name;

restapi.get('/badpenny/tasks/' + name, {while: 'fetching task'})
var loadJobs = function(task) {
restapi.get('/badpenny/tasks/' + task.name, {while: 'fetching task'})
.then(function (data, status, headers, config) {
$scope.tasks[name] = data.data.result;
$scope.tasks[task.name] = data.data.result;
});
};

$scope.expandJob = function(job) {
$scope.expandedLogs = 'loading';
$scope.toggleTask = function(task) {
if (task.jobs) {
task.jobs = null;
} else {
loadJobs(task);
}
};

var loadLogs = function(job) {
job.logs = 'loading';
restapi.get('/badpenny/jobs/' + job.id + '/logs',
{while: 'fetching logs', expected_status: 404})
.then(function (data, status, headers, config) {
$scope.expandedLogs = data.data.result.content;
job.logs = data.data.result.content;
}, function (data, status, header, config) {
if (data.status == 404) {
$scope.expandedLogs = '(no logs)';
job.logs = '(no logs)';
}
});
};

$scope.closeJob = function() {
$scope.expandedLogs = null;
$scope.toggleJob = function(job) {
if (job.logs) {
job.logs = null;
} else {
loadLogs(job);
}
};

$scope.humanJobDuration = function(job) {
var dur = moment.duration(moment(job.started_at).diff(job.completed_at));
if (dur < 100) {
return "instantly";
} else if (dur < 10000) {
// humanize just says "a few seconds", which isn't good enough
return "in " + (dur / 1000).toFixed(2) + ' seconds';
} else {
return "in " + moment.duration(diff).humanize();
}
};

$scope.refresh = function() {
restapi.get('/badpenny/tasks', {while: 'refreshing tasks'})
.then(function (data, status, headers, config) {
// copy over the job from the expanded task
var newTasks = tasksByName(data.data.result);
var expandedTask = $scope.tasks[$scope.expandedTask];
if (expandedTask) {
newTasks[expandedTask.name].jobs = expandedTask.jobs;
}
$scope.tasks = newTasks;
// re-request any existing job data, and start refreshing it
angular.forEach(data.data.result, function(task) {
if ($scope.tasks[task.name] && $scope.tasks[task.name].jobs) {
task.jobs = $scope.tasks[task.name].jobs;
loadTask(task);
}
});
$scope.tasks = tasksByName(data.data.result);
});
};
});

// reload the currently expanded task simultaneously
if ($scope.expandedTask) {
$scope.expandTask($scope.expandedTask);
angular.module('badpenny').directive('statusIcon', function() {
var refresh = function(scope) {
if (scope.good) {
scope.status = 'good';
} else if (scope.bad) {
scope.status = 'bad';
} else if (scope.gray) {
scope.status = 'gray';
} else {
scope.status = '';
}
};

return {
restrict: 'E',
replace: true,
templateUrl: 'static/statusIcon.html',
scope: {
bad: '=badWhen',
good: '=goodWhen',
gray: '=grayWhen',
},
link: function(scope, element, attrs) {
scope.$watch('bad', function() { refresh(scope); });
scope.$watch('good', function() { refresh(scope); });
scope.$watch('gray', function() { refresh(scope); });
},
};
});
11 changes: 11 additions & 0 deletions relengapi/blueprints/badpenny/static/statusIcon.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<span ng-class="{
'text-success': status == 'good',
'text-danger': status == 'bad',
'text-muted': status == 'gray'
}">
<span class="glyphicon" ng-class="{
'glyphicon-minus-sign': status == 'bad',
'glyphicon-ok-sign': status == 'good',
'glyphicon-question-sign': status == 'gray'
}"></span>
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
{% block widget_title %}<a href="/badpenny">Bad Penny</a>{% endblock %}
{% block widget_content %}
<p>
Badpenny handles scheduling and monitoring of periodic tasks.
Bad Penny handles scheduling and monitoring of periodic tasks.
</p>
{% endblock %}
20 changes: 20 additions & 0 deletions relengapi/blueprints/tokenauth/static/permissionSelector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>Permission</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="perm in available_permissions|orderBy:'name'"
ng-class="{'success': permissions.indexOf(perm.name) != -1}"
ng-click="togglePermission(perm)">
</td><td>
<perm>{{perm.name}}</perm>
</td><td>
<small>{{perm.doc}}</small>
</td>
</tr>
</tbody>
</table>

Loading

0 comments on commit 270ba25

Please sign in to comment.