Files
weirsoe-party-protocol/fupogfakta/payloads.py
dev-bot 16c9cf6b57
All checks were successful
CI / test-and-quality (push) Successful in 3m51s
CI / test-and-quality (pull_request) Successful in 3m53s
refactor(gameplay): extract round start payload builders
2026-03-17 19:15:44 +00:00

254 lines
8.4 KiB
Python

from datetime import timedelta
from .models import GameSession, Player, RoundConfig, RoundQuestion
def build_player_ref(player: Player | None) -> dict | None:
if player is None:
return None
return {
"player_id": player.id,
"nickname": player.nickname,
}
def build_round_question_payload(round_question: RoundQuestion | None) -> dict | None:
if round_question is None:
return None
return {
"id": round_question.id,
"round_number": round_question.round_number,
"prompt": round_question.question.prompt,
"shown_at": round_question.shown_at.isoformat(),
"answers": [{"text": text} for text in (round_question.mixed_answers or [])],
}
def build_reveal_payload(round_question: RoundQuestion | None) -> dict | None:
if round_question is None:
return None
lies = [
{
**build_player_ref(lie.player),
"text": lie.text,
"created_at": lie.created_at.isoformat(),
}
for lie in round_question.lies.select_related("player").order_by("created_at", "id")
]
guesses = []
for guess in round_question.guesses.select_related("player", "fooled_player").order_by("created_at", "id"):
guess_payload = {
**build_player_ref(guess.player),
"selected_text": guess.selected_text,
"is_correct": guess.is_correct,
"created_at": guess.created_at.isoformat(),
"fooled_player_id": guess.fooled_player_id,
}
if guess.fooled_player is not None:
guess_payload["fooled_player_nickname"] = guess.fooled_player.nickname
guesses.append(guess_payload)
return {
"round_question_id": round_question.id,
"round_number": round_question.round_number,
"prompt": round_question.question.prompt,
"correct_answer": round_question.correct_answer,
"lies": lies,
"guesses": guesses,
}
def build_leaderboard(session: GameSession) -> list[dict]:
return list(
Player.objects.filter(session=session)
.order_by("-score", "nickname")
.values("id", "nickname", "score")
)
def build_lie_started_payload(session: GameSession, round_config: RoundConfig, round_question: RoundQuestion) -> dict:
lie_deadline_at = round_question.shown_at + timedelta(seconds=round_config.lie_seconds)
return {
"round_number": session.current_round,
"category": {"slug": round_config.category.slug, "name": round_config.category.name},
"round_question_id": round_question.id,
"prompt": round_question.question.prompt,
"shown_at": round_question.shown_at.isoformat(),
"lie_deadline_at": lie_deadline_at.isoformat(),
"lie_seconds": round_config.lie_seconds,
}
def build_phase_view_model(session: GameSession, *, players_count: int, has_round_question: bool) -> dict:
status = session.status
in_lobby = status == GameSession.Status.LOBBY
in_lie = status == GameSession.Status.LIE
in_guess = status == GameSession.Status.GUESS
in_scoreboard = status == GameSession.Status.SCOREBOARD
in_finished = status == GameSession.Status.FINISHED
min_players_reached = players_count >= 3
max_players_allowed = players_count <= 5
return {
"status": status,
"current_phase": status,
"round_number": session.current_round,
"players_count": players_count,
"constraints": {
"min_players_to_start": 3,
"max_players_mvp": 5,
"min_players_reached": min_players_reached,
"max_players_allowed": max_players_allowed,
},
"readiness": {
"question_ready": has_round_question,
"scoreboard_ready": status in {GameSession.Status.REVEAL, GameSession.Status.SCOREBOARD, GameSession.Status.FINISHED},
"can_advance_to_next_round": in_scoreboard,
},
"host": {
"can_start_round": in_lobby and min_players_reached and max_players_allowed,
"can_show_question": False,
"can_mix_answers": False,
"can_calculate_scores": False,
"can_reveal_scoreboard": False,
"can_start_next_round": in_scoreboard,
"can_finish_game": in_scoreboard,
},
"player": {
"can_join": status in {
GameSession.Status.LOBBY,
GameSession.Status.LIE,
GameSession.Status.GUESS,
GameSession.Status.REVEAL,
GameSession.Status.SCOREBOARD,
},
"can_submit_lie": in_lie and has_round_question,
"can_submit_guess": in_guess and has_round_question,
"can_view_final_result": in_finished,
},
}
def build_start_round_response(
session: GameSession,
round_config: RoundConfig,
round_question: RoundQuestion,
) -> dict:
lie_started_payload = build_lie_started_payload(session, round_config, round_question)
return {
"session": {
"code": session.code,
"status": session.status,
"current_round": session.current_round,
},
"round": {
"number": round_config.number,
"category": {
"slug": round_config.category.slug,
"name": round_config.category.name,
},
},
"round_question": {
"id": round_question.id,
"prompt": round_question.question.prompt,
"round_number": round_question.round_number,
"shown_at": round_question.shown_at.isoformat(),
"lie_deadline_at": lie_started_payload["lie_deadline_at"],
},
"config": {
"lie_seconds": round_config.lie_seconds,
},
}
def build_question_shown_payload(round_question: RoundQuestion, lie_deadline_at: str, lie_seconds: int) -> dict:
return {
"round_question_id": round_question.id,
"prompt": round_question.question.prompt,
"shown_at": round_question.shown_at.isoformat(),
"lie_deadline_at": lie_deadline_at,
"lie_seconds": lie_seconds,
}
def build_question_shown_response(round_question: RoundQuestion, lie_deadline_at: str, lie_seconds: int) -> dict:
return {
"round_question": {
"id": round_question.id,
"prompt": round_question.question.prompt,
"round_number": round_question.round_number,
"shown_at": round_question.shown_at.isoformat(),
"lie_deadline_at": lie_deadline_at,
},
"config": {
"lie_seconds": lie_seconds,
},
}
def build_start_next_round_response(
session: GameSession,
round_config: RoundConfig,
round_question: RoundQuestion,
) -> dict:
return build_start_round_response(session, round_config, round_question)
def build_start_next_round_phase_event(
session: GameSession,
round_config: RoundConfig,
round_question: RoundQuestion,
) -> dict:
return {
"name": "phase.lie_started",
"payload": build_lie_started_payload(session, round_config, round_question),
}
def build_scoreboard_phase_event(session: GameSession, leaderboard: list[dict] | None = None) -> dict:
return {
"name": "phase.scoreboard",
"payload": {
"leaderboard": leaderboard if leaderboard is not None else build_leaderboard(session),
"current_round": session.current_round,
},
}
def build_reveal_scoreboard_response(session: GameSession, leaderboard: list[dict]) -> dict:
return {
"session": {
"code": session.code,
"status": session.status,
"current_round": session.current_round,
},
"leaderboard": leaderboard,
}
def build_finish_game_phase_event(session: GameSession) -> dict:
leaderboard = build_leaderboard(session)
winner = leaderboard[0] if leaderboard else None
return {
"name": "phase.game_over",
"payload": {"winner": winner, "leaderboard": leaderboard},
}
def build_finish_game_response(session: GameSession) -> dict:
finish_event = build_finish_game_phase_event(session)
return {
"session": {
"code": session.code,
"status": GameSession.Status.FINISHED,
"current_round": session.current_round,
},
"winner": finish_event["payload"]["winner"],
"leaderboard": finish_event["payload"]["leaderboard"],
}