/
Mihaham
/
Table-Time
Обзор
Документация
Войти
/
Mihaham
/
Table-Time
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
services/api/app/modules/admin/service.py
244 строки
10 KB
MihahamYT
feat: dedicated web panels for all 46 games
14 июн 2026, 21:36
14 июн 2026, 21:36
4e11c3f
Код
Авторство
О чём код?
import uuid from datetime import datetime from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.db.models import GameAction, GameEvent, GameInstance, Participant, Session class AdminService: def __init__(self, db: AsyncSession): self.db = db async def list_games( self, *, page: int = 1, limit: int = 50, plugin_id: str | None = None, status: str | None = None, invite_code: str | None = None, session_id: uuid.UUID | None = None, from_date: datetime | None = None, to_date: datetime | None = None, ) -> dict: query = ( select(GameInstance, Session.invite_code) .join(Session, GameInstance.session_id == Session.id) .order_by(GameInstance.started_at.desc()) ) if plugin_id: query = query.where(GameInstance.plugin_id == plugin_id) if status: query = query.where(GameInstance.status == status) if invite_code: query = query.where(Session.invite_code == invite_code.upper()) if session_id: query = query.where(GameInstance.session_id == session_id) if from_date: query = query.where(GameInstance.started_at >= from_date) if to_date: query = query.where(GameInstance.started_at <= to_date) count_query = select(func.count()).select_from(query.subquery()) total = (await self.db.execute(count_query)).scalar() or 0 offset = (page - 1) * limit result = await self.db.execute(query.offset(offset).limit(limit)) rows = result.all() participant_names: dict[str, str] = {} game_ids = [row[0].id for row in rows] action_counts: dict[uuid.UUID, int] = {} rejection_counts: dict[uuid.UUID, int] = {} if game_ids: ac = await self.db.execute( select(GameAction.game_instance_id, func.count()) .where(GameAction.game_instance_id.in_(game_ids)) .group_by(GameAction.game_instance_id) ) action_counts = {row[0]: row[1] for row in ac.all()} rc = await self.db.execute( select(GameEvent.game_instance_id, func.count()) .where( GameEvent.game_instance_id.in_(game_ids), GameEvent.event_type == "action.rejected", ) .group_by(GameEvent.game_instance_id) ) rejection_counts = {row[0]: row[1] for row in rc.all()} if rows: winner_ids = {row[0].winner_id for row in rows if row[0].winner_id} if winner_ids: p_result = await self.db.execute( select(Participant).where(Participant.id.in_(winner_ids)) ) for p in p_result.scalars().all(): participant_names[str(p.id)] = p.display_name games = [] for game, code in rows: games.append({ "id": str(game.id), "plugin_id": game.plugin_id, "session_id": str(game.session_id), "invite_code": code, "status": game.status, "started_at": game.started_at.isoformat(), "finished_at": game.finished_at.isoformat() if game.finished_at else None, "winner_id": str(game.winner_id) if game.winner_id else None, "winner_name": participant_names.get(str(game.winner_id)) if game.winner_id else None, "action_count": action_counts.get(game.id, 0), "rejection_count": rejection_counts.get(game.id, 0), }) return {"games": games, "total": total, "page": page, "limit": limit} async def get_game(self, game_id: uuid.UUID) -> dict | None: game = await self._load_game(game_id) if not game: return None return self._game_detail(game) async def get_game_log(self, game_id: uuid.UUID) -> dict | None: game = await self._load_game(game_id) if not game: return None detail = self._game_detail(game) names = {str(p.id): p.display_name for p in game.session.participants} actions_result = await self.db.execute( select(GameAction) .where(GameAction.game_instance_id == game_id) .order_by(GameAction.created_at) ) actions = actions_result.scalars().all() events_result = await self.db.execute( select(GameEvent) .where(GameEvent.game_instance_id == game_id) .order_by(GameEvent.created_at) ) events = events_result.scalars().all() entries: list[tuple[str, datetime, dict]] = [] for a in actions: entries.append(("action", a.created_at, { "kind": "action", "action_id": str(a.action_id), "action_type": a.action_type, "player_id": str(a.player_id), "player_name": names.get(str(a.player_id), "Unknown"), "channel": a.channel, "payload": a.payload, "result_state": a.result_state, "at": a.created_at.isoformat(), })) for e in events: entries.append(("event", e.created_at, { "kind": "event", "event_type": e.event_type, "player_id": str(e.player_id) if e.player_id else None, "player_name": names.get(str(e.player_id)) if e.player_id else None, "payload": e.payload, "at": e.created_at.isoformat(), })) entries.sort(key=lambda x: x[1]) timeline = [{**entry[2], "seq": i + 1} for i, entry in enumerate(entries)] return {**detail, "timeline": timeline} async def list_sessions(self, *, page: int = 1, limit: int = 50) -> dict: query = select(Session).order_by(Session.created_at.desc()) count_query = select(func.count()).select_from(query.subquery()) total = (await self.db.execute(count_query)).scalar() or 0 offset = (page - 1) * limit result = await self.db.execute(query.offset(offset).limit(limit)) sessions = result.scalars().all() session_ids = [s.id for s in sessions] counts: dict[uuid.UUID, int] = {} participant_counts: dict[uuid.UUID, int] = {} if session_ids: count_result = await self.db.execute( select(GameInstance.session_id, func.count()) .where(GameInstance.session_id.in_(session_ids)) .group_by(GameInstance.session_id) ) counts = {row[0]: row[1] for row in count_result.all()} pc_result = await self.db.execute( select(Participant.session_id, func.count()) .where(Participant.session_id.in_(session_ids)) .group_by(Participant.session_id) ) participant_counts = {row[0]: row[1] for row in pc_result.all()} return { "sessions": [ { "id": str(s.id), "invite_code": s.invite_code, "status": s.status, "created_at": s.created_at.isoformat(), "closed_at": s.closed_at.isoformat() if s.closed_at else None, "game_count": counts.get(s.id, 0), "participant_count": participant_counts.get(s.id, 0), } for s in sessions ], "total": total, "page": page, "limit": limit, } async def list_session_games(self, session_id: uuid.UUID) -> dict | None: session = await self.db.execute(select(Session).where(Session.id == session_id)) if not session.scalar_one_or_none(): return None return await self.list_games(session_id=session_id, limit=100) async def _load_game(self, game_id: uuid.UUID) -> GameInstance | None: result = await self.db.execute( select(GameInstance) .options(selectinload(GameInstance.session).selectinload(Session.participants)) .where(GameInstance.id == game_id) ) return result.scalar_one_or_none() def _game_detail(self, game: GameInstance) -> dict: session = game.session winner_name = None if game.winner_id: winner = next((p for p in session.participants if p.id == game.winner_id), None) winner_name = winner.display_name if winner else None return { "game": { "id": str(game.id), "plugin_id": game.plugin_id, "session_id": str(session.id), "invite_code": session.invite_code, "status": game.status, "started_at": game.started_at.isoformat(), "finished_at": game.finished_at.isoformat() if game.finished_at else None, "winner_id": str(game.winner_id) if game.winner_id else None, "winner_name": winner_name, }, "participants": [ { "id": str(p.id), "display_name": p.display_name, "role": p.role, "control_channel": p.control_channel, "guest_id": p.guest_id, "user_id": str(p.user_id) if p.user_id else None, } for p in session.participants ], }