blob: 2873b31e4e63677747391ed56c192f15863aae4e [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:
18 field_names.append(field)
19 else:
20 field_names.append(self._get_key_unless_is_function(field))
21 return field_names
showard35444862008-08-07 22:35:30 +000022
23
24 def _get_group_query_sql(self, query, group_by, extra_select_fields):
showardf2489522008-10-23 23:08:00 +000025 group_fields = self._get_field_names(group_by, extra_select_fields)
showard35444862008-08-07 22:35:30 +000026
showarda5288b42009-07-28 20:06:08 +000027 # Clone the queryset, so that we don't change the original
28 query = query.all()
showard35444862008-08-07 22:35:30 +000029
showarda5288b42009-07-28 20:06:08 +000030 # In order to use query.extra(), we need to first clear the limits
31 # and then add them back in after the extra
32 low = query.query.low_mark
33 high = query.query.high_mark
34 query.query.clear_limits()
35
36 select_fields = dict(
37 (field_name, self._get_key_unless_is_function(field_sql))
38 for field_name, field_sql in extra_select_fields.iteritems())
39 query = query.extra(select=select_fields)
40
41 query.query.set_limits(low=low, high=high)
42
43 sql, params = query.query.as_sql()
showardd50ffb42008-09-04 02:47:45 +000044
45 # insert GROUP BY clause into query
showard7c199df2008-10-03 10:17:15 +000046 group_by_clause = ' GROUP BY ' + ', '.join(group_fields)
showarda5288b42009-07-28 20:06:08 +000047 group_by_position = sql.rfind('ORDER BY')
showardd50ffb42008-09-04 02:47:45 +000048 if group_by_position == -1:
showarda5288b42009-07-28 20:06:08 +000049 group_by_position = len(sql)
50 sql = (sql[:group_by_position] +
51 group_by_clause + ' ' +
52 sql[group_by_position:])
showard35444862008-08-07 22:35:30 +000053
showarda5288b42009-07-28 20:06:08 +000054 return sql, params
showard35444862008-08-07 22:35:30 +000055
56
showard06b82fc2009-06-30 01:59:42 +000057 def _get_column_names(self, cursor):
58 """\
59 Gets the column names from the cursor description. This method exists
60 so that it can be mocked in the unit test for sqlite3 compatibility."
61 """
62 return [column_info[0] for column_info in cursor.description]
63
64
showard8a6eb0c2008-10-01 11:38:59 +000065 def execute_group_query(self, query, group_by, extra_select_fields=[]):
showard35444862008-08-07 22:35:30 +000066 """
showard8a6eb0c2008-10-01 11:38:59 +000067 Performs the given query grouped by the fields in group_by with the
showard7c199df2008-10-03 10:17:15 +000068 given extra select fields added. extra_select_fields should be a dict
69 mapping field alias to field SQL. Usually, the extra fields will use
showard8a6eb0c2008-10-01 11:38:59 +000070 group aggregation functions. Returns a list of dicts, where each dict
71 corresponds to single row and contains a key for each grouped field as
72 well as all of the extra select fields.
showard35444862008-08-07 22:35:30 +000073 """
74 sql, params = self._get_group_query_sql(query, group_by,
75 extra_select_fields)
showard56e93772008-10-06 10:06:22 +000076 cursor = readonly_connection.connection().cursor()
showard8a6eb0c2008-10-01 11:38:59 +000077 cursor.execute(sql, params)
showard06b82fc2009-06-30 01:59:42 +000078 field_names = self._get_column_names(cursor)
showard8a6eb0c2008-10-01 11:38:59 +000079 row_dicts = [dict(zip(field_names, row)) for row in cursor.fetchall()]
80 return row_dicts
81
82
83 def get_count_sql(self, query):
84 """
85 Get the SQL to properly select a per-group count of unique matches for
showard7c199df2008-10-03 10:17:15 +000086 a grouped query. Returns a tuple (field alias, field SQL)
showard8a6eb0c2008-10-01 11:38:59 +000087 """
showarda5288b42009-07-28 20:06:08 +000088 if query.query.distinct:
showard7c199df2008-10-03 10:17:15 +000089 pk_field = self.get_key_on_this_table()
showard8a6eb0c2008-10-01 11:38:59 +000090 count_sql = 'COUNT(DISTINCT %s)' % pk_field
91 else:
92 count_sql = 'COUNT(1)'
showard7c199df2008-10-03 10:17:15 +000093 return self._GROUP_COUNT_NAME, count_sql
showard35444862008-08-07 22:35:30 +000094
95
96 def _get_num_groups_sql(self, query, group_by):
97 group_fields = self._get_field_names(group_by)
showarda5288b42009-07-28 20:06:08 +000098 query = query.order_by() # this can mess up the query and isn't needed
99
100 sql, params = query.query.as_sql()
101 from_ = sql[sql.find(' FROM'):]
showard35444862008-08-07 22:35:30 +0000102 return ('SELECT COUNT(DISTINCT %s) %s' % (','.join(group_fields),
showarda5288b42009-07-28 20:06:08 +0000103 from_),
showard35444862008-08-07 22:35:30 +0000104 params)
105
106
107 def get_num_groups(self, query, group_by):
108 """
109 Returns the number of distinct groups for the given query grouped by the
110 fields in group_by.
111 """
112 sql, params = self._get_num_groups_sql(query, group_by)
showard56e93772008-10-06 10:06:22 +0000113 cursor = readonly_connection.connection().cursor()
showard35444862008-08-07 22:35:30 +0000114 cursor.execute(sql, params)
115 return cursor.fetchone()[0]
116
117
118class Machine(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000119 machine_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000120 hostname = dbmodels.CharField(unique=True, max_length=300)
121 machine_group = dbmodels.CharField(blank=True, max_length=240)
122 owner = dbmodels.CharField(blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000123
124 class Meta:
125 db_table = 'machines'
126
127
128class Kernel(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000129 kernel_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000130 kernel_hash = dbmodels.CharField(max_length=105, editable=False)
131 base = dbmodels.CharField(max_length=90)
132 printable = dbmodels.CharField(max_length=300)
showard35444862008-08-07 22:35:30 +0000133
134 class Meta:
135 db_table = 'kernels'
136
137
138class Patch(dbmodels.Model):
139 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
showarda5288b42009-07-28 20:06:08 +0000140 name = dbmodels.CharField(blank=True, max_length=240)
141 url = dbmodels.CharField(blank=True, max_length=900)
142 the_hash = dbmodels.CharField(blank=True, max_length=105, db_column='hash')
showard35444862008-08-07 22:35:30 +0000143
144 class Meta:
145 db_table = 'patches'
146
147
148class Status(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000149 status_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000150 word = dbmodels.CharField(max_length=30)
showard35444862008-08-07 22:35:30 +0000151
152 class Meta:
153 db_table = 'status'
154
155
156class Job(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000157 job_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000158 tag = dbmodels.CharField(unique=True, max_length=300)
159 label = dbmodels.CharField(max_length=300)
160 username = dbmodels.CharField(max_length=240)
showard35444862008-08-07 22:35:30 +0000161 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
162 queued_time = dbmodels.DateTimeField(null=True, blank=True)
163 started_time = dbmodels.DateTimeField(null=True, blank=True)
164 finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000165 afe_job_id = dbmodels.IntegerField(null=True, default=None)
showard35444862008-08-07 22:35:30 +0000166
167 class Meta:
168 db_table = 'jobs'
169
170
showardf8b19042009-05-12 17:22:49 +0000171class Test(dbmodels.Model, model_logic.ModelExtensions,
172 model_logic.ModelWithAttributes):
173 test_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000174 job = dbmodels.ForeignKey(Job, db_column='job_idx')
showarda5288b42009-07-28 20:06:08 +0000175 test = dbmodels.CharField(max_length=90)
176 subdir = dbmodels.CharField(blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000177 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
178 status = dbmodels.ForeignKey(Status, db_column='status')
showarda5288b42009-07-28 20:06:08 +0000179 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000180 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
181 finished_time = dbmodels.DateTimeField(null=True, blank=True)
182 started_time = dbmodels.DateTimeField(null=True, blank=True)
183
showardf8b19042009-05-12 17:22:49 +0000184 objects = model_logic.ExtendedManager()
185
186 def _get_attribute_model_and_args(self, attribute):
187 return TestAttribute, dict(test=self, attribute=attribute,
188 user_created=True)
189
190
191 def set_attribute(self, attribute, value):
192 # ensure non-user-created attributes remain immutable
193 try:
194 TestAttribute.objects.get(test=self, attribute=attribute,
195 user_created=False)
196 raise ValueError('Attribute %s already exists for test %s and is '
197 'immutable' % (attribute, self.test_idx))
198 except TestAttribute.DoesNotExist:
199 super(Test, self).set_attribute(attribute, value)
200
201
showard35444862008-08-07 22:35:30 +0000202 class Meta:
203 db_table = 'tests'
204
205
showarde732ee72008-09-23 19:15:43 +0000206class TestAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000207 test = dbmodels.ForeignKey(Test, db_column='test_idx')
showarda5288b42009-07-28 20:06:08 +0000208 attribute = dbmodels.CharField(max_length=90)
209 value = dbmodels.CharField(blank=True, max_length=300)
showardf8b19042009-05-12 17:22:49 +0000210 user_created = dbmodels.BooleanField(default=False)
211
212 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000213
214 class Meta:
215 db_table = 'test_attributes'
216
217
jadmanski430dca92008-12-16 20:56:53 +0000218class IterationAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000219 # this isn't really a primary key, but it's necessary to appease Django
220 # and is harmless as long as we're careful
showarde732ee72008-09-23 19:15:43 +0000221 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000222 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000223 attribute = dbmodels.CharField(max_length=90)
224 value = dbmodels.CharField(blank=True, max_length=300)
showard35444862008-08-07 22:35:30 +0000225
showardf8b19042009-05-12 17:22:49 +0000226 objects = model_logic.ExtendedManager()
227
showard35444862008-08-07 22:35:30 +0000228 class Meta:
229 db_table = 'iteration_attributes'
230
231
jadmanski430dca92008-12-16 20:56:53 +0000232class IterationResult(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000233 # see comment on IterationAttribute regarding primary_key=True
jadmanski430dca92008-12-16 20:56:53 +0000234 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000235 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000236 attribute = dbmodels.CharField(max_length=90)
237 value = dbmodels.DecimalField(null=True, max_digits=12, decimal_places=31,
238 blank=True)
showard35444862008-08-07 22:35:30 +0000239
showardf8b19042009-05-12 17:22:49 +0000240 objects = model_logic.ExtendedManager()
241
showard35444862008-08-07 22:35:30 +0000242 class Meta:
243 db_table = 'iteration_result'
244
245
246class TestLabel(dbmodels.Model, model_logic.ModelExtensions):
showarda5288b42009-07-28 20:06:08 +0000247 name = dbmodels.CharField(max_length=80, unique=True)
showard35444862008-08-07 22:35:30 +0000248 description = dbmodels.TextField(blank=True)
showarda5288b42009-07-28 20:06:08 +0000249 tests = dbmodels.ManyToManyField(Test, blank=True)
showard35444862008-08-07 22:35:30 +0000250
251 name_field = 'name'
showardf8b19042009-05-12 17:22:49 +0000252 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000253
254 class Meta:
255 db_table = 'test_labels'
256
257
258class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):
259 # TODO: change this to foreign key once DBs are merged
showarda5288b42009-07-28 20:06:08 +0000260 owner = dbmodels.CharField(max_length=80)
261 name = dbmodels.CharField(max_length=100)
showard35444862008-08-07 22:35:30 +0000262 url_token = dbmodels.TextField()
263
264 class Meta:
265 db_table = 'saved_queries'
266
267
showardce12f552008-09-19 00:48:59 +0000268class EmbeddedGraphingQuery(dbmodels.Model, model_logic.ModelExtensions):
269 url_token = dbmodels.TextField(null=False, blank=False)
showarda5288b42009-07-28 20:06:08 +0000270 graph_type = dbmodels.CharField(max_length=16, null=False, blank=False)
showardce12f552008-09-19 00:48:59 +0000271 params = dbmodels.TextField(null=False, blank=False)
272 last_updated = dbmodels.DateTimeField(null=False, blank=False,
273 editable=False)
274 # refresh_time shows the time at which a thread is updating the cached
275 # image, or NULL if no one is updating the image. This is used so that only
276 # one thread is updating the cached image at a time (see
277 # graphing_utils.handle_plot_request)
278 refresh_time = dbmodels.DateTimeField(editable=False)
279 cached_png = dbmodels.TextField(editable=False)
280
281 class Meta:
282 db_table = 'embedded_graphing_queries'
283
284
showard35444862008-08-07 22:35:30 +0000285# views
286
287class TestViewManager(TempManager):
showard35444862008-08-07 22:35:30 +0000288 def get_query_set(self):
289 query = super(TestViewManager, self).get_query_set()
showard5bf7c502008-08-20 01:22:22 +0000290
showard35444862008-08-07 22:35:30 +0000291 # add extra fields to selects, using the SQL itself as the "alias"
292 extra_select = dict((sql, sql)
293 for sql in self.model.extra_fields.iterkeys())
294 return query.extra(select=extra_select)
295
296
showardf2489522008-10-23 23:08:00 +0000297 def _get_include_exclude_suffix(self, exclude):
298 if exclude:
showard2aa318e2009-08-20 23:43:10 +0000299 return '_exclude'
300 return '_include'
301
302
303 def _add_attribute_join(self, query_set, join_condition,
304 suffix=None, exclude=False):
305 if suffix is None:
306 suffix = self._get_include_exclude_suffix(exclude)
307 return self.add_join(query_set, 'test_attributes', join_key='test_idx',
308 join_condition=join_condition,
309 suffix=suffix, exclude=exclude)
310
311
312 def _add_label_pivot_table_join(self, query_set, suffix, join_condition='',
313 exclude=False, force_left_join=False):
314 return self.add_join(query_set, 'test_labels_tests', join_key='test_id',
315 join_condition=join_condition,
316 suffix=suffix, exclude=exclude,
317 force_left_join=force_left_join)
showardf2489522008-10-23 23:08:00 +0000318
319
showard64aeecd2008-09-19 21:32:58 +0000320 def _add_label_joins(self, query_set, suffix=''):
showard2aa318e2009-08-20 23:43:10 +0000321 query_set = self._add_label_pivot_table_join(
322 query_set, suffix=suffix, force_left_join=True)
showardd50ffb42008-09-04 02:47:45 +0000323
showard2aa318e2009-08-20 23:43:10 +0000324 # since we're not joining from the original table, we can't use
325 # self.add_join() again
showardd50ffb42008-09-04 02:47:45 +0000326 second_join_alias = 'test_labels' + suffix
327 second_join_condition = ('%s.id = %s.testlabel_id' %
showard64aeecd2008-09-19 21:32:58 +0000328 (second_join_alias,
329 'test_labels_tests' + suffix))
330 filter_object = self._CustomSqlQ()
showardd50ffb42008-09-04 02:47:45 +0000331 filter_object.add_join('test_labels',
332 second_join_condition,
showarda5288b42009-07-28 20:06:08 +0000333 query_set.query.LOUTER,
showardd50ffb42008-09-04 02:47:45 +0000334 alias=second_join_alias)
showarda5288b42009-07-28 20:06:08 +0000335 return self._add_customSqlQ(query_set, filter_object)
showardd50ffb42008-09-04 02:47:45 +0000336
showard64aeecd2008-09-19 21:32:58 +0000337
showardd50ffb42008-09-04 02:47:45 +0000338 def _get_label_ids_from_names(self, label_names):
showard2aa318e2009-08-20 23:43:10 +0000339 assert label_names
340 label_ids = list( # listifying avoids a double query below
341 TestLabel.objects.filter(name__in=label_names).values('id'))
342 if len(label_ids) < len(set(label_names)):
343 raise ValueError('Not all labels found: %s' %
344 ', '.join(label_names))
345 return [str(label['id']) for label in label_ids]
346
347
348 def _include_or_exclude_labels(self, query_set, label_names, exclude=False):
349 label_ids = self._get_label_ids_from_names(label_names)
350 suffix = self._get_include_exclude_suffix(exclude)
351 condition = ('test_labels_tests%s.testlabel_id IN (%s)' %
352 (suffix, ','.join(label_ids)))
353 return self._add_label_pivot_table_join(query_set,
354 join_condition=condition,
355 suffix=suffix,
356 exclude=exclude)
showard02813502008-08-20 20:52:56 +0000357
358
showardf2489522008-10-23 23:08:00 +0000359 def get_query_set_with_joins(self, filter_data, include_host_labels=False):
showardfc8c6ae2008-11-11 19:06:01 +0000360 include_labels = filter_data.pop('include_labels', [])
showardd50ffb42008-09-04 02:47:45 +0000361 exclude_labels = filter_data.pop('exclude_labels', [])
showard35444862008-08-07 22:35:30 +0000362 query_set = self.get_query_set()
showardd50ffb42008-09-04 02:47:45 +0000363 joined = False
showard2aa318e2009-08-20 23:43:10 +0000364
365 # TODO: make this feature obsolete in favor of include_labels and
366 # exclude_labels
showardf2489522008-10-23 23:08:00 +0000367 extra_where = filter_data.get('extra_where', '')
368 if 'test_labels' in extra_where:
showard02813502008-08-20 20:52:56 +0000369 query_set = self._add_label_joins(query_set)
showardd50ffb42008-09-04 02:47:45 +0000370 joined = True
371
showard2aa318e2009-08-20 23:43:10 +0000372 if include_labels:
373 query_set = self._include_or_exclude_labels(query_set,
374 include_labels)
showardfc8c6ae2008-11-11 19:06:01 +0000375 joined = True
showard2aa318e2009-08-20 23:43:10 +0000376 if exclude_labels:
377 query_set = self._include_or_exclude_labels(query_set,
378 exclude_labels,
379 exclude=True)
showard64aeecd2008-09-19 21:32:58 +0000380 joined = True
381
382 include_attributes_where = filter_data.pop('include_attributes_where',
383 '')
384 exclude_attributes_where = filter_data.pop('exclude_attributes_where',
385 '')
386 if include_attributes_where:
387 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000388 query_set,
389 join_condition=self.escape_user_sql(include_attributes_where))
showard64aeecd2008-09-19 21:32:58 +0000390 joined = True
391 if exclude_attributes_where:
392 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000393 query_set,
394 join_condition=self.escape_user_sql(exclude_attributes_where),
showard64aeecd2008-09-19 21:32:58 +0000395 exclude=True)
396 joined = True
showardd50ffb42008-09-04 02:47:45 +0000397
398 if not joined:
showard35444862008-08-07 22:35:30 +0000399 filter_data['no_distinct'] = True
showardd50ffb42008-09-04 02:47:45 +0000400
showard2aa318e2009-08-20 23:43:10 +0000401 # TODO: make test_attributes_host_labels obsolete too
showardf2489522008-10-23 23:08:00 +0000402 if include_host_labels or 'test_attributes_host_labels' in extra_where:
403 query_set = self._add_attribute_join(
404 query_set, suffix='_host_labels',
405 join_condition='test_attributes_host_labels.attribute = '
406 '"host-labels"')
407
showard35444862008-08-07 22:35:30 +0000408 return query_set
409
410
showard02813502008-08-20 20:52:56 +0000411 def query_test_ids(self, filter_data):
412 dicts = self.model.query_objects(filter_data).values('test_idx')
413 return [item['test_idx'] for item in dicts]
414
415
showard02813502008-08-20 20:52:56 +0000416 def query_test_label_ids(self, filter_data):
showardd50ffb42008-09-04 02:47:45 +0000417 query_set = self.model.query_objects(filter_data)
418 query_set = self._add_label_joins(query_set, suffix='_list')
419 rows = self._custom_select_query(query_set, ['test_labels_list.id'])
420 return [row[0] for row in rows if row[0] is not None]
showard02813502008-08-20 20:52:56 +0000421
422
showardeaccf8f2009-04-16 03:11:33 +0000423 def escape_user_sql(self, sql):
424 sql = super(TestViewManager, self).escape_user_sql(sql)
425 return sql.replace('test_idx', self.get_key_on_this_table('test_idx'))
426
427
showardc4780402009-08-31 18:31:34 +0000428 def _join_one_iteration_key(self, query_set, result_key, index):
429 suffix = '_%s' % index
430 table_name = IterationResult._meta.db_table
431 alias = table_name + suffix
432 condition_parts = ["%s.attribute = '%s'" %
433 (alias, self.escape_user_sql(result_key))]
434 if index > 0:
435 # after the first join, we need to match up iteration indices,
436 # otherwise each join will expand the query by the number of
437 # iterations and we'll have extraneous rows
438 first_alias = table_name + '_0'
439 condition_parts.append('%s.iteration = %s.iteration' %
440 (alias, first_alias))
441
442 condition = ' and '.join(condition_parts)
443 # add a join to IterationResult
444 query_set = self.add_join(query_set, table_name, join_key='test_idx',
445 join_condition=condition, suffix=suffix)
446 # select the iteration value for this join
447 query_set = query_set.extra(select={result_key: '%s.value' % alias})
448 if index == 0:
449 # pull the iteration index from the first join
450 query_set = query_set.extra(
451 select={'iteration_index': '%s.iteration' % alias})
452
453 return query_set
454
455
456 def join_iterations(self, test_view_query_set, result_keys):
457 """
458 Join the given TestView QuerySet to IterationResult. The resulting
459 query looks like a TestView query but has one row per iteration. Each
460 row includes all the attributes of TestView, an attribute for each key
461 in result_keys and an iteration_index attribute.
462
463 We accomplish this by joining the TestView query to IterationResult
464 once per result key. Each join is restricted on the result key (and on
465 the test index, like all one-to-many joins). For the first join, this
466 is the only restriction, so each TestView row expands to a row per
467 iteration (per iteration that includes the key, of course). For each
468 subsequent join, we also restrict the iteration index to match that of
469 the initial join. This makes each subsequent join produce exactly one
470 result row for each input row. (This assumes each iteration contains
471 the same set of keys.)
472 """
473 query_set = test_view_query_set
474 for index, result_key in enumerate(result_keys):
475 query_set = self._join_one_iteration_key(query_set, result_key,
476 index)
477 return query_set
478
479
showard35444862008-08-07 22:35:30 +0000480class TestView(dbmodels.Model, model_logic.ModelExtensions):
481 extra_fields = {
showardf4c702e2009-07-08 21:14:27 +0000482 'DATE(job_queued_time)': 'job queued day',
483 'DATE(test_finished_time)': 'test finished day',
showard35444862008-08-07 22:35:30 +0000484 }
485
486 group_fields = [
showardf4c702e2009-07-08 21:14:27 +0000487 'test_name',
488 'status',
489 'kernel',
490 'hostname',
491 'job_tag',
492 'job_name',
493 'platform',
494 'reason',
495 'job_owner',
496 'job_queued_time',
497 'DATE(job_queued_time)',
498 'test_started_time',
499 'test_finished_time',
500 'DATE(test_finished_time)',
showard35444862008-08-07 22:35:30 +0000501 ]
502
503 test_idx = dbmodels.IntegerField('test index', primary_key=True)
504 job_idx = dbmodels.IntegerField('job index', null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000505 test_name = dbmodels.CharField(blank=True, max_length=90)
506 subdir = dbmodels.CharField('subdirectory', blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000507 kernel_idx = dbmodels.IntegerField('kernel index')
508 status_idx = dbmodels.IntegerField('status index')
showarda5288b42009-07-28 20:06:08 +0000509 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000510 machine_idx = dbmodels.IntegerField('host index')
511 test_started_time = dbmodels.DateTimeField(null=True, blank=True)
512 test_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000513 job_tag = dbmodels.CharField(blank=True, max_length=300)
514 job_name = dbmodels.CharField(blank=True, max_length=300)
515 job_owner = dbmodels.CharField('owner', blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000516 job_queued_time = dbmodels.DateTimeField(null=True, blank=True)
517 job_started_time = dbmodels.DateTimeField(null=True, blank=True)
518 job_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000519 afe_job_id = dbmodels.IntegerField(null=True)
showarda5288b42009-07-28 20:06:08 +0000520 hostname = dbmodels.CharField(blank=True, max_length=300)
521 platform = dbmodels.CharField(blank=True, max_length=240)
522 machine_owner = dbmodels.CharField(blank=True, max_length=240)
523 kernel_hash = dbmodels.CharField(blank=True, max_length=105)
524 kernel_base = dbmodels.CharField(blank=True, max_length=90)
525 kernel = dbmodels.CharField(blank=True, max_length=300)
526 status = dbmodels.CharField(blank=True, max_length=30)
showard35444862008-08-07 22:35:30 +0000527
528 objects = TestViewManager()
529
530 def save(self):
531 raise NotImplementedError('TestView is read-only')
532
533
534 def delete(self):
535 raise NotImplementedError('TestView is read-only')
536
537
538 @classmethod
539 def query_objects(cls, filter_data, initial_query=None):
540 if initial_query is None:
showard64aeecd2008-09-19 21:32:58 +0000541 initial_query = cls.objects.get_query_set_with_joins(filter_data)
showard35444862008-08-07 22:35:30 +0000542 return super(TestView, cls).query_objects(filter_data,
543 initial_query=initial_query)
544
545
546 @classmethod
showarde732ee72008-09-23 19:15:43 +0000547 def list_objects(cls, filter_data, initial_query=None, fields=None):
548 # include extra fields
549 if fields is None:
550 fields = cls.get_field_dict().keys() + cls.extra_fields.keys()
551 return super(TestView, cls).list_objects(filter_data, initial_query,
552 fields)
showard35444862008-08-07 22:35:30 +0000553
554
555 class Meta:
556 db_table = 'test_view_2'