Spaces:
Paused
Paused
| # Copyright (c) 2025-2026, RTE (https://www.rte-france.com) | |
| # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. | |
| # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, | |
| # you can obtain one at http://mozilla.org/MPL/2.0/. | |
| # SPDX-License-Identifier: MPL-2.0 | |
| """Game Mode HTTP routes — solution capitalisation + lever hints. | |
| Registered on the app via :func:`install_game_routes` (same install-once | |
| pattern as ``services/api_errors.install_error_handlers``); kept out of | |
| ``main.py`` so it stays under the module-size ceiling | |
| (scripts/check_code_quality.py). Store logic: ``services/game_solutions.py``; | |
| wire models: ``services/game_solution_models.py``. | |
| These routes are NOT lockdown-gated (D7): unlike the desktop-era filesystem | |
| RPCs, they never touch a client-supplied path — the store root is fixed | |
| server-side (``COSTUDY4GRID_DATA_DIR`` cascade) and ids are slugged, so they | |
| stay available on the public HuggingFace Space where the shared base lives. | |
| """ | |
| from __future__ import annotations | |
| from fastapi import FastAPI, HTTPException, Query | |
| from expert_backend.services import game_solutions | |
| from expert_backend.services.game_solution_models import ( | |
| GameLeverStatsResponse, | |
| LogGameSolutionRequest, | |
| LogGameSolutionResponse, | |
| PlayerSessionsResponse, | |
| ) | |
| def install_game_routes(app: FastAPI) -> None: | |
| """Register the Game Mode routes on ``app`` (call once at import).""" | |
| def log_game_solution(request: LogGameSolutionRequest) -> dict: | |
| """Capitalise a Game Mode retained proposition into the shared | |
| solution base and report novelty (bonus points) + per-action usage | |
| frequencies. | |
| Pure file IO on the store directory — no network state involved, so | |
| no network lock / busy gate; the store serializes its own | |
| read-modify-write with a module-level lock. | |
| """ | |
| try: | |
| return game_solutions.log_solution(request.model_dump()) | |
| except ValueError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| def game_lever_stats( | |
| network_path: str = Query(""), | |
| contingency_id: str = Query(...), | |
| top_n: int = Query(5, ge=1, le=20), | |
| ) -> dict: | |
| """Most-used unitary levers of a (network, contingency) context in | |
| the shared solution base — the Game Mode beginner-assistance hints. | |
| Read-only scan of the store.""" | |
| try: | |
| return game_solutions.lever_stats( | |
| network_path, contingency_id, top_n=top_n) | |
| except ValueError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| def game_player_sessions(player: str = Query("")) -> dict: | |
| """Distinct sessions a player has already recorded in the shared | |
| solution base — seeds the default session name / index on the Game | |
| Mode config screen. Read-only store scan; an empty handle → zero.""" | |
| return game_solutions.player_session_count(player) | |