10 KiB
📋 Tâches d'implémentation - API Diagramme PH
Vue d'ensemble
Implémentation progressive et testable de l'API, étape par étape.
🎯 Phase 1 : Configuration initiale (MAINTENANT)
✅ Tâche 1.1 : Structure de base du projet
Durée estimée : 30 minutes
Actions :
# Créer structure
mkdir -p app/api/v1/endpoints
mkdir -p app/core
mkdir -p app/models
mkdir -p app/services
mkdir -p app/utils
mkdir -p libs/so
mkdir -p tests/test_api
mkdir -p tests/test_core
mkdir -p docker
mkdir -p deployment/scripts
Fichiers à créer :
app/__init__.pyapp/config.pyapp/main.pyrequirements.txt.env.example.gitignore
Test : Structure des dossiers existe
✅ Tâche 1.2 : Configuration requirements.txt
Durée estimée : 10 minutes
Créer : requirements.txt
fastapi==0.109.0
uvicorn[standard]==0.27.0
pydantic==2.5.0
pydantic-settings==2.1.0
numpy==1.26.3
pandas==2.2.0
matplotlib==3.8.2
plotly==5.18.0
python-multipart==0.0.6
cachetools==5.3.2
python-json-logger==2.0.7
Test : pip install -r requirements.txt fonctionne
✅ Tâche 1.3 : Configuration de base
Durée estimée : 15 minutes
Créer : app/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
APP_NAME: str = "Diagram PH API"
VERSION: str = "1.0.0"
ENV: str = "development"
LOG_LEVEL: str = "DEBUG"
class Config:
env_file = ".env"
settings = Settings()
Créer : .env.example
ENV=development
LOG_LEVEL=DEBUG
Test : Importer settings fonctionne
✅ Tâche 1.4 : Application FastAPI minimale
Durée estimée : 20 minutes
Créer : app/main.py
from fastapi import FastAPI
from app.config import settings
app = FastAPI(
title=settings.APP_NAME,
version=settings.VERSION
)
@app.get("/")
def root():
return {
"message": "Diagram PH API",
"version": settings.VERSION,
"status": "running"
}
@app.get("/api/v1/health")
def health():
return {
"status": "healthy",
"version": settings.VERSION
}
Test :
uvicorn app.main:app --reload
# Visiter http://localhost:8000
# Visiter http://localhost:8000/api/v1/health
Résultat attendu : API démarre, endpoints répondent
🎯 Phase 2 : Intégration bibliothèques .so (ENSUITE)
✅ Tâche 2.1 : Copier bibliothèques .so
Durée estimée : 5 minutes
Actions :
# Copier les fichiers .so
cp IPM_SO/*.so libs/so/
Test : Fichiers existent dans libs/so/
✅ Tâche 2.2 : Wrapper bibliothèque de base
Durée estimée : 45 minutes
Créer : app/core/refrigerant_loader.py
import ctypes
import platform
from pathlib import Path
from typing import Optional
class RefrigerantLoader:
"""Gestionnaire de chargement des bibliothèques .so"""
BASE_DIR = Path(__file__).parent.parent.parent / "libs"
@classmethod
def get_library_path(cls, refrigerant: str) -> Path:
"""Retourne le chemin de la bibliothèque"""
system = platform.system()
if system == "Windows":
return cls.BASE_DIR / "dll" / f"{refrigerant}.dll"
elif system == "Linux":
return cls.BASE_DIR / "so" / f"lib{refrigerant}.so"
else:
raise OSError(f"Unsupported OS: {system}")
@classmethod
def load(cls, refrigerant: str):
"""Charge la bibliothèque"""
lib_path = cls.get_library_path(refrigerant)
if not lib_path.exists():
raise FileNotFoundError(f"Library not found: {lib_path}")
try:
return ctypes.CDLL(str(lib_path))
except OSError as e:
raise RuntimeError(f"Failed to load {lib_path}: {e}")
@classmethod
def list_available(cls) -> list:
"""Liste les réfrigérants disponibles"""
system = platform.system()
if system == "Windows":
dir_path = cls.BASE_DIR / "dll"
pattern = "R*.dll"
else:
dir_path = cls.BASE_DIR / "so"
pattern = "libR*.so"
if not dir_path.exists():
return []
files = list(dir_path.glob(pattern))
refrigerants = []
for f in files:
if system == "Windows":
name = f.stem
else:
name = f.stem[3:] # Remove 'lib' prefix
refrigerants.append(name)
return sorted(refrigerants)
Test :
# test_loader.py
from app.core.refrigerant_loader import RefrigerantLoader
# Lister disponibles
refrigerants = RefrigerantLoader.list_available()
print(f"Disponibles: {refrigerants}")
# Charger R134a
lib = RefrigerantLoader.load("R134a")
print(f"✅ R134a chargé: {lib}")
Résultat attendu : Liste des réfrigérants + chargement réussi
✅ Tâche 2.3 : Endpoint liste réfrigérants
Durée estimée : 20 minutes
Créer : app/api/v1/endpoints/refrigerants.py
from fastapi import APIRouter
from app.core.refrigerant_loader import RefrigerantLoader
router = APIRouter()
@router.get("/refrigerants")
def list_refrigerants():
"""Liste des réfrigérants disponibles"""
refrigerants = RefrigerantLoader.list_available()
return {
"success": True,
"count": len(refrigerants),
"refrigerants": refrigerants
}
Modifier : app/main.py
from app.api.v1.endpoints import refrigerants
app.include_router(
refrigerants.router,
prefix="/api/v1",
tags=["refrigerants"]
)
Test :
curl http://localhost:8000/api/v1/refrigerants
Résultat attendu : JSON avec liste des réfrigérants
🎯 Phase 3 : Calculs thermodynamiques (APRÈS)
✅ Tâche 3.1 : Interface simple_refrig_api.py
Durée estimée : 30 minutes
Copier et adapter : IPM_DLL/simple_refrig_api.py → app/core/refrig_api.py
Modifications :
- Adapter paths pour
libs/so/ - Simplifier pour besoins API
- Ajouter gestion erreurs
Test :
from app.core.refrig_api import Refifc
# Test R134a
refrig = Refifc("R134a")
props = refrig.get_properties_PT(500000, 278.15)
print(f"✅ Propriétés calculées: {props}")
✅ Tâche 3.2 : Endpoint propriétés basiques
Durée estimée : 40 minutes
Créer : app/models/requests.py
from pydantic import BaseModel, Field
from typing import Optional
class PropertyRequest(BaseModel):
refrigerant: str = Field(..., description="Nom du réfrigérant")
pressure: float = Field(..., gt=0, description="Pression [Pa]")
temperature: float = Field(..., description="Température [K]")
Créer : app/api/v1/endpoints/properties.py
from fastapi import APIRouter, HTTPException
from app.models.requests import PropertyRequest
from app.core.refrig_api import Refifc
router = APIRouter()
@router.post("/properties/calculate")
def calculate_properties(request: PropertyRequest):
"""Calcule les propriétés thermodynamiques"""
try:
refrig = Refifc(request.refrigerant)
# TODO: Appeler méthodes DLL
return {
"success": True,
"refrigerant": request.refrigerant,
"properties": {
"pressure": request.pressure,
"temperature": request.temperature,
# TODO: Ajouter propriétés calculées
}
}
except FileNotFoundError:
raise HTTPException(404, "Refrigerant not found")
except Exception as e:
raise HTTPException(500, str(e))
Test :
curl -X POST http://localhost:8000/api/v1/properties/calculate \
-H "Content-Type: application/json" \
-d '{
"refrigerant": "R134a",
"pressure": 500000,
"temperature": 278.15
}'
🎯 Phase 4 : Génération diagrammes (PLUS TARD)
✅ Tâche 4.1 : Courbe de saturation basique
Durée estimée : 1 heure
Créer : app/core/diagram_generator.py
- Générer courbe saturation
- Format JSON simple
✅ Tâche 4.2 : Export Plotly JSON
Durée estimée : 1 heure
- Créer figure Plotly
- Exporter en JSON
- Endpoint
/diagram/generate
✅ Tâche 4.3 : Export Matplotlib PNG
Durée estimée : 45 minutes
- Générer image PNG
- Encoder en base64
- Option dans endpoint
🎯 Phase 5 : Calculs cycle (ENCORE PLUS TARD)
✅ Tâche 5.1 : Calculs COP basiques
Durée estimée : 1.5 heures
✅ Tâche 5.2 : Endpoint cycle complet
Durée estimée : 1 heure
🎯 Checklist de progression
Maintenant (Session actuelle)
- Tâche 1.1 : Structure projet
- Tâche 1.2 : Requirements
- Tâche 1.3 : Configuration
- Tâche 1.4 : FastAPI minimal
- TEST : API démarre et répond
Session suivante
- Tâche 2.1 : Copier .so
- Tâche 2.2 : Loader bibliothèques
- Tâche 2.3 : Endpoint réfrigérants
- TEST : Liste réfrigérants fonctionne
Après
- Phase 3 : Calculs propriétés
- Phase 4 : Diagrammes
- Phase 5 : Cycles
🧪 Tests à chaque étape
Test 1 : Structure
ls -la app/
ls -la libs/so/
Test 2 : API démarre
uvicorn app.main:app --reload
curl http://localhost:8000/api/v1/health
Test 3 : Bibliothèques
python -c "from app.core.refrigerant_loader import RefrigerantLoader; print(RefrigerantLoader.list_available())"
Test 4 : Endpoint complet
curl http://localhost:8000/api/v1/refrigerants
✅ Critères de succès
Après Phase 1 (aujourd'hui) :
- ✅ API FastAPI fonctionne
- ✅ Endpoint /health répond
- ✅ Documentation auto (/docs)
Après Phase 2 :
- ✅ Bibliothèques .so chargées
- ✅ Liste réfrigérants disponible
- ✅ Aucune erreur au démarrage
🚀 On commence maintenant !
Prochaine action : Basculer en mode Code et créer la structure (Tâche 1.1)
Êtes-vous prêt ?