diff --git a/registrasion/reporting/reports.py b/registrasion/reporting/reports.py index ab8e924d..eb2c33a6 100644 --- a/registrasion/reporting/reports.py +++ b/registrasion/reporting/reports.py @@ -1,6 +1,10 @@ +import csv +import forms + from django.contrib.auth.decorators import user_passes_test from django.shortcuts import render from django.core.urlresolvers import reverse +from django.http import HttpResponse from functools import wraps from registrasion import views @@ -194,44 +198,114 @@ def report_view(title, form_type=None): ''' + # Create & return view def _report(view): - - @wraps(view) - @user_passes_test(views._staff_only) - def inner_view(request, *a, **k): - - if form_type is not None: - form = form_type(request.GET) - form.is_valid() - else: - form = None - - reports = view(request, form, *a, **k) - - if isinstance(reports, Report): - reports = [reports] - - reports = [ - _ReportTemplateWrapper("text/html", report) - for report in reports - ] - - ctx = { - "title": title, - "form": form, - "reports": reports, - } - - return render(request, "registrasion/report.html", ctx) + report_view = ReportView(view, title, form_type) + report_view = user_passes_test(views._staff_only)(report_view) + report_view = wraps(view)(report_view) # Add this report to the list of reports. - _all_report_views.append(inner_view) + _all_report_views.append(report_view) + + return report_view - # Return the callable - return inner_view return _report +class ReportView(object): + + def __init__(self, inner_view, title, form_type): + # Consolidate form_type so it has content type and section + self.inner_view = inner_view + self.title = title + self.form_type = form_type + + def __call__(self, request, *a, **k): + data = ReportViewRequestData(self, request, *a, **k) + return self.render(data) + + def get_form(self, request): + + # Create a form instance + if self.form_type is not None: + form = self.form_type(request.GET) + + # Pre-validate it + form.is_valid() + else: + form = None + + return form + + @classmethod + def wrap_reports(cls, reports, content_type): + reports = [ + _ReportTemplateWrapper(content_type, report) + for report in reports + ] + + return reports + + def render(self, data): + renderers = { + "text/csv": self._render_as_csv, + "text/html": self._render_as_html, + None: self._render_as_html, + } + render = renderers[data.content_type] + return render(data) + + def _render_as_html(self, data): + ctx = { + "title": self.title, + "form": data.form, + "reports": data.reports, + } + + return render(data.request, "registrasion/report.html", ctx) + + def _render_as_csv(self, data): + report = data.reports[data.section] + + # Create the HttpResponse object with the appropriate CSV header. + response = HttpResponse(content_type='text/csv') + + writer = csv.writer(response) + encode = lambda i: i.encode("utf8") if isinstance(i, unicode) else i + writer.writerow(list(encode(i) for i in report.headings())) + for row in report.rows(): + writer.writerow(list(encode(i) for i in row)) + + return response + + +class ReportViewRequestData(object): + + def __init__(self, report_view, request, *a, **k): + self.report_view = report_view + self.request = request + + # Calculate other data + self.form = report_view.get_form(request) + + # Content type and section come from request.GET + self.content_type = request.GET.get("content_type") + self.section = request.GET.get("section") + self.section = int(self.section) if self.section else None + + # Reports come from calling the inner view + reports = report_view.inner_view(request, self.form, *a, **k) + + # Normalise to a list + if isinstance(reports, Report): + reports = [reports] + + # Wrap them in appropriate format + reports = ReportView.wrap_reports(reports, self.content_type) + + self.reports = reports + + def get_all_reports(): ''' Returns all the views that have been registered with @report ''' diff --git a/registrasion/reporting/views.py b/registrasion/reporting/views.py index a5784014..2aaa3c31 100644 --- a/registrasion/reporting/views.py +++ b/registrasion/reporting/views.py @@ -408,7 +408,7 @@ def attendee(request, form, user_id=None): ''' Returns a list of all manifested attendees if no attendee is specified, else displays the attendee manifest. ''' - if user_id is None and not form.has_changed(): + if user_id is None: return attendee_list(request) if form.cleaned_data["user"] is not None: diff --git a/registrasion/templatetags/registrasion_tags.py b/registrasion/templatetags/registrasion_tags.py index fda5b2c0..1f408ed1 100644 --- a/registrasion/templatetags/registrasion_tags.py +++ b/registrasion/templatetags/registrasion_tags.py @@ -4,6 +4,7 @@ from registrasion.controllers.item import ItemController from django import template from django.db.models import Sum +from urllib import urlencode register = template.Library() @@ -74,3 +75,16 @@ def items_purchased(context, category=None): return ItemController(context.request.user).items_purchased( category=category ) + + +@register.assignment_tag(takes_context=True) +def report_as_csv(context, section): + + old_query = context.request.META["QUERY_STRING"] + query = dict([("section", section), ("content_type", "text/csv")]) + querystring = urlencode(query) + + if old_query: + querystring = old_query + "&" + querystring + + return context.request.path + "?" + querystring