All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Port build tables to ToasterTables
@ 2016-05-26 15:12 Michael Wood
  2016-05-26 15:12 ` [PATCH v2 1/9] toaster: ToasterTable widget add an 'a' to Name in Exception class Michael Wood
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

Port the main build tables in the build analysis area to ToasterTables.
This is the table for Installed packages, Tasks, Recipes, Packages, Time, CPU usage and Disk I/O

Changes in v2
 - Addressed issues identified by Belen in v1 apart from 'Size' reporting question where we are no just having both as columns if applicable 
 - Reworked the display of dependencies popover

Michael Wood (9):
  toaster: ToasterTable widget add an 'a' to Name in Exception class
  toaster: port table for Built packages to ToasterTable
  toaster: port Built recipes table to toastertables
  toaster: port Task tables to ToasterTables widget
  toaster: port Installed packages table to ToasterTable
  toaster: table.js Add the ability to highlight a particular row
  toaster: tests Add new build tables to tests
  toaster: widgets ToasterTable Catch template rendering exceptions
  toaster: Rework displaying package dependencies across Toaster

 bitbake/lib/toaster/orm/models.py                  |  61 +-
 bitbake/lib/toaster/toastergui/buildtables.py      | 574 ++++++++++++++++++
 .../fixtures/toastergui-unittest-data.xml          |  91 ++-
 bitbake/lib/toaster/toastergui/static/js/table.js  |   9 +
 bitbake/lib/toaster/toastergui/tables.py           |  25 +-
 .../toastergui/templates/basebuildpage.html        |  26 +-
 .../lib/toaster/toastergui/templates/bpackage.html | 108 ----
 .../toastergui/templates/builddashboard.html       |   4 +-
 .../templates/buildinfo-toastertable.html          |  25 +
 .../toaster/toastergui/templates/buildtime.html    |   4 -
 .../lib/toaster/toastergui/templates/recipes.html  | 113 ----
 .../templates/snippets/gitrev_popover.html         |   8 +
 .../snippets/pkg_dependencies_popover.html         |  44 +-
 .../snippets/pkg_revdependencies_popover.html      |  46 +-
 .../lib/toaster/toastergui/templates/target.html   | 115 +---
 bitbake/lib/toaster/toastergui/templates/task.html |   2 +-
 .../lib/toaster/toastergui/templates/tasks.html    | 143 -----
 .../toaster/toastergui/templatetags/projecttags.py |   8 +
 bitbake/lib/toaster/toastergui/tests.py            |  40 +-
 bitbake/lib/toaster/toastergui/urls.py             |  46 +-
 bitbake/lib/toaster/toastergui/views.py            | 654 +--------------------
 bitbake/lib/toaster/toastergui/widgets.py          |  31 +-
 22 files changed, 934 insertions(+), 1243 deletions(-)
 create mode 100644 bitbake/lib/toaster/toastergui/buildtables.py
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/bpackage.html
 create mode 100644 bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/buildtime.html
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/recipes.html
 create mode 100644 bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/tasks.html

-- 
2.7.4



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v2 1/9] toaster: ToasterTable widget add an 'a' to Name in Exception class
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 2/9] toaster: port table for Built packages to ToasterTable Michael Wood
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/widgets.py | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/widgets.py b/bitbake/lib/toaster/toastergui/widgets.py
index 4117031..17c97fd 100644
--- a/bitbake/lib/toaster/toastergui/widgets.py
+++ b/bitbake/lib/toaster/toastergui/widgets.py
@@ -46,7 +46,7 @@ logger = logging.getLogger("toaster")
 from toastergui.tablefilter import TableFilterMap
 
 
-class NoFieldOrDataNme(Exception):
+class NoFieldOrDataName(Exception):
     pass
 
 class ToasterTable(TemplateView):
@@ -326,10 +326,11 @@ class ToasterTable(TemplateView):
                     if not field:
                         field = col['static_data_name']
                     if not field:
-                        raise NoFieldOrDataNme("Must supply a field_name or"
-                                               "static_data_name for column"
-                                               "%s.%s" %
-                                               (self.__class__.__name__, col))
+                        raise NoFieldOrDataName("Must supply a field_name or"
+                                                "static_data_name for column"
+                                                "%s.%s" %
+                                                (self.__class__.__name__, col)
+                                                )
 
                     # Check if we need to process some static data
                     if "static_data_name" in col and col['static_data_name']:
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 2/9] toaster: port table for Built packages to ToasterTable
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
  2016-05-26 15:12 ` [PATCH v2 1/9] toaster: ToasterTable widget add an 'a' to Name in Exception class Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 3/9] toaster: port Built recipes table to toastertables Michael Wood
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

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

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/buildtables.py      | 152 +++++++++++++++++++++
 bitbake/lib/toaster/toastergui/tables.py           |  10 +-
 .../toastergui/templates/basebuildpage.html        |  26 ++--
 .../lib/toaster/toastergui/templates/bpackage.html | 108 ---------------
 .../templates/buildinfo-toastertable.html          |  23 ++++
 .../templates/snippets/gitrev_popover.html         |   8 ++
 bitbake/lib/toaster/toastergui/urls.py             |   7 +-
 bitbake/lib/toaster/toastergui/views.py            |  90 ------------
 8 files changed, 203 insertions(+), 221 deletions(-)
 create mode 100644 bitbake/lib/toaster/toastergui/buildtables.py
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/bpackage.html
 create mode 100644 bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
 create mode 100644 bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html

diff --git a/bitbake/lib/toaster/toastergui/buildtables.py b/bitbake/lib/toaster/toastergui/buildtables.py
new file mode 100644
index 0000000..cf07ea8
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/buildtables.py
@@ -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))
diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py
index 29b68de..39d3f93 100644
--- a/bitbake/lib/toaster/toastergui/tables.py
+++ b/bitbake/lib/toaster/toastergui/tables.py
@@ -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 %}
         '''
 
@@ -699,6 +692,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}}")
diff --git a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
index 0d8c882..52a32b6 100644
--- a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
+++ b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
@@ -21,10 +21,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>
@@ -32,7 +41,7 @@
   <div class="row-fluid">
     <!-- begin left sidebar container -->
     <div id="nav" class="span2">
-      <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"
@@ -51,25 +60,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>
 
diff --git a/bitbake/lib/toaster/toastergui/templates/bpackage.html b/bitbake/lib/toaster/toastergui/templates/bpackage.html
deleted file mode 100644
index 81973cb..0000000
--- a/bitbake/lib/toaster/toastergui/templates/bpackage.html
+++ /dev/null
@@ -1,108 +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="span10">
-
-{% 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="row-fluid">
-      <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="add-on btn" tabindex="-1"><i class="icon-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>
-  </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='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 %}
diff --git a/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html b/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
new file mode 100644
index 0000000..4ce5c4a
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
@@ -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 %}
diff --git a/bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html b/bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html
new file mode 100644
index 0000000..281a3bd
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html
@@ -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 %}
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 27b0baa..3ce0d51 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -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+)$',
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index e78b0da..3c19ec6 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -1474,96 +1474,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)
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 3/9] toaster: port Built recipes table to toastertables
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
  2016-05-26 15:12 ` [PATCH v2 1/9] toaster: ToasterTable widget add an 'a' to Name in Exception class Michael Wood
  2016-05-26 15:12 ` [PATCH v2 2/9] toaster: port table for Built packages to ToasterTable Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 4/9] toaster: port Task tables to ToasterTables widget Michael Wood
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/buildtables.py      | 129 +++++++++++++++++++++
 .../lib/toaster/toastergui/templates/recipes.html  | 113 ------------------
 bitbake/lib/toaster/toastergui/urls.py             |   7 +-
 bitbake/lib/toaster/toastergui/views.py            | 111 ------------------
 4 files changed, 135 insertions(+), 225 deletions(-)
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/recipes.html

diff --git a/bitbake/lib/toaster/toastergui/buildtables.py b/bitbake/lib/toaster/toastergui/buildtables.py
index cf07ea8..dc742b9 100644
--- a/bitbake/lib/toaster/toastergui/buildtables.py
+++ b/bitbake/lib/toaster/toastergui/buildtables.py
@@ -150,3 +150,132 @@ class BuiltPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
                 yield column
 
         self.columns = list(remove_dep_cols(self.columns))
+
+
+class BuiltRecipesTable(BuildTablesMixin):
+    """ Table to show the recipes that have been built in this build """
+
+    def __init__(self, *args, **kwargs):
+        super(BuiltRecipesTable, self).__init__(*args, **kwargs)
+        self.title = "Recipes 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.get_recipes()
+        self.queryset = self.queryset.order_by(self.default_orderby)
+
+    def setup_columns(self, *args, **kwargs):
+        recipe_name_tmpl =\
+            '<a href="{% url "recipe" extra.build.pk data.pk %}">'\
+            '{{data.name}}'\
+            '</a>'
+
+        recipe_version_tmpl =\
+            '<a href="{% url "recipe" extra.build.pk data.pk %}">'\
+            '{{data.version}}'\
+            '</a>'
+
+        recipe_file_tmpl =\
+            '{{data.file_path}}'\
+            '{% if data.pathflags %}<i>({{data.pathflags}})</i>'\
+            '{% endif %}'
+
+        git_rev_template = '''
+        {% with vcs_ref=data.layer_version.commit %}
+        {% include 'snippets/gitrev_popover.html' %}
+        {% endwith %}
+        '''
+
+        depends_on_tmpl = '''
+        {% with deps=data.r_dependencies_recipe.all %}
+        {% with count=deps|length %}
+        {% if count %}
+        <a class="btn" title="
+        <a href='{% url "recipe" extra.build.pk data.pk %}#dependencies'>
+        {{data.name}}</a> dependencies"
+        data-content="<ul class='unstyled'>
+        {% for dep in deps|dictsort:"depends_on.name"%}
+        <li><a href='{% url "recipe" extra.build.pk dep.depends_on.pk %}'>
+        {{dep.depends_on.name}}</a></li>
+        {% endfor %}
+        </ul>">
+         {{count}}
+        </a>
+        {% endif %}{% endwith %}{% endwith %}
+        '''
+
+        rev_depends_tmpl = '''
+        {% with revs=data.r_dependencies_depends.all %}
+        {% with count=revs|length %}
+        {% if count %}
+        <a class="btn"
+        title="
+        <a href='{% url "recipe" extra.build.pk data.pk %}#brought-in-by'>
+        {{data.name}}</a> reverse dependencies"
+        data-content="<ul class='unstyled'>
+        {% for dep in revs|dictsort:"recipe.name" %}
+        <li>
+        <a href='{% url "recipe" extra.build.pk dep.recipe.pk %}'>
+        {{dep.recipe.name}}
+        </a></li>
+        {% endfor %}
+        </ul>">
+        {{count}}
+        </a>
+        {% endif %}{% endwith %}{% endwith %}
+        '''
+
+        self.add_column(title="Name",
+                        field_name="name",
+                        static_data_name='name',
+                        orderable=True,
+                        static_data_template=recipe_name_tmpl)
+
+        self.add_column(title="Version",
+                        field_name="version",
+                        static_data_name='version',
+                        static_data_template=recipe_version_tmpl)
+
+        self.add_column(title="Dependencies",
+                        static_data_name="dependencies",
+                        static_data_template=depends_on_tmpl,
+                        hidden=True)
+
+        self.add_column(title="Reverse dependencies",
+                        static_data_name="revdeps",
+                        static_data_template=rev_depends_tmpl,
+                        help_text='Recipe build-time reverse dependencies'
+                        ' (i.e. the recipes that depend on this recipe)',
+                        hidden=True)
+
+        self.add_column(title="Recipe file",
+                        field_name="file_path",
+                        static_data_name="file_path",
+                        static_data_template=recipe_file_tmpl)
+
+        self.add_column(title="Section",
+                        field_name="section",
+                        orderable=True)
+
+        self.add_column(title="License",
+                        field_name="license",
+                        help_text='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',
+                        orderable=True)
+
+        self.add_column(title="Layer",
+                        field_name="layer_version__layer__name",
+                        orderable=True)
+
+        self.add_column(title="Layer branch",
+                        field_name="layer_version__branch",
+                        orderable=True)
+
+        self.add_column(title="Layer commit",
+                        static_data_name="commit",
+                        static_data_template=git_rev_template)
diff --git a/bitbake/lib/toaster/toastergui/templates/recipes.html b/bitbake/lib/toaster/toastergui/templates/recipes.html
deleted file mode 100644
index d144893..0000000
--- a/bitbake/lib/toaster/toastergui/templates/recipes.html
+++ /dev/null
@@ -1,113 +0,0 @@
-{% extends "basebuildpage.html" %}
-
-{% load projecttags %}
-
-{% block title %} Recipes - {{build.target_set.all|dictsort:"target"|join:", "}} {{build.machine}} - {{build.project.name}} - Toaster {% endblock %}
-{% block localbreadcrumb %}
-<li>Recipes</li>
-{% endblock %}
-
-{% block nav-recipes %}
-  <li class="active"><a href="{% url 'recipes' build.pk %}">Recipes</a></li>
-{% endblock %}
-
-{% block buildinfomain %}
-<div class="span10">
-<div class="page-header">
-<h1>
-  {% if request.GET.search and objects.paginator.count > 0  %}
-      {{objects.paginator.count}} recipe{{objects.paginator.count|pluralize}} found
-  {%elif request.GET.search and objects.paginator.count == 0%}
-      No recipes found
-  {%else%}
-      Recipes
-  {%endif%}
- </h1>
-</div>
-
-{% if objects.paginator.count == 0 %}
-  <div class="row-fluid">
-      <div class="alert">
-        <form class="no-results input-append" id="searchform">
-            <input id="search" name="search" class="input-xxlarge" type="text" value="{%if request.GET.search%}{{request.GET.search}}{%endif%}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-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 recipes</button>
-        </form>
-      </div>
-  </div>
-
-{% else %}
-{% include "basetable_top.html" %}
-
-    {% for recipe in objects %}
-
-    <tr class="data">
-        <td class="recipe__name">
-            <a href="{% url "recipe" build.pk recipe.pk %}">{{recipe.name}}</a>
-        </td>
-        <td class="recipe__version">
-            <a href="{% url "recipe" build.pk recipe.pk %}">{{recipe.version}}</a>
-        </td>
-        <!-- Depends -->
-        <td class="depends_on">
-            {% with deps=recipe_deps|get_dict_value:recipe.pk %}
-            {% with count=deps|length %}
-            {% if count %}
-            <a class="btn"
-                title="<a href='{% url "recipe" build.pk recipe.pk %}#dependencies'>{{recipe.name}}</a> dependencies"
-                data-content="<ul class='unstyled'>
-                  {% for i in deps|dictsort:"depends_on.name"%}
-                    <li><a href='{% url "recipe" build.pk i.depends_on.pk %}'>{{i.depends_on.name}}</a></li>
-                  {% endfor %}
-                </ul>">
-                {{count}}
-            </a>
-            {% endif %}
-            {% endwith %}
-            {% endwith %}
-        </td>
-        <!--  Brought in by -->
-        <td class="depends_by">
-            {% with revs=recipe_revs|get_dict_value:recipe.pk %}
-            {% with count=revs|length %}
-            {% if count %}
-            <a class="btn"
-                title="<a href='{% url "recipe" build.pk recipe.pk %}#brought-in-by'>{{recipe.name}}</a> reverse dependencies"
-                data-content="<ul class='unstyled'>
-                  {% for i in revs|dictsort:"recipe.name" %}
-                    <li><a href='{% url "recipe" build.pk i.recipe.pk %}'>{{i.recipe.name}}</a></li>
-                  {% endfor %}
-                </ul>">
-                {{count}}
-            </a>
-            {% endif %}
-            {% endwith %}
-            {% endwith %}
-        </td>
-        <!-- Recipe file -->
-        <td class="recipe_file">{{recipe.file_path}} {% if recipe.pathflags %}<i>({{recipe.pathflags}})</i>{% endif %}</td>
-        <!-- Section -->
-        <td class="recipe_section">{{recipe.section}}</td>
-        <!-- License -->
-        <td class="recipe_license">{{recipe.license}}</td>
-        <!-- Layer -->
-        <td class="layer_version__layer__name">{{recipe.layer_version.layer.name}}</td>
-        <!-- Layer branch -->
-        <td class="layer_version__branch">{{recipe.layer_version.branch}}</td>
-        <!-- Layer commit -->
-        <td class="layer_version__layer__commit">
-            <a class="btn"
-                data-content="<ul class='unstyled'>
-                  <li>{{recipe.layer_version.commit}}</li>
-                </ul>">
-                {{recipe.layer_version.commit|truncatechars:13}}
-            </a>
-        </td>
-    </tr>
-
-    {% endfor %}
-
-{% include "basetable_bottom.html" %}
-{% endif %}
-</div>
-{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 3ce0d51..0636c95 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -40,8 +40,13 @@ urlpatterns = patterns('toastergui.views',
         url(r'^build/(?P<build_id>\d+)/tasks/(?P<task_id>\d+)/$', 'tasks_task', name='tasks_task'),
         url(r'^build/(?P<build_id>\d+)/task/(?P<task_id>\d+)$', 'task', name='task'),
 
-        url(r'^build/(?P<build_id>\d+)/recipes/$', 'recipes', name='recipes'),
+        url(r'^build/(?P<build_id>\d+)/recipes/$',
+            buildtables.BuiltRecipesTable.as_view(
+                template_name="buildinfo-toastertable.html"),
+            name='recipes'),
+
         url(r'^build/(?P<build_id>\d+)/recipe/(?P<recipe_id>\d+)/active_tab/(?P<active_tab>\d{1})$', 'recipe', name='recipe'),
+
         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'),
 
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 3c19ec6..4995e9b 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -1255,117 +1255,6 @@ def diskio(request, build_id):
 def cputime(request, build_id):
     return tasks_common(request, build_id, 'cputime', '')
 
-def recipes(request, build_id):
-    template = 'recipes.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( 'recipes', request.GET, mandatory_parameters, build_id = build_id)
-    (filter_string, search_term, ordering_string) = _search_tuple(request, Recipe)
-
-    build = Build.objects.get(pk=build_id)
-
-    queryset = build.get_recipes()
-    queryset = _get_queryset(Recipe, queryset, filter_string, search_term, ordering_string, 'name')
-
-    recipes = _build_page_range(Paginator(queryset, pagesize),request.GET.get('page', 1))
-
-    # prefetch the forward and reverse recipe dependencies
-    deps = { }
-    revs = { }
-    queryset_dependency=Recipe_Dependency.objects.filter(recipe__layer_version__build_id = build_id).select_related("depends_on", "recipe")
-    for recipe in recipes:
-        deplist = [ ]
-        for recipe_dep in [x for x in queryset_dependency if x.recipe_id == recipe.id]:
-            deplist.append(recipe_dep)
-        deps[recipe.id] = deplist
-        revlist = [ ]
-        for recipe_dep in [x for x in queryset_dependency if x.depends_on_id == recipe.id]:
-            revlist.append(recipe_dep)
-        revs[recipe.id] = revlist
-
-    context = {
-        'objectname': 'recipes',
-        'build': build,
-        'project': build.project,
-        'objects': recipes,
-        'default_orderby' : 'name:+',
-        'recipe_deps' : deps,
-        'recipe_revs' : revs,
-        'tablecols':[
-            {
-                'name':'Recipe',
-                'qhelp':'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',
-                'orderfield': _get_toggle_order(request, "name"),
-                'ordericon':_get_toggle_order_icon(request, "name"),
-            },
-            {
-                'name':'Recipe version',
-                'qhelp':'The recipe version and revision',
-            },
-            {
-                'name':'Dependencies',
-                'qhelp':'Recipe build-time dependencies (i.e. other recipes)',
-                'clclass': 'depends_on', 'hidden': 1,
-            },
-            {
-                'name':'Reverse dependencies',
-                'qhelp':'Recipe build-time reverse dependencies (i.e. the recipes that depend on this recipe)',
-                'clclass': 'depends_by', 'hidden': 1,
-            },
-            {
-                'name':'Recipe file',
-                'qhelp':'Path to the recipe .bb file',
-                'orderfield': _get_toggle_order(request, "file_path"),
-                'ordericon':_get_toggle_order_icon(request, "file_path"),
-                'orderkey' : 'file_path',
-                'clclass': 'recipe_file', 'hidden': 0,
-            },
-            {
-                'name':'Section',
-                'qhelp':'The section in which recipes should be categorized',
-                'orderfield': _get_toggle_order(request, "section"),
-                'ordericon':_get_toggle_order_icon(request, "section"),
-                'orderkey' : 'section',
-                'clclass': 'recipe_section', 'hidden': 0,
-            },
-            {
-                'name':'License',
-                'qhelp':'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',
-                'orderfield': _get_toggle_order(request, "license"),
-                'ordericon':_get_toggle_order_icon(request, "license"),
-                'orderkey' : 'license',
-                'clclass': 'recipe_license', 'hidden': 0,
-            },
-            {
-                'name':'Layer',
-                'qhelp':'The name of the layer providing the recipe',
-                'orderfield': _get_toggle_order(request, "layer_version__layer__name"),
-                'ordericon':_get_toggle_order_icon(request, "layer_version__layer__name"),
-                'orderkey' : 'layer_version__layer__name',
-                'clclass': 'layer_version__layer__name', 'hidden': 0,
-            },
-            {
-                'name':'Layer branch',
-                'qhelp':'The Git branch of the layer providing the recipe',
-                'orderfield': _get_toggle_order(request, "layer_version__branch"),
-                'ordericon':_get_toggle_order_icon(request, "layer_version__branch"),
-                'orderkey' : 'layer_version__branch',
-                'clclass': 'layer_version__branch', 'hidden': 1,
-            },
-            {
-                'name':'Layer commit',
-                'qhelp':'The Git commit of the layer providing the recipe',
-                'clclass': 'layer_version__layer__commit', 'hidden': 1,
-            },
-            ]
-        }
-
-    response = render(request, template, context)
-    _set_parameters_values(pagesize, orderby, request)
-    return response
-
 def configuration(request, build_id):
     template = 'configuration.html'
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 4/9] toaster: port Task tables to ToasterTables widget
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (2 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 3/9] toaster: port Built recipes table to toastertables Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 5/9] toaster: port Installed packages table to ToasterTable Michael Wood
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

Port the Task based tables to ToasterTable. This is the Task, Time, CPU
usage and Disk I/O tables.

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/buildtables.py      | 227 ++++++++++++++++-
 .../templates/buildinfo-toastertable.html          |   4 +-
 .../toaster/toastergui/templates/buildtime.html    |   4 -
 bitbake/lib/toaster/toastergui/templates/task.html |   2 +-
 .../lib/toaster/toastergui/templates/tasks.html    | 143 -----------
 bitbake/lib/toaster/toastergui/urls.py             |  23 +-
 bitbake/lib/toaster/toastergui/views.py            | 280 +--------------------
 7 files changed, 248 insertions(+), 435 deletions(-)
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/buildtime.html
 delete mode 100644 bitbake/lib/toaster/toastergui/templates/tasks.html

diff --git a/bitbake/lib/toaster/toastergui/buildtables.py b/bitbake/lib/toaster/toastergui/buildtables.py
index dc742b9..51c136f 100644
--- a/bitbake/lib/toaster/toastergui/buildtables.py
+++ b/bitbake/lib/toaster/toastergui/buildtables.py
@@ -19,10 +19,13 @@
 # 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 orm.models import Build, Task
+from django.db.models import Q
 
+import toastergui.tables as tables
 from toastergui.widgets import ToasterTable
+from toastergui.tablefilter import TableFilter
+from toastergui.tablefilter import TableFilterActionToggle
 
 
 class BuildTablesMixin(ToasterTable):
@@ -279,3 +282,223 @@ class BuiltRecipesTable(BuildTablesMixin):
         self.add_column(title="Layer commit",
                         static_data_name="commit",
                         static_data_template=git_rev_template)
+
+
+class BuildTasksTable(BuildTablesMixin):
+    """ Table to show the tasks that run in this build """
+
+    def __init__(self, *args, **kwargs):
+        super(BuildTasksTable, self).__init__(*args, **kwargs)
+        self.title = "Tasks"
+        self.default_orderby = "order"
+
+        # Toggle these columns on off for Time/CPU usage/Disk I/O tables
+        self.toggle_columns = {}
+
+    def setup_queryset(self, *args, **kwargs):
+        build = Build.objects.get(pk=kwargs['build_id'])
+        self.static_context_extra['build'] = build
+        self.queryset = build.task_build.filter(~Q(order=None))
+        self.queryset = self.queryset.order_by(self.default_orderby)
+
+    def setup_filters(self, *args, **kwargs):
+        # Execution outcome types filter
+        executed_outcome = TableFilter(name="execution_outcome",
+                                       title="Filter Tasks by 'Executed")
+
+        exec_outcome_action_exec = TableFilterActionToggle(
+            "executed",
+            "Executed Tasks",
+            Q(task_executed=True))
+
+        exec_outcome_action_not_exec = TableFilterActionToggle(
+            "not_executed",
+            "Not Executed Tasks",
+            Q(task_executed=False))
+
+        executed_outcome.add_action(exec_outcome_action_exec)
+        executed_outcome.add_action(exec_outcome_action_not_exec)
+
+        # Task outcome types filter
+        task_outcome = TableFilter(name="task_outcome",
+                                   title="Filter Task by 'Outcome'")
+
+        for outcome_enum, title in Task.TASK_OUTCOME:
+            action = TableFilterActionToggle(
+                title.replace(" ", "_").lower(),
+                "%s Tasks" % title,
+                Q(outcome=outcome_enum))
+
+            task_outcome.add_action(action)
+
+        # SSTATE outcome types filter
+        sstate_outcome = TableFilter(name="sstate_outcome",
+                                     title="Filter Task by 'Cache attempt'")
+
+        for sstate_result_enum, title in Task.SSTATE_RESULT:
+            action = TableFilterActionToggle(
+                title.replace(" ", "_").lower(),
+                "Tasks with '%s' attempts" % title,
+                Q(sstate_result=sstate_result_enum))
+
+            sstate_outcome.add_action(action)
+
+        self.add_filter(sstate_outcome)
+        self.add_filter(executed_outcome)
+        self.add_filter(task_outcome)
+
+    def setup_columns(self, *args, **kwargs):
+        self.toggle_columns['order'] = len(self.columns)
+
+        recipe_name_tmpl =\
+            '<a href="{% url "recipe" extra.build.pk data.recipe.pk %}">'\
+            '{{data.recipe.name}}'\
+            '</a>'
+
+        recipe_version_tmpl =\
+            '<a href="{% url "recipe" extra.build.pk data.recipe.pk %}">'\
+            '{{data.recipe.version}}'\
+            '</a>'
+
+        def task_link_tmpl(val):
+            return ('<a name="task-{{data.order}}"'
+                    'href="{%% url "task" extra.build.pk data.pk %%}">'
+                    '%s'
+                    '</a>') % str(val)
+
+        self.add_column(title="Order",
+                        static_data_name="order",
+                        static_data_template=task_link_tmpl('{{data.order}}'),
+                        orderable=True)
+
+        self.add_column(title="Recipe",
+                        static_data_name='recipe__name',
+                        static_data_template=recipe_name_tmpl,
+                        orderable=True)
+
+        self.add_column(title="Recipe version",
+                        static_data_name='recipe__version',
+                        static_data_template=recipe_version_tmpl)
+
+        self.add_column(title="Task",
+                        static_data_name="task_name",
+                        static_data_template=task_link_tmpl(
+                            "{{data.task_name}}"),
+                        orderable=True)
+
+        self.add_column(title="Executed",
+                        static_data_name="task_executed",
+                        static_data_template=task_link_tmpl(
+                            "{{data.get_executed_display}}"),
+                        filter_name='execution_outcome',
+                        orderable=True)
+
+        self.static_context_extra['OUTCOME_FAILED'] = Task.OUTCOME_FAILED
+        outcome_tmpl = task_link_tmpl("{{data.outcome_text}}")
+        outcome_tmpl = ('%s '
+                        '{%% if data.outcome = extra.OUTCOME_FAILED %%}'
+                        '<a href="{%% url "build_artifact" extra.build.pk '
+                        '          "tasklogfile" data.pk %%}">'
+                        ' <i class="icon-download-alt" '
+                        '    title="Download task log file"></i>'
+                        '</a> {%% endif %%}'
+                        '<i class="icon-question-sign get-help '
+                        'hover-help" style="visibility: hidden;" '
+                        'title="{{data.get_outcome_help}}"></i>'
+                        ) % outcome_tmpl
+
+        self.add_column(title="Outcome",
+                        static_data_name="outcome",
+                        static_data_template=outcome_tmpl,
+                        filter_name="task_outcome",
+                        orderable=True)
+
+        self.add_column(title="Cache attempt",
+                        static_data_name="sstate_result",
+                        static_data_template=task_link_tmpl(
+                            "{{data.sstate_text}}"),
+                        filter_name="sstate_outcome",
+                        orderable=True)
+
+        self.toggle_columns['elapsed_time'] = len(self.columns)
+
+        self.add_column(
+            title="Time (secs)",
+            static_data_name="elapsed_time",
+            static_data_template='{% load projecttags %}{% load humanize %}'
+            '{{data.elapsed_time|format_none_and_zero|floatformat:2}}',
+            orderable=True,
+            hidden=True)
+
+        self.toggle_columns['cpu_time_sys'] = len(self.columns)
+
+        self.add_column(
+            title="System CPU time (secs)",
+            static_data_name="cpu_time_system",
+            static_data_template='{% load projecttags %}{% load humanize %}'
+            '{{data.cpu_time_system|format_none_and_zero|floatformat:2}}',
+            hidden=True,
+            orderable=True)
+
+        self.toggle_columns['cpu_time_user'] = len(self.columns)
+
+        self.add_column(
+            title="User CPU time (secs)",
+            static_data_name="cpu_time_user",
+            static_data_template='{% load projecttags %}{% load humanize %}'
+            '{{data.cpu_time_user|format_none_and_zero|floatformat:2}}',
+            hidden=True,
+            orderable=True)
+
+        self.toggle_columns['disk_io'] = len(self.columns)
+
+        self.add_column(
+            title="Disk I/O (ms)",
+            static_data_name="disk_io",
+            static_data_template='{% load projecttags %}{% load humanize %}'
+            '{{data.disk_io|format_none_and_zero|filtered_filesizeformat}}',
+            hidden=True,
+            orderable=True)
+
+
+class BuildTimeTable(BuildTasksTable):
+    """ Same as tasks table but the Time column is default displayed"""
+
+    def __init__(self, *args, **kwargs):
+        super(BuildTimeTable, self).__init__(*args, **kwargs)
+        self.default_orderby = "-elapsed_time"
+
+    def setup_columns(self, *args, **kwargs):
+        super(BuildTimeTable, self).setup_columns(**kwargs)
+
+        self.columns[self.toggle_columns['order']]['hidden'] = True
+        self.columns[self.toggle_columns['elapsed_time']]['hidden'] = False
+
+
+class BuildCPUTimeTable(BuildTasksTable):
+    """ Same as tasks table but the CPU usage columns are default displayed"""
+
+    def __init__(self, *args, **kwargs):
+        super(BuildCPUTimeTable, self).__init__(*args, **kwargs)
+        self.default_orderby = "-cpu_time_system"
+
+    def setup_columns(self, *args, **kwargs):
+        super(BuildCPUTimeTable, self).setup_columns(**kwargs)
+
+        self.columns[self.toggle_columns['order']]['hidden'] = True
+        self.columns[self.toggle_columns['cpu_time_sys']]['hidden'] = False
+        self.columns[self.toggle_columns['cpu_time_user']]['hidden'] = False
+
+
+class BuildIOTable(BuildTasksTable):
+    """ Same as tasks table but the Disk IO column is default displayed"""
+
+    def __init__(self, *args, **kwargs):
+        super(BuildIOTable, self).__init__(*args, **kwargs)
+        self.default_orderby = "-disk_io"
+
+    def setup_columns(self, *args, **kwargs):
+        super(BuildIOTable, self).setup_columns(**kwargs)
+
+        self.columns[self.toggle_columns['order']]['hidden'] = True
+        self.columns[self.toggle_columns['disk_io']]['hidden'] = False
diff --git a/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html b/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
index 4ce5c4a..52cc056 100644
--- a/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
+++ b/bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
@@ -12,12 +12,14 @@
 
 {% block buildinfomain %}
 <div class="span10">
-{% url 'builtpackagestable' build.id as xhr_table_url %}
+{# xhr_table_url is just the current url so leave it blank #}
+{% with xhr_table_url='' %}
   <div class="page-header">
      <h1>
        {{title}} (<span class="table-count-{{table_name}}">0</span>) </h2>
      </h1>
   </div>
   {% include "toastertable.html" %}
+{% endwith %}
 </div>
 {% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/buildtime.html b/bitbake/lib/toaster/toastergui/templates/buildtime.html
deleted file mode 100644
index ea84ae7..0000000
--- a/bitbake/lib/toaster/toastergui/templates/buildtime.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% extends "basebuildpage.html" %}
-{% block localbreadcrumb %}
-<li>Build Time</li>
-{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/task.html b/bitbake/lib/toaster/toastergui/templates/task.html
index 5768262..dcd4dbc 100644
--- a/bitbake/lib/toaster/toastergui/templates/task.html
+++ b/bitbake/lib/toaster/toastergui/templates/task.html
@@ -195,7 +195,7 @@
         <i class="icon-question-sign get-help" title="The running sequence of each task in the build"></i>
         Task order
     </dt>
-    <dd><a href="{%url "tasks_task" build.pk task.order %}#{{task.order}}">{{task.order}}</a></dd>
+    <dd><a href="{%url "tasks" build.pk %}?page={{task_in_tasks_table_pg}}&limit=25#task-{{task.order}}">{{task.order}}</a></dd>
     {% if task.task_executed %}
         <dt>
             <i class="icon-question-sign get-help" title="Indicates if this task executes a Python or Shell function(s)"></i>
diff --git a/bitbake/lib/toaster/toastergui/templates/tasks.html b/bitbake/lib/toaster/toastergui/templates/tasks.html
deleted file mode 100644
index 84bc103..0000000
--- a/bitbake/lib/toaster/toastergui/templates/tasks.html
+++ /dev/null
@@ -1,143 +0,0 @@
-{% extends "basebuildpage.html" %}
-{% load humanize %}
-{% load projecttags %}
-
-{% block title %} {{mainheading}} - {{build.target_set.all|dictsort:"target"|join:", "}} {{build.machine}} - {{build.project.name}} - Toaster{% endblock %}
-{% block localbreadcrumb %}
-<li>{{mainheading}}</li>
-{% endblock %}
-
-{% block nav-tasks %}
-  {% if 'Tasks' == mainheading %}
-    <li class="active"><a href="{% url 'tasks' build.pk %}">Tasks</a></li>
-  {% else %}
-    <li><a href="{% url 'tasks' build.pk %}">Tasks</a></li>
-  {% endif %}
-{% endblock %}
-{% block nav-buildtime %}
-  {% if 'Time' == mainheading %}
-    <li class="active"><a href="{% url 'buildtime' build.pk %}">Time</a></li>
-  {% else %}
-    <li><a href="{% url 'buildtime' build.pk %}">Time</a></li>
-  {% endif %}
-{% endblock %}
-
-{% block nav-cputime %}
-  {% if 'CPU time' == mainheading %}
-    <li class="active"><a href="{% url 'cputime' build.pk %}">CPU time</a></li>
-  {% else %}
-    <li><a href="{% url 'cputime' build.pk %}">CPU time</a></li>
-  {% endif %}
-{% endblock %}
-
-{% block nav-diskio %}
-  {% if 'Disk I/O' == mainheading %}
-    <li class="active"><a href="{% url 'diskio' build.pk %}">Disk I/O</a></li>
-  {% else %}
-    <li><a href="{% url 'diskio' build.pk %}">Disk I/O</a></li>
-  {% endif %}
-{% endblock %}
-
-{% block buildinfomain %}
-<div class="span10">
-{% 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>{{mainheading}}</h1>
-  </div>
-  <div class="alert alert-info lead">
-  No data was recorded for this build.
-  </div>
-
-{% else %}
-
-  <div class="page-header">
-  <h1>
-  {% if  request.GET.filter and objects.paginator.count >  0 or request.GET.search and objects.paginator.count >  0 %}
-      {{objects.paginator.count}} task{{objects.paginator.count|pluralize}} found
-  {%elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %}
-      No tasks found
-  {%else%}
-      {{mainheading}}
-  {%endif%}
-  </h1>
-  </div>
-
-  {% if objects.paginator.count == 0 %}
-  <div class="row-fluid">
-      <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="add-on btn" tabindex="-1"><i class="icon-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 tasks</button>
-        </form>
-      </div>
-  </div>
-
-
-  {% else %}
-  {% include "basetable_top.html" %}
-
-    {% for task in objects %}
-        <tr {{ task|task_color }} id="{{task.order}}">
-            <td class="order">
-                <a href="{%url "task" build.pk task.pk%}">{{task.order}}</a>
-            </td>
-            <td class="recipe_name" >
-                <a href="{% url "recipe" build.pk task.recipe.pk %}">{{task.recipe.name}}</a>
-            </td>
-            <td class="recipe_version">
-                <a href="{% url "recipe" build.pk task.recipe.pk %}">{{task.recipe.version}}</a>
-            </td>
-            <td class="task_name">
-                <a href="{%url "task" build.pk task.pk%}">{{task.task_name}}</a> {% if task.get_description %}<i class="icon-question-sign get-help hover-help" title="{{task.get_description}}"></i> {% endif %}
-            </td>
-            <td class="executed">
-                <a href="{%url "task" build.pk task.pk%}">{{task.get_executed_display}}</a>
-            </td>
-            <td class="outcome">
-                <a href="{%url "task" build.pk task.pk%}">{{task.get_outcome_display}} </a>
-                {% if task.outcome = task.OUTCOME_FAILED %}
-                    <a href="{% url 'build_artifact' build.pk "tasklogfile" task.pk %}">
-                        <i class="icon-download-alt" title="Download task log file"></i>
-                    </a>
-                {% endif %}
-                <i class="icon-question-sign get-help hover-help" title="{{task.get_outcome_help}}"></i>
-            </td>
-            <td class="cache_attempt">
-                <a href="{%url "task" build.pk task.pk%}">{{task.get_sstate_result_display|format_none_and_zero}}</a>
-            </td>
-            <td class="time_taken">
-                {{task.elapsed_time|format_none_and_zero|floatformat:2}}
-            </td>
-            <td class="cpu_time_system">
-                {{task.cpu_time_system|format_none_and_zero|floatformat:2}}
-            </td>
-            <td class="cpu_time_user">
-                {{task.cpu_time_user|format_none_and_zero|floatformat:2}}
-            </td>
-            <td class="disk_io">
-                {{task.disk_io|format_none_and_zero|intcomma}}
-            </td>
-
-        </tr>
-    {% endfor %}
-
-  {% include "basetable_bottom.html" %}
-  {% endif %} {# objects.paginator.count #}
-{% endif %} {# empty #}
-</div>
-
-<script type="text/javascript">
-
-    $(document).ready(function() {
-        // highlight heading on the column for the field used for ordering
-        if (location.href.search('#') > -1) {
-            var task_order = location.href.split('#')[1];
-            $("#" + task_order).addClass("highlight");
-        }
-    });
-
-</script>
-
-{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 0636c95..c4913f1 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -35,9 +35,11 @@ urlpatterns = patterns('toastergui.views',
 
         # build info navigation
         url(r'^build/(?P<build_id>\d+)$', 'builddashboard', name="builddashboard"),
+        url(r'^build/(?P<build_id>\d+)/tasks/$',
+            buildtables.BuildTasksTable.as_view(
+                template_name="buildinfo-toastertable.html"),
+            name='tasks'),
 
-        url(r'^build/(?P<build_id>\d+)/tasks/$', 'tasks', name='tasks'),
-        url(r'^build/(?P<build_id>\d+)/tasks/(?P<task_id>\d+)/$', 'tasks_task', name='tasks_task'),
         url(r'^build/(?P<build_id>\d+)/task/(?P<task_id>\d+)$', 'task', name='task'),
 
         url(r'^build/(?P<build_id>\d+)/recipes/$',
@@ -74,9 +76,20 @@ urlpatterns = patterns('toastergui.views',
         url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/dirinfo_filepath/_(?P<file_path>(?:/[^/\n]+)*)$', 'dirinfo', name='dirinfo_filepath'),
         url(r'^build/(?P<build_id>\d+)/configuration$', 'configuration', name='configuration'),
         url(r'^build/(?P<build_id>\d+)/configvars$', 'configvars', name='configvars'),
-        url(r'^build/(?P<build_id>\d+)/buildtime$', 'buildtime', name='buildtime'),
-        url(r'^build/(?P<build_id>\d+)/cputime$', 'cputime', name='cputime'),
-        url(r'^build/(?P<build_id>\d+)/diskio$', 'diskio', name='diskio'),
+        url(r'^build/(?P<build_id>\d+)/buildtime$',
+            buildtables.BuildTimeTable.as_view(
+                template_name="buildinfo-toastertable.html"),
+            name='buildtime'),
+
+        url(r'^build/(?P<build_id>\d+)/cputime$',
+            buildtables.BuildCPUTimeTable.as_view(
+                template_name="buildinfo-toastertable.html"),
+            name='cputime'),
+
+        url(r'^build/(?P<build_id>\d+)/diskio$',
+            buildtables.BuildIOTable.as_view(
+                template_name="buildinfo-toastertable.html"),
+            name='diskio'),
 
         # image information dir
         url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/packagefile/(?P<packagefile_id>\d+)$',
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 4995e9b..bf292dc 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -578,6 +578,7 @@ def task( request, build_id, task_id ):
             'log_body'        : log_body,
             'showing_matches' : False,
             'uri_list'        : uri_list,
+            'task_in_tasks_table_pg':  int(task_object.order / 25) + 1
     }
     if request.GET.get( 'show_matches', "" ):
         context[ 'showing_matches' ] = True
@@ -976,285 +977,6 @@ def _find_task_provider(task_object):
             return trc
     return None
 
-def tasks_common(request, build_id, variant, task_anchor):
-# This class is shared between these pages
-#
-# Column    tasks  buildtime  diskio  cpuusage
-# --------- ------ ---------- ------- ---------
-# Cache      def
-# CPU                                   min -
-# Disk                         min -
-# Executed   def     def       def      def
-# Log
-# Order      def +
-# Outcome    def     def       def      def
-# Recipe     min     min       min      min
-# Version
-# Task       min     min       min      min
-# Time               min -
-#
-# 'min':on always, 'def':on by default, else hidden
-# '+' default column sort up, '-' default column sort down
-
-    anchor = request.GET.get('anchor', '')
-    if not anchor:
-        anchor=task_anchor
-
-    # default ordering depends on variant
-    default_orderby = None
-    filter_search_display = 'tasks'
-
-    if 'buildtime' == variant:
-        default_orderby = 'elapsed_time:-'
-        title_variant = 'Time'
-        object_search_display = 'time data'
-    elif 'diskio' == variant:
-        default_orderby = 'disk_io:-'
-        title_variant = 'Disk I/O'
-        object_search_display = 'disk I/O data'
-    elif 'cputime' == variant:
-        default_orderby = 'cpu_time_system:-'
-        title_variant='CPU time'
-        object_search_display = 'CPU time data'
-    else:
-        default_orderby = 'order:+'
-        title_variant = 'Tasks'
-        object_search_display = 'tasks'
-
-    (pagesize, orderby) = _get_parameters_values(request, 25, default_orderby)
-
-    mandatory_parameters = {'count': pagesize, 'page' : 1, 'orderby': orderby}
-
-    template = 'tasks.html'
-    retval = _verify_parameters( request.GET, mandatory_parameters )
-    if retval:
-        if task_anchor:
-            mandatory_parameters['anchor']=task_anchor
-        return _redirect_parameters( variant, request.GET, mandatory_parameters, build_id = build_id)
-    (filter_string, search_term, ordering_string) = _search_tuple(request, Task)
-    queryset_all = Task.objects.filter(build=build_id).exclude(order__isnull=True).exclude(outcome=Task.OUTCOME_NA)
-    queryset_all = queryset_all.select_related("recipe", "build")
-
-    queryset_with_search = _get_queryset(Task, queryset_all, None , search_term, ordering_string, 'order')
-
-    if ordering_string.startswith('outcome'):
-        queryset = _get_queryset(Task, queryset_all, filter_string, search_term, 'order:+', 'order')
-        queryset = sorted(queryset, key=lambda ur: (ur.outcome_text), reverse=ordering_string.endswith('-'))
-    elif ordering_string.startswith('sstate_result'):
-        queryset = _get_queryset(Task, queryset_all, filter_string, search_term, 'order:+', 'order')
-        queryset = sorted(queryset, key=lambda ur: (ur.sstate_text), reverse=ordering_string.endswith('-'))
-    else:
-        queryset = _get_queryset(Task, queryset_all, filter_string, search_term, ordering_string, 'order')
-
-
-    # compute the anchor's page
-    if anchor:
-        request.GET = request.GET.copy()
-        del request.GET['anchor']
-        i=0
-        a=int(anchor)
-        count_per_page=int(pagesize)
-        for task_object in queryset.iterator():
-            if a == task_object.order:
-                new_page= (i / count_per_page ) + 1
-                request.GET.__setitem__('page', new_page)
-                mandatory_parameters['page']=new_page
-                return _redirect_parameters( variant, request.GET, mandatory_parameters, build_id = build_id)
-            i += 1
-
-    task_objects = _build_page_range(Paginator(queryset, pagesize),request.GET.get('page', 1))
-
-    # define (and modify by variants) the 'tablecols' members
-    tc_order={
-        'name':'Order',
-        'qhelp':'The running sequence of each task in the build',
-        'clclass': 'order', 'hidden' : 1,
-        'orderkey' : 'order',
-        'orderfield':_get_toggle_order(request, "order"),
-        'ordericon':_get_toggle_order_icon(request, "order")}
-    if 'tasks' == variant:
-        tc_order['hidden']='0'
-        del tc_order['clclass']
-
-    tc_recipe={
-        'name':'Recipe',
-        'qhelp':'The name of the recipe to which each task applies',
-        'orderkey' : 'recipe__name',
-        'orderfield': _get_toggle_order(request, "recipe__name"),
-        'ordericon':_get_toggle_order_icon(request, "recipe__name"),
-    }
-    tc_recipe_version={
-        'name':'Recipe version',
-        'qhelp':'The version of the recipe to which each task applies',
-        'clclass': 'recipe_version', 'hidden' : 1,
-    }
-    tc_task={
-        'name':'Task',
-        'qhelp':'The name of the task',
-        'orderfield': _get_toggle_order(request, "task_name"),
-        'ordericon':_get_toggle_order_icon(request, "task_name"),
-        'orderkey' : 'task_name',
-    }
-    tc_executed={
-        'name':'Executed',
-        'qhelp':"This value tells you if a task had to run (executed) in order to generate the task output, or if the output was provided by another task and therefore the task didn't need to run (not executed)",
-        'clclass': 'executed', 'hidden' : 0,
-        'orderfield': _get_toggle_order(request, "task_executed"),
-        'ordericon':_get_toggle_order_icon(request, "task_executed"),
-        'orderkey' : 'task_executed',
-        'filter' : {
-                   'class' : 'executed',
-                   'label': 'Show:',
-                   'options' : [
-                               ('Executed Tasks', 'task_executed:1', queryset_with_search.filter(task_executed=1).count()),
-                               ('Not Executed Tasks', 'task_executed:0', queryset_with_search.filter(task_executed=0).count()),
-                               ]
-                   }
-
-    }
-    tc_outcome={
-        'name':'Outcome',
-        'qhelp':"This column tells you if 'executed' tasks succeeded or failed. The column also tells you why 'not executed' tasks did not need to run",
-        'clclass': 'outcome', 'hidden' : 0,
-        'orderfield': _get_toggle_order(request, "outcome"),
-        'ordericon':_get_toggle_order_icon(request, "outcome"),
-        'orderkey' : 'outcome',
-        'filter' : {
-                   'class' : 'outcome',
-                   'label': 'Show:',
-                   'options' : [
-                               ('Succeeded Tasks', 'outcome:%d'%Task.OUTCOME_SUCCESS, queryset_with_search.filter(outcome=Task.OUTCOME_SUCCESS).count(), "'Succeeded' tasks are those that ran and completed during the build" ),
-                               ('Failed Tasks', 'outcome:%d'%Task.OUTCOME_FAILED, queryset_with_search.filter(outcome=Task.OUTCOME_FAILED).count(), "'Failed' tasks are those that ran but did not complete during the build"),
-                               ('Cached Tasks', 'outcome:%d'%Task.OUTCOME_CACHED, queryset_with_search.filter(outcome=Task.OUTCOME_CACHED).count(), 'Cached tasks restore output from the <code>sstate-cache</code> directory or mirrors'),
-                               ('Prebuilt Tasks', 'outcome:%d'%Task.OUTCOME_PREBUILT, queryset_with_search.filter(outcome=Task.OUTCOME_PREBUILT).count(),'Prebuilt tasks didn\'t need to run because their output was reused from a previous build'),
-                               ('Covered Tasks', 'outcome:%d'%Task.OUTCOME_COVERED, queryset_with_search.filter(outcome=Task.OUTCOME_COVERED).count(), 'Covered tasks didn\'t need to run because their output is provided by another task in this build'),
-                               ('Empty Tasks', 'outcome:%d'%Task.OUTCOME_EMPTY, queryset_with_search.filter(outcome=Task.OUTCOME_EMPTY).count(), 'Empty tasks have no executable content'),
-                               ]
-                   }
-
-    }
-
-    tc_cache={
-        'name':'Cache attempt',
-        'qhelp':'This column tells you if a task tried to restore output from the <code>sstate-cache</code> directory or mirrors, and reports the result: Succeeded, Failed or File not in cache',
-        'clclass': 'cache_attempt', 'hidden' : 0,
-        'orderfield': _get_toggle_order(request, "sstate_result"),
-        'ordericon':_get_toggle_order_icon(request, "sstate_result"),
-        'orderkey' : 'sstate_result',
-        'filter' : {
-                   'class' : 'cache_attempt',
-                   'label': 'Show:',
-                   'options' : [
-                               ('Tasks with cache attempts', 'sstate_result__gt:%d'%Task.SSTATE_NA, queryset_with_search.filter(sstate_result__gt=Task.SSTATE_NA).count(), 'Show all tasks that tried to restore ouput from the <code>sstate-cache</code> directory or mirrors'),
-                               ("Tasks with 'File not in cache' attempts", 'sstate_result:%d'%Task.SSTATE_MISS,  queryset_with_search.filter(sstate_result=Task.SSTATE_MISS).count(), 'Show tasks that tried to restore output, but did not find it in the <code>sstate-cache</code> directory or mirrors'),
-                               ("Tasks with 'Failed' cache attempts", 'sstate_result:%d'%Task.SSTATE_FAILED,  queryset_with_search.filter(sstate_result=Task.SSTATE_FAILED).count(), 'Show tasks that found the required output in the <code>sstate-cache</code> directory or mirrors, but could not restore it'),
-                               ("Tasks with 'Succeeded' cache attempts", 'sstate_result:%d'%Task.SSTATE_RESTORED,  queryset_with_search.filter(sstate_result=Task.SSTATE_RESTORED).count(), 'Show tasks that successfully restored the required output from the <code>sstate-cache</code> directory or mirrors'),
-                               ]
-                   }
-
-    }
-    #if   'tasks' == variant: tc_cache['hidden']='0';
-    tc_time={
-        'name':'Time (secs)',
-        'qhelp':'How long it took the task to finish in seconds',
-        'orderfield': _get_toggle_order(request, "elapsed_time", True),
-        'ordericon':_get_toggle_order_icon(request, "elapsed_time"),
-        'orderkey' : 'elapsed_time',
-        'clclass': 'time_taken', 'hidden' : 1,
-    }
-    if 'buildtime' == variant:
-        tc_time['hidden']='0'
-        del tc_time['clclass']
-        tc_cache['hidden']='1'
-
-    tc_cpu_time_system={
-        'name':'System CPU time (secs)',
-        'qhelp':'Total amount of time spent executing in kernel mode, in ' +
-                'seconds. Note that this time can be greater than the task ' +
-                'time due to parallel execution.',
-        'orderfield': _get_toggle_order(request, "cpu_time_system", True),
-        'ordericon':_get_toggle_order_icon(request, "cpu_time_system"),
-        'orderkey' : 'cpu_time_system',
-        'clclass': 'cpu_time_system', 'hidden' : 1,
-    }
-
-    tc_cpu_time_user={
-        'name':'User CPU time (secs)',
-        'qhelp':'Total amount of time spent executing in user mode, in seconds. ' +
-                'Note that this time can be greater than the task time due to ' +
-                'parallel execution.',
-        'orderfield': _get_toggle_order(request, "cpu_time_user", True),
-        'ordericon':_get_toggle_order_icon(request, "cpu_time_user"),
-        'orderkey' : 'cpu_time_user',
-        'clclass': 'cpu_time_user', 'hidden' : 1,
-    }
-
-    if 'cputime' == variant:
-        tc_cpu_time_system['hidden']='0'
-        tc_cpu_time_user['hidden']='0'
-        del tc_cpu_time_system['clclass']
-        del tc_cpu_time_user['clclass']
-        tc_cache['hidden']='1'
-
-    tc_diskio={
-        'name':'Disk I/O (bytes)',
-        'qhelp':'Number of bytes written to and read from the disk during the task',
-        'orderfield': _get_toggle_order(request, "disk_io", True),
-        'ordericon':_get_toggle_order_icon(request, "disk_io"),
-        'orderkey' : 'disk_io',
-        'clclass': 'disk_io', 'hidden' : 1,
-    }
-    if 'diskio' == variant:
-        tc_diskio['hidden']='0'
-        del tc_diskio['clclass']
-        tc_cache['hidden']='1'
-
-    build = Build.objects.get(pk=build_id)
-
-    context = { 'objectname': variant,
-                'object_search_display': object_search_display,
-                'filter_search_display': filter_search_display,
-                'mainheading': title_variant,
-                'build': build,
-                'project': build.project,
-                'objects': task_objects,
-                'default_orderby' : default_orderby,
-                'search_term': search_term,
-                'total_count': queryset_with_search.count(),
-                'tablecols':[
-                    tc_order,
-                    tc_recipe,
-                    tc_recipe_version,
-                    tc_task,
-                    tc_executed,
-                    tc_outcome,
-                    tc_cache,
-                    tc_time,
-                    tc_cpu_time_system,
-                    tc_cpu_time_user,
-                    tc_diskio,
-                ]}
-
-
-    response = render(request, template, context)
-    _set_parameters_values(pagesize, orderby, request)
-    return response
-
-def tasks(request, build_id):
-    return tasks_common(request, build_id, 'tasks', '')
-
-def tasks_task(request, build_id, task_id):
-    return tasks_common(request, build_id, 'tasks', task_id)
-
-def buildtime(request, build_id):
-    return tasks_common(request, build_id, 'buildtime', '')
-
-def diskio(request, build_id):
-    return tasks_common(request, build_id, 'diskio', '')
-
-def cputime(request, build_id):
-    return tasks_common(request, build_id, 'cputime', '')
-
 def configuration(request, build_id):
     template = 'configuration.html'
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 5/9] toaster: port Installed packages table to ToasterTable
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (3 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 4/9] toaster: port Task tables to ToasterTables widget Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 6/9] toaster: table.js Add the ability to highlight a particular row Michael Wood
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/buildtables.py      |  63 +++++++-
 .../toastergui/templates/builddashboard.html       |   4 +-
 .../lib/toaster/toastergui/templates/target.html   | 115 +-------------
 bitbake/lib/toaster/toastergui/urls.py             |   9 +-
 bitbake/lib/toaster/toastergui/views.py            | 169 ---------------------
 5 files changed, 73 insertions(+), 287 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/buildtables.py b/bitbake/lib/toaster/toastergui/buildtables.py
index 51c136f..17de369 100644
--- a/bitbake/lib/toaster/toastergui/buildtables.py
+++ b/bitbake/lib/toaster/toastergui/buildtables.py
@@ -19,8 +19,8 @@
 # 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, Task
-from django.db.models import Q
+from orm.models import Build, Task, Target, Package
+from django.db.models import Q, Sum
 
 import toastergui.tables as tables
 from toastergui.widgets import ToasterTable
@@ -155,6 +155,65 @@ class BuiltPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
         self.columns = list(remove_dep_cols(self.columns))
 
 
+class InstalledPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
+    """ Show all packages installed in an image """
+    def __init__(self, *args, **kwargs):
+        super(InstalledPackagesTable, self).__init__(*args, **kwargs)
+        self.title = "Installed Packages"
+        self.default_orderby = "name"
+
+    def make_package_list(self, target):
+        # The database design means that you get the intermediate objects and
+        # not package objects like you'd really want so we get them here
+        pkgs = target.target_installed_package_set.values_list('package',
+                                                               flat=True)
+        return Package.objects.filter(pk__in=pkgs)
+
+    def get_context_data(self, **kwargs):
+        context = super(InstalledPackagesTable,
+                        self).get_context_data(**kwargs)
+
+        target = Target.objects.get(pk=kwargs['target_id'])
+        packages = self.make_package_list(target)
+
+        context['packages_sum'] = packages.aggregate(
+            Sum('installed_size'))['installed_size__sum']
+
+        context['target'] = target
+        return context
+
+    def setup_queryset(self, *args, **kwargs):
+        build = Build.objects.get(pk=kwargs['build_id'])
+        self.static_context_extra['build'] = build
+
+        target = Target.objects.get(pk=kwargs['target_id'])
+        self.queryset = self.make_package_list(target)
+
+    def setup_columns(self, *args, **kwargs):
+        super(InstalledPackagesTable, self).setup_columns(**kwargs)
+        self.add_column(title="Installed size",
+                        static_data_name="installed_size",
+                        static_data_template="{% load projecttags %}"
+                        "{{data.size|filtered_filesizeformat}}",
+                        orderable=True)
+
+        # Add the template to show installed name for installed packages
+        install_name_tmpl =\
+            ('{{data.name}} '
+             '{% if data.installed_name and data.installed_name !='
+             ' data.name %}'
+             '<span class="muted"> as {{data.installed_name}}</span>'
+             ' <i class="icon-question-sign get-help hover-help"'
+             ' title="{{data.name}} was renamed at packaging time and'
+             ' was installed in your image as {{data.installed_name}}'
+             '"></i>{% endif %} ')
+
+        for column in self.columns:
+            if column['static_data_name'] == 'name':
+                column['static_data_template'] = install_name_tmpl
+                break
+
+
 class BuiltRecipesTable(BuildTablesMixin):
     """ Table to show the recipes that have been built in this build """
 
diff --git a/bitbake/lib/toaster/toastergui/templates/builddashboard.html b/bitbake/lib/toaster/toastergui/templates/builddashboard.html
index a0da71e..dfb659c 100644
--- a/bitbake/lib/toaster/toastergui/templates/builddashboard.html
+++ b/bitbake/lib/toaster/toastergui/templates/builddashboard.html
@@ -105,7 +105,7 @@
                                     files from a previous build already exist in your
                                     <code>.../poky/build/tmp/deploy</code>
                                     directory. You can
-                                    also <a href="{% url 'targetpkg' build.pk target.target.pk %}">view the
+                                    also <a href="{% url 'target' build.pk target.target.pk %}">view the
                                       license manifest information</a> in Toaster.
                                   </p>
                     </div>
@@ -117,7 +117,7 @@
                 License manifest
             </dt>
             <dd>
-                <a href="{% url 'targetpkg' build.pk target.target.pk %}">View in Toaster</a> |
+                <a href="{% url 'target' build.pk target.target.pk %}">View in Toaster</a> |
                 <a href="{% url 'build_artifact' build.pk 'licensemanifest' target.target.pk %}">Download</a></dd>
             <dt>
                 <i class="icon-question-sign get-help" title="Image files are stored in <code>/build/tmp/deploy/images/</code>"></i>
diff --git a/bitbake/lib/toaster/toastergui/templates/target.html b/bitbake/lib/toaster/toastergui/templates/target.html
index 4c33eaa..a58332d 100644
--- a/bitbake/lib/toaster/toastergui/templates/target.html
+++ b/bitbake/lib/toaster/toastergui/templates/target.html
@@ -17,7 +17,6 @@
 {% endblock %}
 
 {% block buildinfomain %}
-
 <div class="row-fluid span10">
     <div class="page-header">
         <h1>
@@ -31,7 +30,6 @@
         </h1>
     </div>
 </div>
-
 <div class="row-fluid pull-right span10" id="navTab">
     <ul class="nav nav-pills">
         <li class="active">
@@ -49,115 +47,10 @@
     </ul>
 
     <div id="image-packages" class="tab-pane">
-
-    {% if objects.paginator.count == 0 %}
-    <div class="row-fluid">
-        <div class="alert">
-            <form class="no-results input-append" id="searchform">
-                <input id="search" name="search" class="input-xxlarge" type="text" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-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>
-    </div>
-
-
-    {% else %}
-    {% include "basetable_top.html" %}
-    {% for package in objects %}
-    <tr>
-      {# order of the table data must match the columns defined in template's context tablecols  #}
-        <td class="package_name">
-            <a href="{% url 'package_included_detail' build.id target.id package.id %}">
-            {{package.name}}
-            </a>
-            {% if package.installed_name and package.name != package.installed_name %}
-            <span class="muted"> as {{package.installed_name}}</span>
-            <i class="icon-question-sign get-help hover-help" title='{{package.name|add:" was renamed at packaging time and was installed in your image as "|add:package.installed_name}}'></i>
-            {% endif %}
-        </td>
-        <td class="package_version">
-            <a href="{% url 'package_included_detail' build.id target.id package.id %}">
-            {{package.version|filtered_packageversion:package.revision}}
-            </a>
-        </td>
-        <td class="license">
-            {{package.license}}
-        </td>
-        <td class="size sizecol">
-            {{package.size|filtered_installedsize:package.installed_size|filtered_filesizeformat}}
-        </td>
-
-        <td class="size_over_total sizecol">
-            {{package|filter_sizeovertotal:packages_sum}}
-        </td>
-       <td class="depends">
-            {% with deps=package.runtime_dependencies %}
-            {% with deps_count=deps|length %}
-            {% if deps_count > 0 %}
-            <a class="btn"
-                title="<a href='{% url "package_included_dependencies" build.id target.id package.id %}'>{{package.name}}</a> dependencies"
-                data-content="<ul class='unstyled'>
-                  {% for i in deps|dictsort:'depends_on.name' %}
-                    <li><a href='{% url "package_included_detail" build.pk target.id i.depends_on.pk %}'>{{i.depends_on.name}}</a></li>
-                  {% endfor %}
-                </ul>">
-                {{deps_count}}
-            </a>
-            {% endif %}
-            {% endwith %}
-            {% endwith %}
-        </td>
-        <td class="brought_in_by">
-            {% with rdeps=package.reverse_runtime_dependencies %}
-            {% with rdeps_count=rdeps|length %}
-            {% if rdeps_count > 0 %}
-            <a class="btn"
-                title="<a href='{% url "package_included_reverse_dependencies" build.id target.id package.id %}'>{{package.name}}</a> reverse dependencies"
-                data-content="<ul class='unstyled'>
-                  {% for i in rdeps|dictsort:'package.name' %}
-                    <li><a href='{% url "package_included_detail" build.id target.id i.package.id %}'>{{i.package.name}}</a></li>
-                  {% endfor %}
-                </ul>">
-                {{rdeps_count}}
-            </a>
-            {% endif %}
-            {% endwith %}
-            {% endwith %}
-        </td>
-        <td class="recipe_name">
-            {% if package.recipe.version %}
-            <a href="{% url 'recipe' build.id package.recipe_id %}">
-            {{ package.recipe.name }}
-            </a>
-            {% endif %}
-        </td>
-        <td class="recipe_version">
-            {% if package.recipe.version %}
-            <a href="{% url 'recipe' build.id package.recipe_id %}">
-            {{ package.recipe.version }}
-            </a>
-            {% endif %}
-        </td>
-        <td class="layer_name">
-            {{ package.recipe.layer_version.layer.name }}
-        </td>
-        <td class="layer_branch">
-            {{ package.recipe.layer_version.branch}}
-        </td>
-        <td class="layer_commit">
-            <a class="btn"
-                data-content="<ul class='unstyled'>
-                  <li>{{package.recipe.layer_version.commit}}</li>
-                </ul>">
-                {{package.recipe.layer_version.commit|truncatechars:13}}
-            </a>
-        </td>
-    </tr>
-    {% endfor %}
-
-    {% include "basetable_bottom.html" %}
-    {% endif %}
+        {# xhr_table_url is just the current url so leave it blank #}
+        {% with xhr_table_url='' %}
+        {% include "toastertable.html" %}
+        {% endwith %}
     </div> <!-- tabpane -->
 </div> <!--span 10-->
 {% endblock buildinfomain %}
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index c4913f1..9510a38 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -68,9 +68,12 @@ urlpatterns = patterns('toastergui.views',
         url(r'^build/(?P<build_id>\d+)/package_included_reverse_dependencies/(?P<target_id>\d+)/(?P<package_id>\d+)$',
             'package_included_reverse_dependencies', name='package_included_reverse_dependencies'),
 
-        # images are known as targets in the internal model
-        url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)$', 'target', name='target'),
-        url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/targetpkg$', 'targetpkg', name='targetpkg'),
+        url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)$',
+            buildtables.InstalledPackagesTable.as_view(
+                template_name="target.html"),
+            name='target'),
+
+
         url(r'^dentries/build/(?P<build_id>\d+)/target/(?P<target_id>\d+)$', 'xhr_dirinfo', name='dirinfo_ajax'),
         url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/dirinfo$', 'dirinfo', name='dirinfo'),
         url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/dirinfo_filepath/_(?P<file_path>(?:/[^/\n]+)*)$', 'dirinfo', name='dirinfo_filepath'),
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index bf292dc..ed29c93 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -663,175 +663,6 @@ def recipe_packages(request, build_id, recipe_id):
     _set_parameters_values(pagesize, orderby, request)
     return response
 
-def target_common( request, build_id, target_id, variant ):
-    template = "target.html"
-    default_orderby = 'name:+'
-
-    (pagesize, orderby) = _get_parameters_values(request, 25, default_orderby)
-    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby': orderby }
-    retval = _verify_parameters( request.GET, mandatory_parameters )
-    if retval:
-        return _redirect_parameters(
-                    variant, request.GET, mandatory_parameters,
-                    build_id = build_id, target_id = target_id )
-    ( filter_string, search_term, ordering_string ) = _search_tuple( request, Package )
-
-    # FUTURE:  get rid of nested sub-queries replacing with ManyToMany field
-    queryset = Package.objects.filter(
-                    size__gte = 0,
-                    id__in = Target_Installed_Package.objects.filter(
-                        target_id=target_id ).values( 'package_id' ))
-    packages_sum =  queryset.aggregate( Sum( 'installed_size' ))
-    queryset = _get_queryset(
-            Package, queryset, filter_string, search_term, ordering_string, 'name' )
-    queryset = queryset.select_related("recipe", "recipe__layer_version", "recipe__layer_version__layer")
-    packages = _build_page_range( Paginator(queryset, pagesize), request.GET.get( 'page', 1 ))
-
-    build = Build.objects.get( pk = build_id )
-
-    # bring in package dependencies
-    for p in packages.object_list:
-        p.runtime_dependencies = p.package_dependencies_source.filter(
-            target_id = target_id, dep_type=Package_Dependency.TYPE_TRDEPENDS ).select_related("depends_on")
-        p.reverse_runtime_dependencies = p.package_dependencies_target.filter(
-            target_id = target_id, dep_type=Package_Dependency.TYPE_TRDEPENDS ).select_related("package")
-    tc_package = {
-        'name'       : 'Package',
-        'qhelp'      : 'Packaged output resulting from building a recipe included in this image',
-        'orderfield' : _get_toggle_order( request, "name" ),
-        'ordericon'  : _get_toggle_order_icon( request, "name" ),
-        }
-    tc_packageVersion = {
-        'name'       : 'Package version',
-        'qhelp'      : 'The package version and revision',
-        }
-    tc_size = {
-        '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',
-        'dclass'     : 'span2',
-        }
-    if ( variant == 'target' ):
-        tc_size[ "hidden" ] = 0
-    else:
-        tc_size[ "hidden" ] = 1
-    tc_sizePercentage = {
-        'name'       : 'Size over total (%)',
-        'qhelp'      : 'Proportion of the overall size represented by this package',
-        'clclass'    : 'size_over_total',
-        'hidden'     : 1,
-        }
-    tc_license = {
-        'name'       : 'License',
-        'qhelp'      : 'The license under which the package is distributed. Separate license names u\
-sing | (pipe) means there is a choice between licenses. Separate license names using & (ampersand) m\
-eans 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',
-        }
-    if ( variant == 'target' ):
-        tc_license[ "hidden" ] = 1
-    else:
-        tc_license[ "hidden" ] = 0
-    tc_dependencies = {
-        'name'       : 'Dependencies',
-        'qhelp'      : "Package runtime dependencies (other packages)",
-        'clclass'    : 'depends',
-        }
-    if ( variant == 'target' ):
-        tc_dependencies[ "hidden" ] = 0
-    else:
-        tc_dependencies[ "hidden" ] = 1
-    tc_rdependencies = {
-        'name'       : 'Reverse dependencies',
-        'qhelp'      : 'Package run-time reverse dependencies (i.e. which other packages depend on this package',
-        'clclass'    : 'brought_in_by',
-        }
-    if ( variant == 'target' ):
-        tc_rdependencies[ "hidden" ] = 0
-    else:
-        tc_rdependencies[ "hidden" ] = 1
-    tc_recipe = {
-        '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,
-        }
-    tc_recipeVersion = {
-        'name'       : 'Recipe version',
-        'qhelp'      : 'Version and revision of the recipe building the package',
-        'clclass'    : 'recipe_version',
-        'hidden'     : 1,
-        }
-    tc_layer = {
-        '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'    : 'layer_name',
-        'hidden'     : 1,
-        }
-    tc_layerBranch = {
-        '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'    : 'layer_branch',
-        'hidden'     : 1,
-        }
-    tc_layerCommit = {
-        'name'       : 'Layer commit',
-        'qhelp'      : 'The Git commit of the layer providing the recipe that builds the package',
-        'clclass'    : 'layer_commit',
-        'hidden'     : 1,
-        }
-
-    context = {
-        'objectname': variant,
-        'build'                : build,
-        'project'              : build.project,
-        'target'               : Target.objects.filter( pk = target_id )[ 0 ],
-        'objects'              : packages,
-        'packages_sum'         : packages_sum[ 'installed_size__sum' ],
-        'object_search_display': "packages included",
-        'default_orderby'      : default_orderby,
-        'tablecols'            : [
-                    tc_package,
-                    tc_packageVersion,
-                    tc_license,
-                    tc_size,
-                    tc_sizePercentage,
-                    tc_dependencies,
-                    tc_rdependencies,
-                    tc_recipe,
-                    tc_recipeVersion,
-                    tc_layer,
-                    tc_layerBranch,
-                    tc_layerCommit,
-                ]
-        }
-
-
-    response = render(request, template, context)
-    _set_parameters_values(pagesize, orderby, request)
-    return response
-
-def target( request, build_id, target_id ):
-    return( target_common( request, build_id, target_id, "target" ))
-
-def targetpkg( request, build_id, target_id ):
-    return( target_common( request, build_id, target_id, "targetpkg" ))
-
 from django.core.serializers.json import DjangoJSONEncoder
 from django.http import HttpResponse
 def xhr_dirinfo(request, build_id, target_id):
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 6/9] toaster: table.js Add the ability to highlight a particular row
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (4 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 5/9] toaster: port Installed packages table to ToasterTable Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 7/9] toaster: tests Add new build tables to tests Michael Wood
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

As in the old build tables it's useful to jump and highlight a
particular row in the table using the #hash in the URL.

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/static/js/table.js | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/bitbake/lib/toaster/toastergui/static/js/table.js b/bitbake/lib/toaster/toastergui/static/js/table.js
index 7f76f55..7f9c44f 100644
--- a/bitbake/lib/toaster/toastergui/static/js/table.js
+++ b/bitbake/lib/toaster/toastergui/static/js/table.js
@@ -162,6 +162,15 @@ function tableInit(ctx){
     table.css("padding-bottom", 0);
     tableContainer.css("visibility", "visible");
 
+    /* If we have a hash in the url try and highlight that item in the table */
+    if (window.location.hash){
+      var highlight = $("table a[name="+window.location.hash.replace('#',''));
+      if (highlight.length > 0){
+        highlight.parents("tr").addClass('highlight');
+        window.scroll(0, highlight.position().top - 50);
+      }
+    }
+
     table.trigger("table-done", [tableData.total, tableParams]);
   }
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 7/9] toaster: tests Add new build tables to tests
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (5 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 6/9] toaster: table.js Add the ability to highlight a particular row Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 8/9] toaster: widgets ToasterTable Catch template rendering exceptions Michael Wood
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

- Add new build tables to be tested
- Add required data into the fixture and clean up a few empty fields
- Fix the SoftwareRecipesTable specific test so as not to rely on two
  particular defined recipes

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 .../fixtures/toastergui-unittest-data.xml          | 91 ++++++++++++++++------
 bitbake/lib/toaster/toastergui/tests.py            | 40 ++++++----
 2 files changed, 94 insertions(+), 37 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/fixtures/toastergui-unittest-data.xml b/bitbake/lib/toaster/toastergui/fixtures/toastergui-unittest-data.xml
index cf35be4..213bf0f 100644
--- a/bitbake/lib/toaster/toastergui/fixtures/toastergui-unittest-data.xml
+++ b/bitbake/lib/toaster/toastergui/fixtures/toastergui-unittest-data.xml
@@ -82,9 +82,8 @@
     <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
     <field type="CharField" name="target">a image recipe</field>
     <field type="CharField" name="task"><None></None></field>
-    <field type="BooleanField" name="is_image">False</field>
-    <field type="IntegerField" name="image_size">0</field>
-    <field type="CharField" name="license_manifest_path"><None></None></field>
+    <field type="BooleanField" name="is_image">True</field>
+    <field type="IntegerField" name="image_size">290</field>
   </object>
   <object pk="2" model="orm.target">
     <field to="orm.build" name="build" rel="ManyToOneRel">2</field>
@@ -118,9 +117,9 @@
   </object>
   <object pk="2" model="orm.package">
     <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
-    <field to="orm.recipe" name="recipe" rel="ManyToOneRel">2</field>
+    <field to="orm.recipe" name="recipe" rel="ManyToOneRel">1</field>
     <field type="CharField" name="name">f pkg</field>
-    <field type="CharField" name="installed_name"></field>
+    <field type="CharField" name="installed_name">f</field>
     <field type="CharField" name="version"></field>
     <field type="CharField" name="revision"></field>
     <field type="TextField" name="summary"></field>
@@ -297,6 +296,16 @@
     <field type="CharField" name="pathflags"></field>
     <field type="BooleanField" name="is_image">False</field>
   </object>
+  <!-- Added for an additional built recipe -->
+  <object pk="6" model="orm.recipe">
+    <field type="CharField" name="name">g recipe</field>
+    <field type="CharField" name="version">1.2.3</field>
+    <field to="orm.layer_version" name="layer_version" rel="ManyToOneRel">3</field>
+    <field type="CharField" name="license">g license</field>
+    <field type="FilePathField" name="file_path">/g</field>
+    <field type="BooleanField" name="is_image">False</field>
+  </object>
+
   <object pk="1" model="orm.machine">
     <field to="orm.layersource" name="layer_source" rel="ManyToOneRel"><None></None></field>
     <field type="IntegerField" name="up_id"><None></None></field>
@@ -353,8 +362,6 @@
   </object>
   <object pk="1" model="orm.branch">
     <field to="orm.layersource" name="layer_source" rel="ManyToOneRel">1</field>
-    <field type="IntegerField" name="up_id"><None></None></field>
-    <field type="DateTimeField" name="up_date"><None></None></field>
     <field type="CharField" name="name">master</field>
     <field type="CharField" name="short_description"></field>
   </object>
@@ -363,13 +370,7 @@
     <field type="IntegerField" name="up_id"><None></None></field>
     <field type="DateTimeField" name="up_date"><None></None></field>
     <field type="CharField" name="name">a layer</field>
-    <field type="CharField" name="layer_index_url"></field>
     <field type="CharField" name="vcs_url">/tmp/</field>
-    <field type="CharField" name="vcs_web_url"><None></None></field>
-    <field type="CharField" name="vcs_web_tree_base_url"><None></None></field>
-    <field type="CharField" name="vcs_web_file_base_url"><None></None></field>
-    <field type="TextField" name="summary"><None></None></field>
-    <field type="TextField" name="description"><None></None></field>
   </object>
   <object pk="2" model="orm.layer">
     <field to="orm.layersource" name="layer_source" rel="ManyToOneRel">1</field>
@@ -378,21 +379,16 @@
     <field type="CharField" name="name">z layer</field>
     <field type="CharField" name="layer_index_url"></field>
     <field type="CharField" name="vcs_url">git://two/</field>
-    <field type="CharField" name="vcs_web_url"><None></None></field>
-    <field type="CharField" name="vcs_web_tree_base_url"><None></None></field>
-    <field type="CharField" name="vcs_web_file_base_url"><None></None></field>
-    <field type="TextField" name="summary"><None></None></field>
-    <field type="TextField" name="description"><None></None></field>
   </object>
   <object pk="1" model="orm.layer_version">
-    <field to="orm.build" name="build" rel="ManyToOneRel"><None></None></field>
+    <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
     <field to="orm.layer" name="layer" rel="ManyToOneRel">1</field>
     <field to="orm.layersource" name="layer_source" rel="ManyToOneRel">1</field>
     <field type="IntegerField" name="up_id"><None></None></field>
     <field type="DateTimeField" name="up_date"><None></None></field>
     <field to="orm.branch" name="up_branch" rel="ManyToOneRel">1</field>
-    <field type="CharField" name="branch"></field>
-    <field type="CharField" name="commit">master</field>
+    <field type="CharField" name="branch">master</field>
+    <field type="CharField" name="commit">abcdef123</field>
     <field type="CharField" name="dirpath">/tmp/</field>
     <field type="IntegerField" name="priority">0</field>
     <field type="FilePathField" name="local_path">/</field>
@@ -405,8 +401,22 @@
     <field type="IntegerField" name="up_id"><None></None></field>
     <field type="DateTimeField" name="up_date"><None></None></field>
     <field to="orm.branch" name="up_branch" rel="ManyToOneRel">1</field>
-    <field type="CharField" name="branch"></field>
-    <field type="CharField" name="commit">master</field>
+    <field type="CharField" name="branch">testing-branch</field>
+    <field type="CharField" name="commit">9876fedcba</field>
+    <field type="CharField" name="dirpath"><None></None></field>
+    <field type="IntegerField" name="priority">0</field>
+    <field type="FilePathField" name="local_path">/</field>
+    <field to="orm.project" name="project" rel="ManyToOneRel"><None></None></field>
+  </object>
+  <object pk="3" model="orm.layer_version">
+    <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
+    <field to="orm.layer" name="layer" rel="ManyToOneRel">2</field>
+    <field to="orm.layersource" name="layer_source" rel="ManyToOneRel">1</field>
+    <field type="IntegerField" name="up_id"><None></None></field>
+    <field type="DateTimeField" name="up_date"><None></None></field>
+    <field to="orm.branch" name="up_branch" rel="ManyToOneRel">1</field>
+    <field type="CharField" name="branch">testing-branch</field>
+    <field type="CharField" name="commit">9876fedcba</field>
     <field type="CharField" name="dirpath"><None></None></field>
     <field type="IntegerField" name="priority">0</field>
     <field type="FilePathField" name="local_path">/</field>
@@ -443,4 +453,39 @@
     <field type="FilePathField" name="pathname"></field>
     <field type="IntegerField" name="lineno"><None></None></field>
   </object>
+  <!-- Some tasks for build 1 to test build tables -->
+  <object pk="1" model="orm.task">
+    <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
+    <field type="IntegerField" name="order">1</field>
+    <field type="BooleanField" name="task_executed">False</field>
+    <field type="IntegerField" name="outcome">-1</field>
+    <field type="CharField" name="sstate_checksum">abcdef123</field>
+    <field type="FilePathField" name="path_to_sstate_obj">34/wefw.tar</field>
+    <field to="orm.recipe" name="recipe" rel="ManyToOneRel">1</field>
+    <field type="CharField" name="task_name">a_do_compile</field>
+    <field type="DecimalField" name="elapsed_time">100</field>
+    <field type="IntegerField" name="disk_io">10</field>
+    <field type="IntegerField" name="disk_io_read">11</field>
+    <field type="IntegerField" name="disk_io_write">12</field>
+    <field type="DecimalField" name="cpu_time_user">10.1</field>
+    <field type="DecimalField" name="cpu_time_system">10.2</field>
+    <field type="IntegerField" name="sstate_result">3</field>
+  </object>
+  <object pk="2" model="orm.task">
+    <field to="orm.build" name="build" rel="ManyToOneRel">1</field>
+    <field type="IntegerField" name="order">2</field>
+    <field type="BooleanField" name="task_executed">True</field>
+    <field type="IntegerField" name="outcome">2</field>
+    <field type="CharField" name="sstate_checksum">85bccb7802fd5f48</field>
+    <field type="FilePathField" name="path_to_sstate_obj">85/sstarpm.tgz</field>
+    <field to="orm.recipe" name="recipe" rel="ManyToOneRel">2</field>
+    <field type="CharField" name="task_name">z_do_package_write_rpm</field>
+    <field type="DecimalField" name="elapsed_time">245</field>
+    <field type="IntegerField" name="disk_io">12424</field>
+    <field type="IntegerField" name="disk_io_read">23423</field>
+    <field type="IntegerField" name="disk_io_write">83943</field>
+    <field type="DecimalField" name="cpu_time_user">20394.3</field>
+    <field type="DecimalField" name="cpu_time_system">5363.3</field>
+    <field type="IntegerField" name="sstate_result">1</field>
+  </object>
 </django-objects>
diff --git a/bitbake/lib/toaster/toastergui/tests.py b/bitbake/lib/toaster/toastergui/tests.py
index 869c39d..9af1018 100644
--- a/bitbake/lib/toaster/toastergui/tests.py
+++ b/bitbake/lib/toaster/toastergui/tests.py
@@ -25,6 +25,7 @@ from django.test import TestCase
 from django.test.client import RequestFactory
 from django.core.urlresolvers import reverse
 from django.utils import timezone
+from django.db.models import Q
 
 from orm.models import Project, Release, BitbakeVersion, Package, LogMessage
 from orm.models import ReleaseLayerSourcePriority, LayerSource, Layer, Build
@@ -57,7 +58,6 @@ class ViewTests(TestCase):
 
         self.project = Project.objects.first()
         self.recipe1 = Recipe.objects.get(pk=2)
-        self.recipe2 = Recipe.objects.last()
         self.customr = CustomImageRecipe.objects.first()
         self.cust_package = CustomImagePackage.objects.first()
         self.package = Package.objects.first()
@@ -311,7 +311,6 @@ class ViewTests(TestCase):
 
         self.assertEqual(response.status_code, 200)
 
-
     def test_software_recipes_table(self):
         """Test structure returned for Software RecipesTable"""
         table = SoftwareRecipesTable()
@@ -319,27 +318,35 @@ class ViewTests(TestCase):
         response = table.get(request, pid=self.project.id)
         data = json.loads(response.content)
 
+        recipes = Recipe.objects.filter(Q(is_image=False))
+        self.assertTrue(len(recipes) > 1,
+                        "Need more than one software recipe to test "
+                        "SoftwareRecipesTable")
+
+        recipe1 = recipes[0]
+        recipe2 = recipes[1]
+
         rows = data['rows']
-        row1 = next(x for x in rows if x['name'] == self.recipe1.name)
-        row2 = next(x for x in rows if x['name'] == self.recipe2.name)
+        row1 = next(x for x in rows if x['name'] == recipe1.name)
+        row2 = next(x for x in rows if x['name'] == recipe2.name)
 
         self.assertEqual(response.status_code, 200, 'should be 200 OK status')
 
         # check other columns have been populated correctly
-        self.assertTrue(self.recipe1.name in row1['name'])
-        self.assertTrue(self.recipe1.version in row1['version'])
-        self.assertTrue(self.recipe1.description in
+        self.assertTrue(recipe1.name in row1['name'])
+        self.assertTrue(recipe1.version in row1['version'])
+        self.assertTrue(recipe1.description in
                         row1['get_description_or_summary'])
 
-        self.assertTrue(self.recipe1.layer_version.layer.name in
+        self.assertTrue(recipe1.layer_version.layer.name in
                         row1['layer_version__layer__name'])
 
-        self.assertTrue(self.recipe2.name in row2['name'])
-        self.assertTrue(self.recipe2.version in row2['version'])
-        self.assertTrue(self.recipe2.description in
+        self.assertTrue(recipe2.name in row2['name'])
+        self.assertTrue(recipe2.version in row2['version'])
+        self.assertTrue(recipe2.description in
                         row2['get_description_or_summary'])
 
-        self.assertTrue(self.recipe2.layer_version.layer.name in
+        self.assertTrue(recipe2.layer_version.layer.name in
                         row2['layer_version__layer__name'])
 
     def test_toaster_tables(self):
@@ -360,7 +367,9 @@ class ViewTests(TestCase):
                     'layerid': self.lver.pk,
                     'recipeid': self.recipe1.pk,
                     'recipe_id': image_recipe.pk,
-                    'custrecipeid': self.customr.pk}
+                    'custrecipeid': self.customr.pk,
+                    'build_id': 1,
+                    'target_id': 1}
 
             response = table.get(request, **args)
             return json.loads(response.content)
@@ -386,11 +395,14 @@ class ViewTests(TestCase):
 
         # Get a list of classes in tables module
         tables = inspect.getmembers(toastergui.tables, inspect.isclass)
+        tables.extend(inspect.getmembers(toastergui.buildtables,
+                                         inspect.isclass))
 
         for name, table_cls in tables:
             # Filter out the non ToasterTables from the tables module
             if not issubclass(table_cls, toastergui.widgets.ToasterTable) or \
-                table_cls == toastergui.widgets.ToasterTable:
+                table_cls == toastergui.widgets.ToasterTable or \
+               'Mixin' in name:
                 continue
 
             # Get the table data without any options, this also does the
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 8/9] toaster: widgets ToasterTable Catch template rendering exceptions
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (6 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 7/9] toaster: tests Add new build tables to tests Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 15:12 ` [PATCH v2 9/9] toaster: Rework displaying package dependencies across Toaster Michael Wood
  2016-05-26 16:42 ` [PATCH v2 0/9] Port build tables to ToasterTables Barros Pena, Belen
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

If a cell template rendering causes an exception catch it and carry on,
this at least allows the table to show the rest of the data if nothing
else. Also improve the error logging so that it's possible what the
offending template snippet was.

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/widgets.py | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/widgets.py b/bitbake/lib/toaster/toastergui/widgets.py
index 17c97fd..55ff33a 100644
--- a/bitbake/lib/toaster/toastergui/widgets.py
+++ b/bitbake/lib/toaster/toastergui/widgets.py
@@ -29,6 +29,8 @@ from django.core.paginator import Paginator, EmptyPage
 from django.db.models import Q
 from orm.models import Project, ProjectLayer, Layer_Version
 from django.template import Context, Template
+from django.template import VariableDoesNotExist
+from django.template import TemplateSyntaxError
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.exceptions import FieldError
 from django.conf.urls import url, patterns
@@ -338,10 +340,20 @@ class ToasterTable(TemplateView):
                         # so that this can be used as the html class name
                         col['field_name'] = col['static_data_name']
 
-                        # Render the template given
-                        required_data[col['static_data_name']] = \
-                            self.render_static_data(
-                                col['static_data_template'], model_obj)
+                        try:
+                            # Render the template given
+                            required_data[col['static_data_name']] = \
+                                    self.render_static_data(
+                                        col['static_data_template'], model_obj)
+                        except (TemplateSyntaxError,
+                                VariableDoesNotExist) as e:
+                            logger.error("could not render template code"
+                                         "%s %s %s",
+                                         col['static_data_template'],
+                                         e, self.__class__.__name__)
+                            required_data[col['static_data_name']] =\
+                                '<!--error-->'
+
                     else:
                         # Traverse to any foriegn key in the field
                         # e.g. recipe__layer_version__name
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 9/9] toaster: Rework displaying package dependencies across Toaster
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (7 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 8/9] toaster: widgets ToasterTable Catch template rendering exceptions Michael Wood
@ 2016-05-26 15:12 ` Michael Wood
  2016-05-26 16:42 ` [PATCH v2 0/9] Port build tables to ToasterTables Barros Pena, Belen
  9 siblings, 0 replies; 11+ messages in thread
From: Michael Wood @ 2016-05-26 15:12 UTC (permalink / raw)
  To: toaster

After porting the build table to a unified mechanism for showing
dependencies in tables it highlighted that the dependencies selected to
be shown were un-filtered. i.e. all dependencies from all contexts were
shown. The context for a package's dependencies is based on the target
that they were installed onto, or if not installed then a "None" target.

Depending on where the template for the dependencies are show we need to
switch this target which is why a filter and utility function on the
model is added.

Additionally to use the same templates in the build analysis we also
need to optionally add links to the build data for the packages being
displayed as dependencies.

Customising a Custom image recipes may or may not have a target
depending on whether they have been built or not, if not we do a best
effort at getting the dependencies by using the last known target on
that package to get the dependency information.

[YOCTO #9676]

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/orm/models.py                  | 61 ++++++++++++++++++----
 bitbake/lib/toaster/toastergui/buildtables.py      | 15 +++++-
 bitbake/lib/toaster/toastergui/tables.py           | 15 +++++-
 .../snippets/pkg_dependencies_popover.html         | 44 ++++++++++++----
 .../snippets/pkg_revdependencies_popover.html      | 46 ++++++++++++----
 .../toaster/toastergui/templatetags/projecttags.py |  8 +++
 bitbake/lib/toaster/toastergui/views.py            |  4 +-
 7 files changed, 157 insertions(+), 36 deletions(-)

diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
index 88967a2..7c26fcf 100644
--- a/bitbake/lib/toaster/orm/models.py
+++ b/bitbake/lib/toaster/orm/models.py
@@ -862,31 +862,70 @@ class CustomImagePackage(Package):
                                             related_name='appends_set')
 
 
-
 class Package_DependencyManager(models.Manager):
     use_for_related_fields = True
+    TARGET_LATEST = "use-latest-target-for-target"
 
     def get_queryset(self):
         return super(Package_DependencyManager, self).get_queryset().exclude(package_id = F('depends_on__id'))
 
-    def get_total_source_deps_size(self):
-        """ Returns the total file size of all the packages that depend on
-        thispackage.
-        """
-        return self.all().aggregate(Sum('depends_on__size'))
+    def for_target_or_none(self, target):
+        """ filter the dependencies to be displayed by the supplied target
+        if no dependences are found for the target then try None as the target
+        which will return the dependences calculated without the context of a
+        target e.g. non image recipes.
 
-    def get_total_revdeps_size(self):
-        """ Returns the total file size of all the packages that depend on
-        this package.
+        returns: { size, packages }
         """
-        return self.all().aggregate(Sum('package_id__size'))
+        package_dependencies = self.all_depends().order_by('depends_on__name')
 
+        if target is self.TARGET_LATEST:
+            installed_deps =\
+                    package_dependencies.filter(~Q(target__target=None))
+        else:
+            installed_deps =\
+                    package_dependencies.filter(Q(target__target=target))
+
+        packages_list = None
+        total_size = 0
+
+        # If we have installed depdencies for this package and target then use
+        # these to display
+        if installed_deps.count() > 0:
+            packages_list = installed_deps
+            total_size = installed_deps.aggregate(
+                Sum('depends_on__size'))['depends_on__size__sum']
+        else:
+            new_list = []
+            package_names = []
+
+            # Find dependencies for the package that we know about even if
+            # it's not installed on a target e.g. from a non-image recipe
+            for p in package_dependencies.filter(Q(target=None)):
+                if p.depends_on.name in package_names:
+                    continue
+                else:
+                    package_names.append(p.depends_on.name)
+                    new_list.append(p.pk)
+                    # while we're here we may as well total up the size to
+                    # avoid iterating again
+                    total_size += p.depends_on.size
+
+            # We want to return a queryset here for consistency so pick the
+            # deps from the new_list
+            packages_list = package_dependencies.filter(Q(pk__in=new_list))
+
+        return {'packages': packages_list,
+                'size': total_size}
 
     def all_depends(self):
-        """ Returns just the depends packages and not any other dep_type """
+        """ Returns just the depends packages and not any other dep_type
+        Note that this is for any target
+        """
         return self.filter(Q(dep_type=Package_Dependency.TYPE_RDEPENDS) |
                            Q(dep_type=Package_Dependency.TYPE_TRDEPENDS))
 
+
 class Package_Dependency(models.Model):
     TYPE_RDEPENDS = 0
     TYPE_TRDEPENDS = 1
diff --git a/bitbake/lib/toaster/toastergui/buildtables.py b/bitbake/lib/toaster/toastergui/buildtables.py
index 17de369..e237e4e 100644
--- a/bitbake/lib/toaster/toastergui/buildtables.py
+++ b/bitbake/lib/toaster/toastergui/buildtables.py
@@ -47,6 +47,7 @@ class BuiltPackagesTableBase(tables.PackagesTable):
     def setup_queryset(self, *args, **kwargs):
         build = Build.objects.get(pk=kwargs['build_id'])
         self.static_context_extra['build'] = build
+        self.static_context_extra['target_name'] = None
         self.queryset = build.package_set.all().exclude(recipe=None)
         self.queryset = self.queryset.order_by(self.default_orderby)
 
@@ -187,7 +188,15 @@ class InstalledPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
         self.static_context_extra['build'] = build
 
         target = Target.objects.get(pk=kwargs['target_id'])
+        # We send these separately because in the case of image details table
+        # we don't have a target just the recipe name as the target
+        self.static_context_extra['target_name'] = target.target
+        self.static_context_extra['target_id'] = target.pk
+
+        self.static_context_extra['add_links'] = True
+
         self.queryset = self.make_package_list(target)
+        self.queryset = self.queryset.order_by(self.default_orderby)
 
     def setup_columns(self, *args, **kwargs):
         super(InstalledPackagesTable, self).setup_columns(**kwargs)
@@ -195,11 +204,13 @@ class InstalledPackagesTable(BuildTablesMixin, BuiltPackagesTableBase):
                         static_data_name="installed_size",
                         static_data_template="{% load projecttags %}"
                         "{{data.size|filtered_filesizeformat}}",
-                        orderable=True)
+                        orderable=True,
+                        hidden=True)
 
         # Add the template to show installed name for installed packages
         install_name_tmpl =\
-            ('{{data.name}} '
+            ('<a href="{% url "package_included_detail" extra.build.pk'
+             ' extra.target_id data.pk %}">{{data.name}}</a>'
              '{% if data.installed_name and data.installed_name !='
              ' data.name %}'
              '<span class="muted"> as {{data.installed_name}}</span>'
diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py
index 39d3f93..1add5b6 100644
--- a/bitbake/lib/toaster/toastergui/tables.py
+++ b/bitbake/lib/toaster/toastergui/tables.py
@@ -22,7 +22,7 @@
 from toastergui.widgets import ToasterTable
 from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project
 from orm.models import CustomImageRecipe, Package, Target, Build, LogMessage, Task
-from orm.models import CustomImagePackage
+from orm.models import CustomImagePackage, Package_DependencyManager
 from django.db.models import Q, Max, Sum, Count, When, Case, Value, IntegerField
 from django.conf.urls import url
 from django.core.urlresolvers import reverse, resolve
@@ -676,6 +676,7 @@ class PackagesTable(ToasterTable):
 
     def setup_queryset(self, *args, **kwargs):
         recipe = Recipe.objects.get(pk=kwargs['recipe_id'])
+        self.static_context_extra['target_name'] = recipe.name
 
         self.queryset = self.create_package_list(recipe, kwargs['pid'])
         self.queryset = self.queryset.order_by('name')
@@ -746,7 +747,19 @@ class SelectPackagesTable(PackagesTable):
 
         self.queryset = self.queryset.order_by('name')
 
+        # This target is the target used to work out which group of dependences
+        # to display, if we've built the custom image we use it otherwise we
+        # can use the based recipe instead
+        if prj.build_set.filter(target__target=self.cust_recipe.name).count()\
+           > 0:
+            self.static_context_extra['target_name'] = self.cust_recipe.name
+        else:
+            self.static_context_extra['target_name'] =\
+                    Package_DependencyManager.TARGET_LATEST
+
         self.static_context_extra['recipe_id'] = kwargs['custrecipeid']
+
+
         self.static_context_extra['current_packages'] = \
                 current_packages.values_list('pk', flat=True)
 
diff --git a/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html
index a3fcdb0..04d5f2b 100644
--- a/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html
+++ b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html
@@ -1,14 +1,38 @@
 {# Popover that displays the dependences and sizes of a package 'data' used in the Packages table #}
-{% with data.package_dependencies_source.all_depends.count as dep_count %}
 {% load projecttags %}
-{% if dep_count %}
- <a data-content="<ul class='unstyled'>
-   {% for dep in data.package_dependencies_source.all_depends %}
-  <li>{{dep.depends_on.name}} {% if dep.depends_on.size > 0 %}({{dep.depends_on.size|filtered_filesizeformat}}){% endif %}</li>
-    {% endfor %}
-  </ul>" title="" class="btn" data-original-title="
-  <strong>{{data.name}}</strong> dependencies - <strong>{{data.package_dependencies_source.get_total_source_deps_size.depends_on__size__sum|filtered_filesizeformat}}</strong>">
-    {{dep_count}}
-</a>
+
+{% with package_deps=data.package_dependencies_source|for_target:extra.target_name %}
+{% with count_package=package_deps.packages|length %}
+
+{% if count_package > 0 %}
+  <a data-content='<ul class="unstyled">
+  {% for dep in package_deps.packages %}
+     <li>
+      {% if extra.add_links %}
+      <a href="{% url 'package_included_detail' extra.build.pk extra.target_id dep.depends_on.pk %}">
+      {{dep.depends_on.name}}</a>
+      {% else %}
+        {{dep.depends_on.name}}
+      {% endif %}
+      {% if dep.depends_on.size > 0 %}
+        ({{dep.depends_on.size|filtered_filesizeformat}})
+      {% endif %}
+     </li>
+   {% endfor %}
+   </ul>' title="" class="btn" data-original-title='
+   <strong>
+     {% if extra.add_links %}
+      <a href="{% url 'package_included_dependencies' extra.build.pk extra.target_id data.pk %}">
+      {{data.name}}</a>
+      {% else %}
+      {{data.name}}
+      {% endif %}
+      </strong>
+      dependencies -
+      <strong>{{package_deps.size|filtered_filesizeformat}}</strong>'>
+      {{count_package}}
+  </a>
 {% endif %}
+
+{% endwith %}
 {% endwith %}
diff --git a/bitbake/lib/toaster/toastergui/templates/snippets/pkg_revdependencies_popover.html b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_revdependencies_popover.html
index 453a9d0..21c2b9f 100644
--- a/bitbake/lib/toaster/toastergui/templates/snippets/pkg_revdependencies_popover.html
+++ b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_revdependencies_popover.html
@@ -1,14 +1,38 @@
-{# Popover that displays the reverse dependencies and sizes of a package 'data' used in the Packages table #}
-{% with data.package_dependencies_target.all_depends.count as dep_count %}
+{# Popover that displays the reverse dependences and sizes of a package 'data' used in the Packages table #}
 {% load projecttags %}
-{% if dep_count %}
- <a data-content="<ul class='unstyled'>
-   {% for dep in data.package_dependencies_target.all_depends|dictsort:'package.name' %}
-  <li>{{dep.package.name}} {% if dep.package.size > 0 %}({{dep.package.size|filtered_filesizeformat}}){% endif %}</li>
-    {% endfor %}
-  </ul>" title="" class="btn" data-original-title="
-  <strong>{{data.name}}</strong> reverse dependencies - <strong>{{data.package_dependencies_target.get_total_revdeps_size.package_id__size__sum|filtered_filesizeformat}}</strong>">
-    {{dep_count}}
-</a>
+
+{% with package_deps=data.package_dependencies_target|for_target:extra.target_name %}
+{% with count_package=package_deps.packages|length %}
+
+{% if count_package > 0 %}
+  <a data-content='<ul class="unstyled">
+  {% for dep in package_deps.packages|dictsort:"package.name" %}
+     <li>
+      {% if extra.add_links %}
+      <a href="{% url 'package_included_detail' extra.build.pk extra.target_id dep.package.pk %}">
+      {{dep.package.name}}</a>
+      {% else %}
+      {{dep.package.name}}
+      {% endif %}
+      {% if dep.package.size > 0 %}
+        ({{dep.package.size|filtered_filesizeformat}})
+      {% endif %}
+     </li>
+   {% endfor %}
+   </ul>' title="" class="btn" data-original-title='
+   <strong>
+     {% if extra.add_links %}
+      <a href="{% url 'package_included_reverse_dependencies' extra.build.pk extra.target_id data.pk %}">
+      {{data.name}}</a>
+      {% else %}
+      {{data.name}}
+      {% endif %}
+      </strong>
+      dependencies -
+      <strong>{{package_deps.size|filtered_filesizeformat}}</strong>'>
+      {{count_package}}
+  </a>
 {% endif %}
+
+{% endwith %}
 {% endwith %}
diff --git a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
index 75f2261..9ee7a59 100644
--- a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
@@ -297,3 +297,11 @@ def cut_path_prefix(fullpath, prefixes):
         if fullpath.startswith(prefix):
             return relpath(fullpath, prefix)
     return fullpath
+
+
+@register.filter
+def for_target(package_dependencies, target):
+    """ filter the dependencies to be displayed by the supplied target
+    if no dependences are found for the target then return the predicted
+    dependences"""
+    return package_dependencies.for_target_or_none(target)
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index ed29c93..bba956a 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -2042,7 +2042,9 @@ if True:
 
                 # Dependencies for package which aren't satisfied by the
                 # current packages in the custom image recipe
-                deps = package.package_dependencies_source.annotate(
+                deps =\
+                    package.package_dependencies_source.for_target_or_none(
+                        recipe.name)['packages'].annotate(
                     name=F('depends_on__name'),
                     pk=F('depends_on__pk'),
                     size=F('depends_on__size'),
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 0/9] Port build tables to ToasterTables
  2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
                   ` (8 preceding siblings ...)
  2016-05-26 15:12 ` [PATCH v2 9/9] toaster: Rework displaying package dependencies across Toaster Michael Wood
@ 2016-05-26 16:42 ` Barros Pena, Belen
  9 siblings, 0 replies; 11+ messages in thread
From: Barros Pena, Belen @ 2016-05-26 16:42 UTC (permalink / raw)
  To: Wood, Michael G, toaster



On 26/05/2016 16:12, "toaster-bounces@yoctoproject.org on behalf of
Michael Wood" <toaster-bounces@yoctoproject.org on behalf of
michael.g.wood@intel.com> wrote:

>Port the main build tables in the build analysis area to ToasterTables.
>This is the table for Installed packages, Tasks, Recipes, Packages, Time,
>CPU usage and Disk I/O
>
>Changes in v2
> - Addressed issues identified by Belen in v1 apart from 'Size' reporting
>question where we are no just having both as columns if applicable
> - Reworked the display of dependencies popover

This looks great. The only issue is that in the packages installed table
the link for the columns

Package version
Approx size and
License

Is going to the wrong page (for example
/toastergui/build/66/package/129651 instead of
/toastergui/build/66/package_included_detail/70/129651).

I can easily fix this while moving these tables to Boostrap 3, so if there
are no issues with the code, it would be good to get this merged into
toaster-next soonish.

Thanks!

Belén

>
>Michael Wood (9):
>  toaster: ToasterTable widget add an 'a' to Name in Exception class
>  toaster: port table for Built packages to ToasterTable
>  toaster: port Built recipes table to toastertables
>  toaster: port Task tables to ToasterTables widget
>  toaster: port Installed packages table to ToasterTable
>  toaster: table.js Add the ability to highlight a particular row
>  toaster: tests Add new build tables to tests
>  toaster: widgets ToasterTable Catch template rendering exceptions
>  toaster: Rework displaying package dependencies across Toaster
>
> bitbake/lib/toaster/orm/models.py                  |  61 +-
> bitbake/lib/toaster/toastergui/buildtables.py      | 574
>++++++++++++++++++
> .../fixtures/toastergui-unittest-data.xml          |  91 ++-
> bitbake/lib/toaster/toastergui/static/js/table.js  |   9 +
> bitbake/lib/toaster/toastergui/tables.py           |  25 +-
> .../toastergui/templates/basebuildpage.html        |  26 +-
> .../lib/toaster/toastergui/templates/bpackage.html | 108 ----
> .../toastergui/templates/builddashboard.html       |   4 +-
> .../templates/buildinfo-toastertable.html          |  25 +
> .../toaster/toastergui/templates/buildtime.html    |   4 -
> .../lib/toaster/toastergui/templates/recipes.html  | 113 ----
> .../templates/snippets/gitrev_popover.html         |   8 +
> .../snippets/pkg_dependencies_popover.html         |  44 +-
> .../snippets/pkg_revdependencies_popover.html      |  46 +-
> .../lib/toaster/toastergui/templates/target.html   | 115 +---
> bitbake/lib/toaster/toastergui/templates/task.html |   2 +-
> .../lib/toaster/toastergui/templates/tasks.html    | 143 -----
> .../toaster/toastergui/templatetags/projecttags.py |   8 +
> bitbake/lib/toaster/toastergui/tests.py            |  40 +-
> bitbake/lib/toaster/toastergui/urls.py             |  46 +-
> bitbake/lib/toaster/toastergui/views.py            | 654
>+--------------------
> bitbake/lib/toaster/toastergui/widgets.py          |  31 +-
> 22 files changed, 934 insertions(+), 1243 deletions(-)
> create mode 100644 bitbake/lib/toaster/toastergui/buildtables.py
> delete mode 100644 bitbake/lib/toaster/toastergui/templates/bpackage.html
> create mode 100644
>bitbake/lib/toaster/toastergui/templates/buildinfo-toastertable.html
> delete mode 100644
>bitbake/lib/toaster/toastergui/templates/buildtime.html
> delete mode 100644 bitbake/lib/toaster/toastergui/templates/recipes.html
> create mode 100644
>bitbake/lib/toaster/toastergui/templates/snippets/gitrev_popover.html
> delete mode 100644 bitbake/lib/toaster/toastergui/templates/tasks.html
>
>-- 
>2.7.4
>
>-- 
>_______________________________________________
>toaster mailing list
>toaster@yoctoproject.org
>https://lists.yoctoproject.org/listinfo/toaster



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-05-26 16:42 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-26 15:12 [PATCH v2 0/9] Port build tables to ToasterTables Michael Wood
2016-05-26 15:12 ` [PATCH v2 1/9] toaster: ToasterTable widget add an 'a' to Name in Exception class Michael Wood
2016-05-26 15:12 ` [PATCH v2 2/9] toaster: port table for Built packages to ToasterTable Michael Wood
2016-05-26 15:12 ` [PATCH v2 3/9] toaster: port Built recipes table to toastertables Michael Wood
2016-05-26 15:12 ` [PATCH v2 4/9] toaster: port Task tables to ToasterTables widget Michael Wood
2016-05-26 15:12 ` [PATCH v2 5/9] toaster: port Installed packages table to ToasterTable Michael Wood
2016-05-26 15:12 ` [PATCH v2 6/9] toaster: table.js Add the ability to highlight a particular row Michael Wood
2016-05-26 15:12 ` [PATCH v2 7/9] toaster: tests Add new build tables to tests Michael Wood
2016-05-26 15:12 ` [PATCH v2 8/9] toaster: widgets ToasterTable Catch template rendering exceptions Michael Wood
2016-05-26 15:12 ` [PATCH v2 9/9] toaster: Rework displaying package dependencies across Toaster Michael Wood
2016-05-26 16:42 ` [PATCH v2 0/9] Port build tables to ToasterTables Barros Pena, Belen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.