Code source de mangetamain_analytics.utils.color_theme

"""Classe ColorTheme - Charte graphique Back to the Kitchen.

Ce module fournit une classe centralisée pour gérer toutes les couleurs
de l'application Mangetamain Analytics avec une approche POO.
"""


[docs] class ColorTheme: """Thème de couleurs 'Back to the Kitchen' avec accesseurs typés. Cette classe encapsule toutes les couleurs de la charte graphique dans une interface POO avec méthodes utilitaires. Examples: >>> # Accès direct aux couleurs >>> bar_color = ColorTheme.ORANGE_PRIMARY >>> text_color = ColorTheme.TEXT_PRIMARY >>> # Conversion en RGBA >>> rgba = ColorTheme.to_rgba(ColorTheme.ORANGE_PRIMARY, 0.5) >>> # 'rgba(255, 140, 0, 0.5)' >>> # Récupération thème Plotly >>> theme = ColorTheme.get_plotly_theme() """ # ======================================================================== # COULEURS PRINCIPALES # ======================================================================== # Fond et surfaces BACKGROUND: str = "#1E1E1E" # Fond sombre principal SECONDARY_BACKGROUND: str = "#333333" # Fond widgets/cartes BACKGROUND_MAIN: str = "#1E1E1E" # Gris foncé - zone principale BACKGROUND_SIDEBAR: str = "#000000" # Noir pur - sidebar BACKGROUND_FOOTER: str = "#1a1a1a" # Noir foncé - footer BACKGROUND_CARD: str = "#333333" # Gris moyen - cards et widgets # Texte TEXT: str = "#F0F0F0" # Texte clair principal TEXT_PRIMARY: str = "#F0F0F0" # Gris clair - texte principal TEXT_SECONDARY: str = "#888888" # Gris moyen - texte secondaire TEXT_WHITE: str = "#ffffff" # Blanc pur - texte sur fond sombre # Orange primaire (accent principal) - COULEURS DU LOGO PRIMARY: str = "#FF8C00" # Orange vif - couleur principale SECONDARY_ACCENT: str = "#FFD700" # Jaune doré - accent secondaire ORANGE_PRIMARY: str = "#FF8C00" # Orange vif (alias de PRIMARY) ORANGE_SECONDARY: str = "#E24E1B" # Rouge/Orange profond du logo ORANGE_LIGHT: str = "#FFA07A" # Saumon - teinte douce # ======================================================================== # COULEURS D'ÉTAT # ======================================================================== SUCCESS: str = "#28A745" # Vert - succès, PROD badge WARNING: str = "#FFC107" # Jaune - warnings, PREPROD badge ERROR: str = "#DC3545" # Rouge - erreurs INFO: str = "#17A2B8" # Cyan - informations # ======================================================================== # PALETTE GRAPHIQUES (Plotly/Matplotlib) # ======================================================================== CHART_COLORS: list[str] = [ "#FF8C00", # Base Orange (du milieu du dégradé du logo) "#FFD700", # Base Jaune (du point lumineux du dégradé) "#E24E1B", # Rouge/Orange Profond (de la base du dégradé) "#1E90FF", # Bleu Vif (du contour et des effets de vitesse) "#00CED1", # Cyan (accent technologique du logo) "#FFA07A", # Saumon (teinte plus douce du dégradé) "#B0E0E6", # Bleu Clair (teinte plus claire du contour) "#DAA520", # Jaune Doré (variation riche du jaune) ] # Palette steelblue (comme dans les wireframes) STEELBLUE_PALETTE: list[str] = [ "#4682b4", # SteelBlue principal "#5a9bd5", # SteelBlue clair "#3a6ba5", # SteelBlue foncé ] # ======================================================================== # DÉGRADÉS # ========================================================================
[docs] @classmethod def gradient_orange(cls) -> str: """Dégradé orange (PRIMARY → ORANGE_SECONDARY).""" return ( f"linear-gradient(135deg, {cls.PRIMARY} 0%, " f"{cls.ORANGE_SECONDARY} 100%)" )
[docs] @classmethod def gradient_dark(cls) -> str: """Dégradé fond sombre (SIDEBAR → MAIN).""" return ( f"linear-gradient(180deg, {cls.BACKGROUND_SIDEBAR} 0%, " f"{cls.BACKGROUND_MAIN} 100%)" )
# ======================================================================== # BADGES ENVIRONNEMENT # ======================================================================== ENV_PROD: dict[str, str] = { "bg": SUCCESS, "text": TEXT_WHITE, "icon": "🟢", } ENV_PREPROD: dict[str, str] = { "bg": WARNING, "text": "#333333", # Texte sombre sur jaune "icon": "🔍", } # Constantes séparées pour utilisation directe ENV_PROD_BG: str = SUCCESS ENV_PROD_TEXT: str = TEXT_WHITE ENV_PREPROD_BG: str = WARNING ENV_PREPROD_TEXT: str = "#333333" # ======================================================================== # COMPOSANTS UI # ======================================================================== # Boutons BUTTON_PRIMARY_BG: str = ORANGE_PRIMARY BUTTON_PRIMARY_TEXT: str = TEXT_WHITE BUTTON_PRIMARY_HOVER: str = ORANGE_LIGHT BUTTON_SECONDARY_BG: str = "transparent" BUTTON_SECONDARY_BORDER: str = ORANGE_PRIMARY BUTTON_SECONDARY_TEXT: str = ORANGE_PRIMARY # Cards et conteneurs CARD_BORDER: str = "#333333" CARD_SHADOW: str = "0 4px 6px rgba(0, 0, 0, 0.3)" CARD_SHADOW_ORANGE: str = "0 4px 6px rgba(255, 140, 66, 0.3)" # Inputs et contrôles INPUT_BORDER: str = "#444444" INPUT_FOCUS_BORDER: str = ORANGE_PRIMARY INPUT_BG: str = BACKGROUND_CARD # ======================================================================== # MÉTHODES UTILITAIRES # ========================================================================
[docs] @classmethod def to_rgba(cls, hex_color: str, alpha: float = 1.0) -> str: """Convertit une couleur HEX en RGBA avec validation. Args: hex_color: Couleur au format hex (#RRGGBB) alpha: Transparence (0.0 à 1.0) Returns: Couleur au format rgba(r, g, b, a) Raises: ValueError: Si hex_color invalide ou alpha hors range Examples: >>> ColorTheme.to_rgba("#FF8C00", 0.5) 'rgba(255, 140, 0, 0.5)' >>> ColorTheme.to_rgba(ColorTheme.ORANGE_PRIMARY, 0.8) 'rgba(255, 140, 0, 0.8)' """ if not isinstance(hex_color, str) or not hex_color.startswith("#"): raise ValueError(f"Couleur hex invalide: {hex_color}") if not 0.0 <= alpha <= 1.0: raise ValueError(f"Alpha doit être entre 0 et 1, reçu: {alpha}") hex_color = hex_color.lstrip("#") if len(hex_color) != 6: raise ValueError(f"Couleur hex doit avoir 6 caractères, reçu: #{hex_color}") try: r, g, b = tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) except ValueError as e: raise ValueError(f"Couleur hex invalide: #{hex_color}") from e return f"rgba({r}, {g}, {b}, {alpha})"
[docs] @classmethod def get_plotly_theme(cls) -> dict: """Retourne le thème Plotly complet. Returns: Dictionnaire de configuration Plotly avec fond, grilles, axes et palette de couleurs Examples: >>> theme = ColorTheme.get_plotly_theme() >>> fig.update_layout(**theme['layout']) """ return { "layout": { "plot_bgcolor": cls.BACKGROUND_MAIN, "paper_bgcolor": cls.BACKGROUND_MAIN, "font": {"color": cls.TEXT_PRIMARY}, "xaxis": { "gridcolor": "#333333", "linecolor": "#444444", }, "yaxis": { "gridcolor": "#333333", "linecolor": "#444444", }, "colorway": cls.CHART_COLORS, } }
[docs] @classmethod def get_seasonal_colors(cls) -> dict[str, str]: """Retourne le mapping couleurs saisonnières. Returns: Dictionnaire {saison: couleur_hex} Examples: >>> colors = ColorTheme.get_seasonal_colors() >>> autumn_color = colors["Automne"] >>> # '#FF8C00' """ return { "Printemps": "#90EE90", # Vert clair "Été": "#FFD700", # Jaune doré "Automne": cls.ORANGE_PRIMARY, # Orange vif "Hiver": "#4682B4", # Bleu acier }
[docs] @classmethod def get_seasonal_color(cls, season: str) -> str: """Retourne la couleur pour une saison donnée. Args: season: Nom de la saison (Printemps, Été, Automne, Hiver) Returns: Couleur hex de la saison, ou ORANGE_PRIMARY si saison inconnue Examples: >>> ColorTheme.get_seasonal_color("Automne") '#FF8C00' >>> ColorTheme.get_seasonal_color("Inconnu") '#FF8C00' # Fallback """ seasonal_colors = cls.get_seasonal_colors() return seasonal_colors.get(season, cls.ORANGE_PRIMARY)