=== modified file 'dashboard_app/tests/views/redirects.py'
--- dashboard_app/tests/views/redirects.py	2011-07-18 16:29:23 +0000
+++ dashboard_app/tests/views/redirects.py	2011-08-24 04:36:41 +0000
@@ -58,6 +58,21 @@
                     args=(self.bundle.content_sha1, )))
         self.assertRedirects(response, self.bundle.get_absolute_url())
 
+    def test_bundle_permalink_trailing(self):
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_bundle",
+                    args=(self.bundle.content_sha1, 'trailing/')))
+        self.assertRedirects(
+            response, self.bundle.get_absolute_url() + 'trailing/',
+            target_status_code=404)
+
+    def test_bundle_permalink_query_string(self):
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_bundle",
+                    args=(self.bundle.content_sha1, )), data={'foo': 'bar'})
+        self.assertRedirects(
+            response, self.bundle.get_absolute_url()+'?foo=bar')
+
     def test_test_run_permalink(self):
         test_run = self.bundle.test_runs.all()[0]
         response = self.client.get(
@@ -65,6 +80,24 @@
                     args=(test_run.analyzer_assigned_uuid, )))
         self.assertRedirects(response, test_run.get_absolute_url())
 
+    def test_test_run_permalink_trailing(self):
+        test_run = self.bundle.test_runs.all()[0]
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_test_run",
+                    args=(test_run.analyzer_assigned_uuid, 'trailing/')))
+        self.assertRedirects(
+            response, test_run.get_absolute_url() + 'trailing/',
+            target_status_code=404)
+
+    def test_test_run_permalink_query_string(self):
+        test_run = self.bundle.test_runs.all()[0]
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_test_run",
+                    args=(test_run.analyzer_assigned_uuid, )),
+            data={'foo': 'bar'})
+        self.assertRedirects(
+            response, test_run.get_absolute_url() + '?foo=bar')
+
     def test_test_result_permalink(self):
         test_run = self.bundle.test_runs.all()[0]
         test_result = test_run.test_results.all()[0]
@@ -73,3 +106,25 @@
                     args=(test_run.analyzer_assigned_uuid,
                           test_result.relative_index)))
         self.assertRedirects(response, test_result.get_absolute_url())
+
+    def test_test_result_permalink_trailing(self):
+        test_run = self.bundle.test_runs.all()[0]
+        test_result = test_run.test_results.all()[0]
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_test_result",
+                    args=(test_run.analyzer_assigned_uuid,
+                          test_result.relative_index, 'trailing/')))
+        self.assertRedirects(
+            response, test_result.get_absolute_url() + 'trailing/',
+            target_status_code=404)
+
+    def test_test_result_permalink_query_string(self):
+        test_run = self.bundle.test_runs.all()[0]
+        test_result = test_run.test_results.all()[0]
+        response = self.client.get(
+            reverse("dashboard_app.views.redirect_to_test_result",
+                    args=(test_run.analyzer_assigned_uuid,
+                          test_result.relative_index)),
+            data={'foo': 'bar'})
+        self.assertRedirects(
+            response, test_result.get_absolute_url() + '?foo=bar')

=== modified file 'dashboard_app/urls.py'
--- dashboard_app/urls.py	2011-07-22 00:57:44 +0000
+++ dashboard_app/urls.py	2011-08-24 04:03:56 +0000
@@ -48,6 +48,7 @@
     url(r'^streams/$', 'bundle_stream_list'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/$', 'bundle_list'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/$', 'bundle_detail'),
+    url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/json$', 'bundle_json'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/$', 'test_run_detail'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/attachments$', 'attachment_list'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/attachments/(?P<pk>[0-9]+)/$', 'attachment_detail'),
@@ -56,8 +57,11 @@
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/software-context/$', 'test_run_software_context'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/_-]+)test-runs/$', 'test_run_list'),
     url(r'^permalink/test-run/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/$', 'redirect_to_test_run'),
+    url(r'^permalink/test-run/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/(?P<trailing>.*)$', 'redirect_to_test_run'),
     url(r'^permalink/test-result/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/(?P<relative_index>[0-9]+)/$', 'redirect_to_test_result'),
+    url(r'^permalink/test-result/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/(?P<relative_index>[0-9]+)/(?P<trailing>.*)$', 'redirect_to_test_result'),
     url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/$', 'redirect_to_bundle'),
+    url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/(?P<trailing>.*)$', 'redirect_to_bundle'),
     url(r'^image_status/$', 'image_status_list'),
     url(r'^image_status/(?P<rootfs_type>[a-zA-Z0-9_-]+)\+(?P<hwpack_type>[a-zA-Z0-9_-]+)/$', 'image_status_detail'),
     url(r'^image_status/(?P<rootfs_type>[a-zA-Z0-9_-]+)\+(?P<hwpack_type>[a-zA-Z0-9_-]+)/test-history/(?P<test_id>[^/]+)/$', 'image_test_history'),

=== modified file 'dashboard_app/views.py'
--- dashboard_app/views.py	2011-07-22 00:57:44 +0000
+++ dashboard_app/views.py	2011-08-24 04:39:48 +0000
@@ -20,9 +20,11 @@
 Views for the Dashboard application
 """
 
+import json
+
 from django.db.models.manager import Manager
 from django.db.models.query import QuerySet
-from django.http import Http404
+from django.http import Http404, HttpResponse
 from django.shortcuts import render_to_response, redirect, get_object_or_404
 from django.template import RequestContext
 from django.views.generic.list_detail import object_list, object_detail
@@ -165,6 +167,31 @@
         })
 
 
+def bundle_json(request, pathname, content_sha1):
+    bundle_stream = get_restricted_object_or_404(
+        BundleStream,
+        lambda bundle_stream: bundle_stream,
+        request.user,
+        pathname=pathname
+    )
+    bundle = bundle_stream.bundles.get(content_sha1=content_sha1)
+    test_runs = []
+    for test_run in bundle.test_runs.all():
+        test_runs.append({
+            'name': test_run.test.test_id,
+            'url': request.build_absolute_uri(test_run.get_absolute_url()),
+            'results': test_run.get_summary_results()
+            })
+    json_text = json.dumps({
+        'test_runs':test_runs,
+        })
+    content_type = 'application/json'
+    if 'callback' in request.GET:
+        json_text = '%s(%s)'%(request.GET['callback'], json_text)
+        content_type = 'text/javascript'
+    return HttpResponse(json_text, content_type=content_type)
+
+
 def ajax_bundle_viewer(request, pk):
     bundle = get_restricted_object_or_404(
         Bundle,
@@ -443,32 +470,41 @@
         })
 
 
-def redirect_to_test_run(request, analyzer_assigned_uuid):
+def redirect_to(request, object, trailing):
+    url = object.get_absolute_url() + trailing
+    qs = request.META.get('QUERY_STRING')
+    if qs:
+        url += '?' + qs
+    return redirect(url)
+
+
+def redirect_to_test_run(request, analyzer_assigned_uuid, trailing=''):
     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 redirect(test_run.get_absolute_url())
-
-
-def redirect_to_test_result(request, analyzer_assigned_uuid, relative_index):
+    return redirect_to(request, test_run, trailing)
+
+
+def redirect_to_test_result(request, analyzer_assigned_uuid, relative_index,
+                            trailing=''):
     test_result = get_restricted_object_or_404(
         TestResult,
         lambda test_result: test_result.test_run.bundle.bundle_stream,
         request.user,
         test_run__analyzer_assigned_uuid=analyzer_assigned_uuid,
         relative_index=relative_index)
-    return redirect(test_result.get_absolute_url())
-
-
-def redirect_to_bundle(request, content_sha1): 
+    return redirect_to(request, test_result, trailing)
+
+
+def redirect_to_bundle(request, content_sha1, trailing=''):
     bundle = get_restricted_object_or_404(
         Bundle,
         lambda bundle: bundle.bundle_stream,
         request.user,
         content_sha1=content_sha1)
-    return redirect(bundle.get_absolute_url())
+    return redirect_to(request, bundle, trailing)
 
 
 @BreadCrumb("Image Status Matrix", parent=index)

