diff --git a/doorcontrol/urls.py b/doorcontrol/urls.py index f8702ab..3eac564 100644 --- a/doorcontrol/urls.py +++ b/doorcontrol/urls.py @@ -1,23 +1,5 @@ -from django.urls import path - from . import views app_name = "doorcontrol" -urlpatterns = [ - path( - "reports/access-per-", - views.AccessPerUnitTime.as_view(), - name="access-per-unit-time", - ), - path( - "reports/denied-access", - views.DeniedAccess.as_view(), - name="denied-access", - ), - path( - "reports/most-active-members", - views.MostActiveMembers.as_view(), - name="most-active-members", - ), -] +urlpatterns = [report._urlpattern() for report in views.REPORTS] diff --git a/doorcontrol/views.py b/doorcontrol/views.py index 5c0424f..572cab2 100644 --- a/doorcontrol/views.py +++ b/doorcontrol/views.py @@ -1,3 +1,4 @@ +import calendar import datetime from django.contrib.auth.mixins import PermissionRequiredMixin @@ -5,7 +6,7 @@ from django.core.paginator import Page from django.core.exceptions import BadRequest from django.db.models import Count from django.db.models.functions import Trunc -from django.urls import reverse_lazy +from django.urls import reverse_lazy, path from django.utils import dateparse from django.utils.formats import date_format from django.utils.text import slugify @@ -15,11 +16,11 @@ from django.views.generic.list import ListView from .models import HIDEvent -REPORT_TYPES = [] +REPORTS = [] def register_report(cls: "BaseAccessReport"): - REPORT_TYPES.extend(cls._report_types()) + REPORTS.append(cls) return cls @@ -39,6 +40,11 @@ class BaseAccessReport(PermissionRequiredMixin, ListView): reverse_lazy("doorcontrol:" + slugify(cls._report_name)), ] + @classmethod + def _urlpattern(cls): + slug = slugify(cls._report_name) + return path(f"reports/{slug}", cls.as_view(), name=slug) + def _selected_report(self): return self._report_name @@ -69,7 +75,9 @@ class BaseAccessReport(PermissionRequiredMixin, ListView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["report_types"] = REPORT_TYPES + context["report_types"] = [ + rt for report in REPORTS for rt in report._report_types() + ] page: Page = context["page_obj"] context["paginator_range"] = page.paginator.get_elided_page_range(page.number) @@ -100,6 +108,14 @@ class AccessPerUnitTime(BaseAccessReport): reverse_lazy("doorcontrol:access-per-unit-time", args=[unit_time]), ) + @classmethod + def _urlpattern(cls): + return path( + "reports/access-per-", + cls.as_view(), + name="access-per-unit-time", + ) + def _selected_report(self) -> str: return "Access per " + self.kwargs["unit_time"].title() @@ -204,3 +220,35 @@ class MostActiveMembers(BaseAccessReport): } for count in counts ] + + +@register_report +class BusiestDayOfWeek(BaseAccessReport): + _report_name = "Busiest Day of the Week" + + def get_queryset(self): + return [ + { + "week day": calendar.day_name[(count["timestamp__week_day"] - 2) % 7], + "events": count["events"], + } + for count in super() + .get_queryset() + .values("timestamp__week_day") + .annotate(events=Count("timestamp")) + ] + + +@register_report +class BusiestTimeOfDay(BaseAccessReport): + _report_name = "Busiest Time of Day" + paginate_by = 24 + + def get_queryset(self): + return [ + {"hour": f'{count["timestamp__hour"]}:00', "events": count["events"]} + for count in super() + .get_queryset() + .values("timestamp__hour") + .annotate(events=Count("timestamp")) + ]