199 lines
6.6 KiB
Python
199 lines
6.6 KiB
Python
"""Tests du calcul de rareté des pièces."""
|
|
|
|
import csv
|
|
from pathlib import Path
|
|
|
|
from lib.rebrickable.part_rarity import build_part_rarity, select_until_reused, write_part_rarity
|
|
|
|
|
|
def write_csv(path: Path, headers: list[str], rows: list[list[str]]) -> None:
|
|
"""Écrit un CSV utilitaire pour les cas de test."""
|
|
with path.open("w", newline="") as csv_file:
|
|
writer = csv.writer(csv_file)
|
|
writer.writerow(headers)
|
|
writer.writerows(rows)
|
|
|
|
|
|
def test_build_part_rarity_counts_spares_and_ignores_categories(tmp_path: Path) -> None:
|
|
"""Comptabilise les pièces (rechanges incluses) et ignore les catégories animales."""
|
|
parts_filtered = tmp_path / "parts_filtered.csv"
|
|
write_csv(
|
|
parts_filtered,
|
|
[
|
|
"part_num",
|
|
"color_rgb",
|
|
"is_translucent",
|
|
"set_num",
|
|
"set_id",
|
|
"year",
|
|
"quantity_in_set",
|
|
"is_spare",
|
|
"is_minifig_part",
|
|
],
|
|
[
|
|
["p1", "AAAAAA", "false", "1000-1", "1000", "2020", "2", "false", "false"],
|
|
["p1", "AAAAAA", "false", "2000-1", "2000", "2021", "1", "true", "false"],
|
|
["p2", "BBBBBB", "false", "1000-1", "1000", "2020", "5", "false", "false"],
|
|
["p4", "CCCCCC", "false", "2000-1", "2000", "2021", "2", "false", "false"],
|
|
["p6", "DDDDDD", "false", "2000-1", "2000", "2021", "1", "false", "false"],
|
|
],
|
|
)
|
|
sets_enriched = tmp_path / "sets_enriched.csv"
|
|
write_csv(
|
|
sets_enriched,
|
|
["set_num", "name", "year", "theme_id", "num_parts", "img_url", "set_id", "rebrickable_url", "in_collection"],
|
|
[
|
|
["1000-1", "Set A", "2020", "1", "10", "http://example.com", "1000", "http://example.com", "false"],
|
|
["2000-1", "Set B", "2021", "1", "10", "http://example.com", "2000", "http://example.com", "false"],
|
|
],
|
|
)
|
|
parts_catalog = tmp_path / "parts.csv"
|
|
write_csv(
|
|
parts_catalog,
|
|
["part_num", "name", "part_cat_id", "part_material"],
|
|
[
|
|
["p1", "Brick 1x1", "1", "Plastic"],
|
|
["p2", "Baby Dino", "28", "Plastic"],
|
|
["p3", "Raptor Body", "75", "Plastic"],
|
|
["p4", "Figure Limb", "41", "Plastic"],
|
|
["p5", "Sticker Sheet", "58", "Plastic"],
|
|
["p6", "Exclusive Tile", "1", "Plastic"],
|
|
["p7", "Slope 45 print", "1", "Plastic"],
|
|
],
|
|
)
|
|
part_categories = tmp_path / "part_categories.csv"
|
|
write_csv(
|
|
part_categories,
|
|
["id", "name"],
|
|
[
|
|
["1", "Bricks"],
|
|
["28", "Animals / Creatures"],
|
|
["41", "Large Buildable Figures"],
|
|
["75", "Animal / Creature Body Parts"],
|
|
["58", "Stickers"],
|
|
],
|
|
)
|
|
inventories = tmp_path / "inventories.csv"
|
|
write_csv(
|
|
inventories,
|
|
["id", "version", "set_num"],
|
|
[
|
|
["1", "1", "3000-1"],
|
|
["2", "2", "3000-1"],
|
|
["3", "1", "4000-1"],
|
|
["4", "1", "1000-1"],
|
|
["5", "1", "5000-1"],
|
|
],
|
|
)
|
|
inventory_parts = tmp_path / "inventory_parts.csv"
|
|
write_csv(
|
|
inventory_parts,
|
|
["inventory_id", "part_num", "color_id", "quantity", "is_spare", "img_url"],
|
|
[
|
|
["1", "p1", "1", "1", "False", ""],
|
|
["2", "p1", "1", "3", "False", ""],
|
|
["2", "p2", "1", "2", "False", ""],
|
|
["3", "p4", "1", "4", "True", ""],
|
|
["4", "p1", "1", "8", "False", ""],
|
|
["5", "p5", "1", "9", "False", ""],
|
|
["5", "p7", "1", "5", "False", ""],
|
|
],
|
|
)
|
|
|
|
rows = build_part_rarity(
|
|
parts_filtered,
|
|
inventories,
|
|
inventory_parts,
|
|
parts_catalog,
|
|
part_categories,
|
|
sets_enriched,
|
|
)
|
|
|
|
assert rows == [
|
|
{
|
|
"part_num": "p6",
|
|
"part_name": "Exclusive Tile",
|
|
"part_cat_id": "1",
|
|
"part_category": "Bricks",
|
|
"sample_set_num": "2000-1",
|
|
"sample_set_id": "2000",
|
|
"sample_set_year": "2021",
|
|
"filtered_quantity": "1",
|
|
"filtered_set_count": "1",
|
|
"other_sets_quantity": "0",
|
|
"catalog_total_quantity": "1",
|
|
"filtered_share": "1.0000",
|
|
},
|
|
{
|
|
"part_num": "p1",
|
|
"part_name": "Brick 1x1",
|
|
"part_cat_id": "1",
|
|
"part_category": "Bricks",
|
|
"sample_set_num": "1000-1",
|
|
"sample_set_id": "1000",
|
|
"sample_set_year": "2020",
|
|
"filtered_quantity": "3",
|
|
"filtered_set_count": "2",
|
|
"other_sets_quantity": "3",
|
|
"catalog_total_quantity": "6",
|
|
"filtered_share": "0.5000",
|
|
},
|
|
{
|
|
"part_num": "p4",
|
|
"part_name": "Figure Limb",
|
|
"part_cat_id": "41",
|
|
"part_category": "Large Buildable Figures",
|
|
"sample_set_num": "2000-1",
|
|
"sample_set_id": "2000",
|
|
"sample_set_year": "2021",
|
|
"filtered_quantity": "2",
|
|
"filtered_set_count": "1",
|
|
"other_sets_quantity": "4",
|
|
"catalog_total_quantity": "6",
|
|
"filtered_share": "0.3333",
|
|
},
|
|
]
|
|
assert select_until_reused(rows) == [rows[0], rows[1]]
|
|
|
|
rows_no_print = build_part_rarity(
|
|
parts_filtered,
|
|
inventories,
|
|
inventory_parts,
|
|
parts_catalog,
|
|
part_categories,
|
|
sets_enriched,
|
|
exclude_printed=True,
|
|
)
|
|
assert all(r["part_num"] != "p7" for r in rows_no_print)
|
|
|
|
|
|
def test_write_part_rarity_outputs_csv(tmp_path: Path) -> None:
|
|
"""Sérialise le classement de rareté."""
|
|
destination = tmp_path / "part_rarity.csv"
|
|
rows = [
|
|
{
|
|
"part_num": "p1",
|
|
"part_name": "Brick 1x1",
|
|
"part_cat_id": "1",
|
|
"part_category": "Bricks",
|
|
"sample_set_num": "123-1",
|
|
"sample_set_id": "123",
|
|
"sample_set_year": "2020",
|
|
"filtered_quantity": "3",
|
|
"filtered_set_count": "2",
|
|
"other_sets_quantity": "3",
|
|
"catalog_total_quantity": "6",
|
|
"filtered_share": "0.5000",
|
|
}
|
|
]
|
|
|
|
write_part_rarity(destination, rows)
|
|
|
|
assert destination.exists()
|
|
content = destination.read_text().strip().splitlines()
|
|
assert content[0] == (
|
|
"part_num,part_name,part_cat_id,part_category,sample_set_num,sample_set_id,sample_set_year,filtered_quantity,filtered_set_count,"
|
|
"other_sets_quantity,catalog_total_quantity,filtered_share"
|
|
)
|
|
assert content[1] == "p1,Brick 1x1,1,Bricks,123-1,123,2020,3,2,3,6,0.5000"
|