fix: render flat-logged hands + on-demand "build replay"

Quick-logged hands (log_hand) store flat fields with no structured JSON, so the
hand viewer dead-ended with "no structured data to replay" — even when the full
street-by-street action was captured (e.g. the KhQh-vs-Louis hand). Now:

- hand.html renders a readable STATIC view of any flat hand (hero cards, board,
  street narratives, result, lesson) instead of erroring; also handles empty/garbage
  structured rows by falling back to the flat view.
- "▶ Build replay" button + poker.reconstruct_hand + POST /hand/{id}/reconstruct:
  parse a flat hand's narrative into structured form on demand, making any
  quick-logged hand replayable without an LLM call per log during live play.
- test_modes.py +1 (reconstruct wiring).

(Also reconstructed the two live Meadows hands and removed one empty hand in the DB.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 06:02:10 +00:00
parent f2de7dec61
commit 44a559c5f9
4 changed files with 97 additions and 1 deletions
+7
View File
@@ -278,6 +278,13 @@ def create_app() -> FastAPI:
async def hand_data(hand_id: int) -> dict:
return poker.get_hand(hand_id) or {}
@app.post("/hand/{hand_id}/reconstruct")
async def hand_reconstruct(hand_id: int) -> dict:
"""Parse a flat (quick-logged) hand's narrative into a replayable structure."""
out = await asyncio.to_thread(poker.reconstruct_hand, hand_id)
logbus.log("info", "hand reconstructed", id=hand_id, ok=out is not None)
return {"ok": out is not None}
@app.get("/hands")
async def hands_page() -> FileResponse:
return FileResponse(str(_STATIC / "hands.html"))