Real extraction from shelf photos; test title list moves to tests/data

First live vision run: 4 shelf photos -> 27 unique titles with edition
cues (publishers, Bookcase Game/Designer's Edition wording, art notes);
cross-photo dedupe merged repeat sightings. data/titles.json now holds
real pipeline data, so the hand-typed resolve test list moves to
tests/data/titles.json (tests and record_fixtures updated), and the
matches.csv generated from stub fixtures for that list is removed —
the real resolve run will regenerate it once the BGG token arrives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-01 13:16:43 -04:00
parent 9897da7f82
commit 4f2ae5f525
5 changed files with 377 additions and 30 deletions
-8
View File
@@ -1,8 +0,0 @@
title_raw,bgg_id,bgg_name,year,type,match_status,version_id,version_name,version_status,candidates_json,version_candidates_json,source_photos
Catan,13,CATAN,1995,boardgame,auto,,,version_unknown,"[{""bgg_id"": 13, ""name"": ""CATAN"", ""year"": 1995, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],hand-typed-test-list
Wingspan,266192,Wingspan,2019,boardgame,auto,465063,English first edition,version_auto,"[{""bgg_id"": 266192, ""name"": ""Wingspan"", ""year"": 2019, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 465063, ""name"": ""English first edition"", ""year"": 2019, ""publishers"": [""Stonemaier Games""], ""languages"": [""English""], ""score"": 5}, {""version_id"": 521212, ""name"": ""English fourth printing"", ""year"": 2020, ""publishers"": [""Stonemaier Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 472430, ""name"": ""German edition"", ""year"": 2019, ""publishers"": [""Feuerland Spiele""], ""languages"": [""German""], ""score"": 2}]",hand-typed-test-list
Wingspan: European Expansion,290448,Wingspan: European Expansion,2019,boardgameexpansion,auto,,,version_unknown,"[{""bgg_id"": 290448, ""name"": ""Wingspan: European Expansion"", ""year"": 2019, ""type"": ""boardgameexpansion"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],hand-typed-test-list
Wingspan Europe,,,,,unmatched,,,,[],[],hand-typed-test-list
Café International,373,Café International,1989,boardgame,auto,,,version_unknown,"[{""bgg_id"": 373, ""name"": ""Café International"", ""year"": 1989, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],hand-typed-test-list
Citadels,,,,,ambiguous,,,,"[{""bgg_id"": 478, ""name"": ""Citadels"", ""year"": 2000, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": 85000, ""rank"": 250}, {""bgg_id"": 205398, ""name"": ""Citadels"", ""year"": 2016, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": 24000, ""rank"": 400}]",[],hand-typed-test-list
Blorvath: Quest of the Zzyzx,,,,,unmatched,,,,[],[],hand-typed-test-list
1 title_raw bgg_id bgg_name year type match_status version_id version_name version_status candidates_json version_candidates_json source_photos
2 Catan 13 CATAN 1995 boardgame auto version_unknown [{"bgg_id": 13, "name": "CATAN", "year": 1995, "type": "boardgame", "exact": true, "fuzzy": 100.0, "owned": null, "rank": null}] [] hand-typed-test-list
3 Wingspan 266192 Wingspan 2019 boardgame auto 465063 English first edition version_auto [{"bgg_id": 266192, "name": "Wingspan", "year": 2019, "type": "boardgame", "exact": true, "fuzzy": 100.0, "owned": null, "rank": null}] [{"version_id": 465063, "name": "English first edition", "year": 2019, "publishers": ["Stonemaier Games"], "languages": ["English"], "score": 5}, {"version_id": 521212, "name": "English fourth printing", "year": 2020, "publishers": ["Stonemaier Games"], "languages": ["English"], "score": 3}, {"version_id": 472430, "name": "German edition", "year": 2019, "publishers": ["Feuerland Spiele"], "languages": ["German"], "score": 2}] hand-typed-test-list
4 Wingspan: European Expansion 290448 Wingspan: European Expansion 2019 boardgameexpansion auto version_unknown [{"bgg_id": 290448, "name": "Wingspan: European Expansion", "year": 2019, "type": "boardgameexpansion", "exact": true, "fuzzy": 100.0, "owned": null, "rank": null}] [] hand-typed-test-list
5 Wingspan Europe unmatched [] [] hand-typed-test-list
6 Café International 373 Café International 1989 boardgame auto version_unknown [{"bgg_id": 373, "name": "Café International", "year": 1989, "type": "boardgame", "exact": true, "fuzzy": 100.0, "owned": null, "rank": null}] [] hand-typed-test-list
7 Citadels ambiguous [{"bgg_id": 478, "name": "Citadels", "year": 2000, "type": "boardgame", "exact": true, "fuzzy": 100.0, "owned": 85000, "rank": 250}, {"bgg_id": 205398, "name": "Citadels", "year": 2016, "type": "boardgame", "exact": true, "fuzzy": 100.0, "owned": 24000, "rank": 400}] [] hand-typed-test-list
8 Blorvath: Quest of the Zzyzx unmatched [] [] hand-typed-test-list
+335 -20
View File
@@ -1,40 +1,355 @@
[ [
{ {
"title_raw": "Catan", "title_raw": "ROYALS",
"confidence": "high", "confidence": "high",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "",
}, "edition_hint": "",
{ "year_hint": null,
"title_raw": "Wingspan",
"confidence": "high",
"publisher_hint": "Stonemaier Games",
"year_hint": 2019,
"language_hint": "English", "language_hint": "English",
"source_photos": ["hand-typed-test-list"] "art_notes": "Dark maroon/brown spine with ornate gold scroll lettering and a portrait of a woman in period dress on the lower spine",
"source_photos": [
"IMG_4499.jpeg"
],
"title_normalized": "royals"
}, },
{ {
"title_raw": "Wingspan: European Expansion", "title_raw": "CAT CRIMES",
"confidence": "high", "confidence": "high",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "ThinkFun",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Top-facing box with subtitle 'Who's to Blame Logic Game', illustration of an orange cat, 'AGES 8+' label",
"source_photos": [
"IMG_4499.jpeg"
],
"title_normalized": "cat crimes"
}, },
{ {
"title_raw": "Wingspan Europe", "title_raw": "COSMIC ENCOUNTER",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Black spine with colorful bubble-style logo text, 'the game that breaks its own rules' tagline, 'For 3 to 6 Players AGES 12 TO ADULT'",
"source_photos": [
"IMG_4499.jpeg"
],
"title_normalized": "cosmic encounter"
},
{
"title_raw": "SHERIFF OF NOTTINGHAM",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Orange/wood-toned spine with green stylized text, illustrated medieval character artwork at top",
"source_photos": [
"IMG_4499.jpeg"
],
"title_normalized": "sheriff of nottingham"
},
{
"title_raw": "5 MINUTE DUNGEON",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Yellow/orange spine with dark fantasy creature artwork, credited 'Connor Reid' at top, green '5 MINUTE' text near bottom",
"source_photos": [
"IMG_4499.jpeg"
],
"title_normalized": "5 minute dungeon"
},
{
"title_raw": "Britannia",
"confidence": "high",
"publisher_hint": "The Avalon Hill Game Company, Baltimore, Maryland",
"edition_hint": "Bookcase Game",
"year_hint": null,
"language_hint": "English",
"art_notes": "Dark navy spine, illustration of warriors/chariots at top in gold-framed box",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "britannia"
},
{
"title_raw": "CIVILIZATION Game of the Heroic Age - The Dawn of History 8000 BC to 250 BC",
"confidence": "high",
"publisher_hint": "The Avalon Hill Game Company, Baltimore, Maryland",
"edition_hint": "Bookcase Game",
"year_hint": null,
"language_hint": "English",
"art_notes": "Blue spine with illustrated figures from ancient civilizations",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "civilization game of heroic age dawn of history 8000 bc to 250 bc"
},
{
"title_raw": "ADVANCED CIVILIZATION Game Expansion of the Heroic Age - Featuring New Civilization, Commodity, and Calamity Cards",
"confidence": "high",
"publisher_hint": "The Avalon Hill Game Company, Baltimore, Maryland",
"edition_hint": "Bookcase Game",
"year_hint": null,
"language_hint": "English",
"art_notes": "Red/orange spine with illustrated ancient figures, similar style to Civilization",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "advanced civilization game expansion of heroic age featuring new civilization commodity and calamity cards"
},
{
"title_raw": "DOCTOR WHO The Game of Time & Space",
"confidence": "high",
"publisher_hint": "Games Workshop",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Bright red spine with yellow/green Doctor Who logo, star burst design, image of Doctor Who character at top",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "doctor who game of time and space"
},
{
"title_raw": "SORCERER The Game of Magical Conflict",
"confidence": "high",
"publisher_hint": "Simulations Publications Incorporated (SPI)",
"edition_hint": "Designer's Edition",
"year_hint": null,
"language_hint": "English",
"art_notes": "Black spine with white bold text, worn/aged appearance, SPI logo at bottom",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "sorcerer game of magical conflict"
},
{
"title_raw": "STARFORCE ALPHA CENTAURI Interstellar Conflict in the 25th Century",
"confidence": "high",
"publisher_hint": "Simulations Publications Incorporated (SPI)",
"edition_hint": "Designer's Edition",
"year_hint": null,
"language_hint": "English",
"art_notes": "Gray/silver spine with futuristic imagery, 'A Future-History Simulation Game' subtitle",
"source_photos": [
"IMG_4500.jpeg"
],
"title_normalized": "starforce alpha centauri interstellar conflict in 25th century"
},
{
"title_raw": "SKIP-BO",
"confidence": "medium", "confidence": "medium",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Red box spine, partially visible on far left edge",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "skip bo"
}, },
{ {
"title_raw": "Café International", "title_raw": "a Gentle Rain",
"confidence": "high", "confidence": "high",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "",
"edition_hint": "Bloom Edition",
"year_hint": null,
"language_hint": "English",
"art_notes": "Dark teal/green box top with colorful vertical striped pattern below title",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "gentle rain"
}, },
{ {
"title_raw": "Citadels", "title_raw": "PATCH WORK",
"confidence": "medium",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Salmon/pink colored box, partially obscured behind 'a Gentle Rain' box",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "patch work"
},
{
"title_raw": "SANTORINI",
"confidence": "high", "confidence": "high",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Blue box spine with white bold lettering, Mediterranean/Greek architecture theme implied by name",
"source_photos": [
"IMG_4501.jpeg",
"IMG_4502.jpeg"
],
"title_normalized": "santorini"
}, },
{ {
"title_raw": "Blorvath: Quest of the Zzyzx", "title_raw": "Utter Nonsense!",
"confidence": "low", "confidence": "high",
"source_photos": ["hand-typed-test-list"] "publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Yellow/mustard colored box with vintage illustration of two men in coats and hats facing each other",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "utter nonsense"
},
{
"title_raw": "Cards Against Humanity",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Black box with white bold text, simple minimalist design",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "cards against humanity"
},
{
"title_raw": "Joking Hazard",
"confidence": "high",
"publisher_hint": "CYANIDE & HAPPINESS",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "White box with yellow stick figure character illustration",
"source_photos": [
"IMG_4501.jpeg",
"IMG_4502.jpeg"
],
"title_normalized": "joking hazard"
},
{
"title_raw": "EXPLODING KITTENS",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Bottom right corner, colorful playful cat-themed box art",
"source_photos": [
"IMG_4501.jpeg"
],
"title_normalized": "exploding kittens"
},
{
"title_raw": "Herbaceous",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Colorful spine with illustrated herb/plant imagery, 'A player's game of...' text visible",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "herbaceous"
},
{
"title_raw": "Wiz-War",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Dark blue/black box, partial text 'board game of...battle and treasures'",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "wiz war"
},
{
"title_raw": "SCRAWL",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Bright pink/magenta box with striped pattern, tagline 'Doodle your way to disaster'",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "scrawl"
},
{
"title_raw": "FLAT TOP",
"confidence": "high",
"publisher_hint": "",
"edition_hint": "",
"year_hint": 1942,
"language_hint": "English",
"art_notes": "Blue spine, subtitle 'A GAME of Carrier Battles in the Pacific • 1942'",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "flat top"
},
{
"title_raw": "Diplomacy",
"confidence": "high",
"publisher_hint": "Avalon Hill",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Black spine, subtitle 'The Exciting Game of International Intrigue - For 2 to 7 Players'",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "diplomacy"
},
{
"title_raw": "Age of Renaissance",
"confidence": "high",
"publisher_hint": "Avalon Hill",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Tan/brown spine with gold diamond emblem, subtitle about building dominant civilization of Europe",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "age of renaissance"
},
{
"title_raw": "History of the World",
"confidence": "high",
"publisher_hint": "Avalon Hill",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Blue/purple spine, subtitle 'Game of World Conquest'",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "history of world"
},
{
"title_raw": "Dungeon!",
"confidence": "high",
"publisher_hint": "TSR",
"edition_hint": "",
"year_hint": null,
"language_hint": "English",
"art_notes": "Purple box bottom with fantasy dungeon-themed artwork, visible at base of stack",
"source_photos": [
"IMG_4502.jpeg"
],
"title_normalized": "dungeon"
} }
] ]
+1 -1
View File
@@ -30,7 +30,7 @@ def main() -> None:
"real recordings." "real recordings."
) )
client = BGGClient(cache_dir=FIXTURE_CACHE) client = BGGClient(cache_dir=FIXTURE_CACHE)
for entry in load_titles(Path("data/titles.json")): for entry in load_titles(Path("tests/data/titles.json")):
row = resolve_entry(client, entry) row = resolve_entry(client, entry)
print( print(
f"{entry.title_raw!r}: {row.match_status} " f"{entry.title_raw!r}: {row.match_status} "
+40
View File
@@ -0,0 +1,40 @@
[
{
"title_raw": "Catan",
"confidence": "high",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Wingspan",
"confidence": "high",
"publisher_hint": "Stonemaier Games",
"year_hint": 2019,
"language_hint": "English",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Wingspan: European Expansion",
"confidence": "high",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Wingspan Europe",
"confidence": "medium",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Café International",
"confidence": "high",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Citadels",
"confidence": "high",
"source_photos": ["hand-typed-test-list"]
},
{
"title_raw": "Blorvath: Quest of the Zzyzx",
"confidence": "low",
"source_photos": ["hand-typed-test-list"]
}
]
+1 -1
View File
@@ -28,7 +28,7 @@ from bggpipe.resolve import (
) )
FIXTURES = Path(__file__).parent / "fixtures" / "bgg_cache" FIXTURES = Path(__file__).parent / "fixtures" / "bgg_cache"
TITLES_JSON = Path(__file__).parent.parent / "data" / "titles.json" TITLES_JSON = Path(__file__).parent / "data" / "titles.json"
def _no_network(request: httpx.Request) -> httpx.Response: def _no_network(request: httpx.Request) -> httpx.Response: