blob: 4bb9a598c97b89fc8ab6a9d1957fe6e05e2bd267 [file] [log] [blame]
from django.db import models as dbmodels, connection
from django.utils import datastructures
from autotest_lib.frontend.afe import model_logic, readonly_connection
class TempManager(model_logic.ExtendedManager):
_GROUP_COUNT_NAME = 'group_count'
def _get_key_unless_is_function(self, field):
if '(' in field:
return field
return self._get_key_on_this_table(field)
def _get_field_names(self, fields):
return [self._get_key_unless_is_function(field) for field in fields]
def _get_group_query_sql(self, query, group_by, extra_select_fields):
group_fields = self._get_field_names(group_by)
if query._distinct:
pk_field = self._get_key_on_this_table(self.model._meta.pk.name)
count_sql = 'COUNT(DISTINCT %s)' % pk_field
else:
count_sql = 'COUNT(1)'
select_fields = (group_fields +
[count_sql + ' AS ' + self._GROUP_COUNT_NAME] +
extra_select_fields)
# add the count field and all group fields to the query selects, so
# they'll be sortable and Django won't mess with any of them
#for field in group_fields + [self._GROUP_COUNT_NAME]:
# query._select[field] = ''
query._select[self._GROUP_COUNT_NAME] = count_sql
# Inject the GROUP_BY clause into the query by adding it to the end of
# the queries WHERE clauses. We need it to come before the ORDER BY and
# LIMIT clauses.
num_real_where_clauses = len(query._where)
query._where.append('GROUP BY ' + ', '.join(group_fields))
_, where, params = query._get_sql_clause()
if num_real_where_clauses == 0:
# handle the special case where there were no actual WHERE clauses
where = where.replace('WHERE GROUP BY', 'GROUP BY')
else:
where = where.replace('AND GROUP BY', 'GROUP BY')
return ('SELECT ' + ', '.join(select_fields) + where), params
def get_group_counts(self, query, group_by, extra_select_fields=[]):
"""
Performs the given query grouped by the fields in group_by. Returns a
list of rows, where each row is a list containing the value of each
field in group_by, followed by the group count.
"""
sql, params = self._get_group_query_sql(query, group_by,
extra_select_fields)
cursor = readonly_connection.connection.cursor()
num_rows = cursor.execute(sql, params)
return cursor.fetchall()
def _get_num_groups_sql(self, query, group_by):
group_fields = self._get_field_names(group_by)
query._order_by = None # this can mess up the query is isn't needed
_, where, params = query._get_sql_clause()
return ('SELECT COUNT(DISTINCT %s) %s' % (','.join(group_fields),
where),
params)
def get_num_groups(self, query, group_by):
"""
Returns the number of distinct groups for the given query grouped by the
fields in group_by.
"""
sql, params = self._get_num_groups_sql(query, group_by)
cursor = readonly_connection.connection.cursor()
cursor.execute(sql, params)
return cursor.fetchone()[0]
class Machine(dbmodels.Model):
machine_idx = dbmodels.IntegerField(primary_key=True)
hostname = dbmodels.CharField(unique=True, maxlength=300)
machine_group = dbmodels.CharField(blank=True, maxlength=240)
owner = dbmodels.CharField(blank=True, maxlength=240)
class Meta:
db_table = 'machines'
class Kernel(dbmodels.Model):
kernel_idx = dbmodels.IntegerField(primary_key=True)
kernel_hash = dbmodels.CharField(maxlength=105, editable=False)
base = dbmodels.CharField(maxlength=90)
printable = dbmodels.CharField(maxlength=300)
class Meta:
db_table = 'kernels'
class Patch(dbmodels.Model):
kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
name = dbmodels.CharField(blank=True, maxlength=240)
url = dbmodels.CharField(blank=True, maxlength=900)
hash_ = dbmodels.CharField(blank=True, maxlength=105, db_column='hash')
class Meta:
db_table = 'patches'
class Status(dbmodels.Model):
status_idx = dbmodels.IntegerField(primary_key=True)
word = dbmodels.CharField(maxlength=30)
class Meta:
db_table = 'status'
class Job(dbmodels.Model):
job_idx = dbmodels.IntegerField(primary_key=True)
tag = dbmodels.CharField(unique=True, maxlength=300)
label = dbmodels.CharField(maxlength=300)
username = dbmodels.CharField(maxlength=240)
machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
queued_time = dbmodels.DateTimeField(null=True, blank=True)
started_time = dbmodels.DateTimeField(null=True, blank=True)
finished_time = dbmodels.DateTimeField(null=True, blank=True)
class Meta:
db_table = 'jobs'
class TestManager(dbmodels.Manager):
'Custom manager for Test model'
def query_using_test_view(self, filter_data):
test_ids = [test_view.test_idx for test_view
in TestView.query_objects(filter_data)]
return self.in_bulk(test_ids).values()
class Test(dbmodels.Model):
test_idx = dbmodels.IntegerField(primary_key=True)
job = dbmodels.ForeignKey(Job, db_column='job_idx')
test = dbmodels.CharField(maxlength=90)
subdir = dbmodels.CharField(blank=True, maxlength=180)
kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
status = dbmodels.ForeignKey(Status, db_column='status')
reason = dbmodels.CharField(blank=True, maxlength=3072)
machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
finished_time = dbmodels.DateTimeField(null=True, blank=True)
started_time = dbmodels.DateTimeField(null=True, blank=True)
objects = TestManager()
class Meta:
db_table = 'tests'
class TestAttribute(dbmodels.Model):
test = dbmodels.ForeignKey(Test, db_column='test_idx')
attribute = dbmodels.CharField(maxlength=90)
value = dbmodels.CharField(blank=True, maxlength=300)
class Meta:
db_table = 'test_attributes'
class IterationAttribute(dbmodels.Model):
test = dbmodels.ForeignKey(Test, db_column='test_idx')
iteration = dbmodels.IntegerField()
attribute = dbmodels.CharField(maxlength=90)
value = dbmodels.CharField(blank=True, maxlength=300)
class Meta:
db_table = 'iteration_attributes'
class IterationResult(dbmodels.Model):
test = dbmodels.ForeignKey(Test, db_column='test_idx')
iteration = dbmodels.IntegerField()
attribute = dbmodels.CharField(maxlength=90)
value = dbmodels.FloatField(null=True, max_digits=12, decimal_places=31,
blank=True)
class Meta:
db_table = 'iteration_result'
class TestLabel(dbmodels.Model, model_logic.ModelExtensions):
name = dbmodels.CharField(maxlength=80)
description = dbmodels.TextField(blank=True)
tests = dbmodels.ManyToManyField(Test, blank=True,
filter_interface=dbmodels.HORIZONTAL)
name_field = 'name'
class Meta:
db_table = 'test_labels'
class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):
# TODO: change this to foreign key once DBs are merged
owner = dbmodels.CharField(maxlength=80)
name = dbmodels.CharField(maxlength=100)
url_token = dbmodels.TextField()
class Meta:
db_table = 'saved_queries'
# views
class TestViewManager(TempManager):
class _JoinQ(dbmodels.Q):
def __init__(self):
self._joins = datastructures.SortedDict()
def add_join(self, table, condition, join_type, alias=None):
if alias is None:
alias = table
self._joins[alias] = (table, join_type, condition)
def get_sql(self, opts):
return self._joins, [], []
def get_query_set(self):
query = super(TestViewManager, self).get_query_set()
# add extra fields to selects, using the SQL itself as the "alias"
extra_select = dict((sql, sql)
for sql in self.model.extra_fields.iterkeys())
return query.extra(select=extra_select)
def get_query_set_with_labels(self, filter_data):
query_set = self.get_query_set()
# TODO: make this check more thorough if necessary
if 'test_labels' in filter_data.get('extra_where', ''):
filter_object = self._JoinQ()
filter_object.add_join(
'test_labels_tests',
'test_labels_tests.test_id = test_view.test_idx',
'LEFT JOIN')
filter_object.add_join(
'test_labels',
'test_labels.id = test_labels_tests.testlabel_id',
'LEFT JOIN')
query_set = query_set.complex_filter(filter_object).distinct()
else:
filter_data['no_distinct'] = True
return query_set
class TestView(dbmodels.Model, model_logic.ModelExtensions):
extra_fields = {
'DATE(test_finished_time)' : 'test finished day',
}
group_fields = [
'test_name',
'status',
'kernel',
'hostname',
'job_tag',
'job_name',
'platform',
'reason',
'job_owner',
'test_finished_time',
'DATE(test_finished_time)',
]
test_idx = dbmodels.IntegerField('test index', primary_key=True)
job_idx = dbmodels.IntegerField('job index', null=True, blank=True)
test_name = dbmodels.CharField(blank=True, maxlength=90)
subdir = dbmodels.CharField('subdirectory', blank=True, maxlength=180)
kernel_idx = dbmodels.IntegerField('kernel index')
status_idx = dbmodels.IntegerField('status index')
reason = dbmodels.CharField(blank=True, maxlength=3072)
machine_idx = dbmodels.IntegerField('host index')
test_started_time = dbmodels.DateTimeField(null=True, blank=True)
test_finished_time = dbmodels.DateTimeField(null=True, blank=True)
job_tag = dbmodels.CharField(blank=True, maxlength=300)
job_name = dbmodels.CharField(blank=True, maxlength=300)
job_owner = dbmodels.CharField('owner', blank=True, maxlength=240)
job_queued_time = dbmodels.DateTimeField(null=True, blank=True)
job_started_time = dbmodels.DateTimeField(null=True, blank=True)
job_finished_time = dbmodels.DateTimeField(null=True, blank=True)
hostname = dbmodels.CharField(blank=True, maxlength=300)
platform = dbmodels.CharField(blank=True, maxlength=240)
machine_owner = dbmodels.CharField(blank=True, maxlength=240)
kernel_hash = dbmodels.CharField(blank=True, maxlength=105)
kernel_base = dbmodels.CharField(blank=True, maxlength=90)
kernel = dbmodels.CharField(blank=True, maxlength=300)
status = dbmodels.CharField(blank=True, maxlength=30)
objects = TestViewManager()
def save(self):
raise NotImplementedError('TestView is read-only')
def delete(self):
raise NotImplementedError('TestView is read-only')
@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)
return super(TestView, cls).query_objects(filter_data,
initial_query=initial_query)
@classmethod
def list_objects(cls, filter_data, initial_query=None):
"""
Django's ValuesQuerySet (used when you call query.values()) doesn't
support custom select fields, so we have to basically reimplement it
here.
TODO: merge this up to ModelExtensions after some settling time.
"""
query = cls.query_objects(filter_data, initial_query=initial_query)
object_dicts = []
for model_object in query:
object_dict = model_object.get_object_dict()
for sql in cls.extra_fields.iterkeys():
object_dict[sql] = getattr(model_object, sql)
object_dicts.append(object_dict)
return object_dicts
class Meta:
db_table = 'test_view_2'