Select Git revision
radbservice
-
Jorrit Schaap authored
Task #8758: merge from branch RA-Task8758 to trunk. svn merge did not work, so I used svn diff on branch and svn patch to trunk. For revision history, see branch.
Jorrit Schaap authoredTask #8758: merge from branch RA-Task8758 to trunk. svn merge did not work, so I used svn diff on branch and svn patch to trunk. For revision history, see branch.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ganttresourcecontroller.js 21.90 KiB
// $Id: ganttresourcecontroller.js 32761 2015-11-02 11:50:21Z schaap $
var ganttResourceControllerMod = angular.module('GanttResourceControllerMod', [
'gantt',
'gantt.sortable',
'gantt.movable',
'gantt.drawtask',
'gantt.tooltips',
'gantt.bounds',
'gantt.progress',
'gantt.tree',
'gantt.groups',
'gantt.overlap',
'gantt.resizeSensor',
'gantt.contextmenu']).config(['$compileProvider', function($compileProvider) {
$compileProvider.debugInfoEnabled(false); // Remove debug info (angularJS >= 1.3)
}]);
ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dataService', function($scope, dataService) {
var self = this;
$scope.dataService = dataService;
$scope.ganttData = []
$scope.enabled = true;
self.taskStatusColors = dataService.taskStatusColors;
self.resourceClaimStatusColors = dataService.resourceClaimStatusColors;
$scope.options = {
mode: 'custom',
viewScale: '1 hours',
currentDate: 'line',
currentDateValue: $scope.dataService.lofarTime,
columnMagnet: '1 minutes',
timeFramesMagnet: false,
sideMode: 'Tree',
autoExpand: 'both',
taskOutOfRange: 'truncate',
api: function(api) {
// API Object is used to control methods and events from angular-gantt.
$scope.api = api;
api.core.on.ready($scope, function () {
api.tasks.on.moveEnd($scope, moveHandler);
api.tasks.on.resizeEnd($scope, moveHandler);
});
api.directives.on.new($scope, function(directiveName, directiveScope, directiveElement) {
if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' ) {
directiveElement.bind('click', function(event) {
if(directiveScope.row.model.resource) {
$scope.dataService.selected_resource_id = directiveScope.row.model.resource.id;
} else if(directiveScope.row.model.resourceGroup) {
$scope.dataService.selected_resourceGroup_id = directiveScope.row.model.resourceGroup.id;
}
});
} else if (directiveName === 'ganttTask') {
directiveElement.bind('click', function(event) {
if(directiveScope.task.model.raTask) {
if(event.ctrlKey) {
$scope.dataService.toggleTaskSelection(directiveScope.task.model.raTask.id);
} else {
$scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id);
}
}
if(directiveScope.task.model.claim) {
$scope.dataService.selected_resourceClaim_id = directiveScope.task.model.claim.id;
$scope.dataService.selected_resource_id = directiveScope.task.model.claim.resource_id;
}
});
directiveElement.bind('contextmenu', function(event) {
//search for already existing contextmenu element
if(directiveElement.find('#gantt-resource-context-menu').length) {
//found, remove it, so we can create a fresh one
directiveElement.find('#gantt-resource-context-menu')[0].remove();
}
//create contextmenu element
//with list of menu items,
//each with it's own action
var contextmenuElement = angular.element('<div id="gantt-resource-context-menu"></div>');
ulElement = angular.element('<ul style="z-index:10000; position:fixed; top:initial; left:initial; display:block;" role="menu" class="dropdown-menu"></ul>');
contextmenuElement.append(ulElement);
liElement = angular.element('<li><a href="#">Copy Task</a></li>');
ulElement.append(liElement);
liElement.on('click', function() {
$scope.dataService.copyTask(directiveScope.task.model.raTask);
closeContextMenu();
});
var closeContextMenu = function() {
contextmenuElement.remove();
angular.element(document).unbind('click', closeContextMenu);
};
//click anywhere to remove the contextmenu
angular.element(document).bind('click', closeContextMenu);
//add contextmenu to clicked element
directiveElement.append(contextmenuElement);
//prevent bubbling event upwards
return false;
});
}
});
api.directives.on.destroy($scope, function(directiveName, directiveScope, directiveElement) {
if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' || directiveName === 'ganttTask') {
directiveElement.unbind('click');
directiveElement.unbind('contextmenu');
}
});
}
};
function moveHandler(item)
{
var task = item.model.raTask;
var updatedTask = {
id: task.id,
starttime: item.model.from._d,
endtime: item.model.to._d
};
$scope.dataService.putTask(updatedTask);
};
function updateGanttDataAsync() {
$scope.$evalAsync(updateGanttData);
};
function updateGanttData() {
dataService.loadResourceClaims = $scope.enabled;
if(!$scope.enabled) {
return;
}
if(!dataService.initialLoadComplete) {
return;
}
var ganttRowsDict = {};
var resourceGroupsDict = $scope.dataService.resourceGroupsDict;
var resourceGroups = $scope.dataService.resourceGroups;
var numResourceGroups = resourceGroups.length;
var resourceDict = $scope.dataService.resourceDict;
var resources = $scope.dataService.resources;
var numResources = resources.length;
var resourceGroupMemberships = $scope.dataService.resourceGroupMemberships;
var taskDict = $scope.dataService.taskDict;
var numTasks = $scope.dataService.filteredTasks.length;
var resourceClaimDict = $scope.dataService.resourceClaimDict;
var resourceClaims = $scope.dataService.resourceClaims;
var numResourceClaims = resourceClaims.length;
if(numResourceGroups == 0 || numResources == 0){
$scope.ganttData = [];
return;
}
$scope.options.fromDate = $scope.dataService.viewTimeSpan.from;
$scope.options.toDate = $scope.dataService.viewTimeSpan.to;
var fullTimespanInMinutes = ($scope.options.toDate - $scope.options.fromDate) / (60 * 1000);
if(fullTimespanInMinutes > 14*24*60) {
$scope.options.viewScale = '1 days';
} else if(fullTimespanInMinutes > 7*24*60) {
$scope.options.viewScale = '6 hours';
} else if(fullTimespanInMinutes > 2*24*60) {
$scope.options.viewScale = '3 hours';
} else if(fullTimespanInMinutes > 12*60) {
$scope.options.viewScale = '1 hours';
} else {
$scope.options.viewScale = '10 minutes';
}
var editableTaskStatusIds = $scope.dataService.editableTaskStatusIds;
//dict resourceGroup2GanttRows for fast lookup of ganttrows based on groupId
var resourceGroup2GanttRows = {};
// recursive method which creates ganttrows for a resourceGroup(Id),
// and its childs, and adds the subtree to the parentRow
var createGanttRowTree = function(groupId, parentRow) {
var resourceGroup = resourceGroupsDict[groupId];
if(resourceGroup) {
var groupRowId = 'group_' + groupId;
if(parentRow) {
groupRowId += '_parent' + parentRow.id;
}
var ganttRow = {
id: groupRowId,
parent: parentRow ? parentRow.id : null,
name: resourceGroup.name,
resourceGroup: resourceGroup,
tasks: []
};
ganttRowsDict[groupRowId] = ganttRow;
//store ganttRow also in dict resourceGroup2GanttRows for fast lookup based on groupId
if(!resourceGroup2GanttRows.hasOwnProperty(groupId)) {
resourceGroup2GanttRows[groupId] = [];
}
resourceGroup2GanttRows[groupId].push(ganttRow);
//recurse over the childs
var numChilds = resourceGroupMemberships.groups[groupId].child_ids.length;
for(var i = 0; i < numChilds; i++) {
var childGroupId = resourceGroupMemberships.groups[groupId].child_ids[i];
createGanttRowTree(childGroupId, ganttRow);
}
}
};
//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) {
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);
}
}
}
//dict resource2GanttRows for fast lookup of ganttrows based on resourceId
var resource2GanttRows = {};
//add resources to their parent resourceGroups
//note that one resource can be a child of multiple parent resourceGroups
if(resourceGroupMemberships.hasOwnProperty('resources')) {
for(var resourceId in resourceGroupMemberships.resources) {
var resource = resourceDict[resourceId];
if(resource) {
//of which parent(s) is this resource a child?
var parentGroupIds = resourceGroupMemberships.resources[resourceId].parent_group_ids;
//loop over parents
//add a ganttRow for the resource to each parent ganttRow
for(var parentGroupId of parentGroupIds) {
//note that one parentResourceGroup can actually have multiple rows
//since each resourceGroup itself can have multiple parents
var parentGanttRows = resourceGroup2GanttRows[parentGroupId];
if(parentGanttRows) {
for(var parentGanttRow of parentGanttRows) {
var resourceGanttRowId = 'resource_' + resource.id + '_' + parentGanttRow.id;
var ganttRow = {
id: resourceGanttRowId,
parent: parentGanttRow.id,
name: resource.name,
tasks: [],
resource: resource
};
ganttRowsDict[resourceGanttRowId] = ganttRow;
//store ganttRow also in dict resource2GanttRows for fast lookup based on groupId
if(!resource2GanttRows.hasOwnProperty(resourceId)) {
resource2GanttRows[resourceId] = [];
}
resource2GanttRows[resourceId].push(ganttRow);
}
}
}
}
}
}
//there are also resources which are not part of a group
//add these as well.
for(var resourceId in resourceDict) {
var resource = resourceDict[resourceId];
if(!resource2GanttRows.hasOwnProperty(resourceId)) {
var resourceGanttRowId = 'resource_' + resource.id;
var ganttRow = {
id: resourceGanttRowId,
name: resource.name,
tasks: []
};
ganttRowsDict[resourceGanttRowId] = ganttRow;
resource2GanttRows[resourceId] = [ganttRow];
}
}
if(numResourceClaims > 0 && numTasks > 0) {
//dict resource2Claims for fast lookup of claims based on resourceId
var resource2Claims = {};
//and finally assign each resourceclaim to its resource in each group
for(var claim of resourceClaims) {
var resourceId = claim.resource_id;
var task = taskDict[claim.task_id];
if(!task) {
continue;
}
if(!resource2Claims.hasOwnProperty(resourceId)) {
resource2Claims[resourceId] = [];
}
resource2Claims[resourceId].push(claim);
var ganttRows = resource2GanttRows[resourceId];
if(!ganttRows) {
continue;
}
for(var ganttRow of ganttRows) {
var claimTask = {
id: claim.id,
name: task.name,
from: claim.starttime,
to: claim.endtime,
color: self.resourceClaimStatusColors[claim.status],
classes: 'claim-task-status-' + task.status,
raTask: task,
claim: claim,
movable: $.inArray(task.status_id, editableTaskStatusIds) > -1
};
if(claim.id == dataService.selected_resourceClaim_id) {
claimTask.classes += ' claim-selected-claim';
} else if(dataService.isTaskIdSelected(task.id)) {
claimTask.classes += ' claim-selected-task';
}
ganttRow.tasks.push(claimTask);
}
}
// recursive method which aggregates the properties of the descendants tree
var aggregateDescendants = function(groupId) {
var aggregatedClaims = {};
var resourceIds = resourceGroupMemberships.groups[groupId].resource_ids;
for(var resourceId of resourceIds) {
var claims = resource2Claims[resourceId];
if(claims) {
for(var claim of claims) {
var taskId = claim.task_id;
var task = taskDict[taskId];
if(taskId in aggregatedClaims) {
if(claim.starttime < aggregatedClaims[taskId].starttime) {
aggregatedClaims[taskId].starttime = claim.starttime.getTime() > task.starttime.getTime() ? claim.starttime : task.starttime;
}
if(claim.endtime > aggregatedClaims[taskId].endtime) {
aggregatedClaims[taskId].endtime = claim.endtime.getTime() < task.endtime.getTime() ? claim.endtime: task.endtime;
}
if(claim.status == 'conflict') {
aggregatedClaims[taskId].status = 'conflict';
} else if(claim.status != aggregatedClaims[taskId].status && aggregatedClaims[taskId].status != 'conflict') {
aggregatedClaims[taskId].status = 'mixed';
}
} else {
aggregatedClaims[taskId] = { starttime: claim.starttime,
endtime: claim.endtime,
status: claim.status
};
}
}
}
}
var childGroupIds = resourceGroupMemberships.groups[groupId].child_ids;
for(var childGroupId of childGroupIds) {
var subAggregatedClaims = aggregateDescendants(childGroupId);
for(var taskId in subAggregatedClaims) {
var subAggregatedClaim = subAggregatedClaims[taskId];
if(taskId in aggregatedClaims) {
if(subAggregatedClaim.starttime < aggregatedClaims[taskId].starttime) {
aggregatedClaims[taskId].starttime = subAggregatedClaim.starttime;
}
if(subAggregatedClaim.endtime > aggregatedClaims[taskId].endtime) {
aggregatedClaims[taskId].endtime = subAggregatedClaim.endtime;
}
if(subAggregatedClaim.status == 'conflict') {
aggregatedClaims[taskId].status = 'conflict';
} else if(subAggregatedClaim.status != aggregatedClaims[taskId].status && aggregatedClaims[taskId].status != 'conflict') {
aggregatedClaims[taskId].status = 'mixed';
}
} else {
aggregatedClaims[taskId] = { starttime: subAggregatedClaim.starttime,
endtime: subAggregatedClaim.endtime,
status: subAggregatedClaim.status };
}
}
}
var ganttRows = resourceGroup2GanttRows[groupId];
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
};
if(dataService.isTaskIdSelected(task.id)) {
claimTask.classes += ' claim-selected-task';
}
ganttRow.tasks.push(claimTask);
}
}
}
}
return aggregatedClaims;
};
//now that the whole tree has been built,
//and all resourceClaims are processed
//loop over the root resourceGroup again
//and aggregate the claims of the subtrees
if(resourceGroupMemberships.hasOwnProperty('groups')) {
for(var groupId in resourceGroupMemberships.groups) {
if(resourceGroupMemberships.groups[groupId].parent_ids.length == 0) {
//resourceGroup is a root item (no parents)
//aggregate the claims of the subtrees
aggregateDescendants(groupId);
}
}
}
}
var ganttRows = [];
for (var rowId in ganttRowsDict)
ganttRows.push(ganttRowsDict[rowId]);
$scope.ganttData = ganttRows;
};
$scope.$watch('dataService.initialLoadComplete', updateGanttDataAsync);
$scope.$watch('dataService.selected_task_ids', updateGanttDataAsync, true);
$scope.$watch('dataService.viewTimeSpan', updateGanttDataAsync, true);
$scope.$watch('dataService.claimChangeCntr', updateGanttDataAsync);
$scope.$watch('dataService.filteredTaskChangeCntr', updateGanttDataAsync);
$scope.$watch('enabled', function() { setTimeout(updateGanttDataAsync, 500); } );
$scope.$watch('dataService.lofarTime', function() {
$scope.$evalAsync(function() {
if($scope.dataService.lofarTime.getSeconds() % 10 == 0) {
$scope.options.currentDateValue= $scope.dataService.lofarTime;}
});
});
}
]);