From patchwork Fri Jan 11 02:31:12 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael-Doyle Hudson X-Patchwork-Id: 13966 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 E106E23E21 for ; Fri, 11 Jan 2013 02:31:15 +0000 (UTC) Received: from mail-vc0-f178.google.com (mail-vc0-f178.google.com [209.85.220.178]) by fiordland.canonical.com (Postfix) with ESMTP id 3D1AFA183A4 for ; Fri, 11 Jan 2013 02:31:15 +0000 (UTC) Received: by mail-vc0-f178.google.com with SMTP id l6so1021589vcl.9 for ; Thu, 10 Jan 2013 18:31:14 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:content-type: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; bh=gJZwAoYtd+o7d8cD0tFnhnalTw8sj53e96HidN4qNPY=; b=aHKF8+mFnTCFobFYV553PcG1TMOyB6GReNmhMHFkYtQKP3xFKm+3QIsD6yos/xwBwR GNrebiVOWq3xS5Eirc3ADJ8WkvjH0dbk7P62GbCDjjBe5xIWCv+FFl6NZ+5T5t7wamSI n7w2Zql6yUctIrlmFKuNzJpAlRkuJZnuaDI3oDKPt1maHQLohKvSJ300C6avlzu0vkYW Srg8OKD+f8tryi/3N+gWIJmk2xo+7TH+rFran8lcew0NMK2KP1NzSFcAjG1rUUqpjPBD YOM6SvDOG8UvfkkzkL3wu38CbQS1Kgk9khDJCLQS+iOm+DJ6+42np6uzo1GuY6gnd9iR 6i/g== X-Received: by 10.52.70.205 with SMTP id o13mr80852787vdu.75.1357871474741; Thu, 10 Jan 2013 18:31:14 -0800 (PST) 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.58.145.101 with SMTP id st5csp92671veb; Thu, 10 Jan 2013 18:31:13 -0800 (PST) X-Received: by 10.194.78.162 with SMTP id c2mr117532024wjx.46.1357871473411; Thu, 10 Jan 2013 18:31:13 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id w10si6443389wje.70.2013.01.10.18.31.12 (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 10 Jan 2013 18:31:13 -0800 (PST) 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 1TtUOm-00061c-IM for ; Fri, 11 Jan 2013 02:31:12 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 75A6AE0135 for ; Fri, 11 Jan 2013 02:31:12 +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: 386 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 386: a view to show the pwrmgmt results in a useful way Message-Id: <20130111023112.3110.65948.launchpad@ackee.canonical.com> Date: Fri, 11 Jan 2013 02:31:12 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="16412"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: bfb5a3e761aa62a894633e09a323ad0f53ac7a46 X-Gm-Message-State: ALoCoQmWFvqgLztY0E+RpPU5lmDCmS6WRoYexpu16YgdUuppe9sXzwg17+R5sQ+L4BOwDkqyXvQ6 Merge authors: Michael Hudson-Doyle (mwhudson) Related merge proposals: https://code.launchpad.net/~mwhudson/lava-dashboard/pm-qa-views/+merge/142626 proposed by: Michael Hudson-Doyle (mwhudson) review: Approve - Andy Doan (doanac) ------------------------------------------------------------ revno: 386 [merge] committer: Michael Hudson-Doyle branch nick: trunk timestamp: Fri 2013-01-11 15:30:38 +1300 message: a view to show the pwrmgmt results in a useful way added: dashboard_app/migrations/0026_auto__add_pmqabundlestream.py dashboard_app/templates/dashboard_app/pmqa-view.html dashboard_app/templates/dashboard_app/pmqa_filter.html dashboard_app/views/pmqa.py modified: dashboard_app/admin.py dashboard_app/filters.py dashboard_app/models.py dashboard_app/urls.py dashboard_app/views/filters/tables.py dashboard_app/views/filters/views.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 2012-09-25 22:40:20 +0000 +++ dashboard_app/admin.py 2013-01-09 02:57:13 +0000 @@ -36,6 +36,7 @@ ImageSet, LaunchpadBug, NamedAttribute, + PMQABundleStream, SoftwarePackage, SoftwareSource, Tag, @@ -95,7 +96,7 @@ the bundle stream itself. """ my_modeladmin = BundleAdmin(Bundle, modeladmin.admin_site) - my_modeladmin.delete_selected_confirmation_template = 'admin/dashboard_app/cleanup_selected_bundle_confirmation.html' + my_modeladmin.delete_selected_confirmation_template = 'admin/dashboard_app/cleanup_selected_bundle_confirmation.html' my_queryset = None if request.POST.get('post'): # handle bundles selected_bundles = request.POST.getlist('_selected_action') @@ -106,7 +107,7 @@ my_queryset = bundle_stream.bundles.all() else: my_queryset = my_queryset | bundle_stream.bundles.all() - return delete_selected(my_modeladmin, request, my_queryset) + return delete_selected(my_modeladmin, request, my_queryset) cleanup_bundle_stream_selected.short_description = "Clean up selected %(verbose_name_plural)s" @@ -215,6 +216,7 @@ admin.site.register(Image, ImageAdmin) admin.site.register(ImageSet, ImageSetAdmin) admin.site.register(LaunchpadBug, LaunchpadBugAdmin) +admin.site.register(PMQABundleStream) admin.site.register(SoftwarePackage, SoftwarePackageAdmin) admin.site.register(SoftwareSource, SoftwareSourceAdmin) admin.site.register(Test, TestAdmin) === modified file 'dashboard_app/filters.py' --- dashboard_app/filters.py 2013-01-08 01:27:34 +0000 +++ dashboard_app/filters.py 2013-01-09 00:15:10 +0000 @@ -111,7 +111,7 @@ pass_count = None # Only filled out for filters that dont specify a test result_count = None # Ditto - def serializable(self): + def serializable(self, include_links=True): cases_by_test = {} for test in self.filter_data['tests']: # Not right if filter specifies a test more than once... @@ -136,8 +136,9 @@ 'skip': 0, 'unknown': 0, 'total': 0, - 'link': url_prefix + tr.get_absolute_url(), } + if include_links: + d['link'] = url_prefix + tr.get_absolute_url() if tr.test in cases_by_test: results = d['specific_results'] = [] for result in self.specific_results: @@ -146,8 +147,9 @@ result_data = { 'test_case_id': result.test_case.test_case_id, 'result': result_str, - 'link': url_prefix + result.get_absolute_url() } + if include_links: + result_data['link'] = url_prefix + result.get_absolute_url() if result.measurement is not None: result_data['measurement'] = str(result.measurement) if result.units is not None: @@ -221,7 +223,7 @@ self.queryset = queryset self.filter_data = filter_data self.prefetch_related = prefetch_related - if filter_data['build_number_attribute']: + if filter_data.get('build_number_attribute'): self.key = 'build_number' self.key_name = 'Build' else: @@ -372,9 +374,9 @@ ).values('object_id'))) test_condition = None - for test in filter_data['tests']: + for test in filter_data.get('tests', []): case_ids = set() - for test_case in test['test_cases']: + for test_case in test.get('test_cases', []): case_ids.add(test_case.id) if case_ids: q = models.Q( @@ -389,12 +391,12 @@ if test_condition: conditions.append(test_condition) - if filter_data['uploaded_by']: + if filter_data.get('uploaded_by'): conditions.append(models.Q(bundle__uploaded_by=filter_data['uploaded_by'])) testruns = TestRun.objects.filter(*conditions) - if filter_data['build_number_attribute']: + if filter_data.get('build_number_attribute'): if descending: ob = ['-build_number'] else: === added file 'dashboard_app/migrations/0026_auto__add_pmqabundlestream.py' --- dashboard_app/migrations/0026_auto__add_pmqabundlestream.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/migrations/0026_auto__add_pmqabundlestream.py 2013-01-09 02:52:50 +0000 @@ -0,0 +1,277 @@ +# -*- 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 'PMQABundleStream' + db.create_table('dashboard_app_pmqabundlestream', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('bundle_stream', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['dashboard_app.BundleStream'])), + )) + db.send_create_signal('dashboard_app', ['PMQABundleStream']) + + + def backwards(self, orm): + # Deleting model 'PMQABundleStream' + db.delete_table('dashboard_app_pmqabundlestream') + + + 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': '64', 'blank': 'True'}), + 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'dashboard_app.testcase': { + 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}), + 'test_case_id': ('django.db.models.fields.TextField', [], {}), + 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'dashboard_app.testingeffort': { + 'Meta': {'object_name': 'TestingEffort'}, + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'testing_efforts'", 'to': "orm['lava_projects.Project']"}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'testing_efforts'", 'symmetrical': 'False', 'to': "orm['dashboard_app.Tag']"}) + }, + 'dashboard_app.testresult': { + 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'}, + '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}), + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}), + 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'dashboard_app.testrun': { + 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'}, + 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}), + 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), + 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}), + 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + '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']"}) + }, + 'lava_projects.project': { + 'Meta': {'object_name': 'Project'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}), + 'is_aggregate': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'registered_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'to': "orm['auth.User']"}), + 'registered_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['dashboard_app'] \ No newline at end of file === modified file 'dashboard_app/models.py' --- dashboard_app/models.py 2012-12-16 20:23:44 +0000 +++ dashboard_app/models.py 2013-01-09 02:52:50 +0000 @@ -1772,3 +1772,8 @@ bundle_was_deserialized.connect(send_bundle_notifications) + + +class PMQABundleStream(models.Model): + + bundle_stream = models.ForeignKey(BundleStream, related_name='+') === added file 'dashboard_app/templates/dashboard_app/pmqa-view.html' --- dashboard_app/templates/dashboard_app/pmqa-view.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/pmqa-view.html 2013-01-10 01:56:51 +0000 @@ -0,0 +1,87 @@ +{% extends "dashboard_app/_content.html" %} + +{% block extrahead %} +{{ block.super }} + +{% endblock %} + +{% block content %} +

PMQA view

+ + + + + + {% for device_type in device_types_with_results %} + {% if device_type.width %} + + {% endif %} + {% endfor %} + + + + {% for device_type in device_types_with_results %} + + {% endfor %} + + + + {% for device_type in device_types_with_results %} + + {% endfor %} + + + + {% for prefix, board_results in results %} + + + {% for board_result in board_results %} + + {% endfor %} + + {% endfor %} + +
+ + {{ device_type.sn }} +
+ + {{ device_type.device_type }} +
+ Test prefix + + {{ device_type.date }} (build {{device_type.build}}) +
+ {% if device_type.last_difference %} + A different result was last seen in build {{ device_type.last_difference.0 }} + {% else %} + No different result has been seen + {% endif %} +
+ {{ prefix }} + + {% if board_result.present %} + {{ board_result.pass }} / {{ board_result.total }} + {% endif %} +
+ +{% endblock %} === added file 'dashboard_app/templates/dashboard_app/pmqa_filter.html' --- dashboard_app/templates/dashboard_app/pmqa_filter.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/pmqa_filter.html 2013-01-10 01:56:51 +0000 @@ -0,0 +1,29 @@ +{% extends "dashboard_app/_content.html" %} +{% load i18n %} +{% load django_tables2 %} + +{% block extrahead %} +{{ block.super }} + + +{% endblock %} + +{% block content %} + +

PMQA results for {{ bundle_stream }} on {{ device_type }}

+{% render_table filter_table %} + +

+ + + + +

+ +{% endblock %} === modified file 'dashboard_app/urls.py' --- dashboard_app/urls.py 2013-01-08 23:44:15 +0000 +++ dashboard_app/urls.py 2013-01-11 02:26:37 +0000 @@ -82,6 +82,10 @@ url(r'^efforts/(?P[0-9]+)/update/$', 'testing_effort_update'), url(r'^efforts/(?P[a-z0-9-]+)/\+new/$', 'testing_effort_create'), url(r'^image-reports/$', 'images.image_report_list'), + url(r'^pmqa$', 'pmqa.pmqa_view'), + url(r'^pmqa(?P/[a-zA-Z0-9/._-]+/)(?P[a-zA-Z0-9-_]+)$', 'pmqa.pmqa_filter_view'), + url(r'^pmqa(?P/[a-zA-Z0-9/._-]+/)(?P[a-zA-Z0-9-_]+)/json$', 'pmqa.pmqa_filter_view_json'), + url(r'^pmqa(?P/[a-zA-Z0-9/._-]+/)(?P[a-zA-Z0-9-_]+)/\+compare/(?P[0-9]+)/(?P[0-9]+)$', 'pmqa.compare_pmqa_results'), 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'), === modified file 'dashboard_app/views/filters/tables.py' --- dashboard_app/views/filters/tables.py 2013-01-08 23:11:01 +0000 +++ dashboard_app/views/filters/tables.py 2013-01-10 01:56:51 +0000 @@ -216,8 +216,8 @@ passes = Column(accessor='pass_count') total = Column(accessor='result_count') - def get_queryset(self, user, filter): - return evaluate_filter(user, filter.as_data()) + def get_queryset(self, user, filter_data): + return evaluate_filter(user, filter_data) datatable_opts = { "sPaginationType": "full_numbers", @@ -227,9 +227,6 @@ class FilterPreviewTable(FilterTable): - def get_queryset(self, user, form): - return evaluate_filter(user, form.as_data()) - datatable_opts = FilterTable.datatable_opts.copy() datatable_opts.update({ "iDisplayLength": 10, === modified file 'dashboard_app/views/filters/views.py' --- dashboard_app/views/filters/views.py 2013-01-10 00:34:58 +0000 +++ dashboard_app/views/filters/views.py 2013-01-10 01:56:51 +0000 @@ -83,7 +83,7 @@ def filter_json(request, username, name): filter = TestRunFilter.objects.get(owner__username=username, name=name) - return FilterTable.json(request, params=(request.user, filter)) + return FilterTable.json(request, params=(request.user, filter.as_data())) @@ -95,7 +95,7 @@ form = TestRunFilterForm(request.user, request.GET, instance=filter) if not form.is_valid(): raise ValidationError(str(form.errors)) - return FilterPreviewTable.json(request, params=(request.user, form)) + return FilterPreviewTable.json(request, params=(request.user, form.as_data())) @BreadCrumb("Filter ~{username}/{name}", parent=filters_list, needs=['username', 'name']) @@ -118,7 +118,7 @@ 'filter_table': FilterTable( "filter-table", reverse(filter_json, kwargs=dict(username=username, name=name)), - params=(request.user, filter)), + params=(request.user, filter.as_data())), 'bread_crumb_trail': BreadCrumbTrail.leading_to( filter_detail, name=name, username=username), }, RequestContext(request) @@ -177,7 +177,7 @@ 'table': FilterPreviewTable( 'filter-preview', reverse(filter_preview_json) + '?' + c.urlencode(), - params=(request.user, form)), + params=(request.user, form.as_data())), }, RequestContext(request)) else: form = TestRunFilterForm(request.user, instance=instance) @@ -323,16 +323,8 @@ return differences -@BreadCrumb( - "Comparing builds {tag1} and {tag2}", - parent=filter_detail, - needs=['username', 'name', 'tag1', 'tag2']) -def compare_matches(request, username, name, tag1, tag2): - filter = TestRunFilter.objects.get(owner__username=username, name=name) - if not filter.public and filter.owner != request.user: - raise PermissionDenied() - filter_data = filter.as_data() - matches = evaluate_filter(request.user, filter_data) +def compare_filter_matches(user, filter_data, tag1, tag2): + matches = evaluate_filter(user, filter_data) match1, match2 = matches.with_tags(tag1, tag2) test_cases_for_test_id = {} for test in filter_data['tests']: @@ -388,6 +380,19 @@ tr=tr, tag=tag, cases=cases)) + return test_run_info + + +@BreadCrumb( + "Comparing builds {tag1} and {tag2}", + parent=filter_detail, + needs=['username', 'name', 'tag1', 'tag2']) +def compare_matches(request, username, name, tag1, tag2): + filter = TestRunFilter.objects.get(owner__username=username, name=name) + if not filter.public and filter.owner != request.user: + raise PermissionDenied() + filter_data = filter.as_data() + test_run_info = compare_filter_matches(request.user, filter_data, tag1, tag2) return render_to_response( "dashboard_app/filter_compare_matches.html", { 'test_run_info': test_run_info, === added file 'dashboard_app/views/pmqa.py' --- dashboard_app/views/pmqa.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/views/pmqa.py 2013-01-11 02:26:37 +0000 @@ -0,0 +1,203 @@ +# Copyright (C) 2010-2012 Linaro Limited +# +# Author: Michael Hudson-Doyle +# +# 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.contrib.auth.decorators import login_required +from django.core.urlresolvers import reverse +from django.shortcuts import render_to_response +from django.template import RequestContext + +from lava_server.bread_crumbs import ( + BreadCrumb, + BreadCrumbTrail, +) + +from dashboard_app.filters import evaluate_filter +from dashboard_app.models import ( + BundleStream, + PMQABundleStream, + Test, +) +from dashboard_app.views import index +from dashboard_app.views.filters.tables import ( + FilterTable, + ) +from dashboard_app.views.filters.views import ( + compare_filter_matches, + ) + + +@BreadCrumb("PM QA view", parent=index) +@login_required +def pmqa_view(request): + test = Test.objects.get(test_id='pwrmgmt') + device_types_with_results = [] + prefix__device_type_result = {} + + from lava_scheduler_app.models import DeviceType + device_types = list(DeviceType.objects.filter(display=True).values_list('name', flat=True)) + bundle_streams = [pmqabs.bundle_stream for pmqabs in PMQABundleStream.objects.all()] + bundle_streams.sort(key=lambda bs:bs.pathname) + + for bs in bundle_streams: + c = len(device_types_with_results) + for device_type in device_types: + if device_type.startswith('rtsm'): + continue + filter_data = { + 'bundle_streams': [bs], + 'attributes': [('target.device_type', device_type)], + 'tests': [{'test':test, 'test_cases':[]}], + 'build_number_attribute': 'build.id', + } + matches = list(evaluate_filter(request.user, filter_data)[:50]) + if matches: + match = matches[0] + m0 = match.serializable(include_links=False) + del m0['tag'] + last_difference = None + for m in matches[1:]: + m1 = m.serializable(include_links=False) + del m1['tag'] + if m1 != m0: + last_difference = ( + m.tag, + reverse(compare_pmqa_results, + kwargs={ + 'pathname': bs.pathname, + 'device_type': device_type, + 'build1': str(m.tag), + 'build2': str(match.tag), + })) + break + tr = match.test_runs[0] + device_types_with_results.append({ + 'sn': bs.slug, + 'device_type': device_type, + 'date': tr.bundle.uploaded_on, + 'build': match.tag, + 'link': tr.get_absolute_url(), + 'width': 0, + 'last_difference': last_difference, + 'filter_link': reverse(pmqa_filter_view, kwargs=dict( + pathname=bs.pathname, device_type=device_type)), + }) + for result in tr.test_results.all().select_related('test_case'): + prefix = result.test_case.test_case_id.split('.')[0] + device_type__result = prefix__device_type_result.setdefault(prefix, {}) + d = device_type__result.setdefault(device_type, {'pass': 0, 'total': 0, 'present':True}) + if result.result == result.RESULT_PASS: + d['pass'] += 1 + d['total'] += 1 + if len(device_types_with_results) > c: + device_types_with_results[c]['width'] = len(device_types_with_results) - c + results = [] + prefixes = sorted(prefix__device_type_result) + for prefix in prefixes: + board_results = [] + for d in device_types_with_results: + cell_data = prefix__device_type_result[prefix].get(d['device_type']) + if cell_data is not None: + if cell_data['total'] == cell_data['pass']: + cell_data['css_class'] = 'pass' + else: + cell_data['css_class'] = 'fail' + else: + cell_data = { + 'css_class': 'missing', + 'present': False, + } + board_results.append(cell_data) + results.append((prefix, board_results)) + return render_to_response( + "dashboard_app/pmqa-view.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(pmqa_view), + 'device_types_with_results': device_types_with_results, + 'results': results, + }, RequestContext(request)) + + +def pmqa_filter_view_json(request, pathname, device_type): + test = Test.objects.get(test_id='pwrmgmt') + bs = BundleStream.objects.get(pathname=pathname) + filter_data = { + 'bundle_streams': [bs], + 'attributes': [('target.device_type', device_type)], + 'tests': [{'test':test, 'test_cases':[]}], + 'build_number_attribute': 'build.id', + } + return FilterTable.json(request, params=(request.user, filter_data)) + + +@BreadCrumb( + "PMQA results for {pathname} on {device_type}", + parent=pmqa_view, + needs=['pathname', 'device_type']) +def pmqa_filter_view(request, pathname, device_type): + test = Test.objects.get(test_id='pwrmgmt') + bs = BundleStream.objects.get(pathname=pathname) + filter_data = { + 'bundle_streams': [bs], + 'attributes': [('target.device_type', device_type)], + 'tests': [{'test':test, 'test_cases':[]}], + 'build_number_attribute': 'build.id', + } + return render_to_response( + "dashboard_app/pmqa_filter.html", { + 'filter_table': FilterTable( + "filter-table", + reverse( + pmqa_filter_view_json, + kwargs=dict( + pathname=pathname, + device_type=device_type)), + params=(request.user, filter_data)), + 'bundle_stream': bs.slug, + 'device_type': device_type, + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + pmqa_filter_view, + pathname=pathname, + device_type=device_type), + }, RequestContext(request)) + + +@BreadCrumb( + "Comparing builds {build1} and {build2}", + parent=pmqa_filter_view, + needs=['pathname', 'device_type', 'build1', 'build2']) +def compare_pmqa_results(request, pathname, device_type, build1, build2): + test = Test.objects.get(test_id='pwrmgmt') + bs = BundleStream.objects.get(pathname=pathname) + filter_data = { + 'bundle_streams': [bs], + 'attributes': [('target.device_type', device_type)], + 'tests': [{'test':test, 'test_cases':[]}], + 'build_number_attribute': 'build.id', + } + test_run_info = compare_filter_matches(request.user, filter_data, build1, build2) + return render_to_response( + "dashboard_app/filter_compare_matches.html", { + 'test_run_info': test_run_info, + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + compare_pmqa_results, + pathname=pathname, + device_type=device_type, + build1=build1, + build2=build2), + }, RequestContext(request)) +