feat: POST /hands endpoint + seat in HUD villains (recorder step 1)
- POST /hands: store a structured hand from the recorder; normalize_structured() (via store_hand_history) is the shape authority, so the client stays best-effort. Enriches villain dossiers from the recorded players, same as the parser path. Verified live: best-effort body -> normalized (version, completeness, hero sync). - _session_villains: surface each player's latest seat so the recorder can auto-place known players on the table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -339,6 +339,24 @@ def create_app() -> FastAPI:
|
||||
async def hands_data(limit: int = 60) -> dict:
|
||||
return {"hands": poker.list_recent_hands(limit=limit)}
|
||||
|
||||
@app.post("/hands")
|
||||
async def hands_create(request: Request) -> dict:
|
||||
"""Store a structured hand built by the recorder. Body:
|
||||
{structured, session_id?, tag?, lesson?}. normalize_structured() (in
|
||||
store_hand_history) is the authority on shape, so the client can be best-effort."""
|
||||
body = await request.json()
|
||||
structured = body.get("structured")
|
||||
if not isinstance(structured, dict):
|
||||
return {"ok": False, "error": "missing structured hand body"}
|
||||
hid = await asyncio.to_thread(
|
||||
poker.store_hand_history, structured,
|
||||
session_id=body.get("session_id"), tag=body.get("tag"), lesson=body.get("lesson"),
|
||||
)
|
||||
# Enrich villain dossiers from the recorded players, same as the parser path.
|
||||
await asyncio.to_thread(poker.link_hand_players, hid, structured, body.get("session_id"))
|
||||
logbus.log("info", "hand recorded", id=hid, session=body.get("session_id"))
|
||||
return {"ok": True, "id": hid}
|
||||
|
||||
@app.get("/recap/{session_id}")
|
||||
async def recap_page() -> FileResponse:
|
||||
return FileResponse(str(_STATIC / "recap.html"))
|
||||
|
||||
Reference in New Issue
Block a user