=== modified file 'dashboard_app/admin.py'
@@ -33,7 +33,6 @@
BundleStream,
HardwareDevice,
Image,
- ImageAttribute,
ImageSet,
LaunchpadBug,
NamedAttribute,
@@ -183,27 +182,16 @@
list_display = ('__unicode__', 'project')
-class ImageAttributeInline(admin.TabularInline):
- model = ImageAttribute
- verbose_name = 'required metadata attribute'
- verbose_name_plural = 'required metadata attributes'
-
-
class ImageAdmin(admin.ModelAdmin):
- filter_horizontal = ['bundle_streams']
- inlines = [ImageAttributeInline]
save_as = True
class ImageSetAdmin(admin.ModelAdmin):
- filter_horizontal = ['filters']
- def formfield_for_manytomany(self, db_field, request, **kwargs):
- field = super(ImageSetAdmin, self).formfield_for_manytomany(
- db_field, request, **kwargs)
- print db_field
- if db_field.name == 'filters':
- field.queryset = TestRunFilter.objects.filter(enable_as_image=True).order_by('name')
- return field
+ class Media:
+ css = {
+ "all": ("dashboard_app/css/wider-filter-horizontal.css",)
+ }
+ filter_horizontal = ['images']
save_as = True
=== added file 'dashboard_app/migrations/0024_auto__del_imageattribute__del_field_image_uploaded_by__del_field_image.py'
@@ -0,0 +1,325 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Deleting model 'ImageAttribute'
+ db.delete_table('dashboard_app_imageattribute')
+
+ # Deleting field 'Image.uploaded_by'
+ db.delete_column('dashboard_app_image', 'uploaded_by_id')
+
+ # Deleting field 'Image.build_number_attribute'
+ db.delete_column('dashboard_app_image', 'build_number_attribute')
+
+ # Adding field 'Image.filter'
+ db.add_column('dashboard_app_image', 'filter',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', null=True, to=orm['dashboard_app.TestRunFilter']),
+ keep_default=False)
+
+ # Removing M2M table for field bundle_streams on 'Image'
+ db.delete_table('dashboard_app_image_bundle_streams')
+
+ # Removing M2M table for field filters on 'ImageSet'
+ db.delete_table('dashboard_app_imageset_filters')
+
+ # Deleting field 'TestRunFilter.enable_as_image'
+ db.delete_column('dashboard_app_testrunfilter', 'enable_as_image')
+
+
+ def backwards(self, orm):
+ # Adding model 'ImageAttribute'
+ db.create_table('dashboard_app_imageattribute', (
+ ('image', self.gf('django.db.models.fields.related.ForeignKey')(related_name='required_attributes', to=orm['dashboard_app.Image'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('value', self.gf('django.db.models.fields.CharField')(max_length=1024)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=1024)),
+ ))
+ db.send_create_signal('dashboard_app', ['ImageAttribute'])
+
+ # Adding field 'Image.uploaded_by'
+ db.add_column('dashboard_app_image', 'uploaded_by',
+ self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True),
+ keep_default=False)
+
+
+ # User chose to not deal with backwards NULL issues for 'Image.build_number_attribute'
+ raise RuntimeError("Cannot reverse this migration. 'Image.build_number_attribute' and its values cannot be restored.")
+ # Deleting field 'Image.filter'
+ db.delete_column('dashboard_app_image', 'filter_id')
+
+ # Adding M2M table for field bundle_streams on 'Image'
+ db.create_table('dashboard_app_image_bundle_streams', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('image', models.ForeignKey(orm['dashboard_app.image'], null=False)),
+ ('bundlestream', models.ForeignKey(orm['dashboard_app.bundlestream'], null=False))
+ ))
+ db.create_unique('dashboard_app_image_bundle_streams', ['image_id', 'bundlestream_id'])
+
+ # Adding M2M table for field filters on 'ImageSet'
+ db.create_table('dashboard_app_imageset_filters', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('imageset', models.ForeignKey(orm['dashboard_app.imageset'], null=False)),
+ ('testrunfilter', models.ForeignKey(orm['dashboard_app.testrunfilter'], null=False))
+ ))
+ db.create_unique('dashboard_app_imageset_filters', ['imageset_id', 'testrunfilter_id'])
+
+ # Adding field 'TestRunFilter.enable_as_image'
+ db.add_column('dashboard_app_testrunfilter', 'enable_as_image',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'dashboard_app.attachment': {
+ 'Meta': {'object_name': 'Attachment'},
+ 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+ 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'})
+ },
+ 'dashboard_app.bundle': {
+ 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'},
+ '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}),
+ '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}),
+ 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}),
+ 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
+ },
+ 'dashboard_app.bundledeserializationerror': {
+ 'Meta': {'object_name': 'BundleDeserializationError'},
+ 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}),
+ 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'})
+ },
+ 'dashboard_app.bundlestream': {
+ 'Meta': {'object_name': 'BundleStream'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+ },
+ 'dashboard_app.hardwaredevice': {
+ 'Meta': {'object_name': 'HardwareDevice'},
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'dashboard_app.image': {
+ 'Meta': {'object_name': 'Image'},
+ 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'})
+ },
+ 'dashboard_app.imageset': {
+ 'Meta': {'object_name': 'ImageSet'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'})
+ },
+ 'dashboard_app.launchpadbug': {
+ 'Meta': {'object_name': 'LaunchpadBug'},
+ 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"})
+ },
+ 'dashboard_app.namedattribute': {
+ 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.TextField', [], {}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'dashboard_app.softwarepackage': {
+ 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'dashboard_app.softwarepackagescratch': {
+ 'Meta': {'object_name': 'SoftwarePackageScratch'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'dashboard_app.softwaresource': {
+ 'Meta': {'object_name': 'SoftwareSource'},
+ 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
+ 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+ },
+ 'dashboard_app.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'})
+ },
+ 'dashboard_app.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
+ },
+ 'dashboard_app.testcase': {
+ 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}),
+ 'test_case_id': ('django.db.models.fields.TextField', [], {}),
+ 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ 'dashboard_app.testingeffort': {
+ 'Meta': {'object_name': 'TestingEffort'},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'testing_efforts'", 'to': "orm['lava_projects.Project']"}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'testing_efforts'", 'symmetrical': 'False', 'to': "orm['dashboard_app.Tag']"})
+ },
+ 'dashboard_app.testresult': {
+ 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'},
+ '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+ 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}),
+ 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'dashboard_app.testrun': {
+ 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'},
+ 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}),
+ 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}),
+ 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}),
+ 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}),
+ 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}),
+ 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}),
+ 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'dashboard_app.testrundenormalization': {
+ 'Meta': {'object_name': 'TestRunDenormalization'},
+ 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"})
+ },
+ 'dashboard_app.testrunfilter': {
+ 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'},
+ 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+ 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"})
+ },
+ 'dashboard_app.testrunfilterattribute': {
+ 'Meta': {'object_name': 'TestRunFilterAttribute'},
+ 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'})
+ },
+ 'dashboard_app.testrunfiltersubscription': {
+ 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'},
+ 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'dashboard_app.testrunfiltertest': {
+ 'Meta': {'object_name': 'TestRunFilterTest'},
+ 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"})
+ },
+ 'dashboard_app.testrunfiltertestcase': {
+ 'Meta': {'object_name': 'TestRunFilterTestCase'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}),
+ 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"})
+ },
+ 'lava_projects.project': {
+ 'Meta': {'object_name': 'Project'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'identifier': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}),
+ 'is_aggregate': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'registered_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'to': "orm['auth.User']"}),
+ 'registered_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['dashboard_app']
\ No newline at end of file
=== modified file 'dashboard_app/models.py'
@@ -1432,84 +1432,15 @@
tags__in=self.tags.all())
-class ImageAttribute(models.Model):
-
- name = models.CharField(max_length=1024)
- value = models.CharField(max_length=1024)
-
- image = models.ForeignKey("Image", related_name="required_attributes")
-
- def __unicode__(self):
- return '%s = %s' % (self.name, self.value)
-
class Image(models.Model):
name = models.SlugField(max_length=1024, unique=True)
- build_number_attribute = models.CharField(max_length=1024)
-
- bundle_streams = models.ManyToManyField(BundleStream)
-
- uploaded_by = models.ForeignKey(User, null=True, blank=True)
+ filter = models.ForeignKey("TestRunFilter", related_name='+', null=True)
def __unicode__(self):
- return self.name
-
- def _get_bundles(self, user):
- accessible_bundles = BundleStream.objects.accessible_by_principal(
- user)
- args = [models.Q(bundle_stream__in=accessible_bundles)]
- if self.bundle_streams.exists():
- args += [models.Q(bundle_stream__in=self.bundle_streams.all())]
- if self.uploaded_by:
- args += [models.Q(uploaded_by=self.uploaded_by)]
- bundles = Bundle.objects.filter(*args)
-
- # This is a little tricky. We want to AND together the conditions
- # that the attribute matches, but with the Django ORM we can only join
- # the attribute table once per query so we put each condition in a
- # nested query, so for example instead of something like this:
- #
- # select * from bundle
- # where <bundle.testrun.name is vexpress>
- # and <bundle.testrun.image_type = 'desktop';
- #
- # we generate this:
- #
- # select * from bundle
- # where <bundle.testrun.name is vexpress>
- # and bundle.id in
- # (select * from bundle
- # where <bundle.testrun.image_type = 'desktop');
- #
- # (additionally, we only consider the lava testrun to avoid returning
- # bundles repeatedly).
-
- for attr in self.required_attributes.all():
- bundles = Bundle.objects.filter(
- id__in=bundles.values_list('id'),
- test_runs__test__test_id='lava',
- test_runs__attributes__name=attr.name,
- test_runs__attributes__value=attr.value)
-
- return bundles
-
- def get_bundles(self, user):
- return Bundle.objects.filter(
- id__in=self._get_bundles(user).values('id'),
- test_runs__test__test_id='lava',
- test_runs__attributes__name=self.build_number_attribute)
-
- def get_latest_bundles(self, user, count):
- return Bundle.objects.filter(
- id__in=self._get_bundles(user).values('id'),
- test_runs__test__test_id='lava',
- test_runs__attributes__name=self.build_number_attribute).extra(
- select={
- 'build_number': 'convert_to_integer("dashboard_app_namedattribute"."value")',
- }).extra(
- order_by=['-build_number'],
- )[:count]
+ owner_name = getattr(self.filter, 'owner_name', '<NULL>')
+ return '%s, based on %s' % (self.name, owner_name)
@models.permalink
def get_absolute_url(self):
@@ -1520,9 +1451,7 @@
name = models.CharField(max_length=1024, unique=True)
- images = models.ManyToManyField(Image, help_text="This field is now obsolete")
-
- filters = models.ManyToManyField("TestRunFilter")
+ images = models.ManyToManyField(Image)
def __unicode__(self):
return self.name
@@ -1621,14 +1550,15 @@
"""Wrap a QuerySet and construct FilterMatchs from what the wrapped query
set returns.
- Just enough of the QuerySet API to work with DataTable (i.e. ordering and
- slicing)."""
+ Just enough of the QuerySet API to work with DataTable (i.e. pretend
+ ordering and real slicing)."""
model = TestRun
- def __init__(self, queryset, filter_data):
+ def __init__(self, queryset, filter_data, prefetch_related):
self.queryset = queryset
self.filter_data = filter_data
+ self.prefetch_related = prefetch_related
if filter_data['build_number_attribute']:
self.key = 'build_number'
self.key_name = 'Build'
@@ -1642,7 +1572,8 @@
test_run_ids.update(datum['id__arrayagg'])
r = []
trs = TestRun.objects.filter(id__in=test_run_ids).select_related(
- 'denormalization', 'bundle', 'bundle__bundle_stream', 'test')
+ 'denormalization', 'bundle', 'bundle__bundle_stream', 'test').prefetch_related(
+ *self.prefetch_related)
trs_by_id = {}
for tr in trs:
trs_by_id[tr.id] = tr
@@ -1690,7 +1621,7 @@
return iter(r)
def _wrap(self, queryset, **kw):
- return self.__class__(queryset, self.filter_data, **kw)
+ return self.__class__(queryset, self.filter_data, self.prefetch_related, **kw)
def order_by(self, *args):
# the generic tables code calls this even when it shouldn't...
@@ -1771,6 +1702,11 @@
help_text=("The <b>name</b> of a filter is used to refer to it in "
"the web UI and in email notifications triggered by this "
"filter."))
+
+ @property
+ def owner_name(self):
+ return '~%s/%s' % (self.owner.username, self.name)
+
class Meta:
unique_together = (('owner', 'name'))
@@ -1788,8 +1724,6 @@
User, null=True, blank=True, related_name='+',
help_text="Only consider bundles uploaded by this user")
- enable_as_image = models.BooleanField(default=False)
-
@property
def summary_data(self):
return {
@@ -1811,7 +1745,7 @@
# and testrun has attribute with key = keyN and value = valueN
# and testrun has any of the tests/testcases requested
- def get_test_runs_impl(self, user, bundle_streams, attributes, tests):
+ def get_test_runs_impl(self, user, bundle_streams, attributes, tests, prefetch_related=[]):
accessible_bundle_streams = BundleStream.objects.accessible_by_principal(
user)
bs_ids = [bs.id for bs in set(accessible_bundle_streams) & set(bundle_streams)]
@@ -1868,7 +1802,7 @@
'build_number_attribute': self.build_number_attribute,
}
- return MatchMakingQuerySet(testruns, filter_data)
+ return MatchMakingQuerySet(testruns, filter_data, prefetch_related)
# given bundle:
# select from filter
@@ -1933,12 +1867,13 @@
matches.append(match)
return matches
- def get_test_runs(self, user):
+ def get_test_runs(self, user, prefetch_related=[]):
return self.get_test_runs_impl(
user,
self.bundle_streams.all(),
self.attributes.values_list('name', 'value'),
- self.tests.all())
+ self.tests.all(),
+ prefetch_related)
@models.permalink
def get_absolute_url(self):
=== added file 'dashboard_app/static/css/wider-filter-horizontal.css'
@@ -0,0 +1,16 @@
+.selector {
+ width: 1000px;
+ float: left;
+}
+
+.selector select {
+ width: 480px;
+ height: 17.2em;
+}
+
+.selector-available, .selector-chosen {
+ float: left;
+ width: 480px;
+ text-align: center;
+ margin-bottom: 5px;
+}
=== modified file 'dashboard_app/templates/dashboard_app/image-report.html'
@@ -41,18 +41,18 @@
<table id="results-table" class="inner-table">
<thead>
<tr>
- {% for bundle in bundles %}
+ {% for col in cols %}
<th>
- <a href="{{ bundle.link }}">{{ bundle.number }}</a>
+ <a href="{{ col.link }}">{{ col.number }}</a>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
- {% for bundle in bundles %}
+ {% for col in cols %}
<td>
- {{ bundle.date|date }}
+ {{ col.date|date }}
</td>
{% endfor %}
</tr>
=== modified file 'dashboard_app/views.py'
@@ -1485,17 +1485,20 @@
@BreadCrumb("Image Reports", parent=index)
def image_report_list(request):
- imagesets = ImageSet.objects.all()
+ imagesets = ImageSet.objects.filter()
imagesets_data = []
for imageset in imagesets:
images_data = []
- for filter in imageset.filters.all():
- image_data = {
- 'name': filter.name,
- 'bundle_count': filter.get_test_runs(request.user).count(),
- 'link': filter.name,
- }
- images_data.append(image_data)
+ for image in imageset.images.all():
+ # Migration hack: Image.filter cannot be auto populated, so ignore
+ # images that have not been migrated to filters for now.
+ if image.filter:
+ image_data = {
+ 'name': image.name,
+ 'bundle_count': image.filter.get_test_runs(request.user).count(),
+ 'link': image.name,
+ }
+ images_data.append(image_data)
images_data.sort(key=lambda d:d['name'])
imageset_data = {
'name': imageset.name,
@@ -1513,83 +1516,55 @@
@BreadCrumb("{name}", parent=image_report_list, needs=['name'])
def image_report_detail(request, name):
- filter = TestRunFilter.objects.get(name=name)
-
- # We are aiming to produce a table like this:
-
- # Build Number | 23 | ... | 40 |
- # Date | YYYY-MM-DD | ... | YYYY-MM-DD |
- # lava | 1/3 | ... | 4/5 |
- # cts | 100/100 | ... | 88/100 |
- # ... | ... | ... | ... |
- # skia | 1/2 | ... | 3/3 |
-
- # Data processing proceeds in 3 steps:
-
- # 1) Get the bundles/builds. Image.get_latest_bundles() does the hard
- # work here and then we just peel off the data we need from the bundles.
-
- # 2) Get all the test runs we are interested in, extract the data we
- # need from them and associate them with the corresponding bundles.
-
- # 3) Organize the data so that it's natural for rendering the table
- # (basically transposing it from being bundle -> testrun -> result to
- # testrun -> bundle -> result).
-
- bundle_id_to_data = {}
-
- matches = filter.get_test_runs(request.user)[:50]
+ image = Image.objects.get(name=name)
+ matches = image.filter.get_test_runs(request.user, prefetch_related=['launchpad_bugs'])[:50]
+
+ build_number_to_cols = {}
+
+ test_run_names = set()
for match in matches:
- for tr in match.test_runs:
- if tr.bundle_id not in bundle_id_to_data:
- bundle = tr.bundle
- bundle_id_to_data[bundle.id] = dict(
- number=match.tag,
- date=bundle.uploaded_on,
- test_runs={},
- link=bundle.get_permalink(),
- )
-
- test_runs = TestRun.objects.filter(
- bundle__id__in=list(bundle_id_to_data),
- ).select_related(
- 'bundle', 'denormalization', 'test').prefetch_related(
- 'launchpad_bugs')
-
- test_run_names = set()
- for test_run in test_runs:
- name = test_run.test.test_id
- denorm = test_run.denormalization
- if denorm.count_pass == denorm.count_all():
- cls = 'present pass'
- else:
- cls = 'present fail'
- bug_ids = sorted([b.bug_id for b in test_run.launchpad_bugs.all()])
- test_run_data = dict(
- present=True,
- cls=cls,
- uuid=test_run.analyzer_assigned_uuid,
- passes=denorm.count_pass,
- total=denorm.count_all(),
- link=test_run.get_permalink(),
- bug_ids=bug_ids,
- )
- bundle_id_to_data[test_run.bundle.id]['test_runs'][name] = test_run_data
- if name != 'lava':
- test_run_names.add(name)
+ for test_run in match.test_runs:
+ name = test_run.test.test_id
+ denorm = test_run.denormalization
+ if denorm.count_pass == denorm.count_all():
+ cls = 'present pass'
+ else:
+ cls = 'present fail'
+ bug_ids = sorted([b.bug_id for b in test_run.launchpad_bugs.all()])
+ test_run_data = dict(
+ present=True,
+ cls=cls,
+ uuid=test_run.analyzer_assigned_uuid,
+ passes=denorm.count_pass,
+ total=denorm.count_all(),
+ link=test_run.get_permalink(),
+ bug_ids=bug_ids,
+ )
+ if match.tag not in build_number_to_cols:
+ # This assumes 1 bundle per match...
+ build_number_to_cols[match.tag] = {
+ 'test_runs': {},
+ 'number': match.tag,
+ 'date': test_run.bundle.uploaded_on,
+ 'link': test_run.bundle.get_absolute_url(),
+ }
+ build_number_to_cols[match.tag]['test_runs'][name] = test_run_data
+ if name != 'lava':
+ test_run_names.add(name)
+
test_run_names = sorted(test_run_names)
test_run_names.insert(0, 'lava')
- bundles = sorted(bundle_id_to_data.values(), key=lambda d:d['number'])
+ cols = [c for n, c in sorted(build_number_to_cols.items())]
table_data = []
for test_run_name in test_run_names:
row_data = []
- for bundle in bundles:
- test_run_data = bundle['test_runs'].get(test_run_name)
+ for col in cols:
+ test_run_data = col['test_runs'].get(test_run_name)
if not test_run_data:
test_run_data = dict(
present=False,
@@ -1601,9 +1576,9 @@
return render_to_response(
"dashboard_app/image-report.html", {
'bread_crumb_trail': BreadCrumbTrail.leading_to(
- image_report_detail, name=filter.name),
- 'image': filter,
- 'bundles': bundles,
+ image_report_detail, name=image.name),
+ 'image': image,
+ 'cols': cols,
'table_data': table_data,
'test_run_names': test_run_names,
}, RequestContext(request))