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