Standards Qualité

Ce projet respecte les standards de qualité pour un projet Python moderne.

Voir aussi : Tests et Coverage (coverage 93%), Pipeline CI/CD (pipeline automatisé), Architecture Technique (stack technique)

Gestion de Projet

Structure Cohérente

  • Packages organisés : utils/, visualization/, data/

  • Modules séparés par fonctionnalité

  • Séparation logique code/tests/documentation

Environnement Python

  • Gestionnaire moderne : uv (remplaçant pip)

  • Configuration : pyproject.toml

  • Environnement virtuel isolé

  • Dépendances versionnées

Git et GitHub

  • Commits réguliers et descriptifs

  • Branches de développement

  • Pull Requests avec review

  • Historique traçable

Documentation

  • README.md complet (installation, utilisation)

  • Documentation Sphinx auto-générée

  • Guides techniques (CI/CD, tests, S3)

  • Docstrings sur toutes les fonctions

Streamlit

  • Interface utilisateur intuitive

  • Widgets interactifs (sliders, selectbox)

  • Storytelling des analyses

  • Graphiques Plotly dynamiques

Programmation

Programmation Orientée Objet

Classes implémentées dans le projet :

Classe DataLoader (data.loaders)

Gestion du chargement de données avec exceptions :

class DataLoader:
    def load_recipes(self) -> pl.DataFrame:
        """Charge recettes depuis S3 avec gestion erreurs."""

    def load_ratings(
        self,
        min_interactions: int = 100,
        return_metadata: bool = False,
        verbose: bool = False
    ) -> pl.DataFrame | tuple:
        """Charge ratings avec options configurables."""

Hiérarchie Exceptions (exceptions.py)

6 classes d’exceptions personnalisées :

class MangetamainError(Exception):
    """Exception de base."""

class DataLoadError(MangetamainError):
    """Erreur chargement S3/DuckDB."""
    def __init__(self, source: str, detail: str): ...

class AnalysisError(MangetamainError):
    """Erreur analyse statistique."""

class ConfigurationError(MangetamainError):
    """Erreur configuration."""

class DatabaseError(MangetamainError):
    """Erreur opérations DuckDB."""

class ValidationError(MangetamainError):
    """Erreur validation données."""

Autres Classes

  • Configuration environnement (logging, détection preprod/prod)

  • Utilitaires graphiques (application thème, gestion couleurs)

Type Hinting

Annotations de types complètes :

def apply_chart_theme(fig: go.Figure, title: str = None) -> go.Figure:
    """Applique le thème à un graphique."""
    pass

def get_ratings_longterm(
    min_interactions: int = 100,
    return_metadata: bool = False,
    verbose: bool = False
) -> pd.DataFrame:
    """Charge les ratings depuis S3."""
    pass

Respect PEP8

  • Validation automatique avec flake8

  • Formatage avec black

  • Ligne maximale : 88 caractères

  • Pipeline CI vérifie à chaque push

Gestion des Exceptions

Try/except personnalisés avec messages clairs :

try:
    data = load_from_s3(bucket, key)
except boto3.exceptions.NoCredentialsError:
    st.error("Credentials S3 introuvables. Vérifier 96_keys/credentials")
except Exception as e:
    st.error(f"Erreur chargement données : {e}")
    return None

Logging

Système Loguru 0.7.3 complet avec séparation PREPROD/PROD :

  • Architecture : 2 fichiers (debug.log, errors.log) par environnement

  • Détection auto : Variable APP_ENV ou path automatique

  • Rotation : 10 MB (debug), 5 MB (errors) avec compression

  • Thread-safe : enqueue=True pour Streamlit multithread

  • Backtrace : Diagnostic complet des erreurs

from loguru import logger

def load_data():
    try:
        logger.info("Starting data load")
        data = load_from_s3()
        logger.success(f"Loaded {len(data)} records")
    except Exception as e:
        logger.error(f"Load failed: {e}")
        raise

Voir : Architecture Technique section Logging pour configuration complète.

Événements Loggués

L’application Streamlit enregistre 21 événements dans les fichiers de log.

main.py (13 événements)

Démarrage application:

  • logger.info (519) : « 🚀 Enhanced Streamlit application starting »

  • logger.info (833) : « ✅ Application fully loaded »

  • logger.info (837) : « 🌟 Starting Enhanced Mangetamain Analytics »

Ressources et vérifications:

  • logger.warning (527) : « CSS file not found: {css_path} »

  • logger.warning (633) : « S3 not accessible: {e} »

  • logger.warning (636) : « Unexpected error checking S3: {e} »

Erreurs analyses:

  • logger.warning (246) : « Erreur lors de l’analyse de {table}: {e} »

  • logger.error (315) : « DatabaseError in temporal analysis: {e} »

  • logger.error (318) : « AnalysisError in temporal analysis: {e} »

  • logger.error (321) : « Unexpected error in temporal analysis: {e} »

  • logger.error (381) : « DatabaseError in user analysis: {e} »

  • logger.error (384) : « AnalysisError in user analysis: {e} »

  • logger.error (387) : « Unexpected error in user analysis: {e} »

Chargement données (data/loaders.py - 8 événements)

Le chargement des fichiers Parquet depuis S3 génère des logs détaillés avec gestion d’erreurs via DataLoadError :

Chargement recettes :

  • logger.error (40) : "Module mangetamain_data_utils introuvable: {e}"

  • logger.info (47) : "Chargement recettes depuis S3 (Parquet)"

  • logger.info (49) : "Recettes chargées: {len(recipes)} lignes"

  • logger.error (52) : "Échec chargement recettes depuis S3: {e}"

Chargement ratings :

  • logger.error (81) : "Module mangetamain_data_utils introuvable: {e}"

  • logger.info (88) : "Chargement ratings depuis S3 (Parquet) - min_interactions={min_interactions}"

  • logger.info (98/100) : "Ratings chargés: {len(data)} lignes (avec metadata)" ou "Ratings chargés: {len(result)} lignes"

  • logger.error (103) : "Échec chargement ratings depuis S3: {e}"

Répartition par niveau:

  • INFO : 7 événements (3 démarrage + 4 chargement données)

  • WARNING : 4 événements (CSS, S3, analyses)

  • ERROR : 10 événements (6 analyses + 4 chargement données)

Sécurité

  • Credentials S3 dans fichier gitignore (96_keys/)

  • Secrets GitHub chiffrés

  • Pas de tokens en clair dans le code

  • Validation des inputs utilisateurs

Tests et Qualité

Tests Unitaires

  • Framework : pytest 8.5.0

  • Nombre : 118 tests

  • Résultat : 118 tests passent

  • Organisation : tests/unit/ + 50_test/

Coverage

  • Objectif : >= 90%

  • Atteint : 93%

  • Outil : pytest-cov

  • Rapport : HTML avec lignes manquantes

Métriques par Module

Module

Coverage

Tests

utils/color_theme.py

97%

35

utils/chart_theme.py

100%

10

visualization/trendlines.py

100%

8

visualization/ratings.py

90-100%

5-14

data/cached_loaders.py

78%

3

Commentaires

  • Documentation inline des sections complexes

  • Explication des algorithmes

  • Références aux sources de données

  • Notes sur les optimisations

Docstrings

  • Format : Google Style

  • Couverture : Toutes fonctions/classes/modules

  • Validation : pydocstyle dans CI

  • Exemple :

def calculate_seasonal_patterns(df: pd.DataFrame) -> pd.DataFrame:
    """Calcule les patterns saisonniers des recettes.

    Analyse la distribution mensuelle des recettes et identifie
    les pics d'activité saisonniers.

    Args:
        df: DataFrame avec colonnes 'date' et 'recipe_id'

    Returns:
        DataFrame avec patterns saisonniers agrégés par mois

    Raises:
        ValueError: Si colonnes requises manquantes
    """
    pass

Documentation Sphinx

  • Génération automatique depuis docstrings

  • Theme Read the Docs professionnel

  • API documentation complète

  • Guides utilisateur (installation, usage, architecture)

CI/CD

Pipeline CI

Vérifications automatiques à chaque push :

  1. PEP8 : flake8 avec config .flake8

  2. Docstrings : pydocstyle (convention Google)

  3. Tests : pytest avec coverage >= 90%

  4. Quality : black, mypy (optionnel)

Exécution Automatique

  • Sur push vers branche de développement

  • Sur Pull Request vers main

  • Sur merge vers main

  • Bloque le merge si tests échouent

CD PREPROD

Déploiement automatique sur https://mangetamain.lafrance.io/

  • Déclenché en parallèle du CI (pas d’attente)

  • Déploiement ultra-rapide : ~40 secondes

  • Rollback automatique si CI échoue

  • Runner self-hosted (VM dataia)

  • Health checks automatiques

  • Notifications Discord

CD PRODUCTION

Déploiement manuel sur https://backtothefuturekitchen.lafrance.io/

  • Confirmation obligatoire (taper « DEPLOY »)

  • Backup automatique avant déploiement

  • Rollback documenté si échec

  • Notifications Discord avec détails

Alerting

Notifications Discord temps réel :

  • Démarrage déploiement

  • Succès/échec avec détails

  • Instructions rollback si échec

  • Historique complet des déploiements

Choix Techniques Avancés

Base de Données OLAP

DuckDB - Base de données columnar haute performance :

  • Performance : 10-100x plus rapide que SQLite sur agrégations

  • Zero-copy : Lecture directe Parquet sans import

  • Volume : 581 MB, 7 tables

  • Données : 178K recettes, 1.1M+ interactions

Runner Self-Hosted

Infrastructure autonome : Déploiement sans dépendance VPN

  • Runner GitHub hébergé sur VM dataia

  • Déploiement ultra-rapide : 30-40 secondes

  • Gain productivité : 10 min manuelles → 30s automatiques

  • Disponibilité : 24/7 sans intervention

Architecture Multi-Environnement

Isolation complète PREPROD/PROD :

  • Bases de données : Distinctes par environnement

  • Logs : Niveau debug (PREPROD), errors only (PROD)

  • Variables : Environment-specific configuration

  • Ports : 8500 (PREPROD) vs 8501 (PROD)

Résumé Standards

Standard

Statut

Détails

Structure projet

Packages, modules

Environnement Python

uv + pyproject.toml

Git + GitHub

Commits réguliers

README.md

Complet

Streamlit

UX interactive

POO

DataLoader + hiérarchie exceptions

Type Hinting

Complet

PEP8

100% compliance

Exceptions personnalisées

Hiérarchie 6 classes

Logger

Loguru complet

Sécurité

Secrets protégés

Tests unitaires

118 tests

Coverage >= 90%

93% atteint

Commentaires

Sections complexes

Docstrings

Google Style

Documentation Sphinx

Auto-générée

Pipeline CI

PEP8 + tests + cov

Exécution auto

Push + PR + merge

CD (bonus)

PREPROD + PROD