from __future__ import annotations import logging from collections import defaultdict from typing import TYPE_CHECKING, Any from django.db.models import Prefetch from django.http import HttpRequest, HttpResponse from django.shortcuts import render from django.template.response import TemplateResponse from django.utils import timezone from django.views.decorators.http import require_http_methods from core.models import DropCampaign, Game, TimeBasedDrop if TYPE_CHECKING: from django.db.models.query import QuerySet from django.http import HttpRequest logger: logging.Logger = logging.getLogger(__name__) @require_http_methods(request_method_list=["GET", "HEAD"]) def get_home(request: HttpRequest) -> HttpResponse: """Render the index page. Args: request (HttpRequest): The request object. Returns: HttpResponse: The response object """ now: timezone.datetime = timezone.now() grouped_drops = defaultdict(list) current_drops_qs = ( TimeBasedDrop.objects.filter(start_at__lte=now, end_at__gte=now) .select_related("campaign__game") .prefetch_related("benefits") .order_by("campaign__game__display_name", "name") ) for drop in current_drops_qs: if drop.campaign and drop.campaign.game: game: Game = drop.campaign.game grouped_drops[game].append(drop) else: logger.warning("Drop %s does not have an associated game or campaign.", drop.name) context = {"grouped_drops": dict(grouped_drops)} return render(request, "index.html", context) @require_http_methods(request_method_list=["GET", "HEAD"]) def get_game(request: HttpRequest, twitch_id: int) -> HttpResponse: """Render the game view page. Args: request (HttpRequest): The request object. twitch_id (int): The Twitch ID of the game. Returns: HttpResponse: The response object. """ try: time_based_drops_prefetch = Prefetch( lookup="drops", queryset=TimeBasedDrop.objects.prefetch_related("benefits"), ) drop_campaigns_prefetch = Prefetch( lookup="drop_campaigns", queryset=DropCampaign.objects.prefetch_related(time_based_drops_prefetch), ) game: Game = ( Game.objects.select_related("org").prefetch_related(drop_campaigns_prefetch).get(twitch_id=twitch_id) ) except Game.DoesNotExist: return HttpResponse(status=404, content="Game not found.") except Game.MultipleObjectsReturned: return HttpResponse(status=500, content="Multiple games found with the same Twitch ID.") context: dict[str, Any] = {"game": game} return TemplateResponse(request=request, template="game.html", context=context) @require_http_methods(request_method_list=["GET", "HEAD"]) def get_games(request: HttpRequest) -> HttpResponse: """Render the game view page. Args: request (HttpRequest): The request object. Returns: HttpResponse: The response object. """ games: QuerySet[Game] = Game.objects.all() context: dict[str, QuerySet[Game] | str] = {"games": games} return TemplateResponse(request=request, template="games.html", context=context)