blob: 8d0cfb12751cd1666076d11226d0c46deead36f4 [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
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):
77 group_fields = self._get_field_names(group_by)
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'):]
showard35444862008-08-07 22:35:30 +000082 return ('SELECT COUNT(DISTINCT %s) %s' % (','.join(group_fields),
showarda5288b42009-07-28 20:06:08 +000083 from_),
showard35444862008-08-07 22:35:30 +000084 params)
85
86
87 def get_num_groups(self, query, group_by):
88 """
89 Returns the number of distinct groups for the given query grouped by the
90 fields in group_by.
91 """
92 sql, params = self._get_num_groups_sql(query, group_by)
showard56e93772008-10-06 10:06:22 +000093 cursor = readonly_connection.connection().cursor()
showard35444862008-08-07 22:35:30 +000094 cursor.execute(sql, params)
95 return cursor.fetchone()[0]
96
97
98class Machine(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +000099 machine_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000100 hostname = dbmodels.CharField(unique=True, max_length=300)
101 machine_group = dbmodels.CharField(blank=True, max_length=240)
102 owner = dbmodels.CharField(blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000103
104 class Meta:
105 db_table = 'machines'
106
107
108class Kernel(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000109 kernel_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000110 kernel_hash = dbmodels.CharField(max_length=105, editable=False)
111 base = dbmodels.CharField(max_length=90)
112 printable = dbmodels.CharField(max_length=300)
showard35444862008-08-07 22:35:30 +0000113
114 class Meta:
115 db_table = 'kernels'
116
117
118class Patch(dbmodels.Model):
119 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
showarda5288b42009-07-28 20:06:08 +0000120 name = dbmodels.CharField(blank=True, max_length=240)
121 url = dbmodels.CharField(blank=True, max_length=900)
122 the_hash = dbmodels.CharField(blank=True, max_length=105, db_column='hash')
showard35444862008-08-07 22:35:30 +0000123
124 class Meta:
125 db_table = 'patches'
126
127
128class Status(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000129 status_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000130 word = dbmodels.CharField(max_length=30)
showard35444862008-08-07 22:35:30 +0000131
132 class Meta:
133 db_table = 'status'
134
135
136class Job(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000137 job_idx = dbmodels.AutoField(primary_key=True)
showarda5288b42009-07-28 20:06:08 +0000138 tag = dbmodels.CharField(unique=True, max_length=300)
139 label = dbmodels.CharField(max_length=300)
140 username = dbmodels.CharField(max_length=240)
showard35444862008-08-07 22:35:30 +0000141 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
142 queued_time = dbmodels.DateTimeField(null=True, blank=True)
143 started_time = dbmodels.DateTimeField(null=True, blank=True)
144 finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000145 afe_job_id = dbmodels.IntegerField(null=True, default=None)
showard35444862008-08-07 22:35:30 +0000146
147 class Meta:
148 db_table = 'jobs'
149
150
showardf8b19042009-05-12 17:22:49 +0000151class Test(dbmodels.Model, model_logic.ModelExtensions,
152 model_logic.ModelWithAttributes):
153 test_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000154 job = dbmodels.ForeignKey(Job, db_column='job_idx')
showarda5288b42009-07-28 20:06:08 +0000155 test = dbmodels.CharField(max_length=90)
156 subdir = dbmodels.CharField(blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000157 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
158 status = dbmodels.ForeignKey(Status, db_column='status')
showarda5288b42009-07-28 20:06:08 +0000159 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000160 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
161 finished_time = dbmodels.DateTimeField(null=True, blank=True)
162 started_time = dbmodels.DateTimeField(null=True, blank=True)
163
showardf8b19042009-05-12 17:22:49 +0000164 objects = model_logic.ExtendedManager()
165
166 def _get_attribute_model_and_args(self, attribute):
167 return TestAttribute, dict(test=self, attribute=attribute,
168 user_created=True)
169
170
171 def set_attribute(self, attribute, value):
172 # ensure non-user-created attributes remain immutable
173 try:
174 TestAttribute.objects.get(test=self, attribute=attribute,
175 user_created=False)
176 raise ValueError('Attribute %s already exists for test %s and is '
177 'immutable' % (attribute, self.test_idx))
178 except TestAttribute.DoesNotExist:
179 super(Test, self).set_attribute(attribute, value)
180
181
showard35444862008-08-07 22:35:30 +0000182 class Meta:
183 db_table = 'tests'
184
185
showarde732ee72008-09-23 19:15:43 +0000186class TestAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000187 test = dbmodels.ForeignKey(Test, db_column='test_idx')
showarda5288b42009-07-28 20:06:08 +0000188 attribute = dbmodels.CharField(max_length=90)
189 value = dbmodels.CharField(blank=True, max_length=300)
showardf8b19042009-05-12 17:22:49 +0000190 user_created = dbmodels.BooleanField(default=False)
191
192 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000193
194 class Meta:
195 db_table = 'test_attributes'
196
197
jadmanski430dca92008-12-16 20:56:53 +0000198class IterationAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000199 # this isn't really a primary key, but it's necessary to appease Django
200 # and is harmless as long as we're careful
showarde732ee72008-09-23 19:15:43 +0000201 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000202 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000203 attribute = dbmodels.CharField(max_length=90)
204 value = dbmodels.CharField(blank=True, max_length=300)
showard35444862008-08-07 22:35:30 +0000205
showardf8b19042009-05-12 17:22:49 +0000206 objects = model_logic.ExtendedManager()
207
showard35444862008-08-07 22:35:30 +0000208 class Meta:
209 db_table = 'iteration_attributes'
210
211
jadmanski430dca92008-12-16 20:56:53 +0000212class IterationResult(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000213 # see comment on IterationAttribute regarding primary_key=True
jadmanski430dca92008-12-16 20:56:53 +0000214 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000215 iteration = dbmodels.IntegerField()
showarda5288b42009-07-28 20:06:08 +0000216 attribute = dbmodels.CharField(max_length=90)
217 value = dbmodels.DecimalField(null=True, max_digits=12, decimal_places=31,
218 blank=True)
showard35444862008-08-07 22:35:30 +0000219
showardf8b19042009-05-12 17:22:49 +0000220 objects = model_logic.ExtendedManager()
221
showard35444862008-08-07 22:35:30 +0000222 class Meta:
223 db_table = 'iteration_result'
224
225
226class TestLabel(dbmodels.Model, model_logic.ModelExtensions):
showarda5288b42009-07-28 20:06:08 +0000227 name = dbmodels.CharField(max_length=80, unique=True)
showard35444862008-08-07 22:35:30 +0000228 description = dbmodels.TextField(blank=True)
showarda5288b42009-07-28 20:06:08 +0000229 tests = dbmodels.ManyToManyField(Test, blank=True)
showard35444862008-08-07 22:35:30 +0000230
231 name_field = 'name'
showardf8b19042009-05-12 17:22:49 +0000232 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000233
234 class Meta:
235 db_table = 'test_labels'
236
237
238class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):
239 # TODO: change this to foreign key once DBs are merged
showarda5288b42009-07-28 20:06:08 +0000240 owner = dbmodels.CharField(max_length=80)
241 name = dbmodels.CharField(max_length=100)
showard35444862008-08-07 22:35:30 +0000242 url_token = dbmodels.TextField()
243
244 class Meta:
245 db_table = 'saved_queries'
246
247
showardce12f552008-09-19 00:48:59 +0000248class EmbeddedGraphingQuery(dbmodels.Model, model_logic.ModelExtensions):
249 url_token = dbmodels.TextField(null=False, blank=False)
showarda5288b42009-07-28 20:06:08 +0000250 graph_type = dbmodels.CharField(max_length=16, null=False, blank=False)
showardce12f552008-09-19 00:48:59 +0000251 params = dbmodels.TextField(null=False, blank=False)
252 last_updated = dbmodels.DateTimeField(null=False, blank=False,
253 editable=False)
254 # refresh_time shows the time at which a thread is updating the cached
255 # image, or NULL if no one is updating the image. This is used so that only
256 # one thread is updating the cached image at a time (see
257 # graphing_utils.handle_plot_request)
258 refresh_time = dbmodels.DateTimeField(editable=False)
259 cached_png = dbmodels.TextField(editable=False)
260
261 class Meta:
262 db_table = 'embedded_graphing_queries'
263
264
showard35444862008-08-07 22:35:30 +0000265# views
266
267class TestViewManager(TempManager):
showard35444862008-08-07 22:35:30 +0000268 def get_query_set(self):
269 query = super(TestViewManager, self).get_query_set()
showard5bf7c502008-08-20 01:22:22 +0000270
showard35444862008-08-07 22:35:30 +0000271 # add extra fields to selects, using the SQL itself as the "alias"
272 extra_select = dict((sql, sql)
273 for sql in self.model.extra_fields.iterkeys())
274 return query.extra(select=extra_select)
275
276
showardf2489522008-10-23 23:08:00 +0000277 def _get_include_exclude_suffix(self, exclude):
278 if exclude:
showard2aa318e2009-08-20 23:43:10 +0000279 return '_exclude'
280 return '_include'
281
282
283 def _add_attribute_join(self, query_set, join_condition,
284 suffix=None, exclude=False):
285 if suffix is None:
286 suffix = self._get_include_exclude_suffix(exclude)
287 return self.add_join(query_set, 'test_attributes', join_key='test_idx',
288 join_condition=join_condition,
289 suffix=suffix, exclude=exclude)
290
291
292 def _add_label_pivot_table_join(self, query_set, suffix, join_condition='',
293 exclude=False, force_left_join=False):
294 return self.add_join(query_set, 'test_labels_tests', join_key='test_id',
295 join_condition=join_condition,
296 suffix=suffix, exclude=exclude,
297 force_left_join=force_left_join)
showardf2489522008-10-23 23:08:00 +0000298
299
showard64aeecd2008-09-19 21:32:58 +0000300 def _add_label_joins(self, query_set, suffix=''):
showard2aa318e2009-08-20 23:43:10 +0000301 query_set = self._add_label_pivot_table_join(
302 query_set, suffix=suffix, force_left_join=True)
showardd50ffb42008-09-04 02:47:45 +0000303
showard2aa318e2009-08-20 23:43:10 +0000304 # since we're not joining from the original table, we can't use
305 # self.add_join() again
showardd50ffb42008-09-04 02:47:45 +0000306 second_join_alias = 'test_labels' + suffix
307 second_join_condition = ('%s.id = %s.testlabel_id' %
showard64aeecd2008-09-19 21:32:58 +0000308 (second_join_alias,
309 'test_labels_tests' + suffix))
310 filter_object = self._CustomSqlQ()
showardd50ffb42008-09-04 02:47:45 +0000311 filter_object.add_join('test_labels',
312 second_join_condition,
showarda5288b42009-07-28 20:06:08 +0000313 query_set.query.LOUTER,
showardd50ffb42008-09-04 02:47:45 +0000314 alias=second_join_alias)
showarda5288b42009-07-28 20:06:08 +0000315 return self._add_customSqlQ(query_set, filter_object)
showardd50ffb42008-09-04 02:47:45 +0000316
showard64aeecd2008-09-19 21:32:58 +0000317
showardd50ffb42008-09-04 02:47:45 +0000318 def _get_label_ids_from_names(self, label_names):
showard2aa318e2009-08-20 23:43:10 +0000319 assert label_names
320 label_ids = list( # listifying avoids a double query below
321 TestLabel.objects.filter(name__in=label_names).values('id'))
322 if len(label_ids) < len(set(label_names)):
323 raise ValueError('Not all labels found: %s' %
324 ', '.join(label_names))
325 return [str(label['id']) for label in label_ids]
326
327
328 def _include_or_exclude_labels(self, query_set, label_names, exclude=False):
329 label_ids = self._get_label_ids_from_names(label_names)
330 suffix = self._get_include_exclude_suffix(exclude)
331 condition = ('test_labels_tests%s.testlabel_id IN (%s)' %
332 (suffix, ','.join(label_ids)))
333 return self._add_label_pivot_table_join(query_set,
334 join_condition=condition,
335 suffix=suffix,
336 exclude=exclude)
showard02813502008-08-20 20:52:56 +0000337
338
showardf2489522008-10-23 23:08:00 +0000339 def get_query_set_with_joins(self, filter_data, include_host_labels=False):
showardfc8c6ae2008-11-11 19:06:01 +0000340 include_labels = filter_data.pop('include_labels', [])
showardd50ffb42008-09-04 02:47:45 +0000341 exclude_labels = filter_data.pop('exclude_labels', [])
showard35444862008-08-07 22:35:30 +0000342 query_set = self.get_query_set()
showardd50ffb42008-09-04 02:47:45 +0000343 joined = False
showard2aa318e2009-08-20 23:43:10 +0000344
345 # TODO: make this feature obsolete in favor of include_labels and
346 # exclude_labels
showardf2489522008-10-23 23:08:00 +0000347 extra_where = filter_data.get('extra_where', '')
348 if 'test_labels' in extra_where:
showard02813502008-08-20 20:52:56 +0000349 query_set = self._add_label_joins(query_set)
showardd50ffb42008-09-04 02:47:45 +0000350 joined = True
351
showard2aa318e2009-08-20 23:43:10 +0000352 if include_labels:
353 query_set = self._include_or_exclude_labels(query_set,
354 include_labels)
showardfc8c6ae2008-11-11 19:06:01 +0000355 joined = True
showard2aa318e2009-08-20 23:43:10 +0000356 if exclude_labels:
357 query_set = self._include_or_exclude_labels(query_set,
358 exclude_labels,
359 exclude=True)
showard64aeecd2008-09-19 21:32:58 +0000360 joined = True
361
362 include_attributes_where = filter_data.pop('include_attributes_where',
363 '')
364 exclude_attributes_where = filter_data.pop('exclude_attributes_where',
365 '')
366 if include_attributes_where:
367 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000368 query_set,
369 join_condition=self.escape_user_sql(include_attributes_where))
showard64aeecd2008-09-19 21:32:58 +0000370 joined = True
371 if exclude_attributes_where:
372 query_set = self._add_attribute_join(
showard2aa318e2009-08-20 23:43:10 +0000373 query_set,
374 join_condition=self.escape_user_sql(exclude_attributes_where),
showard64aeecd2008-09-19 21:32:58 +0000375 exclude=True)
376 joined = True
showardd50ffb42008-09-04 02:47:45 +0000377
showard8bfb5cb2009-10-07 20:49:15 +0000378 test_attributes = filter_data.pop('test_attributes', [])
379 for attribute in test_attributes:
380 query_set = self.join_attribute(query_set, attribute)
381 joined = True
382
showardd50ffb42008-09-04 02:47:45 +0000383 if not joined:
showard35444862008-08-07 22:35:30 +0000384 filter_data['no_distinct'] = True
showardd50ffb42008-09-04 02:47:45 +0000385
showard2aa318e2009-08-20 23:43:10 +0000386 # TODO: make test_attributes_host_labels obsolete too
showardf2489522008-10-23 23:08:00 +0000387 if include_host_labels or 'test_attributes_host_labels' in extra_where:
388 query_set = self._add_attribute_join(
389 query_set, suffix='_host_labels',
390 join_condition='test_attributes_host_labels.attribute = '
391 '"host-labels"')
392
showard35444862008-08-07 22:35:30 +0000393 return query_set
394
395
showard8bfb5cb2009-10-07 20:49:15 +0000396 def query_test_ids(self, filter_data, apply_presentation=True):
397 query = self.model.query_objects(filter_data,
398 apply_presentation=apply_presentation)
399 dicts = query.values('test_idx')
showard02813502008-08-20 20:52:56 +0000400 return [item['test_idx'] for item in dicts]
401
402
showard02813502008-08-20 20:52:56 +0000403 def query_test_label_ids(self, filter_data):
showardd50ffb42008-09-04 02:47:45 +0000404 query_set = self.model.query_objects(filter_data)
405 query_set = self._add_label_joins(query_set, suffix='_list')
406 rows = self._custom_select_query(query_set, ['test_labels_list.id'])
407 return [row[0] for row in rows if row[0] is not None]
showard02813502008-08-20 20:52:56 +0000408
409
showardeaccf8f2009-04-16 03:11:33 +0000410 def escape_user_sql(self, sql):
411 sql = super(TestViewManager, self).escape_user_sql(sql)
412 return sql.replace('test_idx', self.get_key_on_this_table('test_idx'))
413
414
showardc4780402009-08-31 18:31:34 +0000415 def _join_one_iteration_key(self, query_set, result_key, index):
416 suffix = '_%s' % index
417 table_name = IterationResult._meta.db_table
418 alias = table_name + suffix
419 condition_parts = ["%s.attribute = '%s'" %
420 (alias, self.escape_user_sql(result_key))]
421 if index > 0:
422 # after the first join, we need to match up iteration indices,
423 # otherwise each join will expand the query by the number of
424 # iterations and we'll have extraneous rows
425 first_alias = table_name + '_0'
426 condition_parts.append('%s.iteration = %s.iteration' %
427 (alias, first_alias))
428
429 condition = ' and '.join(condition_parts)
430 # add a join to IterationResult
431 query_set = self.add_join(query_set, table_name, join_key='test_idx',
432 join_condition=condition, suffix=suffix)
433 # select the iteration value for this join
434 query_set = query_set.extra(select={result_key: '%s.value' % alias})
435 if index == 0:
436 # pull the iteration index from the first join
437 query_set = query_set.extra(
438 select={'iteration_index': '%s.iteration' % alias})
439
440 return query_set
441
442
443 def join_iterations(self, test_view_query_set, result_keys):
444 """
445 Join the given TestView QuerySet to IterationResult. The resulting
446 query looks like a TestView query but has one row per iteration. Each
447 row includes all the attributes of TestView, an attribute for each key
448 in result_keys and an iteration_index attribute.
449
450 We accomplish this by joining the TestView query to IterationResult
451 once per result key. Each join is restricted on the result key (and on
452 the test index, like all one-to-many joins). For the first join, this
453 is the only restriction, so each TestView row expands to a row per
454 iteration (per iteration that includes the key, of course). For each
455 subsequent join, we also restrict the iteration index to match that of
456 the initial join. This makes each subsequent join produce exactly one
457 result row for each input row. (This assumes each iteration contains
458 the same set of keys.)
459 """
460 query_set = test_view_query_set
461 for index, result_key in enumerate(result_keys):
462 query_set = self._join_one_iteration_key(query_set, result_key,
463 index)
464 return query_set
465
466
showard8bfb5cb2009-10-07 20:49:15 +0000467 def join_attribute(self, test_view_query_set, attribute):
468 """
469 Join the given TestView QuerySet to TestAttribute. The resulting query
470 has an additional column for the given attribute named
471 "attribute_<attribute name>".
472 """
473 table_name = TestAttribute._meta.db_table
474 suffix = '_' + attribute
475 alias = table_name + suffix
476 condition = "%s.attribute = '%s'" % (alias,
477 self.escape_user_sql(attribute))
478 query_set = self.add_join(test_view_query_set, table_name,
479 join_key='test_idx', join_condition=condition,
480 suffix=suffix, force_left_join=True)
481
482 select_name = 'attribute_' + attribute
483 query_set = query_set.extra(select={select_name: '%s.value' % alias})
484 return query_set
485
486
showard35444862008-08-07 22:35:30 +0000487class TestView(dbmodels.Model, model_logic.ModelExtensions):
488 extra_fields = {
showardf4c702e2009-07-08 21:14:27 +0000489 'DATE(job_queued_time)': 'job queued day',
490 'DATE(test_finished_time)': 'test finished day',
showard35444862008-08-07 22:35:30 +0000491 }
492
493 group_fields = [
showardf4c702e2009-07-08 21:14:27 +0000494 'test_name',
495 'status',
496 'kernel',
497 'hostname',
498 'job_tag',
499 'job_name',
500 'platform',
501 'reason',
502 'job_owner',
503 'job_queued_time',
504 'DATE(job_queued_time)',
505 'test_started_time',
506 'test_finished_time',
507 'DATE(test_finished_time)',
showard35444862008-08-07 22:35:30 +0000508 ]
509
510 test_idx = dbmodels.IntegerField('test index', primary_key=True)
511 job_idx = dbmodels.IntegerField('job index', null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000512 test_name = dbmodels.CharField(blank=True, max_length=90)
513 subdir = dbmodels.CharField('subdirectory', blank=True, max_length=180)
showard35444862008-08-07 22:35:30 +0000514 kernel_idx = dbmodels.IntegerField('kernel index')
515 status_idx = dbmodels.IntegerField('status index')
showarda5288b42009-07-28 20:06:08 +0000516 reason = dbmodels.CharField(blank=True, max_length=3072)
showard35444862008-08-07 22:35:30 +0000517 machine_idx = dbmodels.IntegerField('host index')
518 test_started_time = dbmodels.DateTimeField(null=True, blank=True)
519 test_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showarda5288b42009-07-28 20:06:08 +0000520 job_tag = dbmodels.CharField(blank=True, max_length=300)
521 job_name = dbmodels.CharField(blank=True, max_length=300)
522 job_owner = dbmodels.CharField('owner', blank=True, max_length=240)
showard35444862008-08-07 22:35:30 +0000523 job_queued_time = dbmodels.DateTimeField(null=True, blank=True)
524 job_started_time = dbmodels.DateTimeField(null=True, blank=True)
525 job_finished_time = dbmodels.DateTimeField(null=True, blank=True)
showardc1c1caf2009-09-08 16:26:50 +0000526 afe_job_id = dbmodels.IntegerField(null=True)
showarda5288b42009-07-28 20:06:08 +0000527 hostname = dbmodels.CharField(blank=True, max_length=300)
528 platform = dbmodels.CharField(blank=True, max_length=240)
529 machine_owner = dbmodels.CharField(blank=True, max_length=240)
530 kernel_hash = dbmodels.CharField(blank=True, max_length=105)
531 kernel_base = dbmodels.CharField(blank=True, max_length=90)
532 kernel = dbmodels.CharField(blank=True, max_length=300)
533 status = dbmodels.CharField(blank=True, max_length=30)
showard35444862008-08-07 22:35:30 +0000534
535 objects = TestViewManager()
536
537 def save(self):
538 raise NotImplementedError('TestView is read-only')
539
540
541 def delete(self):
542 raise NotImplementedError('TestView is read-only')
543
544
545 @classmethod
showard8bfb5cb2009-10-07 20:49:15 +0000546 def query_objects(cls, filter_data, initial_query=None,
547 apply_presentation=True):
showard35444862008-08-07 22:35:30 +0000548 if initial_query is None:
showard64aeecd2008-09-19 21:32:58 +0000549 initial_query = cls.objects.get_query_set_with_joins(filter_data)
showard8bfb5cb2009-10-07 20:49:15 +0000550 return super(TestView, cls).query_objects(
551 filter_data, initial_query=initial_query,
552 apply_presentation=apply_presentation)
showard35444862008-08-07 22:35:30 +0000553
554
555 class Meta:
556 db_table = 'test_view_2'