-
Ruud Beukema authored
Task #9607: added info and size columns to grid, and ingest icons. Removed indication of blocked tasks in gantt-chart view.
Ruud Beukema authoredTask #9607: added info and size columns to grid, and ingest icons. Removed indication of blocked tasks in gantt-chart view.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
datacontroller.js 43.68 KiB
// $Id$
angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, $q){
var self = this;
self.tasks = [];
self.resources = [];
self.resourceGroups = [];
self.resourceClaims = [];
self.tasktypes = [];
self.taskstatustypes = [];
self.editableTaskStatusIds = [];
self.taskDict = {};
self.resourceDict = {};
self.resourceGroupsDict = {};
self.resourceGroupMemberships = {};
self.resourceClaimDict = {};
self.resourceUsagesDict = {};
self.tasktypesDict = {};
self.momProjects = [];
self.momProjectsDict = {};
self.resourcesWithClaims = [];
self.filteredTasks = [];
self.filteredTaskDict = {};
self.taskTimes = {};
self.resourceClaimTimes = {};
self.config = {};
self.selected_resource_id;
self.selected_resourceGroup_id;
self.selected_task_ids = [];
self.selected_project_id;
self.selected_resourceClaim_id;
self.initialLoadComplete = false;
self.taskChangeCntr = 0;
self.filteredTaskChangeCntr = 0;
self.claimChangeCntr = 0;
self.resourceUsagesChangeCntr = 0;
self.loadedHours = {};
self.viewTimeSpan = {from: new Date(), to: new Date() };
self.autoFollowNow = true;
self.humanreadablesize = function(num) {
var units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z'];
for(unit of units) {
if(Math.abs(num) < 1000.0) {
return num.toPrecision(4).toString() + unit;
}
num /= 1000.0;
}
return num.toPrecision(5).toString() + 'Y';
}
self.isTaskIdSelected = function(task_id) {
return self.selected_task_ids.indexOf(task_id) != -1;
}
self.toggleTaskSelection = function(task_id) {
if(self.isTaskIdSelected(task_id)) {
self.removeSelectedTaskId(task_id);
} else {
self.addSelectedTaskId(task_id);
}
}
self.addSelectedTaskId = function(task_id) {
if(self.selected_task_ids.indexOf(task_id) == -1) {
self.selected_task_ids.push(task_id);
}
}
self.removeSelectedTaskId = function(task_id) {
var idx = self.selected_task_ids.indexOf(task_id);
if(idx != -1) {
self.selected_task_ids.splice(idx, 1);
}
}
self.setSelectedTaskId = function(task_id) {
self.selected_task_ids.splice(0, self.selected_task_ids.length);
self.selected_task_ids.push(task_id);
}
self.setSelectedTaskIds = function(task_ids) {
self.selected_task_ids.splice(0, self.selected_task_ids.length);
for(var task_id of task_ids) {
self.selected_task_ids.push(task_id);
}
}
self.selectTasksInSameGroup = function(task) {
self.selected_task_ids.splice(0, self.selected_task_ids.length);
var groupTasks = self.filteredTasks.filter(function(t) { return t.mom_object_group_id == task.mom_object_group_id; });
for(var t of groupTasks) {
self.selected_task_ids.push(t.id);
}
}
self.floorDate = function(date, hourMod=1, minMod=1) {
var min = date.getMinutes();
min = date.getMinutes()/minMod;
min = Math.floor(date.getMinutes()/minMod);
min = minMod*Math.floor(date.getMinutes()/minMod);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hourMod*Math.floor(date.getHours()/hourMod), minMod*Math.floor(date.getMinutes()/minMod));
};
self.ceilDate = function(date, hourMod=1, minMod=1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hourMod*Math.ceil(date.getHours()/hourMod), minMod*Math.ceil(date.getMinutes()/minMod));
};
self.resourceClaimStatusColors = {'claimed':'#ffa64d',
'conflict':'#ff0000',
'allocated': '#66ff66',
'mixed': '#bfbfbf'}
self.taskStatusColors = {'prepared':'#cccccc',
'approved':'#8cb3d9',
'on_hold':'#b34700',
'conflict':'#ff0000',
'prescheduled': '#6666ff',
'scheduled': '#0000ff',
'queued': '#ccffff',
'active': '#ffff00',
'completing': '#ffdd99',
'finished': '#00ff00',
'aborted': '#cc0000',
'error': '#990033',
'obsolete': '#555555',
'opened': '#d9e5f2',
'suspended': '#996666'};
//-- IMPORTANT REMARKS ABOUT UTC/LOCAL DATE --
//Dates (datetimes) across javascript/angularjs/3rd party modules are a mess!
//every module uses their own parsing and displaying
//some care about utc/local date, others don't
//So, to be consistent in this schedular client app, we chose the following conventions:
//1) All received/sent timestamps are strings in iso format 'yyyy-MM-ddTHH:mm:ssZ'
//2) All displayed timestamps should display the time in UTC, regardless of the timezone of the client.
//All javascript/angularjs/3rd party modules display local times correct and the same...
//So, to make 1&2 happen, we convert all received timestamps to 'local-with-utc-diff-correction', and then treat them as local.
//And if we send them to the web server, we convert them back to real utc.
//It's a stupid solution, but it works.
//-- IMPORTANT REMARKS ABOUT UTC/LOCAL DATE --
self.convertDatestringToLocalUTCDate = function(dateString) {
//first convert the dateString to proper Date
var date = new Date(dateString)
//then do our trick to offset the timestamp with the utcOffset, see explanation above.
return new Date(date.getTime() - self.utcOffset)
};
self.convertLocalUTCDateToISOString = function(local_utc_date) {
if(local_utc_date) {
//reverse trick to offset the timestamp with the utcOffset, see explanation above.
var real_utc = new Date(local_utc_date.getTime() + self.utcOffset)
return real_utc.toISOString();
}
return undefined;
};
//local client time offset to utc in milliseconds
self.utcOffset = moment().utcOffset()*60000;
self.toIdBasedDict = function(list) {
var dict = {}
if(list) {
for(var i = list.length-1; i >=0; i--) {
var item = list[i];
dict[item.id] = item;
}
}
return dict;
};
self.applyChanges = function(existingObj, changedObj) {
for(var prop in changedObj) {
if(existingObj.hasOwnProperty(prop) &&
changedObj.hasOwnProperty(prop) &&
existingObj[prop] != changedObj[prop]) {
if(existingObj[prop] instanceof Date && typeof changedObj[prop] === "string") {
existingObj[prop] = self.convertDatestringToLocalUTCDate(changedObj[prop]);
} else {
existingObj[prop] = changedObj[prop];
}
}
}
};
self.getTasksAndClaimsForViewSpan = function() {
var from = self.floorDate(self.viewTimeSpan.from, 1, 60);
var until = self.ceilDate(self.viewTimeSpan.to, 1, 60);
var lowerTS = from.getTime();
var upperTS = until.getTime();
for (var timestamp = lowerTS; timestamp < upperTS; ) {
if(self.loadedHours.hasOwnProperty(timestamp)) {
timestamp += 3600000;
}
else {
var chuckUpperLimit = Math.min(upperTS, timestamp + 24*3600000);
for (var chunkTimestamp = timestamp; chunkTimestamp < chuckUpperLimit; chunkTimestamp += 3600000) {
if(self.loadedHours.hasOwnProperty(chunkTimestamp))
break;
self.loadedHours[chunkTimestamp] = null;
}
var hourLower = new Date(timestamp);
var hourUpper = new Date(chunkTimestamp);
if(hourUpper > hourLower) {
self.getTasks(hourLower, hourUpper);
self.getResourceClaims(hourLower, hourUpper);
}
timestamp = chunkTimestamp;
}
}
};
self.clearTasksAndClaimsOutsideViewSpan = function() {
var from = self.floorDate(self.viewTimeSpan.from, 1, 60);
var until = self.ceilDate(self.viewTimeSpan.to, 1, 60);
var numTasks = self.tasks.length;
var visibleTasks = [];
for(var i = 0; i < numTasks; i++) {
var task = self.tasks[i];
if(task.endtime >= from && task.starttime <= until)
visibleTasks.push(task);
}
self.tasks = visibleTasks;
self.taskDict = self.toIdBasedDict(visibleTasks);
self.taskChangeCntr++;
self.computeMinMaxTaskTimes();
var numClaims = self.resourceClaims.length;
var visibleClaims = [];
for(var i = 0; i < numClaims; i++) {
var claim = self.resourceClaims[i];
if(claim.endtime >= from && claim.starttime <= until)
visibleClaims.push(claim);
}
self.resourceClaims = visibleClaims;
self.resourceClaimDict = self.toIdBasedDict(self.resourceClaims);
self.computeMinMaxResourceClaimTimes();
var newLoadedHours = {};
var fromTS = from.getTime();
var untilTS = until.getTime();
for(var hourTS in self.loadedHours) {
if(hourTS >= fromTS && hourTS <= untilTS)
newLoadedHours[hourTS] = null;
}
self.loadedHours = newLoadedHours;
};
self.getTasks = function(from, until) {
var defer = $q.defer();
var url = '/rest/tasks';
if(from) {
url += '/' + self.convertLocalUTCDateToISOString(from);
if(until) {
url += '/' + self.convertLocalUTCDateToISOString(until);
}
}
$http.get(url).success(function(result) {
//convert datetime strings to Date objects
for(var i in result.tasks) {
var task = result.tasks[i];
task.starttime = self.convertDatestringToLocalUTCDate(task.starttime);
task.endtime = self.convertDatestringToLocalUTCDate(task.endtime);
}
var initialTaskLoad = self.tasks.length == 0;
var newTaskDict = self.toIdBasedDict(result.tasks);
var newTaskIds = Object.keys(newTaskDict);
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;
}
}
self.taskChangeCntr++;
self.computeMinMaxTaskTimes();
if(initialTaskLoad && self.tasks.length > 0) {
setTimeout(function() {
self.selected_task_ids.splice(0,self.selected_task_ids.length);
//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_ids.push(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_ids.push(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_ids.push(prevTasks[prevTasks.length-1].id);
} else {
self.selected_task_ids.push(self.tasks[0].id);
}
}
}
}, 1000);
}
defer.resolve();
});
return defer.promise;
};
self.putTask = function(task) {
task.starttime = self.convertLocalUTCDateToISOString(task.starttime);
task.endtime = self.convertLocalUTCDateToISOString(task.endtime);
$http.put('/rest/tasks/' + task.id, task).error(function(result) {
console.log("Error. Could not update task. " + result);
//TODO: revert to old state
})
};
var _getTaskBy = function(id_name, id) {
var defer = $q.defer();
if(typeof(id) === 'string') {
id = parseInt(id);
}
var foundTask = id_name == 'id' ? self.taskDict[id] : self.tasks.find(function(t) { return t[id_name] == id; });
if(foundTask) {
defer.resolve(foundTask);
} else {
var url;
switch(id_name) {
case 'id': url = '/rest/tasks/' + id; break;
case 'otdb_id': url = '/rest/tasks/otdb/' + id; break;
case 'mom_id': url = '/rest/tasks/mom/' + id; break;
}
if(url) {
$http.get(url).success(function(result) {
var task = result.task;
if(task) {
task.starttime = self.convertDatestringToLocalUTCDate(task.starttime);
task.endtime = self.convertDatestringToLocalUTCDate(task.endtime);
if(!self.taskDict.hasOwnProperty(task.id)) {
self.tasks.push(task);
self.taskDict[task.id] = task;
self.taskChangeCntr++;
}
}
defer.resolve(task);
}).error(function(result) {
defer.resolve(undefined);
})
} else {
defer.resolve(undefined);
}
}
return defer.promise;
};
self.getTask= function(id) {
return _getTaskBy('id', id);
};
self.getTaskByOTDBId = function(otdb_id) {
return _getTaskBy('otdb_id', otdb_id);
};
self.getTaskByMoMId = function(mom_id) {
return _getTaskBy('mom_id', mom_id);
};
self.getTasksByMoMGroupId = function(mom_object_group_id) {
var defer = $q.defer();
var url = '/rest/tasks/mom/group/' + mom_object_group_id;
$http.get(url).success(function(result) {
//convert datetime strings to Date objects
for(var i in result.tasks) {
var task = result.tasks[i];
task.starttime = self.convertDatestringToLocalUTCDate(task.starttime);
task.endtime = self.convertDatestringToLocalUTCDate(task.endtime);
}
var newTaskDict = self.toIdBasedDict(result.tasks);
var newTaskIds = Object.keys(newTaskDict);
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;
}
}
self.taskChangeCntr++;
self.computeMinMaxTaskTimes();
defer.resolve(result.tasks);
}).error(function(result) {
defer.resolve(undefined);
});
return defer.promise;
};
self.copyTask = function(task) {
$http.put('/rest/tasks/' + task.id + '/copy').error(function(result) {
console.log("Error. Could not copy task. " + result);
alert("Error: Could not copy task with mom id " + task.mom_id);
})
};
self.getTaskDiskUsageByOTDBId = function(otdb_id) {
var defer = $q.defer();
$http.get('/rest/tasks/otdb/' + otdb_id + '/diskusage').success(function(result) {
defer.resolve(result);
}).error(function(result) {
defer.resolve({found:false});
});
return defer.promise;
};
self.getTaskDiskUsage = function(task) {
var defer = $q.defer();
$http.get('/rest/tasks/otdb/' + task.otdb_id + '/diskusage').success(function(result) {
result.task = task;
defer.resolve(result);
}).error(function(result) {
defer.resolve({found:false});
});
return defer.promise;
};
self.computeMinMaxTaskTimes = function() {
var starttimes = self.filteredTasks.map(function(t) { return t.starttime;});
var endtimes = self.filteredTasks.map(function(t) { return t.endtime;});
var minStarttime = new Date(Math.min.apply(null, starttimes));
var maxEndtime = new Date(Math.max.apply(null, endtimes));
self.taskTimes = {
min: minStarttime,
max: maxEndtime
};
};
self.getResources = function() {
var defer = $q.defer();
$http.get('/rest/resources').success(function(result) {
self.resources = result.resources;
self.resourceDict = self.toIdBasedDict(self.resources);
//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();
});
return defer.promise;
};
self.getResourceUsages = function() {
var defer = $q.defer();
$http.get('/rest/resourceusages').success(function(result) {
//convert datetime strings to Date objects
for(var i in result.resourceusages) {
var resource_usages = result.resourceusages[i].usages;
for(var status in resource_usages) {
var usages = resource_usages[status];
for(var usage of usages) {
usage.timestamp = self.convertDatestringToLocalUTCDate(usage.timestamp);
}
}
self.resourceUsagesDict[result.resourceusages[i].resource_id] = result.resourceusages[i];
}
self.resourceUsagesChangeCntr++;
defer.resolve();
});
return defer.promise;
};
self.getResourceClaims = function(from, until) {
var defer = $q.defer();
var url = '/rest/resourceclaims';
if(from) {
url += '/' + self.convertLocalUTCDateToISOString(from);
if(until) {
url += '/' + self.convertLocalUTCDateToISOString(until);
}
}
$http.get(url).success(function(result) {
//convert datetime strings to Date objects
for(var i in result.resourceclaims) {
var resourceclaim = result.resourceclaims[i];
resourceclaim.starttime = self.convertDatestringToLocalUTCDate(resourceclaim.starttime);
resourceclaim.endtime = self.convertDatestringToLocalUTCDate(resourceclaim.endtime);
}
var newClaimDict = self.toIdBasedDict(result.resourceclaims);
var newClaimIds = Object.keys(newClaimDict);
for(var i = newClaimIds.length-1; i >= 0; i--) {
var claim_id = newClaimIds[i];
var claim = newClaimDict[claim_id];
if(!self.resourceClaimDict.hasOwnProperty(claim_id)) {
self.resourceClaims.push(claim);
self.resourceClaimDict[claim_id] = claim;
}
}
self.computeMinMaxResourceClaimTimes();
defer.resolve();
});
return defer.promise;
};
self.computeMinMaxResourceClaimTimes = function() {
var starttimes = self.resourceClaims.map(function(rc) { return rc.starttime;});
var endtimes = self.resourceClaims.map(function(rc) { return rc.endtime;});
var minStarttime = new Date(Math.min.apply(null, starttimes));
var maxEndtime = new Date(Math.max.apply(null, endtimes));
self.resourceClaimTimes = {
min: minStarttime,
max: maxEndtime
};
};
self.getResourceGroups = function() {
var defer = $q.defer();
$http.get('/rest/resourcegroups').success(function(result) {
self.resourceGroups = result.resourcegroups;
self.resourceGroupsDict = self.toIdBasedDict(self.resourceGroups);
defer.resolve();
});
return defer.promise;
};
self.getResourceGroupMemberships = function() {
var defer = $q.defer();
$http.get('/rest/resourcegroupmemberships').success(function(result) {
self.resourceGroupMemberships = result.resourcegroupmemberships;
defer.resolve();
});
return defer.promise;
};
self.getTaskTypes = function() {
var defer = $q.defer();
$http.get('/rest/tasktypes').success(function(result) {
self.tasktypes = result.tasktypes;
self.tasktypesDict = self.toIdBasedDict(self.tasktypes);
defer.resolve();
});
return defer.promise;
};
self.getTaskStatusTypes = function() {
var defer = $q.defer();
$http.get('/rest/taskstatustypes').success(function(result) {
self.taskstatustypes = result.taskstatustypes;
self.editableTaskStatusIds = [];
for(var taskstatustype of self.taskstatustypes) {
if(taskstatustype.name == 'approved' || taskstatustype.name == 'conflict' || taskstatustype.name == 'prescheduled') {
self.editableTaskStatusIds.push(taskstatustype.id);
}
}
defer.resolve();
});
return defer.promise;
};
self.getMoMProjects = function() {
var defer = $q.defer();
$http.get('/rest/momprojects').success(function(result) {
//convert datetime strings to Date objects
var dict = {};
var list = [];
for(var i in result.momprojects) {
var momproject = result.momprojects[i];
momproject.statustime = new Date(momproject.statustime);
dict[momproject.mom_id] = momproject;
list.push(momproject);
}
list.sort(function(a, b) { return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0)); });
self.momProjects = list;
self.momProjectsDict = dict;
defer.resolve();
});
return defer.promise;
};
self.getMoMObjectDetailsForTask = function(task) {
$http.get('/rest/momobjectdetails/'+task.mom_id).success(function(result) {
if(result.momobjectdetails) {
task.name = result.momobjectdetails.object_name;
task.project_name = result.momobjectdetails.project_name;
task.project_id = result.momobjectdetails.project_mom_id;
}
});
};
self.getProjectDiskUsage = function(project_name) {
var defer = $q.defer();
$http.get('/rest/momprojects/' + project_name + '/diskusage').success(function(result) {
defer.resolve(result);
}).error(function(result) {
defer.resolve({found:false});
});
return defer.promise;
};
self.getProjectsDiskUsage = function() {
var defer = $q.defer();
$http.get('/rest/momprojects/diskusage').success(function(result) {
defer.resolve(result);
}).error(function(result) {
defer.resolve({found:false});
});
return defer.promise;
};
self.getConfig = function() {
var defer = $q.defer();
$http.get('/rest/config').success(function(result) {
self.config = result.config;
defer.resolve();
});
return defer.promise;
};
//start with local client time
//lofarTime will be synced with server,
//because local machine might have incorrect clock
//take utcOffset into account, see explanation above.
self.lofarTime = new Date(Date.now() - self.utcOffset);
self._syncLofarTimeWithServer = function() {
$http.get('/rest/lofarTime', {timeout:1000}).success(function(result) {
self.lofarTime = self.convertDatestringToLocalUTCDate(result.lofarTime);
//check if local to utc offset has changed
self.utcOffset = moment().utcOffset()*60000;
});
setTimeout(self._syncLofarTimeWithServer, 60000);
};
self._syncLofarTimeWithServer();
self.lastUpdateChangeNumber = undefined;
self.initialLoad = function() {
$http.get('/rest/mostRecentChangeNumber').success(function(result) {
if(result.mostRecentChangeNumber >= 0) {
self.lastUpdateChangeNumber = result.mostRecentChangeNumber;
}
var nrOfItemsToLoad = 7;
var nrOfItemsLoaded = 0;
var checkInitialLoadCompleteness = function() {
nrOfItemsLoaded += 1;
if(nrOfItemsLoaded >= nrOfItemsToLoad) {
self.initialLoadComplete = true;
}
};
self.getConfig().then(checkInitialLoadCompleteness);
self.getMoMProjects().then(checkInitialLoadCompleteness);
self.getTaskTypes().then(checkInitialLoadCompleteness);
self.getTaskStatusTypes().then(checkInitialLoadCompleteness);
self.getResourceGroups().then(checkInitialLoadCompleteness);
self.getResources().then(checkInitialLoadCompleteness);
self.getResourceGroupMemberships().then(checkInitialLoadCompleteness);
self.getTasksAndClaimsForViewSpan();
self.getResourceUsages();
self.subscribeToUpdates();
});
};
self.subscribeToUpdates = function() {
var url = '/rest/updates';
if(self.lastUpdateChangeNumber) {
url += '/' + self.lastUpdateChangeNumber;
}
$http.get(url, {timeout:300000}).success(function(result) {
try {
var changeNumbers = result.changes.map(function(item) { return item.changeNumber; });
self.lastUpdateChangeNumber = changeNumbers.reduce(function(a, b, idx, arr) { return a > b ? a : b; }, undefined);
var anyResourceClaims = false;
for(var i in result.changes) {
try {
var change = result.changes[i];
if(change.objectType == 'task') {
var changedTask = change.value;
if(change.changeType == 'update') {
var task = self.taskDict[changedTask.id];
if(task) {
self.applyChanges(task, changedTask);
}
} else if(change.changeType == 'insert') {
var task = self.taskDict[changedTask.id];
if(!task) {
changedTask.starttime = self.convertDatestringToLocalUTCDate(changedTask.starttime);
changedTask.endtime = self.convertDatestringToLocalUTCDate(changedTask.endtime);
self.tasks.push(changedTask);
self.taskDict[changedTask.id] = changedTask;
}
} else if(change.changeType == 'delete') {
delete self.taskDict[changedTask.id]
for(var k = self.tasks.length-1; k >= 0; k--) {
if(self.tasks[k].id == changedTask.id) {
self.tasks.splice(k, 1);
break;
}
}
}
self.taskChangeCntr++;
self.computeMinMaxTaskTimes();
} else if(change.objectType == 'resourceClaim') {
anyResourceClaims = true;
var changedClaim = change.value;
if(change.changeType == 'update') {
var claim = self.resourceClaimDict[changedClaim.id];
if(claim) {
self.applyChanges(claim, changedClaim);
}
} else if(change.changeType == 'insert') {
var claim = self.resourceClaimDict[changedClaim.id];
if(!claim) {
changedClaim.starttime = self.convertDatestringToLocalUTCDate(changedClaim.starttime);
changedClaim.endtime = self.convertDatestringToLocalUTCDate(changedClaim.endtime);
self.resourceClaims.push(changedClaim);
self.resourceClaimDict[changedClaim.id] = changedClaim;
}
} else if(change.changeType == 'delete') {
delete self.resourceClaimDict[changedClaim.id]
for(var k = self.resourceClaims.length-1; k >= 0; k--) {
if(self.resourceClaims[k].id == changedClaim.id) {
self.resourceClaims.splice(k, 1);
break;
}
}
}
self.claimChangeCntr++;
self.computeMinMaxResourceClaimTimes();
} else if(change.objectType == 'resourceCapacity') {
if(change.changeType == 'update') {
var changedCapacity = change.value;
var resource = self.resourceDict[changedCapacity.resource_id];
if(resource) {
resource.available_capacity = changedCapacity.available;
resource.total_capacity = changedCapacity.total;
}
}
} else if(change.objectType == 'resourceAvailability') {
if(change.changeType == 'update') {
var changedAvailability = change.value;
var resource = self.resourceDict[changedAvailability.resource_id];
if(resource) {
resource.active = changedAvailability.total;
}
}
}
} catch(err) {
console.log(err)
}
}
if(anyResourceClaims) {
self.getResourceUsages();
}
} catch(err) {
console.log(err)
}
//and update again
self.subscribeToUpdates();
}).error(function() {
setTimeout(self.subscribeToUpdates, 1000);
});
};
return self;
}]);
var dataControllerMod = angular.module('DataControllerMod', ['ngResource']);
dataControllerMod.controller('DataController',
['$scope', '$q', 'dataService',
function($scope, $q, dataService) {
var self = this;
$scope.dataService = dataService;
dataService.dataCtrl = this;
$scope.dateOptions = {
formatYear: 'yyyy',
startingDay: 1
};
$scope.viewFromDatePopupOpened = false;
$scope.viewToDatePopupOpened = false;
$scope.openViewFromDatePopup = function() { $scope.viewFromDatePopupOpened = true; };
$scope.openViewToDatePopup = function() { $scope.viewToDatePopupOpened = true; };
$scope.zoomTimespans = [{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'}, {value:1, name:'Custom (1 min)'}];
$scope.zoomTimespan = $scope.zoomTimespans[5];
$scope.jumpToNow = function() {
var floorLofarTime = dataService.floorDate(dataService.lofarTime, 1, 5);
dataService.viewTimeSpan = {
from: dataService.floorDate(new Date(floorLofarTime.getTime() - 0.25*$scope.zoomTimespan.value*60*1000), 1, 5),
to: dataService.floorDate(new Date(floorLofarTime.getTime() + 0.75*$scope.zoomTimespan.value*60*1000), 1, 5)
};
};
$scope.jumpToNow();
$scope.loadTasksSelectAndJumpIntoView = function(task_ids) {
var list_of_promises = task_ids.map(function(t_id) { return $scope.dataService.getTask(t_id); });
var defer = $q.defer();
$q.all(list_of_promises).then(function(in_tasks) {
var loaded_tasks = in_tasks.filter(function(t) { return t != undefined; });
var loaded_tasks_ids = loaded_tasks.map(function(t) { return t.id; });
$scope.dataService.setSelectedTaskIds(loaded_tasks_ids);
$scope.jumpToSelectedTasks();
defer.resolve(loaded_tasks);
});
return defer.promise;
};
$scope.loadTaskByOTDBIdSelectAndJumpIntoView = function(otdb_id) {
var defer = $q.defer();
$scope.dataService.getTaskByOTDBId(otdb_id).then(function(task) {
if(task) {
$scope.dataService.setSelectedTaskId(task.id);
$scope.jumpToSelectedTasks();
defer.resolve(task);
} else {
defer.resolve(undefined);
}
});
return defer.promise;
};
$scope.loadTaskByMoMIdSelectAndJumpIntoView = function(mom_id) {
var defer = $q.defer();
$scope.dataService.getTaskByMoMId(mom_id).then(function(task) {
if(task) {
$scope.dataService.setSelectedTaskId(task.id);
$scope.jumpToSelectedTasks();
defer.resolve(task);
} else {
defer.resolve(undefined);
}
});
return defer.promise;
};
$scope.loadTasksByMoMGroupIdSelectAndJumpIntoView = function(mom_group_id) {
var defer = $q.defer();
$scope.dataService.getTasksByMoMGroupId(mom_group_id).then(function(tasks) {
if(tasks) {
var task_ids = tasks.map(function(t) { return t.id; });
$scope.dataService.setSelectedTaskIds(task_ids);
$scope.jumpToSelectedTasks();
defer.resolve(tasks);
} else {
defer.resolve(undefined);
}
});
return defer.promise;
};
$scope.selectCurrentTask = function() {
var currentTasks = dataService.tasks.filter(function(t) { return t.starttime <= dataService.viewTimeSpan.to && t.endime >= dataService.viewTimeSpan.from; });
if(currentTasks.lenght > 0) {
dataService.setSelectedTaskId(currentTasks[0].id);
}
};
$scope.jumpToSelectedTasks = function() {
if(dataService.selected_task_ids == undefined)
return;
var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; });
if(tasks.lenght == 0)
return;
var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; })));
var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; })));
var selectedTasksDurationInmsec = maxEndtime.getTime() - minStarttime.getTime();
var selectedTasksDurationInMinutes = selectedTasksDurationInmsec/60000;
var viewSpanInMinutes = selectedTasksDurationInMinutes;
var fittingSpans = $scope.zoomTimespans.filter(function(w) { return w.value >= selectedTasksDurationInMinutes; });
if(fittingSpans.length > 0) {
$scope.zoomTimespan = fittingSpans[0];
//select one span larger if possible
if(fittingSpans.length > 1)
$scope.zoomTimespan = fittingSpans[1];
viewSpanInMinutes = $scope.zoomTimespan.value;
}
var focusTime = new Date(minStarttime.getTime() + 0.5*selectedTasksDurationInmsec);
dataService.viewTimeSpan = {
from: dataService.floorDate(new Date(focusTime.getTime() - 0.25*viewSpanInMinutes*60*1000), 1, 5),
to: dataService.floorDate(new Date(focusTime.getTime() + 0.75*viewSpanInMinutes*60*1000), 1, 5)
};
dataService.autoFollowNow = false;
};
$scope.scrollBack = function() {
dataService.autoFollowNow = false;
var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime();
dataService.viewTimeSpan = {
from: dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() - 0.25*viewTimeSpanInmsec), 1, 5),
to: dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() - 0.25*viewTimeSpanInmsec), 1, 5)
};
};
$scope.scrollForward = function() {
dataService.autoFollowNow = false;
var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime();
dataService.viewTimeSpan = {
from: dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() + 0.25*viewTimeSpanInmsec), 1, 5),
to: dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() + 0.25*viewTimeSpanInmsec), 1, 5)
};
};
$scope.onZoomTimespanChanged = function() {
var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime();
var focusTime = new Date(dataService.viewTimeSpan.from + 0.5*viewTimeSpanInmsec);
if(dataService.autoFollowNow) {
focusTime = dataService.floorDate(dataService.lofarTime, 1, 5);
} else {
var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; });
if(tasks.lenght > 0) {
var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; })));
var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; })));
focusTime = dataService.floorDate(new Date(0.5*(minStarttime.getTime() + maxEndtime.getTime())), 1, 5);
}
}
dataService.viewTimeSpan = {
from: dataService.floorDate(new Date(focusTime.getTime() - 0.25*$scope.zoomTimespan.value*60*1000)),
to: dataService.floorDate(new Date(focusTime.getTime() + 0.75*$scope.zoomTimespan.value*60*1000))
};
};
$scope.selectZoomTimespan = function() {
var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime();
var viewTimeSpanInMinutes = Math.round(viewTimeSpanInmsec/60000);
var foundZoomTimespan = $scope.zoomTimespans.find(function(zts) { return zts.value == viewTimeSpanInMinutes; });
if(foundZoomTimespan) {
$scope.zoomTimespan = foundZoomTimespan;
} else {
var customZoomTimespan = $scope.zoomTimespans.find(function(zts) { return zts.name.startsWith('Custom'); });
customZoomTimespan.value = viewTimeSpanInMinutes;
customZoomTimespan.name = 'Custom (' + viewTimeSpanInMinutes + ' min)';
$scope.zoomTimespan = customZoomTimespan;
}
};
$scope.onViewTimeSpanFromChanged = function() {
if (!isNaN(dataService.viewTimeSpan.from)) {
dataService.autoFollowNow = false;
if(dataService.viewTimeSpan.from >= dataService.viewTimeSpan.to) {
dataService.viewTimeSpan.to = dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() + $scope.zoomTimespan.value*60*1000), 1, 5);
}
}
};
$scope.onViewTimeSpanToChanged = function() {
if (!isNaN(dataService.viewTimeSpan.to)) {
dataService.autoFollowNow = false;
if(dataService.viewTimeSpan.to <= dataService.viewTimeSpan.from) {
dataService.viewTimeSpan.from = dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() - $scope.zoomTimespan.value*60*1000), 1, 5);
}
}
};
$scope.$watch('dataService.viewTimeSpan', function() {
$scope.selectZoomTimespan();
$scope.$evalAsync(function() { dataService.clearTasksAndClaimsOutsideViewSpan(); });
$scope.$evalAsync(function() { dataService.getTasksAndClaimsForViewSpan(); });
}, true);
$scope.$watch('dataService.filteredTaskChangeCntr', dataService.computeMinMaxTaskTimes);
$scope.$watch('dataService.lofarTime', function() {
if(dataService.autoFollowNow && (Math.round(dataService.lofarTime.getTime()/1000))%5==0) {
$scope.jumpToNow();
}
});
$scope.$watch('dataService.autoFollowNow', function() {
if(dataService.autoFollowNow) {
$scope.jumpToNow();
}
});
dataService.initialLoad();
//clock ticking every second
//updating current lofarTime by the elapsed time since previous tick
//lofarTime is synced every minute with server utc time.
self._prevTick = Date.now();
self._doTimeTick = function() {
var tick = Date.now();
var elapsed = tick - self._prevTick;
self._prevTick = tick;
//evalAsync, so lofarTime will be seen by watches
$scope.$evalAsync(function() {
dataService.lofarTime = new Date(dataService.lofarTime.getTime() + elapsed);
});
setTimeout(self._doTimeTick, 1000);
};
self._doTimeTick();
}
]);
//extend the default dateFilter so that it always displays dates as 'yyyy-MM-dd HH:mm:ss'
//without any extra timezone string.
//see also comments above why we do tricks with local and utc dates
angular.module('raeApp').config(['$provide', function($provide) {
$provide.decorator('dateFilter', ['$delegate', function($delegate) {
var srcFilter = $delegate;
function zeroPaddedString(num) {
var numstr = num.toString();
if(numstr.length < 2) {
return '0' + numstr;
}
return numstr;
};
var extendsFilter = function() {
if(arguments[0] instanceof Date && arguments.length == 1) {
var date = arguments[0];
var dateString = date.getFullYear() + '-' + zeroPaddedString(date.getMonth()+1) + '-' + zeroPaddedString(date.getDate()) + ' ' +
zeroPaddedString(date.getHours()) + ':' + zeroPaddedString(date.getMinutes()) + ':' + zeroPaddedString(date.getSeconds());
return dateString;
}
return srcFilter.apply(this, arguments);
}
return extendsFilter;
}])
}])