From patchwork Tue Mar 20 21:29:11 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael-Doyle Hudson X-Patchwork-Id: 7376 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 BBD3123DE1 for ; Tue, 20 Mar 2012 21:29:14 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 484F0A18188 for ; Tue, 20 Mar 2012 21:29:14 +0000 (UTC) Received: by iage36 with SMTP id e36so767599iag.11 for ; Tue, 20 Mar 2012 14:29:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to: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=L3VEfAbtXsTQaFPeKZEOw4/ZpfMBJ8wOhyT0fQLWINA=; b=WxQemsB25KVqPhplNsGw2rr2nxtYpoxqHsJPcYYz1u/IZMDBz1qD7PWiVdNW+E4Ayh /8g4JeY/HwSICHSf06HFNkODg/VeZ8xl9DwNrNF3mNHu7E59+VP9juAM/tMlCK/EoyWc ilSjXo6axbPVrQqDqyt3zeFnHHgUxMpUGPOvTBucJUvP3faJJwe1Kwiop8i+h4XignXC THCGK7mwn0JQIk1VsfRvVOg3YUg39rxLdmzfS6dYDVGkLo2DEuQVRRVB7bgSYe3ldkX0 jOW+qlk1wUiDJqlljZZAZ6xyTLBch/p6fJkMc4DMlBeBKoPEJWDbOC8Ew8qtrCBEL7Q4 +ehA== Received: by 10.50.214.36 with SMTP id nx4mr10028574igc.2.1332278953660; Tue, 20 Mar 2012 14:29:13 -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.231.203.79 with SMTP id fh15csp13335ibb; Tue, 20 Mar 2012 14:29:13 -0700 (PDT) Received: by 10.180.80.9 with SMTP id n9mr3278944wix.4.1332278952437; Tue, 20 Mar 2012 14:29:12 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id h56si2958124wed.94.2012.03.20.14.29.12 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 20 Mar 2012 14:29:12 -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 1SA6cB-0000Wk-T2 for ; Tue, 20 Mar 2012 21:29:11 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id CCA0BE00BD for ; Tue, 20 Mar 2012 21:29:11 +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: 307 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 307: convert some tables to use ajax pagination Message-Id: <20120320212911.12177.10419.launchpad@ackee.canonical.com> Date: Tue, 20 Mar 2012 21:29:11 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14969"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: d175dd2e89d583ce6453f37b067fe14a982810be X-Gm-Message-State: ALoCoQkDzRm86VgUrjFSGD9w/1nol9yfpP4Q8zvc84hDgF8k3A0nmYndptpgKrfH2lyKC2LZdZaS Merge authors: Michael Hudson-Doyle (mwhudson) Related merge proposals: https://code.launchpad.net/~mwhudson/lava-dashboard/use-tables-code/+merge/97795 proposed by: Michael Hudson-Doyle (mwhudson) review: Approve - Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 307 [merge] committer: Michael Hudson-Doyle branch nick: trunk timestamp: Wed 2012-03-21 10:27:34 +1300 message: convert some tables to use ajax pagination modified: dashboard_app/templates/dashboard_app/bundle_list.html dashboard_app/templates/dashboard_app/bundle_stream_list.html dashboard_app/templates/dashboard_app/test_run_detail.html dashboard_app/templates/dashboard_app/test_run_list.html dashboard_app/urls.py dashboard_app/views.py doc/changes.rst --- 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/templates/dashboard_app/bundle_list.html' --- dashboard_app/templates/dashboard_app/bundle_list.html 2011-12-15 16:13:43 +0000 +++ dashboard_app/templates/dashboard_app/bundle_list.html 2012-03-12 21:00:15 +0000 @@ -1,4 +1,6 @@ {% extends "dashboard_app/_content_with_sidebar.html" %} + +{% load django_tables2 %} {% load i18n %} {% load humanize %} @@ -32,14 +34,6 @@ - - - - - - - - - - - - - - {% for bundle in bundle_list %} - - - - - - - - - - {% endfor %} - -
{% trans "Bundle Name" %}{% trans "Passes" %}{% trans "Fails " %}{% trans "Total Results" %}{% trans "Uploaded On" %}{% trans "Uploaded By" %}{% trans "Deserialized?" %}
{{ bundle.content_filename }}{{ bundle.get_summary_results.pass }}{{ bundle.get_summary_results.fail }}{{ bundle.get_summary_results.total }}{{ bundle.uploaded_on|date:"Y-m-d H:i:s"}} - {% if bundle.uploaded_by %} - {{ bundle.uploaded_by }} - {% else %} - {% trans "anonymous user" %} - {% endif %} - {{ bundle.is_deserialized|yesno }}
+ + +{% render_table bundle_table %} + {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/bundle_stream_list.html' --- dashboard_app/templates/dashboard_app/bundle_stream_list.html 2011-07-12 02:34:12 +0000 +++ dashboard_app/templates/dashboard_app/bundle_stream_list.html 2012-03-12 03:58:38 +0000 @@ -1,38 +1,14 @@ {% extends "dashboard_app/_content_with_sidebar.html" %} + +{% load django_tables2 %} {% load i18n %} {% load pagination_tags %} {% block content %} - - - - - - - - - - - - {% for bundle_stream in bundle_stream_list %} - - - - - - - {% endfor %} - -
PathnameNameNumber of test runsNumber of bundles
{{ bundle_stream.pathname }}{{ bundle_stream.name|default:"not set" }}{{ bundle_stream.get_test_run_count }}{{ bundle_stream.bundles.count}}
+ +{% render_table bundle_stream_table %} + {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_detail.html' --- dashboard_app/templates/dashboard_app/test_run_detail.html 2011-09-27 20:58:16 +0000 +++ dashboard_app/templates/dashboard_app/test_run_detail.html 2012-03-12 05:02:49 +0000 @@ -1,43 +1,14 @@ {% extends "dashboard_app/_content_with_sidebar.html" %} + +{% load django_tables2 %} {% load i18n %} {% load humanize %} {% block content %} - - - - - - - - - - - - {% for test_result in test_run.get_results %} - - - - - - - {% endfor %} - -
#{% trans "Test case" %}{% 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 }}
+ +{% render_table test_table %} + {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_list.html' --- dashboard_app/templates/dashboard_app/test_run_list.html 2011-09-27 21:26:42 +0000 +++ dashboard_app/templates/dashboard_app/test_run_list.html 2012-03-16 03:31:30 +0000 @@ -1,4 +1,6 @@ {% extends "dashboard_app/_content_with_sidebar.html" %} + +{% load django_tables2 %} {% load i18n %} {% load humanize %} @@ -55,7 +57,7 @@ "padding", "5pt").css("text-align", "center"); }); -{% include "dashboard_app/_test_run_list_table.html" %} +{% render_table test_run_table %} {% endblock %} === modified file 'dashboard_app/urls.py' --- dashboard_app/urls.py 2011-10-06 12:03:00 +0000 +++ dashboard_app/urls.py 2012-03-16 03:31:30 +0000 @@ -46,16 +46,20 @@ 'mapper': legacy_mapper, 'template_name': 'dashboard_app/api.html'}), url(r'^streams/$', 'bundle_stream_list'), + url(r'^streams/json$', 'bundle_stream_list_json'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/$', 'bundle_list'), + url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/json$', 'bundle_list_table_json'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/$', 'bundle_detail'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/json$', 'bundle_json'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/$', 'test_run_detail'), + url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/json$', 'test_run_detail_test_json'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/attachments$', 'attachment_list'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/attachments/(?P[0-9]+)/$', 'attachment_detail'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/result/(?P[0-9]+)/$', 'test_result_detail'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/hardware-context/$', 'test_run_hardware_context'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)bundles/(?P[0-9a-z]+)/(?P[a-zA-Z0-9-]+)/software-context/$', 'test_run_software_context'), url(r'^streams(?P/[a-zA-Z0-9/._-]+)test-runs/$', 'test_run_list'), + url(r'^streams(?P/[a-zA-Z0-9/._-]+)test-runs/json$', 'test_run_list_json'), url(r'^permalink/test-run/(?P[a-zA-Z0-9-]+)/$', 'redirect_to_test_run'), url(r'^permalink/test-run/(?P[a-zA-Z0-9-]+)/(?P.*)$', 'redirect_to_test_run'), url(r'^permalink/test-result/(?P[a-zA-Z0-9-]+)/(?P[0-9]+)/$', 'redirect_to_test_result'), === modified file 'dashboard_app/views.py' --- dashboard_app/views.py 2011-12-15 16:13:43 +0000 +++ dashboard_app/views.py 2012-03-20 21:19:58 +0000 @@ -25,14 +25,24 @@ from django.contrib.auth.decorators import login_required from django.contrib.sites.models import Site +from django.core.urlresolvers import reverse from django.db.models.manager import Manager from django.db.models.query import QuerySet from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.template import RequestContext, loader -from django.views.generic.create_update import create_object +from django.utils.safestring import mark_safe from django.views.generic.list_detail import object_list, object_detail +from django_tables2 import Attrs, Column, TemplateColumn + +from lava.utils.data_tables.tables import DataTablesTable +from lava_server.views import index as lava_index +from lava_server.bread_crumbs import ( + BreadCrumb, + BreadCrumbTrail, +) + from dashboard_app.models import ( Attachment, Bundle, @@ -46,11 +56,6 @@ TestRun, TestingEffort, ) -from lava_server.views import index as lava_index -from lava_server.bread_crumbs import ( - BreadCrumb, - BreadCrumbTrail, -) def _get_queryset(klass): @@ -99,6 +104,37 @@ }, RequestContext(request)) + + +class BundleStreamTable(DataTablesTable): + + pathname = TemplateColumn( + '' + '{{ record.pathname }}') + name = TemplateColumn( + '{{ record.name|default:"not set" }}') + number_of_test_runs = TemplateColumn( + '' + '{{ record.get_test_run_count }}') + number_of_bundles = TemplateColumn( + '' + '{{ record.bundles.count}}') + + def get_queryset(self, user): + return BundleStream.objects.accessible_by_principal(user) + + datatable_opts = { + 'iDisplayLength': 25, + 'sPaginationType': "full_numbers", + } + + searchable_columns = ['pathname', 'name'] + + +def bundle_stream_list_json(request): + return BundleStreamTable.json(request, params=(request.user,)) + + @BreadCrumb("Bundle Streams", parent=index) def bundle_stream_list(request): """ @@ -108,7 +144,9 @@ 'dashboard_app/bundle_stream_list.html', { 'bread_crumb_trail': BreadCrumbTrail.leading_to( bundle_stream_list), - "bundle_stream_list": BundleStream.objects.accessible_by_principal(request.user).order_by('pathname'), + "bundle_stream_table": BundleStreamTable( + 'bundle-stream-table', reverse(bundle_stream_list_json), + params=(request.user,)), 'has_personal_streams': ( request.user.is_authenticated() and BundleStream.objects.filter(user=request.user).count() > 0), @@ -120,6 +158,52 @@ ) +class BundleTable(DataTablesTable): + + content_filename = TemplateColumn( + '' + '{{ record.content_filename }}', + verbose_name="bundle name") + + passes = TemplateColumn('{{ record.get_summary_results.pass }}') + fails = TemplateColumn('{{ record.get_summary_results.pass }}') + total_results = TemplateColumn('{{ record.get_summary_results.total }}') + + uploaded_on = TemplateColumn('{{ record.uploaded_on|date:"Y-m-d H:i:s"}}') + uploaded_by = TemplateColumn(''' + {% load i18n %} + {% if record.uploaded_by %} + {{ record.uploaded_by }} + {% else %} + {% trans "anonymous user" %} + {% endif %}''') + deserializaled = TemplateColumn('{{ record.is_deserialized|yesno }}') + + def get_queryset(self, bundle_stream): + return bundle_stream.bundles.select_related( + 'bundle_stream', 'deserialization_error') + + datatable_opts = { + 'aaSorting': [[4, 'desc']], + 'sPaginationType': 'full_numbers', + 'iDisplayLength': 25, +# 'aLengthMenu': [[10, 25, 50, -1], [10, 25, 50, "All"]], + 'sDom': 'lfr<"#master-toolbar">t<"F"ip>', + } + + searchable_columns = ['content_filename'] + + +def bundle_list_table_json(request, pathname): + bundle_stream = get_restricted_object_or_404( + BundleStream, + lambda bundle_stream: bundle_stream, + request.user, + pathname=pathname + ) + return BundleTable.json(request, params=(bundle_stream,)) + + @BreadCrumb( "Bundles in {pathname}", parent=bundle_stream_list, @@ -134,18 +218,20 @@ request.user, pathname=pathname ) - return object_list( - request, - queryset=bundle_stream.bundles.select_related( - 'bundle_stream', 'deserialization_error'), - template_name="dashboard_app/bundle_list.html", - template_object_name="bundle", - extra_context={ + return render_to_response( + "dashboard_app/bundle_list.html", + { + 'bundle_table': BundleTable( + 'bundle-table', + reverse( + bundle_list_table_json, kwargs=dict(pathname=pathname)), + params=(bundle_stream,)), 'bread_crumb_trail': BreadCrumbTrail.leading_to( bundle_list, pathname=pathname), "bundle_stream": bundle_stream - }) + }, + RequestContext(request)) @BreadCrumb( @@ -229,28 +315,28 @@ RequestContext(request)) -@BreadCrumb( - "Test runs in {pathname}", - parent=bundle_stream_list, - needs=['pathname']) -def test_run_list(request, pathname): - """ - List of test runs in a specified bundle stream. - """ - bundle_stream = get_restricted_object_or_404( - BundleStream, - lambda bundle_stream: bundle_stream, - request.user, - pathname=pathname - ) - return render_to_response( - 'dashboard_app/test_run_list.html', { - 'bread_crumb_trail': BreadCrumbTrail.leading_to( - test_run_list, - pathname=pathname), - "test_run_list": TestRun.objects.filter( +class TestRunTable(DataTablesTable): + + record = TemplateColumn( + '' + '{{ record.test }} results', + accessor="test__test_id", + ) + + test = TemplateColumn( + '{{ record.test }}', + accessor="test__test_id", + ) + + uploaded_on = TemplateColumn( + '{{ record.bundle.uploaded_on|date:"Y-m-d H:i:s" }}') + + analyzed_on = TemplateColumn( + '{{ record.analyzer_assigned_date|date:"Y-m-d H:i:s" }}') + + def get_queryset(self, bundle_stream): + return TestRun.objects.filter( bundle__bundle_stream=bundle_stream - ).order_by( # clean any implicit ordering ).select_related( "test", "bundle", @@ -264,12 +350,94 @@ "bundle__bundle_stream__pathname", # Needed by TestRun.get_absolute_url "test__name", # needed by Test.__unicode__ "test__test_id", # needed by Test.__unicode__ - ), + ) + + datatable_opts = { + "sPaginationType": "full_numbers", + "aaSorting": [[1, "desc"]], + "iDisplayLength": 25, + "sDom": 'lfr<"#master-toolbar">t<"F"ip>' + } + + searchable_columns = ['test__test_id'] + + +def test_run_list_json(request, pathname): + bundle_stream = get_restricted_object_or_404( + BundleStream, + lambda bundle_stream: bundle_stream, + request.user, + pathname=pathname + ) + return TestRunTable.json(request, params=(bundle_stream,)) + + +@BreadCrumb( + "Test runs in {pathname}", + parent=bundle_stream_list, + needs=['pathname']) +def test_run_list(request, pathname): + """ + List of test runs in a specified bundle stream. + """ + bundle_stream = get_restricted_object_or_404( + BundleStream, + lambda bundle_stream: bundle_stream, + request.user, + pathname=pathname + ) + return render_to_response( + 'dashboard_app/test_run_list.html', { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_run_list, + pathname=pathname), + "test_run_table": TestRunTable( + 'test-runs', + reverse(test_run_list_json, kwargs=dict(pathname=pathname)), + params=(bundle_stream,)), "bundle_stream": bundle_stream, }, RequestContext(request) ) +class TestTable(DataTablesTable): + + relative_index = Column( + verbose_name="#", attrs=Attrs(th=dict(style="width: 1%")), + default=mark_safe("Not specified")) + + test_case = Column() + + result = TemplateColumn(''' + + {{ record.get_result_display }} + {{ record.get_result_display }} + ''') + + units = TemplateColumn( + '{{ record.measurement|default_if_none:"Not specified" }} {{ record.units }}', + verbose_name="measurement") + + def get_queryset(self, test_run): + return test_run.get_results() + + datatable_opts = { + 'sPaginationType': "full_numbers", + } + + searchable_columns = ['test_case__test_case_id'] + + +def test_run_detail_test_json(request, pathname, content_sha1, analyzer_assigned_uuid): + test_run = get_restricted_object_or_404( + TestRun, lambda test_run: test_run.bundle.bundle_stream, + request.user, + analyzer_assigned_uuid=analyzer_assigned_uuid + ) + return TestTable.json(request, params=(test_run,)) + + @BreadCrumb( "Run {analyzer_assigned_uuid}", parent=bundle_detail, @@ -288,7 +456,15 @@ pathname=pathname, content_sha1=content_sha1, analyzer_assigned_uuid=analyzer_assigned_uuid), - "test_run": test_run + "test_run": test_run, + "test_table": TestTable( + 'test-table', + reverse(test_run_detail_test_json, kwargs=dict( + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid)), + params=(test_run,)) + }, RequestContext(request)) === modified file 'doc/changes.rst' --- doc/changes.rst 2012-03-17 11:15:24 +0000 +++ doc/changes.rst 2012-03-20 21:26:58 +0000 @@ -6,6 +6,7 @@ Version 0.14 ============ +* Convert some tables to use AJAX pagination. * Add an admin function to support deleting an entire bundle, including referenced test runs and results