xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/toaster/toastergui/buildtables.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# BitBake Toaster Implementation
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Copyright (C) 2016 Intel Corporation
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
7*4882a593Smuzhiyun#
8*4882a593Smuzhiyun
9*4882a593Smuzhiyunfrom orm.models import Build, Task, Target, Package
10*4882a593Smuzhiyunfrom django.db.models import Q, Sum
11*4882a593Smuzhiyun
12*4882a593Smuzhiyunimport toastergui.tables as tables
13*4882a593Smuzhiyunfrom toastergui.widgets import ToasterTable
14*4882a593Smuzhiyunfrom toastergui.tablefilter import TableFilter
15*4882a593Smuzhiyunfrom toastergui.tablefilter import TableFilterActionToggle
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun
18*4882a593Smuzhiyunclass BuildTablesMixin(ToasterTable):
19*4882a593Smuzhiyun    def get_context_data(self, **kwargs):
20*4882a593Smuzhiyun        # We need to be explicit about which superclass we're calling here
21*4882a593Smuzhiyun        # Otherwise the MRO gets in a right mess
22*4882a593Smuzhiyun        context = ToasterTable.get_context_data(self, **kwargs)
23*4882a593Smuzhiyun        context['build'] = Build.objects.get(pk=kwargs['build_id'])
24*4882a593Smuzhiyun        return context
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun
27*4882a593Smuzhiyunclass BuiltPackagesTableBase(tables.PackagesTable):
28*4882a593Smuzhiyun    """ Table to display all the packages built in a build """
29*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
30*4882a593Smuzhiyun        super(BuiltPackagesTableBase, self).__init__(*args, **kwargs)
31*4882a593Smuzhiyun        self.title = "Packages built"
32*4882a593Smuzhiyun        self.default_orderby = "name"
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun    def setup_queryset(self, *args, **kwargs):
35*4882a593Smuzhiyun        build = Build.objects.get(pk=kwargs['build_id'])
36*4882a593Smuzhiyun        self.static_context_extra['build'] = build
37*4882a593Smuzhiyun        self.static_context_extra['target_name'] = None
38*4882a593Smuzhiyun        self.queryset = build.package_set.all().exclude(recipe=None)
39*4882a593Smuzhiyun        self.queryset = self.queryset.order_by(self.default_orderby)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
42*4882a593Smuzhiyun        super(BuiltPackagesTableBase, self).setup_columns(*args, **kwargs)
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun        def pkg_link_template(val):
45*4882a593Smuzhiyun            """ return the template used for the link with the val as the
46*4882a593Smuzhiyun            element value i.e. inside the <a></a>"""
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun            return ('''
49*4882a593Smuzhiyun                    <a href="
50*4882a593Smuzhiyun                    {%% url "package_built_detail" extra.build.pk data.pk %%}
51*4882a593Smuzhiyun                    ">%s</a>
52*4882a593Smuzhiyun                    ''' % val)
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun        def recipe_link_template(val):
55*4882a593Smuzhiyun            return ('''
56*4882a593Smuzhiyun                    {%% if data.recipe %%}
57*4882a593Smuzhiyun                    <a href="
58*4882a593Smuzhiyun                    {%% url "recipe" extra.build.pk data.recipe.pk %%}
59*4882a593Smuzhiyun                    ">%(value)s</a>
60*4882a593Smuzhiyun                    {%% else %%}
61*4882a593Smuzhiyun                    %(value)s
62*4882a593Smuzhiyun                    {%% endif %%}
63*4882a593Smuzhiyun                    ''' % {'value': val})
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun        add_pkg_link_to = 'name'
66*4882a593Smuzhiyun        add_recipe_link_to = 'recipe__name'
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun        # Add the recipe and pkg build links to the required columns
69*4882a593Smuzhiyun        for column in self.columns:
70*4882a593Smuzhiyun            # Convert to template field style accessors
71*4882a593Smuzhiyun            tmplv = column['field_name'].replace('__', '.')
72*4882a593Smuzhiyun            tmplv = "{{data.%s}}" % tmplv
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun            if column['field_name'] is add_pkg_link_to:
75*4882a593Smuzhiyun                # Don't overwrite an existing template
76*4882a593Smuzhiyun                if column['static_data_template']:
77*4882a593Smuzhiyun                    column['static_data_template'] =\
78*4882a593Smuzhiyun                        pkg_link_template(column['static_data_template'])
79*4882a593Smuzhiyun                else:
80*4882a593Smuzhiyun                    column['static_data_template'] = pkg_link_template(tmplv)
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun                column['static_data_name'] = column['field_name']
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun            elif column['field_name'] is add_recipe_link_to:
85*4882a593Smuzhiyun                # Don't overwrite an existing template
86*4882a593Smuzhiyun                if column['static_data_template']:
87*4882a593Smuzhiyun                    column['static_data_template'] =\
88*4882a593Smuzhiyun                        recipe_link_template(column['static_data_template'])
89*4882a593Smuzhiyun                else:
90*4882a593Smuzhiyun                    column['static_data_template'] =\
91*4882a593Smuzhiyun                        recipe_link_template(tmplv)
92*4882a593Smuzhiyun                column['static_data_name'] = column['field_name']
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun        self.add_column(title="Layer",
95*4882a593Smuzhiyun                        field_name="recipe__layer_version__layer__name",
96*4882a593Smuzhiyun                        hidden=True,
97*4882a593Smuzhiyun                        orderable=True)
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun        layer_branch_template = '''
100*4882a593Smuzhiyun        {%if not data.recipe.layer_version.layer.local_source_dir %}
101*4882a593Smuzhiyun        <span class="text-muted">{{data.recipe.layer_version.branch}}</span>
102*4882a593Smuzhiyun        {% else %}
103*4882a593Smuzhiyun        <span class="text-muted">Not applicable</span>
104*4882a593Smuzhiyun        <span class="glyphicon glyphicon-question-sign get-help" data-original-title="" title="The source code of {{data.recipe.layer_version.layer.name}} is not in a Git repository, so there is no branch associated with it"> </span>
105*4882a593Smuzhiyun        {% endif %}
106*4882a593Smuzhiyun        '''
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun        self.add_column(title="Layer branch",
109*4882a593Smuzhiyun                        field_name="recipe__layer_version__branch",
110*4882a593Smuzhiyun                        hidden=True,
111*4882a593Smuzhiyun                        static_data_name="recipe__layer_version__branch",
112*4882a593Smuzhiyun                        static_data_template=layer_branch_template,
113*4882a593Smuzhiyun                        orderable=True)
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun        git_rev_template = '''
116*4882a593Smuzhiyun        {% if not data.recipe.layer_version.layer.local_source_dir %}
117*4882a593Smuzhiyun        {% with vcs_ref=data.recipe.layer_version.commit %}
118*4882a593Smuzhiyun        {% include 'snippets/gitrev_popover.html' %}
119*4882a593Smuzhiyun        {% endwith %}
120*4882a593Smuzhiyun        {% else %}
121*4882a593Smuzhiyun        <span class="text-muted">Not applicable</span>
122*4882a593Smuzhiyun        <span class="glyphicon glyphicon-question-sign get-help" data-original-title="" title="The source code of {{data.recipe.layer_version.layer.name}} is not in a Git repository, so there is no revision associated with it"> </span>
123*4882a593Smuzhiyun        {% endif %}
124*4882a593Smuzhiyun        '''
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun        self.add_column(title="Layer commit",
127*4882a593Smuzhiyun                        static_data_name='vcs_ref',
128*4882a593Smuzhiyun                        static_data_template=git_rev_template,
129*4882a593Smuzhiyun                        hidden=True)
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun
132*4882a593Smuzhiyunclass BuiltPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
133*4882a593Smuzhiyun    """ Show all the packages built for the selected build """
134*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
135*4882a593Smuzhiyun        super(BuiltPackagesTable, self).__init__(*args, **kwargs)
136*4882a593Smuzhiyun        self.title = "Packages built"
137*4882a593Smuzhiyun        self.default_orderby = "name"
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun        self.empty_state =\
140*4882a593Smuzhiyun            ('<strong>No packages were built.</strong> How did this happen? '
141*4882a593Smuzhiyun             'Well, BitBake reuses as much stuff as possible. '
142*4882a593Smuzhiyun             'If all of the packages needed were already built and available '
143*4882a593Smuzhiyun             'in your build infrastructure, BitBake '
144*4882a593Smuzhiyun             'will not rebuild any of them. This might be slightly confusing, '
145*4882a593Smuzhiyun             'but it does make everything faster.')
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
148*4882a593Smuzhiyun        super(BuiltPackagesTable, self).setup_columns(*args, **kwargs)
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun        def remove_dep_cols(columns):
151*4882a593Smuzhiyun            for column in columns:
152*4882a593Smuzhiyun                # We don't need these fields
153*4882a593Smuzhiyun                if column['static_data_name'] in ['reverse_dependencies',
154*4882a593Smuzhiyun                                                  'dependencies']:
155*4882a593Smuzhiyun                    continue
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun                yield column
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun        self.columns = list(remove_dep_cols(self.columns))
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun
162*4882a593Smuzhiyunclass InstalledPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
163*4882a593Smuzhiyun    """ Show all packages installed in an image """
164*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
165*4882a593Smuzhiyun        super(InstalledPackagesTable, self).__init__(*args, **kwargs)
166*4882a593Smuzhiyun        self.title = "Packages Included"
167*4882a593Smuzhiyun        self.default_orderby = "name"
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun    def make_package_list(self, target):
170*4882a593Smuzhiyun        # The database design means that you get the intermediate objects and
171*4882a593Smuzhiyun        # not package objects like you'd really want so we get them here
172*4882a593Smuzhiyun        pkgs = target.target_installed_package_set.values_list('package',
173*4882a593Smuzhiyun                                                               flat=True)
174*4882a593Smuzhiyun        return Package.objects.filter(pk__in=pkgs)
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun    def get_context_data(self, **kwargs):
177*4882a593Smuzhiyun        context = super(InstalledPackagesTable,
178*4882a593Smuzhiyun                        self).get_context_data(**kwargs)
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun        target = Target.objects.get(pk=kwargs['target_id'])
181*4882a593Smuzhiyun        packages = self.make_package_list(target)
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun        context['packages_sum'] = packages.aggregate(
184*4882a593Smuzhiyun            Sum('installed_size'))['installed_size__sum']
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun        context['target'] = target
187*4882a593Smuzhiyun        return context
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun    def setup_queryset(self, *args, **kwargs):
190*4882a593Smuzhiyun        build = Build.objects.get(pk=kwargs['build_id'])
191*4882a593Smuzhiyun        self.static_context_extra['build'] = build
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun        target = Target.objects.get(pk=kwargs['target_id'])
194*4882a593Smuzhiyun        # We send these separately because in the case of image details table
195*4882a593Smuzhiyun        # we don't have a target just the recipe name as the target
196*4882a593Smuzhiyun        self.static_context_extra['target_name'] = target.target
197*4882a593Smuzhiyun        self.static_context_extra['target_id'] = target.pk
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun        self.static_context_extra['add_links'] = True
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun        self.queryset = self.make_package_list(target)
202*4882a593Smuzhiyun        self.queryset = self.queryset.order_by(self.default_orderby)
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
205*4882a593Smuzhiyun        super(InstalledPackagesTable, self).setup_columns(**kwargs)
206*4882a593Smuzhiyun        self.add_column(title="Installed size",
207*4882a593Smuzhiyun                        static_data_name="installed_size",
208*4882a593Smuzhiyun                        static_data_template="{% load projecttags %}"
209*4882a593Smuzhiyun                        "{{data.size|filtered_filesizeformat}}",
210*4882a593Smuzhiyun                        orderable=True,
211*4882a593Smuzhiyun                        hidden=True)
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun        # Add the template to show installed name for installed packages
214*4882a593Smuzhiyun        install_name_tmpl =\
215*4882a593Smuzhiyun            ('<a href="{% url "package_included_detail" extra.build.pk'
216*4882a593Smuzhiyun             ' extra.target_id data.pk %}">{{data.name}}</a>'
217*4882a593Smuzhiyun             '{% if data.installed_name and data.installed_name !='
218*4882a593Smuzhiyun             ' data.name %}'
219*4882a593Smuzhiyun             '<span class="text-muted"> as {{data.installed_name}}</span>'
220*4882a593Smuzhiyun             ' <span class="glyphicon glyphicon-question-sign get-help hover-help"'
221*4882a593Smuzhiyun             ' title="{{data.name}} was renamed at packaging time and'
222*4882a593Smuzhiyun             ' was installed in your image as {{data.installed_name}}'
223*4882a593Smuzhiyun             '"></span>{% endif %} ')
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun        for column in self.columns:
226*4882a593Smuzhiyun            if column['static_data_name'] == 'name':
227*4882a593Smuzhiyun                column['static_data_template'] = install_name_tmpl
228*4882a593Smuzhiyun                break
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun
231*4882a593Smuzhiyunclass BuiltRecipesTable(BuildTablesMixin):
232*4882a593Smuzhiyun    """ Table to show the recipes that have been built in this build """
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
235*4882a593Smuzhiyun        super(BuiltRecipesTable, self).__init__(*args, **kwargs)
236*4882a593Smuzhiyun        self.title = "Recipes built"
237*4882a593Smuzhiyun        self.default_orderby = "name"
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun    def setup_queryset(self, *args, **kwargs):
240*4882a593Smuzhiyun        build = Build.objects.get(pk=kwargs['build_id'])
241*4882a593Smuzhiyun        self.static_context_extra['build'] = build
242*4882a593Smuzhiyun        self.queryset = build.get_recipes()
243*4882a593Smuzhiyun        self.queryset = self.queryset.order_by(self.default_orderby)
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
246*4882a593Smuzhiyun        recipe_name_tmpl =\
247*4882a593Smuzhiyun            '<a href="{% url "recipe" extra.build.pk data.pk %}">'\
248*4882a593Smuzhiyun            '{{data.name}}'\
249*4882a593Smuzhiyun            '</a>'
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun        recipe_file_tmpl =\
252*4882a593Smuzhiyun            '{{data.file_path}}'\
253*4882a593Smuzhiyun            '{% if data.pathflags %}<i>({{data.pathflags}})</i>'\
254*4882a593Smuzhiyun            '{% endif %}'
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun        git_branch_template = '''
257*4882a593Smuzhiyun        {% if data.layer_version.layer.local_source_dir %}
258*4882a593Smuzhiyun        <span class="text-muted">Not applicable</span>
259*4882a593Smuzhiyun        <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 branch associated with it"> </span>
260*4882a593Smuzhiyun        {% else %}
261*4882a593Smuzhiyun        <span>{{data.layer_version.branch}}</span>
262*4882a593Smuzhiyun        {% endif %}
263*4882a593Smuzhiyun        '''
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun        git_rev_template = '''
266*4882a593Smuzhiyun        {% if data.layer_version.layer.local_source_dir %}
267*4882a593Smuzhiyun        <span class="text-muted">Not applicable</span>
268*4882a593Smuzhiyun        <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 commit associated with it"> </span>
269*4882a593Smuzhiyun        {% else %}
270*4882a593Smuzhiyun        {% with vcs_ref=data.layer_version.commit %}
271*4882a593Smuzhiyun        {% include 'snippets/gitrev_popover.html' %}
272*4882a593Smuzhiyun        {% endwith %}
273*4882a593Smuzhiyun        {% endif %}
274*4882a593Smuzhiyun        '''
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun        depends_on_tmpl = '''
277*4882a593Smuzhiyun        {% with deps=data.r_dependencies_recipe.all %}
278*4882a593Smuzhiyun        {% with count=deps|length %}
279*4882a593Smuzhiyun        {% if count %}
280*4882a593Smuzhiyun        <a class="btn btn-default" title="
281*4882a593Smuzhiyun        <a href='{% url "recipe" extra.build.pk data.pk %}#dependencies'>
282*4882a593Smuzhiyun        {{data.name}}</a> dependencies"
283*4882a593Smuzhiyun        data-content="<ul class='list-unstyled'>
284*4882a593Smuzhiyun        {% for dep in deps|dictsort:"depends_on.name"%}
285*4882a593Smuzhiyun        <li><a href='{% url "recipe" extra.build.pk dep.depends_on.pk %}'>
286*4882a593Smuzhiyun        {{dep.depends_on.name}}</a></li>
287*4882a593Smuzhiyun        {% endfor %}
288*4882a593Smuzhiyun        </ul>">
289*4882a593Smuzhiyun         {{count}}
290*4882a593Smuzhiyun        </a>
291*4882a593Smuzhiyun        {% endif %}{% endwith %}{% endwith %}
292*4882a593Smuzhiyun        '''
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun        rev_depends_tmpl = '''
295*4882a593Smuzhiyun        {% with revs=data.r_dependencies_depends.all %}
296*4882a593Smuzhiyun        {% with count=revs|length %}
297*4882a593Smuzhiyun        {% if count %}
298*4882a593Smuzhiyun        <a class="btn btn-default"
299*4882a593Smuzhiyun        title="
300*4882a593Smuzhiyun        <a href='{% url "recipe" extra.build.pk data.pk %}#brought-in-by'>
301*4882a593Smuzhiyun        {{data.name}}</a> reverse dependencies"
302*4882a593Smuzhiyun        data-content="<ul class='list-unstyled'>
303*4882a593Smuzhiyun        {% for dep in revs|dictsort:"recipe.name" %}
304*4882a593Smuzhiyun        <li>
305*4882a593Smuzhiyun        <a href='{% url "recipe" extra.build.pk dep.recipe.pk %}'>
306*4882a593Smuzhiyun        {{dep.recipe.name}}
307*4882a593Smuzhiyun        </a></li>
308*4882a593Smuzhiyun        {% endfor %}
309*4882a593Smuzhiyun        </ul>">
310*4882a593Smuzhiyun        {{count}}
311*4882a593Smuzhiyun        </a>
312*4882a593Smuzhiyun        {% endif %}{% endwith %}{% endwith %}
313*4882a593Smuzhiyun        '''
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun        self.add_column(title="Recipe",
316*4882a593Smuzhiyun                        field_name="name",
317*4882a593Smuzhiyun                        static_data_name='name',
318*4882a593Smuzhiyun                        orderable=True,
319*4882a593Smuzhiyun                        hideable=False,
320*4882a593Smuzhiyun                        static_data_template=recipe_name_tmpl)
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun        self.add_column(title="Version",
323*4882a593Smuzhiyun                        hideable=False,
324*4882a593Smuzhiyun                        field_name="version")
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun        self.add_column(title="Dependencies",
327*4882a593Smuzhiyun                        static_data_name="dependencies",
328*4882a593Smuzhiyun                        static_data_template=depends_on_tmpl)
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun        self.add_column(title="Reverse dependencies",
331*4882a593Smuzhiyun                        static_data_name="revdeps",
332*4882a593Smuzhiyun                        static_data_template=rev_depends_tmpl,
333*4882a593Smuzhiyun                        help_text='Recipe build-time reverse dependencies'
334*4882a593Smuzhiyun                        ' (i.e. the recipes that depend on this recipe)')
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun        self.add_column(title="Recipe file",
337*4882a593Smuzhiyun                        field_name="file_path",
338*4882a593Smuzhiyun                        static_data_name="file_path",
339*4882a593Smuzhiyun                        static_data_template=recipe_file_tmpl,
340*4882a593Smuzhiyun                        hidden=True)
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun        self.add_column(title="Section",
343*4882a593Smuzhiyun                        field_name="section",
344*4882a593Smuzhiyun                        orderable=True,
345*4882a593Smuzhiyun                        hidden=True)
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun        self.add_column(title="License",
348*4882a593Smuzhiyun                        field_name="license",
349*4882a593Smuzhiyun                        help_text='Multiple license names separated by the'
350*4882a593Smuzhiyun                        ' pipe character indicates a choice between licenses.'
351*4882a593Smuzhiyun                        ' Multiple license names separated by the ampersand'
352*4882a593Smuzhiyun                        ' character indicates multiple licenses exist that'
353*4882a593Smuzhiyun                        ' cover different parts of the source',
354*4882a593Smuzhiyun                        orderable=True)
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun        self.add_column(title="Layer",
357*4882a593Smuzhiyun                        field_name="layer_version__layer__name",
358*4882a593Smuzhiyun                        orderable=True)
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun        self.add_column(title="Layer branch",
361*4882a593Smuzhiyun                        field_name="layer_version__branch",
362*4882a593Smuzhiyun                        static_data_name="layer_version__branch",
363*4882a593Smuzhiyun                        static_data_template=git_branch_template,
364*4882a593Smuzhiyun                        orderable=True,
365*4882a593Smuzhiyun                        hidden=True)
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun        self.add_column(title="Layer commit",
368*4882a593Smuzhiyun                        static_data_name="commit",
369*4882a593Smuzhiyun                        static_data_template=git_rev_template,
370*4882a593Smuzhiyun                        hidden=True)
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun
373*4882a593Smuzhiyunclass BuildTasksTable(BuildTablesMixin):
374*4882a593Smuzhiyun    """ Table to show the tasks that run in this build """
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
377*4882a593Smuzhiyun        super(BuildTasksTable, self).__init__(*args, **kwargs)
378*4882a593Smuzhiyun        self.title = "Tasks"
379*4882a593Smuzhiyun        self.default_orderby = "order"
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun        # Toggle these columns on off for Time/CPU usage/Disk I/O tables
382*4882a593Smuzhiyun        self.toggle_columns = {}
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun    def setup_queryset(self, *args, **kwargs):
385*4882a593Smuzhiyun        build = Build.objects.get(pk=kwargs['build_id'])
386*4882a593Smuzhiyun        self.static_context_extra['build'] = build
387*4882a593Smuzhiyun        self.queryset = build.task_build.filter(~Q(order=None))
388*4882a593Smuzhiyun        self.queryset = self.queryset.order_by(self.default_orderby)
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun    def setup_filters(self, *args, **kwargs):
391*4882a593Smuzhiyun        # Execution outcome types filter
392*4882a593Smuzhiyun        executed_outcome = TableFilter(name="execution_outcome",
393*4882a593Smuzhiyun                                       title="Filter Tasks by 'Executed")
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun        exec_outcome_action_exec = TableFilterActionToggle(
396*4882a593Smuzhiyun            "executed",
397*4882a593Smuzhiyun            "Executed Tasks",
398*4882a593Smuzhiyun            Q(task_executed=True))
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun        exec_outcome_action_not_exec = TableFilterActionToggle(
401*4882a593Smuzhiyun            "not_executed",
402*4882a593Smuzhiyun            "Not Executed Tasks",
403*4882a593Smuzhiyun            Q(task_executed=False))
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun        executed_outcome.add_action(exec_outcome_action_exec)
406*4882a593Smuzhiyun        executed_outcome.add_action(exec_outcome_action_not_exec)
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun        # Task outcome types filter
409*4882a593Smuzhiyun        task_outcome = TableFilter(name="task_outcome",
410*4882a593Smuzhiyun                                   title="Filter Task by 'Outcome'")
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun        for outcome_enum, title in Task.TASK_OUTCOME:
413*4882a593Smuzhiyun            if outcome_enum is Task.OUTCOME_NA:
414*4882a593Smuzhiyun                continue
415*4882a593Smuzhiyun            action = TableFilterActionToggle(
416*4882a593Smuzhiyun                title.replace(" ", "_").lower(),
417*4882a593Smuzhiyun                "%s Tasks" % title,
418*4882a593Smuzhiyun                Q(outcome=outcome_enum))
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun            task_outcome.add_action(action)
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun        # SSTATE outcome types filter
423*4882a593Smuzhiyun        sstate_outcome = TableFilter(name="sstate_outcome",
424*4882a593Smuzhiyun                                     title="Filter Task by 'Cache attempt'")
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun        for sstate_result_enum, title in Task.SSTATE_RESULT:
427*4882a593Smuzhiyun            action = TableFilterActionToggle(
428*4882a593Smuzhiyun                title.replace(" ", "_").lower(),
429*4882a593Smuzhiyun                "Tasks with '%s' attempts" % title,
430*4882a593Smuzhiyun                Q(sstate_result=sstate_result_enum))
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun            sstate_outcome.add_action(action)
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun        self.add_filter(sstate_outcome)
435*4882a593Smuzhiyun        self.add_filter(executed_outcome)
436*4882a593Smuzhiyun        self.add_filter(task_outcome)
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
439*4882a593Smuzhiyun        self.toggle_columns['order'] = len(self.columns)
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun        recipe_name_tmpl =\
442*4882a593Smuzhiyun            '<a href="{% url "recipe" extra.build.pk data.recipe.pk %}">'\
443*4882a593Smuzhiyun            '{{data.recipe.name}}'\
444*4882a593Smuzhiyun            '</a>'
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun        def task_link_tmpl(val):
447*4882a593Smuzhiyun            return ('<a name="task-{{data.order}}"'
448*4882a593Smuzhiyun                    'href="{%% url "task" extra.build.pk data.pk %%}">'
449*4882a593Smuzhiyun                    '%s'
450*4882a593Smuzhiyun                    '</a>') % str(val)
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun        self.add_column(title="Order",
453*4882a593Smuzhiyun                        static_data_name="order",
454*4882a593Smuzhiyun                        static_data_template='{{data.order}}',
455*4882a593Smuzhiyun                        hideable=False,
456*4882a593Smuzhiyun                        orderable=True)
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun        self.add_column(title="Task",
459*4882a593Smuzhiyun                        static_data_name="task_name",
460*4882a593Smuzhiyun                        static_data_template=task_link_tmpl(
461*4882a593Smuzhiyun                            "{{data.task_name}}"),
462*4882a593Smuzhiyun                        hideable=False,
463*4882a593Smuzhiyun                        orderable=True)
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun        self.add_column(title="Recipe",
466*4882a593Smuzhiyun                        static_data_name='recipe__name',
467*4882a593Smuzhiyun                        static_data_template=recipe_name_tmpl,
468*4882a593Smuzhiyun                        hideable=False,
469*4882a593Smuzhiyun                        orderable=True)
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun        self.add_column(title="Recipe version",
472*4882a593Smuzhiyun                        field_name='recipe__version',
473*4882a593Smuzhiyun                        hidden=True)
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun        self.add_column(title="Executed",
476*4882a593Smuzhiyun                        static_data_name="task_executed",
477*4882a593Smuzhiyun                        static_data_template='{{data.get_executed_display}}',
478*4882a593Smuzhiyun                        filter_name='execution_outcome',
479*4882a593Smuzhiyun                        orderable=True)
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun        self.static_context_extra['OUTCOME_FAILED'] = Task.OUTCOME_FAILED
482*4882a593Smuzhiyun        outcome_tmpl = '{{data.outcome_text}}'
483*4882a593Smuzhiyun        outcome_tmpl = ('%s '
484*4882a593Smuzhiyun                        '{%% if data.outcome = extra.OUTCOME_FAILED %%}'
485*4882a593Smuzhiyun                        '<a href="{%% url "build_artifact" extra.build.pk '
486*4882a593Smuzhiyun                        '          "tasklogfile" data.pk %%}">'
487*4882a593Smuzhiyun                        ' <span class="glyphicon glyphicon-download-alt'
488*4882a593Smuzhiyun                        ' get-help" title="Download task log file"></span>'
489*4882a593Smuzhiyun                        '</a> {%% endif %%}'
490*4882a593Smuzhiyun                        '<span class="glyphicon glyphicon-question-sign'
491*4882a593Smuzhiyun                        ' get-help hover-help" style="visibility: hidden;" '
492*4882a593Smuzhiyun                        'title="{{data.get_outcome_help}}"></span>'
493*4882a593Smuzhiyun                        ) % outcome_tmpl
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun        self.add_column(title="Outcome",
496*4882a593Smuzhiyun                        static_data_name="outcome",
497*4882a593Smuzhiyun                        static_data_template=outcome_tmpl,
498*4882a593Smuzhiyun                        filter_name="task_outcome",
499*4882a593Smuzhiyun                        orderable=True)
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun        self.toggle_columns['sstate_result'] = len(self.columns)
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun        self.add_column(title="Cache attempt",
504*4882a593Smuzhiyun                        static_data_name="sstate_result",
505*4882a593Smuzhiyun                        static_data_template='{{data.sstate_text}}',
506*4882a593Smuzhiyun                        filter_name="sstate_outcome",
507*4882a593Smuzhiyun                        orderable=True)
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun        self.toggle_columns['elapsed_time'] = len(self.columns)
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun        self.add_column(
512*4882a593Smuzhiyun            title="Time (secs)",
513*4882a593Smuzhiyun            static_data_name="elapsed_time",
514*4882a593Smuzhiyun            static_data_template='{% load projecttags %}{% load humanize %}'
515*4882a593Smuzhiyun            '{{data.elapsed_time|format_none_and_zero|floatformat:2}}',
516*4882a593Smuzhiyun            orderable=True,
517*4882a593Smuzhiyun            hidden=True)
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun        self.toggle_columns['cpu_time_sys'] = len(self.columns)
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun        self.add_column(
522*4882a593Smuzhiyun            title="System CPU time (secs)",
523*4882a593Smuzhiyun            static_data_name="cpu_time_system",
524*4882a593Smuzhiyun            static_data_template='{% load projecttags %}{% load humanize %}'
525*4882a593Smuzhiyun            '{{data.cpu_time_system|format_none_and_zero|floatformat:2}}',
526*4882a593Smuzhiyun            hidden=True,
527*4882a593Smuzhiyun            orderable=True)
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun        self.toggle_columns['cpu_time_user'] = len(self.columns)
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun        self.add_column(
532*4882a593Smuzhiyun            title="User CPU time (secs)",
533*4882a593Smuzhiyun            static_data_name="cpu_time_user",
534*4882a593Smuzhiyun            static_data_template='{% load projecttags %}{% load humanize %}'
535*4882a593Smuzhiyun            '{{data.cpu_time_user|format_none_and_zero|floatformat:2}}',
536*4882a593Smuzhiyun            hidden=True,
537*4882a593Smuzhiyun            orderable=True)
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun        self.toggle_columns['disk_io'] = len(self.columns)
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun        self.add_column(
542*4882a593Smuzhiyun            title="Disk I/O (ms)",
543*4882a593Smuzhiyun            static_data_name="disk_io",
544*4882a593Smuzhiyun            static_data_template='{% load projecttags %}{% load humanize %}'
545*4882a593Smuzhiyun            '{{data.disk_io|format_none_and_zero|filtered_filesizeformat}}',
546*4882a593Smuzhiyun            hidden=True,
547*4882a593Smuzhiyun            orderable=True)
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun
550*4882a593Smuzhiyunclass BuildTimeTable(BuildTasksTable):
551*4882a593Smuzhiyun    """ Same as tasks table but the Time column is default displayed"""
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
554*4882a593Smuzhiyun        super(BuildTimeTable, self).__init__(*args, **kwargs)
555*4882a593Smuzhiyun        self.default_orderby = "-elapsed_time"
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
558*4882a593Smuzhiyun        super(BuildTimeTable, self).setup_columns(**kwargs)
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hidden'] = True
561*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hideable'] = True
562*4882a593Smuzhiyun        self.columns[self.toggle_columns['sstate_result']]['hidden'] = True
563*4882a593Smuzhiyun        self.columns[self.toggle_columns['elapsed_time']]['hidden'] = False
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun
566*4882a593Smuzhiyunclass BuildCPUTimeTable(BuildTasksTable):
567*4882a593Smuzhiyun    """ Same as tasks table but the CPU usage columns are default displayed"""
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
570*4882a593Smuzhiyun        super(BuildCPUTimeTable, self).__init__(*args, **kwargs)
571*4882a593Smuzhiyun        self.default_orderby = "-cpu_time_system"
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
574*4882a593Smuzhiyun        super(BuildCPUTimeTable, self).setup_columns(**kwargs)
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hidden'] = True
577*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hideable'] = True
578*4882a593Smuzhiyun        self.columns[self.toggle_columns['sstate_result']]['hidden'] = True
579*4882a593Smuzhiyun        self.columns[self.toggle_columns['cpu_time_sys']]['hidden'] = False
580*4882a593Smuzhiyun        self.columns[self.toggle_columns['cpu_time_user']]['hidden'] = False
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun
583*4882a593Smuzhiyunclass BuildIOTable(BuildTasksTable):
584*4882a593Smuzhiyun    """ Same as tasks table but the Disk IO column is default displayed"""
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun    def __init__(self, *args, **kwargs):
587*4882a593Smuzhiyun        super(BuildIOTable, self).__init__(*args, **kwargs)
588*4882a593Smuzhiyun        self.default_orderby = "-disk_io"
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun    def setup_columns(self, *args, **kwargs):
591*4882a593Smuzhiyun        super(BuildIOTable, self).setup_columns(**kwargs)
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hidden'] = True
594*4882a593Smuzhiyun        self.columns[self.toggle_columns['order']]['hideable'] = True
595*4882a593Smuzhiyun        self.columns[self.toggle_columns['sstate_result']]['hidden'] = True
596*4882a593Smuzhiyun        self.columns[self.toggle_columns['disk_io']]['hidden'] = False
597