425 lines
14 KiB
Python
425 lines
14 KiB
Python
"""Tests de l'agrégation des minifigs par personnage."""
|
|
|
|
from pathlib import Path
|
|
|
|
from lib.rebrickable.minifig_characters import (
|
|
aggregate_by_character,
|
|
aggregate_by_gender,
|
|
aggregate_characters_by_gender,
|
|
aggregate_new_character_sets,
|
|
aggregate_new_characters_by_year,
|
|
aggregate_variations_and_totals,
|
|
aggregate_character_spans,
|
|
aggregate_presence_by_year,
|
|
load_sets_enriched,
|
|
write_character_counts,
|
|
write_character_gender_counts,
|
|
write_new_character_sets_csv,
|
|
write_new_character_sets_markdown,
|
|
write_new_characters_by_year,
|
|
write_character_variations_totals,
|
|
write_gender_counts,
|
|
)
|
|
from lib.rebrickable.minifig_character_sets import load_sets
|
|
|
|
|
|
def test_aggregate_by_character_counts_unique_figs() -> None:
|
|
"""Compter les minifigs distinctes par personnage en excluant les noms vides."""
|
|
aggregates = aggregate_by_character(
|
|
[
|
|
{
|
|
"set_num": "123-1",
|
|
"part_num": "head-a",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-1",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "124-1",
|
|
"part_num": "head-b",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-1",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "125-1",
|
|
"part_num": "head-c",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-2",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "126-1",
|
|
"part_num": "head-d",
|
|
"known_character": "Figurant",
|
|
"fig_num": "fig-guard-1",
|
|
"gender": "unknown",
|
|
},
|
|
{
|
|
"set_num": "128-1",
|
|
"part_num": "head-f",
|
|
"known_character": "Figurant",
|
|
"fig_num": "fig-guard-1",
|
|
"gender": "unknown",
|
|
},
|
|
{
|
|
"set_num": "129-1",
|
|
"part_num": "head-g",
|
|
"known_character": "",
|
|
"fig_num": "fig-guard-2",
|
|
"gender": "unknown",
|
|
},
|
|
]
|
|
)
|
|
|
|
assert aggregates == [
|
|
{"known_character": "Owen Grady", "gender": "male", "minifig_count": 2},
|
|
{"known_character": "Figurant", "gender": "unknown", "minifig_count": 1},
|
|
]
|
|
|
|
|
|
def test_aggregate_variations_and_totals_excludes_figurants() -> None:
|
|
"""Compter le total et les variations en excluant les figurants."""
|
|
aggregates = aggregate_variations_and_totals(
|
|
[
|
|
{
|
|
"set_num": "123-1",
|
|
"part_num": "head-a",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-1",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "124-1",
|
|
"part_num": "head-b",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-1",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "125-1",
|
|
"part_num": "head-c",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen-2",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "126-1",
|
|
"part_num": "head-d",
|
|
"known_character": "Ellie Sattler",
|
|
"fig_num": "fig-ellie-1",
|
|
"gender": "female",
|
|
},
|
|
{
|
|
"set_num": "127-1",
|
|
"part_num": "head-e",
|
|
"known_character": "Figurant",
|
|
"fig_num": "fig-guard-1",
|
|
"gender": "unknown",
|
|
},
|
|
],
|
|
excluded_characters=["Figurant"],
|
|
)
|
|
|
|
assert aggregates == [
|
|
{"known_character": "Owen Grady", "gender": "male", "variation_count": 2, "total_minifigs": 3},
|
|
{"known_character": "Ellie Sattler", "gender": "female", "variation_count": 1, "total_minifigs": 1},
|
|
]
|
|
|
|
|
|
def test_aggregate_by_gender_counts_unique_figs() -> None:
|
|
"""Compter les minifigs distinctes par genre."""
|
|
aggregates = aggregate_by_gender(
|
|
[
|
|
{"fig_num": "fig-a", "gender": "male"},
|
|
{"fig_num": "fig-a", "gender": "male"},
|
|
{"fig_num": "fig-b", "gender": "female"},
|
|
{"fig_num": "fig-c", "gender": ""},
|
|
]
|
|
)
|
|
|
|
assert aggregates == [
|
|
{"gender": "female", "minifig_count": "1"},
|
|
{"gender": "male", "minifig_count": "1"},
|
|
{"gender": "unknown", "minifig_count": "1"},
|
|
]
|
|
|
|
|
|
def test_aggregate_characters_by_gender_unique_characters() -> None:
|
|
"""Compter les personnages distincts par genre (ignorer unknown)."""
|
|
aggregates = aggregate_characters_by_gender(
|
|
[
|
|
{"known_character": "A", "gender": "male"},
|
|
{"known_character": "A", "gender": "male"},
|
|
{"known_character": "B", "gender": "female"},
|
|
{"known_character": "C", "gender": "unknown"},
|
|
]
|
|
)
|
|
|
|
assert aggregates == [
|
|
{"gender": "female", "character_count": "1"},
|
|
{"gender": "male", "character_count": "1"},
|
|
]
|
|
|
|
|
|
def test_aggregate_new_characters_by_year_limits_range(tmp_path: Path) -> None:
|
|
"""Compter les nouveaux personnages par année en respectant la plage."""
|
|
sets_path = tmp_path / "sets_enriched.csv"
|
|
sets_path.write_text(
|
|
"set_num,name,year,theme_id,num_parts,img_url,set_id,rebrickable_url,in_collection\n"
|
|
"123-1,Set A,2015,0,0,,-,http://r/123-1,true\n"
|
|
"124-1,Set B,2016,0,0,,-,http://r/124-1,true\n"
|
|
"125-1,Set C,2017,0,0,,-,http://r/125-1,true\n"
|
|
"126-1,Set D,2014,0,0,,-,http://r/126-1,true\n"
|
|
)
|
|
sets_years = load_sets_enriched(sets_path)
|
|
minifigs_rows = [
|
|
{"set_num": "123-1", "known_character": "Owen Grady", "fig_num": "fig-owen-1", "part_num": "head-a"},
|
|
{"set_num": "124-1", "known_character": "Owen Grady", "fig_num": "fig-owen-2", "part_num": "head-b"},
|
|
{"set_num": "125-1", "known_character": "Ellie Sattler", "fig_num": "fig-ellie-1", "part_num": "head-c"},
|
|
{"set_num": "126-1", "known_character": "Alan Grant", "fig_num": "fig-grant-1", "part_num": "head-d"},
|
|
]
|
|
|
|
counts = aggregate_new_characters_by_year(
|
|
minifigs_rows,
|
|
sets_years,
|
|
excluded_characters=["Figurant"],
|
|
start_year=2015,
|
|
end_year=2017,
|
|
)
|
|
|
|
assert counts == [
|
|
{"year": "2015", "new_characters": "1"},
|
|
{"year": "2016", "new_characters": "0"},
|
|
{"year": "2017", "new_characters": "1"},
|
|
]
|
|
|
|
|
|
def test_aggregate_new_character_sets_returns_intro_sets(tmp_path: Path) -> None:
|
|
"""Lister les personnages introduits avec les sets de l'année d'introduction."""
|
|
sets_path = tmp_path / "sets_enriched.csv"
|
|
sets_path.write_text(
|
|
"set_num,name,year,theme_id,num_parts,img_url,set_id,rebrickable_url,in_collection\n"
|
|
"123-1,Set A,2015,0,0,,123,http://r/123-1,true\n"
|
|
"124-1,Set B,2015,0,0,,124,http://r/124-1,true\n"
|
|
"125-1,Set C,2016,0,0,,125,http://r/125-1,true\n"
|
|
)
|
|
sets_lookup = load_sets(sets_path)
|
|
minifigs_rows = [
|
|
{"set_num": "123-1", "known_character": "Owen Grady", "fig_num": "fig-owen-1", "part_num": "head-a"},
|
|
{"set_num": "124-1", "known_character": "Owen Grady", "fig_num": "fig-owen-2", "part_num": "head-b"},
|
|
{"set_num": "125-1", "known_character": "Owen Grady", "fig_num": "fig-owen-3", "part_num": "head-c"},
|
|
{"set_num": "125-1", "known_character": "Ellie Sattler", "fig_num": "fig-ellie-1", "part_num": "head-d"},
|
|
]
|
|
|
|
rows = aggregate_new_character_sets(
|
|
minifigs_rows,
|
|
sets_lookup,
|
|
excluded_characters=["Figurant"],
|
|
start_year=2015,
|
|
end_year=2016,
|
|
)
|
|
|
|
assert rows == [
|
|
{
|
|
"year": "2015",
|
|
"known_character": "Owen Grady",
|
|
"set_num": "123-1",
|
|
"set_id": "123",
|
|
"set_name": "Set A",
|
|
"rebrickable_url": "http://r/123-1",
|
|
},
|
|
{
|
|
"year": "2015",
|
|
"known_character": "Owen Grady",
|
|
"set_num": "124-1",
|
|
"set_id": "124",
|
|
"set_name": "Set B",
|
|
"rebrickable_url": "http://r/124-1",
|
|
},
|
|
{
|
|
"year": "2016",
|
|
"known_character": "Ellie Sattler",
|
|
"set_num": "125-1",
|
|
"set_id": "125",
|
|
"set_name": "Set C",
|
|
"rebrickable_url": "http://r/125-1",
|
|
},
|
|
]
|
|
|
|
|
|
def test_write_character_counts_outputs_csv(tmp_path: Path) -> None:
|
|
"""Écrit le CSV des comptes par personnage."""
|
|
destination = tmp_path / "counts.csv"
|
|
rows = [
|
|
{"known_character": "A", "gender": "male", "minifig_count": 2},
|
|
{"known_character": "B", "gender": "female", "minifig_count": 1},
|
|
]
|
|
|
|
write_character_counts(destination, rows)
|
|
|
|
assert destination.read_text() == "known_character,gender,minifig_count\nA,male,2\nB,female,1\n"
|
|
|
|
|
|
def test_write_gender_counts_outputs_csv(tmp_path: Path) -> None:
|
|
"""Écrit le CSV des comptes par genre."""
|
|
destination = tmp_path / "gender_counts.csv"
|
|
rows = [
|
|
{"gender": "male", "minifig_count": "2"},
|
|
{"gender": "female", "minifig_count": "1"},
|
|
]
|
|
|
|
write_gender_counts(destination, rows)
|
|
|
|
assert destination.read_text() == "gender,minifig_count\nmale,2\nfemale,1\n"
|
|
|
|
|
|
def test_write_character_variations_totals_outputs_csv(tmp_path: Path) -> None:
|
|
"""Écrit le CSV comparatif variations/total."""
|
|
destination = tmp_path / "variations.csv"
|
|
rows = [
|
|
{"known_character": "A", "gender": "male", "variation_count": 2, "total_minifigs": 3},
|
|
{"known_character": "B", "gender": "female", "variation_count": 1, "total_minifigs": 1},
|
|
]
|
|
|
|
write_character_variations_totals(destination, rows)
|
|
|
|
assert destination.read_text() == "known_character,gender,variation_count,total_minifigs\nA,male,2,3\nB,female,1,1\n"
|
|
|
|
|
|
def test_write_character_gender_counts_outputs_csv(tmp_path: Path) -> None:
|
|
"""Écrit le CSV des comptes de personnages par genre."""
|
|
destination = tmp_path / "character_gender.csv"
|
|
rows = [
|
|
{"gender": "female", "character_count": "2"},
|
|
{"gender": "male", "character_count": "3"},
|
|
]
|
|
|
|
write_character_gender_counts(destination, rows)
|
|
|
|
assert destination.read_text() == "gender,character_count\nfemale,2\nmale,3\n"
|
|
|
|
|
|
def test_write_new_characters_by_year_outputs_csv(tmp_path: Path) -> None:
|
|
"""Écrit le CSV des nouveaux personnages par année."""
|
|
destination = tmp_path / "new_characters.csv"
|
|
rows = [
|
|
{"year": "2015", "new_characters": "3"},
|
|
{"year": "2016", "new_characters": "1"},
|
|
]
|
|
|
|
write_new_characters_by_year(destination, rows)
|
|
|
|
assert destination.read_text() == "year,new_characters\n2015,3\n2016,1\n"
|
|
|
|
|
|
def test_write_new_character_sets_markdown_outputs_md(tmp_path: Path) -> None:
|
|
"""Écrit le Markdown listant les nouveaux personnages et leurs sets."""
|
|
destination = tmp_path / "new_characters.md"
|
|
rows = [
|
|
{
|
|
"year": "2015",
|
|
"known_character": "Owen Grady",
|
|
"set_num": "123-1",
|
|
"set_id": "123",
|
|
"set_name": "Set A",
|
|
"rebrickable_url": "http://r/123-1",
|
|
},
|
|
{
|
|
"year": "2016",
|
|
"known_character": "Ellie Sattler",
|
|
"set_num": "125-1",
|
|
"set_id": "125",
|
|
"set_name": "Set C",
|
|
"rebrickable_url": "http://r/125-1",
|
|
},
|
|
]
|
|
|
|
write_new_character_sets_markdown(destination, rows)
|
|
|
|
assert destination.read_text() == (
|
|
"##### 2015\n\n"
|
|
"- Owen Grady\n"
|
|
" - [123](http://r/123-1) - Set A\n"
|
|
"\n"
|
|
"##### 2016\n\n"
|
|
"- Ellie Sattler\n"
|
|
" - [125](http://r/125-1) - Set C\n"
|
|
"\n"
|
|
)
|
|
|
|
|
|
def test_aggregate_presence_by_year_excludes_figurants(tmp_path: Path) -> None:
|
|
"""Calcule le total annuel en excluant les figurants."""
|
|
sets_path = tmp_path / "sets_enriched.csv"
|
|
sets_path.write_text(
|
|
"set_num,year\n"
|
|
"123-1,2020\n"
|
|
"124-1,2021\n"
|
|
)
|
|
minifigs_rows = [
|
|
{
|
|
"set_num": "123-1",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen",
|
|
"part_num": "head-a",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "124-1",
|
|
"known_character": "Figurant",
|
|
"fig_num": "fig-guard",
|
|
"part_num": "head-b",
|
|
"gender": "unknown",
|
|
},
|
|
]
|
|
sets_years = load_sets_enriched(sets_path)
|
|
|
|
presence = aggregate_presence_by_year(minifigs_rows, sets_years, excluded_characters=["Figurant"])
|
|
|
|
assert presence == [
|
|
{"known_character": "Owen Grady", "year": "2020", "minifig_count": "1"},
|
|
{"known_character": "Owen Grady", "year": "2021", "minifig_count": "0"},
|
|
]
|
|
|
|
|
|
def test_aggregate_character_spans_excludes_figurants(tmp_path: Path) -> None:
|
|
"""Calcule les bornes min/max par personnage."""
|
|
sets_path = tmp_path / "sets_enriched.csv"
|
|
sets_path.write_text(
|
|
"set_num,year\n"
|
|
"123-1,2020\n"
|
|
"124-1,2021\n"
|
|
"125-1,2022\n"
|
|
)
|
|
sets_years = load_sets_enriched(sets_path)
|
|
minifigs_rows = [
|
|
{
|
|
"set_num": "123-1",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen",
|
|
"part_num": "head-a",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "124-1",
|
|
"known_character": "Owen Grady",
|
|
"fig_num": "fig-owen",
|
|
"part_num": "head-a",
|
|
"gender": "male",
|
|
},
|
|
{
|
|
"set_num": "125-1",
|
|
"known_character": "Figurant",
|
|
"fig_num": "fig-guard",
|
|
"part_num": "head-b",
|
|
"gender": "unknown",
|
|
},
|
|
]
|
|
|
|
spans = aggregate_character_spans(minifigs_rows, sets_years, excluded_characters=["Figurant"])
|
|
|
|
assert spans == [
|
|
{"known_character": "Owen Grady", "start_year": "2020", "end_year": "2021", "total_minifigs": "2", "gender": "male"},
|
|
]
|