Merge "Add global resolution and grouping key options."

This commit is contained in:
Jenkins 2016-01-07 20:01:32 +00:00 committed by Gerrit Code Review
commit c0e53ac836
18 changed files with 300 additions and 65 deletions

View File

@ -5,7 +5,10 @@ var controllersModule = require('./_index');
/** /**
* @ngInject * @ngInject
*/ */
function GroupedRunsController(pageTitleService, healthService, runMetadataKey, name, currentDate) { function GroupedRunsController(
$scope, pageTitleService, healthService, viewService,
runMetadataKey, name, currentDate) {
// ViewModel // ViewModel
var vm = this; var vm = this;
@ -17,6 +20,10 @@ function GroupedRunsController(pageTitleService, healthService, runMetadataKey,
vm.runMetadataKey = decodeURIComponent(runMetadataKey); vm.runMetadataKey = decodeURIComponent(runMetadataKey);
vm.name = decodeURIComponent(name); vm.name = decodeURIComponent(name);
// update the global grouping key - if we arrived here directly, it will not
// be set already
viewService.groupKey(runMetadataKey);
// Updates the page title based on the selected runMetadataKey // Updates the page title based on the selected runMetadataKey
pageTitleService.update(vm.runMetadataKey); pageTitleService.update(vm.runMetadataKey);
@ -105,13 +112,17 @@ function GroupedRunsController(pageTitleService, healthService, runMetadataKey,
healthService.getRunsForRunMetadataKey(vm.runMetadataKey, vm.name, { healthService.getRunsForRunMetadataKey(vm.runMetadataKey, vm.name, {
start_date: startDate, start_date: startDate,
stop_date: stopDate, stop_date: stopDate,
datetime_resolution: 'hour' datetime_resolution: viewService.resolution().key
}).then(function(response) { }).then(function(response) {
vm.processData(response.data); vm.processData(response.data);
}); });
}; };
vm.loadData(); vm.loadData();
$scope.$on('view:resolution', function(event, resolution) {
vm.loadData();
});
} }
controllersModule.controller('GroupedRunsController', GroupedRunsController); controllersModule.controller('GroupedRunsController', GroupedRunsController);

View File

@ -5,7 +5,7 @@ var controllersModule = require('./_index');
/** /**
* @ngInject * @ngInject
*/ */
function HomeController(healthService, startDate, projectService) { function HomeController($scope, healthService, startDate, projectService, viewService) {
var byFailRateDesc = function(project1, project2) { var byFailRateDesc = function(project1, project2) {
// To get descending order, project2 should come first // To get descending order, project2 should come first
@ -65,36 +65,33 @@ function HomeController(healthService, startDate, projectService) {
}; };
}; };
function loadRunMetadataKeys() {
healthService.getRunMetadataKeys().then(function(response) {
vm.runMetadataKeys = response.data;
});
}
var loadData = function(runMetadataKey) { var loadData = function(runMetadataKey) {
var groupBy = runMetadataKey || vm.selectedRunMetadataKey;
vm.selectedRunMetadataKey = groupBy;
var start = new Date(startDate); var start = new Date(startDate);
start.setDate(start.getDate() - 20); start.setDate(start.getDate() - 20);
healthService.getRunsGroupedByMetadataPerDatetime(groupBy, { healthService.getRunsGroupedByMetadataPerDatetime(vm.groupKey, {
start_date: start, start_date: start,
datetime_resolution: 'hour' datetime_resolution: viewService.resolution().key
}).then(function(response) { }).then(function(response) {
vm.processData(response.data); processData(response.data);
}); });
}; };
// ViewModel // ViewModel
var vm = this; var vm = this;
vm.searchProject = '';
vm.selectedRunMetadataKey = 'project';
vm.loadRunMetadataKeys = loadRunMetadataKeys;
vm.processData = processData;
vm.loadData = loadData; vm.loadData = loadData;
vm.groupKey = viewService.groupKey();
vm.searchProject = '';
vm.loadData(); loadData();
vm.loadRunMetadataKeys();
$scope.$on('view:groupKey', function(event, groupKey) {
vm.groupKey = groupKey;
loadData(groupKey);
});
$scope.$on('view:resolution', function(event, resolution) {
loadData();
});
} }
controllersModule.controller('HomeController', HomeController); controllersModule.controller('HomeController', HomeController);

View File

@ -5,7 +5,7 @@ var controllersModule = require('./_index');
/** /**
* @ngInject * @ngInject
*/ */
function JobController(healthService, jobName, startDate) { function JobController($scope, healthService, viewService, jobName, startDate) {
// ViewModel // ViewModel
var vm = this; var vm = this;
@ -141,13 +141,17 @@ function JobController(healthService, jobName, startDate) {
healthService.getTestsFromBuildName(vm.name, { healthService.getTestsFromBuildName(vm.name, {
start_date: start, start_date: start,
datetime_resolution: 'hour' datetime_resolution: viewService.resolution().key
}).then(function(response) { }).then(function(response) {
vm.processData(response.data); vm.processData(response.data);
}); });
}; };
vm.loadData(); vm.loadData();
$scope.$on('view:resolution', function(event, resolution) {
vm.loadData();
});
} }
controllersModule.controller('JobController', JobController); controllersModule.controller('JobController', JobController);

View File

@ -5,7 +5,7 @@ var controllersModule = require('./_index');
/** /**
* @ngInject * @ngInject
*/ */
function TestController(healthService, testService, startDate, testId) { function TestController($scope, healthService, testService, viewService, startDate, testId) {
// ViewModel // ViewModel
var vm = this; var vm = this;
@ -119,12 +119,17 @@ function TestController(healthService, testService, startDate, testId) {
beginDate.setDate(startDate.getDate() - 15); beginDate.setDate(startDate.getDate() - 15);
healthService.getTestRunList(vm.testName, { healthService.getTestRunList(vm.testName, {
start_date: beginDate, start_date: beginDate,
stop_date: stopDate stop_date: stopDate,
datetime_resolution: viewService.resolution().key
}).then(function(response) { }).then(function(response) {
vm.processData(response.data); vm.processData(response.data);
}); });
}; };
vm.loadData(); vm.loadData();
$scope.$on('view:resolution', function(event, resolution) {
vm.loadData();
});
} }
controllersModule.controller('TestController', TestController); controllersModule.controller('TestController', TestController);

View File

@ -8,7 +8,37 @@ var directivesModule = require('./_index.js');
function crumbMenu() { function crumbMenu() {
var link = function(scope, element, attrs, ctrl, transclude) { var link = function(scope, element, attrs, ctrl, transclude) {
transclude(function(clone) { transclude(function(clone) {
angular.element(element[0].querySelector('ol')).append(clone); angular.element(element[0].querySelector('nav ul')).append(clone);
});
};
/**
* @ngInject
*/
var controller = function($scope, healthService, viewService) {
$scope.resolutionOptions = viewService.resolutionOptions();
$scope.selectedResolution = viewService.resolution();
$scope.selectedGroupKey = viewService.groupKey();
$scope.groupKeys = [];
$scope.setResolution = function(resolution) {
viewService.resolution(resolution);
};
$scope.setGroupKey = function(groupKey) {
viewService.groupKey(groupKey);
};
$scope.$on('view:resolution', function(event, resolution) {
$scope.selectedResolution = resolution;
});
$scope.$on('view:groupKey', function(event, groupKey) {
$scope.selectedGroupKey = groupKey;
});
healthService.getRunMetadataKeys().then(function(response) {
$scope.groupKeys = response.data;
}); });
}; };
@ -16,7 +46,12 @@ function crumbMenu() {
restrict: 'E', restrict: 'E',
transclude: true, transclude: true,
templateUrl: 'crumb-menu.html', templateUrl: 'crumb-menu.html',
link: link link: link,
controller: controller,
scope: {
'showGroupKey': '@',
'showResolution': '@'
}
}; };
} }

42
app/js/services/view.js Normal file
View File

@ -0,0 +1,42 @@
'use strict';
var servicesModule = require('./_index.js');
/**
* @ngInject
*/
var viewService = function($rootScope) {
var resolutionOptions = [
{ name: 'Second', key: 'sec'},
{ name: 'Minute', key: 'min' },
{ name: 'Hour', key: 'hour' },
{ name: 'Day', key: 'day' }
];
var resolution = resolutionOptions[2];
var groupKey = 'project';
return {
resolution: function(res) {
if (arguments.length === 1) {
resolution = res;
$rootScope.$broadcast('view:resolution', res);
}
return resolution;
},
resolutionOptions: function() {
return resolutionOptions;
},
groupKey: function(key) {
if (arguments.length === 1) {
groupKey = key;
$rootScope.$broadcast('view:groupKey', groupKey);
}
return groupKey;
}
};
};
servicesModule.factory('viewService', viewService);

View File

@ -0,0 +1,94 @@
.nav-breadcrumb > li:not(:last-child):after {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 17px solid transparent;
border-bottom: 17px solid transparent;
border-left: 10px solid #f8f8f8;
position: absolute;
top: 50%;
margin-top: -17px;
left: 100%;
z-index: 3;
}
.nav-breadcrumb > li:not(:last-child):before {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 17px solid transparent;
border-bottom: 17px solid transparent;
border-left: 10px solid rgb(173, 173, 173);
position: absolute;
top: 50%;
margin-top: -17px;
margin-left: 1px;
left: 100%;
z-index: 3;
}
.navbar-breadcrumb {
min-height: 10px;
}
.navbar-breadcrumb ul:not(.nav-breadcrumb) > li > a {
padding: 6px 10px;
}
.nav-breadcrumb > li {
padding:6px 12px 6px 24px;
}
.nav-breadcrumb > li > a {
padding: 0px;
}
.nav-breadcrumb li:not(:last-child):hover {
background-color: #ebebeb;
}
.nav-breadcrumb > li:first-child {
padding:6px 6px 6px 10px;
}
.btn-breadcrumb > li:last-child {
padding:6px 18px 6px 24px;
}
.nav-breadcrumb > li:not(:last-child):after {
border-left: 10px solid #f8f8f8;
}
.nav-breadcrumb > li:not(:last-child):before {
border-left: 10px solid #ccc;
}
.nav-breadcrumb > li:hover:not(:last-child):after {
border-left: 10px solid #ebebeb;
}
.nav-breadcrumb > li:hover:not(:last-child):before {
border-left: 10px solid #adadad;
}
.navbar-form-xs {
padding: 0px 20px;
margin: 5px 8px;
}
.input-xs {
height: 22px;
padding: 0px 10px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.navbar-breadcrumb .container-fluid li[dropdown]:not(:last-child):before {
background: #ebebeb;
width: 1px;
content: "";
display:block;
position: absolute;
top:0;
bottom: 0;
right: 0;
}
.navbar-breadcrumb ul.dropdown-menu a {
cursor: pointer;
}

View File

@ -332,7 +332,7 @@ $grid-columns: 12 !default;
$grid-gutter-width: 30px !default; $grid-gutter-width: 30px !default;
// Navbar collapse // Navbar collapse
//** Point at which the navbar becomes uncollapsed. //** Point at which the navbar becomes uncollapsed.
$grid-float-breakpoint: $screen-sm-min !default; $grid-float-breakpoint: 0px !default;
//** Point at which the navbar begins collapsing. //** Point at which the navbar begins collapsing.
$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;

View File

@ -6,3 +6,4 @@
@import 'nv.d3'; @import 'nv.d3';
@import 'header'; @import 'header';
@import 'footer'; @import 'footer';
@import 'breadcrumbs'

View File

@ -1,17 +1,62 @@
<ol class="breadcrumb"> <nav class="navbar-breadcrumb navbar navbar-default">
<li dropdown> <ul class="nav navbar-nav navbar-left nav-breadcrumb">
<span ng-switch on="'tests' | isState"> <li><a ui-sref="home"><fa name="home"></fa></a></li>
<a ng-switch-when="true" dropdown-toggle href>Tests <fa name="caret-down"></fa></a> <li dropdown>
<a ng-switch-default dropdown-toggle href>Overview <fa name="caret-down"></fa></a> <span ng-switch on="'tests' | isState">
</span> <a ng-switch-when="true" dropdown-toggle href>
Tests <fa name="caret-down"></fa>
</a>
<a ng-switch-default dropdown-toggle href>
Overview <fa name="caret-down"></fa>
</a>
</span>
<ul role="menu" class="dropdown-menu"> <ul role="menu" class="dropdown-menu">
<li> <li>
<a ui-sref="home"><fa name="line-chart" fw></fa> Overview</a> <a ui-sref="home"><fa name="line-chart" fw></fa> Overview</a>
</li>
<li>
<a ui-sref="tests"><fa name="bar-chart" fw></fa> Tests</a>
</li>
</ul>
</li>
</ul>
<div class="container-fluid">
<ul class="nav navbar-nav navbar-right">
<li dropdown ng-if="showGroupKey == 'true'">
<a dropdown-toggle href title="Group By Key">
<fa name="key"></fa>
&nbsp;Grouping: {{selectedGroupKey}}&nbsp;
<fa name="caret-down"></fa>
</a>
<ul role="menu" class="dropdown-menu">
<li ng-repeat="key in groupKeys">
<a ng-click="setGroupKey(key)">
{{key}}
<fa name="check"
class="pull-right"
ng-if="key == selectedGroupKey"></fa>
</a>
</li>
</ul>
</li> </li>
<li> <li dropdown ng-if="showResolution == 'true'">
<a ui-sref="tests"><fa name="bar-chart" fw></fa> Tests</a> <a dropdown-toggle href title="Resolution">
<fa name="expand"></fa>
&nbsp;Resolution: {{selectedResolution.key}}&nbsp;
<fa name="caret-down"></fa>
</a>
<ul role="menu" class="dropdown-menu">
<li ng-repeat="res in resolutionOptions">
<a ng-click="setResolution(res)">
{{res.name}}
<fa name="check"
class="pull-right"
ng-if="res == selectedResolution"></fa>
</a>
</li>
</ul>
</li> </li>
</ul> </ul>
</li> </div>
</ol> </nav>

View File

@ -1,7 +1,7 @@
<header class="bs-header"> <header class="bs-header">
<div class="container"> <div class="container">
<h1 class="page-header">{{ groupedRuns.name }} jobs</h1> <h1 class="page-header">{{ groupedRuns.name }} jobs</h1>
<crumb-menu> <crumb-menu show-resolution="true">
<li>Project: {{ groupedRuns.name }}</li> <li>Project: {{ groupedRuns.name }}</li>
</crumb-menu> </crumb-menu>
</div> </div>

View File

@ -3,7 +3,7 @@
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header">OpenStack Health</h1> <h1 class="page-header">OpenStack Health</h1>
<crumb-menu></crumb-menu> <crumb-menu show-group-key="true" show-resolution="true"></crumb-menu>
<loading-indicator></loading-indicator> <loading-indicator></loading-indicator>
</div> </div>
</div> </div>
@ -36,15 +36,6 @@
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<form> <form>
<div class="form-group">
<label for="sel1">Group information by:</label>
<select ng-init="selectedRunMetadataKey = 'project'"
ng-model="selectedRunMetadataKey"
ng-options="runMetadataKey for runMetadataKey in home.runMetadataKeys"
ng-change="home.loadData(selectedRunMetadataKey)"
class="form-control">
</select>
</div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"><i class="fa fa-search"></i></div> <div class="input-group-addon"><i class="fa fa-search"></i></div>
@ -56,7 +47,7 @@
<div class="col-lg-4" ng-repeat="project in home.projects | filter:home.searchProject"> <div class="col-lg-4" ng-repeat="project in home.projects | filter:home.searchProject">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<a ui-sref="groupedRuns({ runMetadataKey: home.selectedRunMetadataKey, name: project.name })"> <a ui-sref="groupedRuns({ runMetadataKey: home.groupKey, name: project.name })">
<h3 class="panel-title">{{project.name}}</h3> <h3 class="panel-title">{{project.name}}</h3>
</a> </a>
</div> </div>

View File

@ -1,7 +1,7 @@
<header class="bs-header"> <header class="bs-header">
<div class="container"> <div class="container">
<h1 class="page-header">{{ job.name }} tests</h1> <h1 class="page-header">{{ job.name }} tests</h1>
<crumb-menu> <crumb-menu show-resolution="true">
<li>Job: {{ job.name }}</li> <li>Job: {{ job.name }}</li>
</crumb-menu> </crumb-menu>
</div> </div>

View File

@ -1,7 +1,7 @@
<header class="bs-header"> <header class="bs-header">
<div class="container"> <div class="container">
<h1 class="page-header">{{ testCtrl.testShortName }}</h1> <h1 class="page-header">{{ testCtrl.testShortName }}</h1>
<crumb-menu> <crumb-menu show-resolution="true">
<li>Test: {{testCtrl.testShortName }}</li> <li>Test: {{testCtrl.testShortName }}</li>
</crumb-menu> </crumb-menu>
</div> </div>

View File

@ -4,11 +4,12 @@ describe('GroupedRunsController', function() {
module('app.controllers'); module('app.controllers');
}); });
var $httpBackend, $controller, healthService; var $scope, $httpBackend, $controller, healthService;
var API_ROOT = 'http://8.8.4.4:8080'; var API_ROOT = 'http://8.8.4.4:8080';
var DEFAULT_CURRENT_DATE = new Date(); var DEFAULT_CURRENT_DATE = new Date();
beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
mockConfigService(); mockConfigService();
mockHealthService(); mockHealthService();
@ -78,6 +79,7 @@ describe('GroupedRunsController', function() {
it('should process chart data correctly', function() { it('should process chart data correctly', function() {
var groupedRunsController = $controller('GroupedRunsController', { var groupedRunsController = $controller('GroupedRunsController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
runMetadataKey: 'project', runMetadataKey: 'project',
name: 'openstack/cinder', name: 'openstack/cinder',
@ -104,6 +106,7 @@ describe('GroupedRunsController', function() {
it('should process chart data rate correctly', function() { it('should process chart data rate correctly', function() {
var groupedRunsController = $controller('GroupedRunsController', { var groupedRunsController = $controller('GroupedRunsController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
runMetadataKey: 'project', runMetadataKey: 'project',
name: 'openstack/cinder', name: 'openstack/cinder',

View File

@ -3,7 +3,7 @@ describe('HomeController', function() {
module('app'); module('app');
}); });
var $controller, homeController, projectService; var $scope, $controller, homeController, projectService;
var mockResponse = { data: {} }; var mockResponse = { data: {} };
var mockMetadataKeysResponse = { var mockMetadataKeysResponse = {
data: { data: {
@ -15,7 +15,8 @@ describe('HomeController', function() {
} }
}; };
beforeEach(inject(function(_$controller_) { beforeEach(inject(function($rootScope, _$controller_) {
$scope = $rootScope.$new();
$controller = _$controller_; $controller = _$controller_;
var defaultStartDate = new Date(); var defaultStartDate = new Date();
@ -36,6 +37,7 @@ describe('HomeController', function() {
}; };
homeController = $controller('HomeController', { homeController = $controller('HomeController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
startDate: defaultStartDate, startDate: defaultStartDate,
projectService: projectService projectService: projectService

View File

@ -4,17 +4,17 @@ describe('JobController', function() {
module('app.controllers'); module('app.controllers');
}); });
var $httpBackend, $controller, healthService; var $scope, $httpBackend, $controller, healthService;
var API_ROOT = 'http://8.8.4.4:8080'; var API_ROOT = 'http://8.8.4.4:8080';
var DEFAULT_START_DATE = new Date(); var DEFAULT_START_DATE = new Date();
beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
mockConfigService(); mockConfigService();
mockHealthService(); mockHealthService();
$scope = $rootScope.$new();
$controller = _$controller_; $controller = _$controller_;
healthService = _healthService_; healthService = _healthService_;
})); }));
@ -64,6 +64,7 @@ describe('JobController', function() {
it('should process chart data correctly', function() { it('should process chart data correctly', function() {
var jobController = $controller('JobController', { var jobController = $controller('JobController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
jobName: 'gate-tempest-dsvm-neutron-full', jobName: 'gate-tempest-dsvm-neutron-full',
startDate: DEFAULT_START_DATE startDate: DEFAULT_START_DATE
@ -97,6 +98,7 @@ describe('JobController', function() {
it('should process chart data rate correctly', function() { it('should process chart data rate correctly', function() {
var jobController = $controller('JobController', { var jobController = $controller('JobController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
jobName: 'gate-tempest-dsvm-neutron-full', jobName: 'gate-tempest-dsvm-neutron-full',
startDate: DEFAULT_START_DATE startDate: DEFAULT_START_DATE
@ -115,6 +117,7 @@ describe('JobController', function() {
it('should process tests correctly', function() { it('should process tests correctly', function() {
var jobController = $controller('JobController', { var jobController = $controller('JobController', {
$scope: $scope,
healthService: healthService, healthService: healthService,
jobName: 'gate-tempest-dsvm-neutron-full', jobName: 'gate-tempest-dsvm-neutron-full',
startDate: DEFAULT_START_DATE startDate: DEFAULT_START_DATE

View File

@ -4,16 +4,17 @@ describe('TestsController', function() {
module('app.controllers'); module('app.controllers');
}); });
var $httpBackend, $controller, healthService; var $scope, $httpBackend, $controller, healthService;
var API_ROOT = 'http://8.8.4.4:8080'; var API_ROOT = 'http://8.8.4.4:8080';
var DEFAULT_START_DATE = new Date(); var DEFAULT_START_DATE = new Date();
beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
mockConfigService(); mockConfigService();
mockHealthService(); mockHealthService();
$scope = $rootScope.$new();
$controller = _$controller_; $controller = _$controller_;
healthService = _healthService_; healthService = _healthService_;
})); }));
@ -61,7 +62,8 @@ describe('TestsController', function() {
it('should process chart data correctly', function() { it('should process chart data correctly', function() {
var testsController = $controller('TestsController', { var testsController = $controller('TestsController', {
healthService: healthService healthService: healthService,
$scope: $scope
}); });
$httpBackend.flush(); $httpBackend.flush();