diff --git a/README.md b/README.md index 49ca89d..321684e 100644 --- a/README.md +++ b/README.md @@ -272,17 +272,3 @@ Le script lit `data/intermediate/minifigs_by_set.csv`, agrège le nombre de mini Le script lit `data/intermediate/minifig_counts_by_set.csv`, `data/intermediate/sets_enriched.csv`, `data/raw/sets.csv`, `data/raw/inventories.csv` et `data/raw/inventory_minifigs.csv`, produit `data/intermediate/minifig_parts_correlation.csv` (pièces vs minifigs pour le catalogue global et les thèmes filtrés), puis trace `figures/step26/minifig_parts_correlation.png` en superposant les nuages de points et leurs tendances linéaires. Un second export `data/intermediate/minifigs_per_set_timeline.csv` est généré pour l'évolution annuelle du nombre moyen de minifigs par set, visualisé dans `figures/step26/minifigs_per_set_timeline.png` (courbes catalogue vs thèmes filtrés). - -### Étape 27 : palettes dominantes par set (hors minifigs) - -1. `source .venv/bin/activate` -2. `python -m scripts.plot_set_color_swatches` - -Le script lit `data/intermediate/colors_by_set.csv` (hors rechanges) et `data/intermediate/sets_enriched.csv`, sélectionne pour chaque set les 5 couleurs les plus présentes en excluant les pièces de minifigs (`quantity_non_minifig`), écrit `data/intermediate/set_color_swatches.csv`, puis trace `figures/step27/set_color_swatches.png` affichant chaque set avec ses 5 pastilles de couleurs dominantes. - -### Étape 28 : palettes perceptuelles par set (hors minifigs, pièces techniques exclues) - -1. `source .venv/bin/activate` -2. `python -m scripts.plot_set_color_swatches_perceptual` - -Le script lit `data/intermediate/colors_by_set.csv` (filtres appliqués : couleurs ignorées et pièces techniques/structurelles exclues), calcule pour chaque set les parts relatives de couleurs hors minifigs, sélectionne une palette diversifiée de 5 couleurs (priorité à la variété de teinte avant la luminosité), écrit `data/intermediate/set_color_swatches_perceptual.csv`, puis trace `figures/step28/set_color_swatches_perceptual.png` (pastilles dont la taille reflète la part relative). diff --git a/lib/plots/set_color_swatches.py b/lib/plots/set_color_swatches.py deleted file mode 100644 index 8a5c864..0000000 --- a/lib/plots/set_color_swatches.py +++ /dev/null @@ -1,89 +0,0 @@ -"""Palette dominante par set (hors minifigs).""" - -from collections import defaultdict -from pathlib import Path -from typing import Dict, List, Sequence - -import matplotlib.pyplot as plt - -from lib.filesystem import ensure_parent_dir -from lib.rebrickable.stats import read_rows - - -PLACEHOLDER_COLOR = "#e0e0e0" - - -def load_swatches(path: Path) -> List[dict]: - """Charge le CSV des couleurs dominantes par set.""" - return read_rows(path) - - -def group_swatches(rows: Sequence[dict], top_n: int = 5) -> List[dict]: - """Groupe les couleurs par set et complète avec des placeholders si besoin.""" - grouped: Dict[str, List[dict]] = defaultdict(list) - meta: Dict[str, dict] = {} - for row in rows: - grouped[row["set_num"]].append(row) - meta[row["set_num"]] = {"name": row["name"], "year": int(row["year"])} - result: List[dict] = [] - for set_num, colors in grouped.items(): - sorted_colors = sorted(colors, key=lambda r: int(r["rank"])) - while len(sorted_colors) < top_n: - sorted_colors.append( - { - "set_num": set_num, - "name": meta[set_num]["name"], - "year": str(meta[set_num]["year"]), - "rank": str(len(sorted_colors) + 1), - "color_rgb": "", - "color_name": "N/A", - "quantity_non_minifig": "0", - } - ) - result.append( - { - "set_num": set_num, - "name": meta[set_num]["name"], - "year": meta[set_num]["year"], - "colors": sorted_colors[:top_n], - } - ) - result.sort(key=lambda r: (r["year"], r["name"], r["set_num"])) - return result - - -def plot_set_color_swatches(swatches_path: Path, destination_path: Path) -> None: - """Trace la palette de 5 couleurs dominantes par set (hors minifigs).""" - rows = load_swatches(swatches_path) - if not rows: - return - grouped = group_swatches(rows, top_n=5) - set_labels = [f"{item['year']} – {item['name']}" for item in grouped] - y_positions = list(range(len(grouped))) - height = max(4, len(grouped) * 0.4) - - fig, ax = plt.subplots(figsize=(12, height)) - for y, item in zip(y_positions, grouped): - for idx, color in enumerate(item["colors"]): - rgb = color["color_rgb"].strip() - face_color = f"#{rgb}" if rgb else PLACEHOLDER_COLOR - ax.scatter( - idx, - y, - s=500, - color=face_color, - edgecolor="#0d0d0d", - linewidth=0.6, - ) - ax.set_yticks(y_positions) - ax.set_yticklabels(set_labels) - ax.set_xticks([]) - ax.invert_yaxis() - ax.set_xlim(-0.6, 4.6) - ax.set_title("Top 5 couleurs principales par set (hors minifigs)") - ax.grid(False) - - ensure_parent_dir(destination_path) - fig.tight_layout() - fig.savefig(destination_path, dpi=160) - plt.close(fig) diff --git a/lib/plots/set_color_swatches_perceptual.py b/lib/plots/set_color_swatches_perceptual.py deleted file mode 100644 index 26f0959..0000000 --- a/lib/plots/set_color_swatches_perceptual.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Visualisation des palettes perceptuelles (top 5) par set.""" - -from collections import defaultdict -from pathlib import Path -from typing import Dict, List, Sequence - -import matplotlib.pyplot as plt - -from lib.filesystem import ensure_parent_dir -from lib.rebrickable.stats import read_rows - - -PLACEHOLDER_COLOR = "#e0e0e0" - - -def load_swatches(path: Path) -> List[dict]: - """Charge le CSV des palettes perceptuelles.""" - return read_rows(path) - - -def group_swatches(rows: Sequence[dict], top_n: int = 5) -> List[dict]: - """Groupe les couleurs par set et complète avec placeholders si besoin.""" - grouped: Dict[str, List[dict]] = defaultdict(list) - meta: Dict[str, dict] = {} - for row in rows: - grouped[row["set_num"]].append(row) - meta[row["set_num"]] = {"name": row["name"], "year": int(row["year"])} - result: List[dict] = [] - for set_num, colors in grouped.items(): - sorted_colors = sorted(colors, key=lambda r: int(r["rank"])) - while len(sorted_colors) < top_n: - sorted_colors.append( - { - "set_num": set_num, - "name": meta[set_num]["name"], - "year": str(meta[set_num]["year"]), - "rank": str(len(sorted_colors) + 1), - "color_rgb": "", - "color_name": "N/A", - "share_non_minifig": "0", - "quantity_non_minifig": "0", - } - ) - result.append( - { - "set_num": set_num, - "name": meta[set_num]["name"], - "year": meta[set_num]["year"], - "colors": sorted_colors[:top_n], - } - ) - result.sort(key=lambda r: (r["year"], r["set_num"], r["name"])) - return result - - -def plot_set_color_swatches_perceptual(swatches_path: Path, destination_path: Path) -> None: - """Trace les 5 couleurs perceptuelles par set avec taille proportionnelle à la part.""" - rows = load_swatches(swatches_path) - if not rows: - return - grouped = group_swatches(rows, top_n=5) - set_labels = [f"{item['year']} – {item['name']}" for item in grouped] - y_positions = list(range(len(grouped))) - height = max(4, len(grouped) * 0.4) - - fig, ax = plt.subplots(figsize=(12, height)) - for y, item in zip(y_positions, grouped): - for idx, color in enumerate(item["colors"]): - rgb = color["color_rgb"].strip() - face_color = f"#{rgb}" if rgb else PLACEHOLDER_COLOR - share = float(color.get("share_non_minifig", "0")) - size = 450 + 900 * share - ax.scatter( - idx, - y, - s=size, - color=face_color, - edgecolor="#0d0d0d", - linewidth=0.6, - alpha=0.95, - ) - ax.set_yticks(y_positions) - ax.set_yticklabels(set_labels) - ax.set_xticks([]) - ax.invert_yaxis() - ax.set_xlim(-0.6, 4.6) - ax.set_title("Top 5 couleurs perceptuelles par set (hors minifigs, pièces techniques exclues)") - ax.grid(False) - - ensure_parent_dir(destination_path) - fig.tight_layout() - fig.savefig(destination_path, dpi=160) - plt.close(fig) diff --git a/lib/rebrickable/set_color_swatches.py b/lib/rebrickable/set_color_swatches.py deleted file mode 100644 index 3ebbf6d..0000000 --- a/lib/rebrickable/set_color_swatches.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Préparation des palettes dominantes par set (hors minifigs).""" - -import csv -from collections import defaultdict -from pathlib import Path -from typing import Dict, Iterable, List, Sequence - -import colorsys - -from lib.filesystem import ensure_parent_dir -from lib.rebrickable.color_ignores import is_ignored_color_rgb -from lib.rebrickable.stats import read_rows - - -def load_colors_by_set(path: Path) -> List[dict]: - """Charge colors_by_set.csv.""" - return read_rows(path) - - -def load_sets_enriched(path: Path) -> Dict[str, dict]: - """Indexe nom et année par set_num.""" - lookup: Dict[str, dict] = {} - with path.open() as csv_file: - reader = csv.DictReader(csv_file) - for row in reader: - lookup[row["set_num"]] = {"name": row["name"], "year": int(row["year"]), "set_id": row["set_id"]} - return lookup - - -def parse_rgb_hex(value: str) -> tuple[float, float, float]: - """Parse un code hexadécimal (RRGGBB) en composantes 0-1.""" - normalized = value.strip() - if len(normalized) != 6: - return (0.0, 0.0, 0.0) - r = int(normalized[0:2], 16) / 255.0 - g = int(normalized[2:4], 16) / 255.0 - b = int(normalized[4:6], 16) / 255.0 - return (r, g, b) - - -def hue_bucket(degrees: float) -> int: - """Regroupe les teintes en grandes familles pour l'affichage.""" - if degrees >= 330 or degrees < 30: - return 0 # rouge - if degrees < 90: - return 1 # jaune / orangé - if degrees < 150: - return 2 # vert - if degrees < 270: - return 3 # bleu - return 4 # violet - - -def color_display_key(row: dict) -> tuple[float, int, float, str]: - """Clé de tri visuelle : teinte regroupée d'abord, puis luminosité.""" - r, g, b = parse_rgb_hex(row["color_rgb"]) - h, _s, v = colorsys.rgb_to_hsv(r, g, b) - degrees = h * 360.0 - bucket = hue_bucket(degrees) - return (bucket, degrees, v, row["color_name"]) - - -def build_top_colors_by_set(rows: Iterable[dict], sets_lookup: Dict[str, dict], top_n: int = 5) -> List[dict]: - """Sélectionne les top couleurs hors minifigs pour chaque set.""" - colors_by_set: Dict[str, List[dict]] = defaultdict(list) - for row in rows: - quantity = int(row["quantity_non_minifig"]) - if quantity <= 0: - continue - if is_ignored_color_rgb(row["color_rgb"]): - continue - set_num = row["set_num"] - set_meta = sets_lookup.get(set_num) - if set_meta is None: - continue - colors_by_set[set_num].append( - { - "set_num": set_num, - "set_id": row["set_id"], - "year": set_meta["year"], - "name": set_meta["name"], - "color_rgb": row["color_rgb"], - "color_name": row["color_name"], - "quantity": quantity, - } - ) - results: List[dict] = [] - for set_num, color_rows in colors_by_set.items(): - sorted_rows = sorted(color_rows, key=lambda r: (-r["quantity"], r["color_name"])) - selected = sorted_rows[:top_n] - ordered = sorted(selected, key=color_display_key) - for rank, color_row in enumerate(ordered, start=1): - results.append( - { - "set_num": color_row["set_num"], - "set_id": color_row["set_id"], - "name": color_row["name"], - "year": str(color_row["year"]), - "rank": str(rank), - "color_rgb": color_row["color_rgb"], - "color_name": color_row["color_name"], - "quantity_non_minifig": str(color_row["quantity"]), - } - ) - results.sort(key=lambda r: (int(r["year"]), r["set_num"], r["name"], int(r["rank"]))) - return results - - -def write_top_colors(path: Path, rows: Sequence[dict]) -> None: - """Écrit le CSV des couleurs dominantes par set.""" - ensure_parent_dir(path) - fieldnames = [ - "set_num", - "set_id", - "name", - "year", - "rank", - "color_rgb", - "color_name", - "quantity_non_minifig", - ] - with path.open("w", newline="") as csv_file: - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writeheader() - for row in rows: - writer.writerow(row) diff --git a/lib/rebrickable/set_color_swatches_perceptual.py b/lib/rebrickable/set_color_swatches_perceptual.py deleted file mode 100644 index 9b9fe85..0000000 --- a/lib/rebrickable/set_color_swatches_perceptual.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Construction de palettes perceptuelles (top 5) par set hors minifigs.""" - -from pathlib import Path -from typing import Dict, Iterable, List, Sequence, Set - -from lib.rebrickable.set_color_swatches import color_display_key, load_sets_enriched, parse_rgb_hex, hue_bucket -from lib.rebrickable.stats import read_rows - - -def load_colors_by_set(path: Path) -> List[dict]: - """Charge colors_by_set.csv.""" - return read_rows(path) - - -def compute_shares(rows: Iterable[dict]) -> Dict[str, List[dict]]: - """Calcule les parts relatives de couleurs hors minifigs pour chaque set.""" - by_set: Dict[str, List[dict]] = {} - totals: Dict[str, int] = {} - for row in rows: - quantity = int(row["quantity_non_minifig"]) - if quantity <= 0: - continue - set_num = row["set_num"] - totals[set_num] = totals.get(set_num, 0) + quantity - current = by_set.get(set_num) - if current is None: - by_set[set_num] = [row] - else: - current.append(row) - shares: Dict[str, List[dict]] = {} - for set_num, color_rows in by_set.items(): - total = totals.get(set_num, 0) - if total == 0: - continue - shares[set_num] = [] - for row in color_rows: - share = int(row["quantity_non_minifig"]) / total - shares[set_num].append( - { - "set_num": row["set_num"], - "set_id": row["set_id"], - "name": row.get("name", ""), - "year": row["year"], - "color_rgb": row["color_rgb"], - "color_name": row["color_name"], - "quantity_non_minifig": row["quantity_non_minifig"], - "share_non_minifig": f"{share:.5f}", - } - ) - return shares - - -def select_diverse_palette(rows: List[dict], top_n: int) -> List[dict]: - """Sélectionne une palette diversifiée : priorité à la part et à la variété de teinte.""" - sorted_by_share = sorted(rows, key=lambda r: (-float(r["share_non_minifig"]), r["color_name"])) - selected: List[dict] = [] - buckets_used: Set[int] = set() - for row in sorted_by_share: - r, g, b = parse_rgb_hex(row["color_rgb"]) - h, _s, _v = __import__("colorsys").rgb_to_hsv(r, g, b) - bucket = hue_bucket(h * 360.0) - if bucket in buckets_used: - continue - selected.append(row) - buckets_used.add(bucket) - if len(selected) == top_n: - break - if len(selected) < top_n: - for row in sorted_by_share: - if row in selected: - continue - selected.append(row) - if len(selected) == top_n: - break - while len(selected) < top_n: - selected.append( - { - "set_num": rows[0]["set_num"] if rows else "", - "set_id": rows[0]["set_id"] if rows else "", - "name": rows[0]["name"] if rows else "", - "year": rows[0]["year"] if rows else "", - "color_rgb": "", - "color_name": "N/A", - "quantity_non_minifig": "0", - "share_non_minifig": "0", - } - ) - ordered = sorted(selected, key=color_display_key) - for rank, row in enumerate(ordered, start=1): - row["rank"] = str(rank) - return ordered[:top_n] - - -def build_perceptual_swatches(rows: Iterable[dict], sets_lookup: Dict[str, dict], top_n: int = 5) -> List[dict]: - """Construit les palettes perceptuelles (parts relatives + diversité de teinte).""" - shares = compute_shares(rows) - swatches: List[dict] = [] - for set_num, color_rows in shares.items(): - set_meta = sets_lookup.get(set_num) - if set_meta is None: - continue - selected = select_diverse_palette(color_rows, top_n) - for row in selected: - swatches.append( - { - "set_num": set_num, - "set_id": set_meta["set_id"], - "name": set_meta["name"], - "year": str(set_meta["year"]), - "rank": row["rank"], - "color_rgb": row["color_rgb"], - "color_name": row["color_name"], - "share_non_minifig": row["share_non_minifig"], - "quantity_non_minifig": row["quantity_non_minifig"], - } - ) - swatches.sort(key=lambda r: (int(r["year"]), r["set_num"], int(r["rank"]))) - return swatches - - -def write_perceptual_swatches(path: Path, rows: Sequence[dict]) -> None: - """Écrit le CSV des palettes perceptuelles.""" - from lib.filesystem import ensure_parent_dir - - ensure_parent_dir(path) - fieldnames = [ - "set_num", - "set_id", - "name", - "year", - "rank", - "color_rgb", - "color_name", - "share_non_minifig", - "quantity_non_minifig", - ] - with path.open("w", newline="") as csv_file: - import csv - - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writeheader() - for row in rows: - writer.writerow(row) diff --git a/scripts/plot_set_color_swatches.py b/scripts/plot_set_color_swatches.py deleted file mode 100644 index e0cbeb2..0000000 --- a/scripts/plot_set_color_swatches.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Trace la palette dominante de chaque set (hors minifigs).""" - -from pathlib import Path - -from lib.plots.set_color_swatches import plot_set_color_swatches -from lib.rebrickable.set_color_swatches import build_top_colors_by_set, load_colors_by_set, load_sets_enriched, write_top_colors - - -COLORS_BY_SET_PATH = Path("data/intermediate/colors_by_set.csv") -SETS_ENRICHED_PATH = Path("data/intermediate/sets_enriched.csv") -SWATCHES_PATH = Path("data/intermediate/set_color_swatches.csv") -DESTINATION_PATH = Path("figures/step27/set_color_swatches.png") - - -def main() -> None: - """Construit le CSV de top couleurs par set et trace le nuancier.""" - colors_rows = load_colors_by_set(COLORS_BY_SET_PATH) - sets_lookup = load_sets_enriched(SETS_ENRICHED_PATH) - swatches = build_top_colors_by_set(colors_rows, sets_lookup, top_n=5) - write_top_colors(SWATCHES_PATH, swatches) - plot_set_color_swatches(SWATCHES_PATH, DESTINATION_PATH) - - -if __name__ == "__main__": - main() diff --git a/scripts/plot_set_color_swatches_perceptual.py b/scripts/plot_set_color_swatches_perceptual.py deleted file mode 100644 index c1107c7..0000000 --- a/scripts/plot_set_color_swatches_perceptual.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Trace les palettes perceptuelles (top 5) par set hors minifigs.""" - -from pathlib import Path - -from lib.plots.set_color_swatches_perceptual import plot_set_color_swatches_perceptual -from lib.rebrickable.set_color_swatches_perceptual import ( - build_perceptual_swatches, - load_colors_by_set, - load_sets_enriched, - write_perceptual_swatches, -) - - -COLORS_BY_SET_PATH = Path("data/intermediate/colors_by_set.csv") -SETS_ENRICHED_PATH = Path("data/intermediate/sets_enriched.csv") -SWATCHES_PATH = Path("data/intermediate/set_color_swatches_perceptual.csv") -DESTINATION_PATH = Path("figures/step28/set_color_swatches_perceptual.png") - - -def main() -> None: - """Construit et trace les palettes perceptuelles par set.""" - colors_rows = load_colors_by_set(COLORS_BY_SET_PATH) - sets_lookup = load_sets_enriched(SETS_ENRICHED_PATH) - swatches = build_perceptual_swatches(colors_rows, sets_lookup, top_n=5) - write_perceptual_swatches(SWATCHES_PATH, swatches) - plot_set_color_swatches_perceptual(SWATCHES_PATH, DESTINATION_PATH) - - -if __name__ == "__main__": - main() diff --git a/tests/test_set_color_swatches.py b/tests/test_set_color_swatches.py deleted file mode 100644 index d5fabe6..0000000 --- a/tests/test_set_color_swatches.py +++ /dev/null @@ -1,155 +0,0 @@ -"""Tests de la préparation des palettes par set.""" - -from pathlib import Path - -from lib.rebrickable.set_color_swatches import build_top_colors_by_set - - -def write_csv(path: Path, content: str) -> None: - """Écrit un CSV brut.""" - path.write_text(content) - - -def test_build_top_colors_by_set_selects_top5_non_minifig(tmp_path: Path) -> None: - """Sélectionne les 5 couleurs dominantes en excluant les minifigs.""" - colors_path = tmp_path / "colors_by_set.csv" - write_csv( - colors_path, - "set_num,set_id,year,color_rgb,is_translucent,color_name,quantity_total,quantity_non_spare,quantity_minifig,quantity_non_minifig\n" - "123-1,123,2020,111111,false,Black,10,10,0,10\n" - "123-1,123,2020,222222,false,Red,5,5,0,5\n" - "123-1,123,2020,333333,false,Blue,3,3,0,3\n" - "123-1,123,2020,444444,false,Green,2,2,0,2\n" - "123-1,123,2020,555555,false,Yellow,1,1,0,1\n" - "123-1,123,2020,666666,false,Pink,1,1,0,1\n" - "123-1,123,2020,0033B2,false,Ignored Blue,50,50,0,50\n" - "124-1,124,2021,aaaaaa,false,Gray,4,4,4,0\n", - ) - sets_path = tmp_path / "sets_enriched.csv" - write_csv( - sets_path, - "set_num,name,year,theme_id,num_parts,img_url,set_id,rebrickable_url,in_collection\n" - "123-1,Set A,2020,1,100,,123,,false\n" - "124-1,Set B,2021,1,50,,124,,false\n", - ) - rows = build_top_colors_by_set( - [ - row - for row in [ - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "111111", - "color_name": "Black", - "quantity_non_minifig": "10", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "222222", - "color_name": "Red", - "quantity_non_minifig": "5", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "333333", - "color_name": "Blue", - "quantity_non_minifig": "3", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "444444", - "color_name": "Green", - "quantity_non_minifig": "2", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "555555", - "color_name": "Yellow", - "quantity_non_minifig": "1", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "666666", - "color_name": "Pink", - "quantity_non_minifig": "1", - }, - { - "set_num": "124-1", - "set_id": "124", - "year": "2021", - "color_rgb": "aaaaaa", - "color_name": "Gray", - "quantity_non_minifig": "0", - }, - ] - ], - { - "123-1": {"name": "Set A", "year": 2020, "set_id": "123"}, - "124-1": {"name": "Set B", "year": 2021, "set_id": "124"}, - }, - top_n=5, - ) - - assert rows == [ - { - "set_num": "123-1", - "set_id": "123", - "name": "Set A", - "year": "2020", - "rank": "1", - "color_rgb": "111111", - "color_name": "Black", - "quantity_non_minifig": "10", - }, - { - "set_num": "123-1", - "set_id": "123", - "name": "Set A", - "year": "2020", - "rank": "2", - "color_rgb": "222222", - "color_name": "Red", - "quantity_non_minifig": "5", - }, - { - "set_num": "123-1", - "set_id": "123", - "name": "Set A", - "year": "2020", - "rank": "3", - "color_rgb": "333333", - "color_name": "Blue", - "quantity_non_minifig": "3", - }, - { - "set_num": "123-1", - "set_id": "123", - "name": "Set A", - "year": "2020", - "rank": "4", - "color_rgb": "444444", - "color_name": "Green", - "quantity_non_minifig": "2", - }, - { - "set_num": "123-1", - "set_id": "123", - "name": "Set A", - "year": "2020", - "rank": "5", - "color_rgb": "666666", - "color_name": "Pink", - "quantity_non_minifig": "1", - }, - ] diff --git a/tests/test_set_color_swatches_perceptual.py b/tests/test_set_color_swatches_perceptual.py deleted file mode 100644 index e661494..0000000 --- a/tests/test_set_color_swatches_perceptual.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Tests des palettes perceptuelles par set.""" - -from pathlib import Path - -from lib.rebrickable.set_color_swatches_perceptual import build_perceptual_swatches - - -def write_csv(path: Path, content: str) -> None: - """Écrit un CSV brut.""" - path.write_text(content) - - -def test_build_perceptual_swatches_diversifies_buckets(tmp_path: Path) -> None: - """Sélectionne des couleurs variées par teinte en priorité.""" - colors_path = tmp_path / "colors_by_set.csv" - write_csv( - colors_path, - "set_num,set_id,year,color_rgb,is_translucent,color_name,quantity_total,quantity_non_spare,quantity_minifig,quantity_non_minifig\n" - "123-1,123,2020,FF0000,false,Red,10,10,0,10\n" - "123-1,123,2020,00FF00,false,Green,8,8,0,8\n" - "123-1,123,2020,0000FF,false,Blue,6,6,0,6\n" - "123-1,123,2020,FFFF00,false,Yellow,5,5,0,5\n" - "123-1,123,2020,FF00FF,false,Magenta,4,4,0,4\n" - "123-1,123,2020,00FFFF,false,Cyan,3,3,0,3\n", - ) - sets_lookup = {"123-1": {"name": "Set A", "year": 2020, "set_id": "123"}} - rows = build_perceptual_swatches( - [ - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "FF0000", - "color_name": "Red", - "quantity_non_minifig": "10", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "00FF00", - "color_name": "Green", - "quantity_non_minifig": "8", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "0000FF", - "color_name": "Blue", - "quantity_non_minifig": "6", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "FFFF00", - "color_name": "Yellow", - "quantity_non_minifig": "5", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "FF00FF", - "color_name": "Magenta", - "quantity_non_minifig": "4", - }, - { - "set_num": "123-1", - "set_id": "123", - "year": "2020", - "color_rgb": "00FFFF", - "color_name": "Cyan", - "quantity_non_minifig": "3", - }, - ], - sets_lookup, - top_n=5, - ) - - ranks = [row["rank"] for row in rows if row["set_num"] == "123-1"] - assert ranks == ["1", "2", "3", "4", "5"] - assert len({row["color_name"] for row in rows}) == 5 diff --git a/tests/test_set_color_swatches_perceptual_plot.py b/tests/test_set_color_swatches_perceptual_plot.py deleted file mode 100644 index a3dc90d..0000000 --- a/tests/test_set_color_swatches_perceptual_plot.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Tests du graphique de palettes perceptuelles par set.""" - -import matplotlib -from pathlib import Path - -from lib.plots.set_color_swatches_perceptual import plot_set_color_swatches_perceptual - - -matplotlib.use("Agg") - - -def test_plot_set_color_swatches_perceptual(tmp_path: Path) -> None: - """Génère le graphique perceptuel.""" - swatches_path = tmp_path / "set_color_swatches_perceptual.csv" - destination = tmp_path / "figures" / "step28" / "set_color_swatches_perceptual.png" - swatches_path.write_text( - "set_num,set_id,name,year,rank,color_rgb,color_name,share_non_minifig,quantity_non_minifig\n" - "123-1,123,Set A,2020,1,FF0000,Red,0.40000,10\n" - "123-1,123,Set A,2020,2,00FF00,Green,0.30000,8\n" - "123-1,123,Set A,2020,3,0000FF,Blue,0.20000,6\n" - "123-1,123,Set A,2020,4,FFFF00,Yellow,0.10000,5\n" - "123-1,123,Set A,2020,5,00FFFF,Cyan,0.05000,3\n" - ) - - plot_set_color_swatches_perceptual(swatches_path, destination) - - assert destination.exists() - assert destination.stat().st_size > 0 diff --git a/tests/test_set_color_swatches_plot.py b/tests/test_set_color_swatches_plot.py deleted file mode 100644 index b5da81a..0000000 --- a/tests/test_set_color_swatches_plot.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Tests du graphique de palettes dominantes par set.""" - -import matplotlib -from pathlib import Path - -from lib.plots.set_color_swatches import plot_set_color_swatches - - -matplotlib.use("Agg") - - -def test_plot_set_color_swatches(tmp_path: Path) -> None: - """Génère le nuancier top 5 par set.""" - swatches_path = tmp_path / "set_color_swatches.csv" - destination = tmp_path / "figures" / "step27" / "set_color_swatches.png" - swatches_path.write_text( - "set_num,set_id,name,year,rank,color_rgb,color_name,quantity_non_minifig\n" - "123-1,123,Set A,2020,1,111111,Black,10\n" - "123-1,123,Set A,2020,2,222222,Red,5\n" - "123-1,123,Set A,2020,3,333333,Blue,3\n" - "123-1,123,Set A,2020,4,444444,Green,2\n" - "123-1,123,Set A,2020,5,555555,Yellow,1\n" - "124-1,124,Set B,2021,1,aaaaaa,Gray,4\n" - ) - - plot_set_color_swatches(swatches_path, destination) - - assert destination.exists() - assert destination.stat().st_size > 0