diff options
-rw-r--r-- | bitbake/lib/toaster/toastergui/static/css/default.css | 27 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/static/js/projectapp.js | 182 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/project.html | 62 | ||||
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 41 |
4 files changed, 244 insertions, 68 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/css/default.css b/bitbake/lib/toaster/toastergui/static/css/default.css index fb20fc9241..da9697c408 100644 --- a/bitbake/lib/toaster/toastergui/static/css/default.css +++ b/bitbake/lib/toaster/toastergui/static/css/default.css | |||
@@ -113,6 +113,9 @@ select { width: auto; } | |||
113 | .top-air { margin-top: 40px;} | 113 | .top-air { margin-top: 40px;} |
114 | .progress { margin-bottom: 0px; } | 114 | .progress { margin-bottom: 0px; } |
115 | .lead .badge { font-size: 18px; font-weight: normal; border-radius: 15px; padding: 9px; } | 115 | .lead .badge { font-size: 18px; font-weight: normal; border-radius: 15px; padding: 9px; } |
116 | .lead ol > li, .lead ul > li { | ||
117 | line-height: 35px; | ||
118 | } | ||
116 | .well > .lead, .alert .lead { margin-bottom: 0px; } | 119 | .well > .lead, .alert .lead { margin-bottom: 0px; } |
117 | .well-transparent { background-color: transparent; } | 120 | .well-transparent { background-color: transparent; } |
118 | .no-results { margin: 10px 0; } | 121 | .no-results { margin: 10px 0; } |
@@ -191,3 +194,27 @@ dd > span { line-height: 20px; } | |||
191 | .new-build form { margin: 5px 0 0; } | 194 | .new-build form { margin: 5px 0 0; } |
192 | .new-build .input-append { margin-bottom: 0; } | 195 | .new-build .input-append { margin-bottom: 0; } |
193 | #build-selected { margin-top: 15px; } | 196 | #build-selected { margin-top: 15px; } |
197 | |||
198 | |||
199 | .animate-repeat { | ||
200 | list-style:none; | ||
201 | box-sizing:border-box; | ||
202 | } | ||
203 | |||
204 | .animate-repeat.ng-move, | ||
205 | .animate-repeat.ng-enter, | ||
206 | .animate-repeat.ng-leave { | ||
207 | -webkit-transition:all linear 0.5s; | ||
208 | transition:all linear 0.5s; | ||
209 | } | ||
210 | |||
211 | .animate-repeat.ng-leave.ng-leave-active, | ||
212 | .animate-repeat.ng-move, | ||
213 | .animate-repeat.ng-enter { | ||
214 | opacity:0; | ||
215 | } | ||
216 | |||
217 | .animate-repeat.ng-leave, | ||
218 | .animate-repeat.ng-enter.ng-enter-active { | ||
219 | opacity:1; | ||
220 | } | ||
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js index b347451e88..356b92fc5a 100644 --- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js +++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js | |||
@@ -74,17 +74,34 @@ angular_formpost = function($httpProvider) { | |||
74 | * | 74 | * |
75 | * no return | 75 | * no return |
76 | */ | 76 | */ |
77 | function _diffArrays(oldArray, newArray, compareElements, onAdded, onDeleted ) { | 77 | function _diffArrays(existingArray, newArray, compareElements, onAdded, onDeleted ) { |
78 | if (onDeleted !== undefined) { | 78 | var added = []; |
79 | oldArray.filter(function (e) { var found = 0; newArray.map(function (f) { if (compareElements(e, f)) {found = 1};}); return !found;}).map(onDeleted); | 79 | var removed = []; |
80 | newArray.forEach( function( newElement, newIndex, _newArray) { | ||
81 | var existingIndex = existingArray.findIndex(function ( existingElement, _existingIndex, _existingArray ) { | ||
82 | return compareElements(newElement, existingElement); | ||
83 | }); | ||
84 | if (existingIndex < 0 && onAdded) { added.push(newElement); } | ||
85 | }); | ||
86 | existingArray.forEach( function( existingElement, existingIndex, _existingArray) { | ||
87 | var newIndex = newArray.findIndex(function ( newElement, _newIndex, _newArray ) { | ||
88 | return compareElements(newElement, existingElement); | ||
89 | }); | ||
90 | if (newIndex < 0 && onDeleted) { removed.push(existingElement); } | ||
91 | }); | ||
92 | |||
93 | if (onAdded) { | ||
94 | added.map(onAdded); | ||
80 | } | 95 | } |
81 | if (onAdded !== undefined) { | 96 | |
82 | newArray.filter(function (e) { var found = 0; oldArray.map(function (f) { if (compareElements(e, f)) {found = 1};}); return !found;}).map(onAdded); | 97 | if (onDeleted) { |
98 | removed.map(onDeleted); | ||
83 | } | 99 | } |
100 | |||
84 | } | 101 | } |
85 | 102 | ||
86 | 103 | ||
87 | var projectApp = angular.module('project', ['ui.bootstrap', 'ngCookies'], angular_formpost); | 104 | var projectApp = angular.module('project', ['ngCookies', 'ngAnimate', 'ui.bootstrap' ], angular_formpost); |
88 | 105 | ||
89 | // modify the template tag markers to prevent conflicts with Django | 106 | // modify the template tag markers to prevent conflicts with Django |
90 | projectApp.config(function($interpolateProvider) { | 107 | projectApp.config(function($interpolateProvider) { |
@@ -111,7 +128,7 @@ projectApp.filter('timediff', function() { | |||
111 | 128 | ||
112 | 129 | ||
113 | // main controller for the project page | 130 | // main controller for the project page |
114 | projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce) { | 131 | projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce, $anchorScroll, $animate) { |
115 | 132 | ||
116 | $scope.getSuggestions = function(type, currentValue) { | 133 | $scope.getSuggestions = function(type, currentValue) { |
117 | var deffered = $q.defer(); | 134 | var deffered = $q.defer(); |
@@ -159,40 +176,65 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
159 | deffered.reject(_data.error); | 176 | deffered.reject(_data.error); |
160 | } | 177 | } |
161 | else { | 178 | else { |
162 | // TODO: update screen data if we have fields here | ||
163 | 179 | ||
164 | if (_data.builds !== undefined) { | 180 | if (_data.builds !== undefined) { |
165 | 181 | var toDelete = []; | |
166 | var oldbuilds = $scope.builds; | 182 | // step 1 - delete entries not found |
167 | $scope.builds = _data.builds; | 183 | $scope.builds.forEach(function (elem) { |
168 | 184 | if (-1 == _data.builds.findIndex(function (elemX) { return elemX.id == elem.id && elemX.status == elem.status; })) { | |
169 | // identify canceled builds here, so we can display them. | 185 | toDelete.push(elem); |
170 | _diffArrays(oldbuilds, $scope.builds, | 186 | } |
171 | function (e,f) { return e.id == f.id }, // compare | 187 | }); |
172 | undefined, // added | 188 | toDelete.forEach(function (elem) { |
173 | function (e) { // deleted | 189 | $scope.builds.splice($scope.builds.indexOf(elem),1); |
174 | if (e.status == "deleted") return; | 190 | }); |
175 | e.status = "deleted"; | 191 | // step 2 - merge new entries |
176 | for (var i = 0; i < $scope.builds.length; i++) { | 192 | _data.builds.forEach(function (elem) { |
177 | if ($scope.builds[i].status == "queued" && $scope.builds[i].id > e.id) | 193 | var found = false; |
178 | continue; | 194 | var i = 0; |
179 | $scope.builds.splice(i, 0, e); | 195 | for (i = 0 ; i < $scope.builds.length; i ++) { |
180 | break; | 196 | if ($scope.builds[i].id > elem.id) continue; |
181 | } | 197 | if ($scope.builds[i].id == elem.id) { found=true; break;} |
182 | }); | 198 | if ($scope.builds[i].id < elem.id) break; |
199 | } | ||
200 | if (!found) { | ||
201 | $scope.builds.splice(i, 0, elem); | ||
202 | } | ||
203 | }); | ||
183 | 204 | ||
184 | } | 205 | } |
185 | if (_data.layers !== undefined) { | 206 | if (_data.layers !== undefined) { |
186 | var oldlayers = $scope.layers; | ||
187 | $scope.layers = _data.layers; | ||
188 | 207 | ||
189 | // show added/deleted layer notifications | ||
190 | var addedLayers = []; | 208 | var addedLayers = []; |
191 | var deletedLayers = []; | 209 | var deletedLayers = []; |
192 | _diffArrays( oldlayers, $scope.layers, function (e, f) { return e.id == f.id }, | ||
193 | function (e) { console.log("new layer", e);addedLayers.push(e); }, | ||
194 | function (e) { console.log("del layer", e);deletedLayers.push(e); }); | ||
195 | 210 | ||
211 | // step 1 - delete entries not found | ||
212 | $scope.layers.forEach(function (elem) { | ||
213 | if (-1 == _data.layers.findIndex(function (elemX) { return elemX.id == elem.id && elemX.name == elem.name; })) { | ||
214 | deletedLayers.push(elem); | ||
215 | } | ||
216 | }); | ||
217 | deletedLayers.forEach(function (elem) { | ||
218 | $scope.layers.splice($scope.layers.indexOf(elem),1); | ||
219 | }); | ||
220 | // step 2 - merge new entries | ||
221 | _data.layers.forEach(function (elem) { | ||
222 | var found = false; | ||
223 | var i; | ||
224 | for (i = 0 ; i < $scope.layers.length; i ++) { | ||
225 | if ($scope.layers[i].orderid < elem.orderid) continue; | ||
226 | if ($scope.layers[i].orderid == elem.orderid) { | ||
227 | found = true; break; | ||
228 | } | ||
229 | if ($scope.layers[i].orderid > elem.orderid) break; | ||
230 | } | ||
231 | if (!found) { | ||
232 | $scope.layers.splice(i, 0, elem); | ||
233 | addedLayers.push(elem); | ||
234 | } | ||
235 | }); | ||
236 | |||
237 | // step 3 - display alerts. | ||
196 | if (addedLayers.length > 0) { | 238 | if (addedLayers.length > 0) { |
197 | $scope.displayAlert($scope.zone2alerts, "You have added <b>"+addedLayers.length+"</b> layer" + ((addedLayers.length>1)?"s: ":": ") + addedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", "), "alert-info"); | 239 | $scope.displayAlert($scope.zone2alerts, "You have added <b>"+addedLayers.length+"</b> layer" + ((addedLayers.length>1)?"s: ":": ") + addedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", "), "alert-info"); |
198 | } | 240 | } |
@@ -253,7 +295,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
253 | } | 295 | } |
254 | 296 | ||
255 | $scope.targetNamedBuild = function(target) { | 297 | $scope.targetNamedBuild = function(target) { |
256 | if ($scope.targetName === undefined){ | 298 | if ($scope.targetName === undefined && $scope.targetName1 === undefined){ |
257 | alert("No target defined, please type in a target name"); | 299 | alert("No target defined, please type in a target name"); |
258 | return; | 300 | return; |
259 | } | 301 | } |
@@ -263,17 +305,26 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
263 | $scope._makeXHRCall({ | 305 | $scope._makeXHRCall({ |
264 | method: "POST", url: $scope.urls.xhr_build, | 306 | method: "POST", url: $scope.urls.xhr_build, |
265 | data : { | 307 | data : { |
266 | targets: $scope.targetName | 308 | targets: $scope.safeTargetName, |
267 | } | 309 | } |
268 | }).then(function (data) { | 310 | }).then(function (data) { |
269 | console.log("received ", data); | 311 | console.log("received ", data); |
270 | $scope.targetName = undefined; | 312 | $scope.targetName = undefined; |
313 | $scope.targetName1 = undefined; | ||
314 | $location.hash('buildslist'); | ||
315 | // call $anchorScroll() | ||
316 | $anchorScroll(); | ||
271 | }); | 317 | }); |
272 | } | 318 | } |
273 | 319 | ||
274 | $scope.sanitizeTargetName = function() { | 320 | $scope.sanitizeTargetName = function() { |
275 | if (undefined === $scope.targetName) return; | 321 | $scope.safeTargetName = undefined; |
276 | $scope.targetName = $scope.targetName.replace(/\[.*\]/, '').trim(); | 322 | if (undefined === $scope.targetName) $scope.safeTargetName = $scope.targetName1; |
323 | if (undefined === $scope.targetName1) $scope.safeTargetName = $scope.targetName; | ||
324 | |||
325 | if (undefined === $scope.safeTargetName) return; | ||
326 | |||
327 | $scope.safeTargetName = $scope.safeTargetName.replace(/\[.*\]/, '').trim(); | ||
277 | } | 328 | } |
278 | 329 | ||
279 | $scope.buildCancel = function(id) { | 330 | $scope.buildCancel = function(id) { |
@@ -285,6 +336,16 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
285 | }); | 336 | }); |
286 | } | 337 | } |
287 | 338 | ||
339 | $scope.buildDelete = function(id) { | ||
340 | $scope._makeXHRCall({ | ||
341 | method: "POST", url: $scope.urls.xhr_build, | ||
342 | data: { | ||
343 | buildDelete: id, | ||
344 | } | ||
345 | }); | ||
346 | } | ||
347 | |||
348 | |||
288 | $scope.onLayerSelect = function (item, model, label) { | 349 | $scope.onLayerSelect = function (item, model, label) { |
289 | $scope.layerAddId = item.id; | 350 | $scope.layerAddId = item.id; |
290 | } | 351 | } |
@@ -413,6 +474,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
413 | console.log("edit with ", elementid); | 474 | console.log("edit with ", elementid); |
414 | var alertText = undefined; | 475 | var alertText = undefined; |
415 | var alertZone = undefined; | 476 | var alertZone = undefined; |
477 | var oldLayers = []; | ||
416 | switch(elementid) { | 478 | switch(elementid) { |
417 | case '#select-machine': | 479 | case '#select-machine': |
418 | alertText = "You have changed the machine to: <b>" + $scope.machineName + "</b>"; | 480 | alertText = "You have changed the machine to: <b>" + $scope.machineName + "</b>"; |
@@ -428,25 +490,61 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
428 | data['projectVersion'] = $scope.projectVersion; | 490 | data['projectVersion'] = $scope.projectVersion; |
429 | alertText = "You have changed the release to: "; | 491 | alertText = "You have changed the release to: "; |
430 | alertZone = $scope.zone3alerts; | 492 | alertZone = $scope.zone3alerts; |
493 | // save old layers | ||
494 | oldLayers = $scope.layers.slice(0); | ||
431 | break; | 495 | break; |
432 | default: | 496 | default: |
433 | throw "FIXME: implement conversion for element " + elementid; | 497 | throw "FIXME: implement conversion for element " + elementid; |
434 | } | 498 | } |
435 | 499 | ||
436 | console.log("calling edit with ", data); | ||
437 | $scope._makeXHRCall({ | 500 | $scope._makeXHRCall({ |
438 | method: "POST", url: $scope.urls.xhr_edit, data: data, | 501 | method: "POST", url: $scope.urls.xhr_edit, data: data, |
439 | }).then( function () { | 502 | }).then( function (_data) { |
440 | $scope.toggle(elementid); | 503 | $scope.toggle(elementid); |
441 | if (data['projectVersion'] != undefined) { | 504 | if (data['projectVersion'] != undefined) { |
442 | alertText += "<b>" + $scope.project.release.name + "</b>"; | 505 | alertText += "<strong>" + $scope.project.release.desc + "</strong>. "; |
506 | } | ||
507 | if (elementid == '#change-project-version') { | ||
508 | // requirement https://bugzilla.yoctoproject.org/attachment.cgi?id=2229, notification for changed version to include layers | ||
509 | $scope.zone2alerts.forEach(function (e) { e.close() }); | ||
510 | alertText += "This has caused the following changes in your project layers:<ul>" | ||
511 | |||
512 | if (_data.layers !== undefined) { | ||
513 | // show added/deleted layer notifications; scope.layers is already updated by this point. | ||
514 | var addedLayers = []; | ||
515 | var deletedLayers = []; | ||
516 | _diffArrays( oldLayers, $scope.layers, function (e, f) { return e.id == f.id }, | ||
517 | function (e) {addedLayers.push(e); }, | ||
518 | function (e) {deletedLayers.push(e); }); | ||
519 | |||
520 | // some of the deleted layers are actually replaced (changed) layers | ||
521 | var changedLayers = []; | ||
522 | deletedLayers.forEach(function (e) { | ||
523 | if ( -1 < addedLayers.findIndex(function (f) { return f.name == e.name })) { | ||
524 | changedLayers.push(e); | ||
525 | } | ||
526 | }); | ||
527 | |||
528 | changedLayers.forEach(function (e) { | ||
529 | deletedLayers.splice(deletedLayers.indexOf(e), 1); | ||
530 | }); | ||
531 | |||
532 | if (addedLayers.length > 0) { | ||
533 | alertText += "<li><strong>"+addedLayers.length+"</strong> layer" + ((addedLayers.length>1)?"s changed: ":" changed: ") + addedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", ") + "</li>"; | ||
534 | } | ||
535 | if (deletedLayers.length > 0) { | ||
536 | alertText += "<li><strong>"+deletedLayers.length+"</strong> layer" + ((deletedLayers.length>1)?"s deleted: ":"deleted: ") + deletedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", ") + "</li>"; | ||
537 | } | ||
538 | |||
539 | } | ||
540 | alertText += "</ul>"; | ||
443 | } | 541 | } |
444 | $scope.displayAlert(alertZone, alertText, "alert-info"); | 542 | $scope.displayAlert(alertZone, alertText, "alert-info"); |
445 | }); | 543 | }); |
446 | } | 544 | } |
447 | 545 | ||
448 | 546 | ||
449 | $scope.executeCommands = function() { | 547 | $scope.updateDisplayWithCommands = function() { |
450 | cmd = $location.path(); | 548 | cmd = $location.path(); |
451 | 549 | ||
452 | function _cmdExecuteWithParam(param, f) { | 550 | function _cmdExecuteWithParam(param, f) { |
@@ -524,7 +622,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc | |||
524 | // init code | 622 | // init code |
525 | // | 623 | // |
526 | $scope.init = function() { | 624 | $scope.init = function() { |
527 | $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "GET", url: $scope.urls.xhr_edit, data: undefined});}, 4000, 0); | 625 | $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "GET", url: $scope.urls.xhr_edit, data: undefined});}, 2000, 0); |
528 | } | 626 | } |
529 | 627 | ||
530 | $scope.init(); | 628 | $scope.init(); |
@@ -539,6 +637,8 @@ function test_diff_arrays() { | |||
539 | _diffArrays([1,2,3], [2,3,4], function(e,f) { return e==f; }, function(e) {console.log("added", e)}, function(e) {console.log("deleted", e);}) | 637 | _diffArrays([1,2,3], [2,3,4], function(e,f) { return e==f; }, function(e) {console.log("added", e)}, function(e) {console.log("deleted", e);}) |
540 | } | 638 | } |
541 | 639 | ||
640 | // test_diff_arrays(); | ||
641 | |||
542 | var s = undefined; | 642 | var s = undefined; |
543 | 643 | ||
544 | function test_set_alert(text) { | 644 | function test_set_alert(text) { |
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html index 6a812834d3..a8f2d6ad4f 100644 --- a/bitbake/lib/toaster/toastergui/templates/project.html +++ b/bitbake/lib/toaster/toastergui/templates/project.html | |||
@@ -9,6 +9,7 @@ vim: expandtab tabstop=2 | |||
9 | 9 | ||
10 | {% block projectinfomain %} | 10 | {% block projectinfomain %} |
11 | <script src="{% static "js/angular.min.js" %}"></script> | 11 | <script src="{% static "js/angular.min.js" %}"></script> |
12 | <script src="{% static "js/angular-animate.min.js" %}"></script> | ||
12 | <script src="{% static "js/angular-cookies.min.js" %}"></script> | 13 | <script src="{% static "js/angular-cookies.min.js" %}"></script> |
13 | <script src="{% static "js/ui-bootstrap-tpls-0.11.0.js" %}"></script> | 14 | <script src="{% static "js/ui-bootstrap-tpls-0.11.0.js" %}"></script> |
14 | 15 | ||
@@ -105,19 +106,21 @@ vim: expandtab tabstop=2 | |||
105 | </form> | 106 | </form> |
106 | </div> | 107 | </div> |
107 | 108 | ||
109 | <a id="buildslist"></a> | ||
108 | <h2 class="air" ng-if="builds.length">Latest builds</h2> | 110 | <h2 class="air" ng-if="builds.length">Latest builds</h2> |
109 | 111 | <div class="animate-repeat alert" ng-repeat="b in builds track by b.id" ng-class="{'queued':'alert-info', 'deleted':'alert-info', 'in progress': 'alert-info', 'failed':'alert-error', 'completed':{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.build[0].status]}[b.status]"> | |
110 | <div class="alert" ng-repeat="b in builds" ng-class="{'queued':'alert-info', 'deleted':'alert-info', 'in progress': 'alert-info', 'failed':'alert-error', 'completed':{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.build[0].status]}[b.status]" > | ||
111 | <div class="row-fluid"> | 112 | <div class="row-fluid"> |
112 | <switch ng-switch="b.status"> | 113 | <switch ng-switch="b.status"> |
114 | |||
113 | <case ng-switch-when="failed"> | 115 | <case ng-switch-when="failed"> |
114 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | 116 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span></div> |
115 | <div class="row-fluid"> | 117 | <div class="row-fluid"> |
116 | <div class="air well" ng-repeat="e in b.errors"> | 118 | <div class="air well" ng-repeat="e in b.errors"> |
117 | {[e.type]}: <pre>{[e.msg]}</pre> | 119 | Error type {[e.type]}: <pre>{[e.msg]}</pre> |
118 | </div> | 120 | </div> |
119 | </div> | 121 | </div> |
120 | </case> | 122 | </case> |
123 | |||
121 | <case ng-switch-when="queued"> | 124 | <case ng-switch-when="queued"> |
122 | <div class="lead span5"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | 125 | <div class="lead span5"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> |
123 | <div class="span4 lead" >Build queued | 126 | <div class="span4 lead" >Build queued |
@@ -125,6 +128,7 @@ vim: expandtab tabstop=2 | |||
125 | </div> | 128 | </div> |
126 | <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> | 129 | <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> |
127 | </case> | 130 | </case> |
131 | |||
128 | <case ng-switch-when="created"> | 132 | <case ng-switch-when="created"> |
129 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | 133 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> |
130 | <div class="span6" > | 134 | <div class="span6" > |
@@ -132,35 +136,56 @@ vim: expandtab tabstop=2 | |||
132 | </div> | 136 | </div> |
133 | <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> | 137 | <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> |
134 | </case> | 138 | </case> |
139 | |||
135 | <case ng-switch-when="deleted"> | 140 | <case ng-switch-when="deleted"> |
136 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | 141 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> |
137 | <div class="span6" id="{[b.id]}-deleted" > | 142 | <div class="span6" id="{[b.id]}-deleted" > |
138 | <span class="lead">Build deleted</span> | 143 | <span class="lead">Build deleted</span> |
139 | </div> | 144 | </div> |
140 | <button class="btn pull-right btn-info" ng-click="builds.splice(builds.indexOf(b), 1)">Close</button> | 145 | <button class="btn pull-right btn-info" ng-click="buildDelete(b.id)">Close</button> |
141 | </case> | 146 | </case> |
147 | |||
148 | |||
142 | <case ng-switch-when="in progress"> | 149 | <case ng-switch-when="in progress"> |
143 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | 150 | <switch ng-switch="b.build.length"> |
144 | <div class="span4" > | 151 | <case ng-switch-when="0"> |
145 | <div class="progress" style="margin-top:5px;" data-toggle="tooltip" tooltip="{[b.build[0].completeper]}% of tasks complete"> | 152 | <div class="lead span5"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> |
146 | <div style="width: {[b.build[0].completeper]}%;" class="bar"></div> | 153 | <div class="span4 lead"> |
154 | Checking out layers | ||
147 | </div> | 155 | </div> |
148 | </div> | 156 | </case> |
149 | <div class="lead pull-right">ETA: at {[b.build[0].eta|date:"shortDate"]}</div> | 157 | <case ng-switch-default=""> |
158 | <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> | ||
159 | <div class="span4 offset1" > | ||
160 | <div class="progress" style="margin-top:5px;" data-toggle="tooltip" tooltip="{[b.build[0].completeper]}% of tasks complete"> | ||
161 | <div style="width: {[b.build[0].completeper]}%;" class="bar"></div> | ||
162 | </div> | ||
163 | </div> | ||
164 | <div class="text-right lead">ETA: {[b.build[0].eta|date:"HH:mm:ss"]}</div> | ||
165 | </case> | ||
150 | </case> | 166 | </case> |
167 | |||
168 | |||
151 | <case ng-switch-when="completed"> | 169 | <case ng-switch-when="completed"> |
152 | <div class="lead span3"><a href="{[b.build[0].build_page_url]}"><span ng-repeat="t in b.targets" ng-include src="'target_display'"></span></a></div> | 170 | <div class="lead span3"><a href="{[b.build[0].build_page_url]}"><span ng-repeat="t in b.targets" ng-include src="'target_display'"></span></a></div> |
153 | <div class="span2 lead"> | 171 | <div class="span2 lead"> |
154 | {[b.build[0].completed_on|date:'dd/MM/yy HH:mm']} | 172 | <ngif ng-if="b.build[0].completed_on - todaydate > 0"> |
173 | {[b.build[0].completed_on|date:'HH:mm']} | ||
174 | </ngif> | ||
175 | <ngif ng-if="b.build[0].completed_on - todaydate < 0"> | ||
176 | {[b.build[0].completed_on|date:'dd/MM/yy HH:mm']} | ||
177 | </ngif> | ||
155 | </div> | 178 | </div> |
156 | <div class="span2"><span><a href="{[b.build[0].build_page_url]}#errors" class="lead error" ng-if="b.build[0].errors">{[b.build[0].errors]}</a></span></div> | 179 | <div class="span2"><span><a href="{[b.build[0].build_page_url]}#errors" class="lead error" ng-if="b.build[0].errors">{[b.build[0].errors]}</a></span></div> |
157 | <div class="span2"><span><a href="{[b.build[0].build_page_url]}#warnings" class="lead warning" ng-if="b.build[0].warnings">{[b.build[0].warnings]}</a></span></div> | 180 | <div class="span2"><span><a href="{[b.build[0].build_page_url]}#warnings" class="lead warning" ng-if="b.build[0].warnings">{[b.build[0].warnings]}</a></span></div> |
158 | <div> <span class="lead">Build time: {[b.build[0].build_time|timediff]}</span> | 181 | <div> <span class="lead">Build time: <a href="{[b.build[0].build_time_page_url]}">{[b.build[0].build_time|timediff]}</a></span> |
159 | <button class="btn pull-right" ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.build[0].status]" | 182 | <button class="btn pull-right" ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.build[0].status]" |
160 | ng-click="targetExistingBuild(b.targets)">Run again</button> | 183 | ng-click="targetExistingBuild(b.targets)">Run again</button> |
161 | 184 | ||
162 | </div> | 185 | </div> |
163 | </case> | 186 | </case> |
187 | |||
188 | |||
164 | <case ng-switch-default=""> | 189 | <case ng-switch-default=""> |
165 | <div>FIXME!</div> | 190 | <div>FIXME!</div> |
166 | </case> | 191 | </case> |
@@ -206,7 +231,7 @@ vim: expandtab tabstop=2 | |||
206 | </form> | 231 | </form> |
207 | <p><a href="{% url 'layers' %}">View all layers</a> | <a href="{% url 'importlayer' %}">Import layer</a></p> | 232 | <p><a href="{% url 'layers' %}">View all layers</a> | <a href="{% url 'importlayer' %}">Import layer</a></p> |
208 | <ul class="unstyled configuration-list"> | 233 | <ul class="unstyled configuration-list"> |
209 | <li ng-repeat="l in layers"> | 234 | <li ng-repeat="l in layers track by l.id" class="animate-repeat"> |
210 | <a href="{[l.layerdetailurl]}" target="_#" class="layer-info" data-toggle="tooltip" tooltip="{[l.branch.layersource]} | {[l.branch.name]}">{[l.name]} </a> | 235 | <a href="{[l.layerdetailurl]}" target="_#" class="layer-info" data-toggle="tooltip" tooltip="{[l.branch.layersource]} | {[l.branch.name]}">{[l.name]} </a> |
211 | <i class="icon-trash" ng-click="layerDel(l.id)" tooltip="Delete"></i> | 236 | <i class="icon-trash" ng-click="layerDel(l.id)" tooltip="Delete"></i> |
212 | </li> | 237 | </li> |
@@ -221,8 +246,8 @@ vim: expandtab tabstop=2 | |||
221 | <i class="icon-question-sign get-help heading-help" title="What you build, often a recipe producing a root file system file (an image). Something like <code>core-image-minimal</code> or <code>core-image-sato</code>"></i> | 246 | <i class="icon-question-sign get-help heading-help" title="What you build, often a recipe producing a root file system file (an image). Something like <code>core-image-minimal</code> or <code>core-image-sato</code>"></i> |
222 | </h3> | 247 | </h3> |
223 | <form ng-submit="targetNamedBuild()" class="input-append"> | 248 | <form ng-submit="targetNamedBuild()" class="input-append"> |
224 | <input type="text" class="input-xlarge" placeholder="Type the target(s) you want to build" autocomplete="off" data-minLength="1" ng-model="targetName" typeahead="e.name for e in getSuggestions('targets', $viewValue)|filter:$viewValue" typeahead-template-url="suggestion_details" ng-disabled="!layers.length"> | 249 | <input type="text" class="input-xlarge" placeholder="Type the target(s) you want to build" autocomplete="off" data-minLength="1" ng-model="targetName1" typeahead="e.name for e in getSuggestions('targets', $viewValue)|filter:$viewValue" typeahead-template-url="suggestion_details" ng-disabled="!layers.length"> |
225 | <button type="submit" id="build-button" class="btn btn-primary" ng-disabled="!targetName.length"> | 250 | <button type="submit" id="build-button" class="btn btn-primary" ng-disabled="!targetName1.length"> |
226 | Build </button> | 251 | Build </button> |
227 | {% csrf_token %} | 252 | {% csrf_token %} |
228 | </form> | 253 | </form> |
@@ -343,13 +368,16 @@ angular.element(document).ready(function() { | |||
343 | scope.machine = {{machine|safe}}; | 368 | scope.machine = {{machine|safe}}; |
344 | scope.releases = {{releases|safe}}; | 369 | scope.releases = {{releases|safe}}; |
345 | 370 | ||
371 | var now = (new Date()).getTime(); | ||
372 | scope.todaydate = now - (now % 86400000); | ||
373 | |||
346 | scope.zone1alerts = []; | 374 | scope.zone1alerts = []; |
347 | scope.zone2alerts = []; | 375 | scope.zone2alerts = []; |
348 | scope.zone3alerts = []; | 376 | scope.zone3alerts = []; |
349 | 377 | ||
350 | scope.mostBuiltTargets = {}; | 378 | scope.mostBuiltTargets = {}; |
351 | 379 | ||
352 | scope.executeCommands(); | 380 | scope.updateDisplayWithCommands(); |
353 | scope.validateData(); | 381 | scope.validateData(); |
354 | 382 | ||
355 | scope.$digest(); | 383 | scope.$digest(); |
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index fb8075067a..3b29dbcfb2 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -1943,11 +1943,13 @@ if toastermain.settings.MANAGED: | |||
1943 | "completed_on" : y.completed_on.strftime('%s')+"000", | 1943 | "completed_on" : y.completed_on.strftime('%s')+"000", |
1944 | "build_time" : (y.completed_on - y.started_on).total_seconds(), | 1944 | "build_time" : (y.completed_on - y.started_on).total_seconds(), |
1945 | "build_page_url" : reverse('builddashboard', args=(y.pk,)), | 1945 | "build_page_url" : reverse('builddashboard', args=(y.pk,)), |
1946 | 'build_time_page_url': reverse('buildtime', args=(y.pk,)), | ||
1946 | "errors": y.errors_no, | 1947 | "errors": y.errors_no, |
1947 | "warnings": y.warnings_no, | 1948 | "warnings": y.warnings_no, |
1948 | "completeper": y.completeper(), | 1949 | "completeper": y.completeper(), |
1949 | "eta": y.eta().ctime()}, Build.objects.filter(buildrequest = x)), | 1950 | "eta": y.eta().strftime('%s')+"000"}, Build.objects.filter(buildrequest = x)), |
1950 | }, prj.buildrequest_set.order_by("-pk")[:5]) | 1951 | }, list(prj.buildrequest_set.filter(Q(state__lt=BuildRequest.REQ_COMPLETED) or Q(state=BuildRequest.REQ_DELETED)).order_by("-pk")) + |
1952 | list(prj.buildrequest_set.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])) | ||
1951 | 1953 | ||
1952 | 1954 | ||
1953 | # Shows the edit project page | 1955 | # Shows the edit project page |
@@ -1979,10 +1981,17 @@ if toastermain.settings.MANAGED: | |||
1979 | context = { | 1981 | context = { |
1980 | "project" : prj, | 1982 | "project" : prj, |
1981 | "completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS), | 1983 | "completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS), |
1982 | "prj" : json.dumps({"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name}}), | 1984 | "prj" : json.dumps({"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}), |
1983 | #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), | 1985 | #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), |
1984 | "builds" : json.dumps(_project_recent_build_list(prj)), | 1986 | "builds" : json.dumps(_project_recent_build_list(prj)), |
1985 | "layers" : json.dumps(map(lambda x: {"id": x.layercommit.pk, "name" : x.layercommit.layer.name, "url": x.layercommit.layer.layer_index_url, "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, prj.projectlayer_set.all())), | 1987 | "layers" : json.dumps(map(lambda x: { |
1988 | "id": x.layercommit.pk, | ||
1989 | "orderid": x.pk, | ||
1990 | "name" : x.layercommit.layer.name, | ||
1991 | "url": x.layercommit.layer.layer_index_url, | ||
1992 | "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), | ||
1993 | "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, | ||
1994 | prj.projectlayer_set.all().order_by("id"))), | ||
1986 | "targets" : json.dumps(map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all())), | 1995 | "targets" : json.dumps(map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all())), |
1987 | "freqtargets": json.dumps(freqtargets), | 1996 | "freqtargets": json.dumps(freqtargets), |
1988 | "releases": json.dumps(map(lambda x: {"id": x.pk, "name": x.name}, Release.objects.all())), | 1997 | "releases": json.dumps(map(lambda x: {"id": x.pk, "name": x.name}, Release.objects.all())), |
@@ -2012,12 +2021,17 @@ if toastermain.settings.MANAGED: | |||
2012 | for i in request.POST['buildCancel'].strip().split(" "): | 2021 | for i in request.POST['buildCancel'].strip().split(" "): |
2013 | try: | 2022 | try: |
2014 | br = BuildRequest.objects.select_for_update().get(project = prj, pk = i, state__lte = BuildRequest.REQ_QUEUED) | 2023 | br = BuildRequest.objects.select_for_update().get(project = prj, pk = i, state__lte = BuildRequest.REQ_QUEUED) |
2015 | print "selected for delete", br.pk | 2024 | br.state = BuildRequest.REQ_DELETED |
2016 | br.delete() | 2025 | br.save() |
2017 | print "selected for delete", br.pk | ||
2018 | except BuildRequest.DoesNotExist: | 2026 | except BuildRequest.DoesNotExist: |
2019 | pass | 2027 | pass |
2020 | 2028 | ||
2029 | if 'buildDelete' in request.POST: | ||
2030 | for i in request.POST['buildDelete'].strip().split(" "): | ||
2031 | try: | ||
2032 | br = BuildRequest.objects.select_for_update().get(project = prj, pk = i, state__lte = BuildRequest.REQ_DELETED).delete() | ||
2033 | except BuildRequest.DoesNotExist: | ||
2034 | pass | ||
2021 | 2035 | ||
2022 | if 'targets' in request.POST: | 2036 | if 'targets' in request.POST: |
2023 | ProjectTarget.objects.filter(project = prj).delete() | 2037 | ProjectTarget.objects.filter(project = prj).delete() |
@@ -2054,9 +2068,10 @@ if toastermain.settings.MANAGED: | |||
2054 | prj.name = request.POST['projectName'] | 2068 | prj.name = request.POST['projectName'] |
2055 | prj.save(); | 2069 | prj.save(); |
2056 | 2070 | ||
2057 | |||
2058 | if 'projectVersion' in request.POST: | 2071 | if 'projectVersion' in request.POST: |
2059 | prj.release = Release.objects.get(pk = request.POST['projectVersion']) | 2072 | prj.release = Release.objects.get(pk = request.POST['projectVersion']) |
2073 | # we need to change the bitbake version | ||
2074 | prj.bitbake_version = prj.release.bitbake_version | ||
2060 | prj.save() | 2075 | prj.save() |
2061 | # we need to change the layers | 2076 | # we need to change the layers |
2062 | for i in prj.projectlayer_set.all(): | 2077 | for i in prj.projectlayer_set.all(): |
@@ -2067,13 +2082,19 @@ if toastermain.settings.MANAGED: | |||
2067 | # get rid of the old entry | 2082 | # get rid of the old entry |
2068 | i.delete() | 2083 | i.delete() |
2069 | 2084 | ||
2085 | if 'machineName' in request.POST: | ||
2086 | machinevar = prj.projectvariable_set.get(name="MACHINE") | ||
2087 | machinevar.value=request.POST['machineName'] | ||
2088 | machinevar.save() | ||
2089 | |||
2070 | # return all project settings | 2090 | # return all project settings |
2071 | return HttpResponse(json.dumps( { | 2091 | return HttpResponse(json.dumps( { |
2072 | "error": "ok", | 2092 | "error": "ok", |
2073 | "layers" : map(lambda x: {"id": x.layercommit.pk, "name" : x.layercommit.layer.name, "url": x.layercommit.layer.layer_index_url, "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, prj.projectlayer_set.all()), | 2093 | "layers" : map(lambda x: {"id": x.layercommit.pk, "orderid" : x.pk, "name" : x.layercommit.layer.name, "url": x.layercommit.layer.layer_index_url, "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, prj.projectlayer_set.all().order_by("id")), |
2074 | "builds" : _project_recent_build_list(prj), | 2094 | "builds" : _project_recent_build_list(prj), |
2075 | "variables": map(lambda x: (x.name, x.value), prj.projectvariable_set.all()), | 2095 | "variables": map(lambda x: (x.name, x.value), prj.projectvariable_set.all()), |
2076 | "prj": {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name}}, | 2096 | "machine": {"name": prj.projectvariable_set.get(name="MACHINE").value}, |
2097 | "prj": {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}, | ||
2077 | }), content_type = "application/json") | 2098 | }), content_type = "application/json") |
2078 | 2099 | ||
2079 | except Exception as e: | 2100 | except Exception as e: |