From 87e6e3829118ebbddb37b899e76d97815a65e61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Sat, 24 Oct 2026 02:11:09 +0200 Subject: [PATCH] add place csv export --- polylan_submitter/place/api.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/polylan_submitter/place/api.py b/polylan_submitter/place/api.py index 040f412..8d1a720 100644 --- a/polylan_submitter/place/api.py +++ b/polylan_submitter/place/api.py @@ -1,5 +1,7 @@ +import csv import struct +from django.db.models import Count from django.http import HttpRequest, HttpResponse from ninja import Router from ninja.decorators import decorate_view @@ -9,7 +11,7 @@ from games.decorators import require_game_enabled from .canvas import read_canvas from .gif import build_evolution_gif -from .models import PlaceSettings +from .models import PixelPlacement, PlaceSettings router = Router() @@ -37,3 +39,27 @@ def canvas(request: HttpRequest, event_path: str): def evolution_gif(request: HttpRequest, event_path: str): """Admin-only: replays the full placement history as an animated GIF.""" return HttpResponse(build_evolution_gif(request.event), content_type="image/gif") + + +@router.get(f"{PREFIX}/placements.csv", auth=django_auth_is_staff) +@decorate_view(require_game_enabled(path="place")) +def placements_csv(request: HttpRequest, event_path: str): + """Admin-only: pixel placement count per player, most active first.""" + rows = ( + PixelPlacement.objects.filter(event=request.event) + .values("player__username") + .annotate(count=Count("id")) + .order_by("-count", "player__username") + ) + # 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) + writer.writerow(["username", "placements"]) + for row in rows: + writer.writerow( + [ + row["player__username"], + row["count"], + ] + ) + return response