From patchwork Wed Sep 28 03:11:13 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Larson X-Patchwork-Id: 4381 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id D56A823EF6 for ; Wed, 28 Sep 2011 03:11:15 +0000 (UTC) Received: from mail-fx0-f52.google.com (mail-fx0-f52.google.com [209.85.161.52]) by fiordland.canonical.com (Postfix) with ESMTP id B19ACA19082 for ; Wed, 28 Sep 2011 03:11:15 +0000 (UTC) Received: by fxe23 with SMTP id 23so245689fxe.11 for ; Tue, 27 Sep 2011 20:11:15 -0700 (PDT) Received: by 10.223.63.8 with SMTP id z8mr908624fah.84.1317179475535; Tue, 27 Sep 2011 20:11:15 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.3.234 with SMTP id f10cs98049laf; Tue, 27 Sep 2011 20:11:15 -0700 (PDT) Received: by 10.227.170.4 with SMTP id b4mr9975944wbz.63.1317179474294; Tue, 27 Sep 2011 20:11:14 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id fi7si22524702wbb.34.2011.09.27.20.11.13 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 27 Sep 2011 20:11:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1R8kYD-00050t-F6 for ; Wed, 28 Sep 2011 03:11:13 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 62763E337A for ; Wed, 28 Sep 2011 03:11:13 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-dashboard X-Launchpad-Branch: ~linaro-validation/lava-dashboard/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 266 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 266: Release 0.8 Message-Id: <20110928031113.1857.46644.launchpad@ackee.canonical.com> Date: Wed, 28 Sep 2011 03:11:13 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14049"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 61d032227630674370d10b2903f570d820f490ef Merge authors: Zygmunt Krynicki (zkrynicki) Related merge proposals: https://code.launchpad.net/~zkrynicki/lava-dashboard/0.8-landing/+merge/77263 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Paul Larson (pwlars) https://code.launchpad.net/~zkrynicki/lava-dashboard/better-bundle-view/+merge/77168 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Paul Larson (pwlars) https://code.launchpad.net/~zkrynicki/lava-dashboard/testing-effort/+merge/77166 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Paul Larson (pwlars) https://code.launchpad.net/~zkrynicki/lava-dashboard/support-1.3-format/+merge/77100 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Michael Hudson-Doyle (mwhudson) review: Approve - Paul Larson (pwlars) https://code.launchpad.net/~zkrynicki/lava-dashboard/budle-list-view-performance/+merge/76980 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Paul Larson (pwlars) https://code.launchpad.net/~zkrynicki/lava-dashboard/better-front-page-snippet/+merge/76004 proposed by: Zygmunt Krynicki (zkrynicki) https://code.launchpad.net/~zkrynicki/lava-dashboard/fix-attachment-list/+merge/75691 proposed by: Zygmunt Krynicki (zkrynicki) review: Approve - Paul Larson (pwlars) review: Approve - Spring Zhang (qzhang) ------------------------------------------------------------ revno: 266 [merge] tags: release-0.8, 2011.09 committer: Paul Larson branch nick: lava-dashboard timestamp: Tue 2011-09-27 22:08:44 -0500 message: Release 0.8 added: dashboard_app/migrations/0006_auto__chg_field_bundledeserializationerror_bundle.py dashboard_app/migrations/0007_auto__add_tag.py dashboard_app/migrations/0008_auto__add_testingeffort.py dashboard_app/migrations/0009_auto__add_testrundenormalization.py dashboard_app/migrations/0010_denormalize_test_run.py dashboard_app/signals.py dashboard_app/templates/dashboard_app/testing_effort_detail.html dashboard_app/templates/dashboard_app/testing_effort_list.html modified: dashboard_app/__init__.py dashboard_app/admin.py dashboard_app/helpers.py dashboard_app/managers.py dashboard_app/models.py dashboard_app/templates/dashboard_app/_extension_navigation.html dashboard_app/templates/dashboard_app/_test_run_list_table.html dashboard_app/templates/dashboard_app/attachment_list.html dashboard_app/templates/dashboard_app/bundle_detail.html dashboard_app/templates/dashboard_app/bundle_list.html dashboard_app/templates/dashboard_app/front_page_snippet.html dashboard_app/templates/dashboard_app/test_result_detail.html dashboard_app/templates/dashboard_app/test_run_detail.html dashboard_app/templates/dashboard_app/test_run_list.html dashboard_app/tests/models/bundle.py dashboard_app/tests/other/deserialization.py dashboard_app/urls.py dashboard_app/views.py dashboard_app/xmlrpc.py --- lp:lava-dashboard https://code.launchpad.net/~linaro-validation/lava-dashboard/trunk You are subscribed to branch lp:lava-dashboard. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-dashboard/trunk/+edit-subscription === modified file 'dashboard_app/__init__.py' --- dashboard_app/__init__.py 2011-09-14 13:06:05 +0000 +++ dashboard_app/__init__.py 2011-09-28 02:35:42 +0000 @@ -20,4 +20,4 @@ Dashboard Application (package) """ -__version__ = (0, 7, 2, "final", 0) +__version__ = (0, 8, 0, "final", 0) === modified file 'dashboard_app/admin.py' --- dashboard_app/admin.py 2011-07-19 21:39:22 +0000 +++ dashboard_app/admin.py 2011-09-27 13:06:33 +0000 @@ -26,19 +26,21 @@ from django.utils.translation import ugettext as _ from dashboard_app.models import ( - Attachment, - Bundle, - BundleDeserializationError, - BundleStream, - HardwareDevice, - NamedAttribute, - SoftwarePackage, - SoftwareSource, - Test, - TestCase, - TestResult, - TestRun, - ) + Attachment, + Bundle, + BundleDeserializationError, + BundleStream, + HardwareDevice, + NamedAttribute, + SoftwarePackage, + SoftwareSource, + Tag, + Test, + TestCase, + TestResult, + TestRun, + TestingEffort, +) class BundleAdmin(admin.ModelAdmin): @@ -147,6 +149,10 @@ inlines = [NamedAttributeInline] +class TestingEffortAdmin(admin.ModelAdmin): + list_display = ('__unicode__', 'project') + + admin.site.register(Attachment) admin.site.register(Bundle, BundleAdmin) admin.site.register(BundleDeserializationError, BundleDeserializationErrorAdmin) @@ -158,3 +164,5 @@ admin.site.register(TestCase, TestCaseAdmin) admin.site.register(TestResult, TestResultAdmin) admin.site.register(TestRun, TestRunAdmin) +admin.site.register(Tag) +admin.site.register(TestingEffort, TestingEffortAdmin) === modified file 'dashboard_app/helpers.py' --- dashboard_app/helpers.py 2011-07-20 23:08:27 +0000 +++ dashboard_app/helpers.py 2011-09-28 02:29:55 +0000 @@ -100,7 +100,7 @@ Note: This function uses commit_on_success to ensure the database is in a consistent state after IntegrityErrors that would clog the transaction on pgsql. Since transactions will not rollback any files we - created in the meantime there is is a helper that cleans attachments in + created in the meantime there is a helper that cleans attachments in case something goes wrong """ self._import_document(s_bundle, doc) @@ -161,6 +161,7 @@ self._log('attributes') # collect all the changes that happen before the previous save s_test_run.save() + s_test_run.denormalize() return s_test_run def _import_software_context(self, c_test_run, s_test_run): @@ -681,6 +682,24 @@ ContentFile(content)) +class BundleFormatImporter_1_3(BundleFormatImporter_1_2): + """ + IFormatImporter subclass capable of loading "Dashboard Bundle Format 1.3" + """ + + def _import_test_run(self, c_test_run, s_bundle): + from dashboard_app.models import Tag + + s_test_run = super(BundleFormatImporter_1_3, self)._import_test_run(c_test_run, s_bundle) + self._log('tags') + for c_tag in c_test_run.get("tags", []): + s_tag, created = Tag.objects.get_or_create(name=c_tag) + if created: + s_tag.save() + s_test_run.tags.add(s_tag) + return s_test_run + + class BundleDeserializer(object): """ Helper class for de-serializing JSON bundle content into database models @@ -691,6 +710,7 @@ "Dashboard Bundle Format 1.0.1": BundleFormatImporter_1_0_1, "Dashboard Bundle Format 1.1": BundleFormatImporter_1_1, "Dashboard Bundle Format 1.2": BundleFormatImporter_1_2, + "Dashboard Bundle Format 1.3": BundleFormatImporter_1_3, } def deserialize(self, s_bundle, prefer_evolution): === modified file 'dashboard_app/managers.py' --- dashboard_app/managers.py 2011-07-09 13:47:36 +0000 +++ dashboard_app/managers.py 2011-09-28 02:28:43 +0000 @@ -51,3 +51,20 @@ raise else: return bundle + + +class TestRunDenormalizationManager(models.Manager): + + def create_from_test_run(self, test_run): + from dashboard_app.models import TestResult + stats = test_run.test_results.values('result').annotate( + count=models.Count('result')).order_by() + result = dict([ + (TestResult.RESULT_MAP[item['result']], item['count']) + for item in stats]) + return self.create( + test_run=test_run, + count_pass=result.get('pass', 0), + count_fail=result.get('fail', 0), + count_skip=result.get('skip', 0), + count_unknown=result.get('unknown', 0)) === added file 'dashboard_app/migrations/0006_auto__chg_field_bundledeserializationerror_bundle.py' --- dashboard_app/migrations/0006_auto__chg_field_bundledeserializationerror_bundle.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0006_auto__chg_field_bundledeserializationerror_bundle.py 2011-09-26 12:55:16 +0000 @@ -0,0 +1,176 @@ +# encoding: 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): + + # Changing field 'BundleDeserializationError.bundle' + db.alter_column('dashboard_app_bundledeserializationerror', 'bundle_id', self.gf('django.db.models.fields.related.OneToOneField')(unique=True, primary_key=True, to=orm['dashboard_app.Bundle'])) + + + def backwards(self, orm): + + # Changing field 'BundleDeserializationError.bundle' + db.alter_column('dashboard_app_bundledeserializationerror', 'bundle_id', self.gf('django.db.models.fields.related.ForeignKey')(unique=True, primary_key=True, to=orm['dashboard_app.Bundle'])) + + + 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'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + '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.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.CharField', [], {'max_length': '32'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + '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.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.CharField', [], {'max_length': '100', '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.CharField', [], {'max_length': '100'}), + 'units': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) + }, + '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'}), + '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'}) + } + } + + complete_apps = ['dashboard_app'] === added file 'dashboard_app/migrations/0007_auto__add_tag.py' --- dashboard_app/migrations/0007_auto__add_tag.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0007_auto__add_tag.py 2011-09-26 21:15:33 +0000 @@ -0,0 +1,197 @@ +# encoding: 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): + + # Adding model 'Tag' + db.create_table('dashboard_app_tag', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=256, db_index=True)), + )) + db.send_create_signal('dashboard_app', ['Tag']) + + # Adding M2M table for field tags on 'TestRun' + db.create_table('dashboard_app_testrun_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('testrun', models.ForeignKey(orm['dashboard_app.testrun'], null=False)), + ('tag', models.ForeignKey(orm['dashboard_app.tag'], null=False)) + )) + db.create_unique('dashboard_app_testrun_tags', ['testrun_id', 'tag_id']) + + + def backwards(self, orm): + + # Deleting model 'Tag' + db.delete_table('dashboard_app_tag') + + # Removing M2M table for field tags on 'TestRun' + db.delete_table('dashboard_app_testrun_tags') + + + 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'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + '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.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.CharField', [], {'max_length': '32'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + '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', 'db_index': 'True'}) + }, + '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.CharField', [], {'max_length': '100', '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.CharField', [], {'max_length': '100'}), + 'units': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) + }, + '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'}) + } + } + + complete_apps = ['dashboard_app'] === added file 'dashboard_app/migrations/0008_auto__add_testingeffort.py' --- dashboard_app/migrations/0008_auto__add_testingeffort.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0008_auto__add_testingeffort.py 2011-09-27 13:06:00 +0000 @@ -0,0 +1,220 @@ +# encoding: 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): + + # Adding model 'TestingEffort' + db.create_table('dashboard_app_testingeffort', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project', self.gf('django.db.models.fields.related.ForeignKey')(related_name='testing_efforts', to=orm['lava_projects.Project'])), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal('dashboard_app', ['TestingEffort']) + + # Adding M2M table for field tags on 'TestingEffort' + db.create_table('dashboard_app_testingeffort_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('testingeffort', models.ForeignKey(orm['dashboard_app.testingeffort'], null=False)), + ('tag', models.ForeignKey(orm['dashboard_app.tag'], null=False)) + )) + db.create_unique('dashboard_app_testingeffort_tags', ['testingeffort_id', 'tag_id']) + + + def backwards(self, orm): + + # Deleting model 'TestingEffort' + db.delete_table('dashboard_app_testingeffort') + + # Removing M2M table for field tags on 'TestingEffort' + db.delete_table('dashboard_app_testingeffort_tags') + + + 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'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + '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.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.CharField', [], {'max_length': '32'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + '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', 'db_index': 'True'}) + }, + '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.CharField', [], {'max_length': '100', '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.CharField', [], {'max_length': '100'}), + 'units': ('django.db.models.fields.CharField', [], {'max_length': '100', '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'}) + }, + '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', 'db_index': 'True'}), + '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'] === added file 'dashboard_app/migrations/0009_auto__add_testrundenormalization.py' --- dashboard_app/migrations/0009_auto__add_testrundenormalization.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0009_auto__add_testrundenormalization.py 2011-09-28 02:28:43 +0000 @@ -0,0 +1,218 @@ +# encoding: 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): + + # Adding model 'TestRunDenormalization' + db.create_table('dashboard_app_testrundenormalization', ( + ('test_run', self.gf('django.db.models.fields.related.OneToOneField')(related_name='denormalization', unique=True, primary_key=True, to=orm['dashboard_app.TestRun'])), + ('count_pass', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('count_fail', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('count_skip', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('count_unknown', self.gf('django.db.models.fields.PositiveIntegerField')()), + )) + db.send_create_signal('dashboard_app', ['TestRunDenormalization']) + + + def backwards(self, orm): + + # Deleting model 'TestRunDenormalization' + db.delete_table('dashboard_app_testrundenormalization') + + + 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'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + '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.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.CharField', [], {'max_length': '32'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + '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', 'db_index': 'True'}) + }, + '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.CharField', [], {'max_length': '100', '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.CharField', [], {'max_length': '100'}), + 'units': ('django.db.models.fields.CharField', [], {'max_length': '100', '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']"}) + }, + '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', 'db_index': 'True'}), + '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'] === added file 'dashboard_app/migrations/0010_denormalize_test_run.py' --- dashboard_app/migrations/0010_denormalize_test_run.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0010_denormalize_test_run.py 2011-09-28 02:29:33 +0000 @@ -0,0 +1,235 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + # Result codes + RESULT_PASS = 0 + RESULT_FAIL = 1 + RESULT_SKIP = 2 + RESULT_UNKNOWN = 3 + # Code-to-name mapping + RESULT_MAP = { + RESULT_PASS: 'pass', + RESULT_FAIL: 'fail', + RESULT_SKIP: 'skip', + RESULT_UNKNOWN: 'unknown' + } + for test_run in orm.TestRun.objects.all(): + stats = test_run.test_results.order_by( + # Disable sorting + ).values( + 'result' # only get the result outcome (pass/fail/etc) + ).annotate( + count=models.Count('result') # Count number of items with this value + ) + # Translate the 0-4 element array into a 0-4 element dictionary + result = dict([ + (RESULT_MAP[item['result']], item['count']) + for item in stats]) + # Create a denormalized test run instance + orm.TestRunDenormalization.objects.create( + test_run=test_run, + count_pass=result.get('pass', 0), + count_fail=result.get('fail', 0), + count_skip=result.get('skip', 0), + count_unknown=result.get('unknown', 0)) + + def backwards(self, orm): + orm.TestRunDenormalization.objects.all().delete() + + 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'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + '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.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.CharField', [], {'max_length': '32'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + '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', 'db_index': 'True'}) + }, + '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.CharField', [], {'max_length': '100', '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.CharField', [], {'max_length': '100'}), + 'units': ('django.db.models.fields.CharField', [], {'max_length': '100', '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']"}) + }, + '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', 'db_index': 'True'}), + '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'] === modified file 'dashboard_app/models.py' --- dashboard_app/models.py 2011-09-14 12:38:36 +0000 +++ dashboard_app/models.py 2011-09-28 02:35:22 +0000 @@ -39,12 +39,16 @@ from django.utils.translation import ungettext from django_restricted_resource.models import RestrictedResource +from lava_projects.models import Project +from linaro_dashboard_bundle.io import DocumentIO from dashboard_app.helpers import BundleDeserializer -from dashboard_app.managers import BundleManager +from dashboard_app.managers import BundleManager, TestRunDenormalizationManager from dashboard_app.repositories import RepositoryItem from dashboard_app.repositories.data_report import DataReportRepository from dashboard_app.repositories.data_view import DataViewRepository +from dashboard_app.signals import bundle_was_deserialized + # Fix some django issues we ran into from dashboard_app.patches import patch @@ -397,6 +401,7 @@ return try: self._do_deserialize(prefer_evolution) + bundle_was_deserialized.send(sender=self, bundle=self) except Exception as ex: import_error = BundleDeserializationError.objects.get_or_create( bundle=self)[0] @@ -404,8 +409,10 @@ import_error.traceback = traceback.format_exc() import_error.save() else: - if self.deserialization_error.count(): - self.deserialization_error.get().delete() + try: + self.deserialization_error.delete() + except BundleDeserializationError.DoesNotExist: + pass self.is_deserialized = True self.save() @@ -446,6 +453,17 @@ finally: self.content.close() + def get_document_format(self): + self.content.open('rb') + try: + fmt, doc = DocumentIO.load(self.content) + return fmt + finally: + self.content.close() + + def get_serialization_format(self): + return "JSON" + class SanitizedBundle(object): @@ -485,7 +503,7 @@ The relevant logic for managing this is in the Bundle.deserialize() """ - bundle = models.ForeignKey( + bundle = models.OneToOneField( Bundle, primary_key = True, unique = True, @@ -740,6 +758,14 @@ attributes = generic.GenericRelation(NamedAttribute) + # Tags + + tags = models.ManyToManyField( + "Tag", + blank=True, + related_name='test_runs', + verbose_name=_(u"Tags")) + # Attachments attachments = generic.GenericRelation('Attachment') @@ -757,6 +783,34 @@ def get_permalink(self): return reverse("dashboard_app.views.redirect_to_test_run", args=[self.analyzer_assigned_uuid]) + def get_board(self): + """ + Return an associated Board device, if any. + """ + try: + return self.devices.filter(device_type="device.board").get() + except HardwareDevice.DoesNotExist: + pass + except HardwareDevice.MultipleObjectsReturned: + pass + + def get_results(self): + """ + Get all results efficiently + """ + return self.test_results.select_related( + "test_case", # explicit join on test_case which might be NULL + "test_run", # explicit join on test run, needed by all the get_absolute_url() methods + "test_run__bundle", # explicit join on bundle + "test_run__bundle__bundle_stream", # explicit join on bundle stream + ).order_by("relative_index") # sort as they showed up in the bundle + + def denormalize(self): + try: + self.denormalization + except TestRunDenormalization.DoesNotExist: + TestRunDenormalization.objects.create_from_test_run(self) + def _get_summary_results(self, factor=3): stats = self.test_results.values('result').annotate( count=models.Count('result')).order_by() @@ -776,6 +830,39 @@ ordering = ['-import_assigned_date'] +class TestRunDenormalization(models.Model): + """ + Denormalized model for test run + """ + + test_run = models.OneToOneField( + TestRun, + primary_key=True, + related_name="denormalization") + + count_pass = models.PositiveIntegerField( + null=False, + blank=False) + + count_fail = models.PositiveIntegerField( + null=False, + blank=False) + + count_skip = models.PositiveIntegerField( + null=False, + blank=False) + + count_unknown = models.PositiveIntegerField( + null=False, + blank=False) + + def count_all(self): + return (self.count_pass + self.count_fail + self.count_skip + + self.count_unknown) + + objects = TestRunDenormalizationManager() + + class Attachment(models.Model): """ Model for adding attachments to any other models. @@ -1302,3 +1389,53 @@ for attr in NamedAttribute.objects.filter( name='hwpack.type').values('value').distinct()] return hwpack_list + + +class Tag(models.Model): + """ + Tag used for marking test runs. + """ + name = models.SlugField( + verbose_name=_(u"Tag"), + max_length=256, + db_index=True, + unique=True) + + def __unicode__(self): + return self.name + + +class TestingEffort(models.Model): + """ + A collaborative effort to test something. + + Uses tags to associate with test runs. + """ + project = models.ForeignKey( + Project, + related_name="testing_efforts") + + name = models.CharField( + verbose_name=_(u"Name"), + max_length=100) + + description = models.TextField( + verbose_name=_(u"Description"), + help_text=_(u"Description of this testing effort")) + + tags = models.ManyToManyField( + Tag, + verbose_name=_(u"Tags"), + related_name="testing_efforts") + + def __unicode__(self): + return self.name + + @models.permalink + def get_absolute_url(self): + return ("dashboard_app.views.testing_effort_detail", [self.pk]) + + def get_test_runs(self): + return TestRun.objects.order_by( + ).filter( + tags__in=self.tags.all()) === added file 'dashboard_app/signals.py' --- dashboard_app/signals.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/signals.py 2011-09-27 13:13:53 +0000 @@ -0,0 +1,21 @@ +# Copyright (C) 2010, 2011 Linaro Limited +# +# Author: Zygmunt Krynicki +# +# This file is part of Launch Control. +# +# Launch Control is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License version 3 +# as published by the Free Software Foundation +# +# Launch Control is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Launch Control. If not, see . + +from django.dispatch import Signal + +bundle_was_deserialized = Signal(providing_args=['bundle']) === modified file 'dashboard_app/templates/dashboard_app/_extension_navigation.html' --- dashboard_app/templates/dashboard_app/_extension_navigation.html 2011-07-25 23:19:44 +0000 +++ dashboard_app/templates/dashboard_app/_extension_navigation.html 2011-09-28 00:35:41 +0000 @@ -5,6 +5,8 @@ >{% trans "Back to LAVA" %}
  • {% trans "Image Status" %}
  • +
  • {% trans "Testing efforts" %}
  • {% trans "Bundle Streams" %}
  • {% trans "Test" %} {% trans "Uploaded On" %} {% trans "Analyzed On" %} - {% trans "Pass" %} - {% trans "Fail" %} - {% trans "Skip" %} - {% trans "Unknown" %} @@ -19,12 +15,6 @@ {{ test_run.test }} {{ test_run.bundle.uploaded_on|date:"Y-m-d H:i:s" }} {{ test_run.analyzer_assigned_date|date:"Y-m-d H:i:s" }} - {% with test_run.get_summary_results as summary %} - {{ summary.pass|default:0 }} - {{ summary.fail|default:0 }} - {{ summary.skip|default:0 }} - {{ summary.unknown|default:0 }} - {% endwith %} {% endfor %} === modified file 'dashboard_app/templates/dashboard_app/attachment_list.html' --- dashboard_app/templates/dashboard_app/attachment_list.html 2011-07-12 02:34:12 +0000 +++ dashboard_app/templates/dashboard_app/attachment_list.html 2011-09-16 09:12:11 +0000 @@ -17,11 +17,17 @@ {{ attachment.content_filename }} - {{ attachment.content.size|filesizeformat }} + + {% if attachment.content %} + {{ attachment.content.size|filesizeformat }} + {% else %} + Not available + {% endif %} + {{ attachment.mime_type }} {% if attachment.public_url %} - public url + public URL {% endif %} === modified file 'dashboard_app/templates/dashboard_app/bundle_detail.html' --- dashboard_app/templates/dashboard_app/bundle_detail.html 2011-07-18 16:29:23 +0000 +++ dashboard_app/templates/dashboard_app/bundle_detail.html 2011-09-27 19:29:01 +0000 @@ -1,4 +1,4 @@ -{% extends "dashboard_app/_content.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load humanize %} {% load i18n %} {% load stylize %} @@ -10,21 +10,48 @@ {% endblock %} + +{% block sidebar %} +

    Permalink

    +

    You can navigate to this bundle, regardless of the bundle stream it is +located in, by using this permalink

    + +

    Upload details

    +{% if bundle.uploaded_by %} +

    This bundle was uploaded by {{bundle.uploaded_by}} on +{{bundle.uploaded_on}} ({{bundle.uploaded_on|timesince}} ago)

    +{% else %} +

    This bundle was uploaded by an anonymous contributor on +{{bundle.uploaded_on}} ({{bundle.uploaded_on|timesince}} ago)

    +{% endif %} + +

    File details

    +
    +
    Declared file name:
    +
    {{ bundle.content_filename }}
    +
    Content SHA1:
    +
    {{ bundle.content_sha1 }}
    +
    Content size:
    +
    {{ bundle.content.size|filesizeformat }}
    +
    + +

    Storage and format

    +
    +
    Document format:
    +
    {{bundle.get_document_format}}
    +
    Serialization format:
    +
    {{ bundle.get_serialization_format}}
    +
    + +

    Tips

    +

    You can download this bundle with the following command:

    +
    + lava-dashboard-tool --dashboard-url=http://{{site.domain}}{% url lava.api_handler %} get {{bundle.content_sha1}} +
    +{% endblock %} + + {% block content %} -
    -
    - - {% trans "Note:" %} - {% blocktrans %} - You can navigate to this bundle, regardless of the bundle stream it is - located in, by using this - {% endblocktrans %} - {% trans "permalink" %} -
    -
    -
    @@ -81,7 +80,7 @@ {% endif %} - + {% endfor %} === modified file 'dashboard_app/templates/dashboard_app/front_page_snippet.html' --- dashboard_app/templates/dashboard_app/front_page_snippet.html 2011-08-18 15:04:13 +0000 +++ dashboard_app/templates/dashboard_app/front_page_snippet.html 2011-09-19 11:20:07 +0000 @@ -1,10 +1,29 @@

    Click on image description to see automatic QA report

    {% regroup dashboard.interesting_images by rootfs_type as rootfs_list %} {% for rootfs in rootfs_list %} -

    {{ rootfs.grouper|capfirst }}

    - + +
    +

    {{ rootfs.grouper|capfirst }}

    + +
    {% endfor %} +
    === modified file 'dashboard_app/templates/dashboard_app/test_result_detail.html' --- dashboard_app/templates/dashboard_app/test_result_detail.html 2011-07-18 16:29:23 +0000 +++ dashboard_app/templates/dashboard_app/test_result_detail.html 2011-09-27 21:00:31 +0000 @@ -16,11 +16,11 @@
    -{% if test_result.test_run.test_results.count > 1 %} +{% if test_result.test_run.get_results.count > 1 %}

    Other results

    Results from the same test run are available here

    - - {% for test_result in test_run.test_results.select_related.all %} - - - - - - - {% endfor %} - -
    {{ bundle.is_deserialized|yesno }}{% if bundle.deserialization_error.get %}yes{% endif %}{% if bundle.deserialization_error %}yes{% endif %}
    {% trans "Result" %} {% trans "Measurement" %}
    {{ test_result.relative_index }}{{ test_result.test_case|default_if_none:"Not specified" }} - - {{ test_result.get_result_display }} - {{ test_result.get_result_display }} - {{ test_result.measurement|default_if_none:"Not specified" }} {{ test_result.units }}
    - {% endblock %} + + + {% for test_result in test_run.get_results %} + + {{ test_result.relative_index }} + {{ test_result.test_case|default_if_none:"Not specified" }} + + + {{ test_result.get_result_display }} + {{ test_result.get_result_display }} + + {{ test_result.measurement|default_if_none:"Not specified" }} {{ test_result.units }} + + {% endfor %} + + +{% endblock %} {% block sidebar %} -
    -
    {% trans "Test Run UUID" %}
    -
    {{ test_run.analyzer_assigned_uuid }} {% trans "permalink" %}
    -
    {% trans "Test Name" %}
    -
    {{ test_run.test }}
    -
    {% trans "OS Distribution" %}
    +

    Permalink

    +

    You can navigate to this test run, regardless of the bundle stream it is +located in, by using this permalink.

    + +

    Test run details

    +
    +
    {% trans "Test Name:" %}
    +
    {{ test_run.test.test_id }} +

    This is the identifier of the test that was invoked. A + test is a collection of test cases. Test is also the smallest piece of code + that can be invoked by lava-test.

    +
    +
    {% trans "Test Run UUID:" %}
    +
    {{ test_run.analyzer_assigned_uuid }} +

    This is a globally unique identifier that was assigned + by the log analyzer. Running the same test multiple times results in + different values of this identifier. The dashboard uses this identifier to + refer to a particular test run. It is preserved across different LAVA + installations, that is, if you pull test results (as bundles) from one system + to another this identifier remains intact

    +
    +
    {% trans "Bundle SHA1:" %}
    +
    {{ test_run.bundle.content_sha1 }} +

    This is the SHA1 hash of the bundle that contains this test run.

    +
    + +
    {% trans "Attachments:" %}
    +
    +
      + {% for attachment in test_run.attachments.all %} +
    • {{ attachment }} + {% if attachment.content %} + ({{ attachment.content.size|filesizeformat }}) + {% endif %} +
    • + {% empty %} + {% trans "There are no attachments associated with this test run." %} + {% endfor %} +
    +

    LAVA can store attachments associated with a + particular test run. Those attachments can be used to store log files, crash + dumps, screen shots or other useful test artifacts.

    +
    + +
    {% trans "Tags:" %}
    +
    +
      + {% for tag in test_run.tags.all %} +
    • {{ tag }}
    • + {% empty %} + {% trans "There are no tags associated with this test run." %} + {% endfor %} +
    +

    LAVA can store tags associated with a particular + test run. Tags are simple strings like project-foo-prerelase-testing + or linaro-image-2011-09-27. Tags can be used by the testing effort + feature to group results together.

    +
    +
    + +

    Software context

    +
    +
    {% trans "OS Distribution:" %}
    {{ test_run.sw_image_desc|default:"Unspecified" }}
    -
    {% trans "Bundle SHA1" %}
    -
    {{ test_run.bundle.content_sha1 }}
    -
    {% trans "Time check performed" %}
    -
    {{ test_run.time_check_performed|yesno }}
    +
    {% trans "Software packages:" %}
    +
    See all {{ test_run.packages.all.count }} software packages +

    LAVA keeps track of all the software packages (such as + Debian packages managed with dpkg) that were installed prior to running a + test. This information can help you track down errors caused by a particular + buggy dependency

    +
    +
    {% trans "Software sources:" %}
    +
    See all {{ test_run.sources.all.count }} source references +

    LAVA can track more data than just package name and + version. You can track precise software information such as the version + control system branch or repository, revision or tag name and more

    +
    +
    + +

    Hardware context

    +
    +
    {% trans "Board:" %}
    +
    {{ test_run.get_board|default_if_none:"There are no boards associated with this test run" }}
    +
    {% trans "Other devices:" %}
    +
    See all {{ test_run.devices.all.count }} devices +

    LAVA keeps track of the hardware that was used for + testing. This can help cross-reference benchmarks and identify + hardware-specific issues.

    +
    +
    + +

    Custom attributes

    +

    LAVA can store arbitrary key-value attributes associated +with each test run (and separately, each test result)

    +
      + {% for attribute in test_run.attributes.all %} +
    • {{ attribute.name }} = {{ attribute.value }}
    • + {% empty %} + {% trans "There are no attributes associated with this test run." %} + {% endfor %} +
    + +

    Time stamps

    +

    There are three different timestamps +associated with each test run. They are explained below.

    +
    {% trans "Log analyzed on:" %}
    {{ test_run.analyzer_assigned_date|naturalday }} {{ test_run.analyzer_assigned_date|time }} -
    -
    {% trans "Data uploaded on:" %}
    + ({{ test_run.analyzer_assigned_date|timesince }} ago) +

    This is the moment this that this test run's artifacts + (such as log files and other output) were processed by the log analyzer. + Typically the analyzer is a part of lava-test framework and test output is + analyzed on right on the device so this time may not be trusted, see below + for the description of time check performed

    + +
    {% trans "Time check performed" %}
    +
    {{ test_run.time_check_performed|yesno }} +

    The value no indicates that the log analyzer + was not certain that the time and date is accurate.

    +
    +
    {% trans "Data imported on:" %}
    {{ test_run.import_assigned_date|naturalday }} {{ test_run.import_assigned_date|time }} + ({{ test_run.import_assigned_date|timesince }} ago) +

    This is the moment this test run entry was created in + the LAVA database. It can differ from upload date if there were any initial + deserialization problems and the data was deserialized later.

    -
    {% trans "Other information:" %}
    -
    {% trans "Hardware context" %} ({{ test_run.devices.all.count }} devices)
    -
    {% trans "Software context" %} ({{ test_run.packages.all.count }} packages, {{ test_run.sources.all.count }} sources)
    -
    {% trans "Attachments" %} ({{ test_run.attachments.count }})
    +
    {% trans "Data uploaded on:" %}
    +
    + {{ test_run.bundle.uploaded_on|naturalday }} + {{ test_run.bundle.uploaded_on|time }} + ({{ test_run.bundle.uploaded_on|timesince }} ago) +

    This is the moment this data was first uploaded to LAVA + (as a serialized bundle).

    {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_list.html' --- dashboard_app/templates/dashboard_app/test_run_list.html 2011-07-12 02:34:12 +0000 +++ dashboard_app/templates/dashboard_app/test_run_list.html 2011-09-27 21:26:42 +0000 @@ -53,7 +53,6 @@ $("#master-toolbar-splice").remove(); $("#master-toolbar").addClass("ui-widget-header ui-corner-tl ui-corner-tr").css( "padding", "5pt").css("text-align", "center"); - new FixedHeader(oTable); }); {% include "dashboard_app/_test_run_list_table.html" %} === added file 'dashboard_app/templates/dashboard_app/testing_effort_detail.html' --- dashboard_app/templates/dashboard_app/testing_effort_detail.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/testing_effort_detail.html 2011-09-28 02:30:30 +0000 @@ -0,0 +1,105 @@ +{% extends "dashboard_app/_content_with_sidebar.html" %} +{% load humanize %} +{% load markup %} +{% load i18n %} + + +{% block extrahead %} +{{ block.super }} + + + + +{% endblock %} + + +{% block sidebar %} +

    {{ effort }}

    +{{ effort.description|markdown }} +

    Tags

    +

    The concept of testing efforts is based on using +tags to associate test runs with a common goal or task. This testing effort +will list any test runs that have any of the following tags +present.

    +
      + {% for tag in effort.tags.all %} +
    • {{ tag }}
    • + {% empty %} + This testing effort has not defined any tags yet, tests + runs will not show up unless this is done + {% endfor %} +
    +{% endblock %} + + +{% block content %} + + + {% regroup test_run_list|dictsortreversed:"analyzer_assigned_date" by analyzer_assigned_date|date:"Y-m-d" as test_run_cluster_list %} + {% for test_run_cluster in test_run_cluster_list %} + + + + + + + + {% for test_run in test_run_cluster.list %} + + + {% with test_run.denormalization as denormalization %} + + + + + + {% endwith %} + + {% endfor %} + {% endfor %} +
    Tests ran on {{ test_run_cluster.grouper }}PassFailSkipUnknown
    {{ test_run.test }} + {% spaceless %} +
    +
    +
    +
    + {% endspaceless %} +
    {{ denormalization.count_pass }}{{ denormalization.count_fail }}{{ denormalization.count_skip }}{{ denormalization.count_unknown }}
    +{% endblock %} === added file 'dashboard_app/templates/dashboard_app/testing_effort_list.html' --- dashboard_app/templates/dashboard_app/testing_effort_list.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/testing_effort_list.html 2011-09-28 00:35:41 +0000 @@ -0,0 +1,19 @@ +{% extends "dashboard_app/_content.html" %} +{% load humanize %} +{% load markup %} +{% load i18n %} + + +{% block content %} +

    Testing efforts

    +{% regroup effort_list by project as effort_group_list %} +{% for effort_group in effort_group_list %} +

    In project {{ effort_group.grouper }}

    +
    + {% for effort in effort_group.list %} +
    {{ effort }}
    +
    {{ effort.description|markdown }}
    + {% endfor %} +
    +{% endfor %} +{% endblock %} === modified file 'dashboard_app/tests/models/bundle.py' --- dashboard_app/tests/models/bundle.py 2011-06-23 10:09:42 +0000 +++ dashboard_app/tests/models/bundle.py 2011-09-26 12:55:16 +0000 @@ -87,7 +87,7 @@ self.mocker.replay() self.bundle.deserialize(False) self.assertFalse(self.bundle.is_deserialized) - self.assertEqual(self.bundle.deserialization_error.get().error_message, "boom") + self.assertEqual(self.bundle.deserialization_error.error_message, "boom") def test_deserialize_ignores_deserialized_bundles(self): # just reply as we're not using mocker in this test case === modified file 'dashboard_app/tests/other/deserialization.py' --- dashboard_app/tests/other/deserialization.py 2011-07-20 23:06:30 +0000 +++ dashboard_app/tests/other/deserialization.py 2011-09-27 06:52:52 +0000 @@ -516,6 +516,48 @@ ("attr2", "value2")])) +class Bundle13DeserializerSuccessTests(TestCase): + + json_text = ''' + { + "format": "Dashboard Bundle Format 1.3", + "test_runs": [ + { + "test_id": "some_test_id", + "analyzer_assigned_uuid": "1ab86b36-c23d-11df-a81b-002163936223", + "analyzer_assigned_date": "2010-12-31T23:59:59Z", + "time_check_performed": true, + "test_results": [ ], + "tags": [ + "tag-1", + "tag-2" + ] + } + ] + } + ''' + + def setUp(self): + super(Bundle13DeserializerSuccessTests, self).setUp() + self.s_bundle = fixtures.create_bundle( + '/anonymous/', self.json_text, 'bundle.json') + # Decompose the data here + self.s_bundle.deserialize(prefer_evolution=False) + if not self.s_bundle.is_deserialized: + raise AssertionError("Deserialzation failed:" + self.s_bundle.deserialization_error.get().traceback) + # Link to test run for easier testing + self.s_test = self.s_bundle.test_runs.get() + + def tearDown(self): + self.s_bundle.delete_files() + super(Bundle13DeserializerSuccessTests, self).tearDown() + + def test_deserialize_tags(self): + self.assertEqual(self.s_test.tags.count(), 2) + self.assertEqual([tag.name for tag in self.s_test.tags.order_by('name').all()], + ["tag-1", "tag-2"]) + + class BundleDeserializerFailureTestCase(TestCaseWithScenarios): scenarios = [ @@ -690,7 +732,7 @@ # better than not knowing what really happened and hiding other # potential bugs that would otherwise be masked here. self.assertIn( - self.s_bundle.deserialization_error.get().error_message, [ + self.s_bundle.deserialization_error.error_message, [ 'A test with UUID 1ab86b36-c23d-11df-a81b-002163936223 already exists', 'column analyzer_assigned_uuid is not unique', u'duplicate key value violates unique constraint ' === modified file 'dashboard_app/urls.py' --- dashboard_app/urls.py 2011-08-24 04:03:56 +0000 +++ dashboard_app/urls.py 2011-09-28 00:35:41 +0000 @@ -65,4 +65,6 @@ url(r'^image_status/$', 'image_status_list'), url(r'^image_status/(?P[a-zA-Z0-9_-]+)\+(?P[a-zA-Z0-9_-]+)/$', 'image_status_detail'), url(r'^image_status/(?P[a-zA-Z0-9_-]+)\+(?P[a-zA-Z0-9_-]+)/test-history/(?P[^/]+)/$', 'image_test_history'), + url(r'^efforts/$', 'testing_effort_list'), + url(r'^efforts/(?P[0-9]+)/$', 'testing_effort_detail'), ) === modified file 'dashboard_app/views.py' --- dashboard_app/views.py 2011-08-24 04:39:48 +0000 +++ dashboard_app/views.py 2011-09-28 02:30:30 +0000 @@ -22,6 +22,7 @@ import json +from django.contrib.sites.models import Site from django.db.models.manager import Manager from django.db.models.query import QuerySet from django.http import Http404, HttpResponse @@ -39,6 +40,7 @@ Test, TestResult, TestRun, + TestingEffort, ) from dashboard_app.bread_crumbs import BreadCrumb, BreadCrumbTrail @@ -126,7 +128,7 @@ ) return object_list( request, - queryset=bundle_stream.bundles.all().order_by('-uploaded_on'), + queryset=bundle_stream.bundles.select_related('bundle_stream', 'deserialization_error').order_by('-uploaded_on'), template_name="dashboard_app/bundle_list.html", template_object_name="bundle", extra_context={ @@ -163,6 +165,7 @@ bundle_detail, pathname=pathname, content_sha1=content_sha1), + "site": Site.objects.get_current(), "bundle_stream": bundle_stream }) @@ -226,7 +229,22 @@ test_run_list, pathname=pathname), "test_run_list": TestRun.objects.filter( - bundle__bundle_stream=bundle_stream), + bundle__bundle_stream=bundle_stream + ).order_by( # clean any implicit ordering + ).select_related( + "test", + "bundle", + "bundle__bundle_stream", + "test_results" + ).only( + "analyzer_assigned_uuid", # needed by TestRun.__unicode__ + "analyzer_assigned_date", # used by the view + "bundle__uploaded_on", # needed by Bundle.get_absolute_url + "bundle__content_sha1", # needed by Bundle.get_absolute_url + "bundle__bundle_stream__pathname", # Needed by TestRun.get_absolute_url + "test__name", # needed by Test.__unicode__ + "test__test_id", # needed by Test.__unicode__ + ), "bundle_stream": bundle_stream, }, RequestContext(request) ) @@ -554,3 +572,37 @@ test=test, test_id=test_id), }, RequestContext(request)) + + +@BreadCrumb("Testing efforts", parent=index) +def testing_effort_list(request): + return render_to_response( + "dashboard_app/testing_effort_list.html", { + 'effort_list': TestingEffort.objects.all( + ).order_by('name'), + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + testing_effort_list), + }, RequestContext(request)) + + +@BreadCrumb( + "{effort}", + parent=testing_effort_list, + needs=["pk"]) +def testing_effort_detail(request, pk): + effort = get_object_or_404(TestingEffort, pk=pk) + return render_to_response( + "dashboard_app/testing_effort_detail.html", { + 'effort': effort, + 'test_run_list': effort.get_test_runs( + ).select_related( + 'denormalization', + 'bundle', + 'bundle__bundle_stream', + 'test', + ), + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + testing_effort_detail, + effort=effort, + pk=pk), + }, RequestContext(request)) === modified file 'dashboard_app/xmlrpc.py' --- dashboard_app/xmlrpc.py 2011-08-17 10:33:41 +0000 +++ dashboard_app/xmlrpc.py 2011-09-26 21:12:40 +0000 @@ -443,7 +443,7 @@ if bundle.is_deserialized is False: raise xmlrpclib.Fault( errors.CONFLICT, - bundle.deserialization_error.get().error_message) + bundle.deserialization_error.error_message) return True def make_stream(self, pathname, name):