Coverage for baibu/metrics/views.py: 100%
47 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-06 13:07 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-06 13:07 +0000
1import csv
2from functools import wraps
4from django.conf import settings
5from django.contrib.auth.decorators import login_required
6from django.core.exceptions import PermissionDenied
7from django.http import HttpResponse
8from django.http import HttpResponseBadRequest
9from django.shortcuts import render
10from django.utils import timezone
11from django.views.decorators.http import require_GET
13from . import report
14from .forms import RangeForm
16PERMISSION = "metrics.view_dailymetric"
19def can_view_usage(user) -> bool:
20 """Staff with permission to view daily metrics (superusers have every permission)."""
21 return bool(user.is_authenticated and user.is_staff and user.has_perm(PERMISSION))
24def usage_access_required(view):
25 @wraps(view)
26 @login_required
27 def wrapper(request, *args, **kwargs):
28 if not can_view_usage(request.user):
29 raise PermissionDenied
30 return view(request, *args, **kwargs)
32 return wrapper
35@require_GET
36@usage_access_required
37def usage(request):
38 form = RangeForm(request.GET or None, initial=RangeForm.defaults())
39 chosen = form.range()
40 start, end = chosen["start"], chosen["end"]
41 anchor = report.latest_day(timezone.localdate())
42 rows = report.daily_rows(start, end)
43 context = {
44 "form": form,
45 "start": start,
46 "end": end,
47 "anchor": anchor,
48 "users_total": report.users_total(anchor) if anchor else None,
49 "headlines": report.headlines(anchor) if anchor else [],
50 "columns": report.COLUMNS,
51 "rows": rows,
52 "has_data": any(row["computed"] for row in rows),
53 "chart": report.chart(rows, report.COLUMNS_BY_KEY[chosen["chart"]]),
54 "breakdowns": report.breakdowns(start, end),
55 "consent_day": report.latest_day(end),
56 "retention": report.retention(start, end),
57 "min_group_size": settings.METRICS_MIN_GROUP_SIZE,
58 }
59 context["consent"] = report.consent(context["consent_day"])
60 return render(request, "metrics/usage.html", context)
63@require_GET
64@usage_access_required
65def export(request):
66 form = RangeForm(request.GET or None)
67 if form.is_bound and not form.is_valid():
68 return HttpResponseBadRequest("Invalid date range.")
69 chosen = form.range()
70 start, end = chosen["start"], chosen["end"]
71 response = HttpResponse(content_type="text/csv; charset=utf-8")
72 response["Content-Disposition"] = f'attachment; filename="usage-{start}-{end}.csv"'
73 writer = csv.writer(response)
74 writer.writerows(report.export_rows(start, end))
75 return response