diff options
Diffstat (limited to 'bitbake/lib/toaster')
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 @@ | |||
22 | import operator,re | 22 | import operator,re |
23 | import HTMLParser | 23 | import HTMLParser |
24 | 24 | ||
25 | from django.db.models import Q, Sum | 25 | from django.db.models import Q, Sum, Count |
26 | from django.db import IntegrityError | 26 | from django.db import IntegrityError |
27 | from django.shortcuts import render, redirect | 27 | from django.shortcuts import render, redirect |
28 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable | 28 | from 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 | ||
119 | FIELD_SEPARATOR = ":" | 119 | FIELD_SEPARATOR = ":" |
120 | VALUE_SEPARATOR = "!" | 120 | AND_VALUE_SEPARATOR = "!" |
121 | OR_VALUE_SEPARATOR = "|" | ||
121 | DESCENDING = "-" | 122 | DESCENDING = "-" |
122 | 123 | ||
123 | def __get_q_for_val(name, value): | 124 | def __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 | ||
135 | def _get_filtering_query(filter_string): | 141 | def _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 | ||
144 | def _get_toggle_order(request, orderkey, reverse = False): | 156 | def _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): | |||
216 | def _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''): | 228 | def _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 | |||
3015 | else: | 3041 | else: |
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") | ||