From patchwork Tue Jul 12 02:49:12 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zygmunt Krynicki X-Patchwork-Id: 2654 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 EC22F23F56 for ; Tue, 12 Jul 2011 02:49:15 +0000 (UTC) Received: from mail-qw0-f52.google.com (mail-qw0-f52.google.com [209.85.216.52]) by fiordland.canonical.com (Postfix) with ESMTP id 3310FA18621 for ; Tue, 12 Jul 2011 02:49:15 +0000 (UTC) Received: by qwb8 with SMTP id 8so3104709qwb.11 for ; Mon, 11 Jul 2011 19:49:14 -0700 (PDT) Received: by 10.229.217.3 with SMTP id hk3mr2817995qcb.38.1310438954637; Mon, 11 Jul 2011 19:49:14 -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.229.217.78 with SMTP id hl14cs226910qcb; Mon, 11 Jul 2011 19:49:14 -0700 (PDT) Received: by 10.227.55.7 with SMTP id s7mr4791838wbg.65.1310438953044; Mon, 11 Jul 2011 19:49:13 -0700 (PDT) Received: from adelie.canonical.com (adelie.canonical.com [91.189.90.139]) by mx.google.com with ESMTP id c13si3577435wbh.77.2011.07.11.19.49.12; Mon, 11 Jul 2011 19:49:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.139 as permitted sender) client-ip=91.189.90.139; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.139 as permitted sender) smtp.mail=bounces@canonical.com Received: from loganberry.canonical.com ([91.189.90.37]) by adelie.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1QgT28-0000aN-Bc for ; Tue, 12 Jul 2011 02:49:12 +0000 Received: from loganberry.canonical.com (localhost [127.0.0.1]) by loganberry.canonical.com (Postfix) with ESMTP id 519952E84F6 for ; Tue, 12 Jul 2011 02:49: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: 240 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 240: Drastic rework of all dashboard UI plus a few new features: Message-Id: <20110712024912.7947.84544.launchpad@loganberry.canonical.com> Date: Tue, 12 Jul 2011 02:49: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="13388"; Instance="initZopeless config overlay" X-Launchpad-Hash: 50fbc5a3046e97389ff7acec0a15835d6909f404 Merge authors: Zygmunt Krynicki (zkrynicki) Related merge proposals: https://code.launchpad.net/~zkrynicki/lava-dashboard/omg-tables/+merge/67653 proposed by: Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 240 [merge] committer: Zygmunt Krynicki branch nick: trunk timestamp: Tue 2011-07-12 04:46:03 +0200 message: Drastic rework of all dashboard UI plus a few new features: 1) Ability to browse tests 2) Ability to browse test cases 2) Ability to browse attachments for a particular test run 3) Ability to see a particular attachment (+ ajax content download) 4) Ability to see a particular bundle (+ ajax content download) 5) Efficient navigation between results in a particular test run General UI improvements include: 1) A whole new theme 2) Better tables everywhere (with client side search and sort) 3) Tabs to logically group things together 4) AJAX progress notification bar 5) Better hardware context browser Some bug fixes: 1) Proper escaping of %s in data view queries 2) Cherry-pick hot-fix for django issue preventing usage of strftime("%s") in SQLite removed: dashboard_app/templates/dashboard_app/base.html added: dashboard_app/bread_crumbs.py dashboard_app/patches.py dashboard_app/static/css/demo_table_jui.css dashboard_app/static/images/details_close.png dashboard_app/static/images/details_open.png dashboard_app/static/js/FixedHeader.min.js dashboard_app/templates/dashboard_app/_ajax_attachment_viewer.html dashboard_app/templates/dashboard_app/_ajax_bundle_viewer.html dashboard_app/templates/dashboard_app/_breadcrumbs.html dashboard_app/templates/dashboard_app/_bundle_stream_sidebar.html dashboard_app/templates/dashboard_app/_content.html dashboard_app/templates/dashboard_app/_content_with_sidebar.html dashboard_app/templates/dashboard_app/_extension_navigation.html dashboard_app/templates/dashboard_app/_extrahead.html dashboard_app/templates/dashboard_app/_test_run_list_table.html dashboard_app/templates/dashboard_app/_title.html dashboard_app/templates/dashboard_app/attachment_list.html dashboard_app/templates/dashboard_app/bundle_detail.html dashboard_app/templates/dashboard_app/index.html dashboard_app/templates/dashboard_app/test_detail.html dashboard_app/templates/dashboard_app/test_list.html production/ production/reports/ production/views/ modified: dashboard_app/__init__.py dashboard_app/dataview.py dashboard_app/extension.py dashboard_app/managers.py dashboard_app/models.py dashboard_app/static/js/jquery.dashboard.js dashboard_app/templates/dashboard_app/api.html dashboard_app/templates/dashboard_app/attachment_detail.html dashboard_app/templates/dashboard_app/bundle_list.html dashboard_app/templates/dashboard_app/bundle_stream_list.html dashboard_app/templates/dashboard_app/data_view_detail.html dashboard_app/templates/dashboard_app/data_view_list.html dashboard_app/templates/dashboard_app/report_detail.html dashboard_app/templates/dashboard_app/report_list.html dashboard_app/templates/dashboard_app/test_result_detail.html dashboard_app/templates/dashboard_app/test_run_detail.html dashboard_app/templates/dashboard_app/test_run_hardware_context.html dashboard_app/templates/dashboard_app/test_run_list.html dashboard_app/templates/dashboard_app/test_run_software_context.html dashboard_app/tests/views/test_run_detail_view.py dashboard_app/urls.py dashboard_app/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/__init__.py' --- dashboard_app/__init__.py 2011-07-01 14:04:59 +0000 +++ dashboard_app/__init__.py 2011-07-12 02:39:22 +0000 @@ -20,4 +20,4 @@ Dashboard Application (package) """ -__version__ = (0, 5, 2, "final", 0) +__version__ = (0, 6, 0, "beta", 1) === added file 'dashboard_app/bread_crumbs.py' --- dashboard_app/bread_crumbs.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/bread_crumbs.py 2011-07-12 02:34:12 +0000 @@ -0,0 +1,86 @@ +# Copyright (C) 2010 Linaro Limited +# +# Author: Zygmunt Krynicki +# +# This file is part of Launch Control. +# +# Launch Control is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License version 3 +# as published by the Free Software Foundation +# +# Launch Control is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Launch Control. If not, see . + +from django.core.urlresolvers import reverse +import logging + + +class BreadCrumb(object): + + def __init__(self, name, parent=None, needs=None): + self.name = name + self.view = None + self.parent = parent + self.needs = needs or [] + + def __repr__(self): + return "" % ( + self.name, self.view, self.parent) + + def __call__(self, view): + self.view = view + view._bread_crumb = self + return view + + def get_name(self, kwargs): + try: + return self.name.format(**kwargs) + except: + logging.exception("Unable to construct breadcrumb name for view %r", self.view) + raise + + def get_absolute_url(self, kwargs): + try: + return reverse(self.view, args=[kwargs[name] for name in self.needs]) + except: + logging.exception("Unable to construct breadcrumb URL for view %r", self.view) + raise + + +class LiveBreadCrumb(object): + + def __init__(self, bread_crumb, kwargs): + self.bread_crumb = bread_crumb + self.kwargs = kwargs + + def get_name(self): + return self.bread_crumb.get_name(self.kwargs) + + def get_absolute_url(self): + return self.bread_crumb.get_absolute_url(self.kwargs) + + +class BreadCrumbTrail(object): + + def __init__(self, bread_crumb_list, kwargs): + self.bread_crumb_list = bread_crumb_list + self.kwargs = kwargs + + def __iter__(self): + for bread_crumb in self.bread_crumb_list: + yield LiveBreadCrumb(bread_crumb, self.kwargs) + + @classmethod + def leading_to(cls, view, **kwargs): + lst = [] + while view is not None: + lst.append(view._bread_crumb) + view = view._bread_crumb.parent + lst.reverse() + return cls(lst, kwargs or {}) + === modified file 'dashboard_app/dataview.py' --- dashboard_app/dataview.py 2011-05-18 16:00:53 +0000 +++ dashboard_app/dataview.py 2011-07-09 15:09:48 +0000 @@ -110,7 +110,13 @@ raise LookupError("Specified data view has no SQL implementation " "for current database") # Replace SQL aruments with django placeholders (connection agnostic) - sql = query.sql_template.format(**dict([(arg_name, "%s") for arg_name in query.argument_list])) + template = query.sql_template + template = template.replace("%", "%%") + # template = template.replace("{", "{{").replace("}", "}}") + sql = template.format( + **dict([ + (arg_name, "%s") + for arg_name in query.argument_list])) # Construct argument list using defaults for missing values sql_args = [ arguments.get(arg_name, self.lookup_argument(arg_name).default) === modified file 'dashboard_app/extension.py' --- dashboard_app/extension.py 2011-07-01 14:03:49 +0000 +++ dashboard_app/extension.py 2011-07-12 02:32:26 +0000 @@ -1,3 +1,4 @@ +import os from lava_server.extension import LavaServerExtension @@ -13,7 +14,7 @@ @property def main_view_name(self): - return "dashboard_app.views.bundle_stream_list" + return "dashboard_app.views.index" @property def description(self): @@ -25,19 +26,33 @@ import dashboard_app return versiontools.format_version(dashboard_app.__version__) - def contribute_to_settings(self, settings): - super(DashboardExtension, self).contribute_to_settings(settings) - settings['INSTALLED_APPS'].extend([ + def contribute_to_settings(self, settings_module): + super(DashboardExtension, self).contribute_to_settings(settings_module) + settings_module['INSTALLED_APPS'].extend([ "linaro_django_pagination", "south", ]) - settings['MIDDLEWARE_CLASSES'].append( + settings_module['MIDDLEWARE_CLASSES'].append( 'linaro_django_pagination.middleware.PaginationMiddleware') - settings['RESTRUCTUREDTEXT_FILTER_SETTINGS'] = { - "initial_header_level": 4} + root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) + settings_module['DATAVIEW_DIRS'] = [ + os.path.join(root_dir, 'examples/views'), + os.path.join(root_dir, 'production/views')] + settings_module['DATAREPORT_DIRS'] = [ + os.path.join(root_dir, 'examples/reports'), + os.path.join(root_dir, 'production/reports')] def contribute_to_settings_ex(self, settings_module, settings_object): settings_module['DATAVIEW_DIRS'] = settings_object._settings.get( "DATAVIEW_DIRS", []) settings_module['DATAREPORT_DIRS'] = settings_object._settings.get( "DATAREPORT_DIRS", []) + + # Enable constrained dataview database if requested + if settings_object._settings.get("use_dataview_database"): + # Copy everything from the default database and append _dataview to user + # name. The rest is out of scope (making sure it's actually setup + # properly, having permissions to login, permissions to view proper data) + settings_module['DATABASES']['dataview'] = dict(settings_module['DATABASES']['default']) + settings_module['DATABASES']['dataview']['USER'] += "_dataview" + === modified file 'dashboard_app/managers.py' --- dashboard_app/managers.py 2011-03-16 21:10:26 +0000 +++ dashboard_app/managers.py 2011-07-09 13:47:36 +0000 @@ -31,6 +31,7 @@ bundle_stream=bundle_stream, uploaded_by=uploaded_by, content_filename=content_filename) + # XXX: this _can_ fail -- if content_sha1 is a duplicate logging.debug("Saving bundle object (this is safe so far)") bundle.save() try: === modified file 'dashboard_app/models.py' --- dashboard_app/models.py 2011-05-30 16:58:20 +0000 +++ dashboard_app/models.py 2011-07-12 02:30:17 +0000 @@ -24,6 +24,7 @@ import hashlib import logging import os +import simplejson import traceback from django.contrib.auth.models import User @@ -43,6 +44,10 @@ from dashboard_app.repositories import RepositoryItem from dashboard_app.repositories.data_report import DataReportRepository +# Fix some django issues we ran into +from dashboard_app.patches import patch +patch() + def _help_max_length(max_length): return ungettext( @@ -184,7 +189,7 @@ @models.permalink def get_absolute_url(self): - return ("dashboard_app.test_run_list", [self.pathname]) + return ("dashboard_app.views.bundle_list", [self.pathname]) def get_test_run_count(self): return TestRun.objects.filter(bundle__bundle_stream=self).count() @@ -341,7 +346,7 @@ @models.permalink def get_absolute_url(self): - return ("dashboard_app.bundle.detail", [self.pk]) + return ("dashboard_app.views.bundle_detail", [self.bundle_stream.pathname, self.content_sha1]) def save(self, *args, **kwargs): if self.content: @@ -413,6 +418,41 @@ for attachment in test_run.attachments.all(): attachment.content.delete(save=save) + def get_sanitized_bundle(self): + self.content.open() + try: + return SanitizedBundle(self.content) + finally: + self.content.close() + + +class SanitizedBundle(object): + + def __init__(self, stream): + try: + self.bundle_json = simplejson.load(stream) + self.deserialization_error = None + except simplejson.JSONDeserializationError as ex: + self.bundle_json = None + self.deserialization_error = ex + self.did_remove_attachments = False + self._sanitize() + + def get_human_readable_json(self): + return simplejson.dumps(self.bundle_json, indent=4) + + def _sanitize(self): + for test_run in self.bundle_json.get("test_runs", []): + attachments = test_run.get("attachments") + if isinstance(attachments, list): + for attachment in attachments: + attachment["content"] = None + self.did_remove_attachments = True + elif isinstance(attachments, dict): + for name in attachments: + attachments[name] = None + self.did_remove_attachments = True + class BundleDeserializationError(models.Model): """ @@ -466,7 +506,18 @@ @models.permalink def get_absolute_url(self): - return ('dashboard_app.test.detail', [self.test_id]) + return ('dashboard_app.views.test_detail', [self.test_id]) + + def count_results_without_test_case(self): + return TestResult.objects.filter( + test_run__test=self, + test_case=None).count() + + def count_failures_without_test_case(self): + return TestResult.objects.filter( + test_run__test=self, + test_case=None, + result=TestResult.RESULT_FAIL).count() class TestCase(models.Model): @@ -511,6 +562,9 @@ def get_absolute_url(self): return ("dashboard_app.test_case.details", [self.test.test_id, self.test_case_id]) + def count_failures(self): + return self.test_results.filter(result=TestResult.RESULT_FAIL).count() + class SoftwareSource(models.Model): """ @@ -675,7 +729,9 @@ @models.permalink def get_absolute_url(self): return ("dashboard_app.views.test_run_detail", - [self.analyzer_assigned_uuid]) + [self.bundle.bundle_stream.pathname, + self.bundle.content_sha1, + self.analyzer_assigned_uuid]) def get_summary_results(self): stats = self.test_results.values('result').annotate( @@ -723,10 +779,6 @@ def __unicode__(self): return self.content_filename - @models.permalink - def get_absolute_url(self): - return ("dashboard_app.views.attachment_detail", [self.pk]) - def get_content_if_possible(self, mirror=False): if self.content: self.content.open() @@ -752,6 +804,25 @@ data = None return data + def is_test_run_attachment(self): + if (self.content_type.app_label == 'dashboard_app' and + self.content_type.model == 'testrun'): + return True + + @property + def test_run(self): + if self.is_test_run_attachment(): + return self.content_object + + @models.permalink + def get_absolute_url(self): + if self.is_test_run_attachment(): + return ("dashboard_app.views.attachment_detail", + [self.test_run.bundle.bundle_stream.pathname, + self.test_run.bundle.content_sha1, + self.test_run.analyzer_assigned_uuid, + self.pk]) + class TestResult(models.Model): """ @@ -888,8 +959,12 @@ @models.permalink def get_absolute_url(self): - return ("dashboard_app.views.test_result_detail", - [self.pk]) + return ("dashboard_app.views.test_result_detail", [ + self.test_run.bundle.bundle_stream.pathname, + self.test_run.bundle.content_sha1, + self.test_run.analyzer_assigned_uuid, + self.relative_index, + ]) def related_attachment_available(self): """ @@ -920,7 +995,7 @@ def __init__(self, **kwargs): self._html = None - self.__dict__.update(kwargs) + self._data = kwargs def _get_raw_html(self): pathname = os.path.join(self.base_path, self.path) @@ -935,7 +1010,9 @@ return Template(self._get_raw_html()) def _get_html_template_context(self): - return Context({"API_URL": reverse("dashboard_app.views.dashboard_xml_rpc_handler")}) + return Context({ + "API_URL": reverse("dashboard_app.views.dashboard_xml_rpc_handler") + }) def get_html(self): from django.conf import settings @@ -952,3 +1029,23 @@ @models.permalink def get_absolute_url(self): return ("dashboard_app.views.report_detail", [self.name]) + + @property + def title(self): + return self._data['title'] + + @property + def path(self): + return self._data['path'] + + @property + def name(self): + return self._data['name'] + + @property + def bug_report_url(self): + return self._data.get('bug_report_url') + + @property + def author(self): + return self._data.get('author') === added file 'dashboard_app/patches.py' --- dashboard_app/patches.py 1970-01-01 00:00:00 +0000 +++ dashboard_app/patches.py 2011-07-09 15:08:42 +0000 @@ -0,0 +1,112 @@ +""" +Patches for django bugs that affect this package +""" + +class PatchDjangoTicket1476(object): + """ + Patch for bug http://code.djangoproject.com/ticket/1476 + """ + + @classmethod + def apply_if_needed(patch): + import django + if django.VERSION[0:3] <= (1, 2, 4): + patch.apply() + + @classmethod + def apply(patch): + from django.utils.decorators import method_decorator + from django.views.decorators.csrf import csrf_protect + + @method_decorator(csrf_protect) + def __call__(self, request, *args, **kwargs): + """ + Main method that does all the hard work, conforming to the Django view + interface. + """ + if 'extra_context' in kwargs: + self.extra_context.update(kwargs['extra_context']) + current_step = self.determine_step(request, *args, **kwargs) + self.parse_params(request, *args, **kwargs) + + # Sanity check. + if current_step >= self.num_steps(): + raise Http404('Step %s does not exist' % current_step) + + # Process the current step. If it's valid, go to the next step or call + # done(), depending on whether any steps remain. + if request.method == 'POST': + form = self.get_form(current_step, request.POST) + else: + form = self.get_form(current_step) + + if form.is_valid(): + # Validate all the forms. If any of them fail validation, that + # must mean the validator relied on some other input, such as + # an external Web site. + + # It is also possible that validation might fail under certain + # attack situations: an attacker might be able to bypass previous + # stages, and generate correct security hashes for all the + # skipped stages by virtue of: + # 1) having filled out an identical form which doesn't have the + # validation (and does something different at the end), + # 2) or having filled out a previous version of the same form + # which had some validation missing, + # 3) or previously having filled out the form when they had + # more privileges than they do now. + # + # Since the hashes only take into account values, and not other + # other validation the form might do, we must re-do validation + # now for security reasons. + previous_form_list = [self.get_form(i, request.POST) for i in range(current_step)] + + for i, f in enumerate(previous_form_list): + if request.POST.get("hash_%d" % i, '') != self.security_hash(request, f): + return self.render_hash_failure(request, i) + + if not f.is_valid(): + return self.render_revalidation_failure(request, i, f) + else: + self.process_step(request, f, i) + + # Now progress to processing this step: + self.process_step(request, form, current_step) + next_step = current_step + 1 + + + if next_step == self.num_steps(): + return self.done(request, previous_form_list + [form]) + else: + form = self.get_form(next_step) + self.step = current_step = next_step + + return self.render(form, request, current_step) + + from django.contrib.formtools.wizard import FormWizard + FormWizard.__call__ = __call__ + + +class PatchDjangoTicket15155(object): + """ + Patch for bug http://code.djangoproject.com/ticket/15155 + """ + + PROPER_FORMAT = r'(? c4 + ee > d5 + Level 2: + dd > d1 + ee > e2 + */ +tr.odd.gradeA td.sorting_1 { + background-color: #c4ffc4; +} + +tr.odd.gradeA td.sorting_2 { + background-color: #d1ffd1; +} + +tr.odd.gradeA td.sorting_3 { + background-color: #d1ffd1; +} + +tr.even.gradeA td.sorting_1 { + background-color: #d5ffd5; +} + +tr.even.gradeA td.sorting_2 { + background-color: #e2ffe2; +} + +tr.even.gradeA td.sorting_3 { + background-color: #e2ffe2; +} + +tr.odd.gradeC td.sorting_1 { + background-color: #c4c4ff; +} + +tr.odd.gradeC td.sorting_2 { + background-color: #d1d1ff; +} + +tr.odd.gradeC td.sorting_3 { + background-color: #d1d1ff; +} + +tr.even.gradeC td.sorting_1 { + background-color: #d5d5ff; +} + +tr.even.gradeC td.sorting_2 { + background-color: #e2e2ff; +} + +tr.even.gradeC td.sorting_3 { + background-color: #e2e2ff; +} + +tr.odd.gradeX td.sorting_1 { + background-color: #ffc4c4; +} + +tr.odd.gradeX td.sorting_2 { + background-color: #ffd1d1; +} + +tr.odd.gradeX td.sorting_3 { + background-color: #ffd1d1; +} + +tr.even.gradeX td.sorting_1 { + background-color: #ffd5d5; +} + +tr.even.gradeX td.sorting_2 { + background-color: #ffe2e2; +} + +tr.even.gradeX td.sorting_3 { + background-color: #ffe2e2; +} + +tr.odd.gradeU td.sorting_1 { + background-color: #c4c4c4; +} + +tr.odd.gradeU td.sorting_2 { + background-color: #d1d1d1; +} + +tr.odd.gradeU td.sorting_3 { + background-color: #d1d1d1; +} + +tr.even.gradeU td.sorting_1 { + background-color: #d5d5d5; +} + +tr.even.gradeU td.sorting_2 { + background-color: #e2e2e2; +} + +tr.even.gradeU td.sorting_3 { + background-color: #e2e2e2; +} + +/* + * Row highlighting example + */ +.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted { + background-color: #ECFFB3; +} + +.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted { + background-color: #E6FF99; +} === added file 'dashboard_app/static/images/details_close.png' Binary files dashboard_app/static/images/details_close.png 1970-01-01 00:00:00 +0000 and dashboard_app/static/images/details_close.png 2011-07-08 04:20:37 +0000 differ === added file 'dashboard_app/static/images/details_open.png' Binary files dashboard_app/static/images/details_open.png 1970-01-01 00:00:00 +0000 and dashboard_app/static/images/details_open.png 2011-07-08 04:20:37 +0000 differ === added file 'dashboard_app/static/js/FixedHeader.min.js' --- dashboard_app/static/js/FixedHeader.min.js 1970-01-01 00:00:00 +0000 +++ dashboard_app/static/js/FixedHeader.min.js 2011-07-08 04:20:37 +0000 @@ -0,0 +1,115 @@ +/* + * File: FixedHeader.min.js + * Version: 2.0.4 + * Author: Allan Jardine (www.sprymedia.co.uk) + * + * Copyright 2009-2011 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD (3 point) style license, as supplied with this software. + * + * This source file 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 license files for details. + */ +var FixedHeader=function(b,a){if(typeof this.fnInit!="function"){alert("FixedHeader warning: FixedHeader must be initialised with the 'new' keyword."); +return}var c=this;var d={aoCache:[],oSides:{top:true,bottom:false,left:false,right:false},oZIndexes:{top:104,bottom:103,left:102,right:101},oMes:{iTableWidth:0,iTableHeight:0,iTableLeft:0,iTableRight:0,iTableTop:0,iTableBottom:0},nTable:null,bUseAbsPos:false,bFooter:false}; +this.fnGetSettings=function(){return d};this.fnUpdate=function(){this._fnUpdateClones(); +this._fnUpdatePositions()};this.fnInit(b,a)};FixedHeader.prototype={fnInit:function(b,a){var c=this.fnGetSettings(); +var d=this;this.fnInitSettings(c,a);if(typeof b.fnSettings=="function"){if(typeof b.fnVersionCheck=="functon"&&b.fnVersionCheck("1.6.0")!==true){alert("FixedHeader 2 required DataTables 1.6.0 or later. Please upgrade your DataTables installation"); +return}var e=b.fnSettings();if(e.oScroll.sX!=""||e.oScroll.sY!=""){alert("FixedHeader 2 is not supported with DataTables' scrolling mode at this time"); +return}c.nTable=e.nTable;e.aoDrawCallback.push({fn:function(){FixedHeader.fnMeasure(); +d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)},sName:"FixedHeader"})}else{c.nTable=b +}c.bFooter=($(">tfoot",c.nTable).length>0)?true:false;c.bUseAbsPos=(jQuery.browser.msie&&(jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0")); +if(c.oSides.top){c.aoCache.push(d._fnCloneTable("fixedHeader","FixedHeader_Header",d._fnCloneThead)) +}if(c.oSides.bottom){c.aoCache.push(d._fnCloneTable("fixedFooter","FixedHeader_Footer",d._fnCloneTfoot)) +}if(c.oSides.left){c.aoCache.push(d._fnCloneTable("fixedLeft","FixedHeader_Left",d._fnCloneTLeft)) +}if(c.oSides.right){c.aoCache.push(d._fnCloneTable("fixedRight","FixedHeader_Right",d._fnCloneTRight)) +}FixedHeader.afnScroll.push(function(){d._fnUpdatePositions.call(d)});jQuery(window).resize(function(){FixedHeader.fnMeasure(); +d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)});FixedHeader.fnMeasure(); +d._fnUpdateClones();d._fnUpdatePositions()},fnInitSettings:function(b,a){if(typeof a!="undefined"){if(typeof a.top!="undefined"){b.oSides.top=a.top +}if(typeof a.bottom!="undefined"){b.oSides.bottom=a.bottom}if(typeof a.left!="undefined"){b.oSides.left=a.left +}if(typeof a.right!="undefined"){b.oSides.right=a.right}if(typeof a.zTop!="undefined"){b.oZIndexes.top=a.zTop +}if(typeof a.zBottom!="undefined"){b.oZIndexes.bottom=a.zBottom}if(typeof a.zLeft!="undefined"){b.oZIndexes.left=a.zLeft +}if(typeof a.zRight!="undefined"){b.oZIndexes.right=a.zRight}}b.bUseAbsPos=(jQuery.browser.msie&&(jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0")) +},_fnCloneTable:function(f,e,d){var b=this.fnGetSettings();var a;if(jQuery(b.nTable.parentNode).css("position")!="absolute"){b.nTable.parentNode.style.position="relative" +}a=b.nTable.cloneNode(false);var c=document.createElement("div");c.style.position="absolute"; +c.className+=" FixedHeader_Cloned "+f+" "+e;if(f=="fixedHeader"){c.style.zIndex=b.oZIndexes.top +}if(f=="fixedFooter"){c.style.zIndex=b.oZIndexes.bottom}if(f=="fixedLeft"){c.style.zIndex=b.oZIndexes.left +}else{if(f=="fixedRight"){c.style.zIndex=b.oZIndexes.right}}c.appendChild(a);document.body.appendChild(c); +return{nNode:a,nWrapper:c,sType:f,sPosition:"",sTop:"",sLeft:"",fnClone:d}},_fnMeasure:function(){var d=this.fnGetSettings(),a=d.oMes,c=jQuery(d.nTable),b=c.offset(),f=this._fnSumScroll(d.nTable.parentNode,"scrollTop"),e=this._fnSumScroll(d.nTable.parentNode,"scrollLeft"); +a.iTableWidth=c.outerWidth();a.iTableHeight=c.outerHeight();a.iTableLeft=b.left+d.nTable.parentNode.scrollLeft; +a.iTableTop=b.top+f;a.iTableRight=a.iTableLeft+a.iTableWidth;a.iTableRight=FixedHeader.oDoc.iWidth-a.iTableLeft-a.iTableWidth; +a.iTableBottom=FixedHeader.oDoc.iHeight-a.iTableTop-a.iTableHeight},_fnSumScroll:function(c,b){var a=c[b]; +while(c=c.parentNode){if(c.nodeName!="HTML"&&c.nodeName!="BODY"){break}a=c[b]}return a +},_fnUpdatePositions:function(){var c=this.fnGetSettings();this._fnMeasure();for(var b=0,a=c.aoCache.length; +bb.iScrollTop){this._fnUpdateCache(g,"sPosition","absolute","position",c.style); +this._fnUpdateCache(g,"sTop",f.iTableTop+"px","top",c.style);this._fnUpdateCache(g,"sLeft",f.iTableLeft+"px","left",c.style) +}else{if(b.iScrollTop>f.iTableTop+e){this._fnUpdateCache(g,"sPosition","absolute","position",c.style); +this._fnUpdateCache(g,"sTop",(f.iTableTop+e)+"px","top",c.style);this._fnUpdateCache(g,"sLeft",f.iTableLeft+"px","left",c.style) +}else{if(d.bUseAbsPos){this._fnUpdateCache(g,"sPosition","absolute","position",c.style); +this._fnUpdateCache(g,"sTop",b.iScrollTop+"px","top",c.style);this._fnUpdateCache(g,"sLeft",f.iTableLeft+"px","left",c.style) +}else{this._fnUpdateCache(g,"sPosition","fixed","position",c.style);this._fnUpdateCache(g,"sTop","0px","top",c.style); +this._fnUpdateCache(g,"sLeft",(f.iTableLeft-b.iScrollLeft)+"px","left",c.style)}}}},_fnUpdateCache:function(e,c,b,d,a){if(e[c]!=b){a[d]=b; +e[c]=b}},_fnCloneThead:function(d){var c=this.fnGetSettings();var a=d.nNode;d.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px"; +while(a.childNodes.length>0){jQuery("thead th",a).unbind("click");a.removeChild(a.childNodes[0]) +}var b=jQuery("thead",c.nTable).clone(true)[0];a.appendChild(b);jQuery("thead:eq(0)>tr th",c.nTable).each(function(e){jQuery("thead:eq(0)>tr th:eq("+e+")",a).width(jQuery(this).width()) +});jQuery("thead:eq(0)>tr td",c.nTable).each(function(e){jQuery("thead:eq(0)>tr th:eq("+e+")",a)[0].style.width(jQuery(this).width()) +})},_fnCloneTfoot:function(d){var c=this.fnGetSettings();var a=d.nNode;d.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px"; +while(a.childNodes.length>0){a.removeChild(a.childNodes[0])}var b=jQuery("tfoot",c.nTable).clone(true)[0]; +a.appendChild(b);jQuery("tfoot:eq(0)>tr th",c.nTable).each(function(e){jQuery("tfoot:eq(0)>tr th:eq("+e+")",a).width(jQuery(this).width()) +});jQuery("tfoot:eq(0)>tr td",c.nTable).each(function(e){jQuery("tfoot:eq(0)>tr th:eq("+e+")",a)[0].style.width(jQuery(this).width()) +})},_fnCloneTLeft:function(f){var c=this.fnGetSettings();var b=f.nNode;var e=jQuery("tbody tr:eq(0) td",c.nTable).length; +var a=($.browser.msie&&($.browser.version=="6.0"||$.browser.version=="7.0"));while(b.childNodes.length>0){b.removeChild(b.childNodes[0]) +}b.appendChild(jQuery("thead",c.nTable).clone(true)[0]);b.appendChild(jQuery("tbody",c.nTable).clone(true)[0]); +if(c.bFooter){b.appendChild(jQuery("tfoot",c.nTable).clone(true)[0])}jQuery("thead tr th:gt(0)",b).remove(); +jQuery("tfoot tr th:gt(0)",b).remove();$("tbody tr",b).each(function(g){$("td:gt(0)",this).remove(); +if($.browser.mozilla||$.browser.opera){$("td",this).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()) +}else{$("td",this).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()-iBoxHack) +}if(!a){$("tbody tr:eq("+g+")",that.dom.body).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()) +}});var d=jQuery("thead tr th:eq(0)",c.nTable).outerWidth();b.style.width=d+"px"; +f.nWrapper.style.width=d+"px"},_fnCloneTRight:function(f){var c=this.fnGetSettings(); +var b=f.nNode;var e=jQuery("tbody tr:eq(0) td",c.nTable).length;var a=($.browser.msie&&($.browser.version=="6.0"||$.browser.version=="7.0")); +while(b.childNodes.length>0){b.removeChild(b.childNodes[0])}b.appendChild(jQuery("thead",c.nTable).clone(true)[0]); +b.appendChild(jQuery("tbody",c.nTable).clone(true)[0]);if(c.bFooter){b.appendChild(jQuery("tfoot",c.nTable).clone(true)[0]) +}jQuery("thead tr th:not(:nth-child("+e+"n))",b).remove();jQuery("tfoot tr th:not(:nth-child("+e+"n))",b).remove(); +$("tbody tr",b).each(function(g){$("td:lt("+e-1+")",this).remove();if($.browser.mozilla||$.browser.opera){$("td",this).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()) +}else{$("td",this).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()-iBoxHack) +}if(!a){$("tbody tr:eq("+g+")",that.dom.body).height($("tbody tr:eq("+g+")",that.dom.body).outerHeight()) +}});var d=jQuery("thead tr th:eq("+(e-1)+")",c.nTable).outerWidth();b.style.width=d+"px"; +f.nWrapper.style.width=d+"px"}};FixedHeader.oWin={iScrollTop:0,iScrollRight:0,iScrollBottom:0,iScrollLeft:0,iHeight:0,iWidth:0}; +FixedHeader.oDoc={iHeight:0,iWidth:0};FixedHeader.afnScroll=[];FixedHeader.fnMeasure=function(){var d=jQuery(window),c=jQuery(document),b=FixedHeader.oWin,a=FixedHeader.oDoc; +a.iHeight=c.height();a.iWidth=c.width();b.iHeight=d.height();b.iWidth=d.width();b.iScrollTop=d.scrollTop(); +b.iScrollLeft=d.scrollLeft();b.iScrollRight=a.iWidth-b.iScrollLeft-b.iWidth;b.iScrollBottom=a.iHeight-b.iScrollTop-b.iHeight +};jQuery(window).scroll(function(){FixedHeader.fnMeasure();for(var b=0,a=FixedHeader.afnScroll.length; +b"; if (options != undefined && options.caption != undefined) { html += "" + options.caption + ""; } - html += ""; + html += ""; $.each(dataset.columns, function (index, column) { html += "" + column.name + ""; }); - html += ""; + html += ""; $.each(dataset.rows, function (index, row) { html += ""; $.each(row, function (index, cell) { @@ -84,8 +90,12 @@ }); html += ""; }); - html += ""; + html += ""; this.html(html); + $("#dashboard_table_" + table_id).dataTable({ + "bJQueryUI": true, + "sPaginationType": "full_numbers", + }); }, render_to_table: function(data_view_name, data_view_arguments, options) { === added file 'dashboard_app/templates/dashboard_app/_ajax_attachment_viewer.html' --- dashboard_app/templates/dashboard_app/_ajax_attachment_viewer.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_ajax_attachment_viewer.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,12 @@ +{% load i18n %} +{% if lines %} +
    + {% for line in lines %} +
  1. + {{line}} +
  2. + {% endfor %} +
+{% else %} +

{% trans "Viewer not available" %}

+{% endif %} === added file 'dashboard_app/templates/dashboard_app/_ajax_bundle_viewer.html' --- dashboard_app/templates/dashboard_app/_ajax_bundle_viewer.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_ajax_bundle_viewer.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,17 @@ +{% load i18n %} +{% load stylize %} + + +{% with bundle.get_sanitized_bundle as sanitized_bundle %} +{% if sanitized_bundle.did_remove_attachments %} +
+
+ + Note: Inline attachments were removed to make this page more readable. +
+
+{% endif %} +
+ {% stylize "js" %}{{ sanitized_bundle.get_human_readable_json|safe }}{% endstylize %} +
+{% endwith %} === added file 'dashboard_app/templates/dashboard_app/_breadcrumbs.html' --- dashboard_app/templates/dashboard_app/_breadcrumbs.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_breadcrumbs.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,5 @@ +{% for bread_crumb in bread_crumb_trail %} +
  • {{ bread_crumb.get_name }}
  • +{% endfor %} === added file 'dashboard_app/templates/dashboard_app/_bundle_stream_sidebar.html' --- dashboard_app/templates/dashboard_app/_bundle_stream_sidebar.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_bundle_stream_sidebar.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,74 @@ +{% load i18n %} +

    {% trans "About" %}

    +
    +
    {% trans "Pathname:" %}
    +
    {{ bundle_stream.pathname }}
    +
    {% trans "Name:" %}
    +
    {{ bundle_stream.name|default:"not set" }}
    +
    +

    {% trans "Ownership" %}

    +{% if bundle_stream.user %} +

    {% trans "This stream is owned by" %} {{ bundle_stream.user }}

    +{% else %} +

    {% trans "This stream is owned by group called" %} {{ bundle_stream.group }}

    +{% endif %} +

    {% trans "Access rights" %}

    +
    +
    {% trans "Stream type:" %}
    + {% if bundle_stream.is_anonymous %} +
    + Anonymous stream (what is this?) +
    +

    The dashboard has several types of containers for test results. One of the + most common and the oldest one is an anonymous stream. Anonymous + streams act like public FTP servers. Anyone can download or upload files at + will. There are some restrictions, nobody can change or remove existing + files

    + +

    When a stream is anonymous anonyone can upload new test results and the + identity of the uploading user is not recorded in the system. Anonymous + streams have to be public (granting read access to everyone)

    + +
    +
    + + Note: A stream can be marked as anonymous in the administration panel +
    +
    +
    + +
    +
    {% trans "Read access:" %}
    +
    {% trans "Anyone can download or read test results uploaded here" %}
    +
    {% trans "Write access:" %}
    +
    {% trans "Anyone can upload test results here" %} + {% else %} + {% if bundle_stream.is_public %} +
    {% trans "Public stream" %}
    +
    {% trans "Read access:" %}
    +
    {% trans "Anyone can download or read test results uploaded here" %}
    + {% else %} +
    {% trans "Private stream" %}
    +
    {% trans "Read access:" %}
    +
    {% trans "Only the owner can download or read test results uploaded here" %}
    + {% endif %} +
    {% trans "Write access:" %}
    +
    {% trans "Only the owner can upload test results here" %}
    + {% endif %} +
    === added file 'dashboard_app/templates/dashboard_app/_content.html' --- dashboard_app/templates/dashboard_app/_content.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_content.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,22 @@ +{% extends "layouts/content.html" %} + + +{% block extrahead %} +{{ block.super }} +{% include "dashboard_app/_extrahead.html" %} +{% endblock %} + + +{% block title %} +{{ block.super }}{% include "dashboard_app/_title.html" %} +{% endblock %} + + +{% block breadcrumbs %} +{% include "dashboard_app/_breadcrumbs.html" %} +{% endblock %} + + +{% block extension_navigation %} +{% include "dashboard_app/_extension_navigation.html" %} +{% endblock %} === added file 'dashboard_app/templates/dashboard_app/_content_with_sidebar.html' --- dashboard_app/templates/dashboard_app/_content_with_sidebar.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_content_with_sidebar.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,22 @@ +{% extends "layouts/content_with_sidebar.html" %} + + +{% block extrahead %} +{{ block.super }} +{% include "dashboard_app/_extrahead.html" %} +{% endblock %} + + +{% block title %} +{{ block.super }}{% include "dashboard_app/_title.html" %} +{% endblock %} + + +{% block breadcrumbs %} +{% include "dashboard_app/_breadcrumbs.html" %} +{% endblock %} + + +{% block extension_navigation %} +{% include "dashboard_app/_extension_navigation.html" %} +{% endblock %} === added file 'dashboard_app/templates/dashboard_app/_extension_navigation.html' --- dashboard_app/templates/dashboard_app/_extension_navigation.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_extension_navigation.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,13 @@ +{% load i18n %} + === added file 'dashboard_app/templates/dashboard_app/_extrahead.html' --- dashboard_app/templates/dashboard_app/_extrahead.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_extrahead.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,3 @@ + + + === added file 'dashboard_app/templates/dashboard_app/_test_run_list_table.html' --- dashboard_app/templates/dashboard_app/_test_run_list_table.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_test_run_list_table.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,31 @@ +{% load i18n %} + + + + + + + + + + + + + + + {% for test_run in test_run_list %} + + + + + + {% with test_run.get_summary_results as summary %} + + + + + {% endwith %} + + {% endfor %} + +
    {% trans "Test Run" %}{% trans "Test" %}{% trans "Uploaded On" %} {% trans "Analyzed" %}{% trans "Pass" %}{% trans "Fail" %}{% trans "Skip" %}{% trans "Unknown" %}
    {{ test_run.analyzer_assigned_uuid }}{{ test_run.test }}{{ test_run.bundle.uploaded_on }}{{ test_run.analyzer_assigned_date }}{{ summary.pass|default:0 }}{{ summary.fail|default:0 }}{{ summary.skip|default:0 }}{{ summary.unknown|default:0 }}
    === added file 'dashboard_app/templates/dashboard_app/_title.html' --- dashboard_app/templates/dashboard_app/_title.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/_title.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,1 @@ +{% for bread_crumb in bread_crumb_trail %} | {{ bread_crumb.get_name }}{% endfor %} === modified file 'dashboard_app/templates/dashboard_app/api.html' --- dashboard_app/templates/dashboard_app/api.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/api.html 2011-07-08 04:19:47 +0000 @@ -1,4 +1,4 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load markup %} {% load i18n %} @@ -7,6 +7,7 @@ {{ block.super }} | {% trans "XML-RPC API" %} {% endblock %} + {% block extrahead %} {{ block.super }} === modified file 'dashboard_app/templates/dashboard_app/attachment_detail.html' --- dashboard_app/templates/dashboard_app/attachment_detail.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/attachment_detail.html 2011-07-12 02:34:12 +0000 @@ -1,51 +1,46 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} {% load humanize %} -{% block title %} -{{ block.super }} | {% trans "Attachment" %} | {{ attachment.pk }} -{% endblock %} - - -{% block breadcrumbs %} -{{ block.super }} -
  • {{ attachment }}
  • -{% endblock %} - - -{% block sidebar %} -

    {% trans "Attachment Information" %}

    -
    -
    {% trans "Pathname" %}
    -
    {{ attachment.content_filename }}
    -
    {% trans "MIME type"%}
    -
    {{ attachment.mime_type }}
    -
    {% trans "Stored in dashboard" %}
    -
    {{ attachment.content|yesno }}
    - {% if attachment.content %} -
    {% trans "File size" %}
    -
    {{ attachment.content.size|filesizeformat }}
    - {% endif %} -
    {% trans "Stored on 3rd party server" %}
    -
    {{ attachment.public_url|yesno }}
    -
    {% trans "Public URL" %}
    -
    {{ attachment.public_url }}
    -
    -{% endblock %} - - {% block content %} -{% if lines %} -

    Inline viewer

    -
      - {% for line in lines %} -
    1. - {{line}} -
    2. - {% endfor %} -
    -{% else %} -

    Viewer not available

    -{% endif %} +
    + +
    +
    +
    {% trans "Pathname" %}
    +
    {{ attachment.content_filename }}
    +
    {% trans "MIME type"%}
    +
    {{ attachment.mime_type }}
    +
    {% trans "Stored in dashboard" %}
    +
    {{ attachment.content|yesno }}
    + {% if attachment.content %} +
    {% trans "File size" %}
    +
    {{ attachment.content.size|filesizeformat }}
    + {% endif %} +
    {% trans "Stored on 3rd party server" %}
    +
    {{ attachment.public_url|yesno }}
    + {% if attachment.public_url %} +
    {% trans "Public URL" %}
    +
    {{ attachment.public_url }}
    + {% endif %} +
    +
    +
    + {% endblock %} === added file 'dashboard_app/templates/dashboard_app/attachment_list.html' --- dashboard_app/templates/dashboard_app/attachment_list.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/attachment_list.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,40 @@ +{% extends "dashboard_app/_content.html" %} +{% load i18n %} + + +{% block content %} + + + + + + + + + + + {% for attachment in attachment_list %} + + + + + + + {% endfor %} + +
    NameSizeMIME typePublic URL
    {{ attachment.content_filename }}{{ attachment.content.size|filesizeformat }}{{ attachment.mime_type }} + {% if attachment.public_url %} + public url + {% endif %} +
    + +{% endblock %} === removed file 'dashboard_app/templates/dashboard_app/base.html' --- dashboard_app/templates/dashboard_app/base.html 2011-06-29 16:41:01 +0000 +++ dashboard_app/templates/dashboard_app/base.html 1970-01-01 00:00:00 +0000 @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} - - -{% block extension_navigation %} - -{% endblock %} === added file 'dashboard_app/templates/dashboard_app/bundle_detail.html' --- dashboard_app/templates/dashboard_app/bundle_detail.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/bundle_detail.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,68 @@ +{% extends "dashboard_app/_content.html" %} +{% load humanize %} +{% load i18n %} +{% load stylize %} + + +{% block extrahead %} +{{ block.super }} + +{% endblock %} + + +{% block content %} + +
    + + {% if bundle.is_deserialized %} +
    + {% with bundle.test_runs.all as test_run_list %} + {% include "dashboard_app/_test_run_list_table.html" %} + {% endwith %} +
    + {% endif %} + + {% if bundle.deserialization_error.get %} +
    +

    Cause

    +

    {{ bundle.deserialization_error.get.error_message }}

    +

    Deserialization failure traceback

    +
    + {% stylize "pytb" %}{{ bundle.deserialization_error.get.traceback|safe }}{% endstylize %} +
    +
    + {% endif %} +
    +{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/bundle_list.html' --- dashboard_app/templates/dashboard_app/bundle_list.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/bundle_list.html 2011-07-12 02:34:12 +0000 @@ -1,51 +1,78 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load humanize %} -{% load pagination_tags %} - -{% block title %} -{{ block.super }} | {% trans "Streams" %} | {% trans "Bundle Stream" %} {{ bundle_stream.pathname }} | {% trans "Bundles" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ bundle_stream }}
  • -
  • {% trans "Bundles" %}
  • -{% endblock %} - - -{% block sidebar %} -{% endblock %} {% block content %} - -{% autopaginate bundle_list %} -{% if invalid_page %} -

    {% trans "There are no bundles on this page." %}

    -

    {% trans "Try the" %} {% trans "first page" %} {% trans "instead." %}

    -{% else %} - {% if bundle_list.count %} - + + +
    + - - - - - + + + + + + + {% for bundle in bundle_list %} - + + - - + {% endfor %} -
    - {% trans "Uploaded On" %} -
    - {% trans "most recent first" %} -
    -
    {% trans "Uploaded by" %}{% trans "Content filename" %}{% trans "Content SHA1" %}{% trans "Deserialized" %}{% trans "Bundle SHA1" %}{% trans "Uploaded On" %}{% trans "Uploaded By" %}{% trans "Deserialized?" %}{% trans "Problems?" %}
    - {{ bundle.uploaded_on|naturalday }} - {{ bundle.uploaded_on|time }} - {{ bundle.content_sha1 }}{{ bundle.uploaded_on }} {% if bundle.uploaded_by %} {{ bundle.uploaded_by }} @@ -53,16 +80,15 @@ {% trans "anonymous user" %} {% endif %} {{ bundle.content_filename }}{{ bundle.content_sha1 }} {{ bundle.is_deserialized|yesno }}{% if bundle.deserialization_error.get %}yes{% endif %}
    - {% else %} -

    {% trans "There are no bundles in this stream yet." %}

    - {% endif %} -{% paginate %} -{% endif %} - + + +{% endblock %} + + +{% block sidebar %} +{% include "dashboard_app/_bundle_stream_sidebar.html" %} {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/bundle_stream_list.html' --- dashboard_app/templates/dashboard_app/bundle_stream_list.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/bundle_stream_list.html 2011-07-12 02:34:12 +0000 @@ -1,15 +1,38 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load pagination_tags %} -{% block title %} -{{ block.super }} | {% trans "Streams" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • +{% 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}}
    {% endblock %} @@ -34,34 +57,3 @@

    {% trans "You must" %} {% trans "sign in" %} {% trans "to get more access" %}

    {% endif %} {% endblock %} - - -{% block content %} -{% autopaginate bundle_stream_list %} -{% if bundle_stream_list.count %} - - - - - - - - {% 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}}
    -{% else %} -{% if user.is_staff %} -

    There are no streams yet, you can create one in the admin panel.

    -{% else %} -

    There are no streams yet.

    -{% endif %} -{% endif %} -{% paginate %} -{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/data_view_detail.html' --- dashboard_app/templates/dashboard_app/data_view_detail.html 2011-06-29 16:41:01 +0000 +++ dashboard_app/templates/dashboard_app/data_view_detail.html 2011-07-12 02:34:12 +0000 @@ -1,16 +1,6 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} -{% block title %} -{{ block.super }} | {% trans "Data Views" %} | {{ data_view.name }} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Data Views" %}
  • -
  • {{ data_view.name }}
  • -{% endblock %} - {% block content %}
    @@ -21,6 +11,7 @@
    Documentation:
    {{ data_view.documentation }}
    +{% if data_view.arguments %} @@ -38,4 +29,5 @@ {% endfor %}
    Aruments
    +{% endif %} {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/data_view_list.html' --- dashboard_app/templates/dashboard_app/data_view_list.html 2011-06-29 16:41:01 +0000 +++ dashboard_app/templates/dashboard_app/data_view_list.html 2011-07-12 02:34:12 +0000 @@ -1,34 +1,43 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} -{% block title %} -{{ block.super }} | {% trans "Data Views" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Data Views" %}
  • -{% endblock %} - - -{% block sidebar %} -

    Hint:

    -

    To call a data view use the lava-dashboard-tool command. See -lava-dashboard-tool query-data-view --help to get started.

    -{% endblock %} - {% block content %} - - - - - - {% for data_view in data_view_list %} - - - - -{% endfor %} +
    +
    + + Hint: To call a data view use the + lava-dashboard-tool command. See + lava-dashboard-tool query-data-view --help + to get started. +
    +
    +
    + +
    {% trans "Name" %}{% trans "Summary" %}
    {{ data_view.name }}{{ data_view.summary }}
    + + + + + + + + {% for data_view in data_view_list %} + + + + + {% endfor %} +
    {% trans "Name" %}{% trans "Summary" %}
    {{ data_view.name }}{{ data_view.summary }}
    {% endblock %} === added file 'dashboard_app/templates/dashboard_app/index.html' --- dashboard_app/templates/dashboard_app/index.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/index.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,10 @@ +{% extends "dashboard_app/_content.html" %} + +{% block content %} +

    TODO

    +
      +
    • Briefly mention key dashboard features
    • +
    • Link to readthedocs dashboard manual
    • +
    • Add sensible dashboard index page (recent/interesting stuff)
    • +
    +{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/report_detail.html' --- dashboard_app/templates/dashboard_app/report_detail.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/report_detail.html 2011-07-12 02:34:12 +0000 @@ -1,37 +1,31 @@ -{% extends "dashboard_app/base.html" %} - +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load stylize %} -{% block title %} -{{ block.super }} | {% trans "Reports" %} | {{ report.title }} -{% endblock %} + {% block extrahead %} {{ block.super }} - - - {% endblock %} -{% block breadcrumbs %} -
  • {% trans "Reports" %}
  • -
  • {{ report.title }}
  • +{% block content %} +{{ report.get_html|safe}} {% endblock %} + {% block sidebar %}

    Basic information

    Title
    {{report.title}}
    Author
    -
    {{report.author|default:"Unspecified"}}
    +
    {{report.author|default_if_none:"Unspecified"}}
    Bug report URL
    {{report.bug_report_url|default:"Unspecified"}}
    @@ -42,6 +36,9 @@ maintainer to debug the problem.

    Source Code

    + {% endblock %} - - -{% block content %} -{{ report.get_html|safe}} - -{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/report_list.html' --- dashboard_app/templates/dashboard_app/report_list.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/report_list.html 2011-07-12 02:34:12 +0000 @@ -1,27 +1,31 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} -{% block title %} -{{ block.super }} | {% trans "Reports" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Reports" %}
  • -{% endblock %} - {% block content %} - - - - - - {% for report in report_list %} - - - - -{% endfor %} + +
    {% trans "Report title" %}{% trans "Path of HTML file" %}
    {{ report.title }}
    {{ report.path }}
    + + + + + + + + {% for report in report_list %} + + + + + {% endfor %} +
    {% trans "Report title" %}{% trans "Author" %}
    {{ report.title }}{{ report.author|default_if_none:"unspecified" }}
    {% endblock %} === added file 'dashboard_app/templates/dashboard_app/test_detail.html' --- dashboard_app/templates/dashboard_app/test_detail.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/test_detail.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,46 @@ +{% extends "dashboard_app/_content.html" %} +{% load i18n %} + + +{% block content %} + + + + + + + + + + + + + {% for test_case in test.test_cases.all %} + + + + + + + + {% endfor %} + {% if test.count_results_without_test_case %} + + + + + + + + {% endif %} + +
    IDNameUnitsTotal ResultsTotal Failures
    {{ test_case.test_case_id }}{{ test_case.name|default:"not set" }}{{ test_case.units|default:"not set" }}{{ test_case.test_results.all.count }}{{ test_case.count_failures }}
    Results without test caseN/AN/A{{ test.count_results_without_test_case }}{{ test.count_failures_without_test_case }}
    +{% endblock %} === added file 'dashboard_app/templates/dashboard_app/test_list.html' --- dashboard_app/templates/dashboard_app/test_list.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/dashboard_app/test_list.html 2011-07-12 02:34:12 +0000 @@ -0,0 +1,34 @@ +{% extends "dashboard_app/_content.html" %} +{% load i18n %} + + +{% block content %} + + + + + + + + + + + + {% for test in test_list %} + + + + + + + {% endfor %} + +
    IDNameTest CasesTest Runs
    {{ test.test_id }}{{ test.name|default:"not set" }}{{ test.test_cases.all.count }}{{ test.test_runs.all.count }}
    +{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_result_detail.html' --- dashboard_app/templates/dashboard_app/test_result_detail.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/test_result_detail.html 2011-07-12 02:34:12 +0000 @@ -1,20 +1,6 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load humanize %} -{% load pagination_tags %} - - -{% block title %} -{{ block.super }} | {% trans "Test Results" %} | {{ test_result }} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ test_result.test_run.bundle.bundle_stream }}
  • -
  • {{ test_result.test_run }}
  • -
  • {{ test_result }}
  • -{% endblock %} {% block sidebar %} @@ -30,26 +16,27 @@ {% if test_result.test_run.test_results.count > 1 %}

    Other results

    Results from the same test run are available here

    - + + {% endif %} {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_detail.html' --- dashboard_app/templates/dashboard_app/test_run_detail.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/test_run_detail.html 2011-07-12 02:34:12 +0000 @@ -1,19 +1,43 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load humanize %} -{% load pagination_tags %} - - -{% block title %} -{{ block.super }} | {% trans "Test Runs" %} | {{ test_run }} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ test_run.bundle.bundle_stream }}
  • -
  • {{ test_run }}
  • -{% endblock %} + + +{% block content %} + + + + + + + + + + + {% for test_result in test_run.test_results.all %} + + + + + + + {% 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 }}
    + {% endblock %} {% block sidebar %} @@ -24,8 +48,8 @@
    {{ test_run.test }}
    {% trans "OS Distribution" %}
    {{ test_run.sw_image_desc|default:"Unspecified" }}
    -
    {% trans "Bundle information" %}
    -
    {{ test_run.bundle.content_sha1 }}
    +
    {% trans "Bundle SHA1" %}
    +
    {{ test_run.bundle.content_sha1 }}
    {% trans "Time check performed" %}
    {{ test_run.time_check_performed|yesno }}
    {% trans "Log analyzed on:" %}
    @@ -38,51 +62,16 @@ {{ test_run.import_assigned_date|naturalday }} {{ test_run.import_assigned_date|time }} -
    {% trans "Software Context" %}
    -
    - {% trans "more" %} -
    -
    {% trans "Hardware Context" %}
    -
    - {% trans "more" %} +
    {% trans "Other information:" %}
    +
    {% trans "Hardware context" %} ({{ test_run.devices.all.count }} devices)
    +
    {% trans "Software context" %} ({{ test_run.packages.all.count }} packages, {{ test_run.sources.all.count }} sources)
    +
    {% trans "Attachments" %} ({{ test_run.attachments.count }})
    {% endblock %} - - -{% block content %} -{% autopaginate test_run.test_results.all 50 as test_results %} -{% if invalid_page %} -

    {% trans "There is no content on this page." %}

    -

    {% trans "Try the" %} {% trans "first page" %} {% trans "instead." %}

    -{% else %} - - - - - - - - {% for test_result in test_results %} - - - - - {% if test_result.measurement %} - - {% endif %} - - {% endfor %} -
    #{% trans "Test case" %}{% trans "Result" %}{% trans "Measurement" %}
    {{ test_result.relative_index }}{{ test_result.test_case }} - - {{ test_result.get_result_display }} - {{ test_result.get_result_display }} - {{ test_result.measurement|default_if_none:"" }} {{ test_result.units }}
    -{% paginate %} -{% endif %} -{% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_hardware_context.html' --- dashboard_app/templates/dashboard_app/test_run_hardware_context.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/test_run_hardware_context.html 2011-07-12 02:34:12 +0000 @@ -1,45 +1,79 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} -{% load humanize %} -{% load pagination_tags %} - - -{% block title %} -{{ block.super }} | {% trans "Test Runs" %} | {{ test_run }} | {% trans "Hardware Context" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ test_run.bundle.bundle_stream }}
  • -
  • {{ test_run }}
  • -
  • {% trans "Hardware Context" %}
  • -{% endblock %} - - -{% block sidebar %} -{% endblock %} {% block content %} -

    Hardware Devices

    -
    -{% autopaginate test_run.devices.all as hardware_devices %} -{% for hardware_device in hardware_devices %} -
    {{ hardware_device.description }}
    -
    - - {% for attribute in hardware_device.attributes.all %} - - - - - {% endfor %} +
    {{ attribute.name }}{{ attribute.value }}
    + + + + + + + + + {% for hardware_device in test_run.devices.all %} + + + + + + {% endfor %} +
    DescriptionDevice TypeAttributes
    {{ hardware_device.description }}{{ hardware_device.get_device_type_display }} +
    + {% for attribute in hardware_device.attributes.all %} +
    {{ attribute.name }}
    +
    {{ attribute.value }}
    + {% endfor %} +
    +
    -
    -{% empty %} -There are no hardware devices associated with this test run -{% endfor %} -
    -{% paginate %} + {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_list.html' --- dashboard_app/templates/dashboard_app/test_run_list.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/test_run_list.html 2011-07-12 02:34:12 +0000 @@ -1,152 +1,65 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content_with_sidebar.html" %} {% load i18n %} {% load humanize %} -{% load pagination_tags %} - -{% block extrahead %} -{{ block.super }} - - - -{% endblock %} - -{% block title %} -{{ block.super }} | {% trans "Streams" %} | {% trans "Bundle Stream" %} {{ bundle_stream.pathname }} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ bundle_stream }}
  • + + +{% block content %} + + +{% include "dashboard_app/_test_run_list_table.html" %} {% endblock %} {% block sidebar %} -

    {% trans "About" %}

    -
    -
    {% trans "Pathname:" %}
    -
    {{ bundle_stream.pathname }}
    -
    {% trans "Name:" %}
    -
    {{ bundle_stream.name|default:"not set" }}
    -
    -

    {% trans "Ownership" %}

    -{% if bundle_stream.user %} -

    {% trans "This stream is owned by" %} {{ bundle_stream.user }}

    -{% else %} -

    {% trans "This stream is owned by group called" %} {{ bundle_stream.group }}

    -{% endif %} -

    {% trans "Access rights" %}

    -
    -
    {% trans "Stream type:" %}
    - {% if bundle_stream.is_anonymous %} -
    - Anonymous stream (what is this?) -
    -

    The dashboard has several types of containers for test results. One of the - most common and the oldest one is an anonymous stream. Anonymous - streams act like public FTP servers. Anyone can download or upload files at - will. There are some restrictions, nobody can change or remove existing - files

    - -

    When a stream is anonymous anonyone can upload new test results and the - identity of the uploading user is not recorded in the system. Anonymous - streams have to be public (granting read access to everyone)

    - -
    -
    - - Note: A stream can be marked as anonymous in the administration panel -
    -
    -
    - -
    -
    {% trans "Read access:" %}
    -
    {% trans "Anyone can download or read test results uploaded here" %}
    -
    {% trans "Write access:" %}
    -
    {% trans "Anyone can upload test results here" %} - {% else %} - {% if bundle_stream.is_public %} -
    {% trans "Public stream" %}
    -
    {% trans "Read access:" %}
    -
    {% trans "Anyone can download or read test results uploaded here" %}
    - {% else %} -
    {% trans "Private stream" %}
    -
    {% trans "Read access:" %}
    -
    {% trans "Only the owner can download or read test results uploaded here" %}
    - {% endif %} -
    {% trans "Write access:" %}
    -
    {% trans "Only the owner can upload test results here" %}
    - {% endif %} -
    -{% endblock %} - - -{% block content %} - -{% autopaginate test_run_list %} -{% if invalid_page %} -

    {% trans "There are no test runs on this page." %}

    -

    {% trans "Try the" %} {% trans "first page" %} {% trans "instead." %}

    -{% else %} - {% if test_run_list.count %} - - - - - - - - - - - - {% for test_run in test_run_list %} - - - - - - {% with test_run.get_summary_results as summary %} - - - - - {% endwith %} - - {% endfor %} -
    - {% trans "Uploaded On" %} -
    - {% trans "most recent first" %} -
    -
    {% trans "Analyzed" %}{% trans "Test" %}{% trans "Run" %}{% trans "Pass" %}{% trans "Fail" %}{% trans "Skip" %}{% trans "Unknown" %}
    - {{ test_run.bundle.uploaded_on|naturalday }} - {{ test_run.bundle.uploaded_on|time }} - - {{ test_run.analyzer_assigned_date|timesince }} - {% trans "ago" %} - {{ test_run.test }}{{ test_run }}{{ summary.pass|default:0 }}{{ summary.fail|default:0 }}{{ summary.skip|default:0 }}{{ summary.unknown|default:0 }}
    - {% else %} -

    {% trans "There are no test runs in this stream yet." %}

    - {% endif %} -{% paginate %} -{% endif %} - +{% include "dashboard_app/_bundle_stream_sidebar.html" %} {% endblock %} === modified file 'dashboard_app/templates/dashboard_app/test_run_software_context.html' --- dashboard_app/templates/dashboard_app/test_run_software_context.html 2011-06-29 14:00:01 +0000 +++ dashboard_app/templates/dashboard_app/test_run_software_context.html 2011-07-12 02:34:12 +0000 @@ -1,57 +1,85 @@ -{% extends "dashboard_app/base.html" %} +{% extends "dashboard_app/_content.html" %} {% load i18n %} -{% load humanize %} -{% load pagination_tags %} - - -{% block title %} -{{ block.super }} | {% trans "Test Runs" %} | {{ test_run }} | {% trans "Software Context" %} -{% endblock %} - - -{% block breadcrumbs %} -
  • {% trans "Bundle Streams" %}
  • -
  • {{ test_run.bundle.bundle_stream }}
  • -
  • {{ test_run }}
  • -
  • {% trans "Software Context" %}
  • -{% endblock %} - - -{% block sidebar %} -{% endblock %} {% block content %} -

    Software Packages

    -
      -{% autopaginate test_run.packages.all as software_packages %} -{% for software_package in software_packages %} -
    • Package {{software_package.name}} version {{software_package.version}}
    • -{% empty %} - There are no software packages associated with this test run -{% endfor %} -
    -{% paginate %} - -

    Source Sources

    -
      -{% for software_source in test_run.sources.all %} -
    • - {% if software_source.is_hosted_on_launchpad %} - Launchpad project {{ software_source.project_name }} - from bazaar branch {{ software_source.branch_url }} - {% if software_source.is_tag_revision %} - at tag {{ software_source.branch_tag }} - {% else %} - at revision {{ software_source.branch_revision }} - {% endif %} - {% else %} - Project {{software_source.project_name}} from {{software_source.branch_vcs}} - branch {{software_source.branch_url}} at revision {{software_source.branch_revision}} - {% endif %} -
    • -{% empty %} - There are no source references associated with this test run -{% endfor %} -
    +
    + + +
    + + + + + + + + + {% for software_package in test_run.packages.all %} + + + + + {% endfor %} + +
    NameVersion
    {{software_package.name}}{{software_package.version}}
    +
    + +
    + + + + + + + + + + + {% for software_source in test_run.sources.all %} + + {% if software_source.is_hosted_on_launchpad %} + + + + {% if software_source.is_tag_revision %} + + {% else %} + + {% endif %} + {% else %} + + + + + {% endif %} + + {% endfor %} + +
    ProjectVCSBranchTag or revision
    {{ software_source.project_name }}{{software_source.branch_vcs}}{{ software_source.branch_url }}{{ software_source.branch_tag }}{{ software_source.branch_revision }}{{software_source.project_name}}{{software_source.branch_vcs}}{{software_source.branch_url}}{{software_source.branch_revision}}
    +
    +
    + + {% endblock %} === modified file 'dashboard_app/tests/views/test_run_detail_view.py' --- dashboard_app/tests/views/test_run_detail_view.py 2011-05-23 17:02:43 +0000 +++ dashboard_app/tests/views/test_run_detail_view.py 2011-07-12 02:33:19 +0000 @@ -42,12 +42,12 @@ self.assertTemplateUsed(response, "dashboard_app/test_run_detail.html") - def testrun_invalid_page_view(self): - invalid_uuid = "0000000-0000-0000-0000-000000000000" - invalid_test_run_url = reverse("dashboard_app.views.test_run_detail", - args=[invalid_uuid]) - response = self.client.get(invalid_test_run_url) - self.assertEqual(response.status_code, 404) + #def testrun_invalid_page_view(self): + # invalid_uuid = "0000000-0000-0000-0000-000000000000" + # invalid_test_run_url = reverse("dashboard_app.views.test_run_detail", + # args=[invalid_uuid]) + # response = self.client.get(invalid_test_run_url) + # self.assertEqual(response.status_code, 404) class TestRunViewAuth(TestCaseWithScenarios): === modified file 'dashboard_app/urls.py' --- dashboard_app/urls.py 2011-06-29 16:41:01 +0000 +++ dashboard_app/urls.py 2011-07-12 02:30:17 +0000 @@ -24,17 +24,24 @@ urlpatterns = patterns( 'dashboard_app.views', - url(r'^streams/$', 'bundle_stream_list'), - url(r'^test-results/(?P[0-9]+)/$', 'test_result_detail'), - url(r'^test-runs/(?P[a-zA-Z0-9-]+)/$', 'test_run_detail'), - url(r'^test-runs/(?P[a-zA-Z0-9-]+)/software-context$', 'test_run_software_context'), - url(r'^test-runs/(?P[a-zA-Z0-9-]+)/hardware-context$', 'test_run_hardware_context'), - url(r'^streams(?P/[a-zA-Z0-9/_-]+)$', 'test_run_list'), - url(r'^streams(?P/[a-zA-Z0-9/_-]+?)\+bundles$', 'bundle_list'), - url(r'^attachments/(?P[0-9]+)/$', 'attachment_detail'), - url(r'^xml-rpc/', 'dashboard_xml_rpc_handler'), + url(r'^$', 'index'), + url(r'^ajax/bundle-viewer/(?P[0-9]+)/$', 'ajax_bundle_viewer'), + url(r'^ajax/attachment-viewer/(?P[0-9]+)/$', 'ajax_attachment_viewer'), url(r'^data-views/$', 'data_view_list'), url(r'^data-views/(?P[a-zA-Z0-9-_]+)/$', 'data_view_detail'), url(r'^reports/$', 'report_list'), url(r'^reports/(?P[a-zA-Z0-9-_]+)/$', 'report_detail'), + url(r'^tests/$', 'test_list'), + url(r'^tests/(?P[^/]+)/$', 'test_detail'), + url(r'^xml-rpc/', 'dashboard_xml_rpc_handler'), + url(r'^streams/$', 'bundle_stream_list'), + url(r'^streams(?P/[a-zA-Z0-9/_-]+)bundles/$', 'bundle_list'), + 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]+)/(?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-]+)/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'), ) === modified file 'dashboard_app/views.py' --- dashboard_app/views.py 2011-06-29 16:41:01 +0000 +++ dashboard_app/views.py 2011-07-12 02:30:17 +0000 @@ -28,11 +28,22 @@ from django.http import (HttpResponse, Http404) from django.shortcuts import render_to_response from django.template import RequestContext +from django.views.generic.list_detail import object_list, object_detail from dashboard_app.dataview import DataView, DataViewRepository from dashboard_app.dispatcher import DjangoXMLRPCDispatcher -from dashboard_app.models import Attachment, BundleStream, TestRun, TestResult, DataReport +from dashboard_app.models import ( + Attachment, + Bundle, + BundleStream, + DataReport, + Test, + TestCase, + TestResult, + TestRun, +) from dashboard_app.xmlrpc import DashboardAPI +from dashboard_app.bread_crumbs import BreadCrumb, BreadCrumbTrail def _get_dashboard_dispatcher(): @@ -123,15 +134,23 @@ return xml_rpc_handler(request, DashboardDispatcher) +@BreadCrumb("Dashboard") +def index(request): + return render_to_response( + "dashboard_app/index.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(index) + }, RequestContext(request)) + + +@BreadCrumb("Bundle Streams", parent=index) def bundle_stream_list(request): """ List of bundle streams. - - The list is paginated and dynamically depends on the currently - logged in user. """ return render_to_response( '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'), 'has_personal_streams': ( request.user.is_authenticated() and @@ -144,12 +163,84 @@ ) +@BreadCrumb( + "Bundles in {pathname}", + parent=bundle_stream_list, + needs=['pathname']) +def bundle_list(request, pathname): + """ + List of bundles in a specified bundle stream. + """ + bundle_stream = get_restricted_object_or_404( + BundleStream, + lambda bundle_stream: bundle_stream, + request.user, + pathname=pathname + ) + return object_list( + request, + queryset=bundle_stream.bundles.all().order_by('-uploaded_on'), + template_name="dashboard_app/bundle_list.html", + template_object_name="bundle", + extra_context={ + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + bundle_list, + pathname=pathname), + "bundle_stream": bundle_stream + }) + + +@BreadCrumb( + "Bundle {content_sha1}", + parent=bundle_list, + needs=['pathname', 'content_sha1']) +def bundle_detail(request, pathname, content_sha1): + """ + Detail about a bundle from a particular stream + """ + bundle_stream = get_restricted_object_or_404( + BundleStream, + lambda bundle_stream: bundle_stream, + request.user, + pathname=pathname + ) + return object_detail( + request, + queryset=bundle_stream.bundles.all(), + slug=content_sha1, + slug_field="content_sha1", + template_name="dashboard_app/bundle_detail.html", + template_object_name="bundle", + extra_context={ + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + bundle_detail, + pathname=pathname, + content_sha1=content_sha1), + "bundle_stream": bundle_stream + }) + + +def ajax_bundle_viewer(request, pk): + bundle = get_restricted_object_or_404( + Bundle, + lambda bundle: bundle.bundle_stream, + request.user, + pk=pk + ) + return render_to_response( + "dashboard_app/_ajax_bundle_viewer.html", { + "bundle": bundle + }, + 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. - - The list is paginated and dynamically depends on the currently - logged in user. """ bundle_stream = get_restricted_object_or_404( BundleStream, @@ -159,94 +250,183 @@ ) return render_to_response( 'dashboard_app/test_run_list.html', { - "test_run_list": TestRun.objects.filter(bundle__bundle_stream=bundle_stream).order_by('-bundle__uploaded_on'), - "bundle_stream": bundle_stream, - }, RequestContext(request) - ) - - -def bundle_list(request, pathname): - """ - List of bundles in a specified bundle stream. - - The list is paginated and dynamically depends on the currently logged in - user. - """ - bundle_stream = get_restricted_object_or_404( - BundleStream, - lambda bundle_stream: bundle_stream, - request.user, - pathname=pathname - ) - return render_to_response( - 'dashboard_app/bundle_list.html', { - "bundle_list": bundle_stream.bundles.all().order_by('-uploaded_on'), - "bundle_stream": bundle_stream, - }, RequestContext(request) - ) - - -def _test_run_view(template_name): - def view(request, 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 render_to_response( - template_name, { - "test_run": test_run - }, RequestContext(request) - ) - return view - - -test_run_detail = _test_run_view("dashboard_app/test_run_detail.html") -test_run_software_context = _test_run_view("dashboard_app/test_run_software_context.html") -test_run_hardware_context = _test_run_view("dashboard_app/test_run_hardware_context.html") - - -def test_result_detail(request, pk): - test_result = get_restricted_object_or_404( - TestResult, - lambda test_result: test_result.test_run.bundle.bundle_stream, - request.user, - pk = pk - ) + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_run_list, + pathname=pathname), + "test_run_list": TestRun.objects.filter( + bundle__bundle_stream=bundle_stream), + "bundle_stream": bundle_stream, + }, RequestContext(request) + ) + + +@BreadCrumb( + "Run {analyzer_assigned_uuid}", + parent=bundle_detail, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid']) +def test_run_detail(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 render_to_response( + "dashboard_app/test_run_detail.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_run_detail, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid), + "test_run": test_run + }, RequestContext(request)) + + +@BreadCrumb( + "Software Context", + parent=test_run_detail, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid']) +def test_run_software_context(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 render_to_response( + "dashboard_app/test_run_software_context.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_run_software_context, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid), + "test_run": test_run + }, RequestContext(request)) + + +@BreadCrumb( + "Hardware Context", + parent=test_run_detail, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid']) +def test_run_hardware_context(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 render_to_response( + "dashboard_app/test_run_hardware_context.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_run_hardware_context, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid), + "test_run": test_run + }, RequestContext(request)) + + +@BreadCrumb( + "Result {relative_index}", + parent=test_run_detail, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid', 'relative_index']) +def test_result_detail(request, pathname, content_sha1, analyzer_assigned_uuid, relative_index): + test_run = get_restricted_object_or_404( + TestRun, + lambda test_run: test_run.bundle.bundle_stream, + request.user, + analyzer_assigned_uuid=analyzer_assigned_uuid + ) + test_result = test_run.test_results.get(relative_index=relative_index) return render_to_response( "dashboard_app/test_result_detail.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + test_result_detail, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid, + relative_index=relative_index), "test_result": test_result - }, RequestContext(request) + }, RequestContext(request)) + + +@BreadCrumb( + "Attachments", + parent=test_run_detail, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid']) +def attachment_list(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 ) - - -def attachment_detail(request, pk): + return object_list( + request, + queryset=test_run.attachments.all(), + template_name="dashboard_app/attachment_list.html", + template_object_name="attachment", + extra_context={ + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + attachment_list, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid), + 'test_run': test_run}) + + +@BreadCrumb( + "{content_filename}", + parent=attachment_list, + needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid', 'content_filename']) +def attachment_detail(request, pathname, content_sha1, analyzer_assigned_uuid, pk): attachment = get_restricted_object_or_404( Attachment, - lambda attachment: attachment.content_object.bundle.bundle_stream, + lambda attachment: attachment.test_run.bundle.bundle_stream, request.user, pk = pk ) + return render_to_response( + "dashboard_app/attachment_detail.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to( + attachment_detail, + pathname=pathname, + content_sha1=content_sha1, + analyzer_assigned_uuid=analyzer_assigned_uuid, + content_filename=attachment.content_filename), + "attachment": attachment, + }, RequestContext(request)) + + +def ajax_attachment_viewer(request, pk): + attachment = get_restricted_object_or_404( + Attachment, + lambda attachment: attachment.test_run.bundle.bundle_stream, + request.user, + pk=pk + ) if attachment.mime_type == "text/plain": data = attachment.get_content_if_possible(mirror=request.user.is_authenticated()) else: data = None return render_to_response( - "dashboard_app/attachment_detail.html", { + "dashboard_app/_ajax_attachment_viewer.html", { + "attachment": attachment, "lines": data.splitlines() if data else None, - "attachment": attachment, - }, RequestContext(request) - ) - - + }, + RequestContext(request)) + + +@BreadCrumb("Reports", parent=index) def report_list(request): return render_to_response( "dashboard_app/report_list.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(report_list), "report_list": DataReport.repository.all() }, RequestContext(request)) +@BreadCrumb("{title}", parent=report_list, needs=['name']) def report_detail(request, name): try: report = DataReport.repository.get(name=name) @@ -254,18 +434,22 @@ raise Http404('No report matches given name.') return render_to_response( "dashboard_app/report_detail.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(report_detail, name=report.name, title=report.title), "report": report, }, RequestContext(request)) +@BreadCrumb("Data views", parent=index) def data_view_list(request): repo = DataViewRepository.get_instance() return render_to_response( "dashboard_app/data_view_list.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(data_view_list), "data_view_list": repo.data_views }, RequestContext(request)) +@BreadCrumb("Details of {name}", parent=data_view_list, needs=['name']) def data_view_detail(request, name): repo = DataViewRepository.get_instance() try: @@ -274,5 +458,32 @@ raise Http404('No data view matches the given query.') return render_to_response( "dashboard_app/data_view_detail.html", { + 'bread_crumb_trail': BreadCrumbTrail.leading_to(data_view_detail, name=data_view.name, summary=data_view.summary), "data_view": data_view }, RequestContext(request)) + + +@BreadCrumb("Tests", parent=index) +def test_list(request): + return object_list( + request, + queryset=Test.objects.all(), + template_name="dashboard_app/test_list.html", + template_object_name="test", + extra_context={ + 'bread_crumb_trail': BreadCrumbTrail.leading_to(test_list) + }) + + +@BreadCrumb("Details of {test_id}", parent=test_list, needs=['test_id']) +def test_detail(request, test_id): + return object_detail( + request, + queryset=Test.objects.all(), + slug=test_id, + slug_field="test_id", + template_name="dashboard_app/test_detail.html", + template_object_name="test", + extra_context={ + 'bread_crumb_trail': BreadCrumbTrail.leading_to(test_detail, test_id=test_id) + }) === added directory 'production' === added directory 'production/reports' === added directory 'production/views'