-add feature to filter on test attributes in TKO
  -new server arguments "include_attributes_where" and "exclude_attributes_where" for filtering on test attributes
  -refactor joining code in TKO models.py to support test attributes joining
  -add new UI to CommonPanel.java to filter on test attributes. some of the UI code was written in a general way so that in the future it could be merged with some of the graphing UI code.
-modified TestSets and code that uses them to fix two bugs - first, TestSets didn't contain all the relevant filtering information (only the SQL clause), and second, the SQL clause would build up incorrectly during drilldown



git-svn-id: http://test.kernel.org/svn/autotest/trunk@2177 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/new_tko/tko/models.py b/new_tko/tko/models.py
index b25a24e..70802d3 100644
--- a/new_tko/tko/models.py
+++ b/new_tko/tko/models.py
@@ -247,39 +247,62 @@
         return query.extra(select=extra_select)
 
 
-    def _add_label_joins(self, query_set, suffix = '', join_condition='',
-                         exclude=False):
+    def _add_join(self, query_set, join_table, join_condition='',
+                  join_key='test_idx', suffix='', exclude=False,
+                  force_left_join=False):
         table_name = self.model._meta.db_table
-        first_join_alias = 'test_labels_tests' + suffix
-        first_join_condition = '%s.test_id = %s.test_idx' % (first_join_alias,
-                                                             table_name)
+        join_alias = join_table + suffix
+        full_join_key = join_alias + '.' + join_key
+        full_join_condition = '%s = %s.test_idx' % (full_join_key, table_name)
         if join_condition:
-            first_join_condition += ' AND ' + join_condition
+            full_join_condition += ' AND (' + join_condition + ')'
+        if exclude or force_left_join:
+            join_type = 'LEFT JOIN'
+        else:
+            join_type = 'INNER JOIN'
+
         filter_object = self._CustomSqlQ()
-        filter_object.add_join('test_labels_tests',
-                               first_join_condition,
-                               'LEFT JOIN',
-                               alias=first_join_alias)
+        filter_object.add_join(join_table,
+                               full_join_condition,
+                               join_type,
+                               alias=join_alias)
+        if exclude:
+            filter_object.add_where(full_join_key + ' IS NULL')
+        return query_set.filter(filter_object).distinct()
+
+
+    def _add_label_joins(self, query_set, suffix=''):
+        query_set = self._add_join(query_set, 'test_labels_tests',
+                                   join_key='test_id', suffix=suffix,
+                                   force_left_join=True)
 
         second_join_alias = 'test_labels' + suffix
         second_join_condition = ('%s.id = %s.testlabel_id' %
-                                 (second_join_alias, first_join_alias))
+                                 (second_join_alias,
+                                  'test_labels_tests' + suffix))
+        filter_object = self._CustomSqlQ()
         filter_object.add_join('test_labels',
                                second_join_condition,
                                'LEFT JOIN',
                                alias=second_join_alias)
+        return query_set.filter(filter_object)
 
-        if exclude:
-            filter_object.add_where(first_join_alias + '.testlabel_id IS NULL')
-        return query_set.filter(filter_object).distinct()
+
+    def _add_attribute_join(self, query_set, suffix='', join_condition='',
+                            exclude=False):
+        return self._add_join(query_set, 'test_attributes',
+                              join_condition=join_condition,
+                              suffix=suffix, exclude=exclude)
 
 
     def _get_label_ids_from_names(self, label_names):
+        if not label_names:
+            return []
         query = TestLabel.objects.filter(name__in=label_names).values('id')
         return [label['id'] for label in query]
 
 
-    def get_query_set_with_labels(self, filter_data):
+    def get_query_set_with_joins(self, filter_data):
         exclude_labels = filter_data.pop('exclude_labels', [])
         query_set = self.get_query_set()
         joined = False
@@ -288,15 +311,33 @@
             query_set = self._add_label_joins(query_set)
             joined = True
 
-        if exclude_labels:
-            label_ids = self._get_label_ids_from_names(exclude_labels)
-            if label_ids:
-                condition = ('test_labels_tests_exclude.testlabel_id IN (%s)' %
-                             ','.join(str(label_id) for label_id in label_ids))
-                query_set = self._add_label_joins(query_set, suffix='_exclude',
-                                                  join_condition=condition,
-                                                  exclude=True)
-                joined = True
+        exclude_label_ids = self._get_label_ids_from_names(exclude_labels)
+        if exclude_label_ids:
+            condition = ('test_labels_tests_exclude.testlabel_id IN (%s)' %
+                         ','.join(str(label_id)
+                                  for label_id in exclude_label_ids))
+            query_set = self._add_join(query_set, 'test_labels_tests',
+                                       join_key='test_id',
+                                       suffix='_exclude',
+                                       join_condition=condition,
+                                       exclude=True)
+            joined = True
+
+        include_attributes_where = filter_data.pop('include_attributes_where',
+                                                   '')
+        exclude_attributes_where = filter_data.pop('exclude_attributes_where',
+                                                   '')
+        if include_attributes_where:
+            query_set = self._add_attribute_join(
+                query_set, suffix='_include',
+                join_condition=include_attributes_where)
+            joined = True
+        if exclude_attributes_where:
+            query_set = self._add_attribute_join(
+                query_set, suffix='_exclude',
+                join_condition=exclude_attributes_where,
+                exclude=True)
+            joined = True
 
         if not joined:
             filter_data['no_distinct'] = True
@@ -384,7 +425,7 @@
     @classmethod
     def query_objects(cls, filter_data, initial_query=None):
         if initial_query is None:
-            initial_query = cls.objects.get_query_set_with_labels(filter_data)
+            initial_query = cls.objects.get_query_set_with_joins(filter_data)
         return super(TestView, cls).query_objects(filter_data,
                                                   initial_query=initial_query)