getaround-api / app.py
pradelf's picture
Update app.py
18c9ca8 verified
Raw History Blame Contribute Delete
6.4 kB
from fastapi import FastAPI
from fastapi import HTTPException
from pydantic import BaseModel
import mlflow.pyfunc
import pandas as pd
import config
import logging
import os
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 1) URL du Space HF qui héberge MLflow (tracking server)
MLFLOW_TRACKING_URI = os.getenv("MLFLOW_TRACKING_URI", "https://pradelf-getaround-mlflow.hf.space")
MODEL_URI = os.getenv("MODEL_URI", "models:/getaround-price-prediction-model@certification")
BOOL_COLS = [
"private_parking_available",
"has_gps",
"has_air_conditioning",
"automatic_car",
"has_getaround_connect",
"has_speed_regulator",
"winter_tires",
]
description = """
Bienvenue sur l'API de Getaround pour prédire le prix journalier de location d'une voiture en fonction de son année d'expérience!
## Point de terminaison d'introduction
Pour tester le fonctionnement de l'API, vous pouvez utiliser le point de terminaison d'introduction suivant:
* `/`: **GET** retourne la version de l'API et un message de bienvenue.
## Point de terminaison de ligne de vie
Cette web fonction permet de vérifier que le serveur de l'API est opérationnel et de surveiller son statut.
* `/health`: **GET** retourne juste OK pour valider que le serveur de l'API est en cours d'exécution sans problèmes.
## Machine Learning : Point de terminaison du prix de location
Cette terminaison de l'API permet de prédire le prix journalier de location d'une voiture en fonction de ses caractéristiques..
* `/predict` accepte une requête POST avec un JSON contenant un objet JSON donnant les caractéristiques d'une voiture
et retourne une prédiction du prix journalier de location de celle-ci.
La documentation est ci-dessous 👇 pour chaque point de terminaison (endpoints).
"""
tags_metadata = [
{
"name": "Point de terminaison d'introduction",
"description": "Terminaison simple de présentation de l'API et de sa version pour vérifier que tout fonctionne correctement.",
},
{
"name": "Point de terminaison de ligne de vie",
"description": "Point de terminaison de ligne de vie pour surveiller le statut du serveur de l'API. Retourne 'ok' si le serveur fonctionne sans problèmes.",
},
{
"name": "Point de terminaison du prix de location",
"description": "Point de terminaison permettant de prédire le prix journalier de location d'une voiture en fonction de ses caractéristiques.",
},
{
"name": "Machine Learning",
"description": "Point de terminaison de prediction du prix de location journalier d'un véhicule en fonction de ses caractéristiques.",
},
]
model= None
app = FastAPI(
title="Getaround API pour le prix journalier de location d'une voiture.",
description=description,
version=config.__version__,
contact={
"name": "Francis Pradel",
"url": "https://promotion.francispradel.fr",
},
openapi_tags=tags_metadata,
swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}},
)
@app.on_event("startup")
def load_model_on_startup():
global model
try:
logger.info(f"Chargement du modèle depuis {MLFLOW_TRACKING_URI}")
logger.info(f"Model : {MODEL_URI}")
mlflow.set_tracking_uri(MLFLOW_TRACKING_URI) # ou via env MLFLOW_TRACKING_URI
model = mlflow.pyfunc.load_model(MODEL_URI)
logger.info("Modèle chargé avec succès")
except Exception:
logger.exception("Impossible de charger le modèle au démarrage")
model = None
class RentalFeatures(BaseModel):
model_key: str
mileage: int
engine_power: int
fuel: str
paint_color: str
car_type: str
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
@app.get("/", tags=["Introduction Endpoints"])
def greet_json():
"""
Bonjour
"""
return {"Hello": "World!", "version": config.__version__}
@app.get("/health")
def health():
"""
Health line to monitor the server status of the API. Returns "ok" if the server is running without issues.
"""
return {"status": "ok"}
@app.post("/predict", tags=["Machine Learning"])
async def predict(predictionFeatures: RentalFeatures):
"""
Prediction of car daily rental cost for a given properties of a car !
"""
# Read data
# pf = [
# predictionFeatures.model_key or "Peugeot",
# predictionFeatures.mileage or 0,
# predictionFeatures.engine_power or 0,
# predictionFeatures.fuel or "petrol",
# predictionFeatures.car_type or "sedan",
# predictionFeatures.private_parking_available or 1,
# predictionFeatures.has_gps or 0,
# predictionFeatures.has_air_conditioning or 0,
# predictionFeatures.automatic_car or 0,
# predictionFeatures.has_getaround_connect or 0,
# predictionFeatures.has_speed_regulator or 0,
# predictionFeatures.winter_tires or 0,
# ]
try:
payload = predictionFeatures.model_dump()
car_characteristic = pd.DataFrame([payload])
#car_caracteristic = pd.DataFrame({"Car": pf})
# Log model from mlflow
BOOL_COLS = [
"private_parking_available",
"has_gps",
"has_air_conditioning",
"automatic_car",
"has_getaround_connect",
"has_speed_regulator",
"winter_tires",
]
# Conversion explicite des colonnes booléennes
for col in BOOL_COLS:
car_characteristic[col] = car_characteristic[col].astype(bool)
logger.info("Payload reçu: %s", payload)
logger.info("dtypes:\n%s", car_characteristic.dtypes)
# Prediction from previously loaded model as a PyFuncModel.
prediction = model.predict(car_characteristic)
logger.info(f"Output : {prediction}")
# Format response
response = {
"prediction": float(prediction[0]),
"detail": "Prédiction du tarif journalier (nul si aucun modèle : model.pkl).",
}
return response
except Exception as e:
logger.exception("Erreur dans /predict")
raise HTTPException(status_code=500, detail=str(e))