Source code for django_program.manage.views_checkin

"""Check-in dashboard and scanner views for the manage app.

Provides the staff-facing scanner interface for on-site check-in and a
real-time dashboard showing check-in statistics, station activity, and
product redemption counts.
"""

from django.db.models import Count, Max
from django.utils import timezone
from django.views.generic import TemplateView

from django_program.manage.views import ManagePermissionMixin
from django_program.registration.attendee import Attendee
from django_program.registration.checkin import CheckIn, DoorCheck, ProductRedemption


[docs] class CheckInScannerView(ManagePermissionMixin, TemplateView): """Staff-facing scanner page for on-site check-in. Renders the scanner template with conference context. All scanning logic is handled client-side via the check-in API endpoints. """ template_name = "django_program/manage/checkin_scanner.html" required_permission = "view_checkin"
[docs] def get_context_data(self, **kwargs: object) -> dict[str, object]: """Add active_nav to the template context. Args: **kwargs: Additional context data. Returns: Template context with conference and active navigation state. """ context = super().get_context_data(**kwargs) context["active_nav"] = "checkin" return context
[docs] class CheckInDashboardView(ManagePermissionMixin, TemplateView): """Real-time check-in statistics dashboard. Displays aggregate check-in data including total attendees, check-in rate, station activity breakdown, recent check-in log, and product redemption statistics. """ template_name = "django_program/manage/checkin_dashboard.html" required_permission = "view_checkin"
[docs] def get_context_data(self, **kwargs: object) -> dict[str, object]: """Build context with check-in statistics for the dashboard. Args: **kwargs: Additional context data. Returns: Template context with attendee counts, check-in rate, station activity, recent check-ins, and redemption stats. """ context = super().get_context_data(**kwargs) conference = self.conference context["active_nav"] = "checkin" total_attendees = Attendee.objects.filter(conference=conference).count() checked_in_count = Attendee.objects.filter(conference=conference, checked_in_at__isnull=False).count() check_in_rate = round((checked_in_count / total_attendees * 100), 1) if total_attendees > 0 else 0 today_start = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) checkins_today = CheckIn.objects.filter(conference=conference, checked_in_at__gte=today_start).count() checkins_by_station = ( CheckIn.objects.filter(conference=conference) .exclude(station="") .values("station") .annotate(count=Count("id"), last_checkin=Max("checked_in_at")) .order_by("-count") ) recent_checkins = ( CheckIn.objects.filter(conference=conference) .select_related("attendee", "attendee__user", "checked_in_by") .order_by("-checked_in_at")[:50] ) redemption_stats = ( ProductRedemption.objects.filter(conference=conference) .values("order_line_item__description") .annotate(count=Count("id")) .order_by("-count") ) door_check_counts = ( DoorCheck.objects.filter(conference=conference) .values("ticket_type__name", "addon__name") .annotate(count=Count("id")) .order_by("-count") ) context["total_attendees"] = total_attendees context["checked_in_count"] = checked_in_count context["check_in_rate"] = check_in_rate context["checkins_today"] = checkins_today context["checkins_by_station"] = checkins_by_station context["recent_checkins"] = recent_checkins context["redemption_stats"] = redemption_stats context["door_check_counts"] = door_check_counts return context