Eric's brief, implemented to the letter. BGG keeps physical dimensions on VERSIONS, not games, so enrich runs a second cached pass over thing?versions=1 (same batching, token, rate limit, and cache as every call). A game with a chosen version takes that exact version's numbers (source "version", mirrored onto its version dict); a versionless game gets numbers only when every printing with data agrees within 0.5" per axis (source "unanimous", keeping the MAX per axis — the planning question is "will it fit"); disagreement stores nulls as "conflicting" — never a guess — and BGG's 0 parses as "never entered", not a real dimension. rpgitems and local games are "absent". Read-only: upload untouched. The new offline `bggpipe dims` reports coverage by source, the ten biggest footprints, and a Kallax fit check (13.2" square opening, 15.4" deep; a box fits if SOME orientation puts two axes through the opening within the depth) — naming every misfit and every game whose dimensions can't be verified, because can't-verify ≠ fits. Trusted numbers surface on the Library detail page as a "box" row. First real run: 54 version-exact, 16 unanimous, 39 conflicting, 27 absent; three genuine misfits (Bugs in the Kitchen's 17" box, History of the World and Risk LotR both over the 15.4" depth). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
85 lines
2.7 KiB
Python
85 lines
2.7 KiB
Python
"""The shelf-space report: fit math, coverage buckets, honest unknowns."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
import typer
|
|
|
|
from bggpipe.config import Config
|
|
from bggpipe.dims import fits_kallax, run_dims_report
|
|
|
|
|
|
def test_fits_kallax_tries_every_orientation():
|
|
assert fits_kallax(11.6, 11.6, 2.8) # ordinary big-box
|
|
# too tall to stand, but slides in lying down: depth axis takes 15.0
|
|
assert fits_kallax(15.0, 12.0, 3.0)
|
|
# two axes over the opening: no orientation works
|
|
assert not fits_kallax(16.2, 16.2, 4.0)
|
|
# fits the opening but too deep to close the wall behind it
|
|
assert not fits_kallax(12.0, 12.0, 15.5)
|
|
# exact boundary counts as fitting
|
|
assert fits_kallax(13.2, 13.2, 15.4)
|
|
|
|
|
|
def test_report_buckets_misfits_and_unknowns(tmp_path, capsys):
|
|
cfg = Config(data_dir=tmp_path / "data")
|
|
cfg.data_dir.mkdir(parents=True)
|
|
cfg.games_path.write_text(
|
|
json.dumps(
|
|
{
|
|
"1": {
|
|
"name": "Fits Fine",
|
|
"dims": {
|
|
"width_in": 11.6,
|
|
"length_in": 11.6,
|
|
"depth_in": 2.8,
|
|
"weight_lb": 4,
|
|
"source": "version",
|
|
},
|
|
},
|
|
"2": {
|
|
"name": "Monster Box",
|
|
"dims": {
|
|
"width_in": 16.2,
|
|
"length_in": 16.2,
|
|
"depth_in": 4.0,
|
|
"weight_lb": 7,
|
|
"source": "unanimous",
|
|
},
|
|
},
|
|
"3": {
|
|
"name": "Argued About",
|
|
"dims": {
|
|
"width_in": None,
|
|
"length_in": None,
|
|
"depth_in": None,
|
|
"weight_lb": None,
|
|
"source": "conflicting",
|
|
},
|
|
},
|
|
"4": {"name": "Never Measured"}, # pre-dims entry: absent
|
|
}
|
|
)
|
|
)
|
|
summary = run_dims_report(cfg)
|
|
assert summary["by_source"] == {
|
|
"version": 1,
|
|
"unanimous": 1,
|
|
"conflicting": 1,
|
|
"absent": 1,
|
|
}
|
|
assert summary["misfits"] == ["Monster Box"]
|
|
# can't verify is NOT the same as fits: both unknowns are named
|
|
assert summary["unknown"] == ["Argued About", "Never Measured"]
|
|
out = capsys.readouterr().out
|
|
assert "do NOT fit" in out and "Monster Box" in out
|
|
assert "can't be verified" in out and "Never Measured" in out
|
|
|
|
|
|
def test_report_without_games_json_exits_with_guidance(tmp_path):
|
|
cfg = Config(data_dir=tmp_path / "data")
|
|
with pytest.raises(typer.Exit):
|
|
run_dims_report(cfg)
|