Add user-selectable date and duration picker.
This adds a new date and duration picker to the crumb menu, along with functionality in the view service to manage window size preferences for views and users. Each controller can now specify allowed period sizes and a preferred default which can be overridden by the user. The user may also request an arbitrary end date, which defaults to the current date. A new library, moment, was added to help with the date caluclations, as the builtin date utilities lack a huge amount of needed functionality for computing date differences. Note that a run of `npm install` may be needed to update local installations to reflect the change. Change-Id: I64c1629e527fcf5b4a167fba50c2bb3fe80a2e96
This commit is contained in:
parent
f95c61d0d6
commit
4473e6e6f6
|
@ -7,12 +7,12 @@ var controllersModule = require('./_index');
|
|||
*/
|
||||
function GroupedRunsController(
|
||||
$scope, pageTitleService, healthService, viewService,
|
||||
runMetadataKey, name, currentDate, $location) {
|
||||
runMetadataKey, name, $location) {
|
||||
|
||||
// ViewModel
|
||||
var vm = this;
|
||||
|
||||
vm.searchJob = '';
|
||||
vm.searchJob = $location.search().searchJob || '';
|
||||
|
||||
// decodeURI is needed here because project names contains slash as part
|
||||
// of the name. As this come from an URL part and URL can be encoded,
|
||||
|
@ -20,6 +20,8 @@ function GroupedRunsController(
|
|||
vm.runMetadataKey = decodeURIComponent(runMetadataKey);
|
||||
vm.name = decodeURIComponent(name);
|
||||
vm.recentRuns = [];
|
||||
vm.loaded = false;
|
||||
vm.hold = 0;
|
||||
|
||||
// update the global grouping key - if we arrived here directly, it will not
|
||||
// be set already
|
||||
|
@ -28,6 +30,35 @@ function GroupedRunsController(
|
|||
// Updates the page title based on the selected runMetadataKey
|
||||
pageTitleService.update(vm.runMetadataKey);
|
||||
|
||||
var configurePeriods = function() {
|
||||
vm.hold += 1;
|
||||
|
||||
var res = viewService.resolution();
|
||||
var min = null;
|
||||
var max = null;
|
||||
var preference = null;
|
||||
|
||||
if (res.key === 'sec') {
|
||||
max = { hours: 6 };
|
||||
preference = { hours: 1 };
|
||||
} else if (res.key === 'min') {
|
||||
max = { days: 1 };
|
||||
preference = { hours: 12 };
|
||||
} else if (res.key === 'hour') {
|
||||
min = { hours: 12 };
|
||||
max = { months: 3 };
|
||||
preference = { weeks: 2 };
|
||||
} else if (res.key === 'day') {
|
||||
min = { hours: 48 };
|
||||
preference = { months: 3 };
|
||||
}
|
||||
|
||||
viewService.periods(min, max, true);
|
||||
viewService.preferredDuration(preference);
|
||||
|
||||
vm.hold -= 1;
|
||||
};
|
||||
|
||||
vm.processData = function(data) {
|
||||
// prepare chart data
|
||||
var jobs = {};
|
||||
|
@ -106,26 +137,37 @@ function GroupedRunsController(
|
|||
};
|
||||
|
||||
vm.loadData = function() {
|
||||
if (vm.hold > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
healthService.getRunsForRunMetadataKey(vm.runMetadataKey, vm.name, {
|
||||
start_date: viewService.windowStart(currentDate, 20),
|
||||
stop_date: currentDate,
|
||||
start_date: viewService.periodStart(),
|
||||
stop_date: viewService.periodEnd(),
|
||||
datetime_resolution: viewService.resolution().key
|
||||
}).then(function(response) {
|
||||
vm.processData(response.data);
|
||||
vm.loaded = true;
|
||||
});
|
||||
healthService.getRecentGroupedRuns(vm.runMetadataKey, vm.name).then(function(response) {
|
||||
vm.recentRuns = response.data;
|
||||
});
|
||||
};
|
||||
|
||||
vm.searchJob = $location.search().searchJob || '';
|
||||
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
|
||||
$scope.$on('view:resolution', function(event, resolution) {
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
});
|
||||
|
||||
$scope.$on('view:period', function(event, corrected) {
|
||||
if (vm.loaded && !corrected) {
|
||||
vm.loadData();
|
||||
}
|
||||
});
|
||||
|
||||
vm.onSearchChange = function() {
|
||||
$location.search("searchJob", $scope.groupedRuns.searchJob);
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ var controllersModule = require('./_index');
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
function HomeController($scope, healthService, startDate, projectService, viewService, $location) {
|
||||
function HomeController($scope, healthService, projectService, viewService, $location) {
|
||||
|
||||
var byFailRateDesc = function(project1, project2) {
|
||||
// To get descending order, project2 should come first
|
||||
|
@ -16,6 +16,35 @@ function HomeController($scope, healthService, startDate, projectService, viewSe
|
|||
return entryA.x - entryB.x;
|
||||
};
|
||||
|
||||
var configurePeriods = function() {
|
||||
vm.hold += 1;
|
||||
|
||||
var res = viewService.resolution();
|
||||
var min = null;
|
||||
var max = null;
|
||||
var preference = null;
|
||||
|
||||
if (res.key === 'sec') {
|
||||
max = { hours: 6 };
|
||||
preference = { hours: 1 };
|
||||
} else if (res.key === 'min') {
|
||||
max = { days: 1 };
|
||||
preference = { hours: 12 };
|
||||
} else if (res.key === 'hour') {
|
||||
min = { hours: 12 };
|
||||
max = { months: 3 };
|
||||
preference = { months: 1 };
|
||||
} else if (res.key === 'day') {
|
||||
min = { hours: 48 };
|
||||
preference = { months: 3 };
|
||||
}
|
||||
|
||||
viewService.periods(min, max, true);
|
||||
viewService.preferredDuration(preference);
|
||||
|
||||
vm.hold -= 1;
|
||||
};
|
||||
|
||||
var processData = function(data) {
|
||||
var projects = projectService.createProjects(data.runs);
|
||||
var blanks = projectService.findBlanks(data.runs);
|
||||
|
@ -65,12 +94,20 @@ function HomeController($scope, healthService, startDate, projectService, viewSe
|
|||
};
|
||||
};
|
||||
|
||||
var loadData = function(runMetadataKey) {
|
||||
var loadData = function() {
|
||||
// don't update if configurePeriods() is in progress - it may throw an extra
|
||||
// period update
|
||||
if (vm.hold > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
healthService.getRunsGroupedByMetadataPerDatetime(vm.groupKey, {
|
||||
start_date: viewService.windowStart(startDate, 20),
|
||||
start_date: viewService.periodStart(),
|
||||
stop_date: viewService.periodEnd(),
|
||||
datetime_resolution: viewService.resolution().key
|
||||
}).then(function(response) {
|
||||
processData(response.data);
|
||||
vm.loaded = true;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -80,18 +117,29 @@ function HomeController($scope, healthService, startDate, projectService, viewSe
|
|||
vm.groupKey = viewService.groupKey();
|
||||
|
||||
vm.searchProject = $location.search().searchProject || '';
|
||||
vm.loaded = false;
|
||||
vm.hold = 0;
|
||||
|
||||
configurePeriods();
|
||||
loadData();
|
||||
|
||||
$scope.$on('view:groupKey', function(event, groupKey) {
|
||||
vm.groupKey = groupKey;
|
||||
loadData(groupKey);
|
||||
configurePeriods();
|
||||
loadData();
|
||||
});
|
||||
|
||||
$scope.$on('view:resolution', function(event, resolution) {
|
||||
configurePeriods();
|
||||
loadData();
|
||||
});
|
||||
|
||||
$scope.$on('view:period', function(event, corrected) {
|
||||
if (vm.loaded && !corrected) {
|
||||
loadData();
|
||||
}
|
||||
});
|
||||
|
||||
vm.onSearchChange = function() {
|
||||
$location.search("searchProject", $scope.home.searchProject);
|
||||
};
|
||||
|
|
|
@ -5,13 +5,44 @@ var controllersModule = require('./_index');
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
function JobController($scope, healthService, viewService, jobName, startDate, $location) {
|
||||
function JobController($scope, healthService, viewService, jobName, $location) {
|
||||
// ViewModel
|
||||
var vm = this;
|
||||
|
||||
vm.searchTest = '';
|
||||
vm.name = decodeURIComponent(jobName);
|
||||
vm.recentRuns = [];
|
||||
vm.loaded = false;
|
||||
vm.hold = 0;
|
||||
|
||||
var configurePeriods = function() {
|
||||
vm.hold += 1;
|
||||
|
||||
var res = viewService.resolution();
|
||||
var min = null;
|
||||
var max = null;
|
||||
var preference = null;
|
||||
|
||||
if (res.key === 'sec') {
|
||||
max = { hours: 6 };
|
||||
preference = { hours: 1 };
|
||||
} else if (res.key === 'min') {
|
||||
max = { days: 1 };
|
||||
preference = { hours: 12 };
|
||||
} else if (res.key === 'hour') {
|
||||
min = { hours: 12 };
|
||||
max = { months: 1 };
|
||||
preference = { weeks: 1 };
|
||||
} else if (res.key === 'day') {
|
||||
min = { hours: 48 };
|
||||
preference = { months: 3 };
|
||||
}
|
||||
|
||||
viewService.periods(min, max, true);
|
||||
viewService.preferredDuration(preference);
|
||||
|
||||
vm.hold -= 1;
|
||||
};
|
||||
|
||||
vm.processData = function(data) {
|
||||
vm.chartData = [];
|
||||
|
@ -131,18 +162,17 @@ function JobController($scope, healthService, viewService, jobName, startDate, $
|
|||
};
|
||||
|
||||
vm.loadData = function() {
|
||||
// Note(mtreinish): this is a hack to make periodic job graphs useful
|
||||
// until we have a user selectable date window available
|
||||
var dateWindow = 2;
|
||||
if (vm.name.indexOf('periodic') > -1) {
|
||||
dateWindow = 15;
|
||||
if (vm.hold > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
healthService.getTestsFromBuildName(vm.name, {
|
||||
start_date: viewService.windowStart(startDate, dateWindow),
|
||||
start_date: viewService.periodStart(),
|
||||
stop_date: viewService.periodEnd(),
|
||||
datetime_resolution: viewService.resolution().key
|
||||
}).then(function(response) {
|
||||
vm.processData(response.data);
|
||||
vm.loaded = true;
|
||||
});
|
||||
healthService.getRecentGroupedRuns('build_name', vm.name).then(function(response) {
|
||||
vm.recentRuns = response.data;
|
||||
|
@ -151,12 +181,20 @@ function JobController($scope, healthService, viewService, jobName, startDate, $
|
|||
|
||||
vm.searchTest = $location.search().searchTest || '';
|
||||
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
|
||||
$scope.$on('view:resolution', function(event, resolution) {
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
});
|
||||
|
||||
$scope.$on('view:period', function(event, corrected) {
|
||||
if (vm.loaded && !corrected) {
|
||||
vm.loadData();
|
||||
}
|
||||
});
|
||||
|
||||
vm.onSearchChange = function() {
|
||||
$location.search("searchTest", $scope.job.searchTest);
|
||||
};
|
||||
|
|
|
@ -5,12 +5,43 @@ var controllersModule = require('./_index');
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
function TestController($scope, healthService, testService, viewService, startDate, testId) {
|
||||
function TestController($scope, healthService, testService, viewService, testId) {
|
||||
|
||||
// ViewModel
|
||||
var vm = this;
|
||||
vm.testName = testId;
|
||||
vm.testShortName = testService.getShortName(testId);
|
||||
vm.loaded = false;
|
||||
vm.hold = 0;
|
||||
|
||||
var configurePeriods = function() {
|
||||
vm.hold += 1;
|
||||
|
||||
var res = viewService.resolution();
|
||||
var min = null;
|
||||
var max = null;
|
||||
var preference = null;
|
||||
|
||||
if (res.key === 'sec') {
|
||||
max = { hours: 6 };
|
||||
preference = { hours: 1 };
|
||||
} else if (res.key === 'min') {
|
||||
max = { days: 1 };
|
||||
preference = { hours: 12 };
|
||||
} else if (res.key === 'hour') {
|
||||
min = { hours: 12 };
|
||||
max = { months: 3 };
|
||||
preference = { months: 1 };
|
||||
} else if (res.key === 'day') {
|
||||
min = { hours: 48 };
|
||||
preference = { months: 3 };
|
||||
}
|
||||
|
||||
viewService.periods(min, max, true);
|
||||
viewService.preferredDuration(preference);
|
||||
|
||||
vm.hold -= 1;
|
||||
};
|
||||
|
||||
vm.processData = function(data) {
|
||||
var dates = {};
|
||||
|
@ -120,21 +151,32 @@ function TestController($scope, healthService, testService, viewService, startDa
|
|||
};
|
||||
|
||||
vm.loadData = function() {
|
||||
var stopDate = new Date(startDate);
|
||||
if (vm.hold > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
healthService.getTestRunList(vm.testName, {
|
||||
start_date: viewService.windowStart(stopDate, 30),
|
||||
stop_date: stopDate,
|
||||
start_date: viewService.periodStart(),
|
||||
stop_date: viewService.periodEnd(),
|
||||
datetime_resolution: viewService.resolution().key
|
||||
}).then(function(response) {
|
||||
vm.processData(response.data);
|
||||
vm.loaded = true;
|
||||
});
|
||||
};
|
||||
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
|
||||
$scope.$on('view:resolution', function(event, resolution) {
|
||||
configurePeriods();
|
||||
vm.loadData();
|
||||
});
|
||||
|
||||
$scope.$on('view:period', function(event, corrected) {
|
||||
if (vm.loaded && !corrected) {
|
||||
vm.loadData();
|
||||
}
|
||||
});
|
||||
}
|
||||
controllersModule.controller('TestController', TestController);
|
||||
|
|
|
@ -20,6 +20,24 @@ function crumbMenu() {
|
|||
$scope.selectedResolution = viewService.resolution();
|
||||
$scope.selectedGroupKey = viewService.groupKey();
|
||||
$scope.groupKeys = [];
|
||||
$scope.periodEnd = viewService.periodEnd();
|
||||
$scope.periodOptions = viewService.periodOptions();
|
||||
$scope.periods = viewService.periods();
|
||||
$scope.duration = viewService.duration();
|
||||
|
||||
var vm = this;
|
||||
this.selectedPeriodIndex = '0';
|
||||
this.selectedPeriodEnd = viewService.periodEnd();
|
||||
|
||||
var updatePeriodIndex = function() {
|
||||
$scope.periodOptions.forEach(function(period, i) {
|
||||
if (period + 0 == $scope.duration + 0) {
|
||||
vm.selectedPeriodIndex = i.toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updatePeriodIndex();
|
||||
|
||||
$scope.setResolution = function(resolution) {
|
||||
viewService.resolution(resolution);
|
||||
|
@ -39,9 +57,35 @@ function crumbMenu() {
|
|||
$scope.selectedGroupKey = groupKey;
|
||||
});
|
||||
|
||||
$scope.$on('view:periodEnd', function(event, periodEnd) {
|
||||
$scope.periodEnd = periodEnd;
|
||||
});
|
||||
|
||||
$scope.$on('view:periods', function(event, periods) {
|
||||
$scope.periods = periods;
|
||||
});
|
||||
|
||||
$scope.$on('view:duration', function(event, duration, corrected) {
|
||||
$scope.duration = duration;
|
||||
updatePeriodIndex();
|
||||
});
|
||||
|
||||
healthService.getRunMetadataKeys().then(function(response) {
|
||||
$scope.groupKeys = response.data;
|
||||
});
|
||||
|
||||
$scope.$watch('menu.selectedPeriodEnd', function(val, old) {
|
||||
if (val === old) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewService.periodEnd(val);
|
||||
});
|
||||
|
||||
vm.updateIndex = function() {
|
||||
var period = $scope.periodOptions[parseInt(vm.selectedPeriodIndex)];
|
||||
viewService.userDuration(period);
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -50,9 +94,11 @@ function crumbMenu() {
|
|||
templateUrl: 'crumb-menu.html',
|
||||
link: link,
|
||||
controller: controller,
|
||||
controllerAs: 'menu',
|
||||
scope: {
|
||||
'showGroupKey': '@',
|
||||
'showResolution': '@'
|
||||
'showResolution': '@',
|
||||
'showPeriod': '@'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ function OnConfig($stateProvider, $locationProvider, $urlRouterProvider) {
|
|||
url: '/',
|
||||
controller: 'HomeController as home',
|
||||
templateUrl: 'home.html',
|
||||
title: 'Home',
|
||||
resolve: /*@ngInject*/ {
|
||||
'startDate': function() {
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
title: 'Home'
|
||||
})
|
||||
.state('groupedRuns', {
|
||||
url: '/g/*runMetadataKey/*name',
|
||||
|
@ -27,9 +22,6 @@ function OnConfig($stateProvider, $locationProvider, $urlRouterProvider) {
|
|||
},
|
||||
'name': function($stateParams) {
|
||||
return $stateParams.name;
|
||||
},
|
||||
'currentDate': function() {
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -47,9 +39,6 @@ function OnConfig($stateProvider, $locationProvider, $urlRouterProvider) {
|
|||
resolve: /*@ngInject*/ {
|
||||
'jobName': function($stateParams) {
|
||||
return $stateParams.jobName;
|
||||
},
|
||||
'startDate': function() {
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -61,9 +50,6 @@ function OnConfig($stateProvider, $locationProvider, $urlRouterProvider) {
|
|||
resolve: /*@ngInject*/ {
|
||||
'testId': function($stateParams) {
|
||||
return $stateParams.testId;
|
||||
},
|
||||
'startDate': function() {
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
|
||||
var servicesModule = require('./_index.js');
|
||||
|
||||
var moment = require('moment');
|
||||
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
var viewService = function($rootScope, $location) {
|
||||
var resolutionOptions = [
|
||||
{ name: 'Second', key: 'sec', window: 0.01 },
|
||||
{ name: 'Minute', key: 'min', window: 0.5 },
|
||||
{ name: 'Hour', key: 'hour', window: 1 },
|
||||
{ name: 'Day', key: 'day', window: 10 }
|
||||
{ name: 'Second', key: 'sec' },
|
||||
{ name: 'Minute', key: 'min' },
|
||||
{ name: 'Hour', key: 'hour' },
|
||||
{ name: 'Day', key: 'day' }
|
||||
];
|
||||
var resolution = resolutionOptions[2];
|
||||
var groupKey = $location.search().groupKey || 'project';
|
||||
|
@ -27,6 +29,41 @@ var viewService = function($rootScope, $location) {
|
|||
$location.search('resolutionKey', resolution.key);
|
||||
});
|
||||
|
||||
var periodEnd = new Date();
|
||||
var periodOptions = [
|
||||
moment.duration({ hours: 1 }),
|
||||
moment.duration({ hours: 12 }),
|
||||
moment.duration({ days: 1 }),
|
||||
moment.duration({ weeks: 1 }),
|
||||
moment.duration({ weeks: 2 }),
|
||||
moment.duration({ months: 1 }),
|
||||
moment.duration({ months: 3 }),
|
||||
moment.duration({ months: 6 })
|
||||
];
|
||||
var preferredDuration = null;
|
||||
var userDuration = null;
|
||||
var periods = periodOptions;
|
||||
|
||||
var searchDuration = $location.search().duration;
|
||||
if (searchDuration) {
|
||||
userDuration = moment.duration(searchDuration);
|
||||
}
|
||||
|
||||
var searchEnd = $location.search().end;
|
||||
if (searchEnd) {
|
||||
periodEnd = new Date(searchEnd);
|
||||
}
|
||||
|
||||
var selectDuration = function() {
|
||||
if (userDuration) {
|
||||
return userDuration;
|
||||
} else if (preferredDuration) {
|
||||
return preferredDuration;
|
||||
} else {
|
||||
return periodOptions[0];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
resolution: function(res) {
|
||||
if (arguments.length === 1) {
|
||||
|
@ -37,14 +74,6 @@ var viewService = function($rootScope, $location) {
|
|||
return resolution;
|
||||
},
|
||||
|
||||
windowStart: function(endDate, days) {
|
||||
var ret = new Date(endDate);
|
||||
var diff = Math.ceil(resolution.window * days);
|
||||
ret.setDate(ret.getDate() - diff);
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
resolutionOptions: function() {
|
||||
return resolutionOptions;
|
||||
},
|
||||
|
@ -56,6 +85,108 @@ var viewService = function($rootScope, $location) {
|
|||
}
|
||||
|
||||
return groupKey;
|
||||
},
|
||||
|
||||
duration: function() {
|
||||
return selectDuration();
|
||||
},
|
||||
|
||||
periodEnd: function(end) {
|
||||
if (arguments.length === 0) {
|
||||
return periodEnd;
|
||||
}
|
||||
|
||||
$location.search('end', end.toISOString());
|
||||
|
||||
periodEnd = end;
|
||||
$rootScope.$broadcast('view:periodEnd', end);
|
||||
$rootScope.$broadcast('view:period', false);
|
||||
|
||||
return end;
|
||||
},
|
||||
|
||||
periodStart: function() {
|
||||
return moment(periodEnd)
|
||||
.subtract(selectDuration())
|
||||
.toDate();
|
||||
},
|
||||
|
||||
periodOptions: function() {
|
||||
return periodOptions;
|
||||
},
|
||||
|
||||
periods: function(min, max, correct) {
|
||||
if (arguments.length === 0) {
|
||||
return periods;
|
||||
}
|
||||
|
||||
correct = (typeof correct === 'undefined' ? false : correct);
|
||||
|
||||
var filtered = periodOptions.slice();
|
||||
if (min) {
|
||||
var d = moment.duration(min);
|
||||
filtered = filtered.filter(function(period) {
|
||||
return period >= d;
|
||||
});
|
||||
}
|
||||
|
||||
if (max) {
|
||||
var d = moment.duration(max);
|
||||
filtered = filtered.filter(function(period) {
|
||||
return period <= d;
|
||||
});
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
throw new Error('Invalid period requirements');
|
||||
}
|
||||
|
||||
periods = filtered;
|
||||
$rootScope.$broadcast('view:periods', periods);
|
||||
|
||||
if (correct && userDuration) {
|
||||
if (min && userDuration < moment.duration(min)) {
|
||||
userDuration = filtered[0];
|
||||
$rootScope.$broadcast('view:duration', userDuration, true);
|
||||
$rootScope.$broadcast('view:period', true);
|
||||
} else if (max && userDuration > moment.duration(max)) {
|
||||
userDuration = filtered[filtered.length - 1];
|
||||
$rootScope.$broadcast('view:duration', userDuration, true);
|
||||
$rootScope.$broadcast('view:period', true);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
|
||||
userDuration: function(duration) {
|
||||
if (arguments.length === 0) {
|
||||
return userDuration;
|
||||
}
|
||||
|
||||
userDuration = moment.duration(duration);
|
||||
$rootScope.$broadcast('view:duration', duration, false);
|
||||
$rootScope.$broadcast('view:period', false);
|
||||
|
||||
$location.search('duration', userDuration.toISOString());
|
||||
|
||||
return duration;
|
||||
},
|
||||
|
||||
preferredDuration: function(duration) {
|
||||
if (arguments.length === 0) {
|
||||
return preferredDuration;
|
||||
}
|
||||
|
||||
preferredDuration = moment.duration(duration);
|
||||
|
||||
// if no user override is active, send a notification
|
||||
if (!userDuration) {
|
||||
$rootScope.$broadcast('view:duration', preferredDuration, false);
|
||||
$rootScope.$broadcast('view:period', false);
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
</ul>
|
||||
<div class="container-fluid">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li dropdown ng-if="showPeriod == 'true'">
|
||||
<a popover-template="'crumb-period-popover.html'"
|
||||
popover-placement="bottom"
|
||||
popover-animation="false"
|
||||
href>
|
||||
<fa name="calendar"></fa>
|
||||
Period: {{duration.humanize()}}
|
||||
<fa name="caret-down"></fa>
|
||||
</a>
|
||||
</li>
|
||||
<li dropdown ng-if="showGroupKey == 'true'">
|
||||
<a dropdown-toggle href title="Group By Key">
|
||||
<fa name="key"></fa>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<div>
|
||||
<label>End Date:</label>
|
||||
<p class="input-group">
|
||||
<span class="input-group-addon"><fa name="calendar"></fa></span>
|
||||
<input ng-model="menu.selectedPeriodEnd" class="form-control" type="date">
|
||||
</p>
|
||||
|
||||
<label>Duration:</label>
|
||||
<p class="input-group">
|
||||
<span class="input-group-addon"><fa name="clock-o"></fa></span>
|
||||
<select class="form-control" ng-model="menu.selectedPeriodIndex" ng-change="menu.updateIndex()">
|
||||
<option value="{{$index}}"
|
||||
ng-repeat="period in periodOptions"
|
||||
ng-disabled="periods.indexOf(period) == -1">
|
||||
{{period.humanize()}}
|
||||
</option>
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
|
@ -1,7 +1,7 @@
|
|||
<header class="bs-header">
|
||||
<div class="container">
|
||||
<h1 class="page-header">{{ groupedRuns.name }} jobs</h1>
|
||||
<crumb-menu show-resolution="true">
|
||||
<crumb-menu show-resolution="true" show-period="true">
|
||||
<li>{{ groupedRuns.runMetadataKey }}: {{ groupedRuns.name }}</li>
|
||||
</crumb-menu>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">OpenStack Health</h1>
|
||||
<crumb-menu show-group-key="true" show-resolution="true"></crumb-menu>
|
||||
<crumb-menu show-group-key="true"
|
||||
show-resolution="true"
|
||||
show-period="true"></crumb-menu>
|
||||
<loading-indicator></loading-indicator>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<header class="bs-header">
|
||||
<div class="container">
|
||||
<h1 class="page-header">{{ job.name }} tests</h1>
|
||||
<crumb-menu show-resolution="true">
|
||||
<crumb-menu show-resolution="true" show-period="true">
|
||||
<li>Job: {{ job.name }}</li>
|
||||
</crumb-menu>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<header class="bs-header">
|
||||
<div class="container">
|
||||
<h1 class="page-header">{{ testCtrl.testShortName }}</h1>
|
||||
<crumb-menu show-resolution="true">
|
||||
<crumb-menu show-resolution="true" show-period="true">
|
||||
<li>Test: {{testCtrl.testShortName }}</li>
|
||||
</crumb-menu>
|
||||
</div>
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"karma-jasmine": "^0.3.6",
|
||||
"karma-phantomjs-launcher": "0.2.0",
|
||||
"karma-spec-reporter": "0.0.20",
|
||||
"moment": "^2.11.1",
|
||||
"morgan": "^1.6.1",
|
||||
"phantomjs": "1.9.17",
|
||||
"pretty-hrtime": "^1.0.0",
|
||||
|
|
|
@ -4,9 +4,12 @@ describe('GroupedRunsController', function() {
|
|||
module('app.controllers');
|
||||
});
|
||||
|
||||
var $scope, $httpBackend, $controller, healthService;
|
||||
var $scope, $httpBackend, $controller, healthService, viewService;
|
||||
var API_ROOT = 'http://8.8.4.4:8080';
|
||||
var DEFAULT_CURRENT_DATE = new Date();
|
||||
var DEFAULT_END_DATE = new Date();
|
||||
var DEFAULT_START_DATE = new Date(
|
||||
(+DEFAULT_END_DATE) -
|
||||
(1000 * 60 * 60 * 24 * 7));
|
||||
|
||||
beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) {
|
||||
$scope = $rootScope.$new();
|
||||
|
@ -15,13 +18,18 @@ describe('GroupedRunsController', function() {
|
|||
mockHealthService();
|
||||
$controller = _$controller_;
|
||||
healthService = _healthService_;
|
||||
|
||||
viewService = {
|
||||
resolution: function() { return { name: 'Hour', key: 'hour' }; },
|
||||
groupKey: function() { return 'project'; },
|
||||
periods: function() {},
|
||||
preferredDuration: function() {},
|
||||
periodStart: function() { return DEFAULT_START_DATE; },
|
||||
periodEnd: function() { return DEFAULT_END_DATE; }
|
||||
};
|
||||
}));
|
||||
|
||||
function mockHealthService() {
|
||||
var startDate = new Date(DEFAULT_CURRENT_DATE);
|
||||
startDate.setDate(startDate.getDate() - 20);
|
||||
var stopDate = new Date(DEFAULT_CURRENT_DATE);
|
||||
|
||||
var expectedResponse = {
|
||||
timedelta: [
|
||||
{
|
||||
|
@ -63,12 +71,12 @@ describe('GroupedRunsController', function() {
|
|||
};
|
||||
|
||||
var endpoint = API_ROOT +
|
||||
'/runs/key/project/openstack/cinder?callback=JSON_CALLBACK&' +
|
||||
'datetime_resolution=hour&' +
|
||||
'start_date=' + startDate.toISOString() + '&' +
|
||||
'stop_date=' + stopDate.toISOString();
|
||||
$httpBackend.expectJSONP(endpoint)
|
||||
.respond(200, expectedResponse);
|
||||
'/runs/key/project/openstack/cinder?callback=JSON_CALLBACK&' +
|
||||
'datetime_resolution=hour&' +
|
||||
'start_date=' + DEFAULT_START_DATE.toISOString() + '&' +
|
||||
'stop_date=' + DEFAULT_END_DATE.toISOString();
|
||||
|
||||
$httpBackend.expectJSONP(endpoint).respond(200, expectedResponse);
|
||||
|
||||
var recentResponse = [
|
||||
{
|
||||
|
@ -119,7 +127,7 @@ describe('GroupedRunsController', function() {
|
|||
healthService: healthService,
|
||||
runMetadataKey: 'project',
|
||||
name: 'openstack/cinder',
|
||||
currentDate: DEFAULT_CURRENT_DATE
|
||||
viewService: viewService
|
||||
});
|
||||
$httpBackend.flush();
|
||||
|
||||
|
@ -146,7 +154,7 @@ describe('GroupedRunsController', function() {
|
|||
healthService: healthService,
|
||||
runMetadataKey: 'project',
|
||||
name: 'openstack/cinder',
|
||||
currentDate: DEFAULT_CURRENT_DATE
|
||||
viewService: viewService
|
||||
});
|
||||
$httpBackend.flush();
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ describe('HomeController', function() {
|
|||
$scope = $rootScope.$new();
|
||||
$controller = _$controller_;
|
||||
|
||||
var defaultStartDate = new Date();
|
||||
var healthService = {
|
||||
getRunsGroupedByMetadataPerDatetime: function(key, options) {
|
||||
return { then: function(callback) { callback(mockResponse); } };
|
||||
|
@ -39,7 +38,6 @@ describe('HomeController', function() {
|
|||
homeController = $controller('HomeController', {
|
||||
$scope: $scope,
|
||||
healthService: healthService,
|
||||
startDate: defaultStartDate,
|
||||
projectService: projectService
|
||||
});
|
||||
}));
|
||||
|
|
|
@ -4,9 +4,12 @@ describe('JobController', function() {
|
|||
module('app.controllers');
|
||||
});
|
||||
|
||||
var $scope, $httpBackend, $controller, healthService;
|
||||
var $scope, $httpBackend, $controller, healthService, viewService;
|
||||
var API_ROOT = 'http://8.8.4.4:8080';
|
||||
var DEFAULT_START_DATE = new Date();
|
||||
var DEFAULT_END_DATE = new Date();
|
||||
var DEFAULT_START_DATE = new Date(
|
||||
(+DEFAULT_END_DATE) -
|
||||
(1000 * 60 * 60 * 24 * 7));
|
||||
|
||||
beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
|
@ -16,12 +19,17 @@ describe('JobController', function() {
|
|||
$scope = $rootScope.$new();
|
||||
$controller = _$controller_;
|
||||
healthService = _healthService_;
|
||||
|
||||
viewService = {
|
||||
resolution: function() { return { name: 'Hour', key: 'hour' }; },
|
||||
periods: function() {},
|
||||
preferredDuration: function() {},
|
||||
periodStart: function() { return DEFAULT_START_DATE; },
|
||||
periodEnd: function() { return DEFAULT_END_DATE; }
|
||||
};
|
||||
}));
|
||||
|
||||
function mockHealthService() {
|
||||
var startDate = new Date(DEFAULT_START_DATE);
|
||||
startDate.setDate(startDate.getDate() - 2);
|
||||
|
||||
var expectedResponse = {
|
||||
tests: {
|
||||
"2014-11-19T01:00:00": {
|
||||
|
@ -47,13 +55,13 @@ describe('JobController', function() {
|
|||
}
|
||||
};
|
||||
var endpoint = API_ROOT +
|
||||
'/build_name/gate-tempest-dsvm-neutron-full/test_runs?' +
|
||||
'callback=JSON_CALLBACK&' +
|
||||
'datetime_resolution=hour&' +
|
||||
'start_date=' +
|
||||
startDate.toISOString();
|
||||
$httpBackend.expectJSONP(endpoint)
|
||||
.respond(200, expectedResponse);
|
||||
'/build_name/gate-tempest-dsvm-neutron-full/test_runs?' +
|
||||
'callback=JSON_CALLBACK&' +
|
||||
'datetime_resolution=hour&' +
|
||||
'start_date=' + DEFAULT_START_DATE.toISOString() + '&' +
|
||||
'stop_date=' + DEFAULT_END_DATE.toISOString();
|
||||
|
||||
$httpBackend.expectJSONP(endpoint).respond(200, expectedResponse);
|
||||
|
||||
var recentResponse = [
|
||||
{
|
||||
|
@ -103,7 +111,8 @@ describe('JobController', function() {
|
|||
$scope: $scope,
|
||||
healthService: healthService,
|
||||
jobName: 'gate-tempest-dsvm-neutron-full',
|
||||
startDate: DEFAULT_START_DATE
|
||||
startDate: DEFAULT_START_DATE,
|
||||
viewService: viewService
|
||||
});
|
||||
$httpBackend.flush();
|
||||
|
||||
|
@ -137,7 +146,7 @@ describe('JobController', function() {
|
|||
$scope: $scope,
|
||||
healthService: healthService,
|
||||
jobName: 'gate-tempest-dsvm-neutron-full',
|
||||
startDate: DEFAULT_START_DATE
|
||||
viewService: viewService
|
||||
});
|
||||
$httpBackend.flush();
|
||||
|
||||
|
@ -156,7 +165,7 @@ describe('JobController', function() {
|
|||
$scope: $scope,
|
||||
healthService: healthService,
|
||||
jobName: 'gate-tempest-dsvm-neutron-full',
|
||||
startDate: DEFAULT_START_DATE
|
||||
viewService: viewService
|
||||
});
|
||||
$httpBackend.flush();
|
||||
|
||||
|
|
Loading…
Reference in New Issue