diff --git a/.gitattributes b/.gitattributes index c845f0cb81ca841f1312f102fe4986c4f91ab559..786f4056a41556e943031714646fb2b5704cc998 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5322,6 +5322,7 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/ui-bootstrap SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/ui-grid-edit-datepicker.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/storage.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/utils.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/test/CMakeLists.txt -text diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt index a2cf8287a7fb0b8a4001fcbc708a7c64efeae66d..2d2a6b7986cb9c3166e34f4b550ef33a9be5daeb 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt @@ -47,7 +47,8 @@ set(app_files static/icons/ingest_in_progress.png static/icons/ingest_failed.png static/icons/ingest_successful.png - templates/index.html) + templates/index.html + templates/projects.html) set(web_files ${jquery_files} diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js index 01c765237718913461df29c709b00635cb00f793..735be7ea7fcce46720c5bc3808dce37273eed113 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js @@ -2,6 +2,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, $q){ var self = this; + self.projectMode = false; self.tasks = []; self.resources = []; self.resourceGroups = []; @@ -37,6 +38,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.selected_project_id; self.selected_resourceClaim_id; + self.selected_project = { name: 'Please select project', value: undefined }; + self.initialLoadComplete = false; self.taskChangeCntr = 0; self.filteredTaskChangeCntr = 0; @@ -212,7 +215,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, timestamp += 3600000; } else { - var chuckUpperLimit = Math.min(upperTS, timestamp + 24*3600000); + var chunkFactor = self.projectMode ? 7 : 1; + var chuckUpperLimit = Math.min(upperTS, timestamp + chunkFactor*24*3600000); for (var chunkTimestamp = timestamp; chunkTimestamp < chuckUpperLimit; chunkTimestamp += 3600000) { if(self.loadedHours.hasOwnProperty(chunkTimestamp)) break; @@ -281,6 +285,15 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getTasks = function(from, until) { var defer = $q.defer(); var url = '/rest/tasks'; + if(self.projectMode) { + if(self.selected_project_id === undefined){ + defer.resolve([]); + return defer; + } + + url = '/rest/projects/' + self.selected_project_id + '/tasks'; + } + if(from) { url += '/' + self.convertLocalUTCDateToISOString(from); @@ -307,10 +320,13 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, for(var i = newTaskIds.length-1; i >= 0; i--) { var task_id = newTaskIds[i]; + if(!self.taskDict.hasOwnProperty(task_id)) { var task = newTaskDict[task_id]; - self.tasks.push(task); - self.taskDict[task_id] = task; + if(!self.projectMode || self.selected_project_id == task.project_mom_id) { + self.tasks.push(task); + self.taskDict[task_id] = task; + } } } @@ -562,6 +578,10 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getResourceUsages = function() { var defer = $q.defer(); + if(self.projectMode) { + defer.resolve([]); + return defer; + } $http.get('/rest/resourceusages').success(function(result) { //convert datetime strings to Date objects for(var i in result.resourceusages) { @@ -586,6 +606,10 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getResourceClaims = function(from, until) { var defer = $q.defer(); + if(self.projectMode) { + defer.resolve([]); + return defer; + } var url = '/rest/resourceclaims'; if(from) { url += '/' + self.convertLocalUTCDateToISOString(from); @@ -724,6 +748,17 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, }); }; + self.getProjectTasksTimeWindow = function(project_mom_id) { + var defer = $q.defer(); + $http.get('/rest/projects/' + project_mom_id + '/taskstimewindow').success(function(result) { + defer.resolve(result); + }).error(function(result) { + defer.resolve(undefined); + }); + + return defer.promise; + }; + self.getProjectDiskUsage = function(project_name) { var defer = $q.defer(); $http.get('/rest/projects/' + project_name + '/diskusage').success(function(result) { @@ -783,13 +818,18 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.lastUpdateChangeNumber = result.mostRecentChangeNumber; } - $q.all([self.getConfig(), - self.getMoMProjects(), - self.getTaskTypes(), - self.getTaskStatusTypes(), - self.getResourceGroups(), - self.getResources(), - self.getResourceGroupMemberships()]).then(function() { + var load_promisses = [self.getConfig(), + self.getMoMProjects(), + self.getTaskTypes(), + self.getTaskStatusTypes()]; + + if(!self.projectMode) { + load_promisses = load_promisses.concat([self.getResourceGroups(), + self.getResources(), + self.getResourceGroupMemberships()]); + } + + $q.all(load_promisses).then(function() { self.initialLoadComplete = true; }); @@ -826,7 +866,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } } else if(change.changeType == 'insert') { var task = self.taskDict[changedTask.id]; - if(!task) { + if(!task && (!self.projectMode || self.selected_project_id == task.project_mom_id)) { changedTask.starttime = self.convertDatestringToLocalUTCDate(changedTask.starttime); changedTask.endtime = self.convertDatestringToLocalUTCDate(changedTask.endtime); changedTask.ingest_status = self.convertNullToUndefined(changedTask.ingest_status); @@ -849,6 +889,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.computeMinMaxTaskTimes(); } else if(change.objectType == 'resourceClaim') { + if(self.projectMode) + continue; //skip claims in projectMode + anyResourceClaims = true; var changedClaim = change.value; if(change.changeType == 'update') { @@ -877,6 +920,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.claimChangeCntr++; self.computeMinMaxResourceClaimTimes(); } else if(change.objectType == 'resourceCapacity') { + if(self.projectMode) + continue; //skip capacities in projectMode + if(change.changeType == 'update') { var changedCapacity = change.value; var resource = self.resourceDict[changedCapacity.resource_id]; @@ -886,6 +932,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } } } else if(change.objectType == 'resourceAvailability') { + if(self.projectMode) + continue; //skip resourceAvailability in projectMode + if(change.changeType == 'update') { var changedAvailability = change.value; var resource = self.resourceDict[changedAvailability.resource_id]; @@ -1159,6 +1208,32 @@ dataControllerMod.controller('DataController', } }); + $scope.$watch('dataService.selected_project_id', function() { + if(dataService.projectMode) { + $scope.$evalAsync(function() { + dataService.autoFollowNow = false; + dataService.viewTimeSpan.from = dataService.lofarTime; + dataService.viewTimeSpan.to = dataService.lofarTime; + dataService.tasks.splice(0, dataService.tasks.length); + dataService.tasksDict = dataService.toIdBasedDict(dataService.tasks); + dataService.taskChangeCntr++; + dataService.getProjectTasksTimeWindow(dataService.selected_project_id).then(function(window) { + if(window.min_starttime && window.max_endtime) { + dataService.viewTimeSpan.from = dataService.convertDatestringToLocalUTCDate(window.min_starttime); + dataService.viewTimeSpan.to = dataService.convertDatestringToLocalUTCDate(window.max_endtime); + dataService.getTasksAndClaimsForViewSpan(); + } + }); + }); + } + }); + + $scope.$watch('dataService.selected_project', function() { + $scope.$evalAsync(function() { + dataService.selected_project_id = dataService.selected_project.value; + }); + }); + dataService.initialLoad(); //clock ticking every second diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js index 4198858bba2d2f9f88880b711f08ebcb1ac9ba8c..6c1dfb2b04e42a88f06335b23f93b53c149ce3d6 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js @@ -237,6 +237,10 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic sort: { direction: uiGridConstants.ASC, priority: 1 } }]; + if($scope.dataService.projectMode) { + $scope.columns.splice(1, 1); + } + $scope.gridOptions = { enableGridMenu: false, enableSorting: true, @@ -338,6 +342,9 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic }; function fillColumFilterSelectOptions(options, columnDef) { + if (columnDef == undefined) + return; + var columnSelectOptions = []; if(options) { for(var i = 0; i < options.length; i++) { @@ -434,7 +441,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic var groupSelectOptions = [ { value: mom_object_group_id, label: label} ]; - fillColumFilterSelectOptions(groupSelectOptions, $scope.columns[9]); + fillColumFilterSelectOptions(groupSelectOptions, $scope.columns.find(function(c) {return c.field == 'mom_object_group_id'; })); group_col.filters[0].term = mom_object_group_id; } } @@ -459,7 +466,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic fillTypeColumFilterSelectOptions(); fillProjectsColumFilterSelectOptions(); fillGroupsColumFilterSelectOptions(); - fillColumFilterSelectOptions(['CEP2', 'CEP4'], $scope.columns[13]); + fillColumFilterSelectOptions(['CEP2', 'CEP4'], $scope.columns.find(function(c) {return c.field == 'cluster'; })); } }; @@ -491,7 +498,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic } } projectNames.sort(); - fillColumFilterSelectOptions(projectNames, $scope.columns[1]); + fillColumFilterSelectOptions(projectNames, $scope.columns.find(function(c) {return c.field == 'project_name'; })); }; function fillStatusColumFilterSelectOptions() { @@ -507,7 +514,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic task_statuses = task_statuses.unique(); task_statuses.sort(); - fillColumFilterSelectOptions(task_statuses, $scope.columns[5]); + fillColumFilterSelectOptions(task_statuses, $scope.columns.find(function(c) {return c.field == 'status'; })); }; function fillTypeColumFilterSelectOptions() { @@ -523,11 +530,12 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic task_types = task_types.unique(); task_types.sort(); - fillColumFilterSelectOptions(task_types, $scope.columns[7]); + fillColumFilterSelectOptions(task_types, $scope.columns.find(function(c) {return c.field == 'type'; })); }; function fillGroupsColumFilterSelectOptions() { - if($scope.columns[9].filter.term) { + var group_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'mom_object_group_id'; }); + if(!group_col || group_col.filter.term) { return; } @@ -548,7 +556,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic groupIds.sort(); - fillColumFilterSelectOptions(groupIds, $scope.columns[9]); + fillColumFilterSelectOptions(groupIds, $scope.columns.find(function(c) {return c.field == 'mom_object_group_id'; })); }; function fillInfoColumFilterSelectOptions() { @@ -592,7 +600,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic } task_info.sort(keysrt('value')); - fillColumFilterSelectOptions(task_info, $scope.columns[6]); + fillColumFilterSelectOptions(task_info, $scope.columns.find(function(c) {return c.field == 'info'; })); }; $scope.$watch('dataService.selected_task_ids', onSelectedTaskIdsChanged, true); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html index 4a2109ce11aebb3ed3f7683b4f1b0558344e6fb2..6090be4503d51ee7e9ed153e646d3992e8829c3e 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html @@ -53,7 +53,7 @@ <div ng-controller="DataController as dataCtrl" class="container-fluid"> <div ng-controller="CleanupController as cleanupCtrl" class="container-fluid"> <div class="row"> - <div class="col-md-2"> + <div class="col-md-1"> <label>Time (UTC):</label> <p> <strong style="font-size:16px">{{dataService.lofarTime | date }}</strong> diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html new file mode 100644 index 0000000000000000000000000000000000000000..6fe5bccb97a2dbe16f418362d627736fe9867266 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html @@ -0,0 +1,120 @@ +<!doctype html> +<!-- $Id: index.html 35449 2016-09-28 06:54:12Z schaap $ --> +<html lang='en' ng-app="raeApp"> + <head> + <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> + <title>{{title}}</title> + <link rel='shortcut icon' href='{{ url_for('static', filename='favicon.ico') }}'> + <link href="/static/js/angular-gantt/angular-gantt.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-gantt/angular-gantt-plugins.min.css" rel="stylesheet" type="text/css"> + <link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-ui-grid/ui-grid.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-ui-layout/angular-ui-layout.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-material/angular-material.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/utils/datetimepicker.css" rel="stylesheet" type="text/css"> + <link href="/static/css/main.css" rel="stylesheet" type="text/css"> + <script src="/static/js/utils/startswith.js"></script> + <script src="/static/js/moment/moment.js"></script> + <script src="/static/js/jquery/jquery.min.js"></script> + <script src="/static/js/utils/bootstrap.min.js"></script> + <script type="text/javascript" src="/static/js/highcharts/highcharts.js"></script> + <script type="text/javascript" src="/static/js/highcharts/exporting.js"></script> + <script src="/static/js/angular/angular.min.js"></script> + <script src="/static/js/utils/ui-bootstrap-tpls.min.js"></script> + <script src="/static/js/angular-route/angular-route.min.js"></script> + <script src="/static/js/angular-touch/angular-touch.js"></script> + <script src="/static/js/angular-resource/angular-resource.min.js"></script> +<!-- <script src="/static/js/utils/ui-grid-edit-datepicker.js"></script>--> + <script src="/static/js/angular-ui-grid/ui-grid.js"></script> + <script src="/static/js/angular-ui-tree/angular-ui-tree.js"></script> + <script src="/static/js/angular-ui-layout/angular-ui-layout.min.js"></script> + <script src="/static/js/angular-ui-tabs/angular-ui.bootstrap.tabs.min.js"></script> + <script src="/static/js/angular-moment/angular-moment.js"></script> + <script src="/static/js/angular-sanitize/angular-sanitize.min.js"></script> + <script src="/static/js/angular-animate/angular-animate.min.js"></script> + <script src="/static/js/angular-aria/angular-aria.min.js"></script> + <script src="/static/js/angular-material/angular-material.min.js"></script> + <script src="/static/js/utils/datetimepicker.js"></script> + <script src="/static/js/jsplumb/jsplumb-2.0.7-min.js"></script> + <script src="/static/js/angular-gantt/angular-gantt.js"></script> + <script src="/static/js/angular-gantt/angular-gantt-plugins.js"></script> + <script type="text/javascript" src="/static/js/highcharts/highcharts-ng.js"></script> + <script src="/static/app/app.js"></script> + <script src="/static/app/controllers/datacontroller.js"></script> + <script src="/static/app/controllers/cleanupcontroller.js"></script> + <script src="/static/app/controllers/gridcontroller.js"></script> + <script src="/static/app/controllers/ganttresourcecontroller.js"></script> + <script src="/static/app/controllers/ganttprojectcontroller.js"></script> + <script src="/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js"></script> + <script src="/static/app/controllers/chartresourceusagecontroller.js"></script> + </head> + <body style="overflow:hidden;"> + {% raw %} + <div ng-controller="DataController as dataCtrl" class="container-fluid" ng-init="dataService.projectMode=true"> + <div ng-controller="CleanupController as cleanupCtrl" class="container-fluid"> + <div> + <div style="float:left; min-width:250px;"> + <label>Project:</label> + <p style="font-size:14px;" > + <select ng-model="dataService.selected_project" ng-options="{ name: item.name, value: item.mom_id } as item.name for item in dataService.momProjects"></select> + </p> + </div> + <div style="float:left; width:220px;"> + <label>Time (UTC):</label> + <p> + <strong style="font-size:16px">{{dataService.lofarTime | date }}</strong> + </p> + </div> + <div style="float:left; width:280px; padding-right:12px; "> + <label>From:</label> + <p class="input-group"> + <input type="text" class="form-control" style="float:left; min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" is-open="viewFromDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="openViewFromDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> + </span> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + </p> + </div> + <div style="float:left; width:280px; padding-right:12px; "> + <label>To:</label> + <p class="input-group"> + <input type="text" class="form-control" style="float:left; min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" is-open="viewToDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="openViewToDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> + </span> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + </p> + </div> + <div style="float:left; min-width:90px;"> + <label>Scroll:</label> + <p class="input-group"> + <button title="Scroll back in time" type="button" class="btn btn-default" ng-click="scrollBack()"><i class="glyphicon glyphicon-step-backward"></i></button> + <button title="Scroll forward in time" type="button" class="btn btn-default" ng-click="scrollForward()"><i class="glyphicon glyphicon-step-forward"></i></button> + </p> + </div> + <div style="float:left; min-width:180px;"> + <label>Zoom:</label> + <p class="input-group"> + <select class="form-control" ng-model="$parent.zoomTimespan" ng-options="option.name for option in $parent.zoomTimespans track by option.value" ng-change="$parent.onZoomTimespanChanged()"></select> + </p> + </div> + <div style="float:left; min-width:50px;"> + <label></label> + <p class="input-group"> + <button title="Show disk usage by project" type="button" class="btn btn-default" ng-click="cleanupCtrl.showAllProjectsDiskUsage()"><i class="glyphicon glyphicon-floppy-disk"></i></button> + </p> + </div> + </div> + + <div class="top-stretch" ui-layout options="{flow: 'column'}"> + <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" ui-layout-init-min-width="1160px"> + <div id="grid" + ui-grid="gridOptions" + ui-grid-edit ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-auto-resize + class="grid"></div> + </div> + </div> + </div> + {% endraw %} + </body> +</html> diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py index cc2ecfe214b65013c20ace70660c916c9076be07..2998ab6df1c0dc874387820d65e85d23872c5037 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py @@ -121,6 +121,13 @@ def index(): '''Serves the ResourceAssignmentEditor's index page''' return render_template('index.html', title='Scheduler') +@app.route('/projects') +@app.route('/projects.htm') +@app.route('/projects.html') +@gzipped +def projects(): + return render_template('projects.html', title='Projects') + @app.route('/rest/config') @gzipped def config(): @@ -525,13 +532,28 @@ def getProjectTasksFromUntil(project_mom2id, fromTimestamp=None, untilTimestamp= tasks = rarpc.getTasks(mom_ids=task_mom2ids, lower_bound=fromTimestamp, upper_bound=untilTimestamp) updateTaskMomDetails(tasks, momqueryrpc) - #updateTaskStorageDetails(tasks, sqrpc) + updateTaskStorageDetails(tasks, sqrpc) return jsonify({'tasks': tasks}) except Exception as e: logger.error(e) abort(404, str(e)) +@app.route('/rest/projects/<int:project_mom2id>/taskstimewindow') +@gzipped +def getProjectTasksTimeWindow(project_mom2id): + try: + return jsonify({'min_starttime': datetime(2016, 01, 01), 'max_endtime': datetime(2016, 12, 01)}) + + task_mom2ids = momqueryrpc.getProjectTaskIds(project_mom2id)['task_mom2ids'] + + timewindow = rarpc.getTasksTimeWindow(mom_ids=task_mom2ids) + + return jsonify(timewindow) + except Exception as e: + logger.error(e) + abort(404, str(e)) + @app.route('/rest/projects/<int:project_mom2id>/diskusage') @gzipped def getProjectDiskUsageById(project_mom2id):