diff options
| -rw-r--r-- | bitbake/lib/toaster/toastergui/static/js/projectapp.js | 14 | ||||
| -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.html | 167 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/managed_mrb_section.html | 193 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/mrb_section.html | 41 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/project.html | 40 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/projectbuilds.html | 1 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/projects.html | 22 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/urls.py | 2 | ||||
| -rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 437 |
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 | <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 | |||
| 170 | function 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 | |||
| 77 | function 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; | ||
| 61 | def _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 | ||
| 59 | def _project_recent_build_list(prj): | 71 | def _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 | ||
