1
etude_lego_jurassic_world/tests/test_minifig_character_collages.py

146 lines
5.6 KiB
Python

"""Tests des frises chronologiques de minifigs par personnage."""
from pathlib import Path
from PIL import Image
from lib.plots.minifig_character_collages import build_character_collages, stack_collages
from lib.rebrickable.minifig_character_sets import build_character_sets, group_by_character
from lib.rebrickable.resources import sanitize_name
def create_image(path: Path, color: tuple[int, int, int], size: tuple[int, int]) -> None:
"""Crée une image unie pour les besoins de tests."""
path.parent.mkdir(parents=True, exist_ok=True)
Image.new("RGB", size, color).save(path)
def test_build_character_sets_and_collages(tmp_path: Path) -> None:
"""Construit les apparitions par personnage puis les frises associées."""
minifigs_rows = [
{"set_num": "1000-1", "known_character": "Alice", "fig_num": "fig-1", "part_num": "p1"},
{"set_num": "1000-1", "known_character": "Alice", "fig_num": "fig-1", "part_num": "p1"},
{"set_num": "1001-1", "known_character": "Bob", "fig_num": "fig-2", "part_num": "p2"},
{"set_num": "1002-1", "known_character": "Bob", "fig_num": "fig-3", "part_num": "p3"},
{"set_num": "1004-1", "known_character": "Claire Dearing", "fig_num": "fig-5", "part_num": "p5"},
{"set_num": "1003-1", "known_character": "Figurant", "fig_num": "fig-4", "part_num": "p4"},
]
sets_lookup = {
"1000-1": {"set_id": "1000", "year": "2020", "in_collection": "true"},
"1001-1": {"set_id": "1001", "year": "2021", "in_collection": "false"},
"1002-1": {"set_id": "1002", "year": "2022", "in_collection": "false"},
"1003-1": {"set_id": "1003", "year": "2023", "in_collection": "false"},
"1004-1": {"set_id": "1004", "year": "2024", "in_collection": "true"},
}
character_sets = build_character_sets(minifigs_rows, sets_lookup, excluded_characters=["Figurant"])
assert character_sets == [
{
"known_character": "Alice",
"set_num": "1000-1",
"set_id": "1000",
"year": "2020",
"fig_num": "fig-1",
"in_collection": "true",
"part_num": "p1",
},
{
"known_character": "Bob",
"set_num": "1001-1",
"set_id": "1001",
"year": "2021",
"fig_num": "fig-2",
"in_collection": "false",
"part_num": "p2",
},
{
"known_character": "Bob",
"set_num": "1002-1",
"set_id": "1002",
"year": "2022",
"fig_num": "fig-3",
"in_collection": "false",
"part_num": "p3",
},
{
"known_character": "Claire Dearing",
"set_num": "1004-1",
"set_id": "1004",
"year": "2024",
"fig_num": "fig-5",
"in_collection": "true",
"part_num": "p5",
},
]
resources_dir = tmp_path / "resources"
for entry in character_sets:
image_path = resources_dir / entry["set_id"] / sanitize_name(entry["known_character"]) / "minifig.jpg"
if "Claire" in entry["known_character"]:
continue
create_image(image_path, (120, 80, 60), (10, 10))
destination_dir = tmp_path / "output"
missing_minifigs = {
str(resources_dir / "1004" / sanitize_name("Claire Dearing") / "minifig.jpg"),
}
generated = build_character_collages(
group_by_character(character_sets),
resources_dir,
destination_dir,
missing_paths=missing_minifigs,
image_filename="minifig.jpg",
image_height=20,
label_height=10,
spacing=2,
)
assert len(generated) == 3
alice = Image.open(destination_dir / f"{sanitize_name('Alice')}.png")
bob = Image.open(destination_dir / f"{sanitize_name('Bob')}.png")
claire = Image.open(destination_dir / f"{sanitize_name('Claire Dearing')}.png")
assert alice.size == (20, 30)
assert bob.size == (42, 30)
assert claire.size == (20, 30)
assert claire.getpixel((5, 10)) == (220, 220, 220)
# Variante sur les têtes : utilise un autre fichier et un autre manque.
heads_destination = tmp_path / "output_heads"
head_missing = {
str(resources_dir / "1001" / sanitize_name("Bob") / "head.jpg"),
str(resources_dir / "1002" / sanitize_name("Bob") / "head.jpg"),
str(resources_dir / "1004" / sanitize_name("Claire Dearing") / "head.jpg"),
}
head_image = resources_dir / "1000" / sanitize_name("Alice") / "head.jpg"
create_image(head_image, (10, 120, 60), (8, 12))
head_collages = build_character_collages(
{"Bob": group_by_character(character_sets)["Bob"]},
resources_dir,
heads_destination,
missing_paths=head_missing,
image_filename="head.jpg",
image_height=16,
label_height=8,
spacing=2,
)
assert len(head_collages) == 1
bob_head = Image.open(heads_destination / f"{sanitize_name('Bob')}.png")
assert bob_head.getpixel((3, 8)) == (220, 220, 220)
def test_stack_collages_with_filter(tmp_path: Path) -> None:
"""Assemble une grille verticale en filtrant les frises autorisées."""
source_dir = tmp_path / "collages"
source_dir.mkdir()
img1 = source_dir / "Alice.png"
img2 = source_dir / "Bob.png"
Image.new("RGB", (10, 6), (100, 100, 100)).save(img1)
Image.new("RGB", (14, 8), (120, 120, 120)).save(img2)
destination = tmp_path / "stacked.png"
stack_collages(source_dir, destination, spacing=2, allowed_stems={"Bob"})
result = Image.open(destination)
assert result.size == (14, 8)
assert result.getpixel((0, 0)) == (120, 120, 120)