Filters are added to tables by passing a list of function to the
ToasterTable.add_filters() method. This means a lot of code is
duplicated.

Add a QuerysetFilter class which encapsulates a filter for a
Queryset and provides a generic count() method.

[YOCTO #8738]

Signed-off-by: Elliot Smith <[email protected]>
---
 bitbake/lib/toaster/toastergui/querysetfilter.py |  24 +++++
 bitbake/lib/toaster/toastergui/tables.py         | 108 +++++++----------------
 bitbake/lib/toaster/toastergui/widgets.py        |  16 ++--
 3 files changed, 65 insertions(+), 83 deletions(-)
 create mode 100644 bitbake/lib/toaster/toastergui/querysetfilter.py

diff --git a/bitbake/lib/toaster/toastergui/querysetfilter.py 
b/bitbake/lib/toaster/toastergui/querysetfilter.py
new file mode 100644
index 0000000..62297e9
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/querysetfilter.py
@@ -0,0 +1,24 @@
+class QuerysetFilter(object):
+    """ Filter for a queryset """
+
+    def __init__(self, criteria=None):
+        if criteria:
+            self.set_criteria(criteria)
+
+    def set_criteria(self, criteria):
+        """
+        criteria is an instance of django.db.models.Q;
+        see 
https://docs.djangoproject.com/en/1.9/ref/models/querysets/#q-objects
+        """
+        self.criteria = criteria
+
+    def filter(self, queryset):
+        """
+        Filter queryset according to the criteria for this filter,
+        returning the filtered queryset
+        """
+        return queryset.filter(self.criteria)
+
+    def count(self, queryset):
+        """ Returns a count of the elements in the filtered queryset """
+        return self.filter(queryset).count()
diff --git a/bitbake/lib/toaster/toastergui/tables.py 
b/bitbake/lib/toaster/toastergui/tables.py
index 0639b00..a49e45c 100644
--- a/bitbake/lib/toaster/toastergui/tables.py
+++ b/bitbake/lib/toaster/toastergui/tables.py
@@ -20,6 +20,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 from toastergui.widgets import ToasterTable
+from toastergui.querysetfilter import QuerysetFilter
 from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project
 from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task
 from django.db.models import Q, Max, Count
@@ -27,22 +28,10 @@ from django.conf.urls import url
 from django.core.urlresolvers import reverse
 from django.views.generic import TemplateView
 
-class ProjectFiltersMixin(object):
-    """Common mixin for recipe, machine in project filters"""
-
-    def filter_in_project(self, count_only=False):
-        query = self.queryset.filter(layer_version__in=self.project_layers)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-    def filter_not_in_project(self, count_only=False):
-        query = self.queryset.exclude(layer_version__in=self.project_layers)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
+class ProjectFilters(object):
+    def __init__(self, project_layers):
+        self.in_project = QuerysetFilter(Q(layer_version__in=project_layers))
+        self.not_in_project = 
QuerysetFilter(~Q(layer_version__in=project_layers))
 
 class LayersTable(ToasterTable):
     """Table of layers in Toaster"""
@@ -60,34 +49,21 @@ class LayersTable(ToasterTable):
 
         return context
 
-
     def setup_filters(self, *args, **kwargs):
         project = Project.objects.get(pk=kwargs['pid'])
         self.project_layers = ProjectLayer.objects.filter(project=project)
 
+        criteria = Q(projectlayer__in=self.project_layers)
+        in_project_filter = QuerysetFilter(criteria)
+        not_in_project_filter = QuerysetFilter(~criteria)
 
         self.add_filter(title="Filter by project layers",
                         name="in_current_project",
                         filter_actions=[
-                            self.make_filter_action("in_project", "Layers 
added to this project", self.filter_in_project),
-                            self.make_filter_action("not_in_project", "Layers 
not added to this project", self.filter_not_in_project)
+                            self.make_filter_action("in_project", "Layers 
added to this project", in_project_filter),
+                            self.make_filter_action("not_in_project", "Layers 
not added to this project", not_in_project_filter)
                         ])
 
-    def filter_in_project(self, count_only=False):
-        query = self.queryset.filter(projectlayer__in=self.project_layers)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-    def filter_not_in_project(self, count_only=False):
-        query = self.queryset.exclude(projectlayer__in=self.project_layers)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-
     def setup_queryset(self, *args, **kwargs):
         prj = Project.objects.get(pk = kwargs['pid'])
         compatible_layers = prj.get_all_compatible_layer_versions()
@@ -204,7 +180,7 @@ class LayersTable(ToasterTable):
                         computation = lambda x: x.layer.name)
 
 
-class MachinesTable(ToasterTable, ProjectFiltersMixin):
+class MachinesTable(ToasterTable):
     """Table of Machines in Toaster"""
 
     def __init__(self, *args, **kwargs):
@@ -221,11 +197,13 @@ class MachinesTable(ToasterTable, ProjectFiltersMixin):
     def setup_filters(self, *args, **kwargs):
         project = Project.objects.get(pk=kwargs['pid'])
 
+        project_filters = ProjectFilters(self.project_layers)
+
         self.add_filter(title="Filter by project machines",
                         name="in_current_project",
                         filter_actions=[
-                            self.make_filter_action("in_project", "Machines 
provided by layers added to this project", self.filter_in_project),
-                            self.make_filter_action("not_in_project", 
"Machines provided by layers not added to this project", 
self.filter_not_in_project)
+                            self.make_filter_action("in_project", "Machines 
provided by layers added to this project", project_filters.in_project),
+                            self.make_filter_action("not_in_project", 
"Machines provided by layers not added to this project", 
project_filters.not_in_project)
                         ])
 
     def setup_queryset(self, *args, **kwargs):
@@ -313,7 +291,7 @@ class LayerMachinesTable(MachinesTable):
                         static_data_template=select_btn_template)
 
 
-class RecipesTable(ToasterTable, ProjectFiltersMixin):
+class RecipesTable(ToasterTable):
     """Table of All Recipes in Toaster"""
 
     def __init__(self, *args, **kwargs):
@@ -338,11 +316,13 @@ class RecipesTable(ToasterTable, ProjectFiltersMixin):
         return context
 
     def setup_filters(self, *args, **kwargs):
+        project_filters = ProjectFilters(self.project_layers)
+
         self.add_filter(title="Filter by project recipes",
                         name="in_current_project",
                         filter_actions=[
-                            self.make_filter_action("in_project", "Recipes 
provided by layers added to this project", self.filter_in_project),
-                            self.make_filter_action("not_in_project", "Recipes 
provided by layers not added to this project", self.filter_not_in_project)
+                            self.make_filter_action("in_project", "Recipes 
provided by layers added to this project", project_filters.in_project),
+                            self.make_filter_action("not_in_project", "Recipes 
provided by layers not added to this project", project_filters.not_in_project)
                         ])
 
     def setup_queryset(self, *args, **kwargs):
@@ -1090,52 +1070,20 @@ class BuildsTable(ToasterTable):
                         static_data_name='project-name',
                         static_data_template=project_template)
 
-    def filter_only_failed_builds(self, count_only=False):
-        """ Only show builds with failed outcome """
-        query = self.queryset.filter(outcome=Build.FAILED)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-    def filter_only_successful_builds(self, count_only=False):
-        """ Only show builds with successful outcome """
-        query = self.queryset.filter(outcome=Build.SUCCEEDED)
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-    def filter_only_builds_with_failed_tasks(self, count_only=False):
-        """ Only show builds with failed tasks """
-        query = self.queryset.filter(task_build__outcome=Task.OUTCOME_FAILED)
-
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
-    def filter_only_builds_without_failed_tasks(self, count_only=False):
-        """ Only show builds without failed tasks """
-        query = 
self.queryset.filter(~Q(task_build__outcome=Task.OUTCOME_FAILED))
-
-        if count_only:
-            return query.count()
-
-        self.queryset = query
-
     def setup_filters(self, *args, **kwargs):
         # outcomes
+        filter_only_successful_builds = 
QuerysetFilter(Q(outcome=Build.SUCCEEDED))
         successful_builds_filter = self.make_filter_action(
             'successful_builds',
             'Successful builds',
-            self.filter_only_successful_builds
+            filter_only_successful_builds
         )
 
+        filter_only_failed_builds = QuerysetFilter(Q(outcome=Build.FAILED))
         failed_builds_filter = self.make_filter_action(
             'failed_builds',
             'Failed builds',
-            self.filter_only_failed_builds
+            filter_only_failed_builds
         )
 
         self.add_filter(title='Filter builds by outcome',
@@ -1146,16 +1094,20 @@ class BuildsTable(ToasterTable):
                         ])
 
         # failed tasks
+        criteria = Q(task_build__outcome=Task.OUTCOME_FAILED)
+        filter_only_builds_with_failed_tasks = QuerysetFilter(criteria)
         with_failed_tasks_filter = self.make_filter_action(
             'with_failed_tasks',
             'Builds with failed tasks',
-            self.filter_only_builds_with_failed_tasks
+            filter_only_builds_with_failed_tasks
         )
 
+        criteria = ~Q(task_build__outcome=Task.OUTCOME_FAILED)
+        filter_only_builds_without_failed_tasks = QuerysetFilter(criteria)
         without_failed_tasks_filter = self.make_filter_action(
             'without_failed_tasks',
             'Builds without failed tasks',
-            self.filter_only_builds_without_failed_tasks
+            filter_only_builds_without_failed_tasks
         )
 
         self.add_filter(title='Filter builds by failed tasks',
diff --git a/bitbake/lib/toaster/toastergui/widgets.py 
b/bitbake/lib/toaster/toastergui/widgets.py
index 6bb3889..71b29ea 100644
--- a/bitbake/lib/toaster/toastergui/widgets.py
+++ b/bitbake/lib/toaster/toastergui/widgets.py
@@ -32,6 +32,7 @@ from django.template import Context, Template
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.exceptions import FieldError
 from django.conf.urls import url, patterns
+from toastergui.querysetfilter import QuerysetFilter
 
 import types
 import json
@@ -113,7 +114,8 @@ class ToasterTable(TemplateView):
                               cls=DjangoJSONEncoder)
         else:
             for actions in self.filters[name]['filter_actions']:
-                actions['count'] = 
self.filter_actions[actions['name']](count_only=True)
+                queryset_filter = self.filter_actions[actions['name']]
+                actions['count'] = queryset_filter.count(self.queryset)
 
             # Add the "All" items filter action
             self.filters[name]['filter_actions'].insert(0, {
@@ -151,15 +153,18 @@ class ToasterTable(TemplateView):
           'filter_actions' : filter_actions,
         }
 
-    def make_filter_action(self, name, title, action_function):
-        """ Utility to make a filter_action """
+    def make_filter_action(self, name, title, queryset_filter):
+        """
+        Utility to make a filter_action; queryset_filter is an instance
+        of QuerysetFilter or a function
+        """
 
         action = {
           'title' : title,
           'name' : name,
         }
 
-        self.filter_actions[name] = action_function
+        self.filter_actions[name] = queryset_filter
 
         return action
 
@@ -222,7 +227,8 @@ class ToasterTable(TemplateView):
             return
 
         try:
-            self.filter_actions[filter_action]()
+            queryset_filter = self.filter_actions[filter_action]
+            self.queryset = queryset_filter.filter(self.queryset)
         except KeyError:
             # pass it to the user - programming error here
             raise
-- 
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

-- 
_______________________________________________
toaster mailing list
[email protected]
https://lists.yoctoproject.org/listinfo/toaster

Reply via email to