diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py index 75d70775208d3a74b8aa5d02db9158b457d3d911..e1d0196bafaf7e7d744692f14f46a40d548e4b25 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py @@ -31,6 +31,7 @@ import collections from optparse import OptionParser from lofar.common import dbcredentials from lofar.common.util import to_csv_string +from lofar.common.datetimeutils import totalSeconds logger = logging.getLogger(__name__) @@ -178,6 +179,7 @@ class RADatabase: for task in tasks: task['predecessor_ids'] = predIds.get(task['id'], []) task['successor_ids'] = succIds.get(task['id'], []) + task['duration'] = totalSeconds(task['endtime'] - task['starttime']) return tasks @@ -199,7 +201,12 @@ class RADatabase: query += '''where tv.otdb_id = (%s);''' result = self._executeQuery(query, validIds, fetch=_FETCH_ONE) - return dict(result) if result else None + task = dict(result) if result else None + + if task: + task['duration'] = totalSeconds(task['endtime'] - task['starttime']) + + return task def _convertTaskTypeAndStatusToIds(self, task_status, task_type): '''converts task_status and task_type to id's in case one and/or the other are strings''' @@ -1391,6 +1398,12 @@ if __name__ == '__main__': print '\n-- ' + str(method.__name__) + ' --' print '\n'.join([str(x) for x in method()]) + resultPrint(db.getTasks) + + for t in db.getTasks(): + print db.getTask(t['id']) + + exit() #print db.getResourceClaims(task_id=440) #print diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js index 3e85058023e62facf40bc2b5c2635c9a51c3ab09..1c33462df9ba33925b88f5353491b52bbf5098b8 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js @@ -14,3 +14,9 @@ var app = angular.module('raeApp', app.config(['$compileProvider', function ($compileProvider) { $compileProvider.debugInfoEnabled(false); }]); + +app.filter('secondsToHHmmss', function($filter) { + return function(seconds) { + return $filter('date')(new Date(0, 0, 0).setSeconds(seconds), 'HH:mm:ss'); + }; +}) diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js index 8d561e85be224421435fe0dee47d09a82fc9b4ba..f503b02ad280d0886a5f155aa3db17a36fa9ef37 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js @@ -98,7 +98,7 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc //set title, axis etc $scope.chartConfig.title.text = resource.name; - $scope.chartConfig.yAxis.title.text = resource.units; + $scope.chartConfig.yAxis.title.text = resource.unit; $scope.chartConfig.xAxis.min = $scope.dataService.viewTimeSpan.from.getTime(); $scope.chartConfig.xAxis.max = $scope.dataService.viewTimeSpan.to.getTime(); @@ -209,7 +209,7 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc series.events = { legendItemClick: function() { self.seriesVisibilityCache[this.name] = !this.visible; }}; $scope.chartSeries.push(series); } - series.visible = self.seriesVisibilityCache.hasOwnProperty(series.name) ? self.seriesVisibilityCache[series.name] : true; + series.visible = self.seriesVisibilityCache.hasOwnProperty(series.name) ? self.seriesVisibilityCache[series.name] : false; series.data = [[timestamps[0].getTime(), resource.total_capacity], [timestamps[timestamps.length-1].getTime(), resource.total_capacity]] expectedSeriesNames.push('total capacity'); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js index 0d773e2e956638580387f4e99183ed62c9e5747b..e524f91b4e2545a417f0d7aa987d7a6be6a54688 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js @@ -66,9 +66,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, 'conflict':'#ff0000', 'prescheduled': '#6666ff', 'scheduled': '#0000ff', - 'queued': '#6666ff', + 'queued': '#ccffff', 'active': '#ffff00', - 'completing': '#99ff33', + 'completing': '#ffdd99', 'finished': '#00ff00', 'aborted': '#cc0000', 'error': '#990033', @@ -221,6 +221,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } + var initialTaskLoad = self.tasks.length == 0; + var newTaskDict = self.toIdBasedDict(result.tasks); var newTaskIds = Object.keys(newTaskDict); @@ -239,6 +241,33 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.computeMinMaxTaskTimes(); + if(initialTaskLoad && self.tasks.length > 0) { + setTimeout(function() { + //try to select current task + var currentTasks = self.tasks.filter(function(t) { return t.starttime <= self.lofarTime && t.endtime >= self.lofarTime; }); + + if(currentTasks.length > 0) { + self.selected_task_id = currentTasks[0].id; + } else { + //try to select next task + var nextTasks = self.tasks.filter(function(t) { return t.starttime >= self.lofarTime; }).sort(); + + if(nextTasks.length > 0) { + self.selected_task_id = nextTasks[0].id; + } else { + //try to select most recent task + var prevTasks = self.tasks.filter(function(t) { return t.endtime <= self.lofarTime; }).sort(); + + if(prevTasks.length > 0) { + self.selected_task_id = prevTasks[prevTasks.length-1].id; + } else { + self.selected_task_id = self.tasks[0].id; + } + } + } + }, 1000); + } + defer.resolve(); }); @@ -273,7 +302,14 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.resources = result.resources; self.resourceDict = self.toIdBasedDict(self.resources); - self.selected_resource = self.resources[0]; + //try to select first storage resource as default selected_resource_id + var storageResources = self.resources.filter(function(r) { return r.type_name == 'storage'; }); + if(storageResources.length > 0) { + self.selected_resource_id = storageResources[0].id; + } else { + //else, just the first resource + self.selected_resource_id = self.resources[0].id; + } defer.resolve(); }); @@ -620,8 +656,8 @@ dataControllerMod.controller('DataController', $scope.openViewFromDatePopup = function() { $scope.viewFromDatePopupOpened = true; }; $scope.openViewToDatePopup = function() { $scope.viewToDatePopupOpened = true; }; - $scope.jumpTimespanWidths = [{value:30, name:'30 Minutes'}, {value:60, name:'1 Hour'}, {value:3*60, name:'3 Hours'}, {value:6*60, name:'6 Hours'}, {value:12*60, name:'12 Hours'}, {value:24*60, name:'1 Day'}, {value:2*24*60, name:'2 Days'}, {value:5*24*60, name:'5 Days'}, {value:7*24*60, name:'1 Week'}, {value:14*24*60, name:'2 Weeks'}, {value:28*24*60, name:'4 Weeks'}]; - $scope.jumpTimespanWidth = $scope.jumpTimespanWidths[8]; + $scope.jumpTimespanWidths = [{value:30, name:'30 Minutes'}, {value:60, name:'1 Hour'}, {value:3*60, name:'3 Hours'}, {value:6*60, name:'6 Hours'}, {value:12*60, name:'12 Hours'}, {value:24*60, name:'1 Day'}, {value:2*24*60, name:'2 Days'}, {value:3*24*60, name:'3 Days'}, {value:5*24*60, name:'5 Days'}, {value:7*24*60, name:'1 Week'}, {value:14*24*60, name:'2 Weeks'}, {value:28*24*60, name:'4 Weeks'}]; + $scope.jumpTimespanWidth = $scope.jumpTimespanWidths[7]; $scope.jumpToNow = function() { var floorLofarTime = dataService.floorDate(dataService.lofarTime, 1, 5); dataService.viewTimeSpan = { diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js index f75da077ea7866cc24a0e95f18236f33202dca55..312e7885528aa5357b1e9374af46483bb10b5cab 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js @@ -174,11 +174,24 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat //build tree of resourceGroups //note that one resourceGroup can be a child of multiple parents if(resourceGroupMemberships.hasOwnProperty('groups')) { + var rootGroupIds = []; + for(var groupId in resourceGroupMemberships.groups) { if(resourceGroupMemberships.groups[groupId].parent_ids.length == 0) { - //resourceGroup is a root item (no parents) - //so start creating a ganttRow tree for this root and all its descendants - createGanttRowTree(groupId, null); + rootGroupIds.push(groupId); + } + } + + if(rootGroupIds.length == 1) + { + //don't show single rootnode + var rootChildGroupIds = resourceGroupMemberships.groups[rootGroupIds[0]].child_ids; + for(var i = 0; i < rootChildGroupIds.length; i++) { + createGanttRowTree(rootChildGroupIds[i], null); + } + } else { + for(var i = 0; i < rootGroupIds.length; i++) { + createGanttRowTree(rootGroupIds[i], null); } } } @@ -357,27 +370,29 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat } var ganttRows = resourceGroup2GanttRows[groupId]; - for(var ganttRow of ganttRows) { - for(var taskId in aggregatedClaims) { - var aggClaimForTask = aggregatedClaims[taskId]; - var task = taskDict[taskId]; - if(task) { - var claimTask = { - id: 'aggregatedClaimForTask_' + taskId + '_' + ganttRow.id, - name: task.name, - from: aggClaimForTask.starttime, - to: aggClaimForTask.endtime, - color: self.resourceClaimStatusColors[aggClaimForTask.status], - classes: 'claim-task-status-' + task.status, - raTask: task, - movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 - }; - - if(task.id == dataService.selected_task_id) { - claimTask.classes += ' claim-selected-task'; - } + if(ganttRows) { + for(var ganttRow of ganttRows) { + for(var taskId in aggregatedClaims) { + var aggClaimForTask = aggregatedClaims[taskId]; + var task = taskDict[taskId]; + if(task) { + var claimTask = { + id: 'aggregatedClaimForTask_' + taskId + '_' + ganttRow.id, + name: task.name, + from: aggClaimForTask.starttime, + to: aggClaimForTask.endtime, + color: self.resourceClaimStatusColors[aggClaimForTask.status], + classes: 'claim-task-status-' + task.status, + raTask: task, + movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 + }; - ganttRow.tasks.push(claimTask); + if(task.id == dataService.selected_task_id) { + claimTask.classes += ' claim-selected-task'; + } + + ganttRow.tasks.push(claimTask); + } } } } diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js index 6aa662528283473b2524cd715310803fbd395107..ae7f5cf8513ce87bae97a21780257fdd699d136e 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js @@ -4,7 +4,8 @@ var gridControllerMod = angular.module('GridControllerMod', ['ui.grid', 'ui.grid.edit', 'ui.grid.selection', 'ui.grid.cellNav', - 'ui.grid.resizeColumns']); + 'ui.grid.resizeColumns', + 'ui.grid.autoResize']); gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGridConstants', function($scope, dataService, uiGridConstants) { @@ -25,37 +26,34 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid selectOptions: [] } }, - { field: 'mom_id', - displayName: 'MoM ID', - enableCellEdit: false, - cellTemplate:'<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.mom2object_id}}">{{row.entity[col.field]}}</a>', - width: '7.5%' - }, - { field: 'otdb_id', - displayName: 'SAS ID', - enableCellEdit: false, - width: '7.5%' - }, { field: 'starttime', displayName: 'Start', - width: '15%', + width: '14%', type: 'date', enableCellEdit: false, enableCellEditOnFocus: false, - cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm\'}}</div>' -// editableCellTemplate: '<div><form name="inputForm"><div ui-grid-edit-datepicker row-field="MODEL_COL_FIELD" ng-class="\'colt\' + col.uid"></div></form></div>' + cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>', + sort: { direction: uiGridConstants.ASC } }, { field: 'endtime', displayName: 'End', - width: '15%', + width: '14%', type: 'date', enableCellEdit: false, enableCellEditOnFocus: false, - cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm\'}}</div>' + cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>' + }, + { field: 'duration', + displayName: 'Duration', + width: '8%', + enableFiltering: false, + enableCellEdit: false, + enableCellEditOnFocus: false, + cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | secondsToHHmmss}}</div>' }, { field: 'status', enableCellEdit: true, - width: '12.5%', + width: '8%', filter: { type: uiGridConstants.filter.SELECT, selectOptions: [] @@ -65,11 +63,22 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid }, { field: 'type', enableCellEdit: false, - width: '12.5%', + width: '8%', filter: { type: uiGridConstants.filter.SELECT, selectOptions: [] } + }, + { field: 'mom_id', + displayName: 'MoM ID', + enableCellEdit: false, + cellTemplate:'<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.mom2object_id}}">{{row.entity[col.field]}}</a>', + width: '8%' + }, + { field: 'otdb_id', + displayName: 'SAS ID', + enableCellEdit: false, + width: '8%' }]; $scope.gridOptions = { enableGridMenu: false, @@ -77,6 +86,7 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid enableFiltering: true, enableCellEdit: false, enableColumnResize: true, + enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER, enableRowSelection: true, enableRowHeaderSelection: true, enableFullRowSelection: false, @@ -104,7 +114,6 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid } }; - function filterTasks() { var taskDict = $scope.dataService.taskDict; var filteredTasks = []; @@ -139,24 +148,31 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid columnDef.filter.selectOptions = columnSelectOptions; }; - $scope.$watch('dataService.tasks', function() { + function populateList() { if('tasks' in $scope.dataService && $scope.dataService.tasks.length > 0) { + var viewFrom = $scope.dataService.viewTimeSpan.from; + var viewTo = $scope.dataService.viewTimeSpan.to; + var tasks = []; + for(var task of $scope.dataService.tasks) { - var gridTask = { - id: task.id, - name: task.name, - project_name: task.project_name, - mom_id: task.mom_id, - otdb_id: task.otdb_id, - starttime: task.starttime, - endtime: task.endtime, - status: task.status, - type: task.type, - project_mom2object_id: task.project_mom2object_id, - mom2object_id: task.mom2object_id - }; - tasks.push(gridTask); + if(task.endtime >= viewFrom && task.starttime <= viewTo) { + var gridTask = { + id: task.id, + name: task.name, + project_name: task.project_name, + mom_id: task.mom_id, + otdb_id: task.otdb_id, + starttime: task.starttime, + endtime: task.endtime, + duration: task.duration, + status: task.status, + type: task.type, + project_mom2object_id: task.project_mom2object_id, + mom2object_id: task.mom2object_id + }; + tasks.push(gridTask); + } } $scope.gridOptions.data = tasks; @@ -164,17 +180,32 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid $scope.gridOptions.data = [] fillProjectsColumFilterSelectOptions(); + }; + + function jumpToSelectedTaskRow() { + var taskIdx = $scope.gridOptions.data.findIndex(function(row) {return row.id == dataService.selected_task_id}); + + if(taskIdx > -1) { + $scope.gridApi.selection.selectRow($scope.gridOptions.data[taskIdx]); + $scope.gridApi.core.scrollTo($scope.gridOptions.data[taskIdx], null); + } + }; + + $scope.$watch('dataService.taskChangeCntr', populateList); + $scope.$watch('dataService.viewTimeSpan', function() { + populateList(); + setTimeout(jumpToSelectedTaskRow, 250); }, true); $scope.$watch('dataService.taskstatustypes', function() { taskstatustypenames = $scope.dataService.taskstatustypes.map(function(x) { return x.name; }); - fillColumFilterSelectOptions(taskstatustypenames, $scope.columns[6]); + fillColumFilterSelectOptions(taskstatustypenames, $scope.columns[5]); $scope.columns[6].editDropdownOptionsArray = $scope.dataService.taskstatustypes.map(function(x) { return {id:x.name, value:x.name}; }); }); $scope.$watch('dataService.tasktypes', function() { tasktypenames = $scope.dataService.tasktypes.map(function(x) { return x.name; }); - fillColumFilterSelectOptions(tasktypenames, $scope.columns[7]); + fillColumFilterSelectOptions(tasktypenames, $scope.columns[6]); }); function fillProjectsColumFilterSelectOptions() { @@ -198,13 +229,6 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid }; $scope.$watch('dataService.momProjectsDict', fillProjectsColumFilterSelectOptions); - - $scope.$watch('dataService.selected_task_id', function() { - var taskIdx = $scope.gridOptions.data.findIndex(function(row) {return row.id == dataService.selected_task_id}); - - if(taskIdx > -1) { - $scope.gridApi.selection.selectRow($scope.gridOptions.data[taskIdx]); - } - }); + $scope.$watch('dataService.selected_task_id', jumpToSelectedTaskRow); } ]); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css index cdd0568f2c5e686eecb680d293843e055d4467ea..2abc793dd109eb8c1d970c65ed641eec7c7fa3ec 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css @@ -3,8 +3,13 @@ .grid { min-height: 400px; min-width: 500px; - height: auto; - width: auto; + height: 100%; + width: 100%; + +} + +.stretch { + overflow: initial; } .gantt { @@ -24,13 +29,13 @@ } .ui-layout-row > .ui-splitbar { - height: 8px; - background-color: #888888; + height: 6px; + background-color: #CCCCCC; } .ui-layout-column > .ui-splitbar { - width: 8px; - background-color: #888888; + width: 6px; + background-color: #CCCCCC; } ul.uib-datepicker-popup.dropdown-menu button.btn.btn-sm.btn-danger.uib-clear { @@ -52,7 +57,7 @@ table.uib-timepicker td.uib-time { } .top-stretch { - top: 60px; + top: 65px; } .gantt-task-content { @@ -112,7 +117,7 @@ div.gantt-task.claim-task-status-scheduled span { } div.gantt-task.claim-task-status-queued span { - background: #6666ff; + background: #ccffff; } div.gantt-task.claim-task-status-active span { @@ -120,7 +125,7 @@ div.gantt-task.claim-task-status-active span { } div.gantt-task.claim-task-status-completing span { - background: #99ff33; + background: #ffdd99; } div.gantt-task.claim-task-status-finished span { diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html index 214ea165b3b292ea76865274977b0be55241a15c..0818ef43f8f240e2d2771e329100588ec14a4cc2 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html @@ -42,7 +42,7 @@ <script src="/static/app/controllers/ganttprojectcontroller.js"></script> <script src="/static/app/controllers/chartresourceusagecontroller.js"></script> </head> - <body> + <body style="overflow:hidden;"> {% raw %} <div ng-controller="DataController as dataCtrl" class="container-fluid"> <div class="row"> @@ -87,68 +87,61 @@ </p> </div> </div> - <div ui-layout class="top-stretch" ui-layout="{flow: 'column'}"> - <header> - <uib-tabset margin-top='10px'> - <uib-tab heading="Tasks" index='0' active='true'> - <div ng-controller="GridController as gridCtrl"> - <div id="grid" ui-grid="gridOptions" ui-grid-edit ui-grid-selection ui-grid-cellnav ui-grid-resize-columns class="grid"> - </div> - </div> - </uib-tab> - <uib-tab heading="Usages" index='1' active='false'> - <div class="tab-pane active" ng-controller="ChartResourceUsageController as chartResourceUsageCtrl"> - <highchart id="chart_resource_usage" config="chartConfig" style="width: 95%; height: 40%; margin: 10px;" ></highchart> - </div> - </uib-tab> - </uib-tabset> - </header> - <header> - <uib-tabset margin-top='10px'> - <uib-tab heading="Projects" index='0' active='true'> - <div class="tab-pane active" ng-controller="GanttProjectController as ganttProjectCtrl"> - <div gantt data=ganttData - api=options.api - show-side='true' - view-scale="options.viewScale" - from-date="options.fromDate" - to-date="options.toDate" - current-date="options.currentDate" - current-date-value="options.currentDateValue" - column-magnet="options.columnMagnet"> - <gantt-tree enabled="true"></gantt-tree> - <gantt-movable enabled="true" - allow-moving="true" - allow-resizing="true" - allow-row-switching="false"> - </gantt-movable> - <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> - <gantt-dependencies enabled="true"></gantt-dependencies> - </div> - </div> - </uib-tab> - <uib-tab heading="Resources" index='1' active='false'> - <div class="tab-pane active" ng-controller="GanttResourceController as ganttResourceCtrl"> - <div gantt data=ganttData - api=options.api - show-side='true' - view-scale="options.viewScale" - from-date="options.fromDate" - to-date="options.toDate" - current-date="options.currentDate" - current-date-value="options.currentDateValue" - column-magnet="options.columnMagnet"> - <gantt-tree enabled="true"></gantt-tree> - <gantt-movable enabled="true" - allow-moving="true" - allow-resizing="true" - allow-row-switching="false"></gantt-movable> - <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> - </div> - </div> - </uib-tab> - <uib-tabset> - <header> + + + <div class="top-stretch" ui-layout options="{flow: 'column'}"> + <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" > + <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 ui-layout options="{flow: 'row'}"> + <div ng-controller="GanttProjectController as ganttProjectCtrl" style="overflow:auto; margin-left:12px; margin-bottom:4px; "> + <div gantt data=ganttData + api=options.api + show-side='true' + view-scale="options.viewScale" + from-date="options.fromDate" + to-date="options.toDate" + current-date="options.currentDate" + current-date-value="options.currentDateValue" + column-magnet="options.columnMagnet"> + <gantt-tree enabled="true"></gantt-tree> + <gantt-movable enabled="true" + allow-moving="true" + allow-resizing="true" + allow-row-switching="false"> + </gantt-movable> + <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> + <gantt-dependencies enabled="true"></gantt-dependencies> + </div> + </div> + + <div ng-controller="ChartResourceUsageController as chartResourceUsageCtrl"> + <highchart id="chart_resource_usage" config="chartConfig" style="width: 96%; height: 100%; margin: 12px;" ></highchart> + </div> + + <div ng-controller="GanttResourceController as ganttResourceCtrl" style="overflow:auto; margin-left:12px; margin-top:12px"> + <div gantt data=ganttData + api=options.api + show-side='true' + view-scale="options.viewScale" + from-date="options.fromDate" + to-date="options.toDate" + current-date="options.currentDate" + current-date-value="options.currentDateValue" + column-magnet="options.columnMagnet"> + <gantt-tree enabled="true"></gantt-tree> + <gantt-movable enabled="true" + allow-moving="true" + allow-resizing="true" + allow-row-switching="false"></gantt-movable> + <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> + </div> + </div> + + </div> </div> </div> {% endraw %}