diff --git a/taiga_tasks/remote/js/taiga_tasks_api.js b/taiga_tasks/remote/js/taiga_tasks_api.js index b35e7ae..4112be4 100644 --- a/taiga_tasks/remote/js/taiga_tasks_api.js +++ b/taiga_tasks/remote/js/taiga_tasks_api.js @@ -18,23 +18,24 @@ * ---------------------------------------------------------------------------------- * Get taiga AUTH_TOKEN used in all subsequent taiga API calls */ -function getAuthToken(website, project, adminUsername, adminPassword, users) { +function getAuthToken(taigaParams) { $.ajax({ method: "POST", - url: website + '/api/v1/auth', + url: taigaParams.website + '/api/v1/auth', data: { "type": "normal", - "username": adminUsername, - "password": adminPassword + "username": taigaParams.adminUsername, + "password": taigaParams.adminPassword }, success: function(json) { - showSuccessAlert("Taiga authentication succeeded for user " + adminUsername + "."); - getProjectID(website, project, json.auth_token, users); + showSuccessAlert("Taiga authentication succeeded for user " + taigaParams.adminUsername + "."); + taigaParams.authToken = json.auth_token; + getProjectID(taigaParams); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); - showErrorAlert("Taiga authentication failed for user " + adminUsername + " because of incorrect username or password."); + showErrorAlert("Taiga authentication failed for user " + taigaParams.adminUsername + " because of incorrect username or password."); } }); }; @@ -43,16 +44,17 @@ function getAuthToken(website, project, adminUsername, adminPassword, users) { * ---------------------------------------------------------------------------------- * Get taiga project ID */ -function getProjectID(website, project, authToken, users) { +function getProjectID(taigaParams) { $.ajax({ method: "GET", - url: website + '/api/v1/resolver?project=' + project, + url: taigaParams.website + '/api/v1/resolver?project=' + taigaParams.project, beforeSend: function(xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + authToken); + xhr.setRequestHeader('Authorization', 'Bearer ' + taigaParams.authToken); }, success: function(json) { showSuccessAlert("ProjectID retrieval succeeded."); - getProjectName(website, authToken, json.project, users); + taigaParams.projectID = json.project; + getProjectName(taigaParams); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); @@ -65,16 +67,17 @@ function getProjectID(website, project, authToken, users) { * ---------------------------------------------------------------------------------- * Get taiga project name */ -function getProjectName(website, authToken, projectID, users) { +function getProjectName(taigaParams) { $.ajax({ method: "GET", - url: website + '/api/v1/projects/' + projectID, + url: taigaParams.website + '/api/v1/projects/' + taigaParams.projectID, beforeSend: function(xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + authToken); + xhr.setRequestHeader('Authorization', 'Bearer ' + taigaParams.authToken); }, success: function(json) { showSuccessAlert("Project name retrieval succeeded."); - processUsers(website, authToken, projectID, json.name, users); + taigaParams.projectName = json.name; + processUsers(taigaParams); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); @@ -87,11 +90,19 @@ function getProjectName(website, authToken, projectID, users) { * ---------------------------------------------------------------------------------- * Get taiga user(s) */ -function processUsers(website, authToken, projectID, projectName, users) { +function processUsers(taigaParams) { - users.forEach(function(element, i) { - $('#container').append('


'); - getUserID(website, authToken, projectID, projectName, element, i); + (taigaParams.users).forEach(function(userName, index) { + + // clear all system alerts, as subsequent alerts will be user-specific + // + clearAlerts(); + + getUserID(taigaParams, { + userName: userName, // user name + index: index + 1, // index of user used for displaying into HTML divs + userID: 0 // user ID + }); }); } @@ -100,24 +111,26 @@ function processUsers(website, authToken, projectID, projectName, users) { * ---------------------------------------------------------------------------------- * Get username id */ -function getUserID(website, authToken, projectID, projectName, username, divIndex) { +function getUserID(taigaParams, userParams) { $.ajax({ method: "GET", - url: website + '/api/v1/users?project=' + projectID, + url: taigaParams.website + '/api/v1/users?project=' + taigaParams.projectID, beforeSend: function(xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + authToken); + xhr.setRequestHeader('Authorization', 'Bearer ' + taigaParams.authToken); }, success: function(json) { - showSuccessAlert("User ID retrieval for " + username + " succeeded."); + showSuccessAlert("User ID retrieval for " + userParams.userName + " succeeded.", userParams.index); var result = $.grep(json, function(element, index) { - return element.username == username; + return element.username == userParams.userName; }); - getUserStories(website, authToken, projectID, projectName, result[0].id, divIndex, getResults); + + userParams.userID = result[0].id; + getUserStories(taigaParams, userParams, getResults); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); - showErrorAlert("Unable to retrieve user ID for " + username + "."); + showErrorAlert("Unable to retrieve user ID for " + userParams.userName + "."); } }); }; @@ -126,52 +139,69 @@ function getUserID(website, authToken, projectID, projectName, username, divInde * ---------------------------------------------------------------------------------- * Get user stories */ -function getUserStories(website, authToken, projectID, projectName, userID, divIndex, callback) { +function getUserStories(taigaParams, userParams, callback) { + + // include/exclude incomplete (in progress) tasks in userstories query + // + var closedTasks = ""; + if (!taigaParams.showIncompleteTasks) { + closedTasks = '\&is_closed=true'; + }; + $.ajax({ method: "GET", - url: website + '/api/v1/userstories?project=' + projectID + '\&assigned_to=' + userID, + url: taigaParams.website + '/api/v1/userstories?project=' + taigaParams.projectID + '\&assigned_to=' + userParams.userID + closedTasks, beforeSend: function(xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + authToken); + xhr.setRequestHeader('Authorization', 'Bearer ' + taigaParams.authToken); }, success: function(json) { var newItem = []; var callbackCount = json.length; - showSuccessAlert("User stories retrieval succeeded."); + showSuccessAlert("User stories retrieval succeeded.", userParams.index); // build newItem array from JSON returns // var json = $.map(json, function(item) { - // get custom fields (actual_points) and include in final JSON return - // if no attributes_values, then set to null (user didn't set a value) + // check if story date is within requested date range // - getCustomFields(website, authToken, projectID, item.id).done(function(data) { - - var actualPoints = data.attributes_values[1]; - - if (actualPoints === undefined) { - actualPoints = null; + if ((item.modified_date < taigaParams.startDate) || (item.modified_date > taigaParams.endDate)) { + if (!--callbackCount) { + showInfoAlert("No user stories found matching date criteria for user " + userParams.userName + ".", userParams.index); } + } else { - newItem.push({ - estimated_points: item.total_points, - subject: item.subject, - story_id: item.id, - full_name: item.assigned_to_extra_info.full_name_display, - finish_date: item.finish_date, - actual_points: actualPoints, - project_name: projectName - }); - - // manage callback counts, given we don't know when all callbacks - // have completed... when all callbacks return, continue + // get custom fields (actual_points) and include in final JSON return + // if no attributes_values, then set to null (user didn't set a value) // - if (!--callbackCount) { - callback(newItem, divIndex); - }; + getCustomFields(taigaParams, userParams, item.id).done(function(data) { + + var actualPoints = data.attributes_values[1]; + + if (actualPoints === undefined) { + actualPoints = null; + } + + newItem.push({ + estimated_points: item.total_points, + subject: item.subject, + story_id: item.id, + full_name: item.assigned_to_extra_info.full_name_display, + finish_date: item.finish_date, + actual_points: actualPoints, + project_name: taigaParams.projectName + }); + + // manage callback counts, given we don't know when all callbacks + // have completed... when all callbacks return, continue + // + if (!--callbackCount) { + callback(taigaParams, userParams, newItem); + }; - }); + }); + }; }); }, error: function(jqXHR, textStatus, errorThrown) { @@ -185,15 +215,15 @@ function getUserStories(website, authToken, projectID, projectName, userID, divI * ---------------------------------------------------------------------------------- * Get user story custom fields (e.g,. actual_points field) */ -function getCustomFields(website, authToken, projectID, storyID) { +function getCustomFields(taigaParams, userParams, storyID) { return $.ajax({ method: "GET", - url: website + '/api/v1/userstories/custom-attributes-values/' + storyID + '?project=' + projectID, + url: taigaParams.website + '/api/v1/userstories/custom-attributes-values/' + storyID + '?project=' + taigaParams.projectID, beforeSend: function(xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + authToken); + xhr.setRequestHeader('Authorization', 'Bearer ' + taigaParams.authToken); }, success: function() { - showSuccessAlert("Custom fields retrieval succeeded."); + showSuccessAlert("Custom fields retrieval succeeded.", userParams.index); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); @@ -311,7 +341,7 @@ function getDateTime() { * ---------------------------------------------------------------------------------- * Get and process the resultng JSON file for highcharts (hc) */ -function getResults(json, divIndex) { +function getResults(taigaParams, userParams, json) { var results = processResults(json); hcCategories = createResultsCategories(results); @@ -322,23 +352,24 @@ function getResults(json, divIndex) { // $('#debug').append('
=========Categories==========
' + JSON.stringify(hcCategories, null, 4)); // $('#debug').append('
=========Series==============
' + JSON.stringify(hcSeries)); - plotChart(results, hcCategories, hcSeries, hcDate, divIndex); + plotChart(taigaParams, userParams, hcCategories, hcSeries, hcDate, results); }; /** * ---------------------------------------------------------------------------------- * Plot data to highcharts object */ -function plotChart(data, categories, series, date, count) { +function plotChart(taigaParams, userParams, categories, series, date, data) { - $('#chart' + [count]).highcharts({ + $('#container').append('


'); + $('#chart' + [userParams.index]).highcharts({ title: { - text: data[0]['full_name'] + ' : Task Activity Report' + text: data[0]['project_name'] + ' Project' + '
' + 'Task Activity Report for ' + data[0]['full_name'] }, subtitle: { - text: data[0]['project_name'] + ' Project
' + 'Generated on ' + date + text: 'Activity Date Range: ' + (new Date(taigaParams.startDate)).toJSON().slice(0, 10) + ' to ' + (new Date(taigaParams.endDate)).toJSON().slice(0, 10) + '
' + 'Report Generated on ' + date }, xAxis: { @@ -364,8 +395,7 @@ function plotChart(data, categories, series, date, count) { series: series }); - clearAlerts(count); - + clearAlerts(userParams.index); }; /** @@ -373,21 +403,29 @@ function plotChart(data, categories, series, date, count) { * Display alerts to browser window */ -function showSuccessAlert(msg) { - ShowAlert(msg, 0); +function showSuccessAlert(msg, index) { + ShowAlert(msg, index, 0); }; -function showErrorAlert(msg) { - ShowAlert(msg, 1); +function showErrorAlert(msg, index) { + ShowAlert(msg, index, 1); }; -function ShowAlert(msg, type) { +function showInfoAlert(msg, index) { + ShowAlert(msg, index, 2); +}; - msgType = { +function ShowAlert(msg, index, type) { + + var msgType = { class: "", text: "" }; + if (typeof index == "undefined") { + index = 0; + } + switch (type) { case 0: msgType.class = "alert-success"; @@ -397,23 +435,34 @@ function ShowAlert(msg, type) { msgType.class = "alert-danger"; msgType.text = "Error"; break; + case 2: + msgType.class = "alert-warning"; + msgType.text = "Warning"; + break; default: msgType.class = "alert-info"; - msgType.text = "Info"; + msgType.text = "Information"; }; - $('#alerts').html("
" + msgType.text + ": " + msg + "
"); + var element = "
" + msgType.text + ": " + msg + "
"; + + if ($("#alert" + index).length) { + $("#alert" + index).replaceWith(element); + } else { + $('#alerts').append(element); + } }; /** * ---------------------------------------------------------------------------------- - * Remove alerts after final chart is rendered + * Remove alerts after final chart(s) is rendered */ -function clearAlerts(count) { +function clearAlerts(index) { - if ($("div[id^='chart']").length - 1 == count) { - $('#alert').remove(); - }; + if (typeof index == "undefined") { + index = 0; + } + $('#alert' + index).remove(); }; diff --git a/taiga_tasks/remote/js/taiga_tasks_dlg.js b/taiga_tasks/remote/js/taiga_tasks_dlg.js index a36da39..43eb0f6 100644 --- a/taiga_tasks/remote/js/taiga_tasks_dlg.js +++ b/taiga_tasks/remote/js/taiga_tasks_dlg.js @@ -14,8 +14,108 @@ --------------------------------------------------------------------------------------------------- */ -angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']); -angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function($scope, $modal, $log) { +var app = angular.module('taiga.tasks', ['ngAnimate', 'ui.bootstrap']); + +app.service('sharedProperties', function() { + + var objectValue = { + dtStart: '', + dtEnd: '' + }; + + return { + setStartDate: function(startDate) { + objectValue.dtStart = startDate; + }, + setEndDate: function(endDate) { + objectValue.dtEnd = endDate; + }, + getStartDate: function() { + return objectValue.dtStart; + }, + getEndDate: function() { + return objectValue.dtEnd; + }, + } +}); + +app.controller('DatepickerDemoCtrl', function($scope, sharedProperties) { + + $scope.today = function() { + var x = new Date(); + x.setDate(1); + x.setHours(0, 0, 0, 0); + x.setMonth(x.getMonth()); + $scope.dtStart = x; + + x = new Date(); + x.setHours(23, 59, 59, 999); + $scope.dtEnd = x; + }; + + $scope.today(); + + $scope.clear = function() { + $scope.dtStart = null; + $scope.dtEnd = null; + }; + + $scope.toggleMin = function() { + $scope.minDate = $scope.minDate ? null : new Date(); + }; + + $scope.toggleMin(); + + $scope.maxDate = new Date(2020, 5, 22); + + $scope.open = function($event, opened) { + $event.preventDefault(); + $event.stopPropagation(); + $scope.status[opened] = true; + }; + + $scope.$watch('dtStart', function() { + sharedProperties.setStartDate($scope.dtStart); + }); + + $scope.$watch('dtEnd', function() { + sharedProperties.setEndDate($scope.dtEnd); + }); + + $scope.dateOptions = { + formatYear: 'yy', + startingDay: 1 + }; + + $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate']; + $scope.format = $scope.formats[0]; + + $scope.status = { + opened: false + }; + +}); + +app.controller('ModalDemoCtrl', function($scope, $modal, $log, sharedProperties) { + + function toTaigaFormat(d) { + function pad(n) { + return n < 10 ? '0' + n : n + } + return d.getUTCFullYear() + '-' + + pad(d.getUTCMonth() + 1) + '-' + + pad(d.getUTCDate()) + 'T' + + pad(d.getUTCHours()) + ':' + + pad(d.getUTCMinutes()) + ':' + + pad(d.getUTCSeconds()) + '+' + "0000" + // pad(d.getUTCMilliseconds()) + }; + + $scope.dlgModel = { + username: "", + password: "", + radio: 1 + }; $scope.checkModel = { duane: false, @@ -24,15 +124,10 @@ angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function($scope, richbl: false }; - $scope.dlgModel = { - username: "", - password: "" - }; - var modalInstance = $modal.open({ backdrop: 'static', keyboard: false, - templateUrl: 'myModalContent.html', + templateUrl: 'taiga_tasks.html', controller: 'ModalInstanceCtrl', resolve: { checkModel: function() { @@ -41,15 +136,17 @@ angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function($scope, dlgModel: function() { return $scope.dlgModel; } - } }).result.then(function() { - checkResults = []; + var users = []; + + var startDate = toTaigaFormat(sharedProperties.getStartDate()); + var endDate = toTaigaFormat(sharedProperties.getEndDate()); angular.forEach($scope.checkModel, function(value, key) { if (value) { - checkResults.push(key); + users.push(key); } }); @@ -57,19 +154,28 @@ angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function($scope, // this is the interesting call that fires up the Taiga API through a series of Ajax calls into the // Taiga project, retrieves and processes the information, and ultimately displays the resulting charts // - getAuthToken( - 'http://www.website.com', // the taiga website to gather tasks - 'project_slug', // the taiga project slug (not project name) - $scope.dlgModel.username, // project user with admin permissions (to access task details) - $scope.dlgModel.password, // project user password - checkResults // users to gather task summaries - ); + getAuthToken({ + website: 'http://public.businesslearninginc.com', // the taiga website to gather tasks + project: 'admin-sorce-migration', // the taiga project slug (not project name) + authToken: '', // session authentication token (derived) + projectID: '', // project ID (derived) + projectName: '', // project name (derived) + adminUsername: $scope.dlgModel.username, // project user with admin permissions (to access task details) + adminPassword: $scope.dlgModel.password, // project user password + startDate: startDate, // user story start date range + endDate: endDate, // user story end date range + showIncompleteTasks: $scope.dlgModel.radio, // whether to include incomplete tasks in results + users: users, // list of users to gather task summaries + userName: '', // user name (derived) + userCount: 0, // count of users passed in (derived) + userID: '' // user ID of current user (derived) + }); }); }); -angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function($scope, $modalInstance, checkModel, dlgModel) { +app.controller('ModalInstanceCtrl', function($scope, $modalInstance, checkModel, dlgModel) { $scope.checkModel = checkModel; $scope.dlgModel = dlgModel; @@ -81,6 +187,7 @@ angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function($sc $scope.reset = function() { $scope.dlgModel.username = ""; $scope.dlgModel.password = ""; + $scope.dlgModel.radio = true; $scope.checkModel.duane = false; $scope.checkModel.troje = false; diff --git a/taiga_tasks/remote/taiga_tasks.html b/taiga_tasks/remote/taiga_tasks.html index c448fad..e975f3e 100644 --- a/taiga_tasks/remote/taiga_tasks.html +++ b/taiga_tasks/remote/taiga_tasks.html @@ -15,22 +15,31 @@ --> - + + - - - - - + + + + + + - +