Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | # |
| 2 | # ex:ts=4:sw=4:sts=4:et |
| 3 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- |
| 4 | # |
| 5 | # BitBake Toaster Implementation |
| 6 | # |
| 7 | # Copyright (C) 2015 Intel Corporation |
| 8 | # |
| 9 | # This program is free software; you can redistribute it and/or modify |
| 10 | # it under the terms of the GNU General Public License version 2 as |
| 11 | # published by the Free Software Foundation. |
| 12 | # |
| 13 | # This program is distributed in the hope that it will be useful, |
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | # GNU General Public License for more details. |
| 17 | # |
| 18 | # You should have received a copy of the GNU General Public License along |
| 19 | # with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 21 | |
| 22 | from toastergui.widgets import ToasterTable |
| 23 | from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 24 | from orm.models import CustomImageRecipe, Package, Target, Build, LogMessage, Task |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 25 | from orm.models import CustomImagePackage, Package_DependencyManager |
Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 26 | from orm.models import Distro |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 27 | from django.db.models import Q, Max, Sum, Count, When, Case, Value, IntegerField |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 28 | from django.conf.urls import url |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 29 | from django.core.urlresolvers import reverse, resolve |
| 30 | from django.http import HttpResponse |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 31 | from django.views.generic import TemplateView |
| 32 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 33 | from toastergui.tablefilter import TableFilter |
| 34 | from toastergui.tablefilter import TableFilterActionToggle |
| 35 | from toastergui.tablefilter import TableFilterActionDateRange |
| 36 | from toastergui.tablefilter import TableFilterActionDay |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 37 | |
Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 38 | import os |
| 39 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 40 | class ProjectFilters(object): |
| 41 | @staticmethod |
| 42 | def in_project(project_layers): |
| 43 | return Q(layer_version__in=project_layers) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 44 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 45 | @staticmethod |
| 46 | def not_in_project(project_layers): |
| 47 | return ~(ProjectFilters.in_project(project_layers)) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 48 | |
| 49 | class LayersTable(ToasterTable): |
| 50 | """Table of layers in Toaster""" |
| 51 | |
| 52 | def __init__(self, *args, **kwargs): |
| 53 | super(LayersTable, self).__init__(*args, **kwargs) |
| 54 | self.default_orderby = "layer__name" |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 55 | self.title = "Compatible layers" |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 56 | |
| 57 | def get_context_data(self, **kwargs): |
| 58 | context = super(LayersTable, self).get_context_data(**kwargs) |
| 59 | |
| 60 | project = Project.objects.get(pk=kwargs['pid']) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 61 | context['project'] = project |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 62 | |
| 63 | return context |
| 64 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 65 | def setup_filters(self, *args, **kwargs): |
| 66 | project = Project.objects.get(pk=kwargs['pid']) |
| 67 | self.project_layers = ProjectLayer.objects.filter(project=project) |
| 68 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 69 | in_current_project_filter = TableFilter( |
| 70 | "in_current_project", |
| 71 | "Filter by project layers" |
| 72 | ) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 73 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 74 | criteria = Q(projectlayer__in=self.project_layers) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 75 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 76 | in_project_action = TableFilterActionToggle( |
| 77 | "in_project", |
| 78 | "Layers added to this project", |
| 79 | criteria |
| 80 | ) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 81 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 82 | not_in_project_action = TableFilterActionToggle( |
| 83 | "not_in_project", |
| 84 | "Layers not added to this project", |
| 85 | ~criteria |
| 86 | ) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 87 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 88 | in_current_project_filter.add_action(in_project_action) |
| 89 | in_current_project_filter.add_action(not_in_project_action) |
| 90 | self.add_filter(in_current_project_filter) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 91 | |
| 92 | def setup_queryset(self, *args, **kwargs): |
| 93 | prj = Project.objects.get(pk = kwargs['pid']) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 94 | compatible_layers = prj.get_all_compatible_layer_versions() |
| 95 | |
| 96 | self.static_context_extra['current_layers'] = \ |
| 97 | prj.get_project_layer_versions(pk=True) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 98 | |
| 99 | self.queryset = compatible_layers.order_by(self.default_orderby) |
| 100 | |
| 101 | def setup_columns(self, *args, **kwargs): |
| 102 | |
| 103 | layer_link_template = ''' |
| 104 | <a href="{% url 'layerdetails' extra.pid data.id %}"> |
| 105 | {{data.layer.name}} |
| 106 | </a> |
| 107 | ''' |
| 108 | |
| 109 | self.add_column(title="Layer", |
| 110 | hideable=False, |
| 111 | orderable=True, |
| 112 | static_data_name="layer__name", |
| 113 | static_data_template=layer_link_template) |
| 114 | |
| 115 | self.add_column(title="Summary", |
| 116 | field_name="layer__summary") |
| 117 | |
| 118 | git_url_template = ''' |
| 119 | <a href="{% url 'layerdetails' extra.pid data.id %}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 120 | {% if data.layer.local_source_dir %} |
| 121 | <code>{{data.layer.local_source_dir}}</code> |
| 122 | {% else %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 123 | <code>{{data.layer.vcs_url}}</code> |
| 124 | </a> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 125 | {% endif %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 126 | {% if data.get_vcs_link_url %} |
| 127 | <a target="_blank" href="{{ data.get_vcs_link_url }}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 128 | <span class="glyphicon glyphicon-new-window"></span> |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 129 | </a> |
| 130 | {% endif %} |
| 131 | ''' |
| 132 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 133 | self.add_column(title="Layer source code location", |
| 134 | help_text="A Git repository or an absolute path to a directory", |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 135 | hidden=True, |
| 136 | static_data_name="layer__vcs_url", |
| 137 | static_data_template=git_url_template) |
| 138 | |
| 139 | git_dir_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 140 | {% if data.layer.local_source_dir %} |
| 141 | <span class="text-muted">Not applicable</span> |
| 142 | <span class="glyphicon glyphicon-question-sign get-help" data-original-title="" title="The source code of {{data.layer.name}} is not in a Git repository, so there is no subdirectory associated with it"> </span> |
| 143 | {% else %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 144 | <a href="{% url 'layerdetails' extra.pid data.id %}"> |
| 145 | <code>{{data.dirpath}}</code> |
| 146 | </a> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 147 | {% endif %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 148 | {% if data.dirpath and data.get_vcs_dirpath_link_url %} |
| 149 | <a target="_blank" href="{{ data.get_vcs_dirpath_link_url }}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 150 | <span class="glyphicon glyphicon-new-window"></span> |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 151 | </a> |
| 152 | {% endif %}''' |
| 153 | |
| 154 | self.add_column(title="Subdirectory", |
| 155 | help_text="The layer directory within the Git repository", |
| 156 | hidden=True, |
| 157 | static_data_name="git_subdir", |
| 158 | static_data_template=git_dir_template) |
| 159 | |
| 160 | revision_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 161 | {% if data.layer.local_source_dir %} |
| 162 | <span class="text-muted">Not applicable</span> |
| 163 | <span class="glyphicon glyphicon-question-sign get-help" data-original-title="" title="The source code of {{data.layer.name}} is not in a Git repository, so there is no revision associated with it"> </span> |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 164 | {% else %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 165 | {% with vcs_ref=data.get_vcs_reference %} |
| 166 | {% include 'snippets/gitrev_popover.html' %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 167 | {% endwith %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 168 | {% endif %} |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 169 | ''' |
| 170 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 171 | self.add_column(title="Git revision", |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 172 | help_text="The Git branch, tag or commit. For the layers from the OpenEmbedded layer source, the revision is always the branch compatible with the Yocto Project version you selected for this project", |
| 173 | static_data_name="revision", |
| 174 | static_data_template=revision_template) |
| 175 | |
| 176 | deps_template = ''' |
| 177 | {% with ods=data.dependencies.all%} |
| 178 | {% if ods.count %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 179 | <a class="btn btn-default" title="<a href='{% url "layerdetails" extra.pid data.id %}'>{{data.layer.name}}</a> dependencies" |
| 180 | data-content="<ul class='list-unstyled'> |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 181 | {% for i in ods%} |
| 182 | <li><a href='{% url "layerdetails" extra.pid i.depends_on.pk %}'>{{i.depends_on.layer.name}}</a></li> |
| 183 | {% endfor %} |
| 184 | </ul>"> |
| 185 | {{ods.count}} |
| 186 | </a> |
| 187 | {% endif %} |
| 188 | {% endwith %} |
| 189 | ''' |
| 190 | |
| 191 | self.add_column(title="Dependencies", |
| 192 | help_text="Other layers a layer depends upon", |
| 193 | static_data_name="dependencies", |
| 194 | static_data_template=deps_template) |
| 195 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 196 | self.add_column(title="Add | Remove", |
| 197 | help_text="Add or remove layers to / from your project", |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 198 | hideable=False, |
| 199 | filter_name="in_current_project", |
| 200 | static_data_name="add-del-layers", |
| 201 | static_data_template='{% include "layer_btn.html" %}') |
| 202 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 203 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 204 | class MachinesTable(ToasterTable): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 205 | """Table of Machines in Toaster""" |
| 206 | |
| 207 | def __init__(self, *args, **kwargs): |
| 208 | super(MachinesTable, self).__init__(*args, **kwargs) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 209 | self.empty_state = "Toaster has no machine information for this project. Sadly, machine information cannot be obtained from builds, so this page will remain empty." |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 210 | self.title = "Compatible machines" |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 211 | self.default_orderby = "name" |
| 212 | |
| 213 | def get_context_data(self, **kwargs): |
| 214 | context = super(MachinesTable, self).get_context_data(**kwargs) |
| 215 | context['project'] = Project.objects.get(pk=kwargs['pid']) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 216 | return context |
| 217 | |
| 218 | def setup_filters(self, *args, **kwargs): |
| 219 | project = Project.objects.get(pk=kwargs['pid']) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 220 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 221 | in_current_project_filter = TableFilter( |
| 222 | "in_current_project", |
| 223 | "Filter by project machines" |
| 224 | ) |
| 225 | |
| 226 | in_project_action = TableFilterActionToggle( |
| 227 | "in_project", |
| 228 | "Machines provided by layers added to this project", |
| 229 | ProjectFilters.in_project(self.project_layers) |
| 230 | ) |
| 231 | |
| 232 | not_in_project_action = TableFilterActionToggle( |
| 233 | "not_in_project", |
| 234 | "Machines provided by layers not added to this project", |
| 235 | ProjectFilters.not_in_project(self.project_layers) |
| 236 | ) |
| 237 | |
| 238 | in_current_project_filter.add_action(in_project_action) |
| 239 | in_current_project_filter.add_action(not_in_project_action) |
| 240 | self.add_filter(in_current_project_filter) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 241 | |
| 242 | def setup_queryset(self, *args, **kwargs): |
| 243 | prj = Project.objects.get(pk = kwargs['pid']) |
| 244 | self.queryset = prj.get_all_compatible_machines() |
| 245 | self.queryset = self.queryset.order_by(self.default_orderby) |
| 246 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 247 | self.static_context_extra['current_layers'] = \ |
| 248 | self.project_layers = \ |
| 249 | prj.get_project_layer_versions(pk=True) |
| 250 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 251 | def setup_columns(self, *args, **kwargs): |
| 252 | |
| 253 | self.add_column(title="Machine", |
| 254 | hideable=False, |
| 255 | orderable=True, |
| 256 | field_name="name") |
| 257 | |
| 258 | self.add_column(title="Description", |
| 259 | field_name="description") |
| 260 | |
| 261 | layer_link_template = ''' |
| 262 | <a href="{% url 'layerdetails' extra.pid data.layer_version.id %}"> |
| 263 | {{data.layer_version.layer.name}}</a> |
| 264 | ''' |
| 265 | |
| 266 | self.add_column(title="Layer", |
| 267 | static_data_name="layer_version__layer__name", |
| 268 | static_data_template=layer_link_template, |
| 269 | orderable=True) |
| 270 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 271 | self.add_column(title="Git revision", |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 272 | help_text="The Git branch, tag or commit. For the layers from the OpenEmbedded layer source, the revision is always the branch compatible with the Yocto Project version you selected for this project", |
| 273 | hidden=True, |
| 274 | field_name="layer_version__get_vcs_reference") |
| 275 | |
| 276 | machine_file_template = '''<code>conf/machine/{{data.name}}.conf</code> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 277 | <a href="{{data.get_vcs_machine_file_link_url}}" target="_blank"><span class="glyphicon glyphicon-new-window"></i></a>''' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 278 | |
| 279 | self.add_column(title="Machine file", |
| 280 | hidden=True, |
| 281 | static_data_name="machinefile", |
| 282 | static_data_template=machine_file_template) |
| 283 | |
| 284 | self.add_column(title="Select", |
| 285 | help_text="Sets the selected machine as the project machine. You can only have one machine per project", |
| 286 | hideable=False, |
| 287 | filter_name="in_current_project", |
| 288 | static_data_name="add-del-layers", |
| 289 | static_data_template='{% include "machine_btn.html" %}') |
| 290 | |
| 291 | |
| 292 | class LayerMachinesTable(MachinesTable): |
| 293 | """ Smaller version of the Machines table for use in layer details """ |
| 294 | |
| 295 | def __init__(self, *args, **kwargs): |
| 296 | super(LayerMachinesTable, self).__init__(*args, **kwargs) |
| 297 | |
| 298 | def get_context_data(self, **kwargs): |
| 299 | context = super(LayerMachinesTable, self).get_context_data(**kwargs) |
| 300 | context['layerversion'] = Layer_Version.objects.get(pk=kwargs['layerid']) |
| 301 | return context |
| 302 | |
| 303 | |
| 304 | def setup_queryset(self, *args, **kwargs): |
| 305 | MachinesTable.setup_queryset(self, *args, **kwargs) |
| 306 | |
| 307 | self.queryset = self.queryset.filter(layer_version__pk=int(kwargs['layerid'])) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 308 | self.queryset = self.queryset.order_by(self.default_orderby) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 309 | self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count() |
| 310 | |
| 311 | def setup_columns(self, *args, **kwargs): |
| 312 | self.add_column(title="Machine", |
| 313 | hideable=False, |
| 314 | orderable=True, |
| 315 | field_name="name") |
| 316 | |
| 317 | self.add_column(title="Description", |
| 318 | field_name="description") |
| 319 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 320 | select_btn_template = ''' |
| 321 | <a href="{% url "project" extra.pid %}?setMachine={{data.name}}" |
| 322 | class="btn btn-default btn-block select-machine-btn |
| 323 | {% if extra.in_prj == 0%}disabled{%endif%}">Select machine</a> |
| 324 | ''' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 325 | |
| 326 | self.add_column(title="Select machine", |
| 327 | static_data_name="add-del-layers", |
| 328 | static_data_template=select_btn_template) |
| 329 | |
| 330 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 331 | class RecipesTable(ToasterTable): |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 332 | """Table of All Recipes in Toaster""" |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 333 | |
| 334 | def __init__(self, *args, **kwargs): |
| 335 | super(RecipesTable, self).__init__(*args, **kwargs) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 336 | self.empty_state = "Toaster has no recipe information. To generate recipe information you need to run a build." |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 337 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 338 | build_col = { 'title' : "Build", |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 339 | 'help_text' : "Before building a recipe, you might need to add the corresponding layer to your project", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 340 | 'hideable' : False, |
| 341 | 'filter_name' : "in_current_project", |
| 342 | 'static_data_name' : "add-del-layers", |
| 343 | 'static_data_template' : '{% include "recipe_btn.html" %}'} |
Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 344 | if '1' == os.environ.get('TOASTER_PROJECTSPECIFIC'): |
| 345 | build_col['static_data_template'] = '{% include "recipe_add_btn.html" %}' |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 346 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 347 | def get_context_data(self, **kwargs): |
| 348 | project = Project.objects.get(pk=kwargs['pid']) |
| 349 | context = super(RecipesTable, self).get_context_data(**kwargs) |
| 350 | |
| 351 | context['project'] = project |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 352 | context['projectlayers'] = [player.layercommit.id for player in ProjectLayer.objects.filter(project=context['project'])] |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 353 | |
| 354 | return context |
| 355 | |
| 356 | def setup_filters(self, *args, **kwargs): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 357 | table_filter = TableFilter( |
| 358 | 'in_current_project', |
| 359 | 'Filter by project recipes' |
| 360 | ) |
| 361 | |
| 362 | in_project_action = TableFilterActionToggle( |
| 363 | 'in_project', |
| 364 | 'Recipes provided by layers added to this project', |
| 365 | ProjectFilters.in_project(self.project_layers) |
| 366 | ) |
| 367 | |
| 368 | not_in_project_action = TableFilterActionToggle( |
| 369 | 'not_in_project', |
| 370 | 'Recipes provided by layers not added to this project', |
| 371 | ProjectFilters.not_in_project(self.project_layers) |
| 372 | ) |
| 373 | |
| 374 | table_filter.add_action(in_project_action) |
| 375 | table_filter.add_action(not_in_project_action) |
| 376 | self.add_filter(table_filter) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 377 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 378 | def setup_queryset(self, *args, **kwargs): |
| 379 | prj = Project.objects.get(pk = kwargs['pid']) |
| 380 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 381 | # Project layers used by the filters |
| 382 | self.project_layers = prj.get_project_layer_versions(pk=True) |
| 383 | |
| 384 | # Project layers used to switch the button states |
| 385 | self.static_context_extra['current_layers'] = self.project_layers |
| 386 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 387 | self.queryset = prj.get_all_compatible_recipes() |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 388 | |
| 389 | |
| 390 | def setup_columns(self, *args, **kwargs): |
| 391 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 392 | self.add_column(title="Version", |
| 393 | hidden=False, |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 394 | field_name="version") |
| 395 | |
| 396 | self.add_column(title="Description", |
| 397 | field_name="get_description_or_summary") |
| 398 | |
| 399 | recipe_file_template = ''' |
| 400 | <code>{{data.file_path}}</code> |
| 401 | <a href="{{data.get_vcs_recipe_file_link_url}}" target="_blank"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 402 | <span class="glyphicon glyphicon-new-window"></i> |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 403 | </a> |
| 404 | ''' |
| 405 | |
| 406 | self.add_column(title="Recipe file", |
| 407 | help_text="Path to the recipe .bb file", |
| 408 | hidden=True, |
| 409 | static_data_name="recipe-file", |
| 410 | static_data_template=recipe_file_template) |
| 411 | |
| 412 | self.add_column(title="Section", |
| 413 | help_text="The section in which recipes should be categorized", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 414 | hidden=True, |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 415 | orderable=True, |
| 416 | field_name="section") |
| 417 | |
| 418 | layer_link_template = ''' |
| 419 | <a href="{% url 'layerdetails' extra.pid data.layer_version.id %}"> |
| 420 | {{data.layer_version.layer.name}}</a> |
| 421 | ''' |
| 422 | |
| 423 | self.add_column(title="Layer", |
| 424 | help_text="The name of the layer providing the recipe", |
| 425 | orderable=True, |
| 426 | static_data_name="layer_version__layer__name", |
| 427 | static_data_template=layer_link_template) |
| 428 | |
| 429 | self.add_column(title="License", |
| 430 | help_text="The list of source licenses for the recipe. Multiple license names separated by the pipe character indicates a choice between licenses. Multiple license names separated by the ampersand character indicates multiple licenses exist that cover different parts of the source", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 431 | hidden=True, |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 432 | orderable=True, |
| 433 | field_name="license") |
| 434 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 435 | revision_link_template = ''' |
| 436 | {% if data.layer_version.layer.local_source_dir %} |
| 437 | <span class="text-muted">Not applicable</span> |
| 438 | <span class="glyphicon glyphicon-question-sign get-help" data-original-title="" title="The source code of {{data.layer_version.layer.name}} is not in a Git repository, so there is no revision associated with it"> </span> |
| 439 | {% else %} |
| 440 | {{data.layer_version.get_vcs_reference}} |
| 441 | {% endif %} |
| 442 | ''' |
| 443 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 444 | self.add_column(title="Git revision", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 445 | hidden=True, |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 446 | static_data_name="layer_version__get_vcs_reference", |
| 447 | static_data_template=revision_link_template) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 448 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 449 | |
| 450 | class LayerRecipesTable(RecipesTable): |
| 451 | """ Smaller version of the Recipes table for use in layer details """ |
| 452 | |
| 453 | def __init__(self, *args, **kwargs): |
| 454 | super(LayerRecipesTable, self).__init__(*args, **kwargs) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 455 | self.default_orderby = "name" |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 456 | |
| 457 | def get_context_data(self, **kwargs): |
| 458 | context = super(LayerRecipesTable, self).get_context_data(**kwargs) |
| 459 | context['layerversion'] = Layer_Version.objects.get(pk=kwargs['layerid']) |
| 460 | return context |
| 461 | |
| 462 | |
| 463 | def setup_queryset(self, *args, **kwargs): |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 464 | self.queryset = \ |
| 465 | Recipe.objects.filter(layer_version__pk=int(kwargs['layerid'])) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 466 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 467 | self.queryset = self.queryset.order_by(self.default_orderby) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 468 | self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count() |
| 469 | |
| 470 | def setup_columns(self, *args, **kwargs): |
| 471 | self.add_column(title="Recipe", |
| 472 | help_text="Information about a single piece of software, including where to download the source, configuration options, how to compile the source files and how to package the compiled output", |
| 473 | hideable=False, |
| 474 | orderable=True, |
| 475 | field_name="name") |
| 476 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 477 | self.add_column(title="Version", |
| 478 | field_name="version") |
| 479 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 480 | self.add_column(title="Description", |
| 481 | field_name="get_description_or_summary") |
| 482 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 483 | build_recipe_template = ''' |
| 484 | <a class="btn btn-default btn-block build-recipe-btn |
| 485 | {% if extra.in_prj == 0 %}disabled{% endif %}" |
| 486 | data-recipe-name="{{data.name}}">Build recipe</a> |
| 487 | ''' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 488 | |
| 489 | self.add_column(title="Build recipe", |
| 490 | static_data_name="add-del-layers", |
| 491 | static_data_template=build_recipe_template) |
| 492 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 493 | class CustomImagesTable(ToasterTable): |
| 494 | """ Table to display your custom images """ |
| 495 | def __init__(self, *args, **kwargs): |
| 496 | super(CustomImagesTable, self).__init__(*args, **kwargs) |
| 497 | self.title = "Custom images" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 498 | self.default_orderby = "name" |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 499 | |
| 500 | def get_context_data(self, **kwargs): |
| 501 | context = super(CustomImagesTable, self).get_context_data(**kwargs) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 502 | |
| 503 | empty_state_template = ''' |
| 504 | You have not created any custom images yet. |
| 505 | <a href="{% url 'newcustomimage' data.pid %}"> |
| 506 | Create your first custom image</a> |
| 507 | ''' |
| 508 | context['empty_state'] = self.render_static_data(empty_state_template, |
| 509 | kwargs) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 510 | project = Project.objects.get(pk=kwargs['pid']) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 511 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 512 | # TODO put project into the ToasterTable base class |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 513 | context['project'] = project |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 514 | return context |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 515 | |
| 516 | def setup_queryset(self, *args, **kwargs): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 517 | prj = Project.objects.get(pk = kwargs['pid']) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 518 | self.queryset = CustomImageRecipe.objects.filter(project=prj) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 519 | self.queryset = self.queryset.order_by(self.default_orderby) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 520 | |
| 521 | def setup_columns(self, *args, **kwargs): |
| 522 | |
| 523 | name_link_template = ''' |
| 524 | <a href="{% url 'customrecipe' extra.pid data.id %}"> |
| 525 | {{data.name}} |
| 526 | </a> |
| 527 | ''' |
| 528 | |
| 529 | self.add_column(title="Custom image", |
| 530 | hideable=False, |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 531 | orderable=True, |
| 532 | field_name="name", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 533 | static_data_name="name", |
| 534 | static_data_template=name_link_template) |
| 535 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 536 | recipe_file_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 537 | {% if data.get_base_recipe_file %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 538 | <code>{{data.name}}_{{data.version}}.bb</code> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 539 | <a href="{% url 'customrecipedownload' extra.pid data.pk %}" |
| 540 | class="glyphicon glyphicon-download-alt get-help" title="Download recipe file"></a> |
| 541 | {% endif %}''' |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 542 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 543 | self.add_column(title="Recipe file", |
| 544 | static_data_name='recipe_file_download', |
| 545 | static_data_template=recipe_file_template) |
| 546 | |
| 547 | approx_packages_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 548 | {% if data.get_all_packages.count > 0 %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 549 | <a href="{% url 'customrecipe' extra.pid data.id %}"> |
| 550 | {{data.get_all_packages.count}} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 551 | </a> |
| 552 | {% endif %}''' |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 553 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 554 | self.add_column(title="Packages", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 555 | static_data_name='approx_packages', |
| 556 | static_data_template=approx_packages_template) |
| 557 | |
| 558 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 559 | build_btn_template = ''' |
| 560 | <button data-recipe-name="{{data.name}}" |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 561 | class="btn btn-default btn-block build-recipe-btn"> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 562 | Build |
| 563 | </button>''' |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 564 | |
| 565 | self.add_column(title="Build", |
| 566 | hideable=False, |
| 567 | static_data_name='build_custom_img', |
| 568 | static_data_template=build_btn_template) |
| 569 | |
| 570 | class ImageRecipesTable(RecipesTable): |
| 571 | """ A subset of the recipes table which displayed just image recipes """ |
| 572 | |
| 573 | def __init__(self, *args, **kwargs): |
| 574 | super(ImageRecipesTable, self).__init__(*args, **kwargs) |
| 575 | self.title = "Compatible image recipes" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 576 | self.default_orderby = "name" |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 577 | |
| 578 | def setup_queryset(self, *args, **kwargs): |
| 579 | super(ImageRecipesTable, self).setup_queryset(*args, **kwargs) |
| 580 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 581 | custom_image_recipes = CustomImageRecipe.objects.filter( |
| 582 | project=kwargs['pid']) |
| 583 | self.queryset = self.queryset.filter( |
| 584 | Q(is_image=True) & ~Q(pk__in=custom_image_recipes)) |
| 585 | self.queryset = self.queryset.order_by(self.default_orderby) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 586 | |
| 587 | |
| 588 | def setup_columns(self, *args, **kwargs): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 589 | |
| 590 | name_link_template = ''' |
| 591 | <a href="{% url 'recipedetails' extra.pid data.pk %}">{{data.name}}</a> |
| 592 | ''' |
| 593 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 594 | self.add_column(title="Image recipe", |
| 595 | help_text="When you build an image recipe, you get an " |
| 596 | "image: a root file system you can" |
| 597 | "deploy to a machine", |
| 598 | hideable=False, |
| 599 | orderable=True, |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 600 | static_data_name="name", |
| 601 | static_data_template=name_link_template, |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 602 | field_name="name") |
| 603 | |
| 604 | super(ImageRecipesTable, self).setup_columns(*args, **kwargs) |
| 605 | |
| 606 | self.add_column(**RecipesTable.build_col) |
| 607 | |
| 608 | |
| 609 | class NewCustomImagesTable(ImageRecipesTable): |
| 610 | """ Table which displays Images recipes which can be customised """ |
| 611 | def __init__(self, *args, **kwargs): |
| 612 | super(NewCustomImagesTable, self).__init__(*args, **kwargs) |
| 613 | self.title = "Select the image recipe you want to customise" |
| 614 | |
| 615 | def setup_queryset(self, *args, **kwargs): |
| 616 | super(ImageRecipesTable, self).setup_queryset(*args, **kwargs) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 617 | prj = Project.objects.get(pk = kwargs['pid']) |
| 618 | self.static_context_extra['current_layers'] = \ |
| 619 | prj.get_project_layer_versions(pk=True) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 620 | |
| 621 | self.queryset = self.queryset.filter(is_image=True) |
| 622 | |
| 623 | def setup_columns(self, *args, **kwargs): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 624 | |
| 625 | name_link_template = ''' |
| 626 | <a href="{% url 'recipedetails' extra.pid data.pk %}">{{data.name}}</a> |
| 627 | ''' |
| 628 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 629 | self.add_column(title="Image recipe", |
| 630 | help_text="When you build an image recipe, you get an " |
| 631 | "image: a root file system you can" |
| 632 | "deploy to a machine", |
| 633 | hideable=False, |
| 634 | orderable=True, |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 635 | static_data_name="name", |
| 636 | static_data_template=name_link_template, |
| 637 | field_name="name") |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 638 | |
| 639 | super(ImageRecipesTable, self).setup_columns(*args, **kwargs) |
| 640 | |
| 641 | self.add_column(title="Customise", |
| 642 | hideable=False, |
| 643 | filter_name="in_current_project", |
| 644 | static_data_name="customise-or-add-recipe", |
| 645 | static_data_template='{% include "customise_btn.html" %}') |
| 646 | |
| 647 | |
| 648 | class SoftwareRecipesTable(RecipesTable): |
| 649 | """ Displays just the software recipes """ |
| 650 | def __init__(self, *args, **kwargs): |
| 651 | super(SoftwareRecipesTable, self).__init__(*args, **kwargs) |
| 652 | self.title = "Compatible software recipes" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 653 | self.default_orderby = "name" |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 654 | |
| 655 | def setup_queryset(self, *args, **kwargs): |
| 656 | super(SoftwareRecipesTable, self).setup_queryset(*args, **kwargs) |
| 657 | |
| 658 | self.queryset = self.queryset.filter(is_image=False) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 659 | self.queryset = self.queryset.order_by(self.default_orderby) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 660 | |
| 661 | |
| 662 | def setup_columns(self, *args, **kwargs): |
| 663 | self.add_column(title="Software recipe", |
| 664 | help_text="Information about a single piece of " |
| 665 | "software, including where to download the source, " |
| 666 | "configuration options, how to compile the source " |
| 667 | "files and how to package the compiled output", |
| 668 | hideable=False, |
| 669 | orderable=True, |
| 670 | field_name="name") |
| 671 | |
| 672 | super(SoftwareRecipesTable, self).setup_columns(*args, **kwargs) |
| 673 | |
| 674 | self.add_column(**RecipesTable.build_col) |
| 675 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 676 | class PackagesTable(ToasterTable): |
| 677 | """ Table to display the packages in a recipe from it's last successful |
| 678 | build""" |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 679 | |
| 680 | def __init__(self, *args, **kwargs): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 681 | super(PackagesTable, self).__init__(*args, **kwargs) |
| 682 | self.title = "Packages included" |
| 683 | self.packages = None |
| 684 | self.default_orderby = "name" |
| 685 | |
| 686 | def create_package_list(self, recipe, project_id): |
| 687 | """Creates a list of packages for the specified recipe by looking for |
| 688 | the last SUCCEEDED build of ther recipe""" |
| 689 | |
| 690 | target = Target.objects.filter(Q(target=recipe.name) & |
| 691 | Q(build__project_id=project_id) & |
| 692 | Q(build__outcome=Build.SUCCEEDED) |
| 693 | ).last() |
| 694 | |
| 695 | if target: |
| 696 | pkgs = target.target_installed_package_set.values_list('package', |
| 697 | flat=True) |
| 698 | return Package.objects.filter(pk__in=pkgs) |
| 699 | |
| 700 | # Target/recipe never successfully built so empty queryset |
| 701 | return Package.objects.none() |
| 702 | |
| 703 | def get_context_data(self, **kwargs): |
| 704 | """Context for rendering the sidebar and other items on the recipe |
| 705 | details page """ |
| 706 | context = super(PackagesTable, self).get_context_data(**kwargs) |
| 707 | |
| 708 | recipe = Recipe.objects.get(pk=kwargs['recipe_id']) |
| 709 | project = Project.objects.get(pk=kwargs['pid']) |
| 710 | |
| 711 | in_project = (recipe.layer_version.pk in |
| 712 | project.get_project_layer_versions(pk=True)) |
| 713 | |
| 714 | packages = self.create_package_list(recipe, project.pk) |
| 715 | |
| 716 | context.update({'project': project, |
| 717 | 'recipe' : recipe, |
| 718 | 'packages': packages, |
| 719 | 'approx_pkg_size' : packages.aggregate(Sum('size')), |
| 720 | 'in_project' : in_project, |
| 721 | }) |
| 722 | |
| 723 | return context |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 724 | |
| 725 | def setup_queryset(self, *args, **kwargs): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 726 | recipe = Recipe.objects.get(pk=kwargs['recipe_id']) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 727 | self.static_context_extra['target_name'] = recipe.name |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 728 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 729 | self.queryset = self.create_package_list(recipe, kwargs['pid']) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 730 | self.queryset = self.queryset.order_by('name') |
| 731 | |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 732 | def setup_columns(self, *args, **kwargs): |
| 733 | self.add_column(title="Package", |
| 734 | hideable=False, |
| 735 | orderable=True, |
| 736 | field_name="name") |
| 737 | |
| 738 | self.add_column(title="Package Version", |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 739 | field_name="version", |
| 740 | hideable=False) |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 741 | |
| 742 | self.add_column(title="Approx Size", |
| 743 | orderable=True, |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 744 | field_name="size", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 745 | static_data_name="size", |
| 746 | static_data_template="{% load projecttags %} \ |
| 747 | {{data.size|filtered_filesizeformat}}") |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 748 | |
| 749 | self.add_column(title="License", |
| 750 | field_name="license", |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 751 | orderable=True, |
| 752 | hidden=True) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 753 | |
| 754 | |
| 755 | self.add_column(title="Dependencies", |
| 756 | static_data_name="dependencies", |
| 757 | static_data_template='\ |
| 758 | {% include "snippets/pkg_dependencies_popover.html" %}') |
| 759 | |
| 760 | self.add_column(title="Reverse dependencies", |
| 761 | static_data_name="reverse_dependencies", |
| 762 | static_data_template='\ |
| 763 | {% include "snippets/pkg_revdependencies_popover.html" %}', |
| 764 | hidden=True) |
| 765 | |
| 766 | self.add_column(title="Recipe", |
| 767 | field_name="recipe__name", |
| 768 | orderable=True, |
| 769 | hidden=True) |
| 770 | |
| 771 | self.add_column(title="Recipe version", |
| 772 | field_name="recipe__version", |
| 773 | hidden=True) |
| 774 | |
| 775 | |
| 776 | class SelectPackagesTable(PackagesTable): |
| 777 | """ Table to display the packages to add and remove from an image """ |
| 778 | |
| 779 | def __init__(self, *args, **kwargs): |
| 780 | super(SelectPackagesTable, self).__init__(*args, **kwargs) |
| 781 | self.title = "Add | Remove packages" |
| 782 | |
| 783 | def setup_queryset(self, *args, **kwargs): |
| 784 | self.cust_recipe =\ |
| 785 | CustomImageRecipe.objects.get(pk=kwargs['custrecipeid']) |
| 786 | prj = Project.objects.get(pk = kwargs['pid']) |
| 787 | |
| 788 | current_packages = self.cust_recipe.get_all_packages() |
| 789 | |
| 790 | current_recipes = prj.get_available_recipes() |
| 791 | |
| 792 | # only show packages where recipes->layers are in the project |
| 793 | self.queryset = CustomImagePackage.objects.filter( |
| 794 | ~Q(recipe=None) & |
| 795 | Q(recipe__in=current_recipes)) |
| 796 | |
| 797 | self.queryset = self.queryset.order_by('name') |
| 798 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 799 | # This target is the target used to work out which group of dependences |
| 800 | # to display, if we've built the custom image we use it otherwise we |
| 801 | # can use the based recipe instead |
| 802 | if prj.build_set.filter(target__target=self.cust_recipe.name).count()\ |
| 803 | > 0: |
| 804 | self.static_context_extra['target_name'] = self.cust_recipe.name |
| 805 | else: |
| 806 | self.static_context_extra['target_name'] =\ |
| 807 | Package_DependencyManager.TARGET_LATEST |
| 808 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 809 | self.static_context_extra['recipe_id'] = kwargs['custrecipeid'] |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 810 | |
| 811 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 812 | self.static_context_extra['current_packages'] = \ |
| 813 | current_packages.values_list('pk', flat=True) |
| 814 | |
| 815 | def get_context_data(self, **kwargs): |
| 816 | # to reuse the Super class map the custrecipeid to the recipe_id |
| 817 | kwargs['recipe_id'] = kwargs['custrecipeid'] |
| 818 | context = super(SelectPackagesTable, self).get_context_data(**kwargs) |
| 819 | custom_recipe = \ |
| 820 | CustomImageRecipe.objects.get(pk=kwargs['custrecipeid']) |
| 821 | |
| 822 | context['recipe'] = custom_recipe |
| 823 | context['approx_pkg_size'] = \ |
| 824 | custom_recipe.get_all_packages().aggregate(Sum('size')) |
| 825 | return context |
| 826 | |
| 827 | |
| 828 | def setup_columns(self, *args, **kwargs): |
| 829 | super(SelectPackagesTable, self).setup_columns(*args, **kwargs) |
| 830 | |
| 831 | add_remove_template = '{% include "pkg_add_rm_btn.html" %}' |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 832 | |
| 833 | self.add_column(title="Add | Remove", |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 834 | hideable=False, |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 835 | help_text="Use the add and remove buttons to modify " |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 836 | "the package content of your custom image", |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 837 | static_data_name="add_rm_pkg_btn", |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 838 | static_data_template=add_remove_template, |
| 839 | filter_name='in_current_image_filter') |
| 840 | |
| 841 | def setup_filters(self, *args, **kwargs): |
| 842 | in_current_image_filter = TableFilter( |
| 843 | 'in_current_image_filter', |
| 844 | 'Filter by added packages' |
| 845 | ) |
| 846 | |
| 847 | in_image_action = TableFilterActionToggle( |
| 848 | 'in_image', |
| 849 | 'Packages in %s' % self.cust_recipe.name, |
| 850 | Q(pk__in=self.static_context_extra['current_packages']) |
| 851 | ) |
| 852 | |
| 853 | not_in_image_action = TableFilterActionToggle( |
| 854 | 'not_in_image', |
| 855 | 'Packages not added to %s' % self.cust_recipe.name, |
| 856 | ~Q(pk__in=self.static_context_extra['current_packages']) |
| 857 | ) |
| 858 | |
| 859 | in_current_image_filter.add_action(in_image_action) |
| 860 | in_current_image_filter.add_action(not_in_image_action) |
| 861 | self.add_filter(in_current_image_filter) |
| 862 | |
| 863 | class ProjectsTable(ToasterTable): |
| 864 | """Table of projects in Toaster""" |
| 865 | |
| 866 | def __init__(self, *args, **kwargs): |
| 867 | super(ProjectsTable, self).__init__(*args, **kwargs) |
| 868 | self.default_orderby = '-updated' |
| 869 | self.title = 'All projects' |
| 870 | self.static_context_extra['Build'] = Build |
| 871 | |
| 872 | def get_context_data(self, **kwargs): |
| 873 | return super(ProjectsTable, self).get_context_data(**kwargs) |
| 874 | |
| 875 | def setup_queryset(self, *args, **kwargs): |
| 876 | queryset = Project.objects.all() |
| 877 | |
| 878 | # annotate each project with its number of builds |
| 879 | queryset = queryset.annotate(num_builds=Count('build')) |
| 880 | |
| 881 | # exclude the command line builds project if it has no builds |
| 882 | q_default_with_builds = Q(is_default=True) & Q(num_builds__gt=0) |
| 883 | queryset = queryset.filter(Q(is_default=False) | |
| 884 | q_default_with_builds) |
| 885 | |
| 886 | # order rows |
| 887 | queryset = queryset.order_by(self.default_orderby) |
| 888 | |
| 889 | self.queryset = queryset |
| 890 | |
| 891 | # columns: last activity on (updated) - DEFAULT, project (name), release, |
| 892 | # machine, number of builds, last build outcome, recipe (name), errors, |
| 893 | # warnings, image files |
| 894 | def setup_columns(self, *args, **kwargs): |
| 895 | name_template = ''' |
| 896 | {% load project_url_tag %} |
| 897 | <span data-project-field="name"> |
| 898 | <a href="{% project_url data %}"> |
| 899 | {{data.name}} |
| 900 | </a> |
| 901 | </span> |
| 902 | ''' |
| 903 | |
| 904 | last_activity_on_template = ''' |
| 905 | {% load project_url_tag %} |
| 906 | <span data-project-field="updated"> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 907 | {{data.updated | date:"d/m/y H:i"}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 908 | </span> |
| 909 | ''' |
| 910 | |
| 911 | release_template = ''' |
| 912 | <span data-project-field="release"> |
| 913 | {% if data.release %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 914 | {{data.release.name}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 915 | {% elif data.is_default %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 916 | <span class="text-muted">Not applicable</span> |
| 917 | <span class="glyphicon glyphicon-question-sign get-help hover-help" |
| 918 | title="This project does not have a release set. |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 919 | It simply collects information about the builds you start from |
| 920 | the command line while Toaster is running" |
| 921 | style="visibility: hidden;"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 922 | </span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 923 | {% else %} |
| 924 | No release available |
| 925 | {% endif %} |
| 926 | </span> |
| 927 | ''' |
| 928 | |
| 929 | machine_template = ''' |
| 930 | <span data-project-field="machine"> |
| 931 | {% if data.is_default %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 932 | <span class="text-muted">Not applicable</span> |
| 933 | <span class="glyphicon glyphicon-question-sign get-help hover-help" |
| 934 | title="This project does not have a machine |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 935 | set. It simply collects information about the builds you |
| 936 | start from the command line while Toaster is running" |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 937 | style="visibility: hidden;"></span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 938 | {% else %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 939 | {{data.get_current_machine_name}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 940 | {% endif %} |
| 941 | </span> |
| 942 | ''' |
| 943 | |
| 944 | number_of_builds_template = ''' |
| 945 | {% if data.get_number_of_builds > 0 %} |
| 946 | <a href="{% url 'projectbuilds' data.id %}"> |
| 947 | {{data.get_number_of_builds}} |
| 948 | </a> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 949 | {% endif %} |
| 950 | ''' |
| 951 | |
| 952 | last_build_outcome_template = ''' |
| 953 | {% if data.get_number_of_builds > 0 %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 954 | {% if data.get_last_outcome == extra.Build.SUCCEEDED %} |
| 955 | <span class="glyphicon glyphicon-ok-circle"></span> |
| 956 | {% elif data.get_last_outcome == extra.Build.FAILED %} |
| 957 | <span class="glyphicon glyphicon-minus-sign"></span> |
| 958 | {% endif %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 959 | {% endif %} |
| 960 | ''' |
| 961 | |
| 962 | recipe_template = ''' |
| 963 | {% if data.get_number_of_builds > 0 %} |
| 964 | <a href="{% url "builddashboard" data.get_last_build_id %}"> |
| 965 | {{data.get_last_target}} |
| 966 | </a> |
| 967 | {% endif %} |
| 968 | ''' |
| 969 | |
| 970 | errors_template = ''' |
| 971 | {% if data.get_number_of_builds > 0 and data.get_last_errors > 0 %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 972 | <a class="errors.count text-danger" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 973 | href="{% url "builddashboard" data.get_last_build_id %}#errors"> |
| 974 | {{data.get_last_errors}} error{{data.get_last_errors | pluralize}} |
| 975 | </a> |
| 976 | {% endif %} |
| 977 | ''' |
| 978 | |
| 979 | warnings_template = ''' |
| 980 | {% if data.get_number_of_builds > 0 and data.get_last_warnings > 0 %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 981 | <a class="warnings.count text-warning" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 982 | href="{% url "builddashboard" data.get_last_build_id %}#warnings"> |
| 983 | {{data.get_last_warnings}} warning{{data.get_last_warnings | pluralize}} |
| 984 | </a> |
| 985 | {% endif %} |
| 986 | ''' |
| 987 | |
| 988 | image_files_template = ''' |
| 989 | {% if data.get_number_of_builds > 0 and data.get_last_outcome == extra.Build.SUCCEEDED %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 990 | {{data.get_last_build_extensions}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 991 | {% endif %} |
| 992 | ''' |
| 993 | |
| 994 | self.add_column(title='Project', |
| 995 | hideable=False, |
| 996 | orderable=True, |
| 997 | static_data_name='name', |
| 998 | static_data_template=name_template) |
| 999 | |
| 1000 | self.add_column(title='Last activity on', |
| 1001 | help_text='Starting date and time of the \ |
| 1002 | last project build. If the project has no \ |
| 1003 | builds, this shows the date the project was \ |
| 1004 | created.', |
| 1005 | hideable=False, |
| 1006 | orderable=True, |
| 1007 | static_data_name='updated', |
| 1008 | static_data_template=last_activity_on_template) |
| 1009 | |
| 1010 | self.add_column(title='Release', |
| 1011 | help_text='The version of the build system used by \ |
| 1012 | the project', |
| 1013 | hideable=False, |
| 1014 | orderable=True, |
| 1015 | static_data_name='release', |
| 1016 | static_data_template=release_template) |
| 1017 | |
| 1018 | self.add_column(title='Machine', |
| 1019 | help_text='The hardware currently selected for the \ |
| 1020 | project', |
| 1021 | hideable=False, |
| 1022 | orderable=False, |
| 1023 | static_data_name='machine', |
| 1024 | static_data_template=machine_template) |
| 1025 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1026 | self.add_column(title='Builds', |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1027 | help_text='The number of builds which have been run \ |
| 1028 | for the project', |
| 1029 | hideable=False, |
| 1030 | orderable=False, |
| 1031 | static_data_name='number_of_builds', |
| 1032 | static_data_template=number_of_builds_template) |
| 1033 | |
| 1034 | self.add_column(title='Last build outcome', |
| 1035 | help_text='Indicates whether the last project build \ |
| 1036 | completed successfully or failed', |
| 1037 | hideable=True, |
| 1038 | orderable=False, |
| 1039 | static_data_name='last_build_outcome', |
| 1040 | static_data_template=last_build_outcome_template) |
| 1041 | |
| 1042 | self.add_column(title='Recipe', |
| 1043 | help_text='The last recipe which was built in this \ |
| 1044 | project', |
| 1045 | hideable=True, |
| 1046 | orderable=False, |
| 1047 | static_data_name='recipe_name', |
| 1048 | static_data_template=recipe_template) |
| 1049 | |
| 1050 | self.add_column(title='Errors', |
| 1051 | help_text='The number of errors encountered during \ |
| 1052 | the last project build (if any)', |
| 1053 | hideable=True, |
| 1054 | orderable=False, |
| 1055 | static_data_name='errors', |
| 1056 | static_data_template=errors_template) |
| 1057 | |
| 1058 | self.add_column(title='Warnings', |
| 1059 | help_text='The number of warnings encountered during \ |
| 1060 | the last project build (if any)', |
| 1061 | hideable=True, |
| 1062 | hidden=True, |
| 1063 | orderable=False, |
| 1064 | static_data_name='warnings', |
| 1065 | static_data_template=warnings_template) |
| 1066 | |
| 1067 | self.add_column(title='Image files', |
| 1068 | help_text='The root file system types produced by \ |
| 1069 | the last project build', |
| 1070 | hideable=True, |
| 1071 | hidden=True, |
| 1072 | orderable=False, |
| 1073 | static_data_name='image_files', |
| 1074 | static_data_template=image_files_template) |
| 1075 | |
| 1076 | class BuildsTable(ToasterTable): |
| 1077 | """Table of builds in Toaster""" |
| 1078 | |
| 1079 | def __init__(self, *args, **kwargs): |
| 1080 | super(BuildsTable, self).__init__(*args, **kwargs) |
| 1081 | self.default_orderby = '-completed_on' |
| 1082 | self.static_context_extra['Build'] = Build |
| 1083 | self.static_context_extra['Task'] = Task |
| 1084 | |
| 1085 | # attributes that are overridden in subclasses |
| 1086 | |
| 1087 | # title for the page |
| 1088 | self.title = '' |
| 1089 | |
| 1090 | # 'project' or 'all'; determines how the mrb (most recent builds) |
| 1091 | # section is displayed |
| 1092 | self.mrb_type = '' |
| 1093 | |
| 1094 | def get_builds(self): |
| 1095 | """ |
| 1096 | overridden in ProjectBuildsTable to return builds for a |
| 1097 | single project |
| 1098 | """ |
| 1099 | return Build.objects.all() |
| 1100 | |
| 1101 | def get_context_data(self, **kwargs): |
| 1102 | context = super(BuildsTable, self).get_context_data(**kwargs) |
| 1103 | |
| 1104 | # should be set in subclasses |
| 1105 | context['mru'] = [] |
| 1106 | |
| 1107 | context['mrb_type'] = self.mrb_type |
| 1108 | |
| 1109 | return context |
| 1110 | |
| 1111 | def setup_queryset(self, *args, **kwargs): |
| 1112 | """ |
| 1113 | The queryset is annotated so that it can be sorted by number of |
| 1114 | errors and number of warnings; but note that the criteria for |
| 1115 | finding the log messages to populate these fields should match those |
| 1116 | used in the Build model (orm/models.py) to populate the errors and |
| 1117 | warnings properties |
| 1118 | """ |
| 1119 | queryset = self.get_builds() |
| 1120 | |
| 1121 | # Don't include in progress builds pr cancelled builds |
| 1122 | queryset = queryset.exclude(Q(outcome=Build.IN_PROGRESS) | |
| 1123 | Q(outcome=Build.CANCELLED)) |
| 1124 | |
| 1125 | # sort |
| 1126 | queryset = queryset.order_by(self.default_orderby) |
| 1127 | |
| 1128 | # annotate with number of ERROR, EXCEPTION and CRITICAL log messages |
| 1129 | criteria = (Q(logmessage__level=LogMessage.ERROR) | |
| 1130 | Q(logmessage__level=LogMessage.EXCEPTION) | |
| 1131 | Q(logmessage__level=LogMessage.CRITICAL)) |
| 1132 | |
| 1133 | queryset = queryset.annotate( |
| 1134 | errors_no=Count( |
| 1135 | Case( |
| 1136 | When(criteria, then=Value(1)), |
| 1137 | output_field=IntegerField() |
| 1138 | ) |
| 1139 | ) |
| 1140 | ) |
| 1141 | |
| 1142 | # annotate with number of WARNING log messages |
| 1143 | queryset = queryset.annotate( |
| 1144 | warnings_no=Count( |
| 1145 | Case( |
| 1146 | When(logmessage__level=LogMessage.WARNING, then=Value(1)), |
| 1147 | output_field=IntegerField() |
| 1148 | ) |
| 1149 | ) |
| 1150 | ) |
| 1151 | |
| 1152 | self.queryset = queryset |
| 1153 | |
| 1154 | def setup_columns(self, *args, **kwargs): |
| 1155 | outcome_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1156 | {% if data.outcome == data.SUCCEEDED %} |
| 1157 | <span class="glyphicon glyphicon-ok-circle"></span> |
| 1158 | {% elif data.outcome == data.FAILED %} |
| 1159 | <span class="glyphicon glyphicon-minus-sign"></span> |
| 1160 | {% endif %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1161 | |
| 1162 | {% if data.cooker_log_path %} |
| 1163 | |
| 1164 | <a href="{% url "build_artifact" data.id "cookerlog" data.id %}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1165 | <span class="glyphicon glyphicon-download-alt get-help" |
| 1166 | data-original-title="Download build log"></span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1167 | </a> |
| 1168 | {% endif %} |
| 1169 | ''' |
| 1170 | |
| 1171 | recipe_template = ''' |
| 1172 | {% for target_label in data.target_labels %} |
| 1173 | <a href="{% url "builddashboard" data.id %}"> |
| 1174 | {{target_label}} |
| 1175 | </a> |
| 1176 | <br /> |
| 1177 | {% endfor %} |
| 1178 | ''' |
| 1179 | |
| 1180 | machine_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1181 | {{data.machine}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1182 | ''' |
| 1183 | |
| 1184 | started_on_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1185 | {{data.started_on | date:"d/m/y H:i"}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1186 | ''' |
| 1187 | |
| 1188 | completed_on_template = ''' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1189 | {{data.completed_on | date:"d/m/y H:i"}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1190 | ''' |
| 1191 | |
| 1192 | failed_tasks_template = ''' |
| 1193 | {% if data.failed_tasks.count == 1 %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1194 | <a class="text-danger" href="{% url "task" data.id data.failed_tasks.0.id %}"> |
| 1195 | <span> |
| 1196 | {{data.failed_tasks.0.recipe.name}} {{data.failed_tasks.0.task_name}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1197 | </span> |
| 1198 | </a> |
| 1199 | <a href="{% url "build_artifact" data.id "tasklogfile" data.failed_tasks.0.id %}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1200 | <span class="glyphicon glyphicon-download-alt get-help" |
| 1201 | title="Download task log file"> |
| 1202 | </span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1203 | </a> |
| 1204 | {% elif data.failed_tasks.count > 1 %} |
| 1205 | <a href="{% url "tasks" data.id %}?filter=outcome%3A{{extra.Task.OUTCOME_FAILED}}"> |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1206 | <span class="text-danger">{{data.failed_tasks.count}} tasks</span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1207 | </a> |
| 1208 | {% endif %} |
| 1209 | ''' |
| 1210 | |
| 1211 | errors_template = ''' |
| 1212 | {% if data.errors_no %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1213 | <a class="errors.count text-danger" href="{% url "builddashboard" data.id %}#errors"> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1214 | {{data.errors_no}} error{{data.errors_no|pluralize}} |
| 1215 | </a> |
| 1216 | {% endif %} |
| 1217 | ''' |
| 1218 | |
| 1219 | warnings_template = ''' |
| 1220 | {% if data.warnings_no %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1221 | <a class="warnings.count text-warning" href="{% url "builddashboard" data.id %}#warnings"> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1222 | {{data.warnings_no}} warning{{data.warnings_no|pluralize}} |
| 1223 | </a> |
| 1224 | {% endif %} |
| 1225 | ''' |
| 1226 | |
| 1227 | time_template = ''' |
| 1228 | {% load projecttags %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1229 | {% if data.outcome == extra.Build.SUCCEEDED %} |
| 1230 | <a href="{% url "buildtime" data.id %}"> |
| 1231 | {{data.timespent_seconds | sectohms}} |
| 1232 | </a> |
| 1233 | {% else %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1234 | {{data.timespent_seconds | sectohms}} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1235 | {% endif %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1236 | ''' |
| 1237 | |
| 1238 | image_files_template = ''' |
| 1239 | {% if data.outcome == extra.Build.SUCCEEDED %} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1240 | {{data.get_image_file_extensions}} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1241 | {% endif %} |
| 1242 | ''' |
| 1243 | |
| 1244 | self.add_column(title='Outcome', |
| 1245 | help_text='Final state of the build (successful \ |
| 1246 | or failed)', |
| 1247 | hideable=False, |
| 1248 | orderable=True, |
| 1249 | filter_name='outcome_filter', |
| 1250 | static_data_name='outcome', |
| 1251 | static_data_template=outcome_template) |
| 1252 | |
| 1253 | self.add_column(title='Recipe', |
| 1254 | help_text='What was built (i.e. one or more recipes \ |
| 1255 | or image recipes)', |
| 1256 | hideable=False, |
| 1257 | orderable=False, |
| 1258 | static_data_name='target', |
| 1259 | static_data_template=recipe_template) |
| 1260 | |
| 1261 | self.add_column(title='Machine', |
| 1262 | help_text='Hardware for which you are building a \ |
| 1263 | recipe or image recipe', |
| 1264 | hideable=False, |
| 1265 | orderable=True, |
| 1266 | static_data_name='machine', |
| 1267 | static_data_template=machine_template) |
| 1268 | |
| 1269 | self.add_column(title='Started on', |
| 1270 | help_text='The date and time when the build started', |
| 1271 | hideable=True, |
| 1272 | hidden=True, |
| 1273 | orderable=True, |
| 1274 | filter_name='started_on_filter', |
| 1275 | static_data_name='started_on', |
| 1276 | static_data_template=started_on_template) |
| 1277 | |
| 1278 | self.add_column(title='Completed on', |
| 1279 | help_text='The date and time when the build finished', |
| 1280 | hideable=False, |
| 1281 | orderable=True, |
| 1282 | filter_name='completed_on_filter', |
| 1283 | static_data_name='completed_on', |
| 1284 | static_data_template=completed_on_template) |
| 1285 | |
| 1286 | self.add_column(title='Failed tasks', |
| 1287 | help_text='The number of tasks which failed during \ |
| 1288 | the build', |
| 1289 | hideable=True, |
| 1290 | orderable=False, |
| 1291 | filter_name='failed_tasks_filter', |
| 1292 | static_data_name='failed_tasks', |
| 1293 | static_data_template=failed_tasks_template) |
| 1294 | |
| 1295 | self.add_column(title='Errors', |
| 1296 | help_text='The number of errors encountered during \ |
| 1297 | the build (if any)', |
| 1298 | hideable=True, |
| 1299 | orderable=True, |
| 1300 | static_data_name='errors_no', |
| 1301 | static_data_template=errors_template) |
| 1302 | |
| 1303 | self.add_column(title='Warnings', |
| 1304 | help_text='The number of warnings encountered during \ |
| 1305 | the build (if any)', |
| 1306 | hideable=True, |
| 1307 | orderable=True, |
| 1308 | static_data_name='warnings_no', |
| 1309 | static_data_template=warnings_template) |
| 1310 | |
| 1311 | self.add_column(title='Time', |
| 1312 | help_text='How long the build took to finish', |
| 1313 | hideable=True, |
| 1314 | hidden=True, |
| 1315 | orderable=False, |
| 1316 | static_data_name='time', |
| 1317 | static_data_template=time_template) |
| 1318 | |
| 1319 | self.add_column(title='Image files', |
| 1320 | help_text='The root file system types produced by \ |
| 1321 | the build', |
| 1322 | hideable=True, |
| 1323 | orderable=False, |
| 1324 | static_data_name='image_files', |
| 1325 | static_data_template=image_files_template) |
| 1326 | |
| 1327 | def setup_filters(self, *args, **kwargs): |
| 1328 | # outcomes |
| 1329 | outcome_filter = TableFilter( |
| 1330 | 'outcome_filter', |
| 1331 | 'Filter builds by outcome' |
| 1332 | ) |
| 1333 | |
| 1334 | successful_builds_action = TableFilterActionToggle( |
| 1335 | 'successful_builds', |
| 1336 | 'Successful builds', |
| 1337 | Q(outcome=Build.SUCCEEDED) |
| 1338 | ) |
| 1339 | |
| 1340 | failed_builds_action = TableFilterActionToggle( |
| 1341 | 'failed_builds', |
| 1342 | 'Failed builds', |
| 1343 | Q(outcome=Build.FAILED) |
| 1344 | ) |
| 1345 | |
| 1346 | outcome_filter.add_action(successful_builds_action) |
| 1347 | outcome_filter.add_action(failed_builds_action) |
| 1348 | self.add_filter(outcome_filter) |
| 1349 | |
| 1350 | # started on |
| 1351 | started_on_filter = TableFilter( |
| 1352 | 'started_on_filter', |
| 1353 | 'Filter by date when build was started' |
| 1354 | ) |
| 1355 | |
| 1356 | started_today_action = TableFilterActionDay( |
| 1357 | 'today', |
| 1358 | 'Today\'s builds', |
| 1359 | 'started_on', |
| 1360 | 'today' |
| 1361 | ) |
| 1362 | |
| 1363 | started_yesterday_action = TableFilterActionDay( |
| 1364 | 'yesterday', |
| 1365 | 'Yesterday\'s builds', |
| 1366 | 'started_on', |
| 1367 | 'yesterday' |
| 1368 | ) |
| 1369 | |
| 1370 | by_started_date_range_action = TableFilterActionDateRange( |
| 1371 | 'date_range', |
| 1372 | 'Build date range', |
| 1373 | 'started_on' |
| 1374 | ) |
| 1375 | |
| 1376 | started_on_filter.add_action(started_today_action) |
| 1377 | started_on_filter.add_action(started_yesterday_action) |
| 1378 | started_on_filter.add_action(by_started_date_range_action) |
| 1379 | self.add_filter(started_on_filter) |
| 1380 | |
| 1381 | # completed on |
| 1382 | completed_on_filter = TableFilter( |
| 1383 | 'completed_on_filter', |
| 1384 | 'Filter by date when build was completed' |
| 1385 | ) |
| 1386 | |
| 1387 | completed_today_action = TableFilterActionDay( |
| 1388 | 'today', |
| 1389 | 'Today\'s builds', |
| 1390 | 'completed_on', |
| 1391 | 'today' |
| 1392 | ) |
| 1393 | |
| 1394 | completed_yesterday_action = TableFilterActionDay( |
| 1395 | 'yesterday', |
| 1396 | 'Yesterday\'s builds', |
| 1397 | 'completed_on', |
| 1398 | 'yesterday' |
| 1399 | ) |
| 1400 | |
| 1401 | by_completed_date_range_action = TableFilterActionDateRange( |
| 1402 | 'date_range', |
| 1403 | 'Build date range', |
| 1404 | 'completed_on' |
| 1405 | ) |
| 1406 | |
| 1407 | completed_on_filter.add_action(completed_today_action) |
| 1408 | completed_on_filter.add_action(completed_yesterday_action) |
| 1409 | completed_on_filter.add_action(by_completed_date_range_action) |
| 1410 | self.add_filter(completed_on_filter) |
| 1411 | |
| 1412 | # failed tasks |
| 1413 | failed_tasks_filter = TableFilter( |
| 1414 | 'failed_tasks_filter', |
| 1415 | 'Filter builds by failed tasks' |
| 1416 | ) |
| 1417 | |
| 1418 | criteria = Q(task_build__outcome=Task.OUTCOME_FAILED) |
| 1419 | |
| 1420 | with_failed_tasks_action = TableFilterActionToggle( |
| 1421 | 'with_failed_tasks', |
| 1422 | 'Builds with failed tasks', |
| 1423 | criteria |
| 1424 | ) |
| 1425 | |
| 1426 | without_failed_tasks_action = TableFilterActionToggle( |
| 1427 | 'without_failed_tasks', |
| 1428 | 'Builds without failed tasks', |
| 1429 | ~criteria |
| 1430 | ) |
| 1431 | |
| 1432 | failed_tasks_filter.add_action(with_failed_tasks_action) |
| 1433 | failed_tasks_filter.add_action(without_failed_tasks_action) |
| 1434 | self.add_filter(failed_tasks_filter) |
| 1435 | |
| 1436 | |
| 1437 | class AllBuildsTable(BuildsTable): |
| 1438 | """ Builds page for all builds """ |
| 1439 | |
| 1440 | def __init__(self, *args, **kwargs): |
| 1441 | super(AllBuildsTable, self).__init__(*args, **kwargs) |
| 1442 | self.title = 'All builds' |
| 1443 | self.mrb_type = 'all' |
| 1444 | |
| 1445 | def setup_columns(self, *args, **kwargs): |
| 1446 | """ |
| 1447 | All builds page shows a column for the project |
| 1448 | """ |
| 1449 | |
| 1450 | super(AllBuildsTable, self).setup_columns(*args, **kwargs) |
| 1451 | |
| 1452 | project_template = ''' |
| 1453 | {% load project_url_tag %} |
| 1454 | <a href="{% project_url data.project %}"> |
| 1455 | {{data.project.name}} |
| 1456 | </a> |
| 1457 | {% if data.project.is_default %} |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1458 | <span class="glyphicon glyphicon-question-sign get-help hover-help" title="" |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1459 | data-original-title="This project shows information about |
| 1460 | the builds you start from the command line while Toaster is |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1461 | running" style="visibility: hidden;"></span> |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1462 | {% endif %} |
| 1463 | ''' |
| 1464 | |
| 1465 | self.add_column(title='Project', |
| 1466 | hideable=True, |
| 1467 | orderable=True, |
| 1468 | static_data_name='project', |
| 1469 | static_data_template=project_template) |
| 1470 | |
| 1471 | def get_context_data(self, **kwargs): |
| 1472 | """ Get all builds for the recent builds area """ |
| 1473 | context = super(AllBuildsTable, self).get_context_data(**kwargs) |
| 1474 | context['mru'] = Build.get_recent() |
| 1475 | return context |
| 1476 | |
| 1477 | class ProjectBuildsTable(BuildsTable): |
| 1478 | """ |
| 1479 | Builds page for a single project; a BuildsTable, with the queryset |
| 1480 | filtered by project |
| 1481 | """ |
| 1482 | |
| 1483 | def __init__(self, *args, **kwargs): |
| 1484 | super(ProjectBuildsTable, self).__init__(*args, **kwargs) |
| 1485 | self.title = 'All project builds' |
| 1486 | self.mrb_type = 'project' |
| 1487 | |
| 1488 | # set from the querystring |
| 1489 | self.project_id = None |
| 1490 | |
| 1491 | def setup_columns(self, *args, **kwargs): |
| 1492 | """ |
| 1493 | Project builds table doesn't show the machines column by default |
| 1494 | """ |
| 1495 | |
| 1496 | super(ProjectBuildsTable, self).setup_columns(*args, **kwargs) |
| 1497 | |
| 1498 | # hide the machine column |
| 1499 | self.set_column_hidden('Machine', True) |
| 1500 | |
| 1501 | # allow the machine column to be hidden by the user |
| 1502 | self.set_column_hideable('Machine', True) |
| 1503 | |
| 1504 | def setup_queryset(self, *args, **kwargs): |
| 1505 | """ |
| 1506 | NOTE: self.project_id must be set before calling super(), |
| 1507 | as it's used in setup_queryset() |
| 1508 | """ |
| 1509 | self.project_id = kwargs['pid'] |
| 1510 | super(ProjectBuildsTable, self).setup_queryset(*args, **kwargs) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1511 | project = Project.objects.get(pk=self.project_id) |
| 1512 | self.queryset = self.queryset.filter(project=project) |
| 1513 | |
| 1514 | def get_context_data(self, **kwargs): |
| 1515 | """ |
| 1516 | Get recent builds for this project, and the project itself |
| 1517 | |
| 1518 | NOTE: self.project_id must be set before calling super(), |
| 1519 | as it's used in get_context_data() |
| 1520 | """ |
| 1521 | self.project_id = kwargs['pid'] |
| 1522 | context = super(ProjectBuildsTable, self).get_context_data(**kwargs) |
| 1523 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1524 | empty_state_template = ''' |
| 1525 | This project has no builds. |
| 1526 | <a href="{% url 'projectimagerecipes' data.pid %}"> |
| 1527 | Choose a recipe to build</a> |
| 1528 | ''' |
| 1529 | context['empty_state'] = self.render_static_data(empty_state_template, |
| 1530 | kwargs) |
| 1531 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1532 | project = Project.objects.get(pk=self.project_id) |
| 1533 | context['mru'] = Build.get_recent(project) |
| 1534 | context['project'] = project |
| 1535 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1536 | self.setup_queryset(**kwargs) |
| 1537 | if self.queryset.count() == 0 and \ |
| 1538 | project.build_set.filter(outcome=Build.IN_PROGRESS).count() > 0: |
| 1539 | context['build_in_progress_none_completed'] = True |
| 1540 | else: |
| 1541 | context['build_in_progress_none_completed'] = False |
| 1542 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 1543 | return context |
Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 1544 | |
| 1545 | |
| 1546 | class DistrosTable(ToasterTable): |
| 1547 | """Table of Distros in Toaster""" |
| 1548 | |
| 1549 | def __init__(self, *args, **kwargs): |
| 1550 | super(DistrosTable, self).__init__(*args, **kwargs) |
| 1551 | self.empty_state = "Toaster has no distro information for this project. Sadly, distro information cannot be obtained from builds, so this page will remain empty." |
| 1552 | self.title = "Compatible Distros" |
| 1553 | self.default_orderby = "name" |
| 1554 | |
| 1555 | def get_context_data(self, **kwargs): |
| 1556 | context = super(DistrosTable, self).get_context_data(**kwargs) |
| 1557 | context['project'] = Project.objects.get(pk=kwargs['pid']) |
| 1558 | return context |
| 1559 | |
| 1560 | def setup_filters(self, *args, **kwargs): |
| 1561 | project = Project.objects.get(pk=kwargs['pid']) |
| 1562 | |
| 1563 | in_current_project_filter = TableFilter( |
| 1564 | "in_current_project", |
| 1565 | "Filter by project Distros" |
| 1566 | ) |
| 1567 | |
| 1568 | in_project_action = TableFilterActionToggle( |
| 1569 | "in_project", |
| 1570 | "Distro provided by layers added to this project", |
| 1571 | ProjectFilters.in_project(self.project_layers) |
| 1572 | ) |
| 1573 | |
| 1574 | not_in_project_action = TableFilterActionToggle( |
| 1575 | "not_in_project", |
| 1576 | "Distros provided by layers not added to this project", |
| 1577 | ProjectFilters.not_in_project(self.project_layers) |
| 1578 | ) |
| 1579 | |
| 1580 | in_current_project_filter.add_action(in_project_action) |
| 1581 | in_current_project_filter.add_action(not_in_project_action) |
| 1582 | self.add_filter(in_current_project_filter) |
| 1583 | |
| 1584 | def setup_queryset(self, *args, **kwargs): |
| 1585 | prj = Project.objects.get(pk = kwargs['pid']) |
| 1586 | self.queryset = prj.get_all_compatible_distros() |
| 1587 | self.queryset = self.queryset.order_by(self.default_orderby) |
| 1588 | |
| 1589 | self.static_context_extra['current_layers'] = \ |
| 1590 | self.project_layers = \ |
| 1591 | prj.get_project_layer_versions(pk=True) |
| 1592 | |
| 1593 | def setup_columns(self, *args, **kwargs): |
| 1594 | |
| 1595 | self.add_column(title="Distro", |
| 1596 | hideable=False, |
| 1597 | orderable=True, |
| 1598 | field_name="name") |
| 1599 | |
| 1600 | self.add_column(title="Description", |
| 1601 | field_name="description") |
| 1602 | |
| 1603 | layer_link_template = ''' |
| 1604 | <a href="{% url 'layerdetails' extra.pid data.layer_version.id %}"> |
| 1605 | {{data.layer_version.layer.name}}</a> |
| 1606 | ''' |
| 1607 | |
| 1608 | self.add_column(title="Layer", |
| 1609 | static_data_name="layer_version__layer__name", |
| 1610 | static_data_template=layer_link_template, |
| 1611 | orderable=True) |
| 1612 | |
| 1613 | self.add_column(title="Git revision", |
| 1614 | help_text="The Git branch, tag or commit. For the layers from the OpenEmbedded layer source, the revision is always the branch compatible with the Yocto Project version you selected for this project", |
| 1615 | hidden=True, |
| 1616 | field_name="layer_version__get_vcs_reference") |
| 1617 | |
Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 1618 | distro_file_template = '''<code>conf/distro/{{data.name}}.conf</code> |
| 1619 | {% if 'None' not in data.get_vcs_distro_file_link_url %}<a href="{{data.get_vcs_distro_file_link_url}}" target="_blank"><span class="glyphicon glyphicon-new-window"></i></a>{% endif %}''' |
Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 1620 | self.add_column(title="Distro file", |
| 1621 | hidden=True, |
| 1622 | static_data_name="templatefile", |
Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 1623 | static_data_template=distro_file_template) |
Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 1624 | |
| 1625 | self.add_column(title="Select", |
| 1626 | help_text="Sets the selected distro to the project", |
| 1627 | hideable=False, |
| 1628 | filter_name="in_current_project", |
| 1629 | static_data_name="add-del-layers", |
| 1630 | static_data_template='{% include "distro_btn.html" %}') |
| 1631 | |