bitbake: toaster: implement the configuration pagedreyna/configure-detail-view

Update the configuration page with the file list pop-up, implement the file and description filters.

[YOCTO #4259]

(Bitbake rev: 54a767809960b66b2fe2d3bc46aa9c7e040c4ae3)

Signed-off-by: David Reyna <David.Reyna@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
David Reyna 2014-02-28 05:55:46 -08:00 committed by Richard Purdie
parent 31d4bf8484
commit 4717749fd6
5 changed files with 299 additions and 44 deletions

View File

@ -48,6 +48,59 @@ function reload_params(params) {
}
</script>
<script>
$(document).ready(function() {
//show or hide selected columns on load
$("input:checkbox").each(function(){
var selectedType = $(this).val();
if($(this).is(":checked")){
$("."+selectedType).show();
}
else{
$("."+selectedType).hide();
}
});
//edit columns functionality (show / hide table columns)
$("input:checkbox").change();
$("input:checkbox").change(function(){
var selectedType = $(this).val();
if($(this).is(":checked")){
$("."+selectedType).show();
}
else{
$("."+selectedType).hide();
}
});
//turn edit columns dropdown into a multi-select menu
$('.dropdown-menu input, .dropdown-menu label').click(function(e) {
e.stopPropagation();
});
//show help information inside modal dialogs
$("#filter-variables i").tooltip({ html: true, delay: {show: 500} });
//show applied filter tooltip
$('.filtered').tooltip({container:'body', placement:'bottom', delay:{hide:1500}, html:true});
//hide the applied filter tooltip when you click the filter button
$('.btn-primary').on('click', function () {
$('.tooltip').hide();
});
$('.full-variable, .full-variable-hide').hide();
$('.full-variable-show').click(function(){
$('.full-variable').slideDown(function(){
$('.full-variable-hide').show();
});
$(this).hide();
});
$('.full-variable-hide').click(function(){
$(this).hide();
$('.full-variable').slideUp(function(){
$('.full-variable-show').show();
});
});
});
</script>
</head>
<body style="height: 100%">

View File

@ -3,6 +3,8 @@
<li>Configuration</li>
{% endblock %}
{% load projecttags %}
{% block buildinfomain %}
<!-- page title -->
<div class="row-fluid span10">
@ -22,15 +24,19 @@
<div id="summary" class="tab-pane active">
<h3>Build configuration</h3>
<dl class="dl-horizontal">
<dt>BitBake version</dt><dd>1.19.1</dd>
<dt>Build system</dt><dd>x86_64-linux</dd>
<dt>Host distribution</dt><dd>Ubuntu-12.04</dd>
<dt>Target system</dt><dd>i586-poky-linux</dd>
<dt><i class="icon-question-sign get-help" data-toggle="tooltip" title="Specifies the target device for which the image is built"></i> Machine</dt><dd>atom-pc</dd>
<dt><i class="icon-question-sign get-help" data-toggle="tooltip" title="The short name of the distribution"></i> Distro</dt><dd>poky</dd>
<dt>Distro version</dt><dd>1.4+snapshot-20130718</dd>
<dt>Tune features</dt><dd>m32 i586</dd>
<dt>Target(s)</dt><dd>core-image-sato</dd>
{%if BB_VERSION %}<dt>BitBake version</dt><dd>{{BB_VERSION}}</dd> {% endif %}
{%if BUILD_SYS %}<dt>Build system</dt><dd>{{BUILD_SYS}}</dd> {% endif %}
{%if NATIVELSBSTRING %}<dt>Host distribution</dt><dd>{{NATIVELSBSTRING}}</dd> {% endif %}
{%if TARGET_SYS %}<dt>Target system</dt><dd>{{TARGET_SYS}}</dd> {% endif %}
{%if MACHINE %}<dt>Machine</dt><dd>{{MACHINE}}</dd> {% endif %}
{%if DISTRO %}<dt>Distro</dt><dd>{{DISTRO}}</dd> {% endif %}
{%if DISTRO_VERSION %}<dt>Distro version</dt><dd>{{DISTRO_VERSION}}</dd> {% endif %}
{%if TUNE_FEATURES %}<dt>Tune features</dt><dd>{{TUNE_FEATURES}}</dd> {% endif %}
{%if TARGET_FPU %}<dt>Target FPU</dt><dd>{{TARGET_FPU}}</dd> {% endif %}
{%if targets.all %}<dt>Target(s)</dt>
<dd> <ul> {% for target in targets.all %}
<li>{{target.target}}{%if forloop.counter > 1 %}<br>{% endif %}</li>
{% endfor %} </ul> </dd> {% endif %}
</dl>
<h3>Layers</h3>
<div class="span9" style="margin-left:0px;">
@ -45,7 +51,13 @@
</thead>
<tbody>{% for lv in build.layer_version_build.all %}
<tr>
<td>{{lv.layer.name}}<a href="{{lv.layer.layer_index_url}}" target="_blank">&nbsp;<i class="icon-share get-info"></i></a></td><td>{{lv.branch}}</td><td class="layer_commit"><a data-content="{{lv.commit}}" title="" href="#" class="btn" data-original-title="">{{lv.commit|slice:":8"}}...</a></td><td>{{lv.layer.local_path}}</td>
<td>{{lv.layer.name}}</td>
<td>{{lv.branch}}</td>
<td> <a class="btn" data-content="<ul class='unstyled'>
<li>{{lv.commit}}</li> </ul>">
{{lv.commit|truncatechars:13}}
</a></td>
<td>{{lv.layer.local_path}}</td>
</tr>{% endfor %}
</tbody>
</table>

View File

@ -3,38 +3,110 @@
<li>Configuration</li>
{% endblock %}
{% load projecttags %}
{% block buildinfomain %}
<!-- page title -->
<div class="row-fluid span10">
<div class="page-header">
<h1>Configuration</h1>
<h1>
{% if request.GET.filter or request.GET.search and objects.count > 0 %}
{{objects.paginator.count}} variable{{objects.paginator.count|pluralize}} found
{%elif objects.paginator.count == 0%}
No variables
{%else%}
Configuration
{%endif%}
</h1>
</div>
</div>
<!-- configuration table -->
<div class="row-fluid pull-right span10" id="navTab">
<ul class="nav nav-pills">
<ul class="nav nav-pills">
<li class=""><a href="{% url 'configuration' build.id %}">Summary</a></li>
<li class="active"><a href="#" >BitBake variables</a></li>
</ul>
</ul>
<!-- variables -->
<div id="variables" class="tab-pane">
{% include "basetable_top.html" %}
{% include "basetable_top.html" %}
{% for variable in objects %}
<tr class="data">
<td class="variable">{{variable.variable_name}}</td>
<td class="variable_value">{{variable.variable_value}}</td>
<td class="file">{% for vh in variable.vhistory_set.all %}{{vh.operation}} in {{vh.file_name}}:{{vh.line_number}}<br/>{%endfor%}</td>
<td class="description">{% if variable.description %}{{variable.description}}{% endif %}</td>
{% for variable in objects %}
<tr class="data">
<td class="variable_name"><a data-toggle="modal" href="#variable-{{variable.pk}}">{{variable.variable_name}}</a></td>
<td class="variable_value"><a data-toggle="modal" href="#variable-{{variable.pk}}">{{variable.variable_value|truncatechars:153}}</a></td>
<td class="file"><a data-toggle="modal" href="#variable-{{variable.pk}}">
{% if variable.vhistory.all %} {% autoescape off %}
{{variable.vhistory.all | filter_setin_files:file_filter }}
{% endautoescape %} {% endif %}
</a></td>
<td class="description">
{% if variable.description %}
{{variable.description}}
<a href="http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-{{variable.variable_name|variable_parent_name}}" target="_blank">
<i class="icon-share get-info"></i></a>
{% endif %}
</td>
</tr>
{% endfor %}
{% include "basetable_bottom.html" %}
</div> <!-- endvariables -->
</div> <!-- endvariables -->
<!-- file list popups -->
{% for variable in objects %}
{% if variable.vhistory.count %}
<div id="variable-{{variable.pk}}" class="modal hide fade" tabindex="-1" role="dialog">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h3>History of {{variable.variable_name}}</h3>
</div>
<div class="modal-body">
{% if variable.variable_value %}
{% if variable.variable_value|length < 570 %}
<h4>{{variable.variable_name}} value is:</h4>
<p>
{{variable.variable_value}}
</p>
{% else %}
<h4>{{variable.variable_name}} value is:</h4>
<p>
<span>{{variable.variable_value|string_slice:':570'}}
<span class="full-variable"> {{variable.variable_value|string_slice:'570:'}}
</span>
<a class="btn btn-mini full-variable-show">...</a>
</span>
</p>
<a class="btn btn-mini full-variable-hide">Collapse variable value<i class="icon-caret-up"></i>
</a>
{% endif %}
{% else %}
<div class="alert alert-info">The value of <strong>{{variable.variable_name}}</strong> is an empty string</div>
{% endif %}
<h4>The value was set in the following configuration files:</h4>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Order</th>
<th>Configuration file</th>
<th>Operation</th>
<th>Line number</th>
</tr>
</thead>
<tbody>
{% for vh in variable.vhistory.all %}
<tr>
<td>{{forloop.counter}}</td><td>{{vh.file_name}}</td><td>{{vh.operation}}</td><td>{{vh.line_number}}</td>
</tr>
{%endfor%}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endfor %}
</div> <!-- buildinfomain -->
</div>
{% endblock %}

View File

@ -129,3 +129,62 @@ def check_filter_status(options, filter):
if filter == option[1]:
return ""
return "checked"
@register.filter
def variable_parent_name(value):
""" filter extended variable names to the parent name
"""
value=re.sub('_\$.*', '', value)
return re.sub('_[a-z].*', '', value)
@register.filter
def filter_setin_files(file_list,matchstr):
""" filter/search the 'set in' file lists. Note
that this output is not autoescaped to allow
the <p> marks, but this is safe as the data
is file paths
"""
# no filters, show last file (if any)
if matchstr == ":":
if file_list:
return file_list[len(file_list)-1].file_name
else:
return ''
search, filter = matchstr.partition(':')[::2]
htmlstr=""
# match only filters
if search == '':
for i in range(len(file_list)):
if file_list[i].file_name.find(filter) >= 0:
htmlstr += file_list[i].file_name + "<p>"
return htmlstr
# match only search string, plus always last file
if filter == "":
for i in range(len(file_list)-1):
if file_list[i].file_name.find(search) >= 0:
htmlstr += file_list[i].file_name + "<p>"
htmlstr += file_list[len(file_list)-1].file_name
return htmlstr
# match filter or search string
for i in range(len(file_list)):
if (file_list[i].file_name.find(filter) >= 0) or (file_list[i].file_name.find(search) >= 0):
htmlstr += file_list[i].file_name + "<p>"
return htmlstr
@register.filter
def string_slice(strvar,slicevar):
""" slice a string with |string_slice:'[first]:[last]'
"""
first,last= slicevar.partition(':')[::2]
if first=='':
return strvar[:int(last)]
elif last=='':
return strvar[int(first):]
else:
return strvar[int(first):int(last)]

View File

@ -733,59 +733,118 @@ def recipes(request, build_id):
def configuration(request, build_id):
template = 'configuration.html'
context = {'build': Build.objects.filter(pk=build_id)[0]}
variables = Variable.objects.filter(build=build_id)
BB_VERSION=variables.filter(variable_name='BB_VERSION')[0].variable_value
BUILD_SYS=variables.filter(variable_name='BUILD_SYS')[0].variable_value
NATIVELSBSTRING=variables.filter(variable_name='NATIVELSBSTRING')[0].variable_value
TARGET_SYS=variables.filter(variable_name='TARGET_SYS')[0].variable_value
MACHINE=variables.filter(variable_name='MACHINE')[0].variable_value
DISTRO=variables.filter(variable_name='DISTRO')[0].variable_value
DISTRO_VERSION=variables.filter(variable_name='DISTRO_VERSION')[0].variable_value
TUNE_FEATURES=variables.filter(variable_name='TUNE_FEATURES')[0].variable_value
TARGET_FPU=variables.filter(variable_name='TARGET_FPU')[0].variable_value
targets = Target.objects.filter(build=build_id)
context = {
'objectname': 'configuration',
'object_search_display':'variables',
'filter_search_display':'variables',
'build': Build.objects.filter(pk=build_id)[0],
'BB_VERSION':BB_VERSION,
'BUILD_SYS':BUILD_SYS,
'NATIVELSBSTRING':NATIVELSBSTRING,
'TARGET_SYS':TARGET_SYS,
'MACHINE':MACHINE,
'DISTRO':DISTRO,
'DISTRO_VERSION':DISTRO_VERSION,
'TUNE_FEATURES':TUNE_FEATURES,
'TARGET_FPU':TARGET_FPU,
'targets':targets,
}
return render(request, template, context)
def configvars(request, build_id):
template = 'configvars.html'
mandatory_parameters = { 'count': 100, 'page' : 1};
mandatory_parameters = { 'count': 100, 'page' : 1, 'orderby':'variable_name:+', 'filter':'description__regex:.+'};
retval = _verify_parameters( request.GET, mandatory_parameters )
if retval:
return _redirect_parameters( 'configvars', request.GET, mandatory_parameters, build_id = build_id)
(filter_string, search_term, ordering_string) = _search_tuple(request, Variable)
queryset = Variable.objects.filter(build=build_id)
queryset = Variable.objects.filter(build=build_id).exclude(variable_name__istartswith='B_').exclude(variable_name__istartswith='do_')
queryset = _get_queryset(Variable, queryset, filter_string, search_term, ordering_string)
# remove duplicate records from multiple search hits in the VariableHistory table
queryset = queryset.distinct()
# remove records where the value is empty AND there are no history files
queryset = queryset.exclude(variable_value='',vhistory__file_name__isnull=True)
variables = _build_page_range(Paginator(queryset, request.GET.get('count', 50)), request.GET.get('page', 1))
file_filter= search_term + ":"
if filter_string.find('conf/local.conf') > 0:
file_filter += 'conf/local.conf'
if filter_string.find('conf/machine/') > 0:
file_filter += 'conf/machine/'
if filter_string.find('conf/distro/') > 0:
file_filter += 'conf/distro/'
if filter_string.find('/bitbake.conf') > 0:
file_filter += '/bitbake.conf'
context = {
'objectname': 'configvars',
'object_search_display':'variables',
'filter_search_display':'variables',
'file_filter': file_filter,
'build': Build.objects.filter(pk=build_id)[0],
'objects' : variables,
# Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
'tablecols' : [
{'name': 'Variable ',
'qhelp': "Base variable expanded name",
'clclass' : 'variable',
'qhelp': "BitBake is a generic task executor that considers a list of tasks with dependencies and handles metadata that consists of variables in a certain format that get passed to the tasks",
'dclass' : "span3",
'orderfield': _get_toggle_order(request, "variable_name"),
'ordericon':_get_toggle_order_icon(request, "variable_name"),
},
{'name': 'Value ',
'qhelp': "The value assigned to the variable",
'clclass': 'variable_value',
'dclass': "span4",
'orderfield': _get_toggle_order(request, "variable_value"),
'ordericon':_get_toggle_order_icon(request, "variable_value"),
},
{'name': 'Configuration file(s) ',
'qhelp': "The configuration file(s) that touched the variable value",
'clclass': 'file',
{'name': 'Set in file',
'qhelp': "The last configuration file that touched the variable value",
'clclass': 'file', 'hidden' : 0,
'dclass': "span6",
'orderfield': _get_toggle_order(request, "variable_vhistory__file_name"),
'filter' : { 'class': 'file', 'label' : 'Show only', 'options' : {
}
}
'orderfield': _get_toggle_order(request, "vhistory__file_name"),
'ordericon':_get_toggle_order_icon(request, "vhistory__file_name"),
'filter' : {
'class' : 'vhistory__file_name',
'label': 'Show:',
'options' : [
('Local configuration variables', 'vhistory__file_name__contains:conf/local.conf'),
('Machine configuration variables', 'vhistory__file_name__contains:conf/machine/'),
('Distro configuration variables', 'vhistory__file_name__contains:conf/distro/'),
('Layer configuration variables', 'vhistory__file_name__contains:conf/layer.conf'),
('bitbake.conf variables', 'vhistory__file_name__contains:/bitbake.conf'),
]
},
},
{'name': 'Description ',
'qhelp': "A brief explanation of a variable",
'clclass': 'description',
'qhelp': "A brief explanation of the variable",
'clclass': 'description', 'hidden' : 0,
'dclass': "span5",
'orderfield': _get_toggle_order(request, "description"),
'filter' : { 'class' : 'description', 'label' : 'No', 'options' : {
}
},
}
]
'filter' : {
'class' : 'description',
'label': 'Show:',
'options' : [
('Variables with description', 'description__regex:.+'),
]
},
},
],
}
return render(request, template, context)