"""Graphique des pièces rares par set.""" from pathlib import Path from typing import List, Tuple import matplotlib.pyplot as plt from matplotlib.patches import Patch from lib.filesystem import ensure_parent_dir from lib.rebrickable.stats import read_rows def load_top_sets(path: Path, limit: int = 15) -> List[dict]: """Charge les sets triés par nombre de pièces rares et limite le top.""" rows = read_rows(path) sorted_rows = sorted( rows, key=lambda row: ( -int(row["rare_parts_distinct"]), -int(row["rare_parts_quantity"]), row["set_num"], ), ) return sorted_rows[:limit] def split_counts(rows: List[dict]) -> Tuple[List[int], List[int]]: """Sépare les comptages minifig vs hors minifig.""" non_minifig: List[int] = [] minifig: List[int] = [] for row in rows: total = int(row["rare_parts_distinct"]) minifig_count = int(row["rare_minifig_parts_distinct"]) non_minifig.append(total - minifig_count) minifig.append(minifig_count) return non_minifig, minifig def plot_rare_parts_per_set(rare_by_set_path: Path, destination_path: Path) -> None: """Trace le top des sets contenant des pièces exclusives.""" rows = load_top_sets(rare_by_set_path) if not rows: return non_minifig, minifig = split_counts(rows) y_positions = list(range(len(rows))) labels = [f"{row['set_num']} · {row['name']} ({row['year']})" for row in rows] owned_mask = [row["in_collection"] == "true" for row in rows] base_color = "#1f77b4" accent_color = "#f28e2b" fig, ax = plt.subplots(figsize=(11, 8)) for y, value, is_owned in zip(y_positions, non_minifig, owned_mask): alpha = 0.92 if is_owned else 0.45 ax.barh(y, value, color=base_color, alpha=alpha, label=None) for y, value, offset, is_owned in zip(y_positions, minifig, non_minifig, owned_mask): alpha = 0.92 if is_owned else 0.45 ax.barh(y, value, left=offset, color=accent_color, alpha=alpha, label=None) ax.set_yticks(y_positions) ax.set_yticklabels(labels) ax.invert_yaxis() ax.set_xlabel("Variantes de pièces exclusives (hors rechanges)") ax.set_title("Pièces rares par set (top)") ax.grid(axis="x", linestyle="--", alpha=0.35) handles = [ Patch(facecolor=base_color, edgecolor="none", label="Pièces hors minifigs"), Patch(facecolor=accent_color, edgecolor="none", label="Pièces de minifigs"), Patch(facecolor="#000000", edgecolor="none", alpha=0.92, label="Set possédé"), Patch(facecolor="#000000", edgecolor="none", alpha=0.45, label="Set manquant"), ] ax.legend(handles=handles, loc="lower right", frameon=False) ensure_parent_dir(destination_path) fig.tight_layout() fig.savefig(destination_path, dpi=170) plt.close(fig)