blob: 92e3b5c6670f140ae773ad59d51d2df2aa99f46a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
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
22from toastergui.widgets import ToasterTable
23from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project
24from django.db.models import Q, Max
25from django.conf.urls import url
26from django.core.urlresolvers import reverse
27from django.views.generic import TemplateView
28
29
30class ProjectFiltersMixin(object):
31 """Common mixin for recipe, machine in project filters"""
32
33 def filter_in_project(self, count_only=False):
34 query = self.queryset.filter(layer_version__in=self.project_layers)
35 if count_only:
36 return query.count()
37
38 self.queryset = query
39
40 def filter_not_in_project(self, count_only=False):
41 query = self.queryset.exclude(layer_version__in=self.project_layers)
42 if count_only:
43 return query.count()
44
45 self.queryset = query
46
47class LayersTable(ToasterTable):
48 """Table of layers in Toaster"""
49
50 def __init__(self, *args, **kwargs):
51 super(LayersTable, self).__init__(*args, **kwargs)
52 self.default_orderby = "layer__name"
53
54 def get_context_data(self, **kwargs):
55 context = super(LayersTable, self).get_context_data(**kwargs)
56
57 project = Project.objects.get(pk=kwargs['pid'])
58
59 context['project'] = project
60 context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=project))
61
62 return context
63
64
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
69
70 self.add_filter(title="Filter by project layers",
71 name="in_current_project",
72 filter_actions=[
73 self.make_filter_action("in_project", "Layers added to this project", self.filter_in_project),
74 self.make_filter_action("not_in_project", "Layers not added to this project", self.filter_not_in_project)
75 ])
76
77 def filter_in_project(self, count_only=False):
78 query = self.queryset.filter(projectlayer__in=self.project_layers)
79 if count_only:
80 return query.count()
81
82 self.queryset = query
83
84 def filter_not_in_project(self, count_only=False):
85 query = self.queryset.exclude(projectlayer__in=self.project_layers)
86 if count_only:
87 return query.count()
88
89 self.queryset = query
90
91
92 def setup_queryset(self, *args, **kwargs):
93 prj = Project.objects.get(pk = kwargs['pid'])
94 compatible_layers = prj.compatible_layerversions()
95
96 self.queryset = compatible_layers.order_by(self.default_orderby)
97
98 def setup_columns(self, *args, **kwargs):
99
100 layer_link_template = '''
101 <a href="{% url 'layerdetails' extra.pid data.id %}">
102 {{data.layer.name}}
103 </a>
104 '''
105
106 self.add_column(title="Layer",
107 hideable=False,
108 orderable=True,
109 static_data_name="layer__name",
110 static_data_template=layer_link_template)
111
112 self.add_column(title="Summary",
113 field_name="layer__summary")
114
115 git_url_template = '''
116 <a href="{% url 'layerdetails' extra.pid data.id %}">
117 <code>{{data.layer.vcs_url}}</code>
118 </a>
119 {% if data.get_vcs_link_url %}
120 <a target="_blank" href="{{ data.get_vcs_link_url }}">
121 <i class="icon-share get-info"></i>
122 </a>
123 {% endif %}
124 '''
125
126 self.add_column(title="Git repository URL",
127 help_text="The Git repository for the layer source code",
128 hidden=True,
129 static_data_name="layer__vcs_url",
130 static_data_template=git_url_template)
131
132 git_dir_template = '''
133 <a href="{% url 'layerdetails' extra.pid data.id %}">
134 <code>{{data.dirpath}}</code>
135 </a>
136 {% if data.dirpath and data.get_vcs_dirpath_link_url %}
137 <a target="_blank" href="{{ data.get_vcs_dirpath_link_url }}">
138 <i class="icon-share get-info"></i>
139 </a>
140 {% endif %}'''
141
142 self.add_column(title="Subdirectory",
143 help_text="The layer directory within the Git repository",
144 hidden=True,
145 static_data_name="git_subdir",
146 static_data_template=git_dir_template)
147
148 revision_template = '''
149 {% load projecttags %}
150 {% with vcs_ref=data.get_vcs_reference %}
151 {% if vcs_ref|is_shaid %}
152 <a class="btn" data-content="<ul class='unstyled'> <li>{{vcs_ref}}</li> </ul>">
153 {{vcs_ref|truncatechars:10}}
154 </a>
155 {% else %}
156 {{vcs_ref}}
157 {% endif %}
158 {% endwith %}
159 '''
160
161 self.add_column(title="Revision",
162 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",
163 static_data_name="revision",
164 static_data_template=revision_template)
165
166 deps_template = '''
167 {% with ods=data.dependencies.all%}
168 {% if ods.count %}
169 <a class="btn" title="<a href='{% url "layerdetails" extra.pid data.id %}'>{{data.layer.name}}</a> dependencies"
170 data-content="<ul class='unstyled'>
171 {% for i in ods%}
172 <li><a href='{% url "layerdetails" extra.pid i.depends_on.pk %}'>{{i.depends_on.layer.name}}</a></li>
173 {% endfor %}
174 </ul>">
175 {{ods.count}}
176 </a>
177 {% endif %}
178 {% endwith %}
179 '''
180
181 self.add_column(title="Dependencies",
182 help_text="Other layers a layer depends upon",
183 static_data_name="dependencies",
184 static_data_template=deps_template)
185
186 self.add_column(title="Add | Delete",
187 help_text="Add or delete layers to / from your project",
188 hideable=False,
189 filter_name="in_current_project",
190 static_data_name="add-del-layers",
191 static_data_template='{% include "layer_btn.html" %}')
192
193 project = Project.objects.get(pk=kwargs['pid'])
194 self.add_column(title="LayerDetailsUrl",
195 displayable = False,
196 field_name="layerdetailurl",
197 computation = lambda x: reverse('layerdetails', args=(project.id, x.id)))
198
199 self.add_column(title="name",
200 displayable = False,
201 field_name="name",
202 computation = lambda x: x.layer.name)
203
204
205class MachinesTable(ToasterTable, ProjectFiltersMixin):
206 """Table of Machines in Toaster"""
207
208 def __init__(self, *args, **kwargs):
209 super(MachinesTable, self).__init__(*args, **kwargs)
210 self.empty_state = "No machines maybe you need to do a build?"
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'])
216 context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project']))
217 return context
218
219 def setup_filters(self, *args, **kwargs):
220 project = Project.objects.get(pk=kwargs['pid'])
221 self.project_layers = project.projectlayer_equivalent_set()
222
223 self.add_filter(title="Filter by project machines",
224 name="in_current_project",
225 filter_actions=[
226 self.make_filter_action("in_project", "Machines provided by layers added to this project", self.filter_in_project),
227 self.make_filter_action("not_in_project", "Machines provided by layers not added to this project", self.filter_not_in_project)
228 ])
229
230 def setup_queryset(self, *args, **kwargs):
231 prj = Project.objects.get(pk = kwargs['pid'])
232 self.queryset = prj.get_all_compatible_machines()
233 self.queryset = self.queryset.order_by(self.default_orderby)
234
235 def setup_columns(self, *args, **kwargs):
236
237 self.add_column(title="Machine",
238 hideable=False,
239 orderable=True,
240 field_name="name")
241
242 self.add_column(title="Description",
243 field_name="description")
244
245 layer_link_template = '''
246 <a href="{% url 'layerdetails' extra.pid data.layer_version.id %}">
247 {{data.layer_version.layer.name}}</a>
248 '''
249
250 self.add_column(title="Layer",
251 static_data_name="layer_version__layer__name",
252 static_data_template=layer_link_template,
253 orderable=True)
254
255 self.add_column(title="Revision",
256 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",
257 hidden=True,
258 field_name="layer_version__get_vcs_reference")
259
260 machine_file_template = '''<code>conf/machine/{{data.name}}.conf</code>
261 <a href="{{data.get_vcs_machine_file_link_url}}" target="_blank"><i class="icon-share get-info"></i></a>'''
262
263 self.add_column(title="Machine file",
264 hidden=True,
265 static_data_name="machinefile",
266 static_data_template=machine_file_template)
267
268 self.add_column(title="Select",
269 help_text="Sets the selected machine as the project machine. You can only have one machine per project",
270 hideable=False,
271 filter_name="in_current_project",
272 static_data_name="add-del-layers",
273 static_data_template='{% include "machine_btn.html" %}')
274
275
276class LayerMachinesTable(MachinesTable):
277 """ Smaller version of the Machines table for use in layer details """
278
279 def __init__(self, *args, **kwargs):
280 super(LayerMachinesTable, self).__init__(*args, **kwargs)
281
282 def get_context_data(self, **kwargs):
283 context = super(LayerMachinesTable, self).get_context_data(**kwargs)
284 context['layerversion'] = Layer_Version.objects.get(pk=kwargs['layerid'])
285 return context
286
287
288 def setup_queryset(self, *args, **kwargs):
289 MachinesTable.setup_queryset(self, *args, **kwargs)
290
291 self.queryset = self.queryset.filter(layer_version__pk=int(kwargs['layerid']))
292 self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count()
293
294 def setup_columns(self, *args, **kwargs):
295 self.add_column(title="Machine",
296 hideable=False,
297 orderable=True,
298 field_name="name")
299
300 self.add_column(title="Description",
301 field_name="description")
302
303 select_btn_template = '<a href="{% url "project" extra.pid %}?setMachine={{data.name}}" class="btn btn-block select-machine-btn" {% if extra.in_prj == 0%}disabled="disabled"{%endif%}>Select machine</a>'
304
305 self.add_column(title="Select machine",
306 static_data_name="add-del-layers",
307 static_data_template=select_btn_template)
308
309
310class RecipesTable(ToasterTable, ProjectFiltersMixin):
311 """Table of Recipes in Toaster"""
312
313 def __init__(self, *args, **kwargs):
314 super(RecipesTable, self).__init__(*args, **kwargs)
315 self.empty_state = "Toaster has no recipe information. To generate recipe information you can configure a layer source then run a build."
316 self.default_orderby = "name"
317
318 def get_context_data(self, **kwargs):
319 project = Project.objects.get(pk=kwargs['pid'])
320 context = super(RecipesTable, self).get_context_data(**kwargs)
321
322 context['project'] = project
323
324 context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project']))
325
326 return context
327
328 def setup_filters(self, *args, **kwargs):
329 project = Project.objects.get(pk=kwargs['pid'])
330 self.project_layers = project.projectlayer_equivalent_set()
331
332 self.add_filter(title="Filter by project recipes",
333 name="in_current_project",
334 filter_actions=[
335 self.make_filter_action("in_project", "Recipes provided by layers added to this project", self.filter_in_project),
336 self.make_filter_action("not_in_project", "Recipes provided by layers not added to this project", self.filter_not_in_project)
337 ])
338
339
340 def setup_queryset(self, *args, **kwargs):
341 prj = Project.objects.get(pk = kwargs['pid'])
342
343 self.queryset = prj.get_all_compatible_recipes()
344 self.queryset = self.queryset.order_by(self.default_orderby)
345
346
347 def setup_columns(self, *args, **kwargs):
348
349 self.add_column(title="Recipe",
350 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",
351 hideable=False,
352 orderable=True,
353 field_name="name")
354
355 self.add_column(title="Recipe Version",
356 hidden=True,
357 field_name="version")
358
359 self.add_column(title="Description",
360 field_name="get_description_or_summary")
361
362 recipe_file_template = '''
363 <code>{{data.file_path}}</code>
364 <a href="{{data.get_vcs_recipe_file_link_url}}" target="_blank">
365 <i class="icon-share get-info"></i>
366 </a>
367 '''
368
369 self.add_column(title="Recipe file",
370 help_text="Path to the recipe .bb file",
371 hidden=True,
372 static_data_name="recipe-file",
373 static_data_template=recipe_file_template)
374
375 self.add_column(title="Section",
376 help_text="The section in which recipes should be categorized",
377 orderable=True,
378 field_name="section")
379
380 layer_link_template = '''
381 <a href="{% url 'layerdetails' extra.pid data.layer_version.id %}">
382 {{data.layer_version.layer.name}}</a>
383 '''
384
385 self.add_column(title="Layer",
386 help_text="The name of the layer providing the recipe",
387 orderable=True,
388 static_data_name="layer_version__layer__name",
389 static_data_template=layer_link_template)
390
391 self.add_column(title="License",
392 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",
393 orderable=True,
394 field_name="license")
395
396 self.add_column(title="Revision",
397 field_name="layer_version__get_vcs_reference")
398
399 self.add_column(title="Build",
400 help_text="Add or delete recipes to and from your project",
401 hideable=False,
402 filter_name="in_current_project",
403 static_data_name="add-del-layers",
404 static_data_template='{% include "recipe_btn.html" %}')
405
406 project = Project.objects.get(pk=kwargs['pid'])
407 self.add_column(title="Project compatible Layer ID",
408 displayable = False,
409 field_name = "projectcompatible_layer",
410 computation = lambda x: (x.layer_version.get_equivalents_wpriority(project)[0]))
411
412class LayerRecipesTable(RecipesTable):
413 """ Smaller version of the Recipes table for use in layer details """
414
415 def __init__(self, *args, **kwargs):
416 super(LayerRecipesTable, self).__init__(*args, **kwargs)
417
418 def get_context_data(self, **kwargs):
419 context = super(LayerRecipesTable, self).get_context_data(**kwargs)
420 context['layerversion'] = Layer_Version.objects.get(pk=kwargs['layerid'])
421 return context
422
423
424 def setup_queryset(self, *args, **kwargs):
425 RecipesTable.setup_queryset(self, *args, **kwargs)
426 self.queryset = self.queryset.filter(layer_version__pk=int(kwargs['layerid']))
427
428 self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count()
429
430 def setup_columns(self, *args, **kwargs):
431 self.add_column(title="Recipe",
432 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",
433 hideable=False,
434 orderable=True,
435 field_name="name")
436
437 self.add_column(title="Description",
438 field_name="get_description_or_summary")
439
440
441 build_recipe_template ='<button class="btn btn-block build-recipe-btn" data-recipe-name="{{data.name}}" {%if extra.in_prj == 0 %}disabled="disabled"{%endif%}>Build recipe</button>'
442
443 self.add_column(title="Build recipe",
444 static_data_name="add-del-layers",
445 static_data_template=build_recipe_template)
446
447class ProjectLayersRecipesTable(RecipesTable):
448 """ Table that lists only recipes available for layers added to the project """
449
450 def setup_queryset(self, *args, **kwargs):
451 super(ProjectLayersRecipesTable, self).setup_queryset(*args, **kwargs)
452 prj = Project.objects.get(pk = kwargs['pid'])
453 self.queryset = self.queryset.filter(layer_version__in = prj.projectlayer_equivalent_set())