Files
Hulumath-Web/backend/contest/views.py
T

158 lines
5.1 KiB
Python

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 .game_services import game_payload, request_sudoku_hint, start_game, submit_game
from .models import Contest, ContestAttempt, MathGameAttempt, 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)
]
)
class MathGameCatalogView(APIView):
permission_classes = [permissions.AllowAny]
def get(self, request):
return Response(
[
{
"kind": MathGameAttempt.Kind.TWENTY_FOUR,
"title": "24 点",
"summary": "四个数字各用一次,只用四则运算得到 24。",
"ability": "connection",
"estimated_minutes": 3,
},
{
"kind": MathGameAttempt.Kind.SUDOKU,
"title": "数独",
"summary": "在行、列和九宫格约束中完成 9×9 数字推理。",
"ability": "detection",
"estimated_minutes": 8,
},
]
)
class MathGameStartView(APIView):
def post(self, request, kind):
payload = start_game(
request.user,
kind,
request.data.get("difficulty", MathGameAttempt.Difficulty.STANDARD),
)
return Response(payload, status=status.HTTP_201_CREATED)
class MathGameSubmitView(APIView):
def post(self, request, attempt_id):
payload = submit_game(
request.user,
attempt_id,
request.data,
request.headers.get("Idempotency-Key"),
)
return Response(payload)
class SudokuHintView(APIView):
def post(self, request, attempt_id):
return Response(request_sudoku_hint(request.user, attempt_id))
class MathGameHistoryView(APIView):
def get(self, request):
attempts = MathGameAttempt.objects.filter(user=request.user)[:20]
return Response([game_payload(attempt) for attempt in attempts])