opus-stats-score
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import csv
|
||||
|
||||
from ninja import Router, File
|
||||
from ninja.files import UploadedFile
|
||||
from ninja.pagination import paginate
|
||||
@@ -5,6 +7,8 @@ from ninja.errors import HttpError
|
||||
from django.db import transaction
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.cache import cache
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from ninja.security import django_auth_is_staff
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import get_object_or_404
|
||||
from typing import List
|
||||
@@ -395,3 +399,41 @@ def get_stats(request, event_path: str):
|
||||
if total_responses
|
||||
else 0,
|
||||
}
|
||||
|
||||
|
||||
@router.get(f"{PREFIX}/points.csv", auth=django_auth_is_staff)
|
||||
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
|
||||
def points_csv(request: HttpRequest, event_path: str):
|
||||
"""Admin-only: points per player for each puzzle of the active collection,
|
||||
one column per puzzle (1 puzzle = 1 round), `;` separated."""
|
||||
puzzles = list(
|
||||
SteamCollectionItem.objects.filter(
|
||||
collection__is_active=True, collection__event=request.event
|
||||
).order_by("order_index")
|
||||
)
|
||||
|
||||
points = {}
|
||||
for response in (
|
||||
PuzzleResponse.objects.filter(
|
||||
needs_manual_validation=False,
|
||||
submission__event=request.event,
|
||||
submission__user__isnull=False,
|
||||
puzzle__in=puzzles,
|
||||
)
|
||||
.filter_user_best_response()
|
||||
.select_related("submission__user")
|
||||
):
|
||||
user_points = points.setdefault(response.submission.user.username, {})
|
||||
user_points[response.puzzle_id] = response.rank_points or 0
|
||||
|
||||
# text/plain so browsers render it inline - most force-download text/csv
|
||||
response = HttpResponse(content_type="text/plain; charset=utf-8")
|
||||
writer = csv.writer(response, delimiter=";")
|
||||
writer.writerow(["username", *(puzzle.title for puzzle in puzzles)])
|
||||
for username, user_points in sorted(
|
||||
points.items(), key=lambda item: (-sum(item[1].values()), item[0])
|
||||
):
|
||||
writer.writerow(
|
||||
[username, *(user_points.get(puzzle.id, 0) for puzzle in puzzles)]
|
||||
)
|
||||
return response
|
||||
|
||||
@@ -1 +1,74 @@
|
||||
# Create your tests here.
|
||||
from django.test import TestCase
|
||||
|
||||
from accounts.models import CustomUser
|
||||
from animations.models import PuzzlePointsFactor, PuzzlePointsValue
|
||||
from events.models import Event
|
||||
from games.models import EventGame, Game
|
||||
|
||||
from .api import OPUS_MAGNUM_APP_ID
|
||||
from .models import PuzzleResponse, SteamCollection, SteamCollectionItem, Submission
|
||||
|
||||
URL = "/api/events/event/opus-magnum/points.csv"
|
||||
|
||||
|
||||
class PointsCsvTests(TestCase):
|
||||
def setUp(self):
|
||||
self.event = Event.objects.create(name="Event", path="event")
|
||||
game = Game.objects.create(name="Opus Magnum", steam_app_id=OPUS_MAGNUM_APP_ID)
|
||||
EventGame.objects.create(event=self.event, game=game)
|
||||
collection = SteamCollection.objects.create(
|
||||
event=self.event, steam_id="1", url="https://example.com", is_active=True
|
||||
)
|
||||
factor = PuzzlePointsFactor.objects.create(cost=1, cycles=1, area=1)
|
||||
value = PuzzlePointsValue.objects.create(points=[10, 5, 1])
|
||||
self.p1, self.p2 = (
|
||||
SteamCollectionItem.objects.create(
|
||||
collection=collection,
|
||||
steam_item_id=str(i),
|
||||
title=f"Puzzle {i}",
|
||||
order_index=i,
|
||||
points_factor=factor,
|
||||
points_value=value,
|
||||
)
|
||||
for i in (1, 2)
|
||||
)
|
||||
self.alice = CustomUser.objects.create_user(username="alice", password="x")
|
||||
self.bob = CustomUser.objects.create_user(username="bob", password="x")
|
||||
self.admin = CustomUser.objects.create_user(
|
||||
username="admin", password="x", is_staff=True
|
||||
)
|
||||
|
||||
def respond(self, user, puzzle, score):
|
||||
submission = Submission.objects.create(event=self.event, user=user)
|
||||
PuzzleResponse.objects.create(
|
||||
submission=submission,
|
||||
puzzle=puzzle,
|
||||
puzzle_name=puzzle.title,
|
||||
validated_cost=score,
|
||||
validated_cycles=0,
|
||||
validated_area=0,
|
||||
)
|
||||
|
||||
def test_requires_staff(self):
|
||||
self.client.force_login(self.alice)
|
||||
|
||||
response = self.client.get(URL)
|
||||
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_points_per_puzzle(self):
|
||||
self.respond(self.alice, self.p1, 10)
|
||||
self.respond(self.bob, self.p1, 20)
|
||||
self.respond(self.bob, self.p2, 30)
|
||||
self.client.force_login(self.admin)
|
||||
|
||||
response = self.client.get(URL)
|
||||
|
||||
self.assertEqual(
|
||||
response.content.decode().splitlines(),
|
||||
[
|
||||
"username;Puzzle 1;Puzzle 2",
|
||||
"bob;5;10",
|
||||
"alice;10;0",
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user