Files
@ ea07469634ee
Branch filter:
Location: symposion_app/registrasion/reporting/reports.py
ea07469634ee
8.3 KiB
text/x-python
Fixes individual attendee view, which had disappeared.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | 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
''' A list of report views objects that can be used to load a list of
reports. '''
_all_report_views = []
class Report(object):
def __init__(self):
pass
def title():
raise NotImplementedError
def headings():
''' Returns the headings for the report. '''
raise NotImplementedError
def rows(content_type):
'''
Arguments:
content_type (str): The content-type for the output format of this
report.
Returns:
An iterator, which yields each row of the data. Each row should
be an iterable containing the cells, rendered appropriately for
content_type.
'''
raise NotImplementedError
def _linked_text(self, content_type, address, text):
'''
Returns:
an HTML linked version of text, if the content_type for this report
is HTMLish, otherwise, the text.
'''
if content_type == "text/html":
return Report._html_link(address, text)
else:
return text
@staticmethod
def _html_link(address, text):
return '<a href="%s">%s</a>' % (address, text)
class _ReportTemplateWrapper(object):
''' Used internally to pass `Report` objects to templates. They effectively
are used to specify the content_type for a report. '''
def __init__(self, content_type, report):
self.content_type = content_type
self.report = report
def title(self):
return self.report.title()
def headings(self):
return self.report.headings()
def rows(self):
return self.report.rows(self.content_type)
class BasicReport(Report):
def __init__(self, title, headings, link_view=None):
super(BasicReport, self).__init__()
self._title = title
self._headings = headings
self._link_view = link_view
def title(self):
''' Returns the title for this report. '''
return self._title
def headings(self):
''' Returns the headings for the table. '''
return self._headings
def cell_text(self, content_type, index, text):
if index > 0 or not self._link_view:
return text
else:
address = self.get_link(text)
return self._linked_text(content_type, address, text)
def get_link(self, argument):
return reverse(self._link_view, args=[argument])
class ListReport(BasicReport):
def __init__(self, title, headings, data, link_view=None):
super(ListReport, self).__init__(title, headings, link_view=link_view)
self._data = data
def rows(self, content_type):
''' Returns the data rows for the table. '''
for row in self._data:
yield [
self.cell_text(content_type, i, cell)
for i, cell in enumerate(row)
]
class QuerysetReport(BasicReport):
def __init__(self, title, attributes, queryset, headings=None,
link_view=None):
super(QuerysetReport, self).__init__(
title, headings, link_view=link_view
)
self._attributes = attributes
self._queryset = queryset
def headings(self):
if self._headings is not None:
return self._headings
return [
" ".join(i.split("_")).capitalize() for i in self._attributes
]
def rows(self, content_type):
def rgetattr(item, attr):
for i in attr.split("__"):
item = getattr(item, i)
if callable(item):
try:
return item()
except TypeError:
pass
return item
for row in self._queryset:
yield [
self.cell_text(content_type, i, rgetattr(row, attribute))
for i, attribute in enumerate(self._attributes)
]
class Links(Report):
def __init__(self, title, links):
'''
Arguments:
links ([tuple, ...]): a list of 2-tuples:
(url, link_text)
'''
self._title = title
self._links = links
def title(self):
return self._title
def headings(self):
return []
def rows(self, content_type):
print self._links
for url, link_text in self._links:
yield [
self._linked_text(content_type, url, link_text)
]
def report_view(title, form_type=None):
''' Decorator that converts a report view function into something that
displays a Report.
Arguments:
title (str):
The title of the report.
form_type (Optional[forms.Form]):
A form class that can make this report display things. If not
supplied, no form will be displayed.
'''
# Create & return view
def _report(view):
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(report_view)
return report_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
if self.content_type is None:
self.content_type = "text/html"
# 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 '''
return list(_all_report_views)
|