feat: undo / delete logged entries (fix fat-fingered live logging)
Previously the only delete was whole-session, so a mis-logged stack or a
mis-parsed hand was stuck on the HUD. Now:
- undo_last tool ("scratch that") — deletes the most recent hand/stack/read/
scar/confidence/reset in the live session; added to the Cash toolset.
- poker.delete_hand/stack/read/ritual + delete_entry dispatch + undo_last.
- DELETE /session/entry/{kind}/{id} endpoint.
- HUD: per-row × delete buttons on hands, confidence-bank, and scar-note rows
(stack/read deletes via the tool). Row ids now surfaced in the hud() bundle.
- test_modes.py +2 (undo_last across kinds, tool handler); 46 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+86
-2
@@ -242,6 +242,90 @@ def delete_session(session_id: int) -> dict:
|
||||
return counts
|
||||
|
||||
|
||||
# --- per-entry deletes / undo (fix fat-fingered live logging) ---
|
||||
|
||||
def delete_hand(hand_id: int) -> bool:
|
||||
"""Delete one hand and any player observations derived from it."""
|
||||
conn = _c()
|
||||
with conn:
|
||||
conn.execute("DELETE FROM player_observations WHERE hand_id = ?", (hand_id,))
|
||||
cur = conn.execute("DELETE FROM poker_hands WHERE id = ?", (hand_id,))
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def delete_stack(stack_id: int) -> bool:
|
||||
conn = _c()
|
||||
with conn:
|
||||
cur = conn.execute("DELETE FROM poker_stack_log WHERE id = ?", (stack_id,))
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def delete_read(read_id: int) -> bool:
|
||||
conn = _c()
|
||||
with conn:
|
||||
cur = conn.execute("DELETE FROM player_reads WHERE id = ?", (read_id,))
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def delete_ritual(ritual_id: int) -> bool:
|
||||
conn = _c()
|
||||
with conn:
|
||||
cur = conn.execute("DELETE FROM poker_rituals WHERE id = ?", (ritual_id,))
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def delete_entry(kind: str, entry_id: int) -> bool:
|
||||
"""Dispatch a per-entry delete by kind — for the HUD's row delete buttons."""
|
||||
return {
|
||||
"hand": delete_hand, "stack": delete_stack,
|
||||
"read": delete_read, "ritual": delete_ritual,
|
||||
}.get(kind, lambda _id: False)(entry_id)
|
||||
|
||||
|
||||
def undo_last(kind: str, session_id: int | None = None) -> str | None:
|
||||
"""Delete the most-recent entry of `kind` in the live session and return a short
|
||||
description of what was removed (None if there was nothing). For "scratch that".
|
||||
|
||||
kind: hand | stack | read | scar | confidence | reset | ritual.
|
||||
"""
|
||||
sid = _resolve(session_id)
|
||||
if sid is None:
|
||||
raise ValueError("no live session")
|
||||
k = (kind or "").lower().strip()
|
||||
|
||||
if k in ("scar", "confidence", "reset", "ritual"):
|
||||
sql = ("SELECT id, kind, content FROM poker_rituals WHERE session_id = ? "
|
||||
+ ("AND kind = ? " if k != "ritual" else "AND kind IN ('scar','confidence','reset') ")
|
||||
+ "ORDER BY id DESC LIMIT 1")
|
||||
params = (sid, k) if k != "ritual" else (sid,)
|
||||
r = _c().execute(sql, params).fetchone()
|
||||
if not r:
|
||||
return None
|
||||
delete_ritual(r["id"])
|
||||
label = _RITUAL_LABEL.get(r["kind"], r["kind"])
|
||||
return f"{label}" + (f": {r['content']}" if r["content"] else "")
|
||||
|
||||
table, desc_cols = {
|
||||
"hand": ("poker_hands", "position, hole_cards"),
|
||||
"stack": ("poker_stack_log", "amount"),
|
||||
"read": ("player_reads", "note"),
|
||||
}.get(k, (None, None))
|
||||
if not table:
|
||||
return None
|
||||
r = _c().execute(
|
||||
f"SELECT id, {desc_cols} FROM {table} WHERE session_id = ? ORDER BY id DESC LIMIT 1",
|
||||
(sid,),
|
||||
).fetchone()
|
||||
if not r:
|
||||
return None
|
||||
delete_entry(k, r["id"])
|
||||
if k == "hand":
|
||||
return f"hand ({(r['position'] or '?')} {r['hole_cards'] or ''})".strip()
|
||||
if k == "stack":
|
||||
return f"stack ${r['amount']:g}"
|
||||
return f"read: {r['note'][:50]}"
|
||||
|
||||
|
||||
def live_session() -> dict | None:
|
||||
"""The current open session, if any."""
|
||||
r = _c().execute(
|
||||
@@ -308,7 +392,7 @@ def stack_log(session_id: int | None = None) -> list[dict]:
|
||||
if sid is None:
|
||||
return []
|
||||
return [dict(r) for r in _c().execute(
|
||||
"SELECT amount, created_at FROM poker_stack_log WHERE session_id = ? ORDER BY id",
|
||||
"SELECT id, amount, created_at FROM poker_stack_log WHERE session_id = ? ORDER BY id",
|
||||
(sid,),
|
||||
).fetchall()]
|
||||
|
||||
@@ -996,7 +1080,7 @@ def hud(session_id: int | None = None) -> dict | None:
|
||||
|
||||
rituals = list_rituals(sid)
|
||||
by_kind = lambda k: [ # noqa: E731
|
||||
{"content": r["content"], "classification": r["classification"],
|
||||
{"id": r["id"], "content": r["content"], "classification": r["classification"],
|
||||
"hand_id": r["hand_id"], "at": r["created_at"]}
|
||||
for r in rituals if r["kind"] == k
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user