Enrich's summary stops reporting a negative count

"136 entries (136 added/refreshed this run; -11 already present or
waiting)" — local library entries were counted in the same tally as API
fetches, but they have no API target, so the remainder went negative
once eleven off-BGG games existed. The two populations are now counted
and named separately, and zero-valued clauses are omitted:
"136 entries (0 fetched from BGG; 11 local-only; 125 already present or
waiting)."

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
Eric Wagoner
2026-08-05 23:32:55 -04:00
co-authored by Claude Fable 5
parent b5e13be335
commit 2e1693c0be
3 changed files with 7235 additions and 3 deletions
+7185
View File
File diff suppressed because it is too large Load Diff
+10 -3
View File
@@ -122,7 +122,6 @@ def run_enrich(
else [], else [],
"source_photos": [p for p in row["source_photos"].split(";") if p], "source_photos": [p for p in row["source_photos"].split(";") if p],
} }
updated += 1
# prune keys no current target claims: a row whose version was approved # prune keys no current target claims: a row whose version was approved
# after a bare-key run (or was later rejected) must not leave an orphan # after a bare-key run (or was later rejected) must not leave an orphan
@@ -143,10 +142,18 @@ def run_enrich(
games_path, json.dumps(games, indent=2, ensure_ascii=False) + "\n" games_path, json.dumps(games, indent=2, ensure_ascii=False) + "\n"
) )
# count the two populations separately: local entries are built from
# photo reads and have no target, so folding them into the API tally
# made "already present or waiting" go negative
waiting = len(targets) - updated
parts = [f"{updated} fetched from BGG"]
if local_keys:
parts.append(f"{len(local_keys)} local-only")
if waiting:
parts.append(f"{waiting} already present or waiting")
typer.echo( typer.echo(
f"games.json: {len(games)} entr{'y' if len(games) == 1 else 'ies'} " f"games.json: {len(games)} entr{'y' if len(games) == 1 else 'ies'} "
f"({updated} added/refreshed this run; " f"({'; '.join(parts)})."
f"{len(targets) - updated} already present or waiting)."
) )
if blocked: if blocked:
remaining = [i for i in need if i not in fetched] remaining = [i for i in need if i not in fetched]
+40
View File
@@ -278,3 +278,43 @@ def test_local_rows_enrich_from_their_own_reads(tmp_path):
# idempotent: a second run keeps the entry (no prune, no dupe) # idempotent: a second run keeps the entry (no prune, no dupe)
games2 = run_enrich(cfg, client=client) games2 = run_enrich(cfg, client=client)
assert key in games2 assert key in games2
def test_summary_counts_local_and_api_entries_separately(tmp_path, capsys):
"""Local entries have no API target, so folding them into the fetched
tally made "already present or waiting" report a NEGATIVE count."""
from bggpipe.enrich import run_enrich
cfg = Config(data_dir=tmp_path / "data")
cfg.data_dir.mkdir(parents=True)
write_matches(
cfg.matches_path,
[
{
"title_raw": "Homebrew",
"bgg_id": "",
"bgg_name": "",
"year": "",
"type": "",
"match_status": "local",
"version_id": "",
"version_name": "",
"version_status": "",
"candidates_json": "[]",
"version_candidates_json": "[]",
"source_photos": "shelf.jpg",
}
],
)
cfg.titles_path.write_text("[]")
client = BGGClient(
cache_dir=cfg.data_dir / "cache",
transport=httpx.MockTransport(_no_network),
)
games = run_enrich(cfg, client=client)
out = capsys.readouterr().out
assert len(games) == 1
assert "1 local-only" in out
import re as _re
assert not _re.search(r"-\d", out) # no negative tallies