""" Modèles Pydantic pour les diagrammes PH. """ from typing import Optional, List, Dict, Any from pydantic import BaseModel, Field, field_validator class DiagramPointRequest(BaseModel): """Point personnalisé à tracer sur le diagramme.""" pressure: float = Field( ..., description="Pression (bar)", gt=0 ) enthalpy: float = Field( ..., description="Enthalpie (kJ/kg)" ) temperature: Optional[float] = Field( None, description="Température (Celsius) - optionnel" ) entropy: Optional[float] = Field( None, description="Entropie (kJ/kg.K) - optionnel" ) quality: Optional[float] = Field( None, description="Titre vapeur (0-1) - optionnel", ge=0, le=1 ) label: Optional[str] = Field( None, description="Label du point - optionnel" ) class PressureRange(BaseModel): """Plage de pression.""" min: float = Field(..., gt=0, description="Pression minimale (bar)") max: float = Field(..., gt=0, description="Pression maximale (bar)") class EnthalpyRange(BaseModel): """Plage d'enthalpie.""" min: float = Field(..., description="Enthalpie minimale (kJ/kg)") max: float = Field(..., description="Enthalpie maximale (kJ/kg)") class DiagramRequest(BaseModel): """Requête pour générer un diagramme PH.""" refrigerant: str = Field( ..., description="Code du réfrigérant (ex: R134a, R410A)", examples=["R134a", "R410A", "R744"] ) pressure_range: PressureRange = Field( ..., description="Plage de pression du diagramme" ) enthalpy_range: Optional[EnthalpyRange] = Field( None, description="Plage d'enthalpie - auto si non fourni" ) include_isotherms: bool = Field( True, description="Inclure les isothermes" ) isotherm_values: Optional[List[float]] = Field( None, description="Températures isothermes spécifiques (Celsius)" ) cycle_points: Optional[List[Dict[str, float]]] = Field( None, description="Points du cycle [(enthalpy, pressure), ...]" ) title: Optional[str] = Field( None, description="Titre personnalisé du diagramme" ) format: str = Field( "both", description="Format: 'png', 'json', ou 'both'", pattern="^(png|json|both)$" ) width: int = Field(1400, gt=0, description="Largeur image (pixels)") height: int = Field(900, gt=0, description="Hauteur image (pixels)") dpi: int = Field(100, gt=0, description="DPI de l'image") class SaturationPoint(BaseModel): """Point sur la courbe de saturation.""" enthalpy: float = Field(..., description="Enthalpie (kJ/kg)") pressure: float = Field(..., description="Pression (bar)") class IsothermCurve(BaseModel): """Courbe isotherme.""" temperature: float = Field(..., description="Température (Celsius)") unit: str = Field("°C", description="Unité de température") points: List[SaturationPoint] = Field( ..., description="Points de la courbe" ) class DiagramPointResponse(BaseModel): """Point personnalisé dans la réponse.""" enthalpy: float = Field(..., description="Enthalpie (kJ/kg)") pressure: float = Field(..., description="Pression (bar)") temperature: Optional[float] = Field(None, description="Température (Celsius)") entropy: Optional[float] = Field(None, description="Entropie (kJ/kg.K)") quality: Optional[float] = Field(None, description="Titre vapeur (0-1)") class DiagramDataResponse(BaseModel): """Données JSON du diagramme.""" refrigerant: str = Field(..., description="Code du réfrigérant") ranges: Dict[str, Optional[float]] = Field( ..., description="Plages de valeurs du diagramme" ) saturation_curve: Dict[str, List[SaturationPoint]] = Field( ..., description="Courbe de saturation (liquide et vapeur)" ) isotherms: Optional[List[IsothermCurve]] = Field( None, description="Courbes isothermes" ) custom_points: Optional[List[DiagramPointResponse]] = Field( None, description="Points personnalisés" ) class DiagramResponse(BaseModel): """Réponse complète de génération de diagramme.""" success: bool = Field(True, description="Succès") image: Optional[str] = Field( None, description="Image PNG base64" ) data: Optional[Dict[str, Any]] = Field( None, description="Données JSON" ) metadata: Dict[str, Any] = Field( default_factory=dict, description="Métadonnées" ) message: Optional[str] = Field( None, description="Message" ) class DiagramError(BaseModel): """Erreur lors de la génération de diagramme.""" success: bool = Field(False, description="Échec de l'opération") error: str = Field(..., description="Message d'erreur") details: Optional[str] = Field(None, description="Détails supplémentaires")