import os import json import joblib import pandas as pd from fastapi import FastAPI from fastapi.responses import HTMLResponse from pydantic import BaseModel, ConfigDict # ========================================================= # Paths # ========================================================= BASE_DIR = os.path.dirname(os.path.abspath(__file__)) PIPELINE_PATH = os.path.join(BASE_DIR, "pipeline.pkl") FEATURES_PATH = os.path.join(BASE_DIR, "feature_names.json") METRICS_PATH = os.path.join(BASE_DIR, "model_metrics.json") # ========================================================= # Load artefacts # ========================================================= pipeline = joblib.load(PIPELINE_PATH) with open(FEATURES_PATH, "r", encoding="utf-8") as f: feature_names = json.load(f) if os.path.exists(METRICS_PATH): with open(METRICS_PATH, "r", encoding="utf-8") as f: model_metrics = json.load(f) else: model_metrics = {} # ========================================================= # FastAPI app # ========================================================= app = FastAPI( title="GetAround Pricing API", description="Predicts the optimal rental price per day for a car", version="1.0.0", docs_url=None, redoc_url=None ) # ========================================================= # Input schema # ========================================================= class PredictInput(BaseModel): input: list[list] model_config = ConfigDict( json_schema_extra={ "example": { "input": [[ "Citroën", 50000, 120, "diesel", "black", "sedan", 1, 1, 1, 0, 1, 1, 0 ]] } } ) # ========================================================= # Helpers # ========================================================= def metric_value(name: str, digits: int = 2) -> str: value = model_metrics.get(name) if value is None: return "N/A" try: return f"{float(value):.{digits}f}" except Exception: return "N/A" def get_model_name() -> str: try: return pipeline.named_steps["model"].__class__.__name__ except Exception: return "Unknown" # ========================================================= # Routes # ========================================================= @app.get("/", response_class=HTMLResponse) def root(): return """

🚗 GetAround Pricing API

API is running.

📄 Go to Documentation """ @app.post("/predict") def predict(data: PredictInput): X = pd.DataFrame(data.input, columns=feature_names) predictions = pipeline.predict(X) return {"prediction": [round(float(p), 2) for p in predictions]} @app.get("/docs", response_class=HTMLResponse) def documentation(): algo_name = get_model_name() rmse = metric_value("RMSE") mae = metric_value("MAE") r2 = metric_value("R²", 3) cv_rmse = metric_value("CV_RMSE_mean") cv_std = metric_value("CV_RMSE_std") feature_rows = "".join([ f"{i+1}{feature}" for i, feature in enumerate(feature_names) ]) return f""" GetAround Pricing API Documentation

🚗 GetAround Pricing API

Predict the optimal rental price per day for a car

POST/predict

Returns a predicted rental price per day based on the car's characteristics.

/predict
Expected input features
{feature_rows}
#Feature
Request example
{{
  "input": [[
    "Citroën",
    50000,
    120,
    "diesel",
    "black",
    "sedan",
    1,
    1,
    1,
    0,
    1,
    1,
    0
  ]]
}}
Response example
{{
  "prediction": [124.52]
}}

GET/

Health check endpoint.

/

GET/docs

Custom API documentation page.

/docs

🤖 Model Information

PropertyValue
Algorithm{algo_name}
Targetrental_price_per_day (€)
RMSE{rmse}
MAE{mae}
{r2}
CV RMSE{cv_rmse}
CV RMSE std{cv_std}
Number of features{len(feature_names)}
"""