wip: planning-slot test

This commit is contained in:
2026-10-16 05:20:52 +02:00
parent 2f8fdb755b
commit 89fb745e74
20 changed files with 849 additions and 17 deletions
@@ -0,0 +1,88 @@
import re
import unicodedata
from ..models import Game, PlanningSlot
def normalize_name(name: str) -> str:
"""'Towerfall: Ascension' and 'TowerFall Ascension' are the same game:
no accents, no case, letters and digits only."""
name = unicodedata.normalize("NFKD", name)
name = "".join(c for c in name if not unicodedata.combining(c))
return re.sub(r"[^0-9a-z]", "", name.casefold())
def _game_out(name: str, game: Game | None) -> dict:
if game is None:
return {"name": name}
return {
"name": game.name,
"library_image": game.library_image_url,
"store_url": game.store_url,
"steam_app_id": game.steam_app_id,
}
def planning_with_games(slots) -> list[dict]:
"""Slots as PlanningSlotOut dicts, with their games matched by name to ours."""
games_by_name = {normalize_name(game.name): game for game in Game.objects.all()}
def match(name: str) -> Game | None:
return games_by_name.get(normalize_name(name))
out = []
for slot in slots:
if slot.is_multi_anims:
games = [_game_out(name, match(name)) for name in slot.game_names]
else:
# The title is the usual match, the legacy game names are a fallback
# ("Golf it!" slot running the "Golf It!" game)
game = next(
filter(None, map(match, [slot.title, *slot.game_names])), None
)
games = [_game_out(slot.title, game)]
out.append(
{
"id": slot.id,
"title": slot.title,
"begin": slot.begin,
"end": slot.end,
"column": slot.column,
"color": slot.color,
"is_multi_anims": slot.is_multi_anims,
"games": games,
}
)
return out
def import_planning(event, entries: list[dict]) -> tuple[int, int, int]:
"""Sync the event planning with the legacy planning entries (its "list").
Returns (created, updated, deleted)."""
created = updated = 0
for entry in entries:
_, was_created = PlanningSlot.objects.update_or_create(
event=event,
source_pk=entry["pk"],
defaults={
"title": entry["title"],
"begin": entry["begin"],
"end": entry["end"],
"column": entry.get("colonneS") or 0,
"color": entry.get("color_code") or "",
"is_multi_anims": bool(entry.get("is_multi_anims")),
"game_names": [a["name"] for a in entry.get("aandts") or []],
},
)
if was_created:
created += 1
else:
updated += 1
deleted, _ = (
PlanningSlot.objects.filter(event=event)
.exclude(source_pk__in=[entry["pk"] for entry in entries])
.delete()
)
return created, updated, deleted