"""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)