summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorAlexandru DAMIAN <alexandru.damian@intel.com>2015-01-23 17:36:14 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-01-29 22:11:35 +0000
commit4f7182775cfa39c589e2e4693b1769127d7dd4d4 (patch)
treef62dfeba0e4a021fcbcb64c05e873b9da3458921 /bitbake
parentfefef50e5474da740f926ef635676c4d5f24b9b7 (diff)
downloadpoky-4f7182775cfa39c589e2e4693b1769127d7dd4d4.tar.gz
bitbake: toastergui: update project build listing
We update the build listings in the project mode to enable proper display and selection of build requests that do not have an actual build object because the bitbake process did not start. We add a page to display error details for build requests that did not start a build. Fixing errors and misspelling in build sections. Sorting by "timespent" is disabled for build-listing pages. [YOCTO #7165] [YOCTO #7156] [YOCTO #7196] [YOCTO #7188] (Bitbake rev: ee13bf45cecd6a0132d724b3206a6f4515669496) Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/toaster/bldcontrol/models.py9
-rw-r--r--bitbake/lib/toaster/toastergui/templates/buildrequestdetails.html67
-rw-r--r--bitbake/lib/toaster/toastergui/templates/managed_builds.html18
-rw-r--r--bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html42
-rw-r--r--bitbake/lib/toaster/toastergui/templates/projectbuilds.html20
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py2
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py103
7 files changed, 193 insertions, 68 deletions
diff --git a/bitbake/lib/toaster/bldcontrol/models.py b/bitbake/lib/toaster/bldcontrol/models.py
index 2386d2345a..dc4afca2f7 100644
--- a/bitbake/lib/toaster/bldcontrol/models.py
+++ b/bitbake/lib/toaster/bldcontrol/models.py
@@ -113,6 +113,15 @@ class BuildRequest(models.Model):
113 created = models.DateTimeField(auto_now_add = True) 113 created = models.DateTimeField(auto_now_add = True)
114 updated = models.DateTimeField(auto_now = True) 114 updated = models.DateTimeField(auto_now = True)
115 115
116 def get_duration(self):
117 return (self.updated - self.created).total_seconds()
118
119 def get_sorted_target_list(self):
120 tgts = self.brtarget_set.order_by( 'target' );
121 return( tgts );
122
123 def get_machine(self):
124 return self.brvariable_set.get(name="MACHINE").value
116 125
117# These tables specify the settings for running an actual build. 126# These tables specify the settings for running an actual build.
118# They MUST be kept in sync with the tables in orm.models.Project* 127# They MUST be kept in sync with the tables in orm.models.Project*
diff --git a/bitbake/lib/toaster/toastergui/templates/buildrequestdetails.html b/bitbake/lib/toaster/toastergui/templates/buildrequestdetails.html
new file mode 100644
index 0000000000..2a4571f42e
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/buildrequestdetails.html
@@ -0,0 +1,67 @@
1{% extends "baseprojectpage.html" %}
2
3{% load static %}
4{% load projecttags %}
5{% load humanize %}
6
7{% block localbreadcrumb %}
8<li> {{buildrequest.get_sorted_target_list.0.target}} {%if buildrequest.brtarget_set.all.count > 1%}(+ {{buildrequest.brtarget_set.all.count|add:"-1"}}){%endif%} {{buildrequest.get_machine}} ({{buildrequest.updated|date:"d/m/y H:i"}}) </li>
9{% endblock %}
10
11{% block projectinfomain %}
12 <!-- begin content -->
13
14 <div class="row-fluid">
15
16 <!-- end left sidebar container -->
17 <!-- Begin right container -->
18 <div class="span10">
19 <div class="page-header">
20 <h1>
21 <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%} {{buildrequest.get_machine}} </span>
22
23 </h1>
24 </div>
25 <div class="alert alert-error">
26 <p class="lead">
27 <strong>Failed</strong>
28 on {{ buildrequest.updated|date:'d/m/y H:i' }}
29 with
30
31 <i class="icon-minus-sign error" style="margin-left:6px;"></i>
32 <strong><a class="error accordion-toggle toggle-errors" href="#errors">
33 {{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}
34 </a></strong>
35 <span class="pull-right">Build time: {{buildrequest.get_duration|sectohms}}</span>
36 </p>
37 </div>
38
39 <div class="accordion" id="errors" name="errors">
40 <div class="accordion-group">
41 <div class="accordion-heading">
42 <a class="accordion-toggle error toggle-errors">
43 <h2>
44 <i class="icon-minus-sign"></i>
45 {{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}
46 </h2>
47 </a>
48 </div>
49 <div class="accordion-body collapse in" id="collapse-errors">
50 <div class="accordion-inner">
51 <div class="span10">
52 {% for error in buildrequest.brerror_set.all %}
53 <div class="alert alert-error">
54 ERROR: <div class="air well"><pre>{{error.errmsg}}</pre></div>
55 </div>
56 {% endfor %}
57 </div>
58 </div>
59 </div>
60
61 </div>
62 </div>
63 </div>
64 </div> <!-- end of row-fluid -->
65
66
67{%endblock%}
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_builds.html b/bitbake/lib/toaster/toastergui/templates/managed_builds.html
index 5944dc4747..183be760ae 100644
--- a/bitbake/lib/toaster/toastergui/templates/managed_builds.html
+++ b/bitbake/lib/toaster/toastergui/templates/managed_builds.html
@@ -35,10 +35,10 @@
35 </div> 35 </div>
36 36
37 37
38 {% else %} 38 {% else %} {# We have builds to display #}
39 {% include "basetable_top_buildprojects.html" %} 39 {% include "basetable_top_buildprojects.html" %}
40 <!-- 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 --> 40 <!-- 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 -->
41 {% for br in objects %}{% if br.build %} {% with build=br.build %} {# if we have a build, just display it #} 41 {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #}
42 <tr class="data"> 42 <tr class="data">
43 <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></td> 43 <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></td>
44 <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> 44 <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td>
@@ -61,7 +61,7 @@
61 <td class="errors_no"> 61 <td class="errors_no">
62 {% if build.errors_no %} 62 {% if build.errors_no %}
63 <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a> 63 <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
64 {% if MANAGED and build.project %} 64 {% if MANAGED and build.project and build.buildartifact_set.count %}
65 <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> 65 <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
66 <i class="icon-download-alt" title="" data-original-title="Download build log"></i> 66 <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
67 </a> 67 </a>
@@ -96,21 +96,21 @@
96 <tr class="data"> 96 <tr class="data">
97 <td class="outcome">{% if buildrequest.state == buildrequest.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td> 97 <td class="outcome">{% if buildrequest.state == buildrequest.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td>
98 <td class="target"> 98 <td class="target">
99 <span data-toggle="tooltip" {%if br.brtarget_set.all.count > 1%}title="Targets: {%for target in br.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{br.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} </span> 99 <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>
100 </td> 100 </td>
101 <td class="machine"> 101 <td class="machine">
102 {{br.machine}} 102 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.machine}}</a>
103 </td> 103 </td>
104 <td class="started_on"> 104 <td class="started_on">
105 {{br.created|date:"d/m/y H:i"}} 105 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.created|date:"d/m/y H:i"}}</a>
106 </td> 106 </td>
107 <td class="completed_on"> 107 <td class="completed_on">
108 {{br.updated|date:"d/m/y H:i"}} 108 <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.updated|date:"d/m/y H:i"}}</a>
109 </td> 109 </td>
110 <td class="failed_tasks error"> 110 <td class="failed_tasks error">
111 {{br.brerror_set.all.0.errmsg|whitespace_slice:":32"}}
112 </td> 111 </td>
113 <td class="errors_no"> 112 <td class="errors_no">
113 <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>
114 </td> 114 </td>
115 <td class="warnings_no"> 115 <td class="warnings_no">
116 </td> 116 </td>
@@ -120,7 +120,7 @@
120 <td class="output"> {# we have no output here #} 120 <td class="output"> {# we have no output here #}
121 </td> 121 </td>
122 <td class="project"> 122 <td class="project">
123 <a href="{% url 'project' br.project.id %}">{{br.project.name}}</a> 123 <a href="{% url 'project' buildrequest.project.id %}">{{buildrequest.project.name}}</a>
124 </td> 124 </td>
125 </tr> 125 </tr>
126 {%endif%} 126 {%endif%}
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html b/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html
index da5a3f7f74..d2ffdcdc3d 100644
--- a/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html
+++ b/bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html
@@ -26,7 +26,7 @@
26 <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}"> 26 <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
27 {% endif %} 27 {% endif %}
28 <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%} 28 <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%}
29 </span> 29 </span>
30 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %} 30 {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
31 </a> 31 </a>
32 {% endif %} 32 {% endif %}
@@ -71,24 +71,42 @@
71 71
72 {% else %} {# we use the project's page recent build design #} 72 {% else %} {# we use the project's page recent build design #}
73 73
74 <div class="alert {% if buildrequest.state == buildrequest.REQ_FAILED %}alert-error{% else %}alert-info{% endif %}">
75 <div class="row-fluid">
76 74
77 75
76
77 <div class="alert {% if buildrequest.state == buildrequest.REQ_FAILED %}alert-error{% else %}alert-info{% endif %} project-name">
78 <span class="label label-danger"> {{buildrequest.project.name}} </span>
79 <div class="row-fluid">
80
78 {% if buildrequest.state == buildrequest.REQ_FAILED %} 81 {% if buildrequest.state == buildrequest.REQ_FAILED %}
79 <div class="lead span3"> 82 <div class="span3 lead">
80 <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> 83 <a href="{%url 'buildrequestdetails' buildrequest.project.id buildrequest.pk%}" class="error">
84 <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>
85 </a>
86 </div>
87 <div class="span2 lead">
88 {% if buildrequest.updated|format_build_date %}
89 {{ buildrequest.updated|date:'d/m/y H:i' }}
90 {% else %}
91 {{ buildrequest.updated|date:'H:i' }}
92 {% endif %}
93 </div>
94 <div class="span2 lead">
95 {% if buildrequest.brerror_set.all.count %}
96 <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>
97 {% endif %}
81 </div> 98 </div>
82 <div > 99 <div class="span2 lead"> {# there are no warnings for buildrequests #}
83 </div> 100 </div>
84 <div class="row-fluid"> 101 <div class="lead ">
85 {% for e in buildrequest.brerror_set.all|slice:":3" %} 102 <span class="lead{%if not MANAGED or not buildrequest.project%} pull-right{%endif%}">
86 <div class="air well"> 103 Build time: <a href="{% url 'buildrequestdetails' buildrequest.project.id buildrequest.pk %}">{{ buildrequest.get_duration|sectohms }}</a>
87 <pre>{{e.errmsg|whitespace_slice:":150"}}</pre> 104 </span>
88 </div> 105 <button class="btn btn-danger pull-right" onclick='scheduleBuild({% url 'xhr_projectbuild' buildrequest.project.id as bpi%}{{bpi|json}}, {{buildrequest.project.name|json}}, {{buildrequest.get_sorted_target_list|mapselect:'target'|json}})'>Run again</button>
89 {% endfor %} 106
90 </div> 107 </div>
91 108
109
92 {% elif buildrequest.state == buildrequest.REQ_QUEUED %} 110 {% elif buildrequest.state == buildrequest.REQ_QUEUED %}
93 111
94 <div class="lead span5"> 112 <div class="lead span5">
diff --git a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
index 8f9172c6d5..2a8bd58f34 100644
--- a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
+++ b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html
@@ -13,11 +13,11 @@
13 No builds found 13 No builds found
14 14
15 {% else %} 15 {% else %}
16 {% if request.GET.filter or request.GET.search %} 16 {% if request.GET.filter or request.GET.search %}
17 {{objects.paginator.count}} builds found 17 {{objects.paginator.count}} builds found
18 {% else %} 18 {% else %}
19 Project builds <small>({{objects.paginator.count}})</small> 19 Project builds <small>({{objects.paginator.count}})</small>
20 {% endif %} 20 {% endif %}
21 {% endif %} 21 {% endif %}
22 <i class="icon-question-sign get-help heading-help" title="This page lists all the builds for the current project"></i> 22 <i class="icon-question-sign get-help heading-help" title="This page lists all the builds for the current project"></i>
23 </h1> 23 </h1>
@@ -89,23 +89,23 @@
89 89
90 90
91 <tr class="data"> 91 <tr class="data">
92 <td class="outcome">{% if buildrequest.state == buildrequest.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td> 92 <td class="outcome">{% if br.state == br.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td>
93 <td class="target"> 93 <td class="target">
94 <span data-toggle="tooltip" {%if br.brtarget_set.all.count > 1%}title="Targets: {%for target in br.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{br.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} </span> 94 <a href="{% url "buildrequestdetails" br.project.id br.id %}"><span data-toggle="tooltip" {%if br.brtarget_set.all.count > 1%}title="Targets: {%for target in br.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{br.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} </span></a>
95 </td> 95 </td>
96 <td class="machine"> 96 <td class="machine">
97 {{br.machine}} 97 <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.machine}}</a>
98 </td> 98 </td>
99 <td class="started_on"> 99 <td class="started_on">
100 {{br.created|date:"d/m/y H:i"}} 100 <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.created|date:"d/m/y H:i"}}</a>
101 </td> 101 </td>
102 <td class="completed_on"> 102 <td class="completed_on">
103 {{br.updated|date:"d/m/y H:i"}} 103 <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.updated|date:"d/m/y H:i"}}</a>
104 </td> 104 </td>
105 <td class="failed_tasks error"> 105 <td class="failed_tasks error">
106 {{br.brerror_set.all.0.errmsg|whitespace_slice:":32"}}
107 </td> 106 </td>
108 <td class="errors_no"> 107 <td class="errors_no">
108 <a class="errors_no error" href="{% url "buildrequestdetails" br.project.id br.id %}#errors">{{br.brerror_set.all.count}} error{{br.brerror_set.all.count|pluralize}}</a>
109 </td> 109 </td>
110 <td class="warnings_no"> 110 <td class="warnings_no">
111 </td> 111 </td>
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 8c3b5a85fd..1c83090f58 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -97,6 +97,8 @@ urlpatterns = patterns('toastergui.views',
97 url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'), 97 url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'),
98 url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'), 98 url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'),
99 99
100 # dashboard for failed build requests
101 url(r'^project/(?P<pid>\d+)/buildrequest/(?P<brid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'),
100 102
101 # default redirection 103 # default redirection
102 url(r'^$', RedirectView.as_view( url= 'landing')), 104 url(r'^$', RedirectView.as_view( url= 'landing')),
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index e718ced570..6ccbf5452d 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -22,7 +22,7 @@
22import operator,re 22import operator,re
23import HTMLParser 23import HTMLParser
24 24
25from django.db.models import Q, Sum 25from django.db.models import Q, Sum, Count
26from django.db import IntegrityError 26from django.db import IntegrityError
27from django.shortcuts import render, redirect 27from django.shortcuts import render, redirect
28from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable 28from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
@@ -117,7 +117,8 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
117 return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) 117 return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs)
118 118
119FIELD_SEPARATOR = ":" 119FIELD_SEPARATOR = ":"
120VALUE_SEPARATOR = "!" 120AND_VALUE_SEPARATOR = "!"
121OR_VALUE_SEPARATOR = "|"
121DESCENDING = "-" 122DESCENDING = "-"
122 123
123def __get_q_for_val(name, value): 124def __get_q_for_val(name, value):
@@ -126,20 +127,31 @@ def __get_q_for_val(name, value):
126 if "AND" in value: 127 if "AND" in value:
127 return reduce(operator.and_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("AND") ])) 128 return reduce(operator.and_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("AND") ]))
128 if value.startswith("NOT"): 129 if value.startswith("NOT"):
129 kwargs = { name : value.strip("NOT") } 130 value = value[3:]
131 if value == 'None':
132 value = None
133 kwargs = { name : value }
130 return ~Q(**kwargs) 134 return ~Q(**kwargs)
131 else: 135 else:
136 if value == 'None':
137 value = None
132 kwargs = { name : value } 138 kwargs = { name : value }
133 return Q(**kwargs) 139 return Q(**kwargs)
134 140
135def _get_filtering_query(filter_string): 141def _get_filtering_query(filter_string):
136 142
137 search_terms = filter_string.split(FIELD_SEPARATOR) 143 search_terms = filter_string.split(FIELD_SEPARATOR)
138 keys = search_terms[0].split(VALUE_SEPARATOR) 144 and_keys = search_terms[0].split(AND_VALUE_SEPARATOR)
139 values = search_terms[1].split(VALUE_SEPARATOR) 145 and_values = search_terms[1].split(AND_VALUE_SEPARATOR)
146
147 and_query = []
148 for kv in zip(and_keys, and_values):
149 or_keys = kv[0].split(OR_VALUE_SEPARATOR)
150 or_values = kv[1].split(OR_VALUE_SEPARATOR)
151 querydict = dict(zip(or_keys, or_values))
152 and_query.append(reduce(operator.or_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict])))
140 153
141 querydict = dict(zip(keys, values)) 154 return reduce(operator.and_, [k for k in and_query])
142 return reduce(operator.and_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict]))
143 155
144def _get_toggle_order(request, orderkey, reverse = False): 156def _get_toggle_order(request, orderkey, reverse = False):
145 if reverse: 157 if reverse:
@@ -169,13 +181,13 @@ def _validate_input(input, model):
169 return None, invalid 181 return None, invalid
170 182
171 # Check we have an equal number of terms both sides of the colon 183 # Check we have an equal number of terms both sides of the colon
172 if len(input_list[0].split(VALUE_SEPARATOR)) != len(input_list[1].split(VALUE_SEPARATOR)): 184 if len(input_list[0].split(AND_VALUE_SEPARATOR)) != len(input_list[1].split(AND_VALUE_SEPARATOR)):
173 invalid = "Not all arg names got values" 185 invalid = "Not all arg names got values"
174 return None, invalid + str(input_list) 186 return None, invalid + str(input_list)
175 187
176 # Check we are looking for a valid field 188 # Check we are looking for a valid field
177 valid_fields = model._meta.get_all_field_names() 189 valid_fields = model._meta.get_all_field_names()
178 for field in input_list[0].split(VALUE_SEPARATOR): 190 for field in input_list[0].split(AND_VALUE_SEPARATOR):
179 if not reduce(lambda x, y: x or y, map(lambda x: field.startswith(x), [ x for x in valid_fields ])): 191 if not reduce(lambda x, y: x or y, map(lambda x: field.startswith(x), [ x for x in valid_fields ])):
180 return None, (field, [ x for x in valid_fields ]) 192 return None, (field, [ x for x in valid_fields ])
181 193
@@ -216,6 +228,7 @@ def _search_tuple(request, model):
216def _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''): 228def _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''):
217 if filter_string: 229 if filter_string:
218 filter_query = _get_filtering_query(filter_string) 230 filter_query = _get_filtering_query(filter_string)
231# raise Exception(filter_query)
219 queryset = queryset.filter(filter_query) 232 queryset = queryset.filter(filter_query)
220 else: 233 else:
221 queryset = queryset.all() 234 queryset = queryset.all()
@@ -1780,12 +1793,13 @@ if toastermain.settings.MANAGED:
1780 # for that object type. copypasta for all needed table searches 1793 # for that object type. copypasta for all needed table searches
1781 (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) 1794 (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest)
1782 # we don't display in-progress or deleted builds 1795 # we don't display in-progress or deleted builds
1783 queryset_all = buildrequests 1796 queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED)
1784 queryset_with_search = _get_queryset(BuildRequest, queryset_all, None, search_term, ordering_string, '-updated') 1797 queryset_all = queryset_all.annotate(Count('brerror'))
1785 queryset = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated') 1798 queryset_with_search = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated')
1799
1786 1800
1787 # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display 1801 # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display
1788 build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) 1802 build_info = _build_page_range(Paginator(queryset_with_search, pagesize), request.GET.get('page', 1))
1789 1803
1790 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) 1804 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
1791 # most recent build is like projects' most recent builds, but across all projects 1805 # most recent build is like projects' most recent builds, but across all projects
@@ -1842,8 +1856,8 @@ if toastermain.settings.MANAGED:
1842 'filter' : {'class' : 'outcome', 1856 'filter' : {'class' : 'outcome',
1843 'label': 'Show:', 1857 'label': 'Show:',
1844 'options' : [ 1858 'options' : [
1845 ('Successful builds', 'state:' + str(BuildRequest.REQ_COMPLETED), queryset_with_search.filter(state=str(BuildRequest.REQ_COMPLETED)).count()), # this is the field search expression 1859 ('Successful builds', 'state:' + str(BuildRequest.REQ_COMPLETED), queryset_all.filter(state=str(BuildRequest.REQ_COMPLETED)).count()), # this is the field search expression
1846 ('Failed builds', 'state:'+ str(BuildRequest.REQ_FAILED), queryset_with_search.filter(state=str(BuildRequest.REQ_FAILED)).count()), 1860 ('Failed builds', 'state:'+ str(BuildRequest.REQ_FAILED), queryset_all.filter(state=str(BuildRequest.REQ_FAILED)).count()),
1847 ] 1861 ]
1848 } 1862 }
1849 }, 1863 },
@@ -1865,9 +1879,9 @@ if toastermain.settings.MANAGED:
1865 'filter' : {'class' : 'created', 1879 'filter' : {'class' : 'created',
1866 'label': 'Show:', 1880 'label': 'Show:',
1867 'options' : [ 1881 'options' : [
1868 ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=timezone.now()).count()), 1882 ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(created__gte=timezone.now()).count()),
1869 ("Yesterday's builds", 'created__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=(timezone.now()-timedelta(hours=24))).count()), 1883 ("Yesterday's builds", 'created__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_all.filter(created__gte=(timezone.now()-timedelta(hours=24))).count()),
1870 ("This week's builds", 'created__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=(timezone.now()-timedelta(days=7))).count()), 1884 ("This week's builds", 'created__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(created__gte=(timezone.now()-timedelta(days=7))).count()),
1871 ] 1885 ]
1872 } 1886 }
1873 }, 1887 },
@@ -1879,9 +1893,9 @@ if toastermain.settings.MANAGED:
1879 'filter' : {'class' : 'updated', 1893 'filter' : {'class' : 'updated',
1880 'label': 'Show:', 1894 'label': 'Show:',
1881 'options' : [ 1895 'options' : [
1882 ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=timezone.now()).count()), 1896 ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=timezone.now()).count()),
1883 ("Yesterday's builds", 'updated__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=(timezone.now()-timedelta(hours=24))).count()), 1897 ("Yesterday's builds", 'updated__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=(timezone.now()-timedelta(hours=24))).count()),
1884 ("This week's builds", 'updated__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=(timezone.now()-timedelta(days=7))).count()), 1898 ("This week's builds", 'updated__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=(timezone.now()-timedelta(days=7))).count()),
1885 ] 1899 ]
1886 } 1900 }
1887 }, 1901 },
@@ -1890,8 +1904,10 @@ if toastermain.settings.MANAGED:
1890 'filter' : {'class' : 'failed_tasks', 1904 'filter' : {'class' : 'failed_tasks',
1891 'label': 'Show:', 1905 'label': 'Show:',
1892 'options' : [ 1906 'options' : [
1893 ('Build with failed tasks', 'build__task_build__outcome:4', queryset_with_search.filter(build__task_build__outcome=4).count()), 1907 ('Builds with failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED,
1894 ('Build without failed tasks', 'build__task_build__outcome:NOT4', queryset_with_search.filter(~Q(build__task_build__outcome=4)).count()), 1908 queryset_all.filter(build__task_build__outcome=Task.OUTCOME_FAILED).count()),
1909 ('Builds without failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED,
1910 queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()),
1895 ] 1911 ]
1896 } 1912 }
1897 }, 1913 },
@@ -1903,8 +1919,10 @@ if toastermain.settings.MANAGED:
1903 'filter' : {'class' : 'errors_no', 1919 'filter' : {'class' : 'errors_no',
1904 'label': 'Show:', 1920 'label': 'Show:',
1905 'options' : [ 1921 'options' : [
1906 ('Build with errors', 'build__errors_no__gte:1', queryset_with_search.filter(build__errors_no__gte=1).count()), 1922 ('Builds with errors', 'build|build__errors_no__gt:None|0',
1907 ('Build without errors', 'build__errors_no:0', queryset_with_search.filter(build__errors_no=0).count()), 1923 queryset_all.filter(Q(build=None) | Q(build__errors_no__gt=0)).count()),
1924 ('Builds without errors', 'build__errors_no:0',
1925 queryset_all.filter(build__errors_no=0).count()),
1908 ] 1926 ]
1909 } 1927 }
1910 }, 1928 },
@@ -1916,8 +1934,8 @@ if toastermain.settings.MANAGED:
1916 'filter' : {'class' : 'build__warnings_no', 1934 'filter' : {'class' : 'build__warnings_no',
1917 'label': 'Show:', 1935 'label': 'Show:',
1918 'options' : [ 1936 'options' : [
1919 ('Build with warnings','build__warnings_no__gte:1', queryset_with_search.filter(build__warnings_no__gte=1).count()), 1937 ('Builds with warnings','build__warnings_no__gte:1', queryset_all.filter(build__warnings_no__gte=1).count()),
1920 ('Build without warnings','build__warnings_no:0', queryset_with_search.filter(build__warnings_no=0).count()), 1938 ('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()),
1921 ] 1939 ]
1922 } 1940 }
1923 }, 1941 },
@@ -2016,7 +2034,7 @@ if toastermain.settings.MANAGED:
2016 2034
2017 context = { 2035 context = {
2018 "project" : prj, 2036 "project" : prj,
2019 "completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS), 2037 "completedbuilds": BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED),
2020 "prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}, 2038 "prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}},
2021 #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), 2039 #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
2022 "builds" : _project_recent_build_list(prj), 2040 "builds" : _project_recent_build_list(prj),
@@ -2061,7 +2079,7 @@ if toastermain.settings.MANAGED:
2061 try: 2079 try:
2062 if request.method != "POST": 2080 if request.method != "POST":
2063 raise BadParameterException("invalid method") 2081 raise BadParameterException("invalid method")
2064 request.session['project_id'] = pid 2082 request.session['project_id'] = pid
2065 prj = Project.objects.get(id = pid) 2083 prj = Project.objects.get(id = pid)
2066 2084
2067 2085
@@ -2167,11 +2185,11 @@ if toastermain.settings.MANAGED:
2167 try: 2185 try:
2168 prj = None 2186 prj = None
2169 if request.GET.has_key('project_id'): 2187 if request.GET.has_key('project_id'):
2170 prj = Project.objects.get(pk = request.GET['project_id']) 2188 prj = Project.objects.get(pk = request.GET['project_id'])
2171 elif 'project_id' in request.session: 2189 elif 'project_id' in request.session:
2172 prj = Project.objects.get(pk = request.session['project_id']) 2190 prj = Project.objects.get(pk = request.session['project_id'])
2173 else: 2191 else:
2174 raise Exception("No valid project selected") 2192 raise Exception("No valid project selected")
2175 2193
2176 2194
2177 def _lv_to_dict(x): 2195 def _lv_to_dict(x):
@@ -2819,10 +2837,10 @@ if toastermain.settings.MANAGED:
2819 2837
2820 vars_blacklist = { 2838 vars_blacklist = {
2821 'DL_DR','PARALLEL_MAKE','BB_NUMBER_THREADS','SSTATE_DIR', 2839 'DL_DR','PARALLEL_MAKE','BB_NUMBER_THREADS','SSTATE_DIR',
2822 'BB_DISKMON_DIRS','BB_NUMBER_THREADS','CVS_PROXY_HOST','CVS_PROXY_PORT', 2840 'BB_DISKMON_DIRS','BB_NUMBER_THREADS','CVS_PROXY_HOST','CVS_PROXY_PORT',
2823 'DL_DIR','PARALLEL_MAKE','SSTATE_DIR','SSTATE_DIR','SSTATE_MIRRORS','TMPDIR', 2841 'DL_DIR','PARALLEL_MAKE','SSTATE_DIR','SSTATE_DIR','SSTATE_MIRRORS','TMPDIR',
2824 'all_proxy','ftp_proxy','http_proxy ','https_proxy' 2842 'all_proxy','ftp_proxy','http_proxy ','https_proxy'
2825 } 2843 }
2826 2844
2827 vars_fstypes = { 2845 vars_fstypes = {
2828 'btrfs','cpio','cpio.gz','cpio.lz4','cpio.lzma','cpio.xz','cramfs', 2846 'btrfs','cpio','cpio.gz','cpio.lz4','cpio.lzma','cpio.xz','cramfs',
@@ -2874,7 +2892,7 @@ if toastermain.settings.MANAGED:
2874 2892
2875 def projectbuilds(request, pid): 2893 def projectbuilds(request, pid):
2876 template = 'projectbuilds.html' 2894 template = 'projectbuilds.html'
2877 buildrequests = BuildRequest.objects.exclude(project_id = pid, state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) 2895 buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED)
2878 2896
2879 try: 2897 try:
2880 context, pagesize, orderby = _build_list_helper(request, buildrequests) 2898 context, pagesize, orderby = _build_list_helper(request, buildrequests)
@@ -3012,6 +3030,14 @@ if toastermain.settings.MANAGED:
3012 } 3030 }
3013 return render(request, template, context) 3031 return render(request, template, context)
3014 3032
3033 def buildrequestdetails(request, pid, brid):
3034 template = "buildrequestdetails.html"
3035 context = {
3036 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid)
3037 }
3038 return render(request, template, context)
3039
3040
3015else: 3041else:
3016 # these are pages that are NOT available in interactive mode 3042 # these are pages that are NOT available in interactive mode
3017 def managedcontextprocessor(request): 3043 def managedcontextprocessor(request):
@@ -3256,3 +3282,6 @@ else:
3256 3282
3257 def xhr_updatelayer(request): 3283 def xhr_updatelayer(request):
3258 raise Exception("page not available in interactive mode") 3284 raise Exception("page not available in interactive mode")
3285
3286 def buildrequestdetails(request, pid, brid):
3287 raise Exception("page not available in interactive mode")