bitbake: toaster: implement the configuration pagedreyna/configure-detail-view
authorDavid Reyna <David.Reyna@windriver.com>
Fri, 28 Feb 2014 13:55:46 +0000 (05:55 -0800)
committerRichard Purdie <richard.purdie@linuxfoundation.org>
Sun, 9 Mar 2014 19:24:01 +0000 (12:24 -0700)
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>
bitbake/lib/toaster/toastergui/templates/base.html
bitbake/lib/toaster/toastergui/templates/configuration.html
bitbake/lib/toaster/toastergui/templates/configvars.html
bitbake/lib/toaster/toastergui/templatetags/projecttags.py
bitbake/lib/toaster/toastergui/views.py

index 8d4771c..96f093f 100644 (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%">
index 467fbd0..ddfa5af 100644 (file)
@@ -3,6 +3,8 @@
 <li>Configuration</li>
 {% endblock %}
 
+{% load projecttags %}
+
 {% block buildinfomain %}
 <!-- page title -->
 <div class="row-fluid span10">
   <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;">
       </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>
index ae45119..cd41cca 100644 (file)
 <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 %}
index b1e573b..857680b 100644 (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)]
+
index b77be1a..3a362e6 100644 (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)