bitbake: toaster: port table for Built packages to ToasterTable

This is the table that displays all the packages built in the build.
Build -> Packages. Adds a template snippet for the git revision popover.

(Bitbake rev: df62f38ff4e634544c9b1e97c5f6ca45e84a4f1e)

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Michael Wood 2016-05-26 16:12:20 +01:00 committed by Richard Purdie
parent b63f9518e7
commit a786ac14f1
8 changed files with 203 additions and 219 deletions

View File

@ -0,0 +1,152 @@
#
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# BitBake Toaster Implementation
#
# Copyright (C) 2016 Intel Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from orm.models import Build
import toastergui.tables as tables
from toastergui.widgets import ToasterTable
class BuildTablesMixin(ToasterTable):
def get_context_data(self, **kwargs):
# We need to be explicit about which superclass we're calling here
# Otherwise the MRO gets in a right mess
context = ToasterTable.get_context_data(self, **kwargs)
context['build'] = Build.objects.get(pk=kwargs['build_id'])
return context
class BuiltPackagesTableBase(tables.PackagesTable):
""" Table to display all the packages built in a build """
def __init__(self, *args, **kwargs):
super(BuiltPackagesTableBase, self).__init__(*args, **kwargs)
self.title = "Packages built"
self.default_orderby = "name"
def setup_queryset(self, *args, **kwargs):
build = Build.objects.get(pk=kwargs['build_id'])
self.static_context_extra['build'] = build
self.queryset = build.package_set.all().exclude(recipe=None)
self.queryset = self.queryset.order_by(self.default_orderby)
def setup_columns(self, *args, **kwargs):
super(BuiltPackagesTableBase, self).setup_columns(*args, **kwargs)
def pkg_link_template(val):
""" return the template used for the link with the val as the
element value i.e. inside the <a></a>"""
return ('''
<a href="
{%% url "package_built_detail" extra.build.pk data.pk %%}
">%s</a>
''' % val)
def recipe_link_template(val):
return ('''
{%% if data.recipe %%}
<a href="
{%% url "recipe" extra.build.pk data.recipe.pk %%}
">%(value)s</a>
{%% else %%}
%(value)s
{%% endif %%}
''' % {'value': val})
add_pkg_link_to = ['name', 'version', 'size', 'license']
add_recipe_link_to = ['recipe__name', 'recipe__version']
# Add the recipe and pkg build links to the required columns
for column in self.columns:
# Convert to template field style accessors
tmplv = column['field_name'].replace('__', '.')
tmplv = "{{data.%s}}" % tmplv
if column['field_name'] in add_pkg_link_to:
# Don't overwrite an existing template
if column['static_data_template']:
column['static_data_template'] =\
pkg_link_template(column['static_data_template'])
else:
column['static_data_template'] = pkg_link_template(tmplv)
column['static_data_name'] = column['field_name']
elif column['field_name'] in add_recipe_link_to:
# Don't overwrite an existing template
if column['static_data_template']:
column['static_data_template'] =\
recipe_link_template(column['static_data_template'])
else:
column['static_data_template'] =\
recipe_link_template(tmplv)
column['static_data_name'] = column['field_name']
self.add_column(title="Layer",
field_name="recipe__layer_version__layer__name",
hidden=True,
orderable=True)
self.add_column(title="Layer branch",
field_name="recipe__layer_version__branch",
hidden=True,
orderable=True)
git_rev_template = '''
{% with vcs_ref=data.recipe.layer_version.commit %}
{% include 'snippets/gitrev_popover.html' %}
{% endwith %}
'''
self.add_column(title="Layer commit",
static_data_name='vcs_ref',
static_data_template=git_rev_template,
hidden=True)
class BuiltPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
""" Show all the packages built for the selected build """
def __init__(self, *args, **kwargs):
super(BuiltPackagesTable, self).__init__(*args, **kwargs)
self.title = "Packages built"
self.default_orderby = "name"
self.empty_state =\
('<strong>No packages were built.</strong> How did this happen?'
'Well, BitBake reuses as much stuff as possible.'
'If all of the packages needed were already built and available'
'in your build infrastructure, BitBake'
'will not rebuild any of them. This might be slightly confusing,'
'but it does make everything faster.')
def setup_columns(self, *args, **kwargs):
super(BuiltPackagesTable, self).setup_columns(*args, **kwargs)
def remove_dep_cols(columns):
for column in columns:
# We don't need these fields
if column['static_data_name'] in ['reverse_dependencies',
'dependencies']:
continue
yield column
self.columns = list(remove_dep_cols(self.columns))

View File

@ -146,15 +146,8 @@ class LayersTable(ToasterTable):
static_data_template=git_dir_template)
revision_template = '''
{% load projecttags %}
{% with vcs_ref=data.get_vcs_reference %}
{% if vcs_ref|is_shaid %}
<a class="btn" data-content="<ul class='unstyled'> <li>{{vcs_ref}}</li> </ul>">
{{vcs_ref|truncatechars:10}}
</a>
{% else %}
{{vcs_ref}}
{% endif %}
{% include 'snippets/gitrev_popover.html' %}
{% endwith %}
'''
@ -718,6 +711,7 @@ class PackagesTable(ToasterTable):
self.add_column(title="Approx Size",
orderable=True,
field_name="size",
static_data_name="size",
static_data_template="{% load projecttags %} \
{{data.size|filtered_filesizeformat}}")

View File

@ -23,10 +23,19 @@
{% block localbreadcrumb %}{% endblock %}
</ul>
<script>
$( function () {
$(document).ready(function(){
$('#breadcrumb > li').append('<span class="divider">&rarr;</span>');
$('#breadcrumb > li:last').addClass("active");
$('#breadcrumb > li:last > span').remove();
$("#build-menu li a").each(function(){
/* Set the page active state in the Build menu */
if (window.location.href.split('?')[0] === $(this).prop("href")){
$(this).parent().addClass("active");
} else {
$(this).parent().removeClass("active");
}
});
});
</script>
</div>
@ -35,7 +44,7 @@
<div class="row">
<!-- begin left sidebar container -->
<div id="nav" class="col-md-2">
<ul class="nav nav-list well">
<ul class="nav nav-list well" id="build-menu">
<li
{% if request.resolver_match.url_name == 'builddashboard' %}
class="active"
@ -54,25 +63,14 @@
{% block nav-configuration %}
<li><a href="{% url 'configuration' build.pk %}">Configuration</a></li>
{% endblock %}
{% block nav-tasks %}
<li><a href="{% url 'tasks' build.pk %}">Tasks</a></li>
{% endblock %}
{% block nav-recipes %}
<li><a href="{% url 'recipes' build.pk %}">Recipes</a></li>
{% endblock %}
{% block nav-packages %}
<li><a href="{% url 'packages' build.pk %}">Packages</a></li>
{% endblock %}
<li class="nav-header">Performance</li>
{% block nav-buildtime %}
<li><a href="{% url 'buildtime' build.pk %}">Time</a></li>
{% endblock %}
{% block nav-cputime %}
<li><a href="{% url 'cputime' build.pk %}">CPU usage</a></li>
{% endblock %}
{% block nav-diskio %}
<li><a href="{% url 'diskio' build.pk %}">Disk I/O</a></li>
{% endblock %}
<li class="divider"></li>

View File

@ -1,106 +0,0 @@
{% extends "basebuildpage.html" %}
{% load projecttags %}
{% block title %} Packages built - {{build.target_set.all|dictsort:"target"|join:", "}} {{build.machine}} - {{build.project.name}} - Toaster {% endblock %}
{% block localbreadcrumb %}
<li>Packages</li>
{% endblock %}
{% block nav-packages %}
<li class="active"><a href="{% url 'packages' build.pk %}">Packages</a></li>
{% endblock %}
{% block buildinfomain %}
<div class="col-md-10">
{% if not request.GET.filter and not request.GET.search and not objects.paginator.count %}
<!-- Empty - no data in database -->
<div class="page-header">
<h1>
Packages
</h1>
</div>
<div class="alert alert-info lead">
<strong>No packages were built.</strong> How did this happen? Well, BitBake reuses as much stuff as possible.
If all of the packages needed were already built and available in your build infrastructure, BitBake
will not rebuild any of them. This might be slightly confusing, but it does make everything faster.
</div>
{% else %}
<div class="page-header">
<h1>
{% if request.GET.search and objects.paginator.count > 0 %}
{{objects.paginator.count}} package{{objects.paginator.count|pluralize}} found
{%elif request.GET.search and objects.paginator.count == 0%}
No packages found
{%else%}
Packages
{%endif%}
</h1>
</div>
{% if objects.paginator.count == 0 %}
<div class="alert">
<form class="no-results input-append" id="searchform">
<input id="search" name="search" class="input-xxlarge" type="text" value="{{request.GET.search}}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="input-append-addon btn" tabindex="-1"><i class="glyphicon glyphicon-remove"></i></a>{% endif %}
<button class="btn" type="submit" value="Search">Search</button>
<button class="btn btn-link" onclick="javascript:$('#search').val('');searchform.submit()">Show all packages</button>
</form>
</div>
{% else %}
{% include "basetable_top.html" %}
{% for package in objects %}
<tr class="data">
<!-- Package -->
<td class="package_name"><a href="{% url "package_built_detail" build.pk package.pk %}">{{package.name}}</a></td>
<!-- Package Version -->
<td class="package_version">{%if package.version%}<a href="{% url "package_built_detail" build.pk package.pk %}">{{package.version}}-{{package.revision}}</a>{%endif%}</td>
<!-- Package Size -->
<td class="size sizecol">{{package.size|filtered_filesizeformat}}</td>
<!-- License -->
<td class="license">{{package.license}}</td>
{%if package.recipe%}
<!-- Recipe -->
<td class="recipe__name"><a href="{% url "recipe" build.pk package.recipe.pk %}">{{package.recipe.name}}</a></td>
<!-- Recipe Version -->
<td class="recipe__version"><a href="{% url "recipe" build.pk package.recipe.pk %}">{{package.recipe.version}}</a></td>
<!-- Layer -->
<td class="recipe__layer_version__layer__name">{{package.recipe.layer_version.layer.name}}</td>
<!-- Layer branch -->
<td class="recipe__layer_version__branch">{{package.recipe.layer_version.branch}}</td>
<!-- Layer commit -->
<td class="recipe__layer_version__layer__commit">
<a class="btn"
data-content="<ul class='list-unstyled'>
<li>{{package.recipe.layer_version.commit}}</li>
</ul>">
{{package.recipe.layer_version.commit|truncatechars:13}}
</a>
</td>
<!-- Layer directory -->
{%else%}
<td class="recipe__name"></td>
<td class="recipe__version"></td>
<td class="recipe__layer_version__layer__name"></td>
<td class="recipe__layer_version__branch"></td>
<td class="recipe__layer_version__layer__commit"></td>
<td class="recipe__layer_version__local_path"></td>
{%endif%}
</tr>
{% endfor %}
{% include "basetable_bottom.html" %}
{% endif %} {# objects.paginator.count #}
{% endif %} {# Empty #}
</div>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "basebuildpage.html" %}
{% load projecttags %}
{% block title %} {{title}} - {{build.target_set.all|dictsort:"target"|join:", "}} {{build.machine}} - {{build.project.name}} - Toaster {% endblock %}
{% block localbreadcrumb %}
<li>{{title}}</li>
{% endblock %}
{% block nav-packages %}
{% endblock %}
{% block buildinfomain %}
<div class="span10">
{% url 'builtpackagestable' build.id as xhr_table_url %}
<div class="page-header">
<h1>
{{title}} (<span class="table-count-{{table_name}}">0</span>) </h2>
</h1>
</div>
{% include "toastertable.html" %}
</div>
{% endblock %}

View File

@ -0,0 +1,8 @@
{% load projecttags %}
{% if vcs_ref|is_shaid %}
<a class="btn" data-content="<ul class='unstyled'> <li>{{vcs_ref}}</li> </ul>">
{{vcs_ref|truncatechars:10}}
</a>
{% else %}
{{vcs_ref}}
{% endif %}

View File

@ -21,6 +21,7 @@ from django.views.generic import RedirectView, TemplateView
from django.http import HttpResponseBadRequest
from toastergui import tables
from toastergui import buildtables
from toastergui import typeaheads
from toastergui import api
@ -44,7 +45,11 @@ urlpatterns = patterns('toastergui.views',
url(r'^build/(?P<build_id>\d+)/recipe/(?P<recipe_id>\d+)$', 'recipe', name='recipe'),
url(r'^build/(?P<build_id>\d+)/recipe_packages/(?P<recipe_id>\d+)$', 'recipe_packages', name='recipe_packages'),
url(r'^build/(?P<build_id>\d+)/packages/$', 'bpackage', name='packages'),
url(r'^build/(?P<build_id>\d+)/packages/$',
buildtables.BuiltPackagesTable.as_view(
template_name="buildinfo-toastertable.html"),
name='packages'),
url(r'^build/(?P<build_id>\d+)/package/(?P<package_id>\d+)$', 'package_built_detail',
name='package_built_detail'),
url(r'^build/(?P<build_id>\d+)/package_built_dependencies/(?P<package_id>\d+)$',

View File

@ -1493,96 +1493,6 @@ def configvars(request, build_id):
_set_parameters_values(pagesize, orderby, request)
return response
def bpackage(request, build_id):
template = 'bpackage.html'
(pagesize, orderby) = _get_parameters_values(request, 100, 'name:+')
mandatory_parameters = { 'count' : pagesize, 'page' : 1, 'orderby' : orderby }
retval = _verify_parameters( request.GET, mandatory_parameters )
if retval:
return _redirect_parameters( 'packages', request.GET, mandatory_parameters, build_id = build_id)
(filter_string, search_term, ordering_string) = _search_tuple(request, Package)
queryset = Package.objects.filter(build = build_id).filter(size__gte=0)
queryset = _get_queryset(Package, queryset, filter_string, search_term, ordering_string, 'name')
packages = _build_page_range(Paginator(queryset, pagesize),request.GET.get('page', 1))
build = Build.objects.get( pk = build_id )
context = {
'objectname': 'packages built',
'build': build,
'project': build.project,
'objects' : packages,
'default_orderby' : 'name:+',
'tablecols':[
{
'name':'Package',
'qhelp':'Packaged output resulting from building a recipe',
'orderfield': _get_toggle_order(request, "name"),
'ordericon':_get_toggle_order_icon(request, "name"),
},
{
'name':'Package version',
'qhelp':'The package version and revision',
},
{
'name':'Size',
'qhelp':'The size of the package',
'orderfield': _get_toggle_order(request, "size", True),
'ordericon':_get_toggle_order_icon(request, "size"),
'orderkey' : 'size',
'clclass': 'size', 'hidden': 0,
'dclass' : 'span2',
},
{
'name':'License',
'qhelp':'The license under which the package is distributed. 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',
'orderfield': _get_toggle_order(request, "license"),
'ordericon':_get_toggle_order_icon(request, "license"),
'orderkey' : 'license',
'clclass': 'license', 'hidden': 1,
},
{
'name':'Recipe',
'qhelp':'The name of the recipe building the package',
'orderfield': _get_toggle_order(request, "recipe__name"),
'ordericon':_get_toggle_order_icon(request, "recipe__name"),
'orderkey' : 'recipe__name',
'clclass': 'recipe__name', 'hidden': 0,
},
{
'name':'Recipe version',
'qhelp':'Version and revision of the recipe building the package',
'clclass': 'recipe__version', 'hidden': 1,
},
{
'name':'Layer',
'qhelp':'The name of the layer providing the recipe that builds the package',
'orderfield': _get_toggle_order(request, "recipe__layer_version__layer__name"),
'ordericon':_get_toggle_order_icon(request, "recipe__layer_version__layer__name"),
'orderkey' : 'recipe__layer_version__layer__name',
'clclass': 'recipe__layer_version__layer__name', 'hidden': 1,
},
{
'name':'Layer branch',
'qhelp':'The Git branch of the layer providing the recipe that builds the package',
'orderfield': _get_toggle_order(request, "recipe__layer_version__branch"),
'ordericon':_get_toggle_order_icon(request, "recipe__layer_version__branch"),
'orderkey' : 'recipe__layer_version__branch',
'clclass': 'recipe__layer_version__branch', 'hidden': 1,
},
{
'name':'Layer commit',
'qhelp':'The Git commit of the layer providing the recipe that builds the package',
'clclass': 'recipe__layer_version__layer__commit', 'hidden': 1,
},
]
}
response = render(request, template, context)
_set_parameters_values(pagesize, orderby, request)
return response
def bfile(request, build_id, package_id):
template = 'bfile.html'
files = Package_File.objects.filter(package = package_id)