From 876370419a50264a28feb4278f8c465b0019e709 Mon Sep 17 00:00:00 2001
From: Alexandru DAMIAN
Date: Thu, 8 Jan 2015 13:15:14 +0000
Subject: bitbake: toaster: implementation of project page
This patch brings the project page in line with the design,
including build error handling and suggestions.
Includes some refactoring for already existing code.
[YOCTO #6587]
(Bitbake rev: 1ea658dcdfde5465d3ecdb97550e0a66cb8b122e)
Signed-off-by: Alexandru DAMIAN
Signed-off-by: Richard Purdie
---
.../lib/toaster/toastergui/static/js/projectapp.js | 234 +++++++++++++++------
.../lib/toaster/toastergui/templates/project.html | 50 +++--
bitbake/lib/toaster/toastergui/views.py | 97 ++++-----
3 files changed, 246 insertions(+), 135 deletions(-)
(limited to 'bitbake')
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
index 767ea13a7e..0bdc55a733 100644
--- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js
+++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
@@ -100,6 +100,16 @@ function _diffArrays(existingArray, newArray, compareElements, onAdded, onDelete
}
+// add Array findIndex if not there
+
+if (Array.prototype.findIndex === undefined) {
+ Array.prototype.findIndex = function (callback) {
+ var i = 0;
+ for ( i = 0; i < this.length; i++ )
+ if (callback(this[i], i, this)) return i;
+ return -1;
+ }
+}
var projectApp = angular.module('project', ['ngCookies', 'ngAnimate', 'ui.bootstrap', 'ngRoute', 'ngSanitize'], angular_formpost);
@@ -126,11 +136,17 @@ projectApp.filter('timediff', function() {
}
});
+/**
+ * main controller for the project page
+ */
-// main controller for the project page
projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $cookieStore, $q, $sce, $anchorScroll, $animate, $sanitize) {
- $scope.getSuggestions = function(type, currentValue) {
+ /**
+ * Retrieves text suggestions for text-edit drop down autocomplete boxes
+ */
+
+ $scope.getAutocompleteSuggestions = function(type, currentValue) {
var deffered = $q.defer();
$http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: type, value: currentValue}})
@@ -147,17 +163,19 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
var inXHRcall = false;
- // default handling of XHR calls that handles errors and updates commonly-used pages
+ /**
+ * XHR call wrapper that automatically handles errors and auto-updates the page content to reflect project state on server side.
+ */
$scope._makeXHRCall = function(callparams) {
if (inXHRcall) {
if (callparams.data === undefined) {
// we simply skip the data refresh calls
- console.warn("race on XHR, aborted");
+ console.warn("TRC1: race on XHR, aborted");
return;
} else {
// we return a promise that we'll solve by reissuing the command later
var delayed = $q.defer();
- console.warn("race on XHR, delayed");
+ console.warn("TRC2: race on XHR, delayed");
$interval(function () {$scope._makeXHRCall(callparams).then(function (d) { delayed.resolve(d); });}, 100, 1);
return delayed.promise;
@@ -178,33 +196,6 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
deffered.reject(_data.error);
}
else {
-
- if (_data.builds !== undefined) {
- var toDelete = [];
- // step 1 - delete entries not found
- $scope.builds.forEach(function (elem) {
- if (-1 == _data.builds.findIndex(function (elemX) { return elemX.id == elem.id && elemX.status == elem.status; })) {
- toDelete.push(elem);
- }
- });
- toDelete.forEach(function (elem) {
- $scope.builds.splice($scope.builds.indexOf(elem),1);
- });
- // step 2 - merge new entries
- _data.builds.forEach(function (elem) {
- var found = false;
- var i = 0;
- for (i = 0 ; i < $scope.builds.length; i ++) {
- if ($scope.builds[i].id > elem.id) continue;
- if ($scope.builds[i].id == elem.id) { found=true; break;}
- if ($scope.builds[i].id < elem.id) break;
- }
- if (!found) {
- $scope.builds.splice(i, 0, elem);
- }
- });
-
- }
if (_data.layers !== undefined) {
var addedLayers = [];
@@ -239,12 +230,59 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
// step 3 - display alerts.
if (addedLayers.length > 0) {
$scope.displayAlert($scope.zone2alerts, "You have added "+addedLayers.length+" layer" + ((addedLayers.length>1)?"s: ":": ") + addedLayers.map(function (e) { return ""+e.name+"" }).join(", "), "alert-info");
+ // invalidate error layer data based on current layers
+ $scope.layersForTargets = {};
}
if (deletedLayers.length > 0) {
$scope.displayAlert($scope.zone2alerts, "You have deleted "+deletedLayers.length+" layer" + ((deletedLayers.length>1)?"s: ":": ") + deletedLayers.map(function (e) { return ""+e.name+"" }).join(", "), "alert-info");
+ // invalidate error layer data based on current layers
+ $scope.layersForTargets = {};
}
}
+
+
+ if (_data.builds !== undefined) {
+ var toDelete = [];
+ // step 1 - delete entries not found
+ $scope.builds.forEach(function (elem) {
+ if (-1 == _data.builds.findIndex(function (elemX) { return elemX.id == elem.id && elemX.status == elem.status; })) {
+ toDelete.push(elem);
+ }
+ });
+ toDelete.forEach(function (elem) {
+ $scope.builds.splice($scope.builds.indexOf(elem),1);
+ });
+ // step 2 - merge new entries
+ _data.builds.forEach(function (elem) {
+ var found = false;
+ var i = 0;
+ for (i = 0 ; i < $scope.builds.length; i ++) {
+ if ($scope.builds[i].id > elem.id) continue;
+ if ($scope.builds[i].id == elem.id) { found=true; break;}
+ if ($scope.builds[i].id < elem.id) break;
+ }
+ if (!found) {
+ $scope.builds.splice(i, 0, elem);
+ }
+ });
+ // step 3 - merge "Canceled" builds
+ $scope.canceledBuilds.forEach(function (elem) {
+ // mock the build object
+ var found = false;
+ var i = 0;
+ for (i = 0; i < $scope.builds.length; i ++) {
+ if ($scope.builds[i].id > elem.id) continue;
+ if ($scope.builds[i].id == elem.id) { found=true; break;}
+ if ($scope.builds[i].id < elem.id) break;
+ }
+ if (!found) {
+ $scope.builds.splice(i, 0, elem);
+ }
+ });
+
+ $scope.fetchLayersForTargets();
+ }
if (_data.targets !== undefined) {
$scope.targets = _data.targets;
}
@@ -267,17 +305,26 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
deffered.resolve(_data);
}
}).error(function(_data, _status, _headers, _config) {
- console.warn("Failed HTTP XHR request (" + _status + ")" + _data);
+ if (_status == 0) {
+ // the server has gone away
+ alert("The server is not responding. The application will terminate now")
+ $interval.cancel($scope.pollHandle);
+ }
+ else {
console.error("Failed HTTP XHR request: ", _data, _status, _headers, _config);
inXHRcall = false;
deffered.reject(_data.error);
+ }
});
return deffered.promise;
}
$scope.layeralert = undefined;
- // shows user alerts on invalid project data
+ /**
+ * Verifies and shows user alerts on invalid project data
+ */
+
$scope.validateData = function () {
if ($scope.layers.length == 0) {
$scope.layeralert = $scope.displayAlert($scope.zone1alerts, "You need to add some layers to this project. View all layers available in Toaster or import a layer");
@@ -289,14 +336,14 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
}
}
- $scope.targetExistingBuild = function(targets) {
+ $scope.buildExistingTarget = function(targets) {
var oldTargetName = $scope.targetName;
$scope.targetName = targets.map(function(v,i,a){return v.target}).join(' ');
$scope.targetNamedBuild();
$scope.targetName = oldTargetName;
}
- $scope.targetNamedBuild = function(target) {
+ $scope.buildNamedTarget = function(target) {
if ($scope.targetName === undefined && $scope.targetName1 === undefined){
console.warn("No target defined, please type in a target name");
return;
@@ -310,7 +357,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
targets: $scope.safeTargetName,
}
}).then(function (data) {
- console.warn("received ", data);
+ console.warn("TRC3: received ", data);
$scope.targetName = undefined;
$scope.targetName1 = undefined;
$location.hash('buildslist');
@@ -329,22 +376,20 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
$scope.safeTargetName = $scope.safeTargetName.replace(/\[.*\]/, '').trim();
}
- $scope.buildCancel = function(id) {
+ $scope.buildCancel = function(build) {
$scope._makeXHRCall({
method: "POST", url: $scope.urls.xhr_build,
data: {
- buildCancel: id,
+ buildCancel: build.id,
}
+ }).then( function () {
+ build['status'] = "deleted";
+ $scope.canceledBuilds.push(build);
});
}
- $scope.buildDelete = function(id) {
- $scope._makeXHRCall({
- method: "POST", url: $scope.urls.xhr_build,
- data: {
- buildDelete: id,
- }
- });
+ $scope.buildDelete = function(build) {
+ $scope.canceledBuilds.splice($scope.canceledBuilds.indexOf(build), 1);
}
@@ -352,6 +397,12 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
$scope.layerAddId = item.id;
}
+
+ $scope.layerAddById = function (id) {
+ $scope.layerAddId = id;
+ $scope.layerAdd();
+ }
+
$scope.layerAdd = function() {
$http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "layerdeps", value: $scope.layerAddId }})
@@ -369,7 +420,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
$scope.selectedItems = (function () { s = {}; for (var i = 0; i < items.length; i++) { s[items[i].id] = true; };return s; })();
$scope.ok = function() {
- console.warn("scope selected is ", $scope.selectedItems);
+ console.warn("TRC4: scope selected is ", $scope.selectedItems);
$modalInstance.close(Object.keys($scope.selectedItems).filter(function (e) { return $scope.selectedItems[e];}));
};
@@ -378,7 +429,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
};
$scope.update = function() {
- console.warn("updated ", $scope.selectedItems);
+ console.warn("TRC5: updated ", $scope.selectedItems);
};
},
resolve: {
@@ -393,7 +444,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
modalInstance.result.then(function (selectedArray) {
selectedArray.push($scope.layerAddId);
- console.warn("selected", selectedArray);
+ console.warn("TRC6: selected", selectedArray);
$scope._makeXHRCall({
method: "POST", url: $scope.urls.xhr_edit,
@@ -429,7 +480,16 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
}
- $scope.test = function(elementid) {
+ /**
+ * Verifies if a project settings change would trigger layer updates. If user confirmation is needed,
+ * a modal dialog will prompt the user to ack the changes. If not, the editProjectSettings() function is called directly.
+ *
+ * Only "versionlayers" change for is supported (and hardcoded) for now.
+ */
+
+ $scope.testProjectSettingsChange = function(elementid) {
+ if (elementid != '#change-project-version') throw "Not implemented";
+
$http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "versionlayers", value: $scope.projectVersion }}).
success(function (_data) {
if (_data.error != "ok") {
@@ -463,17 +523,21 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
}
});
- modalInstance.result.then(function () { $scope.edit(elementid)});
+ modalInstance.result.then(function () { $scope.editProjectSettings(elementid)});
} else {
- $scope.edit(elementid);
+ $scope.editProjectSettings(elementid);
}
}
});
}
- $scope.edit = function(elementid) {
+ /**
+ * Performs changes to project settings, and updates the user interface accordingly.
+ */
+
+ $scope.editProjectSettings = function(elementid) {
var data = {};
- console.warn("edit with ", elementid);
+ console.warn("TRC7: editProjectSettings with ", elementid);
var alertText = undefined;
var alertZone = undefined;
var oldLayers = [];
@@ -508,10 +572,14 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
alertText += "" + $scope.project.release.desc + ". ";
}
if (elementid == '#change-project-version') {
+ $scope.layersForTargets = {}; // invalidate error layers for the targets, since layers changed
+
// requirement https://bugzilla.yoctoproject.org/attachment.cgi?id=2229, notification for changed version to include layers
$scope.zone2alerts.forEach(function (e) { e.close() });
alertText += "This has caused the following changes in your project layers:"
+
+ // warnings - this is executed AFTER the generic XHRCall handling is done; at this point,
if (_data.layers !== undefined) {
// show added/deleted layer notifications; scope.layers is already updated by this point.
var addedLayers = [];
@@ -547,6 +615,10 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
}
+ /**
+ * Extracts a command passed through the local path in location, and executes/updates UI based on the command
+ */
+
$scope.updateDisplayWithCommands = function() {
cmd = $location.path();
@@ -630,6 +702,10 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
});
}
+ /**
+ * Utility function to display an alert to the user
+ */
+
$scope.displayAlert = function(zone, text, type) {
if (zone.maxid === undefined) { zone.maxid = 0; }
var crtid = zone.maxid ++;
@@ -644,6 +720,10 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
return o;
}
+ /**
+ * Toggles display items between label and input box (the edit pencil icon) on selected settings in project page
+ */
+
$scope.toggle = function(id) {
$scope.projectName = $scope.project.name;
$scope.projectVersion = $scope.project.release.id;
@@ -657,34 +737,52 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
keys = Object.keys($scope.mostBuiltTargets);
keys = keys.filter(function (e) { if ($scope.mostBuiltTargets[e]) return e });
return keys.length == 0;
+ }
+
+ /**
+ * Helper function to deal with error string recognition and manipulation
+ */
+ $scope.getTargetNameFromErrorMsg = function (msg) {
+ targets = msg.split(" ").splice(2).map(function (v) { return v.replace(/'/g, '')})
+ return targets;
}
- // init code
- //
- $scope.init = function() {
- $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "GET", url: $scope.urls.xhr_edit, data: undefined});}, 2000, 0);
+ $scope.fetchLayersForTargets = function () {
+ $scope.builds.forEach(function (buildrequest) {
+ buildrequest.errors.forEach(function (error) {
+ if (error.msg.indexOf("Nothin") == 0) {
+ $scope.getTargetNameFromErrorMsg(error.msg).forEach(function (target) {
+ if ($scope.layersForTargets[target] === undefined)
+ $scope.getAutocompleteSuggestions("layers4target", target).then( function (list) {
+ $scope.layersForTargets[target] = list;
+ })
+ })
+ }
+ })
+ })
}
- $scope.init();
-});
+ /**
+ * Page init code - just init variables and set the automated refresh
+ */
-/**
- TESTING CODE
-*/
+ $scope.init = function() {
+ $scope.canceledBuilds = [];
+ $scope.layersForTargets = {};
+ $scope.fetchLayersForTargets();
+ $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "GET", url: $scope.urls.xhr_edit, data: undefined});}, 2000, 0);
+ }
-function test_diff_arrays() {
- _diffArrays([1,2,3], [2,3,4], function(e,f) { return e==f; }, function(e) {console.warn("added", e)}, function(e) {console.warn("deleted", e);})
-}
+});
-// test_diff_arrays();
var s = undefined;
function test_set_alert(text) {
s = angular.element("div#main").scope();
s.displayAlert(s.zone3alerts, text);
- console.warn(s.zone3alerts);
+ console.warn("TRC8: zone3alerts", s.zone3alerts);
s.$digest();
}
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html
index 2979db74ed..67b267256b 100644
--- a/bitbake/lib/toaster/toastergui/templates/project.html
+++ b/bitbake/lib/toaster/toastergui/templates/project.html
@@ -89,9 +89,9 @@ vim: expandtab tabstop=2