Sanad_Package_4/routes_tour.py

113 lines
2.5 KiB
Python

"""/api/tour — guided-tour CRUD + runtime control (P4, package-local).
The TourStore + TourRuntime singletons are resolved lazily from the
Project.Sanad.main shim (app_p4 sets tour_store / tour_runtime). Kept Py-3.8.
"""
from __future__ import annotations
from typing import Any, List, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
router = APIRouter()
def _store():
try:
from Project.Sanad.main import tour_store
except Exception:
tour_store = None
if tour_store is None:
raise HTTPException(503, "tour store unavailable")
return tour_store
def _runtime():
try:
from Project.Sanad.main import tour_runtime
except Exception:
tour_runtime = None
if tour_runtime is None:
raise HTTPException(503, "tour runtime unavailable")
return tour_runtime
class TourStop(BaseModel):
place: Optional[str] = ""
narration: Optional[str] = ""
expression: Optional[str] = ""
gesture: Optional[str] = ""
greet: Optional[bool] = False
dwell_sec: Optional[float] = 8
class TourSave(BaseModel):
name: str
stops: List[TourStop] = []
id: Optional[str] = None
class StartBody(BaseModel):
tour_id: str
# ── tour definitions (builder) ──
@router.get("/")
async def list_tours():
return {"ok": True, "tours": _store().list()}
@router.post("/")
async def save_tour(payload: TourSave):
if not (payload.name or "").strip():
raise HTTPException(400, "name is required")
stops = [s.dict() for s in (payload.stops or [])]
return {"ok": True, "tour": _store().save(payload.name.strip(), stops, tid=payload.id)}
# ── runtime (must precede /{tid}) ──
@router.get("/status")
async def status():
return _runtime().status()
@router.post("/start")
async def start(body: StartBody):
return _runtime().start(body.tour_id)
@router.post("/stop")
async def stop():
return _runtime().stop()
@router.post("/pause")
async def pause():
return _runtime().pause()
@router.post("/resume")
async def resume():
return _runtime().resume()
@router.post("/skip")
async def skip():
return _runtime().skip()
@router.get("/{tid}")
async def get_tour(tid: str):
t = _store().get(tid)
if t is None:
raise HTTPException(404, "no tour %s" % tid)
return {"ok": True, "tour": t}
@router.delete("/{tid}")
async def delete_tour(tid: str):
if not _store().delete(tid):
raise HTTPException(404, "no tour %s" % tid)
return {"ok": True, "deleted": tid}