from django.shortcuts import get_object_or_404 from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView from .models import Contest, ContestAttempt, RealtimeMatch from .services import ( find_match, match_payload, start_attempt, submit_attempt, ) class ContestListView(APIView): permission_classes = [permissions.AllowAny] def get(self, request): contests = Contest.objects.filter(status=Contest.Status.PUBLISHED).order_by( "kind", "title" ) return Response( [ { "slug": contest.slug, "title": contest.title, "kind": contest.kind, "track": contest.track, "duration_seconds": contest.duration_seconds, "starts_at": contest.starts_at, "ends_at": contest.ends_at, } for contest in contests ] ) class AttemptStartView(APIView): def post(self, request, slug): contest = get_object_or_404(Contest, slug=slug) return Response(start_attempt(request.user, contest), status=status.HTTP_201_CREATED) class AttemptSubmitView(APIView): def post(self, request, attempt_id): get_object_or_404(ContestAttempt, id=attempt_id, user=request.user) payload = submit_attempt( user=request.user, attempt_id=attempt_id, raw_answers=request.data.get("answers", []), submission_key=request.headers.get("Idempotency-Key"), ) return Response(payload) class MatchmakingView(APIView): def post(self, request, slug): contest = get_object_or_404(Contest, slug=slug) match = find_match(request.user, contest) return Response(match_payload(match, request.user), status=status.HTTP_202_ACCEPTED) class MatchStateView(APIView): def get(self, request, match_id): match = get_object_or_404( RealtimeMatch.objects.select_related("player_one", "player_two"), id=match_id, ) if request.user.id not in (match.player_one_id, match.player_two_id): return Response(status=status.HTTP_403_FORBIDDEN) return Response(match_payload(match, request.user)) class LeaderboardView(APIView): permission_classes = [permissions.AllowAny] def get(self, request, slug): contest = get_object_or_404(Contest, slug=slug) attempts = ( ContestAttempt.objects.filter( contest=contest, status=ContestAttempt.Status.SUBMITTED, cheat_flags__isnull=True, ) .select_related("user") .order_by("-score", "duration_ms", "submitted_at")[:100] ) return Response( [ { "rank": index, "nickname": attempt.user.nickname, "track": attempt.user.track, "score": attempt.score, "correct_count": attempt.correct_count, "duration_ms": attempt.duration_ms, } for index, attempt in enumerate(attempts, start=1) ] )