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

1import csv 

2from functools import wraps 

3 

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 

12 

13from . import report 

14from .forms import RangeForm 

15 

16PERMISSION = "metrics.view_dailymetric" 

17 

18 

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)) 

22 

23 

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) 

31 

32 return wrapper 

33 

34 

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) 

61 

62 

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