From patchwork Mon Apr 8 01:06:15 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Senthil Kumaran X-Patchwork-Id: 15959 Return-Path: X-Original-To: linaro@staging.patches.linaro.org Delivered-To: linaro@staging.patches.linaro.org Received: from mail-gh0-f197.google.com (mail-gh0-f197.google.com [209.85.160.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id A340127752 for ; Mon, 8 Apr 2013 01:06:30 +0000 (UTC) Received: by mail-gh0-f197.google.com with SMTP id r17sf7505650ghr.0 for ; Sun, 07 Apr 2013 18:06:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-beenthere:x-received:received-spf:x-received :x-forwarded-to:x-forwarded-for:delivered-to:x-received:received-spf :mime-version:x-launchpad-project:x-launchpad-branch :x-launchpad-message-rationale:x-launchpad-branch-revision-number :x-launchpad-notification-type:to:from:subject:message-id:date :reply-to:sender:errors-to:precedence:x-generated-by :x-launchpad-hash:x-gm-message-state:x-original-sender :x-original-authentication-results:mailing-list:list-id :x-google-group-id:list-post:list-help:list-archive:list-unsubscribe :content-type; bh=+yp1xL7WrK+JRCStiU2VZ874HrR6GresZkL6iJbnKms=; b=WbN90/qYZCYzh0fGUV965sM3ZYIPQ0kzv5gptqTyMiIo940mj+7+g6ciSDeavY3cN7 53yGZ/KPcHoYzPdLyFvr/LCihGL856AKpIMGkXh7HE+A5ipK4ezW0ZJ+gFe4VkUSY2Mz dET2pUSO0Ao76f7cqxgIJ8rOVkAheX22YdPM/81DCfcDwFF+I3Sf8xIz4Pjr1A9ywcEe Z/Ml3miJuuV5IbAHmjV6tReld6YMjA8KtmYZjgtyptxPjuDH0RiprD7w+/o7aTgsTJnL aHdIeKqHi2HmNqXg0POvdcWFP0gdhBSu1tAnoXNWZ+PJOI8SiCRCXd/eR+p49ZQD9N0F KIvw== X-Received: by 10.224.65.6 with SMTP id g6mr4769058qai.4.1365383177238; Sun, 07 Apr 2013 18:06:17 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.85.72 with SMTP id f8ls54108qez.98.gmail; Sun, 07 Apr 2013 18:06:17 -0700 (PDT) X-Received: by 10.220.223.80 with SMTP id ij16mr14455475vcb.28.1365383177106; Sun, 07 Apr 2013 18:06:17 -0700 (PDT) Received: from mail-vc0-f181.google.com (mail-vc0-f181.google.com [209.85.220.181]) by mx.google.com with ESMTPS id da5si8561816vdc.154.2013.04.07.18.06.17 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 07 Apr 2013 18:06:17 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.181 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.181; Received: by mail-vc0-f181.google.com with SMTP id ia10so637225vcb.26 for ; Sun, 07 Apr 2013 18:06:17 -0700 (PDT) X-Received: by 10.220.153.143 with SMTP id k15mr14510121vcw.13.1365383176895; Sun, 07 Apr 2013 18:06:16 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.58.85.136 with SMTP id h8csp26113vez; Sun, 7 Apr 2013 18:06:16 -0700 (PDT) X-Received: by 10.180.73.6 with SMTP id h6mr9619495wiv.27.1365383175829; Sun, 07 Apr 2013 18:06:15 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id b3si4077053wix.75.2013.04.07.18.06.15 (version=TLSv1 cipher=RC4-SHA bits=128/128); Sun, 07 Apr 2013 18:06:15 -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; Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1UP0XH-0002aF-7u for ; Mon, 08 Apr 2013 01:06:15 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 2C879E0615 for ; Mon, 8 Apr 2013 01:06:15 +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: 401 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 401: Support test definitions in lava dashboard model. Message-Id: <20130408010615.28248.48595.launchpad@ackee.canonical.com> Date: Mon, 08 Apr 2013 01:06:15 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: list X-Generated-By: Launchpad (canonical.com); Revision="16550"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 6fabdfbd49c0756756f0914e666fcb4bd6d6d816 X-Gm-Message-State: ALoCoQmEBIf50FRg1TP5tYVDi42P5WYtSiWYTe5/AKNfHOM3KSRtRoTbkcBRveWDh630ZvcRSvAL X-Original-Sender: noreply@launchpad.net X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.181 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Merge authors: Senthil Kumaran S (stylesen) Related merge proposals: https://code.launchpad.net/~stylesen/lava-dashboard/support-test-definitions/+merge/145137 proposed by: Senthil Kumaran S (stylesen) review: Approve - Antonio Terceiro (terceiro) review: Needs Fixing - Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 401 [merge] committer: Senthil Kumaran branch nick: trunk timestamp: Mon 2013-04-08 00:06:46 +0530 message: Support test definitions in lava dashboard model. added: dashboard_app/migrations/0029_auto__add_testdefinition.py dashboard_app/templates/dashboard_app/add_test_definition.html dashboard_app/templates/dashboard_app/test_definition.html modified: dashboard_app/admin.py dashboard_app/extension.py dashboard_app/helpers.py dashboard_app/models.py dashboard_app/urls.py dashboard_app/views/__init__.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/admin.py' --- dashboard_app/admin.py 2013-01-11 16:25:54 +0000 +++ dashboard_app/admin.py 2013-04-01 08:50:05 +0000 @@ -47,6 +47,7 @@ TestRunFilter, TestRunFilterAttribute, TestRunFilterSubscription, + TestDefinition, ) @@ -203,6 +204,9 @@ save_as = True +class TestDefinitionAdmin(admin.ModelAdmin): + list_display = ('name', 'version') + admin.site.register(Attachment) admin.site.register(Bundle, BundleAdmin) admin.site.register(BundleDeserializationError, BundleDeserializationErrorAdmin) @@ -221,3 +225,4 @@ admin.site.register(TestRunFilter, TestRunFilterAdmin) admin.site.register(TestRunFilterSubscription) admin.site.register(Tag) +admin.site.register(TestDefinition, TestDefinitionAdmin) === modified file 'dashboard_app/extension.py' --- dashboard_app/extension.py 2013-01-31 02:52:51 +0000 +++ dashboard_app/extension.py 2013-03-25 19:39:46 +0000 @@ -49,6 +49,7 @@ subm.append(Menu("Data Views", reverse("dashboard_app.views.data_view_list"))) if not settings.DATAREPORTS_HIDE: subm.append(Menu("Reports", reverse("dashboard_app.views.report_list"))) + subm.append(Menu("Test Definitions", reverse("dashboard_app.views.test_definition"))) return menu === modified file 'dashboard_app/helpers.py' --- dashboard_app/helpers.py 2012-11-16 01:01:22 +0000 +++ dashboard_app/helpers.py 2013-04-01 11:04:15 +0000 @@ -8,6 +8,7 @@ import time from django.core.files.base import ContentFile +from django.core.exceptions import ObjectDoesNotExist from django.db import connection, transaction, IntegrityError from linaro_dashboard_bundle.errors import DocumentFormatError from linaro_dashboard_bundle.evolution import DocumentEvolution @@ -742,6 +743,50 @@ self._import_test_result_attachments(c_test_result, s_test_result) +class BundleFormatImporter_1_6(BundleFormatImporter_1_5): + """ + IFormatImporter subclass capable of loading "Dashboard Bundle Format 1.6" + """ + + def _import_testdef(self, c_test_id, c_testdef_metadata): + """ + Import dashboard_app.models.TestDefinition into the database + based on a client-side description of a TestRun metadata. + """ + from dashboard_app.models import TestDefinition + + testdef_meta = { + 'name': c_test_id, + 'version': c_testdef_metadata.get("version"), + 'description': c_testdef_metadata.get("description"), + 'format': c_testdef_metadata.get("format"), + 'location': c_testdef_metadata.get("location"), + 'url': c_testdef_metadata.get("url"), + 'environment': c_testdef_metadata.get("environment"), + 'target_os': c_testdef_metadata.get("os"), + 'target_dev_types': c_testdef_metadata.get("devices"), + } + + try: + s_testdef = TestDefinition.objects.get(name=c_test_id) + # Do not try to update name since it is unique, hence + # pop it from the dictionary. + testdef_meta.pop('name', None) + TestDefinition.objects.filter(name=c_test_id).update( + **testdef_meta) + except ObjectDoesNotExist: + s_testdef = TestDefinition.objects.create(**testdef_meta) + s_testdef.save() + + def _import_test_results(self, c_test_run, s_test_run): + from dashboard_app.models import TestResult + super(BundleFormatImporter_1_6, self)._import_test_results(c_test_run, + s_test_run) + if c_test_run.get("testdef_metadata"): + self._import_testdef(c_test_run["test_id"], + c_test_run["testdef_metadata"]) + + class BundleDeserializer(object): """ Helper class for de-serializing JSON bundle content into database models @@ -755,6 +800,7 @@ "Dashboard Bundle Format 1.3": BundleFormatImporter_1_3, "Dashboard Bundle Format 1.4": BundleFormatImporter_1_4, "Dashboard Bundle Format 1.5": BundleFormatImporter_1_5, + "Dashboard Bundle Format 1.6": BundleFormatImporter_1_6, } def deserialize(self, s_bundle, prefer_evolution): === added file 'dashboard_app/migrations/0029_auto__add_testdefinition.py' --- dashboard_app/migrations/0029_auto__add_testdefinition.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0029_auto__add_testdefinition.py 2013-04-01 10:16:02 +0000 @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'TestDefinition' + db.create_table('dashboard_app_testdefinition', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=512)), + ('version', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('description', self.gf('django.db.models.fields.TextField')()), + ('format', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('location', self.gf('django.db.models.fields.CharField')(default='LOCAL', max_length=64)), + ('url', self.gf('django.db.models.fields.CharField')(max_length=1024)), + ('environment', self.gf('django.db.models.fields.CharField')(max_length=256)), + ('target_os', self.gf('django.db.models.fields.CharField')(max_length=512)), + ('target_dev_types', self.gf('django.db.models.fields.CharField')(max_length=512)), + ('content', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True)), + ('mime_type', self.gf('django.db.models.fields.CharField')(default='text/plain', max_length=64)), + )) + db.send_create_signal('dashboard_app', ['TestDefinition']) + + + def backwards(self, orm): + # Deleting model 'TestDefinition' + db.delete_table('dashboard_app_testdefinition') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'dashboard_app.attachment': { + 'Meta': {'object_name': 'Attachment'}, + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'}) + }, + 'dashboard_app.bundle': { + 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'}, + '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}), + '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}), + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}), + 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) + }, + 'dashboard_app.bundledeserializationerror': { + 'Meta': {'object_name': 'BundleDeserializationError'}, + 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}), + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'}) + }, + 'dashboard_app.bundlestream': { + 'Meta': {'object_name': 'BundleStream'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'dashboard_app.hardwaredevice': { + 'Meta': {'object_name': 'HardwareDevice'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'dashboard_app.image': { + 'Meta': {'object_name': 'Image'}, + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'}) + }, + 'dashboard_app.imageset': { + 'Meta': {'object_name': 'ImageSet'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) + }, + 'dashboard_app.launchpadbug': { + 'Meta': {'object_name': 'LaunchpadBug'}, + 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"}) + }, + 'dashboard_app.namedattribute': { + 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'value': ('django.db.models.fields.TextField', [], {}) + }, + 'dashboard_app.pmqabundlestream': { + 'Meta': {'object_name': 'PMQABundleStream'}, + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.BundleStream']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'dashboard_app.softwarepackage': { + 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'dashboard_app.softwarepackagescratch': { + 'Meta': {'object_name': 'SoftwarePackageScratch'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'dashboard_app.softwaresource': { + 'Meta': {'object_name': 'SoftwareSource'}, + 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'dashboard_app.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'}) + }, + 'dashboard_app.test': { + 'Meta': {'object_name': 'Test'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) + }, + 'dashboard_app.testcase': { + 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}), + 'test_case_id': ('django.db.models.fields.TextField', [], {}), + 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'dashboard_app.testdefinition': { + 'Meta': {'object_name': 'TestDefinition'}, + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'environment': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'format': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'default': "'LOCAL'", 'max_length': '64'}), + 'mime_type': ('django.db.models.fields.CharField', [], {'default': "'text/plain'", 'max_length': '64'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}), + 'target_dev_types': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'target_os': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '256'}) + }, + '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'}), + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}), + 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}), + 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}), + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}), + 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'dashboard_app.testrundenormalization': { + 'Meta': {'object_name': 'TestRunDenormalization'}, + 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"}) + }, + 'dashboard_app.testrunfilter': { + 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'}, + 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'dashboard_app.testrunfilterattribute': { + 'Meta': {'object_name': 'TestRunFilterAttribute'}, + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'dashboard_app.testrunfiltersubscription': { + 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'}, + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'dashboard_app.testrunfiltertest': { + 'Meta': {'object_name': 'TestRunFilterTest'}, + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"}) + }, + 'dashboard_app.testrunfiltertestcase': { + 'Meta': {'object_name': 'TestRunFilterTestCase'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}), + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"}) + } + } + + complete_apps = ['dashboard_app'] \ No newline at end of file === modified file 'dashboard_app/models.py' --- dashboard_app/models.py 2013-02-07 22:30:46 +0000 +++ dashboard_app/models.py 2013-04-01 08:50:05 +0000 @@ -701,6 +701,82 @@ return self.test_results.filter(result=TestResult.RESULT_FAIL).count() +class TestDefinition(models.Model): + """ + Model for representing test definitions. + + Test Definition are in YAML format. + """ + LOCATION_CHOICES = ( + ('LOCAL', 'Local'), + ('URL', 'URL'), + ('GIT', 'GIT Repo'), + ('BZR', 'BZR Repo'), + ) + + name = models.CharField( + max_length = 512, + verbose_name = _("Name"), + unique = True, + help_text = _help_max_length(512)) + + version = models.CharField( + max_length=256, + verbose_name = _("Version"), + help_text = _help_max_length(256)) + + description = models.TextField( + verbose_name = _("Description")) + + format = models.CharField( + max_length = 128, + verbose_name = _("Format"), + help_text = _help_max_length(128)) + + location = models.CharField( + max_length = 64, + verbose_name = _("Location"), + choices = LOCATION_CHOICES, + default = 'LOCAL') + + url = models.CharField( + verbose_name = _(u"URL"), + max_length = 1024, + blank = False, + help_text = _help_max_length(1024)) + + environment = models.CharField( + max_length = 256, + verbose_name = _("Environment"), + help_text = _help_max_length(256)) + + target_os = models.CharField( + max_length = 512, + verbose_name = _("Operating Systems"), + help_text = _help_max_length(512)) + + target_dev_types = models.CharField( + max_length = 512, + verbose_name = _("Device types"), + help_text = _help_max_length(512)) + + content = models.FileField( + verbose_name = _(u"Upload Test Definition"), + help_text = _(u"Test definition file"), + upload_to = 'testdef', + blank = True, + null = True) + + mime_type = models.CharField( + verbose_name = _(u"MIME type"), + default = 'text/plain', + max_length = 64, + help_text = _help_max_length(64)) + + def __unicode__(self): + return self.name + + class SoftwareSource(models.Model): """ Model for representing source reference of a particular project === added file 'dashboard_app/templates/dashboard_app/add_test_definition.html' --- dashboard_app/templates/dashboard_app/add_test_definition.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/add_test_definition.html 2013-03-25 21:49:16 +0000 @@ -0,0 +1,25 @@ +{% extends "dashboard_app/_content_with_sidebar.html" %} +{% load i18n %} +{% load stylize %} + +{% block extrahead %} + +{% endblock %} + +{% block sidebar %} +

Actions

+ +{% endblock %} + +{% block content %} +
+ {% csrf_token %} + {{ form.as_table }}
+ +
+{% endblock %} === added file 'dashboard_app/templates/dashboard_app/test_definition.html' --- dashboard_app/templates/dashboard_app/test_definition.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/test_definition.html 2013-03-25 21:39:58 +0000 @@ -0,0 +1,23 @@ +{% extends "dashboard_app/_content_with_sidebar.html" %} +{% load i18n %} +{% load stylize %} +{% load django_tables2 %} + +{% block extrahead %} + + +{% endblock %} + +{% block sidebar %} +

Actions

+ +{% endblock %} + +{% block content %} + {% render_table testdefinition_table %} +{% endblock %} === modified file 'dashboard_app/urls.py' --- dashboard_app/urls.py 2013-01-11 17:07:32 +0000 +++ dashboard_app/urls.py 2013-01-15 14:09:48 +0000 @@ -70,4 +70,7 @@ url(r'^image-reports/(?P[A-Za-z0-9_-]+)$', 'images.image_report_detail'), url(r'^api/link-bug-to-testrun', 'images.link_bug_to_testrun'), url(r'^api/unlink-bug-and-testrun', 'images.unlink_bug_and_testrun'), + url(r'^test-definition/add_test_definition', 'add_test_definition'), + url(r'^test-definition/$', 'test_definition'), + url(r'^testdefinition_table_json$', 'testdefinition_table_json'), ) === modified file 'dashboard_app/views/__init__.py' --- dashboard_app/views/__init__.py 2013-01-11 16:25:54 +0000 +++ dashboard_app/views/__init__.py 2013-04-01 08:50:05 +0000 @@ -40,6 +40,8 @@ from django.template import RequestContext, loader from django.utils.safestring import mark_safe from django.views.generic.list_detail import object_list, object_detail +from django.forms import ModelForm +from django import forms from django_tables2 import Attrs, Column, TemplateColumn @@ -60,6 +62,7 @@ Test, TestResult, TestRun, + TestDefinition, ) @@ -673,3 +676,51 @@ request.user, content_sha1=content_sha1) return redirect_to(request, bundle, trailing) + + +class TestDefinitionTable(DataTablesTable): + name = Column() + version = Column() + location = Column() + description = Column() + def get_queryset(self): + return TestDefinition.objects.all() + + +def testdefinition_table_json(request): + return TestDefinitionTable.json(request) + + +@BreadCrumb("Test Definitions", parent=index) +def test_definition(request): + return render_to_response( + "dashboard_app/test_definition.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(test_definition), + "testdefinition_table": TestDefinitionTable( + 'testdeflist', + reverse(testdefinition_table_json)) + }, RequestContext(request)) + + +class AddTestDefForm(ModelForm): + class Meta: + model = TestDefinition + fields = ('name', 'version', 'description', 'format', 'location', + 'url', 'environment', 'target_os', 'target_dev_types', + 'content', 'mime_type') + +@BreadCrumb("Add Test Definition", parent=index) +def add_test_definition(request): + if request.method == 'POST': + form = AddTestDefForm(request.POST) + if form.is_valid(): + form.save() + return HttpResponseRedirect('/dashboard/test-definition/') + else: + form = AddTestDefForm() + return render_to_response( + "dashboard_app/add_test_definition.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + add_test_definition), + "form": form, + }, RequestContext(request)) === modified file 'dashboard_app/xmlrpc.py' --- dashboard_app/xmlrpc.py 2013-01-15 00:30:25 +0000 +++ dashboard_app/xmlrpc.py 2013-04-04 03:33:50 +0000 @@ -43,6 +43,7 @@ DataView, Test, TestRunFilter, + TestDefinition, ) @@ -882,6 +883,51 @@ matches = matches[:100] return [match.serializable() for match in matches] + @xml_rpc_signature('str') + def get_test_definitions(self, os=None, device=None, environment=None): + """ + Name + ---- + `get_test_definitions` ([`os`[, `device`[, `environment`]]]) + + Description + ----------- + Get the name and url of all the test definitions. + + Arguments + --------- + `os`: string + The type of operating system the retrieved test definitions should + apply to. + + `device`: string + The type of device the retrieved test definitions should apply to. + + `environment`: string + The type of test environment the retrieved test definitions should + apply to. + + Return value + ------------ + This function returns an XML-RPC structure of test definition name and + URL where the test definition exists. + """ + testdefs = {} + tds = TestDefinition.objects.all() + + if os: + tds = tds.filter(target_os__contains=os) + + if device: + tds = tds.filter(target_dev_types__contains=device) + + if environment: + tds = tds.filter(environment__contains=environment) + + for testdef in tds: + testdefs[testdef.name] = testdef.url + return testdefs + # Mapper used by the legacy URL legacy_mapper = Mapper() legacy_mapper.register_introspection_methods()