cache for result api

This commit is contained in:
Loïc Gremaud 2026-05-22 17:21:53 +02:00
parent 9e5ab8539a
commit 25072e8eb4
Signed by: Legrems
GPG Key ID: D4620E6DF3E0121D
3 changed files with 61 additions and 24 deletions

View File

@ -61,8 +61,14 @@ def results(request: HttpRequest) -> dict:
@router.get("top-submissions", response=TournamentSubmissionsOut)
def top_submissions(request: HttpRequest, limit: int = 5) -> TournamentSubmissionsOut:
def top_submissions(request: HttpRequest, limit: int = 5) -> dict:
"""Get tournament top submissions for each puzzle. Only available when tournament is closed."""
cache_key = f"api:results:top_submissions:{limit}"
cached_data = cache.get(cache_key)
if cached_data is not None:
return cached_data
collection = get_object_or_404(SteamCollection, is_active=True)
# Only allow access when tournament is closed
@ -70,15 +76,16 @@ def top_submissions(request: HttpRequest, limit: int = 5) -> TournamentSubmissio
raise HttpError(403, "Tournament is still accepting submissions")
# Get all puzzles
puzzles = SteamCollectionItem.objects.filter(collection=collection).order_by("order_index")
puzzles = SteamCollectionItem.objects.filter(collection=collection).order_by(
"order_index"
)
# Build response
submissions_list = []
for puzzle in puzzles:
# Get the top N responses for this puzzle (ranked by points, highest first)
top_responses = (
PuzzleResponse.objects
.filter(puzzle=puzzle, needs_manual_validation=False)
PuzzleResponse.objects.filter(puzzle=puzzle, needs_manual_validation=False)
.filter_user_best_response()
.annotate_rank_points()
.order_by("-rank_points")[:limit]
@ -92,23 +99,30 @@ def top_submissions(request: HttpRequest, limit: int = 5) -> TournamentSubmissio
response_files = [
WinnerFileOut(
file_url=file.file_url or "",
original_filename=file.original_filename
original_filename=file.original_filename,
)
for file in files
]
# Calculate total coefficient
total_coef = None
if puzzle.points_factor and response.final_cost is not None and response.final_cycles is not None and response.final_area is not None:
if (
puzzle.points_factor
and response.final_cost is not None
and response.final_cycles is not None
and response.final_area is not None
):
total_coef = (
puzzle.points_factor.cost * response.final_cost +
puzzle.points_factor.cycles * response.final_cycles +
puzzle.points_factor.area * response.final_area
puzzle.points_factor.cost * response.final_cost
+ puzzle.points_factor.cycles * response.final_cycles
+ puzzle.points_factor.area * response.final_area
)
submission_data = WinnerResponseOut(
user_id=response.submission.user.id if response.submission.user else 0,
username=response.submission.user.username if response.submission.user else "Anonymous",
username=response.submission.user.username
if response.submission.user
else "Anonymous",
final_cost=response.final_cost,
final_cycles=response.final_cycles,
final_area=response.final_area,
@ -126,12 +140,20 @@ def top_submissions(request: HttpRequest, limit: int = 5) -> TournamentSubmissio
)
)
return TournamentSubmissionsOut(submissions=submissions_list)
data = {"submissions": submissions_list}
cache.set(f"api:results:top_submissions:{limit}", data, 300)
return data
@router.get("puzzle-results", response=TournamentPuzzleResultsOut)
def puzzle_results(request: HttpRequest, limit: int = 5) -> TournamentPuzzleResultsOut:
def puzzle_results(request: HttpRequest, limit: int = 5) -> dict:
"""Get tournament results organized by puzzle with coefficients. Only available when tournament is closed."""
cache_key = f"api:results:puzzle_results:{limit}"
cached_data = cache.get(cache_key)
if cached_data is not None:
return cached_data
collection = get_object_or_404(SteamCollection, is_active=True)
# Only allow access when tournament is closed
@ -139,15 +161,16 @@ def puzzle_results(request: HttpRequest, limit: int = 5) -> TournamentPuzzleResu
raise HttpError(403, "Tournament is still accepting submissions")
# Get all puzzles
puzzles = SteamCollectionItem.objects.filter(collection=collection).order_by("order_index")
puzzles = SteamCollectionItem.objects.filter(collection=collection).order_by(
"order_index"
)
# Build response
results_list = []
for puzzle in puzzles:
# Get the top N responses for this puzzle (ranked by points)
top_responses = (
PuzzleResponse.objects
.filter(puzzle=puzzle, needs_manual_validation=False)
PuzzleResponse.objects.filter(puzzle=puzzle, needs_manual_validation=False)
.filter_user_best_response()
.annotate_rank_points()
.order_by("-rank_points")[:limit]
@ -161,24 +184,31 @@ def puzzle_results(request: HttpRequest, limit: int = 5) -> TournamentPuzzleResu
response_files = [
WinnerFileOut(
file_url=file.file_url or "",
original_filename=file.original_filename
original_filename=file.original_filename,
)
for file in files
]
# Calculate total coefficient
total_coef = None
if puzzle.points_factor and response.final_cost is not None and response.final_cycles is not None and response.final_area is not None:
if (
puzzle.points_factor
and response.final_cost is not None
and response.final_cycles is not None
and response.final_area is not None
):
total_coef = (
puzzle.points_factor.cost * response.final_cost +
puzzle.points_factor.cycles * response.final_cycles +
puzzle.points_factor.area * response.final_area
puzzle.points_factor.cost * response.final_cost
+ puzzle.points_factor.cycles * response.final_cycles
+ puzzle.points_factor.area * response.final_area
)
submission_data = PuzzleSubmissionWithRankOut(
rank=rank,
user_id=response.submission.user.id if response.submission.user else 0,
username=response.submission.user.username if response.submission.user else "Anonymous",
username=response.submission.user.username
if response.submission.user
else "Anonymous",
final_cost=response.final_cost,
final_cycles=response.final_cycles,
final_area=response.final_area,
@ -206,4 +236,6 @@ def puzzle_results(request: HttpRequest, limit: int = 5) -> TournamentPuzzleResu
)
)
return TournamentPuzzleResultsOut(results=results_list)
data = {"results": results_list}
cache.set(f"api:results:puzzle_results:{limit}", data, 300)
return data

View File

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("submissions", "0013_steamcollectionitem_points_value"),
]

View File

@ -3,7 +3,13 @@ from typing import List, Optional
from datetime import datetime
from uuid import UUID
from .models import Submission, PuzzleResponse, SubmissionFile, SteamCollectionItem, SteamCollection
from .models import (
Submission,
PuzzleResponse,
SubmissionFile,
SteamCollectionItem,
SteamCollection,
)
# Input Schemas