import pandas as pd import joblib import uvicorn from fastapi import FastAPI, Body, Request from fastapi.responses import JSONResponse, RedirectResponse from fastapi.exceptions import RequestValidationError from pydantic import BaseModel, Field from typing import Literal # Charger le modèle model = joblib.load("get_around_v1.pkl") # Classe Pydantic pour les entrées class CarData(BaseModel): mileage: float = Field(..., ge=0, description="Kilométrage doit être >= 0") engine_power: float = Field(..., ge=1, le=423, description="Puissance moteur entre 1 et 423") car_brand: Literal[ "Citroën", "Peugeot", "PGO", "Renault", "Audi", "BMW", "Ford", "Mercedes", "Opel", "Porsche", "Volkswagen", "KIA Motors", "Alfa Romeo", "Ferrari", "Fiat", "Lamborghini", "Maserati", "Lexus", "Honda", "Mazda", "Mini", "Mitsubishi", "Nissan", "SEAT", "Subaru", "Suzuki", "Toyota", "Yamaha" ] fuel: Literal["diesel", "petrol", "hybrid_petrol", "electro"] paint_color: Literal[ "black", "grey", "white", "red", "silver", "blue", "orange", "beige", "brown", "green" ] car_type: Literal[ "convertible", "coupe", "estate", "hatchback", "sedan", "subcompact", "suv", "van" ] private_parking_available: bool has_gps: bool has_air_conditioning: bool automatic_car: bool has_getaround_connect: bool has_speed_regulator: bool winter_tires: bool # Initialisation FastAPI app = FastAPI( title="GetAround Prediction API", description=""" Cette API prédit le prix de location journalier d’un véhicule. 👉 Pour les (car_brand, fuel, car_type, paint_color), vous devez choisir une valeur parmi les critères listés, à savoir : - car_brand : "Citroën", "Peugeot", "PGO", "Renault", "Audi", "BMW", "Ford", "Mercedes", "Opel", "Porsche", "Volkswagen", "KIA Motors", "Alfa Romeo", "Ferrari", "Fiat", "Lamborghini", "Maserati", "Lexus", "Honda", "Mazda", "Mini", "Mitsubishi", "Nissan", "SEAT", "Subaru", "Suzuki", "Toyota", "Yamaha". - fuel : "diesel", "petrol", "hybrid_petrol", "electro". - paint_color : "black", "grey", "white", "red", "silver", "blue", "orange", "beige", "brown", "green" 👉 Pour les autres options (private_parking_available, has_gps, etc.), utiliser true pour Oui et false pour Non. """, version="1.0" ) @app.get("/") def root(): return RedirectResponse(url="/docs") # Gestionnaire d'erreurs personnalisé @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): errors = [] for e in exc.errors(): field = e.get("loc")[-1] expected = e.get("ctx", {}).get("expected") msg = e.get("msg") if expected: errors.append({ "field": field, "message": f"Valeur incorrecte pour '{field}'. Valeurs possibles : {expected}" }) else: errors.append({ "field": field, "message": f"Erreur sur '{field}': {msg}" }) return JSONResponse( status_code=422, content={ "error": "Certaines valeurs ne sont pas valides", "details": errors } ) # Endpoint de prédiction @app.post( "/predict", summary="Prédire le prix journalier d'une voiture", description="Fournir toutes les caractéristiques de la voiture pour obtenir le prix prédictif." ) def predict( data: CarData = Body( ..., examples={ "valid_example": { "summary": "Exemple valide", "value": { "mileage": 15000, "engine_power": 100, "car_brand": "Citroën", "fuel": "diesel", "paint_color": "white", "car_type": "sedan", "private_parking_available": True, "has_gps": True, "has_air_conditioning": True, "automatic_car": True, "has_getaround_connect": True, "has_speed_regulator": True, "winter_tires": True } } } ) ): df = pd.DataFrame([data.model_dump()]) df.rename(columns={'car_brand': 'model_key'}, inplace=True) prediction = model.predict(df) return {"prediction": round(float(prediction[0]), 2)} # Lancer localement (utile pour tests) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)