feat: evolution per player (optional)

This commit is contained in:
2026-10-24 03:49:34 +02:00
parent 87e6e38291
commit 026d592483
6 changed files with 92 additions and 18 deletions
+9 -3
View File
@@ -36,9 +36,15 @@ def canvas(request: HttpRequest, event_path: str):
@router.get(f"{PREFIX}/evolution.gif", auth=django_auth_is_staff)
@decorate_view(require_game_enabled(path="place"))
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")
def evolution_gif(request: HttpRequest, event_path: str, players: str = ""):
"""Admin-only: replays the full placement history as an animated GIF.
`players` is an optional comma-separated list of usernames to only
replay those players' placements (combined)."""
usernames = [name.strip() for name in players.split(",") if name.strip()]
return HttpResponse(
build_evolution_gif(request.event, usernames), content_type="image/gif"
)
@router.get(f"{PREFIX}/placements.csv", auth=django_auth_is_staff)
+9 -6
View File
@@ -25,22 +25,25 @@ _PALETTE_RGB = [
]
def build_evolution_gif(event: Event) -> bytes:
def build_evolution_gif(event: Event, usernames: list[str] | None = None) -> bytes:
"""Renders `event`'s full placement history as an animated GIF, upscaled
for visibility. Returns GIF bytes, even if there are zero placements.
If `usernames` is given, only those players' placements are replayed
(combined on the same board).
Always replays at the *current* grid dimensions - a placement made
before a resize that now falls outside them is skipped rather than
raising."""
place_settings = PlaceSettings.current(event)
width, height = place_settings.width, place_settings.height
placements = (
PixelPlacement.objects.filter(event=event)
.order_by("placed_at")
.values_list("x", "y", "color_index")
placements = PixelPlacement.objects.filter(event=event)
if usernames:
placements = placements.filter(player__username__in=usernames)
placements = list(
placements.order_by("placed_at").values_list("x", "y", "color_index")
)
placements = list(placements)
grid = bytearray(width * height)
frames: list[Image.Image] = []