Eric's wall answered the orientation question with a screenshot full of red: the capacity model assumed one flat stack per opening, so his spine-out cubes read 200% full. His reviewer's prescription, built to its acceptance cases: each opening packs a FLAT lane (thinnest axes against interior height, claiming the widest flat box's width) and a STANDING lane beside it (boxes on edge against the remaining width; standing boxes must fit height and depth upright). The largest- footprint class lies flat, smaller boxes stand, any box's lane is flippable per-assignment (▬/▮ toggle in the opening view, ✱ marks an override), and ⚠ now means NO packing fits — not merely "tall stack". Unmeasured boxes take no lane but keep their honesty tag; dual fill bars show each lane's budget; a broken opening is never offered by the suggester. On the real wall: the double-wides and half the cubes went green (7 games = 13.12" flat + 2.91" standing), and the remaining warnings mark cubes that genuinely hold 12-16 boxes. Both reviewer acceptance tests pass verbatim. And the export gains the humanity Eric asked for: game pages say where each box lives, containers list their contents, and a shelves page draws the wall as it physically stands — proportional cells, zones, descriptions, every opening linking its residents. Publishing a shelf layout is a choice: --no-shelves keeps the layer out. "shelves" joins "art" as a reserved slug. 387 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
106 lines
3.2 KiB
Python
106 lines
3.2 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 run_dims_report
|
|
from bggpipe.shelves import save_furniture, save_locations
|
|
|
|
|
|
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
|
|
}
|
|
)
|
|
)
|
|
# no furniture yet: sizes-only report, pointed at the Shelves page
|
|
summary = run_dims_report(cfg)
|
|
assert summary["by_source"] == {
|
|
"version": 1,
|
|
"unanimous": 1,
|
|
"conflicting": 1,
|
|
"absent": 1,
|
|
}
|
|
assert summary["misfits"] == []
|
|
assert summary["unknown"] == ["Argued About", "Never Measured"]
|
|
out = capsys.readouterr().out
|
|
assert "No furniture on file" in out
|
|
|
|
# with furniture: per-opening capacity, misfit and overfull warnings
|
|
save_furniture(
|
|
cfg,
|
|
[
|
|
{
|
|
"name": "Den",
|
|
"openings": [
|
|
{
|
|
"id": "den-a1",
|
|
"label": "A1",
|
|
"zone": "",
|
|
"width_in": 13.25,
|
|
"height_in": 13.25,
|
|
"depth_in": 15.4,
|
|
}
|
|
],
|
|
}
|
|
],
|
|
)
|
|
save_locations(
|
|
cfg,
|
|
{
|
|
"1": {"opening_id": "den-a1", "note": ""},
|
|
"2": {"opening_id": "den-a1", "note": ""},
|
|
},
|
|
)
|
|
summary = run_dims_report(cfg)
|
|
assert summary["misfits"] == ["Monster Box"]
|
|
out = capsys.readouterr().out
|
|
assert "Monster Box" in out # named as unplaceable/misfit
|
|
assert "unshelved" in out # the conflicting + absent games have no home
|
|
assert "can't be size-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)
|