Remove-from-catalog: a third durable curation store
Titles that aren't games (misread box art, out-of-scope items) can now be removed outright: a danger button in the catalog's edit panel posts /api/remove-title, which drops the line's matches rows (veto'd ones too — removal is the human explicitly discarding the line), records the decision photo-scoped in data/title_removals.json, and replays titles.json. Every rebuild filters removed sightings after edits and before dedupe, so re-extraction cannot resurrect them; undo by deleting the record from the store. The three stores now share one scoped-record parser and recorder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
@@ -726,3 +726,43 @@ def test_single_photo_rowless_entry_is_not_splittable(tmp_path):
|
||||
if c["title_raw"] == "Fresh Off The Shelf"
|
||||
)
|
||||
assert line["can_split"] is False
|
||||
|
||||
|
||||
def test_remove_title_is_durable_and_drops_vetoed_rows(tmp_path):
|
||||
cfg = make_cfg(tmp_path)
|
||||
rows = read_matches(cfg.matches_path)
|
||||
rows.append(
|
||||
_row(
|
||||
title_raw="Citadels",
|
||||
match_status="approved",
|
||||
bgg_id="478",
|
||||
source_photos="shelf.jpg",
|
||||
dedupe_veto="1",
|
||||
)
|
||||
)
|
||||
write_matches(cfg.matches_path, rows)
|
||||
web = TestClient(create_app(cfg, client=unauthorized_client(tmp_path)))
|
||||
res = web.post(
|
||||
"/api/remove-title",
|
||||
json={"title_raw": "Citadels", "source_photos": "shelf.jpg"},
|
||||
)
|
||||
assert res.status_code == 200
|
||||
assert "Citadels" not in [c["title_raw"] for c in res.json()["catalog"]]
|
||||
# removal is explicit: the veto'd row goes too (unlike edits)
|
||||
assert not any(r["title_raw"] == "Citadels" for r in read_matches(cfg.matches_path))
|
||||
(record,) = json.loads(cfg.title_removals_path.read_text())
|
||||
assert record == {"title": "Citadels", "photos": ["shelf.jpg"]}
|
||||
# durable: a fresh replay keeps it gone; the sibling title is untouched
|
||||
from bggpipe.extract import replay_titles
|
||||
|
||||
replay_titles(cfg)
|
||||
titles = {e["title_raw"] for e in json.loads(cfg.titles_path.read_text())}
|
||||
assert titles == {"Fresh Off The Shelf"}
|
||||
# unknown titles still 404
|
||||
assert (
|
||||
web.post(
|
||||
"/api/remove-title",
|
||||
json={"title_raw": "No Such Game", "source_photos": "x.jpg"},
|
||||
).status_code
|
||||
== 404
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user