summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastergui
diff options
context:
space:
mode:
authorAlexandru DAMIAN <alexandru.damian@intel.com>2015-06-11 15:00:08 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-06-26 09:27:31 +0100
commit160563532f87bd901e1cc6972fe238be87a8b63c (patch)
tree7fd59522b17516bb1656bf264198cc560a352e14 /bitbake/lib/toaster/toastergui
parent2c7ed96b567386d0f57ad8c088790a515d17b7af (diff)
downloadpoky-160563532f87bd901e1cc6972fe238be87a8b63c.tar.gz
bitbake: toaster: refactor the builds pages
Taking out the managed mode-specific bits in build-related pages, as there is always only one mode available. Also refactors the build pages in order to always display Build objects instead of BuildRequest objects. (Bitbake rev: 6e46e1e3882b9770872d8a0bb459bc7d5d6bfed3) Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/projectapp.js14
-rw-r--r--bitbake/lib/toaster/toastergui/templates/builds.html (renamed from bitbake/lib/toaster/toastergui/templates/build.html)7
-rw-r--r--bitbake/lib/toaster/toastergui/templates/managed_builds.html167
-rw-r--r--bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html193
-rw-r--r--bitbake/lib/toaster/toastergui/templates/mrb_section.html41
-rw-r--r--bitbake/lib/toaster/toastergui/templates/project.html40
-rw-r--r--bitbake/lib/toaster/toastergui/templates/projectbuilds.html1
-rw-r--r--bitbake/lib/toaster/toastergui/templates/projects.html22
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py2
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py437
10 files changed, 175 insertions, 749 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
index b2e65c5bb5..40e2e1fffa 100644
--- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js
+++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
@@ -429,13 +429,17 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
429 */ 429 */
430 430
431 $scope.validateData = function () { 431 $scope.validateData = function () {
432 if ($scope.layers.length === 0) { 432 if ($scope.project.release) {
433 if ($scope.layers.length === 0) {
433 $scope.layeralert = $scope.displayAlert($scope.zone1alerts, "You need to add some layers to this project. <a href=\""+$scope.urls.layers+"\">View all layers available in Toaster</a> or <a href=\""+$scope.urls.importlayer+"\">import a layer</a>"); 434 $scope.layeralert = $scope.displayAlert($scope.zone1alerts, "You need to add some layers to this project. <a href=\""+$scope.urls.layers+"\">View all layers available in Toaster</a> or <a href=\""+$scope.urls.importlayer+"\">import a layer</a>");
434 } else { 435 } else {
435 if ($scope.layeralert !== undefined) { 436 if ($scope.layeralert !== undefined) {
436 $scope.layeralert.close(); 437 $scope.layeralert.close();
437 $scope.layeralert = undefined; 438 $scope.layeralert = undefined;
439 }
438 } 440 }
441 } else {
442 $scope.layeralert = $scope.displayAlert($scope.zone1alerts, "This project is not set to run builds.");
439 } 443 }
440 }; 444 };
441 445
diff --git a/bitbake/lib/toaster/toastergui/templates/build.html b/bitbake/lib/toaster/toastergui/templates/builds.html
index f0b5ea529f..e9211affcd 100644
--- a/bitbake/lib/toaster/toastergui/templates/build.html
+++ b/bitbake/lib/toaster/toastergui/templates/builds.html
@@ -14,13 +14,15 @@
14 14
15{% block pagecontent %} 15{% block pagecontent %}
16 16
17{% if last_date_from and last_date_to %}
17<script> 18<script>
18 // intiialize the date range controls 19 // initialize the date range controls
19 $(document).ready(function () { 20 $(document).ready(function () {
20 date_init('started_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_started_on}}','{{dateMax_started_on}}','{{daterange_selected}}'); 21 date_init('started_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_started_on}}','{{dateMax_started_on}}','{{daterange_selected}}');
21 date_init('completed_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_completed_on}}','{{dateMax_completed_on}}','{{daterange_selected}}'); 22 date_init('completed_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_completed_on}}','{{dateMax_completed_on}}','{{daterange_selected}}');
22 }); 23 });
23</script> 24</script>
25{%endif%} {# last_date_from and last_date_to #}
24 26
25<div class="row-fluid"> 27<div class="row-fluid">
26 28
@@ -87,9 +89,8 @@
87 <a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a> 89 <a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
88 {% endif %} 90 {% endif %}
89 </td> 91 </td>
90 {% if build.project %} 92 <td>
91 <a href="{% url 'project' build.project.id %}">{{build.project.name}}</a> 93 <a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
92 {% endif %}
93 </td> 94 </td>
94 </tr> 95 </tr>
95 96
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_builds.html b/bitbake/lib/toaster/toastergui/templates/managed_builds.html
deleted file mode 100644
index 63ae5408f5..0000000000
--- a/bitbake/lib/toaster/toastergui/templates/managed_builds.html
+++ /dev/null
@@ -1,167 +0,0 @@
1{% extends "base.html" %}
2
3{% load static %}
4{% load projecttags %}
5{% load humanize %}
6
7{% block extraheadcontent %}
8<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'>
9<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'>
10<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'>
11<script src="/static/js/jquery-ui.min.js"></script>
12<script src="/static/js/filtersnippet.js"></script>
13{% endblock %}
14
15{% block pagecontent %}
16
17<script>
18 // initialize the date range controls
19 $(document).ready(function () {
20 date_init('created','{{last_date_from}}','{{last_date_to}}','{{dateMin_created}}','{{dateMax_created}}','{{daterange_selected}}');
21 date_init('updated','{{last_date_from}}','{{last_date_to}}','{{dateMin_updated}}','{{dateMax_updated}}','{{daterange_selected}}');
22 });
23</script>
24
25<div class="row-fluid">
26
27 {% include "managed_mrb_section.html" %}
28
29
30 {% if 1 %}
31 <div class="page-header top-air">
32 <h1>
33 {% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %}
34 {{objects.paginator.count}} build{{objects.paginator.count|pluralize}} found
35 {%elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %}
36 No builds found
37 {%else%}
38 All builds
39 {%endif%}
40 </h1>
41 </div>
42
43 {% if objects.paginator.count == 0 %}
44 <div class="row-fluid">
45 {% if request.GET.filter or request.GET.search %}
46 <div class="alert">
47 <form class="no-results input-append" id="searchform">
48 <input id="search" name="search" class="input-xxlarge" type="text" value="{{request.GET.search}}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-remove"></i></a>{% endif %}
49 <button class="btn" type="submit" value="Search">Search</button>
50 <button class="btn btn-link" onclick="javascript:$('#search').val('');searchform.submit()">Show all builds</button>
51 </form>
52 </div>
53 {% else %}
54 <div class="alert alert-info">
55 <p class="lead">Toaster has not recorded any builds yet. To run a build, <a href="{% url 'all-projects' %}">select the project</a> for which you want to build.
56 </div>
57 {% endif %}
58 </div>
59
60
61 {% else %} {# We have builds to display #}
62 {% include "basetable_top_buildprojects.html" %}
63 <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work -->
64 {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #}
65 <tr class="data">
66 <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a>
67 {% if build.project %}
68 &nbsp; <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
69 <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
70 </a>
71 {% endif %}
72
73 </td>
74 <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td>
75 <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td>
76 {% if MANAGED %}
77 <td class="project">
78 {% if build.project %}
79 <a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
80 {% endif %}
81 </td>
82 {% endif %}
83 <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td>
84 <td class="completed_on"><a href="{% url "builddashboard" build.id %}">{{build.completed_on|date:"d/m/y H:i"}}</a></td>
85 <td class="failed_tasks error">
86 {% query build.task_build outcome=4 order__gt=0 as exectask%}
87 {% if exectask.count == 1 %}
88 <a href="{% url "task" build.id exectask.0.id %}">{{exectask.0.recipe.name}}.{{exectask.0.task_name}}</a>
89 {% if MANAGED and build.project %}
90 <a href="{% url 'build_artifact' build.id "tasklogfile" exectask.0.id %}">
91 <i class="icon-download-alt" title="" data-original-title="Download task log file"></i>
92 </a>
93 {% endif %}
94 {% elif exectask.count > 1%}
95 <a href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}} task{{exectask.count|pluralize}}</a>
96 {%endif%}
97 </td>
98 <td class="errors_no">
99 {% if build.errors_no %}
100 <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
101 {% if MANAGED and build.project and build.buildartifact_set.count %}
102 <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
103 <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
104 </a>
105 {% endif %}
106 {%endif%}
107 </td>
108 <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
109 <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent|sectohms}}</a></td>
110 {% if not MANAGED or not build.project %}
111 <td class="log">{{build.cooker_log_path}}</td>
112 {% endif %}
113 <td class="output">
114 {% if build.outcome == build.SUCCEEDED %}
115 <a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
116 {% endif %}
117 </td>
118 </tr>
119
120
121 {%endwith%}
122 {% else %} {# we don't have a build for this build request, mask the data with build request data #}
123
124
125
126 <tr class="data">
127 <td class="outcome">{% if buildrequest.state == buildrequest.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td>
128 <td class="target">
129 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}"><span data-toggle="tooltip" {%if buildrequest.brtarget_set.all.count > 1%}title="Targets: {%for target in buildrequest.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{buildrequest.brtarget_set.all.0.target}} {%if buildrequest.brtarget_set.all.count > 1%}(+ {{buildrequest.brtarget_set.all.count|add:"-1"}}){%endif%} </span></a>
130 </td>
131 <td class="machine">
132 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.machine}}</a>
133 </td>
134 {% if MANAGED %}
135 <td class="project">
136 <a href="{% url 'project' buildrequest.project.id %}">{{buildrequest.project.name}}</a>
137 </td>
138 {% endif %}
139 <td class="started_on">
140 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.created|date:"d/m/y H:i"}}</a>
141 </td>
142 <td class="completed_on">
143 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.updated|date:"d/m/y H:i"}}</a>
144 </td>
145 <td class="failed_tasks error">
146 </td>
147 <td class="errors_no">
148 <a class="errors_no error" href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}#errors">{{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}</a>
149 </td>
150 <td class="warnings_no">
151 </td>
152 <td class="time">
153 {{br.timespent.total_seconds|sectohms}}
154 </td>
155 <td class="output"> {# we have no output here #}
156 </td>
157 </tr>
158 {%endif%}
159 {% endfor %}
160
161
162 {% include "basetable_bottom.html" %}
163 {% endif %} {# objects.paginator.count #}
164{% endif %} {# empty #}
165</div><!-- end row-fluid-->
166
167{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html b/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html
deleted file mode 100644
index 47e64eaaab..0000000000
--- a/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html
+++ /dev/null
@@ -1,193 +0,0 @@
1{% load static %}
2{% load projecttags %}
3{% load humanize %}
4
5{%if mru|length > 0%}
6{# Template provides the latest builds section requires mru in the context which can be added from _managed_get_latest_builds #}
7 <div class="page-header top-air">
8 <h1>
9 Latest builds
10 </h1>
11 </div>
12 <div id="latest-builds">
13 {% for buildrequest in mru %}{% with build=buildrequest.build %}
14
15 {% if build %} {# if we have a build, just display it #}
16
17 <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} {% if MANAGED and build.project %}project-name{% endif %} ">
18 {% if MANAGED and build.project %}
19 <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
20 <a href="{% url 'project' build.project.id %}"> {{build.project.name}} </a>
21 </span>
22 {% endif %}
23
24 <div class="row-fluid">
25 <div class="span3 lead">
26 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
27 <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
28 {% endif %}
29 {% include "brtargets.html" %}
30 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
31 </a>
32 {% endif %}
33 </div>
34 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
35 <div class="span2 lead">
36 {% if build.completed_on|format_build_date %}
37 {{ build.completed_on|date:'d/m/y H:i' }}
38 {% else %}
39 {{ build.completed_on|date:'H:i' }}
40 {% endif %}
41 </div>
42 <div class="span2 lead">
43 {% if build.errors_no %}
44 <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
45 {% endif %}
46 </div>
47 <div class="span2 lead">
48 {% if build.warnings_no %}
49 <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
50 {% endif %}
51 </div>
52 <div class="lead ">
53 <span class="lead{%if not MANAGED or not build.project%} pull-right{%endif%}">
54 Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
55 </span>
56 {% if build.project %}
57 <button class="btn
58 {% if build.outcome == build.SUCCEEDED %}
59 btn-success
60 {% elif build.outcome == build.FAILED %}
61 btn-danger
62 {% else %}
63 btn-info
64 {%endif%}
65 pull-right"
66 {% include "runagain.html" %}
67 </button>
68 {% endif %}
69 </div>
70 {%endif%}
71 {%if build.outcome == build.IN_PROGRESS %}
72 <div class="span4 offset1">
73 <div class="progress" style="margin-top:5px;" data-toggle="tooltip" title="{{build.completeper}}% of tasks complete">
74 <div style="width: {{build.completeper}}%;" class="bar"></div>
75 </div>
76 </div>
77 <div class="lead pull-right">{{build.completeper}}% tasks completed</div>
78 {%endif%}
79 </div>
80 </div>
81
82 {% else %} {# we use the project's page recent build design #}
83
84
85
86
87 <div class="alert {% if buildrequest.state == buildrequest.REQ_FAILED %}alert-error{% else %}alert-info{% endif %} project-name">
88 <span class="label {% if buildrequest.state == buildrequest.REQ_FAILED %}label-important{% else%}label-info{% endif %}">
89 <a href="{% url 'project' buildrequest.project.id %}"> {{buildrequest.project.name}} </a>
90 </span>
91 <div class="row-fluid">
92
93 {% if buildrequest.state == buildrequest.REQ_FAILED %}
94 <div class="span3 lead">
95 <a href="{%url 'buildrequestdetails' buildrequest.project.id buildrequest.pk%}" class="error">
96 {% include "brtargets.html" %}
97 </a>
98 </div>
99 <div class="span2 lead">
100 {% if buildrequest.updated|format_build_date %}
101 {{ buildrequest.updated|date:'d/m/y H:i' }}
102 {% else %}
103 {{ buildrequest.updated|date:'H:i' }}
104 {% endif %}
105 </div>
106 <div class="span2 lead">
107 {% if buildrequest.brerror_set.all.count %}
108 <i class="icon-minus-sign red"></i> <a href="{%url 'buildrequestdetails' buildrequest.project.id buildrequest.pk %}#errors" class="error">{{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}</a>
109 {% endif %}
110 </div>
111 <div class="span2 lead"> {# there are no warnings for buildrequests #}
112 </div>
113 <div class="lead ">
114 <span class="lead{%if not MANAGED or not buildrequest.project%} pull-right{%endif%}">
115 Build time: {{ buildrequest.get_duration|sectohms }}
116 </span>
117
118 <button class="btn btn-danger pull-right"
119 {% include "runagain.html" %}
120 </button>
121 </div>
122
123
124 {% elif buildrequest.state == buildrequest.REQ_QUEUED %}
125
126 <div class="lead span5">
127 {% include "brtargets.html" %}
128 </div>
129 <div class="span4 lead">Build queued
130 <i title="This build will start as soon as a build server is available" class="icon-question-sign get-help get-help-blue heading-help" data-toggle="tooltip"></i>
131 </div>
132 <button class="btn btn-info pull-right cancel-build-btn" data-build-id="{{buildrequest.id}}" data-request-url="{% url 'projectbuilds' buildrequest.project.id %}" >Cancel</button>
133
134 {% elif buildrequest.state == buildrequest.REQ_CREATED %}
135
136 <div class="lead span3">
137 {% include "brtargets.html" %}
138 </div>
139 <div class="span6" >
140 <span class="lead">Creating build</span>
141 </div>
142
143 {% elif buildrequest.state == buildrequest.REQ_INPROGRESS %}
144
145 <div class="lead span5">
146 {% include "brtargets.html" %}
147 </div>
148 <div class="span4 lead">
149 Checking out layers
150 </div>
151 {% else %}
152
153 <div>FIXME!</div>
154
155 {% endif %}
156 <div class="lead pull-right">
157 </div>
158 </div>
159 </div>
160
161
162
163 {% endif %} {# this ends the build request most recent build section #}
164
165{%endwith%}{% endfor %}
166 </div>
167
168<script>
169
170function scheduleBuild(url, projectName, projectUrl, buildlist) {
171 console.log("scheduleBuild");
172 libtoaster.startABuild(url, null, buildlist.join(" "), function(){
173 window.location.reload();
174 }, null);
175}
176
177$(document).ready(function(){
178
179 $(".cancel-build-btn").click(function (){
180 var url = $(this).data('request-url');
181 var buildIds = $(this).data('build-id');
182 var btn = $(this);
183
184 libtoaster.cancelABuild(url, buildIds, function(){
185 btn.parents(".alert").fadeOut();
186 }, null);
187 });
188});
189
190</script>
191
192{%endif%}
193
diff --git a/bitbake/lib/toaster/toastergui/templates/mrb_section.html b/bitbake/lib/toaster/toastergui/templates/mrb_section.html
index c7bddf0372..7e84e4134d 100644
--- a/bitbake/lib/toaster/toastergui/templates/mrb_section.html
+++ b/bitbake/lib/toaster/toastergui/templates/mrb_section.html
@@ -12,18 +12,24 @@
12 </div> 12 </div>
13 <div id="latest-builds"> 13 <div id="latest-builds">
14 {% for build in mru %} 14 {% for build in mru %}
15 <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} {% if MANAGED and build.project %}project-name{% endif %} "> 15 <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} project-name ">
16 {% if MANAGED and build.project %}
17 <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-danger{%else%}label-info{%endif%}"> {{build.project.name}} </span> 16 <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-danger{%else%}label-info{%endif%}"> {{build.project.name}} </span>
18 {% endif %}
19 17
20 <div class="row-fluid"> 18 <div class="row-fluid">
21 <div class="span3 lead"> 19 <div class="span3 lead">
22 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %} 20 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
23 <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}"> 21 <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
24 {% endif %} 22 {% endif %}
25 <span data-toggle="tooltip" {%if build.target_set.all.count > 1%}title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} 23 {% if build.target_set.all.count > 0 %}
26 </span> 24 <span data-toggle="tooltip"
25 {%if build.target_set.all.count > 1%}
26 title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"
27 {%endif%}
28 >
29
30 {{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%}
31 </span>
32 {% endif %}
27 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %} 33 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
28 </a> 34 </a>
29 {% endif %} 35 {% endif %}
@@ -65,5 +71,30 @@
65 71
66 {% endfor %} 72 {% endfor %}
67 </div> 73 </div>
74
75<script>
76
77function scheduleBuild(url, projectName, projectUrl, buildlist) {
78 console.log("scheduleBuild");
79 libtoaster.startABuild(url, null, buildlist.join(" "), function(){
80 window.location.reload();
81 }, null);
82}
83
84$(document).ready(function(){
85
86 $(".cancel-build-btn").click(function (){
87 var url = $(this).data('request-url');
88 var buildIds = $(this).data('build-id');
89 var btn = $(this);
90
91 libtoaster.cancelABuild(url, buildIds, function(){
92 btn.parents(".alert").fadeOut();
93 }, null);
94 });
95});
96
97</script>
98
68{%endif%} 99{%endif%}
69 100
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html
index bca703a162..0f6a77b63c 100644
--- a/bitbake/lib/toaster/toastergui/templates/project.html
+++ b/bitbake/lib/toaster/toastergui/templates/project.html
@@ -124,7 +124,7 @@ vim: expandtab tabstop=2
124 <div class="well"> 124 <div class="well">
125 <form class="build-form" data-ng-submit="buildNamedTarget()"> 125 <form class="build-form" data-ng-submit="buildNamedTarget()">
126 <div class="input-append controls"> 126 <div class="input-append controls">
127 <input type="text" class="huge input-xxlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-ng-model="targetName" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!layers.length"/> 127 <input type="text" class="huge input-xxlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-ng-model="targetName" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!project.release || !layers.length"/>
128 <button type="submit" class="btn btn-large btn-primary" data-ng-disabled="!targetName.length"> 128 <button type="submit" class="btn btn-large btn-primary" data-ng-disabled="!targetName.length">
129 Build 129 Build
130 </button> 130 </button>
@@ -145,11 +145,11 @@ vim: expandtab tabstop=2
145 145
146 <a id="buildslist"></a> 146 <a id="buildslist"></a>
147 <h2 class="air" data-ng-if="builds.length">Latest builds</h2> 147 <h2 class="air" data-ng-if="builds.length">Latest builds</h2>
148 <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-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]"> 148 <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
149 <div class="row-fluid"> 149 <div class="row-fluid">
150 <switch data-ng-switch="b.status"> 150 <switch data-ng-switch="b.status">
151 151
152 <case data-ng-switch-when="failed"> 152 <case data-ng-switch-when="Failed">
153 <div class="lead span3"> 153 <div class="lead span3">
154 <a data-ng-class="{'succeeded': 'success', 'failed': 'error'}[b.status]" href="{[b.br_page_url]}"> 154 <a data-ng-class="{'succeeded': 'success', 'failed': 'error'}[b.status]" href="{[b.br_page_url]}">
155 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> 155 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
@@ -182,32 +182,8 @@ vim: expandtab tabstop=2
182 </div> 182 </div>
183 </case> 183 </case>
184 184
185 <case data-ng-switch-when="queued">
186 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
187 <div class="span4 lead" >Build queued
188 <i title="This build will start as soon as a build server is available" class="icon-question-sign get-help get-help-blue heading-help" data-toggle="tooltip"></i>
189 </div>
190 <button class="btn pull-right btn-info" data-ng-click="buildCancel(b)">Cancel</button>
191 </case>
192
193 <case data-ng-switch-when="created">
194 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
195 <div class="span4">
196 <span class="lead">Creating build</span>
197 </div>
198 <button class="btn pull-right btn-info" data-ng-click="buildCancel(b)">Cancel</button>
199 </case>
200
201 <case data-ng-switch-when="deleted">
202 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
203 <div class="span4" id="{[b.id]}-deleted" >
204 <span class="lead">Build cancelled</span>
205 </div>
206 <button class="btn pull-right btn-info" data-ng-click="buildDelete(b)">Close</button>
207 </case>
208
209 185
210 <case data-ng-switch-when="in progress"> 186 <case data-ng-switch-when="In Progress">
211 <switch data-ng-switch="b.build.length"> 187 <switch data-ng-switch="b.build.length">
212 <case data-ng-switch-when="0"> 188 <case data-ng-switch-when="0">
213 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div> 189 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
@@ -227,7 +203,7 @@ vim: expandtab tabstop=2
227 </case> 203 </case>
228 204
229 205
230 <case data-ng-switch-when="completed"> 206 <case data-ng-switch-when="Succeeded">
231 <div class="lead span3"> 207 <div class="lead span3">
232 <a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.build[0].status]" href="{[b.build[0].build_page_url]}"> 208 <a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.build[0].status]" href="{[b.build[0].build_page_url]}">
233 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> 209 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
@@ -293,7 +269,7 @@ vim: expandtab tabstop=2
293 Layers <span class="muted counter">({[layers.length]})</span> 269 Layers <span class="muted counter">({[layers.length]})</span>
294 <i class="icon-question-sign get-help heading-help" title="Bitbake reads metadata files from modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>"></i> 270 <i class="icon-question-sign get-help heading-help" title="Bitbake reads metadata files from modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>"></i>
295 </h3> 271 </h3>
296 <div class="alert" data-ng-if="!layers.length"> 272 <div class="alert" data-ng-if="project.release && !layers.length">
297 <b>You need to add some layers </b> 273 <b>You need to add some layers </b>
298 <p> 274 <p>
299 You can: 275 You can:
@@ -307,7 +283,7 @@ vim: expandtab tabstop=2
307 </div> 283 </div>
308 <form data-ng-submit="layerAdd()"> 284 <form data-ng-submit="layerAdd()">
309 <div class="input-append"> 285 <div class="input-append">
310 <input type="text" class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-minLength="1" data-ng-model="layerAddName" data-typeahead="e for e in getLayersAutocompleteSuggestions($viewValue)" data-typeahead-template-url="layers_suggestion_details" data-typeahead-on-select="onLayerSelect($item, $model, $label)" data-typeahead-editable="false" data-ng-class="{ 'has-error': layerAddName.$invalid }" /> 286 <input type="text" class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-minLength="1" data-ng-model="layerAddName" data-typeahead="e for e in getLayersAutocompleteSuggestions($viewValue)" data-typeahead-template-url="layers_suggestion_details" data-typeahead-on-select="onLayerSelect($item, $model, $label)" data-typeahead-editable="false" data-ng-class="{ 'has-error': layerAddName.$invalid }" data-ng-disabled="!project.release" />
311 <input type="submit" id="add-layer" class="btn" value="Add" data-ng-disabled="!layerAddName.length"/> 287 <input type="submit" id="add-layer" class="btn" value="Add" data-ng-disabled="!layerAddName.length"/>
312 </div> 288 </div>
313 {% csrf_token %} 289 {% csrf_token %}
@@ -334,7 +310,7 @@ vim: expandtab tabstop=2
334 </h3> 310 </h3>
335 <form data-ng-submit="buildNamedTarget()"> 311 <form data-ng-submit="buildNamedTarget()">
336 <div class="input-append"> 312 <div class="input-append">
337 <input type="text" class="input-xlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-minLength="1" data-ng-model="targetName1" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!layers.length"> 313 <input type="text" class="input-xlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-minLength="1" data-ng-model="targetName1" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!project.release || !layers.length">
338 <button type="submit" class="btn btn-primary" data-ng-disabled="!targetName1.length"> 314 <button type="submit" class="btn btn-primary" data-ng-disabled="!targetName1.length">
339 Build </button> 315 Build </button>
340 </div> 316 </div>
diff --git a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
index 18b24955d0..896c3b5af7 100644
--- a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
+++ b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
@@ -105,7 +105,6 @@
105 </td> 105 </td>
106 </tr> 106 </tr>
107 107
108
109 {%endwith%} 108 {%endwith%}
110 {% else %} {# we don't have a build for this build request, mask the data with build request data #} 109 {% else %} {# we don't have a build for this build request, mask the data with build request data #}
111 110
diff --git a/bitbake/lib/toaster/toastergui/templates/projects.html b/bitbake/lib/toaster/toastergui/templates/projects.html
index 23340083ca..9c4346c45a 100644
--- a/bitbake/lib/toaster/toastergui/templates/projects.html
+++ b/bitbake/lib/toaster/toastergui/templates/projects.html
@@ -7,7 +7,7 @@
7{% block pagecontent %} 7{% block pagecontent %}
8 8
9 9
10 {% include "managed_mrb_section.html" %} 10 {% include "mrb_section.html" %}
11 11
12 12
13 <div class="page-header top-air"> 13 <div class="page-header top-air">
@@ -39,7 +39,13 @@
39 <tr class="data"> 39 <tr class="data">
40 <td><a href="{% url 'project' o.id %}">{{o.name}}</a></td> 40 <td><a href="{% url 'project' o.id %}">{{o.name}}</a></td>
41 <td class="updated"><a href="{% url 'project' o.id %}">{{o.updated|date:"d/m/y H:i"}}</a></td> 41 <td class="updated"><a href="{% url 'project' o.id %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
42 <td><a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a></td> 42 <td>
43 {% if o.release %}
44 <a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a>
45 {% else %}
46 No release available
47 {% endif %}
48 </td>
43 <td><a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a></td> 49 <td><a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a></td>
44 {% if o.get_number_of_builds == 0 %} 50 {% if o.get_number_of_builds == 0 %}
45 <td class="muted">{{o.get_number_of_builds}}</td> 51 <td class="muted">{{o.get_number_of_builds}}</td>
@@ -52,13 +58,13 @@
52 <td><a href="{% url 'projectbuilds' o.id %}">{{o.get_number_of_builds}}</a></td> 58 <td><a href="{% url 'projectbuilds' o.id %}">{{o.get_number_of_builds}}</a></td>
53 <td class="loutcome"><a href="{% url "builddashboard" o.get_last_build_id %}">{%if o.get_last_outcome == build_SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif o.get_last_outcome == build_FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td> 59 <td class="loutcome"><a href="{% url "builddashboard" o.get_last_build_id %}">{%if o.get_last_outcome == build_SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif o.get_last_outcome == build_FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td>
54 <td class="ltarget"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.get_last_target}} </a></td> 60 <td class="ltarget"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.get_last_target}} </a></td>
55 <td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td> 61 <td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
56 <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td> 62 <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
57 <td class="limagefiles"> 63 <td class="limagefiles">
58 {% if o.get_last_outcome == build_SUCCEEDED %} 64 {% if o.get_last_outcome == build_SUCCEEDED %}
59 <a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a> 65 <a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a>
60 {% endif %} 66 {% endif %}
61 </td> 67 </td>
62 68
63 {% endif %} 69 {% endif %}
64 </tr> 70 </tr>
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index bd3eb401de..681f06787a 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -132,7 +132,7 @@ urlpatterns = patterns('toastergui.views',
132 url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'), 132 url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'),
133 133
134 # dashboard for failed build requests 134 # dashboard for failed build requests
135 url(r'^project/(?P<pid>\d+)/buildrequest/(?P<brid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'), 135 url(r'^project/(?P<pid>\d+)/buildrequest/(?P<bid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'),
136 136
137 # default redirection 137 # default redirection
138 url(r'^$', RedirectView.as_view( url= 'landing')), 138 url(r'^$', RedirectView.as_view( url= 'landing')),
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 060d680b85..0324d17065 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -55,11 +55,23 @@ def landing(request):
55 55
56 return render(request, 'landing.html', context) 56 return render(request, 'landing.html', context)
57 57
58# returns a list for most recent builds; for use in the Project page, xhr_ updates, and other places, as needed 58
59
60# returns a list for most recent builds;
61def _get_latest_builds(prj=None):
62 queryset = Build.objects.all()
63
64 if prj is not None:
65 queryset = queryset.filter(project = prj)
66
67 return itertools.chain(queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk"))
68
69
70# a JSON-able dict of recent builds; for use in the Project page, xhr_ updates, and other places, as needed
59def _project_recent_build_list(prj): 71def _project_recent_build_list(prj):
60 data = [] 72 data = []
61 # take the most recent 3 completed builds, plus any builds in progress 73 # take the most recent 3 completed builds, plus any builds in progress
62 for x in itertools.chain(prj.build_set.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], prj.build_set.filter(outcome=Build.IN_PROGRESS).order_by("-pk")): 74 for x in _get_latest_builds(prj):
63 d = { 75 d = {
64 "id": x.pk, 76 "id": x.pk,
65 "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table 77 "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table
@@ -1866,10 +1878,10 @@ if True:
1866 # be able to display something. 'count' and 'page' are mandatory for all views 1878 # be able to display something. 'count' and 'page' are mandatory for all views
1867 # that use paginators. 1879 # that use paginators.
1868 1880
1869 buildrequests = BuildRequest.objects.exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) 1881 queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS)
1870 1882
1871 try: 1883 try:
1872 context, pagesize, orderby = _build_list_helper(request, buildrequests, True) 1884 context, pagesize, orderby = _build_list_helper(request, queryset)
1873 except InvalidRequestException as e: 1885 except InvalidRequestException as e:
1874 raise RedirectException( builds, request.GET, e.response) 1886 raise RedirectException( builds, request.GET, e.response)
1875 1887
@@ -1878,66 +1890,37 @@ if True:
1878 1890
1879 1891
1880 # helper function, to be used on "all builds" and "project builds" pages 1892 # helper function, to be used on "all builds" and "project builds" pages
1881 def _build_list_helper(request, buildrequests, insert_projects): 1893 def _build_list_helper(request, queryset_all):
1882 # ATTN: we use here the ordering parameters for interactive mode; the translation for BuildRequest fields will happen below 1894
1883 default_orderby = 'completed_on:-' 1895 default_orderby = 'completed_on:-'
1884 (pagesize, orderby) = _get_parameters_values(request, 10, default_orderby) 1896 (pagesize, orderby) = _get_parameters_values(request, 10, default_orderby)
1885 mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } 1897 mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
1886 retval = _verify_parameters( request.GET, mandatory_parameters ) 1898 retval = _verify_parameters( request.GET, mandatory_parameters )
1887 if retval: 1899 if retval:
1888 raise InvalidRequestException(mandatory_parameters) 1900 raise RedirectException( 'all-builds', request.GET, mandatory_parameters)
1889
1890 orig_orderby = orderby
1891 # translate interactive mode ordering to managed mode ordering
1892 ordering_params = orderby.split(":")
1893 if ordering_params[0] == "completed_on":
1894 ordering_params[0] = "updated"
1895 if ordering_params[0] == "started_on":
1896 ordering_params[0] = "created"
1897 if ordering_params[0] == "errors_no":
1898 ordering_params[0] = "build__errors_no"
1899 if ordering_params[0] == "warnings_no":
1900 ordering_params[0] = "build__warnings_no"
1901 if ordering_params[0] == "machine":
1902 ordering_params[0] = "build__machine"
1903 if ordering_params[0] == "target__target":
1904 ordering_params[0] = "brtarget__target"
1905 if ordering_params[0] == "timespent":
1906 ordering_params[0] = "id"
1907 orderby = default_orderby
1908
1909 request.GET = request.GET.copy() # get a mutable copy of the GET QueryDict
1910 request.GET['orderby'] = ":".join(ordering_params)
1911 1901
1912 # boilerplate code that takes a request for an object type and returns a queryset 1902 # boilerplate code that takes a request for an object type and returns a queryset
1913 # for that object type. copypasta for all needed table searches 1903 # for that object type. copypasta for all needed table searches
1914 (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) 1904 (filter_string, search_term, ordering_string) = _search_tuple(request, Build)
1915 # post-process any date range filters 1905 # post-process any date range filters
1916 filter_string,daterange_selected = _modify_date_range_filter(filter_string) 1906 filter_string,daterange_selected = _modify_date_range_filter(filter_string)
1917 1907 queryset_all = queryset_all.select_related("project")
1918 # we don't display in-progress or deleted builds 1908 queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
1919 queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED) 1909 queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
1920 queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror'))
1921 queryset_with_search = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated')
1922
1923 1910
1924 # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display 1911 # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display
1925 build_info = _build_page_range(Paginator(queryset_with_search, pagesize), request.GET.get('page', 1)) 1912 build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
1926 1913
1927 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) 1914 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
1928 # most recent build is like projects' most recent builds, but across all projects 1915 build_mru = Build.objects.order_by("-started_on")[:3]
1929 build_mru = _managed_get_latest_builds() 1916
1917 # calculate the exact begining of local today and yesterday, append context
1918 context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'})
1930 1919
1920 # set up list of fstypes for each build
1931 fstypes_map = {}; 1921 fstypes_map = {};
1932 for build_request in build_info: 1922 for build in build_info:
1933 # set display variables for build request 1923 targets = Target.objects.filter( build_id = build.id )
1934 build_request.machine = build_request.brvariable_set.get(name="MACHINE").value
1935 build_request.timespent = build_request.updated - build_request.created
1936
1937 # set up list of fstypes for each build
1938 if build_request.build is None:
1939 continue
1940 targets = Target.objects.filter( build_id = build_request.build.id )
1941 comma = ""; 1924 comma = "";
1942 extensions = ""; 1925 extensions = "";
1943 for t in targets: 1926 for t in targets:
@@ -1951,8 +1934,7 @@ if True:
1951 if None == re.search(s,extensions): 1934 if None == re.search(s,extensions):
1952 extensions += comma + s 1935 extensions += comma + s
1953 comma = ", " 1936 comma = ", "
1954 fstypes_map[build_request.build.id]=extensions 1937 fstypes_map[build.id]=extensions
1955
1956 1938
1957 # send the data to the template 1939 # send the data to the template
1958 context = { 1940 context = {
@@ -1961,7 +1943,7 @@ if True:
1961 # TODO: common objects for all table views, adapt as needed 1943 # TODO: common objects for all table views, adapt as needed
1962 'objects' : build_info, 1944 'objects' : build_info,
1963 'objectname' : "builds", 1945 'objectname' : "builds",
1964 'default_orderby' : 'updated:-', 1946 'default_orderby' : default_orderby,
1965 'fstypes' : fstypes_map, 1947 'fstypes' : fstypes_map,
1966 'search_term' : search_term, 1948 'search_term' : search_term,
1967 'total_count' : queryset_with_search.count(), 1949 'total_count' : queryset_with_search.count(),
@@ -1971,151 +1953,137 @@ if True:
1971 {'name': 'Outcome', # column with a single filter 1953 {'name': 'Outcome', # column with a single filter
1972 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content 1954 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content
1973 'dclass' : "span2", # indication about column width; comes from the design 1955 'dclass' : "span2", # indication about column width; comes from the design
1974 'orderfield': _get_toggle_order(request, "state"), # adds ordering by the field value; default ascending unless clicked from ascending into descending 1956 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending
1975 'ordericon':_get_toggle_order_icon(request, "state"), 1957 'ordericon':_get_toggle_order_icon(request, "outcome"),
1976 # filter field will set a filter on that column with the specs in the filter description 1958 # filter field will set a filter on that column with the specs in the filter description
1977 # the class field in the filter has no relation with clclass; the control different aspects of the UI 1959 # the class field in the filter has no relation with clclass; the control different aspects of the UI
1978 # still, it is recommended for the values to be identical for easy tracking in the generated HTML 1960 # still, it is recommended for the values to be identical for easy tracking in the generated HTML
1979 'filter' : {'class' : 'outcome', 1961 'filter' : {'class' : 'outcome',
1980 'label': 'Show:', 1962 'label': 'Show:',
1981 'options' : [ 1963 'options' : [
1982 ('Successful builds', 'build__outcome:' + str(Build.SUCCEEDED), queryset_all.filter(build__outcome = Build.SUCCEEDED).count()), # this is the field search expression 1964 ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression
1983 ('Failed builds', 'build__outcome:NOT'+ str(Build.SUCCEEDED), queryset_all.exclude(build__outcome = Build.SUCCEEDED).count()), 1965 ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()),
1984 ] 1966 ]
1985 } 1967 }
1986 }, 1968 },
1987 {'name': 'Recipe', # default column, disabled box, with just the name in the list 1969 {'name': 'Recipe', # default column, disabled box, with just the name in the list
1988 'qhelp': "What you built (i.e. one or more recipes or image recipes)", 1970 'qhelp': "What you built (i.e. one or more recipes or image recipes)",
1989 'orderfield': _get_toggle_order(request, "brtarget__target"), 1971 'orderfield': _get_toggle_order(request, "target__target"),
1990 'ordericon':_get_toggle_order_icon(request, "brtarget__target"), 1972 'ordericon':_get_toggle_order_icon(request, "target__target"),
1991 }, 1973 },
1992 {'name': 'Machine', 1974 {'name': 'Machine',
1993 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe", 1975 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe",
1994 'orderfield': _get_toggle_order(request, "build__machine"), 1976 'orderfield': _get_toggle_order(request, "machine"),
1995 'ordericon':_get_toggle_order_icon(request, "build__machine"), 1977 'ordericon':_get_toggle_order_icon(request, "machine"),
1996 'dclass': 'span3' 1978 'dclass': 'span3'
1997 }, # a slightly wider column 1979 }, # a slightly wider column
1998 ]
1999 }
2000
2001 if (insert_projects):
2002 context['tablecols'].append(
2003 {'name': 'Project', 'clclass': 'project_column',
2004 }
2005 )
2006
2007 # calculate the exact begining of local today and yesterday
2008 context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'created','updated'})
2009 context.update(context_date)
2010
2011 context['tablecols'].append(
2012 {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column 1980 {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
2013 'qhelp': "The date and time you started the build", 1981 'qhelp': "The date and time you started the build",
2014 'orderfield': _get_toggle_order(request, "created", True), 1982 'orderfield': _get_toggle_order(request, "started_on", True),
2015 'ordericon':_get_toggle_order_icon(request, "created"), 1983 'ordericon':_get_toggle_order_icon(request, "started_on"),
2016 'filter' : {'class' : 'created', 1984 'orderkey' : "started_on",
1985 'filter' : {'class' : 'started_on',
2017 'label': 'Show:', 1986 'label': 'Show:',
2018 'options' : [ 1987 'options' : [
2019 ("Today's builds" , 'created__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(created__gte=today_begin).count()), 1988 ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()),
2020 ("Yesterday's builds", 1989 ("Yesterday's builds",
2021 'created__gte!created__lt:' 1990 'started_on__gte!started_on__lt:'
2022 +yesterday_begin.strftime("%Y-%m-%d")+'!' 1991 +yesterday_begin.strftime("%Y-%m-%d")+'!'
2023 +today_begin.strftime("%Y-%m-%d"), 1992 +today_begin.strftime("%Y-%m-%d"),
2024 queryset_all.filter( 1993 queryset_all.filter(
2025 created__gte=yesterday_begin, 1994 started_on__gte=yesterday_begin,
2026 created__lt=today_begin 1995 started_on__lt=today_begin
2027 ).count()), 1996 ).count()),
2028 ("Build date range", 'daterange', 1, '', 'created'), 1997 ("Build date range", 'daterange', 1, '', 'started_on'),
2029 ] 1998 ]
2030 } 1999 }
2031 } 2000 },
2032 )
2033 context['tablecols'].append(
2034 {'name': 'Completed on', 2001 {'name': 'Completed on',
2035 'qhelp': "The date and time the build finished", 2002 'qhelp': "The date and time the build finished",
2036 'orderfield': _get_toggle_order(request, "updated", True), 2003 'orderfield': _get_toggle_order(request, "completed_on", True),
2037 'ordericon':_get_toggle_order_icon(request, "updated"), 2004 'ordericon':_get_toggle_order_icon(request, "completed_on"),
2038 'orderkey' : 'updated', 2005 'orderkey' : 'completed_on',
2039 'filter' : {'class' : 'updated', 2006 'filter' : {'class' : 'completed_on',
2040 'label': 'Show:', 2007 'label': 'Show:',
2041 'options' : [ 2008 'options' : [
2042 ("Today's builds" , 'updated__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=today_begin).count()), 2009 ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()),
2043 ("Yesterday's builds", 2010 ("Yesterday's builds",
2044 'updated__gte!updated__lt:' 2011 'completed_on__gte!completed_on__lt:'
2045 +yesterday_begin.strftime("%Y-%m-%d")+'!' 2012 +yesterday_begin.strftime("%Y-%m-%d")+'!'
2046 +today_begin.strftime("%Y-%m-%d"), 2013 +today_begin.strftime("%Y-%m-%d"),
2047 queryset_all.filter( 2014 queryset_all.filter(
2048 updated__gte=yesterday_begin, 2015 completed_on__gte=yesterday_begin,
2049 updated__lt=today_begin 2016 completed_on__lt=today_begin
2050 ).count()), 2017 ).count()),
2051 ("Build date range", 'daterange', 1, '', 'updated'), 2018 ("Build date range", 'daterange', 1, '', 'completed_on'),
2052 ] 2019 ]
2053 } 2020 }
2054 } 2021 },
2055 )
2056 context['tablecols'].append(
2057 {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox 2022 {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox
2058 'qhelp': "How many tasks failed during the build", 2023 'qhelp': "How many tasks failed during the build",
2059 'filter' : {'class' : 'failed_tasks', 2024 'filter' : {'class' : 'failed_tasks',
2060 'label': 'Show:', 2025 'label': 'Show:',
2061 'options' : [ 2026 'options' : [
2062 ('Builds with failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED, 2027 ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()),
2063 queryset_all.filter(build__task_build__outcome=Task.OUTCOME_FAILED).count()), 2028 ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()),
2064 ('Builds without failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED,
2065 queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()),
2066 ] 2029 ]
2067 } 2030 }
2068 } 2031 },
2069 )
2070 context['tablecols'].append(
2071 {'name': 'Errors', 'clclass': 'errors_no', 2032 {'name': 'Errors', 'clclass': 'errors_no',
2072 'qhelp': "How many errors were encountered during the build (if any)", 2033 'qhelp': "How many errors were encountered during the build (if any)",
2073 'orderfield': _get_toggle_order(request, "build__errors_no", True), 2034 'orderfield': _get_toggle_order(request, "errors_no", True),
2074 'ordericon':_get_toggle_order_icon(request, "build__errors_no"), 2035 'ordericon':_get_toggle_order_icon(request, "errors_no"),
2075 'orderkey' : 'errors_no', 2036 'orderkey' : 'errors_no',
2076 'filter' : {'class' : 'errors_no', 2037 'filter' : {'class' : 'errors_no',
2077 'label': 'Show:', 2038 'label': 'Show:',
2078 'options' : [ 2039 'options' : [
2079 ('Builds with errors', 'build|build__errors_no__gt:None|0', 2040 ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()),
2080 queryset_all.filter(Q(build=None) | Q(build__errors_no__gt=0)).count()), 2041 ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()),
2081 ('Builds without errors', 'build__errors_no:0',
2082 queryset_all.filter(build__errors_no=0).count()),
2083 ] 2042 ]
2084 } 2043 }
2085 } 2044 },
2086 )
2087 context['tablecols'].append(
2088 {'name': 'Warnings', 'clclass': 'warnings_no', 2045 {'name': 'Warnings', 'clclass': 'warnings_no',
2089 'qhelp': "How many warnings were encountered during the build (if any)", 2046 'qhelp': "How many warnings were encountered during the build (if any)",
2090 'orderfield': _get_toggle_order(request, "build__warnings_no", True), 2047 'orderfield': _get_toggle_order(request, "warnings_no", True),
2091 'ordericon':_get_toggle_order_icon(request, "build__warnings_no"), 2048 'ordericon':_get_toggle_order_icon(request, "warnings_no"),
2092 'orderkey' : 'build__warnings_no', 2049 'orderkey' : 'warnings_no',
2093 'filter' : {'class' : 'build__warnings_no', 2050 'filter' : {'class' : 'warnings_no',
2094 'label': 'Show:', 2051 'label': 'Show:',
2095 'options' : [ 2052 'options' : [
2096 ('Builds with warnings','build__warnings_no__gte:1', queryset_all.filter(build__warnings_no__gte=1).count()), 2053 ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()),
2097 ('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()), 2054 ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()),
2098 ] 2055 ]
2099 } 2056 }
2100 } 2057 },
2101 ) 2058 {'name': 'Log',
2102 context['tablecols'].append( 2059 'dclass': "span4",
2060 'qhelp': "Path to the build main log file",
2061 'clclass': 'log', 'hidden': 1,
2062 'orderfield': _get_toggle_order(request, "cooker_log_path"),
2063 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"),
2064 'orderkey' : 'cooker_log_path',
2065 },
2103 {'name': 'Time', 'clclass': 'time', 'hidden' : 1, 2066 {'name': 'Time', 'clclass': 'time', 'hidden' : 1,
2104 'qhelp': "How long it took the build to finish", 2067 'qhelp': "How long it took the build to finish",
2105# 'orderfield': _get_toggle_order(request, "timespent", True), 2068 'orderfield': _get_toggle_order(request, "timespent", True),
2106# 'ordericon':_get_toggle_order_icon(request, "timespent"), 2069 'ordericon':_get_toggle_order_icon(request, "timespent"),
2107 'orderkey' : 'timespent', 2070 'orderkey' : 'timespent',
2108 } 2071 },
2109 )
2110 context['tablecols'].append(
2111 {'name': 'Image files', 'clclass': 'output', 2072 {'name': 'Image files', 'clclass': 'output',
2112 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", 2073 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory",
2113 # TODO: compute image fstypes from Target_Image_File 2074 # TODO: compute image fstypes from Target_Image_File
2075 },
2076 {'name': 'Project', 'clcalss': 'project_column',
2114 } 2077 }
2115 ) 2078 ]
2079 }
2116 2080
2081 # merge daterange values
2082 context.update(context_date)
2117 return context, pagesize, orderby 2083 return context, pagesize, orderby
2118 2084
2085
2086
2119 # new project 2087 # new project
2120 def newproject(request): 2088 def newproject(request):
2121 template = "newproject.html" 2089 template = "newproject.html"
@@ -2236,7 +2204,7 @@ if True:
2236 "lvs_nos" : Layer_Version.objects.all().count(), 2204 "lvs_nos" : Layer_Version.objects.all().count(),
2237 "completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS), 2205 "completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS),
2238 "prj" : {"name": prj.name, }, 2206 "prj" : {"name": prj.name, },
2239 #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), 2207 "buildrequests" : prj.build_set.filter(outcome=Build.IN_PROGRESS),
2240 "builds" : _project_recent_build_list(prj), 2208 "builds" : _project_recent_build_list(prj),
2241 "layers" : map(lambda x: { 2209 "layers" : map(lambda x: {
2242 "id": x.layercommit.pk, 2210 "id": x.layercommit.pk,
@@ -2607,9 +2575,10 @@ if True:
2607 2575
2608 @_template_renderer('projectbuilds.html') 2576 @_template_renderer('projectbuilds.html')
2609 def projectbuilds(request, pid): 2577 def projectbuilds(request, pid):
2610 # process any build request
2611 prj = Project.objects.get(id = pid) 2578 prj = Project.objects.get(id = pid)
2579
2612 if request.method == "POST": 2580 if request.method == "POST":
2581 # process any build request
2613 2582
2614 if 'buildCancel' in request.POST: 2583 if 'buildCancel' in request.POST:
2615 for i in request.POST['buildCancel'].strip().split(" "): 2584 for i in request.POST['buildCancel'].strip().split(" "):
@@ -2641,10 +2610,10 @@ if True:
2641 br = prj.schedule_build() 2610 br = prj.schedule_build()
2642 2611
2643 2612
2644 buildrequests = BuildRequest.objects.filter(project = prj).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) 2613 queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS)
2645 2614
2646 try: 2615 try:
2647 context, pagesize, orderby = _build_list_helper(request, buildrequests, False) 2616 context, pagesize, orderby = _build_list_helper(request, queryset)
2648 except InvalidRequestException as e: 2617 except InvalidRequestException as e:
2649 raise RedirectException('projectbuilds', request.GET, e.response, pid = pid) 2618 raise RedirectException('projectbuilds', request.GET, e.response, pid = pid)
2650 2619
@@ -2759,12 +2728,6 @@ if True:
2759 } 2728 }
2760 return render(request, "unavailable_artifact.html", context) 2729 return render(request, "unavailable_artifact.html", context)
2761 2730
2762 # This returns the mru object that is needed for the
2763 # managed_mrb_section.html template
2764 def _managed_get_latest_builds():
2765 build_mru = BuildRequest.objects.all()
2766 build_mru = list(build_mru.filter(Q(state__lt=BuildRequest.REQ_COMPLETED) or Q(state=BuildRequest.REQ_DELETED)).order_by("-pk")) + list(build_mru.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])
2767 return build_mru
2768 2731
2769 2732
2770 2733
@@ -2796,7 +2759,7 @@ if True:
2796 p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,)) 2759 p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,))
2797 2760
2798 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) 2761 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
2799 build_mru = _managed_get_latest_builds() 2762 build_mru = _get_latest_builds()
2800 2763
2801 # translate the project's build target strings 2764 # translate the project's build target strings
2802 fstypes_map = {}; 2765 fstypes_map = {};
@@ -2878,202 +2841,8 @@ if True:
2878 return context 2841 return context
2879 2842
2880 @_template_renderer("buildrequestdetails.html") 2843 @_template_renderer("buildrequestdetails.html")
2881 def buildrequestdetails(request, pid, brid): 2844 def buildrequestdetails(request, pid, bid):
2882 context = { 2845 context = {
2883 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) 2846 'buildrequest' : Build.objects.get(pk = bid, project_id = pid).buildrequest
2884 } 2847 }
2885 return context 2848 return context
2886
2887
2888 @_template_renderer('builds.html')
2889 def builds_old(request):
2890 # define here what parameters the view needs in the GET portion in order to
2891 # be able to display something. 'count' and 'page' are mandatory for all views
2892 # that use paginators.
2893 (pagesize, orderby) = _get_parameters_values(request, 10, 'completed_on:-')
2894 mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
2895 retval = _verify_parameters( request.GET, mandatory_parameters )
2896 if retval:
2897 raise RedirectException( 'all-builds', request.GET, mandatory_parameters)
2898
2899 # boilerplate code that takes a request for an object type and returns a queryset
2900 # for that object type. copypasta for all needed table searches
2901 (filter_string, search_term, ordering_string) = _search_tuple(request, Build)
2902 # post-process any date range filters
2903 filter_string,daterange_selected = _modify_date_range_filter(filter_string)
2904 queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS)
2905 queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
2906 queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
2907
2908 # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display
2909 build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
2910
2911 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
2912 build_mru = Build.objects.order_by("-started_on")[:3]
2913
2914 # calculate the exact begining of local today and yesterday, append context
2915 context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'})
2916
2917 # set up list of fstypes for each build
2918 fstypes_map = {};
2919 for build in build_info:
2920 targets = Target.objects.filter( build_id = build.id )
2921 comma = "";
2922 extensions = "";
2923 for t in targets:
2924 if ( not t.is_image ):
2925 continue
2926 tif = Target_Image_File.objects.filter( target_id = t.id )
2927 for i in tif:
2928 s=re.sub('.*tar.bz2', 'tar.bz2', i.file_name)
2929 if s == i.file_name:
2930 s=re.sub('.*\.', '', i.file_name)
2931 if None == re.search(s,extensions):
2932 extensions += comma + s
2933 comma = ", "
2934 fstypes_map[build.id]=extensions
2935
2936 # send the data to the template
2937 context = {
2938 # specific info for
2939 'mru' : build_mru,
2940 # TODO: common objects for all table views, adapt as needed
2941 'objects' : build_info,
2942 'objectname' : "builds",
2943 'default_orderby' : 'completed_on:-',
2944 'fstypes' : fstypes_map,
2945 'search_term' : search_term,
2946 'total_count' : queryset_with_search.count(),
2947 'daterange_selected' : daterange_selected,
2948 # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
2949 'tablecols' : [
2950 {'name': 'Outcome', # column with a single filter
2951 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content
2952 'dclass' : "span2", # indication about column width; comes from the design
2953 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending
2954 'ordericon':_get_toggle_order_icon(request, "outcome"),
2955 # filter field will set a filter on that column with the specs in the filter description
2956 # the class field in the filter has no relation with clclass; the control different aspects of the UI
2957 # still, it is recommended for the values to be identical for easy tracking in the generated HTML
2958 'filter' : {'class' : 'outcome',
2959 'label': 'Show:',
2960 'options' : [
2961 ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression
2962 ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()),
2963 ]
2964 }
2965 },
2966 {'name': 'Recipe', # default column, disabled box, with just the name in the list
2967 'qhelp': "What you built (i.e. one or more recipes or image recipes)",
2968 'orderfield': _get_toggle_order(request, "target__target"),
2969 'ordericon':_get_toggle_order_icon(request, "target__target"),
2970 },
2971 {'name': 'Machine',
2972 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe",
2973 'orderfield': _get_toggle_order(request, "machine"),
2974 'ordericon':_get_toggle_order_icon(request, "machine"),
2975 'dclass': 'span3'
2976 }, # a slightly wider column
2977 {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
2978 'qhelp': "The date and time you started the build",
2979 'orderfield': _get_toggle_order(request, "started_on", True),
2980 'ordericon':_get_toggle_order_icon(request, "started_on"),
2981 'filter' : {'class' : 'started_on',
2982 'label': 'Show:',
2983 'options' : [
2984 ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()),
2985 ("Yesterday's builds",
2986 'started_on__gte!started_on__lt:'
2987 +yesterday_begin.strftime("%Y-%m-%d")+'!'
2988 +today_begin.strftime("%Y-%m-%d"),
2989 queryset_all.filter(
2990 started_on__gte=yesterday_begin,
2991 started_on__lt=today_begin
2992 ).count()),
2993 ("Build date range", 'daterange', 1, '', 'started_on'),
2994 ]
2995 }
2996 },
2997 {'name': 'Completed on',
2998 'qhelp': "The date and time the build finished",
2999 'orderfield': _get_toggle_order(request, "completed_on", True),
3000 'ordericon':_get_toggle_order_icon(request, "completed_on"),
3001 'orderkey' : 'completed_on',
3002 'filter' : {'class' : 'completed_on',
3003 'label': 'Show:',
3004 'options' : [
3005 ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()),
3006 ("Yesterday's builds",
3007 'completed_on__gte!completed_on__lt:'
3008 +yesterday_begin.strftime("%Y-%m-%d")+'!'
3009 +today_begin.strftime("%Y-%m-%d"),
3010 queryset_all.filter(
3011 completed_on__gte=yesterday_begin,
3012 completed_on__lt=today_begin
3013 ).count()),
3014 ("Build date range", 'daterange', 1, '', 'completed_on'),
3015 ]
3016 }
3017 },
3018 {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox
3019 'qhelp': "How many tasks failed during the build",
3020 'filter' : {'class' : 'failed_tasks',
3021 'label': 'Show:',
3022 'options' : [
3023 ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()),
3024 ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()),
3025 ]
3026 }
3027 },
3028 {'name': 'Errors', 'clclass': 'errors_no',
3029 'qhelp': "How many errors were encountered during the build (if any)",
3030 'orderfield': _get_toggle_order(request, "errors_no", True),
3031 'ordericon':_get_toggle_order_icon(request, "errors_no"),
3032 'orderkey' : 'errors_no',
3033 'filter' : {'class' : 'errors_no',
3034 'label': 'Show:',
3035 'options' : [
3036 ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()),
3037 ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()),
3038 ]
3039 }
3040 },
3041 {'name': 'Warnings', 'clclass': 'warnings_no',
3042 'qhelp': "How many warnings were encountered during the build (if any)",
3043 'orderfield': _get_toggle_order(request, "warnings_no", True),
3044 'ordericon':_get_toggle_order_icon(request, "warnings_no"),
3045 'orderkey' : 'warnings_no',
3046 'filter' : {'class' : 'warnings_no',
3047 'label': 'Show:',
3048 'options' : [
3049 ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()),
3050 ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()),
3051 ]
3052 }
3053 },
3054 {'name': 'Log',
3055 'dclass': "span4",
3056 'qhelp': "Path to the build main log file",
3057 'clclass': 'log', 'hidden': 1,
3058 'orderfield': _get_toggle_order(request, "cooker_log_path"),
3059 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"),
3060 'orderkey' : 'cooker_log_path',
3061 },
3062 {'name': 'Time', 'clclass': 'time', 'hidden' : 1,
3063 'qhelp': "How long it took the build to finish",
3064 'orderfield': _get_toggle_order(request, "timespent", True),
3065 'ordericon':_get_toggle_order_icon(request, "timespent"),
3066 'orderkey' : 'timespent',
3067 },
3068 {'name': 'Image files', 'clclass': 'output',
3069 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory",
3070 # TODO: compute image fstypes from Target_Image_File
3071 },
3072 ]
3073 }
3074
3075 # merge daterange values
3076 context.update(context_date)
3077 _set_parameters_values(pagesize, orderby, request)
3078
3079 return context