blob: daf1368fe74280aab70e4dee31b3767bee3016f2 [file] [log] [blame]
showard35444862008-08-07 22:35:30 +00001from django.db import models as dbmodels, connection
2from django.utils import datastructures
3from autotest_lib.frontend.afe import model_logic, readonly_connection
4
5class TempManager(model_logic.ExtendedManager):
6 _GROUP_COUNT_NAME = 'group_count'
7
8 def _get_key_unless_is_function(self, field):
9 if '(' in field:
10 return field
showard7c199df2008-10-03 10:17:15 +000011 return self.get_key_on_this_table(field)
showard35444862008-08-07 22:35:30 +000012
13
showardf2489522008-10-23 23:08:00 +000014 def _get_field_names(self, fields, extra_select_fields={}):
15 field_names = []
16 for field in fields:
17 if field in extra_select_fields:
showardd2b0c882009-10-19 18:34:11 +000018 field_names.append(extra_select_fields[field][0])
showardf2489522008-10-23 23:08:00 +000019 else:
20 field_names.append(self._get_key_unless_is_function(field))
21 return field_names
showard35444862008-08-07 22:35:30 +000022
23
showard8bfb5cb2009-10-07 20:49:15 +000024 def _get_group_query_sql(self, query, group_by):
showarda5288b42009-07-28 20:06:08 +000025 sql, params = query.query.as_sql()
showardd50ffb42008-09-04 02:47:45 +000026
27 # insert GROUP BY clause into query
showard8bfb5cb2009-10-07 20:49:15 +000028 group_fields = self._get_field_names(group_by, query.query.extra_select)
showard7c199df2008-10-03 10:17:15 +000029 group_by_clause = ' GROUP BY ' + ', '.join(group_fields)
showarda5288b42009-07-28 20:06:08 +000030 group_by_position = sql.rfind('ORDER BY')
showardd50ffb42008-09-04 02:47:45 +000031 if group_by_position == -1:
showarda5288b42009-07-28 20:06:08 +000032 group_by_position = len(sql)
33 sql = (sql[:group_by_position] +
34 group_by_clause + ' ' +
35 sql[group_by_position:])
showard35444862008-08-07 22:35:30 +000036
showarda5288b42009-07-28 20:06:08 +000037 return sql, params
showard35444862008-08-07 22:35:30 +000038
39
showard06b82fc2009-06-30 01:59:42 +000040 def _get_column_names(self, cursor):
showard8bfb5cb2009-10-07 20:49:15 +000041 """
showard06b82fc2009-06-30 01:59:42 +000042 Gets the column names from the cursor description. This method exists
showard8bfb5cb2009-10-07 20:49:15 +000043 so that it can be mocked in the unit test for sqlite3 compatibility.
showard06b82fc2009-06-30 01:59:42 +000044 """
45 return [column_info[0] for column_info in cursor.description]
46
47
showard8bfb5cb2009-10-07 20:49:15 +000048 def execute_group_query(self, query, group_by):
showard35444862008-08-07 22:35:30 +000049 """
showard8a6eb0c2008-10-01 11:38:59 +000050 Performs the given query grouped by the fields in group_by with the
showard8bfb5cb2009-10-07 20:49:15 +000051 given query's extra select fields added. Returns a list of dicts, where
52 each dict corresponds to single row and contains a key for each grouped
53 field as well as all of the extra select fields.
showard35444862008-08-07 22:35:30 +000054 """
showard8bfb5cb2009-10-07 20:49:15 +000055 sql, params = self._get_group_query_sql(query, group_by)
showard56e93772008-10-06 10:06:22 +000056 cursor = readonly_connection.connection().cursor()
showard8a6eb0c2008-10-01 11:38:59 +000057 cursor.execute(sql, params)
showard06b82fc2009-06-30 01:59:42 +000058 field_names = self._get_column_names(cursor)
showard8a6eb0c2008-10-01 11:38:59 +000059 row_dicts = [dict(zip(field_names, row)) for row in cursor.fetchall()]
60 return row_dicts
61
62
63 def get_count_sql(self, query):
64 """
65 Get the SQL to properly select a per-group count of unique matches for
showard7c199df2008-10-03 10:17:15 +000066 a grouped query. Returns a tuple (field alias, field SQL)
showard8a6eb0c2008-10-01 11:38:59 +000067 """
showarda5288b42009-07-28 20:06:08 +000068 if query.query.distinct:
showard7c199df2008-10-03 10:17:15 +000069 pk_field = self.get_key_on_this_table()
showard8a6eb0c2008-10-01 11:38:59 +000070 count_sql = 'COUNT(DISTINCT %s)' % pk_field
71 else:
72 count_sql = 'COUNT(1)'
showard7c199df2008-10-03 10:17:15 +000073 return self._GROUP_COUNT_NAME, count_sql
showard35444862008-08-07 22:35:30 +000074
75
76 def _get_num_groups_sql(self, query, group_by):
showardd2b0c882009-10-19 18:34:11 +000077 group_fields = self._get_field_names(group_by, query.query.extra_select)
showarda5288b42009-07-28 20:06:08 +000078 query = query.order_by() # this can mess up the query and isn't needed
79
80 sql, params = query.query.as_sql()
81 from_ = sql[sql.find(' FROM'):]
showardd2b0c882009-10-19 18:34:11 +000082 return ('SELECT DISTINCT %s %s' % (','.join(group_fields),
showarda5288b42009-07-28 20:06:08 +000083 from_),
showard35444862008-08-07 22:35:30 +000084 params)
85
86
showardd2b0c882009-10-19 18:34:11 +000087 def _cursor_rowcount(self, cursor):
88 """To be stubbed by tests"""
89 return cursor.rowcount
90
91
showard35444862008-08-07 22:35:30 +000092 def get_num_groups(self, query, group_by):
93 """
94 Returns the number of distinct groups for the given query grouped by the
95 fields in group_by.
96 """
97 sql, params = self._get_num_groups_sql(query, group_by)
showard56e93772008-10-06 10:06:22 +000098 cursor = readonly_connection.connection().cursor()
showard35444862008-08-07 22:35:30 +000099 cursor.execute(sql, params)
showardd2b0c882009-10-19 18:34:11 +0000100 return self._cursor_rowcount(cursor)
showard35444862008-08-07 22:35:30 +0000101
102
103class Machine(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000104 machine_idx = dbmodels.AutoField(primary_key=True)
showard356d2372009-11-10 01:29:53 +0000105 hostname = dbmodels.CharField(unique=True, max_length=255)
showarda5288b42009-07-28 20:06:08 +0000106 machine_group = dbmodels.CharField(blank=True, max_length=240)
107 owner = dbmodels.CharField(blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000108
109 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000110 db_table = 'tko_machines'
showard35444862008-08-07 22:35:30 +0000111
112
113class Kernel(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000114 kernel_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000115 kernel_hash = dbmodels.CharField(max_length=105, editable=False)
116 base = dbmodels.CharField(max_length=90)
117 printable = dbmodels.CharField(max_length=300)
showard35444862008-08-07 22:35:30 +0000118
119 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000120 db_table = 'tko_kernels'
showard35444862008-08-07 22:35:30 +0000121
122
123class Patch(dbmodels.Model):
124 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
showarda5288b42009-07-28 20:06:08 +0000125 name = dbmodels.CharField(blank=True, max_length=240)
126 url = dbmodels.CharField(blank=True, max_length=900)
127 the_hash = dbmodels.CharField(blank=True, max_length=105, db_column='hash')
showard35444862008-08-07 22:35:30 +0000128
129 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000130 db_table = 'tko_patches'
showard35444862008-08-07 22:35:30 +0000131
132
133class Status(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000134 status_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000135 word = dbmodels.CharField(max_length=30)
showard35444862008-08-07 22:35:30 +0000136
137 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000138 db_table = 'tko_status'
showard35444862008-08-07 22:35:30 +0000139
140
141class Job(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000142 job_idx = dbmodels.AutoField(primary_key=True)
showard356d2372009-11-10 01:29:53 +0000143 tag = dbmodels.CharField(unique=True, max_length=100)
showarda5288b42009-07-28 20:06:08 +0000144 label = dbmodels.CharField(max_length=300)
145 username = dbmodels.CharField(max_length=240)
showard35444862008-08-07 22:35:30 +0000146 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
147 queued_time = dbmodels.DateTimeField(null=True, blank=True)
148 started_time = dbmodels.DateTimeField(null=True, blank=True)
149 finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000150 afe_job_id = dbmodels.IntegerField(null=True, default=None)
showard35444862008-08-07 22:35:30 +0000151
152 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000153 db_table = 'tko_jobs'
showard35444862008-08-07 22:35:30 +0000154
155
showardf8b19042009-05-12 17:22:49 +0000156class Test(dbmodels.Model, model_logic.ModelExtensions,
157 model_logic.ModelWithAttributes):
158 test_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000159 job = dbmodels.ForeignKey(Job, db_column='job_idx')
showarda5288b42009-07-28 20:06:08 +0000160 test = dbmodels.CharField(max_length=90)
161 subdir = dbmodels.CharField(blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000162 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
163 status = dbmodels.ForeignKey(Status, db_column='status')
showarda5288b42009-07-28 20:06:08 +0000164 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000165 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
166 finished_time = dbmodels.DateTimeField(null=True, blank=True)
167 started_time = dbmodels.DateTimeField(null=True, blank=True)
168
showardf8b19042009-05-12 17:22:49 +0000169 objects = model_logic.ExtendedManager()
170
171 def _get_attribute_model_and_args(self, attribute):
172 return TestAttribute, dict(test=self, attribute=attribute,
173 user_created=True)
174
175
176 def set_attribute(self, attribute, value):
177 # ensure non-user-created attributes remain immutable
178 try:
179 TestAttribute.objects.get(test=self, attribute=attribute,
180 user_created=False)
181 raise ValueError('Attribute %s already exists for test %s and is '
182 'immutable' % (attribute, self.test_idx))
183 except TestAttribute.DoesNotExist:
184 super(Test, self).set_attribute(attribute, value)
185
186
showard35444862008-08-07 22:35:30 +0000187 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000188 db_table = 'tko_tests'
showard35444862008-08-07 22:35:30 +0000189
190
showarde732ee72008-09-23 19:15:43 +0000191class TestAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000192 test = dbmodels.ForeignKey(Test, db_column='test_idx')
showarda5288b42009-07-28 20:06:08 +0000193 attribute = dbmodels.CharField(max_length=90)
194 value = dbmodels.CharField(blank=True, max_length=300)
showardf8b19042009-05-12 17:22:49 +0000195 user_created = dbmodels.BooleanField(default=False)
196
197 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000198
199 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000200 db_table = 'tko_test_attributes'
showard35444862008-08-07 22:35:30 +0000201
202
jadmanski430dca92008-12-16 20:56:53 +0000203class IterationAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000204 # this isn't really a primary key, but it's necessary to appease Django
205 # and is harmless as long as we're careful
showarde732ee72008-09-23 19:15:43 +0000206 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000207 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000208 attribute = dbmodels.CharField(max_length=90)
209 value = dbmodels.CharField(blank=True, max_length=300)
showard35444862008-08-07 22:35:30 +0000210
showardf8b19042009-05-12 17:22:49 +0000211 objects = model_logic.ExtendedManager()
212
showard35444862008-08-07 22:35:30 +0000213 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000214 db_table = 'tko_iteration_attributes'
showard35444862008-08-07 22:35:30 +0000215
216
jadmanski430dca92008-12-16 20:56:53 +0000217class IterationResult(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000218 # see comment on IterationAttribute regarding primary_key=True
jadmanski430dca92008-12-16 20:56:53 +0000219 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000220 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000221 attribute = dbmodels.CharField(max_length=90)
222 value = dbmodels.DecimalField(null=True, max_digits=12, decimal_places=31,
223 blank=True)
showard35444862008-08-07 22:35:30 +0000224
showardf8b19042009-05-12 17:22:49 +0000225 objects = model_logic.ExtendedManager()
226
showard35444862008-08-07 22:35:30 +0000227 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000228 db_table = 'tko_iteration_result'
showard35444862008-08-07 22:35:30 +0000229
230
231class TestLabel(dbmodels.Model, model_logic.ModelExtensions):
showarda5288b42009-07-28 20:06:08 +0000232 name = dbmodels.CharField(max_length=80, unique=True)
showard35444862008-08-07 22:35:30 +0000233 description = dbmodels.TextField(blank=True)
showardeab66ce2009-12-23 00:03:56 +0000234 tests = dbmodels.ManyToManyField(Test, blank=True,
235 db_table='tko_test_labels_tests')
showard35444862008-08-07 22:35:30 +0000236
237 name_field = 'name'
showardf8b19042009-05-12 17:22:49 +0000238 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000239
240 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000241 db_table = 'tko_test_labels'
showard35444862008-08-07 22:35:30 +0000242
243
244class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):
245 # TODO: change this to foreign key once DBs are merged
showarda5288b42009-07-28 20:06:08 +0000246 owner = dbmodels.CharField(max_length=80)
247 name = dbmodels.CharField(max_length=100)
showard35444862008-08-07 22:35:30 +0000248 url_token = dbmodels.TextField()
249
250 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000251 db_table = 'tko_saved_queries'
showard35444862008-08-07 22:35:30 +0000252
253
showardce12f552008-09-19 00:48:59 +0000254class EmbeddedGraphingQuery(dbmodels.Model, model_logic.ModelExtensions):
255 url_token = dbmodels.TextField(null=False, blank=False)
showarda5288b42009-07-28 20:06:08 +0000256 graph_type = dbmodels.CharField(max_length=16, null=False, blank=False)
showardce12f552008-09-19 00:48:59 +0000257 params = dbmodels.TextField(null=False, blank=False)
258 last_updated = dbmodels.DateTimeField(null=False, blank=False,
259 editable=False)
260 # refresh_time shows the time at which a thread is updating the cached
261 # image, or NULL if no one is updating the image. This is used so that only
262 # one thread is updating the cached image at a time (see
263 # graphing_utils.handle_plot_request)
264 refresh_time = dbmodels.DateTimeField(editable=False)
265 cached_png = dbmodels.TextField(editable=False)
266
267 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000268 db_table = 'tko_embedded_graphing_queries'
showardce12f552008-09-19 00:48:59 +0000269
270
showard35444862008-08-07 22:35:30 +0000271# views
272
273class TestViewManager(TempManager):
showard35444862008-08-07 22:35:30 +0000274 def get_query_set(self):
275 query = super(TestViewManager, self).get_query_set()
showard5bf7c502008-08-20 01:22:22 +0000276
showard35444862008-08-07 22:35:30 +0000277 # add extra fields to selects, using the SQL itself as the "alias"
278 extra_select = dict((sql, sql)
279 for sql in self.model.extra_fields.iterkeys())
280 return query.extra(select=extra_select)
281
282
showardf2489522008-10-23 23:08:00 +0000283 def _get_include_exclude_suffix(self, exclude):
284 if exclude:
showard2aa318e2009-08-20 23:43:10 +0000285 return '_exclude'
286 return '_include'
287
288
289 def _add_attribute_join(self, query_set, join_condition,
290 suffix=None, exclude=False):
291 if suffix is None:
292 suffix = self._get_include_exclude_suffix(exclude)
showardeab66ce2009-12-23 00:03:56 +0000293 return self.add_join(query_set, 'tko_test_attributes',
294 join_key='test_idx',
showard2aa318e2009-08-20 23:43:10 +0000295 join_condition=join_condition,
296 suffix=suffix, exclude=exclude)
297
298
299 def _add_label_pivot_table_join(self, query_set, suffix, join_condition='',
300 exclude=False, force_left_join=False):
showardeab66ce2009-12-23 00:03:56 +0000301 return self.add_join(query_set, 'tko_test_labels_tests',
302 join_key='test_id',
showard2aa318e2009-08-20 23:43:10 +0000303 join_condition=join_condition,
304 suffix=suffix, exclude=exclude,
305 force_left_join=force_left_join)
showardf2489522008-10-23 23:08:00 +0000306
307
showard64aeecd2008-09-19 21:32:58 +0000308 def _add_label_joins(self, query_set, suffix=''):
showard2aa318e2009-08-20 23:43:10 +0000309 query_set = self._add_label_pivot_table_join(
310 query_set, suffix=suffix, force_left_join=True)
showardd50ffb42008-09-04 02:47:45 +0000311
showard2aa318e2009-08-20 23:43:10 +0000312 # since we're not joining from the original table, we can't use
313 # self.add_join() again
showardeab66ce2009-12-23 00:03:56 +0000314 second_join_alias = 'tko_test_labels' + suffix
showardd50ffb42008-09-04 02:47:45 +0000315 second_join_condition = ('%s.id = %s.testlabel_id' %
showard64aeecd2008-09-19 21:32:58 +0000316 (second_join_alias,
showardeab66ce2009-12-23 00:03:56 +0000317 'tko_test_labels_tests' + suffix))
showard64aeecd2008-09-19 21:32:58 +0000318 filter_object = self._CustomSqlQ()
showardeab66ce2009-12-23 00:03:56 +0000319 filter_object.add_join('tko_test_labels',
showardd50ffb42008-09-04 02:47:45 +0000320 second_join_condition,
showarda5288b42009-07-28 20:06:08 +0000321 query_set.query.LOUTER,
showardd50ffb42008-09-04 02:47:45 +0000322 alias=second_join_alias)
showarda5288b42009-07-28 20:06:08 +0000323 return self._add_customSqlQ(query_set, filter_object)
showardd50ffb42008-09-04 02:47:45 +0000324
showard64aeecd2008-09-19 21:32:58 +0000325
showardd50ffb42008-09-04 02:47:45 +0000326 def _get_label_ids_from_names(self, label_names):
showard2aa318e2009-08-20 23:43:10 +0000327 assert label_names
328 label_ids = list( # listifying avoids a double query below
329 TestLabel.objects.filter(name__in=label_names).values('id'))
330 if len(label_ids) < len(set(label_names)):
331 raise ValueError('Not all labels found: %s' %
332 ', '.join(label_names))
333 return [str(label['id']) for label in label_ids]
334
335
336 def _include_or_exclude_labels(self, query_set, label_names, exclude=False):
337 label_ids = self._get_label_ids_from_names(label_names)
338 suffix = self._get_include_exclude_suffix(exclude)
showardeab66ce2009-12-23 00:03:56 +0000339 condition = ('tko_test_labels_tests%s.testlabel_id IN (%s)' %
showard2aa318e2009-08-20 23:43:10 +0000340 (suffix, ','.join(label_ids)))
341 return self._add_label_pivot_table_join(query_set,
342 join_condition=condition,
343 suffix=suffix,
344 exclude=exclude)
showard02813502008-08-20 20:52:56 +0000345
346
showardf2489522008-10-23 23:08:00 +0000347 def get_query_set_with_joins(self, filter_data, include_host_labels=False):
showardfc8c6ae2008-11-11 19:06:01 +0000348 include_labels = filter_data.pop('include_labels', [])
showardd50ffb42008-09-04 02:47:45 +0000349 exclude_labels = filter_data.pop('exclude_labels', [])
showard35444862008-08-07 22:35:30 +0000350 query_set = self.get_query_set()
showardd50ffb42008-09-04 02:47:45 +0000351 joined = False
showard2aa318e2009-08-20 23:43:10 +0000352
353 # TODO: make this feature obsolete in favor of include_labels and
354 # exclude_labels
showardf2489522008-10-23 23:08:00 +0000355 extra_where = filter_data.get('extra_where', '')
showardeab66ce2009-12-23 00:03:56 +0000356 if 'tko_test_labels' in extra_where:
showard02813502008-08-20 20:52:56 +0000357 query_set = self._add_label_joins(query_set)
showardd50ffb42008-09-04 02:47:45 +0000358 joined = True
359
showard2aa318e2009-08-20 23:43:10 +0000360 if include_labels:
361 query_set = self._include_or_exclude_labels(query_set,
362 include_labels)
showardfc8c6ae2008-11-11 19:06:01 +0000363 joined = True
showard2aa318e2009-08-20 23:43:10 +0000364 if exclude_labels:
365 query_set = self._include_or_exclude_labels(query_set,
366 exclude_labels,
367 exclude=True)
showard64aeecd2008-09-19 21:32:58 +0000368 joined = True
369
370 include_attributes_where = filter_data.pop('include_attributes_where',
371 '')
372 exclude_attributes_where = filter_data.pop('exclude_attributes_where',
373 '')
374 if include_attributes_where:
375 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000376 query_set,
377 join_condition=self.escape_user_sql(include_attributes_where))
showard64aeecd2008-09-19 21:32:58 +0000378 joined = True
379 if exclude_attributes_where:
380 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000381 query_set,
382 join_condition=self.escape_user_sql(exclude_attributes_where),
showard64aeecd2008-09-19 21:32:58 +0000383 exclude=True)
384 joined = True
showardd50ffb42008-09-04 02:47:45 +0000385
showardeab66ce2009-12-23 00:03:56 +0000386 test_attributes = filter_data.pop('tko_test_attributes', [])
showard8bfb5cb2009-10-07 20:49:15 +0000387 for attribute in test_attributes:
388 query_set = self.join_attribute(query_set, attribute)
389 joined = True
390
showardd50ffb42008-09-04 02:47:45 +0000391 if not joined:
showard35444862008-08-07 22:35:30 +0000392 filter_data['no_distinct'] = True
showardd50ffb42008-09-04 02:47:45 +0000393
showard2aa318e2009-08-20 23:43:10 +0000394 # TODO: make test_attributes_host_labels obsolete too
showardeab66ce2009-12-23 00:03:56 +0000395 if (include_host_labels or
396 'tko_test_attributes_host_labels' in extra_where):
showardf2489522008-10-23 23:08:00 +0000397 query_set = self._add_attribute_join(
398 query_set, suffix='_host_labels',
showardeab66ce2009-12-23 00:03:56 +0000399 join_condition='tko_test_attributes_host_labels.attribute = '
showardf2489522008-10-23 23:08:00 +0000400 '"host-labels"')
401
showard35444862008-08-07 22:35:30 +0000402 return query_set
403
404
showard8bfb5cb2009-10-07 20:49:15 +0000405 def query_test_ids(self, filter_data, apply_presentation=True):
406 query = self.model.query_objects(filter_data,
407 apply_presentation=apply_presentation)
408 dicts = query.values('test_idx')
showard02813502008-08-20 20:52:56 +0000409 return [item['test_idx'] for item in dicts]
410
411
showard02813502008-08-20 20:52:56 +0000412 def query_test_label_ids(self, filter_data):
showardd50ffb42008-09-04 02:47:45 +0000413 query_set = self.model.query_objects(filter_data)
414 query_set = self._add_label_joins(query_set, suffix='_list')
showardeab66ce2009-12-23 00:03:56 +0000415 rows = self._custom_select_query(query_set, ['tko_test_labels_list.id'])
showardd50ffb42008-09-04 02:47:45 +0000416 return [row[0] for row in rows if row[0] is not None]
showard02813502008-08-20 20:52:56 +0000417
418
showardeaccf8f2009-04-16 03:11:33 +0000419 def escape_user_sql(self, sql):
420 sql = super(TestViewManager, self).escape_user_sql(sql)
421 return sql.replace('test_idx', self.get_key_on_this_table('test_idx'))
422
423
showardc4780402009-08-31 18:31:34 +0000424 def _join_one_iteration_key(self, query_set, result_key, index):
425 suffix = '_%s' % index
426 table_name = IterationResult._meta.db_table
427 alias = table_name + suffix
428 condition_parts = ["%s.attribute = '%s'" %
429 (alias, self.escape_user_sql(result_key))]
430 if index > 0:
431 # after the first join, we need to match up iteration indices,
432 # otherwise each join will expand the query by the number of
433 # iterations and we'll have extraneous rows
434 first_alias = table_name + '_0'
435 condition_parts.append('%s.iteration = %s.iteration' %
436 (alias, first_alias))
437
438 condition = ' and '.join(condition_parts)
439 # add a join to IterationResult
440 query_set = self.add_join(query_set, table_name, join_key='test_idx',
441 join_condition=condition, suffix=suffix)
442 # select the iteration value for this join
443 query_set = query_set.extra(select={result_key: '%s.value' % alias})
444 if index == 0:
445 # pull the iteration index from the first join
446 query_set = query_set.extra(
447 select={'iteration_index': '%s.iteration' % alias})
448
449 return query_set
450
451
452 def join_iterations(self, test_view_query_set, result_keys):
453 """
454 Join the given TestView QuerySet to IterationResult. The resulting
455 query looks like a TestView query but has one row per iteration. Each
456 row includes all the attributes of TestView, an attribute for each key
457 in result_keys and an iteration_index attribute.
458
459 We accomplish this by joining the TestView query to IterationResult
460 once per result key. Each join is restricted on the result key (and on
461 the test index, like all one-to-many joins). For the first join, this
462 is the only restriction, so each TestView row expands to a row per
463 iteration (per iteration that includes the key, of course). For each
464 subsequent join, we also restrict the iteration index to match that of
465 the initial join. This makes each subsequent join produce exactly one
466 result row for each input row. (This assumes each iteration contains
467 the same set of keys.)
468 """
469 query_set = test_view_query_set
470 for index, result_key in enumerate(result_keys):
471 query_set = self._join_one_iteration_key(query_set, result_key,
472 index)
473 return query_set
474
475
showard8bfb5cb2009-10-07 20:49:15 +0000476 def join_attribute(self, test_view_query_set, attribute):
477 """
478 Join the given TestView QuerySet to TestAttribute. The resulting query
479 has an additional column for the given attribute named
480 "attribute_<attribute name>".
481 """
482 table_name = TestAttribute._meta.db_table
483 suffix = '_' + attribute
484 alias = table_name + suffix
485 condition = "%s.attribute = '%s'" % (alias,
486 self.escape_user_sql(attribute))
487 query_set = self.add_join(test_view_query_set, table_name,
488 join_key='test_idx', join_condition=condition,
489 suffix=suffix, force_left_join=True)
490
491 select_name = 'attribute_' + attribute
492 query_set = query_set.extra(select={select_name: '%s.value' % alias})
493 return query_set
494
495
showard35444862008-08-07 22:35:30 +0000496class TestView(dbmodels.Model, model_logic.ModelExtensions):
497 extra_fields = {
showardf4c702e2009-07-08 21:14:27 +0000498 'DATE(job_queued_time)': 'job queued day',
499 'DATE(test_finished_time)': 'test finished day',
showard35444862008-08-07 22:35:30 +0000500 }
501
502 group_fields = [
showardf4c702e2009-07-08 21:14:27 +0000503 'test_name',
504 'status',
505 'kernel',
506 'hostname',
507 'job_tag',
508 'job_name',
509 'platform',
510 'reason',
511 'job_owner',
512 'job_queued_time',
513 'DATE(job_queued_time)',
514 'test_started_time',
515 'test_finished_time',
516 'DATE(test_finished_time)',
showard35444862008-08-07 22:35:30 +0000517 ]
518
519 test_idx = dbmodels.IntegerField('test index', primary_key=True)
520 job_idx = dbmodels.IntegerField('job index', null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000521 test_name = dbmodels.CharField(blank=True, max_length=90)
522 subdir = dbmodels.CharField('subdirectory', blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000523 kernel_idx = dbmodels.IntegerField('kernel index')
524 status_idx = dbmodels.IntegerField('status index')
showarda5288b42009-07-28 20:06:08 +0000525 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000526 machine_idx = dbmodels.IntegerField('host index')
527 test_started_time = dbmodels.DateTimeField(null=True, blank=True)
528 test_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000529 job_tag = dbmodels.CharField(blank=True, max_length=300)
530 job_name = dbmodels.CharField(blank=True, max_length=300)
531 job_owner = dbmodels.CharField('owner', blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000532 job_queued_time = dbmodels.DateTimeField(null=True, blank=True)
533 job_started_time = dbmodels.DateTimeField(null=True, blank=True)
534 job_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000535 afe_job_id = dbmodels.IntegerField(null=True)
showarda5288b42009-07-28 20:06:08 +0000536 hostname = dbmodels.CharField(blank=True, max_length=300)
537 platform = dbmodels.CharField(blank=True, max_length=240)
538 machine_owner = dbmodels.CharField(blank=True, max_length=240)
539 kernel_hash = dbmodels.CharField(blank=True, max_length=105)
540 kernel_base = dbmodels.CharField(blank=True, max_length=90)
541 kernel = dbmodels.CharField(blank=True, max_length=300)
542 status = dbmodels.CharField(blank=True, max_length=30)
showard35444862008-08-07 22:35:30 +0000543
544 objects = TestViewManager()
545
546 def save(self):
547 raise NotImplementedError('TestView is read-only')
548
549
550 def delete(self):
551 raise NotImplementedError('TestView is read-only')
552
553
554 @classmethod
showard8bfb5cb2009-10-07 20:49:15 +0000555 def query_objects(cls, filter_data, initial_query=None,
556 apply_presentation=True):
showard35444862008-08-07 22:35:30 +0000557 if initial_query is None:
showard64aeecd2008-09-19 21:32:58 +0000558 initial_query = cls.objects.get_query_set_with_joins(filter_data)
showard8bfb5cb2009-10-07 20:49:15 +0000559 return super(TestView, cls).query_objects(
560 filter_data, initial_query=initial_query,
561 apply_presentation=apply_presentation)
showard35444862008-08-07 22:35:30 +0000562
563
564 class Meta:
showardeab66ce2009-12-23 00:03:56 +0000565 db_table = 'tko_test_view_2'