Enrichit les frises minifigs avec fig_num et possession
@@ -351,3 +351,4 @@ Le script lit `data/intermediate/minifigs_by_set.csv`, `data/intermediate/sets_e
|
|||||||
|
|
||||||
- `data/intermediate/minifig_character_sets.csv` : apparitions des personnages avec set, identifiant de set, année et fig_num.
|
- `data/intermediate/minifig_character_sets.csv` : apparitions des personnages avec set, identifiant de set, année et fig_num.
|
||||||
- `figures/step32/minifig_characters/{personnage}.png` : frise horizontale par personnage, composée des visuels de minifigs dans l’ordre chronologique, annotés avec l’année et le numéro de set. Les minifigs dont l’image n’est pas disponible sont remplacées par un rectangle neutre pour matérialiser le manque.
|
- `figures/step32/minifig_characters/{personnage}.png` : frise horizontale par personnage, composée des visuels de minifigs dans l’ordre chronologique, annotés avec l’année et le numéro de set. Les minifigs dont l’image n’est pas disponible sont remplacées par un rectangle neutre pour matérialiser le manque.
|
||||||
|
- Les étiquettes affichent aussi l’identifiant de la minifig (`fig-*`) et un astérisque à côté du set (`set_num*`) lorsqu’il est présent dans la collection.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 325 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 502 KiB After Width: | Height: | Size: 504 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 585 KiB After Width: | Height: | Size: 587 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
@@ -67,7 +67,8 @@ def build_character_collage(
|
|||||||
cells: List[Image.Image] = []
|
cells: List[Image.Image] = []
|
||||||
for row in entries:
|
for row in entries:
|
||||||
image_path = resources_dir / row["set_id"] / sanitized / "minifig.jpg"
|
image_path = resources_dir / row["set_id"] / sanitized / "minifig.jpg"
|
||||||
label = f"{row['year']} - {row['set_num']}"
|
owned = "*" if row.get("in_collection", "").lower() == "true" else ""
|
||||||
|
label = f"{row['year']} - {row['set_num']}{owned} ({row['fig_num']})"
|
||||||
if str(image_path) in missing:
|
if str(image_path) in missing:
|
||||||
image = build_placeholder(image_height)
|
image = build_placeholder(image_height)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -48,17 +48,20 @@ def build_character_sets(
|
|||||||
"set_id": set_row["set_id"],
|
"set_id": set_row["set_id"],
|
||||||
"year": set_row["year"],
|
"year": set_row["year"],
|
||||||
"fig_num": fig_num,
|
"fig_num": fig_num,
|
||||||
|
"in_collection": set_row["in_collection"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
character_sets.sort(key=lambda row: (row["known_character"], int(row["year"]), row["set_num"], row["fig_num"]))
|
character_sets.sort(
|
||||||
|
key=lambda row: (row["known_character"], int(row["year"]), row["set_num"], row["fig_num"])
|
||||||
|
)
|
||||||
return character_sets
|
return character_sets
|
||||||
|
|
||||||
|
|
||||||
def write_character_sets(destination_path: Path, rows: Sequence[dict]) -> None:
|
def write_character_sets(destination_path: Path, rows: Sequence[dict]) -> None:
|
||||||
"""Écrit le CSV listant les sets par personnage."""
|
"""Écrit le CSV listant les sets par personnage."""
|
||||||
ensure_parent_dir(destination_path)
|
ensure_parent_dir(destination_path)
|
||||||
fieldnames = ["known_character", "set_num", "set_id", "year", "fig_num"]
|
fieldnames = ["known_character", "set_num", "set_id", "year", "fig_num", "in_collection"]
|
||||||
with destination_path.open("w", newline="") as csv_file:
|
with destination_path.open("w", newline="") as csv_file:
|
||||||
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
|
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
|
||||||
writer.writeheader()
|
writer.writeheader()
|
||||||
|
|||||||
@@ -26,19 +26,47 @@ def test_build_character_sets_and_collages(tmp_path: Path) -> None:
|
|||||||
{"set_num": "1003-1", "known_character": "Figurant", "fig_num": "fig-4"},
|
{"set_num": "1003-1", "known_character": "Figurant", "fig_num": "fig-4"},
|
||||||
]
|
]
|
||||||
sets_lookup = {
|
sets_lookup = {
|
||||||
"1000-1": {"set_id": "1000", "year": "2020"},
|
"1000-1": {"set_id": "1000", "year": "2020", "in_collection": "true"},
|
||||||
"1001-1": {"set_id": "1001", "year": "2021"},
|
"1001-1": {"set_id": "1001", "year": "2021", "in_collection": "false"},
|
||||||
"1002-1": {"set_id": "1002", "year": "2022"},
|
"1002-1": {"set_id": "1002", "year": "2022", "in_collection": "false"},
|
||||||
"1003-1": {"set_id": "1003", "year": "2023"},
|
"1003-1": {"set_id": "1003", "year": "2023", "in_collection": "false"},
|
||||||
"1004-1": {"set_id": "1004", "year": "2024"},
|
"1004-1": {"set_id": "1004", "year": "2024", "in_collection": "true"},
|
||||||
}
|
}
|
||||||
|
|
||||||
character_sets = build_character_sets(minifigs_rows, sets_lookup, excluded_characters=["Figurant"])
|
character_sets = build_character_sets(minifigs_rows, sets_lookup, excluded_characters=["Figurant"])
|
||||||
assert character_sets == [
|
assert character_sets == [
|
||||||
{"known_character": "Alice", "set_num": "1000-1", "set_id": "1000", "year": "2020", "fig_num": "fig-1"},
|
{
|
||||||
{"known_character": "Bob", "set_num": "1001-1", "set_id": "1001", "year": "2021", "fig_num": "fig-2"},
|
"known_character": "Alice",
|
||||||
{"known_character": "Bob", "set_num": "1002-1", "set_id": "1002", "year": "2022", "fig_num": "fig-3"},
|
"set_num": "1000-1",
|
||||||
{"known_character": "Claire Dearing", "set_num": "1004-1", "set_id": "1004", "year": "2024", "fig_num": "fig-5"},
|
"set_id": "1000",
|
||||||
|
"year": "2020",
|
||||||
|
"fig_num": "fig-1",
|
||||||
|
"in_collection": "true",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"known_character": "Bob",
|
||||||
|
"set_num": "1001-1",
|
||||||
|
"set_id": "1001",
|
||||||
|
"year": "2021",
|
||||||
|
"fig_num": "fig-2",
|
||||||
|
"in_collection": "false",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"known_character": "Bob",
|
||||||
|
"set_num": "1002-1",
|
||||||
|
"set_id": "1002",
|
||||||
|
"year": "2022",
|
||||||
|
"fig_num": "fig-3",
|
||||||
|
"in_collection": "false",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"known_character": "Claire Dearing",
|
||||||
|
"set_num": "1004-1",
|
||||||
|
"set_id": "1004",
|
||||||
|
"year": "2024",
|
||||||
|
"fig_num": "fig-5",
|
||||||
|
"in_collection": "true",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
resources_dir = tmp_path / "resources"
|
resources_dir = tmp_path / "resources"
|
||||||
|
|||||||