Review web UI: live-follow data files, --dev code reload
ReviewSession re-reads matches.csv/titles.json on mtime change so external extract/resolve runs show up per-request (and stale in-memory rows can no longer overwrite them); the page polls state every 3s, re-rendering only on change and never mid-typing. --web --dev adds uvicorn source-watch restarts, scoped to the package dir so decision writes to data/ don't trigger them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -334,3 +334,52 @@ def test_static_assets_served_with_allowlist(tmp_path):
|
||||
assert web.get("/static/favicon.png").status_code == 200
|
||||
assert web.get("/static/nope.js").status_code == 404
|
||||
assert web.get("/static/..%2Ftemplates%2Freview.html").status_code == 404
|
||||
|
||||
|
||||
# -- live data reload ---------------------------------------------------
|
||||
|
||||
|
||||
def test_state_picks_up_external_matches_rewrite(tmp_path):
|
||||
web, cfg = make_client(tmp_path)
|
||||
assert web.get("/api/state").json()["summary"]["total"] == 3
|
||||
|
||||
rows = read_matches(cfg.matches_path)
|
||||
rows.append(_row(title_raw="Newcomer", match_status="unmatched"))
|
||||
write_matches(cfg.matches_path, rows)
|
||||
|
||||
fresh = web.get("/api/state").json()
|
||||
assert fresh["summary"]["total"] == 4
|
||||
assert any(p["title_raw"] == "Newcomer" for p in fresh["pending"])
|
||||
|
||||
|
||||
def test_decision_lands_on_externally_added_row(tmp_path):
|
||||
# A row that did not exist at server start is still reviewable.
|
||||
web, cfg = make_client(tmp_path)
|
||||
rows = read_matches(cfg.matches_path)
|
||||
rows.append(_row(title_raw="Newcomer", match_status="unmatched"))
|
||||
write_matches(cfg.matches_path, rows)
|
||||
|
||||
res = web.post(
|
||||
"/api/decision",
|
||||
json={
|
||||
"title_raw": "Newcomer",
|
||||
"source_photos": "shelf.jpg",
|
||||
"action": "reject",
|
||||
},
|
||||
)
|
||||
assert res.status_code == 200
|
||||
saved = {r["title_raw"]: r for r in read_matches(cfg.matches_path)}
|
||||
assert saved["Newcomer"]["match_status"] == "rejected"
|
||||
|
||||
|
||||
def test_own_saves_do_not_count_as_external_changes(tmp_path):
|
||||
from bggpipe.review import ReviewSession
|
||||
|
||||
cfg = make_cfg(tmp_path)
|
||||
session = ReviewSession(cfg, client=unauthorized_client(tmp_path))
|
||||
row = next(r for r in session.rows if r["title_raw"] == "Mystery")
|
||||
session.decide_reject(row)
|
||||
assert session.reload_if_changed() is False # my own write
|
||||
write_matches(cfg.matches_path, session.rows + [_row(title_raw="X")])
|
||||
assert session.reload_if_changed() is True # someone else's
|
||||
assert any(r["title_raw"] == "X" for r in session.rows)
|
||||
|
||||
Reference in New Issue
Block a user