diff --git a/lyra/poker.py b/lyra/poker.py index f4f83d8..f06428a 100644 --- a/lyra/poker.py +++ b/lyra/poker.py @@ -558,6 +558,22 @@ def log_hand(session_id: int | None = None, **fields) -> int: return int(cur.lastrowid) +def update_hand(hand_id: int, **fields) -> dict | None: + """Edit a logged hand's flat fields (fix a mislabeled board, result, villain). + Only known columns are touched. Returns the updated hand row or None.""" + sets, vals = [], [] + for k, v in fields.items(): + if k in _HAND_FIELDS and v is not None: + sets.append(f"{k} = ?") + vals.append(v) + if sets: + conn = _c() + with conn: + conn.execute(f"UPDATE poker_hands SET {', '.join(sets)} WHERE id = ?", + (*vals, hand_id)) + return get_hand(hand_id) + + def list_hands(session_id: int | None = None) -> list[dict]: sid = _resolve(session_id) if sid is None: diff --git a/lyra/web/server.py b/lyra/web/server.py index 0d024b3..931b9f2 100644 --- a/lyra/web/server.py +++ b/lyra/web/server.py @@ -164,6 +164,31 @@ def create_app() -> FastAPI: logbus.log("info", "poker session started (direct)", id=sid) return {"ok": True, "id": sid} + @app.post("/session/hand") + async def session_log_hand(request: Request) -> dict: + """Log a hand directly with flat fields (no LLM parse).""" + body = await request.json() + try: + hid = await asyncio.to_thread(lambda: poker.log_hand(**body)) + except ValueError as exc: + return {"ok": False, "error": str(exc)} + logbus.log("info", "hand logged (direct)", id=hid) + return {"ok": True, "id": hid} + + @app.patch("/hand/{hand_id}") + async def hand_update(hand_id: int, request: Request) -> dict: + """Edit a logged hand's flat fields.""" + body = await request.json() + h = await asyncio.to_thread(lambda: poker.update_hand(hand_id, **body)) + logbus.log("info", "hand edited", id=hand_id, fields=list(body)) + return {"ok": h is not None, "hand": h} + + @app.delete("/hand/{hand_id}") + async def hand_delete(hand_id: int) -> dict: + """Delete a logged hand.""" + ok = await asyncio.to_thread(poker.delete_entry, "hand", hand_id) + return {"ok": ok} + @app.delete("/session/entry/{kind}/{entry_id}") async def delete_entry(kind: str, entry_id: int) -> dict: """Delete one HUD entry (hand | stack | read | ritual) by id.""" diff --git a/tests/test_poker_api.py b/tests/test_poker_api.py index 2fc3adc..34d49d3 100644 --- a/tests/test_poker_api.py +++ b/tests/test_poker_api.py @@ -50,3 +50,17 @@ def test_post_session_starts_live(client): r = c.post("/session", json={"venue": "Wheeling", "stakes": "1/3", "buy_in": 400}) sid = r.json()["id"] assert poker.live_session()["id"] == sid + + +def test_post_hand_edit_and_delete(client): + c, poker = client + poker.start_session(buy_in=400) + r = c.post("/session/hand", json={"position": "BTN", "hole_cards": "22", "result": 120}) + assert r.json()["ok"] is True + hid = r.json()["id"] + r2 = c.patch(f"/hand/{hid}", json={"hole_cards": "2c2d"}) + assert r2.json()["ok"] is True + assert r2.json()["hand"]["hole_cards"] == "2c2d" + r3 = c.delete(f"/hand/{hid}") + assert r3.json()["ok"] is True + assert poker.get_hand(hid) is None