harshit-temkar7-ds
Initial commit - Mumbai Train Project
75c2779
Raw History Blame Contribute Delete
8.09 kB
"""
Mumbai Local Train Crowd Density Predictor
FastAPI Backend - Advanced Edition
"""
import os
import sys
import json
from typing import Optional
from pathlib import Path
import pandas as pd
import numpy as np
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
# Fix path
sys.path.insert(0, str(Path(__file__).parent))
from src.model import (
train_model, load_model, predict_crowd,
LINE_MAP, DIR_MAP, DAY_MAP, HOUR_WEIGHTS
)
from src.risk_engine import (
get_risk_info, build_reasoning, get_best_hours,
get_travel_tip, RISK_CONFIG,
WESTERN_STATIONS_ORDER, CENTRAL_STATIONS_ORDER, HARBOUR_STATIONS_ORDER
)
# ─────────────────────────────────────────
# App Setup
# ─────────────────────────────────────────
app = FastAPI(
title="Mumbai Train Crowd Predictor",
description="AI-powered crowd density prediction for Mumbai Local Trains",
version="2.0.0"
)
BASE_DIR = Path(__file__).parent
DATA_PATH = BASE_DIR / "data" / "raw" / "mumbai_local_crowd_data.csv"
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
# ─────────────────────────────────────────
# Global State
# ─────────────────────────────────────────
MODEL = None
LE_STATION = None
LE_DEST = None
DF = None
MODEL_ACCURACY = None
MODEL_REPORT = None
STATIONS_BY_LINE = {
"Western": WESTERN_STATIONS_ORDER,
"Central": CENTRAL_STATIONS_ORDER,
"Harbour": HARBOUR_STATIONS_ORDER
}
@app.on_event("startup")
async def startup_event():
global MODEL, LE_STATION, LE_DEST, DF, MODEL_ACCURACY, MODEL_REPORT
print("πŸ“‚ Loading dataset...")
DF = pd.read_csv(DATA_PATH)
# Try loading cached model first
MODEL, LE_STATION, LE_DEST = load_model()
if MODEL is None:
print("🧠 Training model...")
MODEL, MODEL_ACCURACY, MODEL_REPORT, LE_STATION, LE_DEST = train_model(DF)
print(f"βœ… Model trained! Accuracy: {MODEL_ACCURACY:.2%}")
else:
# Quick retrain to get accuracy
_, MODEL_ACCURACY, MODEL_REPORT, _, _ = train_model(DF)
print(f"βœ… Model loaded! Accuracy: {MODEL_ACCURACY:.2%}")
# ─────────────────────────────────────────
# Schemas
# ─────────────────────────────────────────
class PredictRequest(BaseModel):
line: str
station: str
destination: str
direction: str
hour: int
day_of_week: str
class HourlyRequest(BaseModel):
line: str
station: str
destination: str
direction: str
day_of_week: str
# ─────────────────────────────────────────
# Routes
# ─────────────────────────────────────────
@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/api/stations/{line}")
async def get_stations(line: str):
if line not in STATIONS_BY_LINE:
raise HTTPException(status_code=404, detail="Line not found")
return {"stations": STATIONS_BY_LINE[line]}
@app.get("/api/lines")
async def get_lines():
return {"lines": list(STATIONS_BY_LINE.keys())}
@app.get("/api/days")
async def get_days():
return {"days": list(DAY_MAP.keys())}
@app.get("/api/hours")
async def get_hours():
return {"hours": list(HOUR_WEIGHTS.keys())}
@app.post("/api/predict")
async def predict(req: PredictRequest):
if MODEL is None:
raise HTTPException(status_code=503, detail="Model not loaded yet")
result = predict_crowd(
MODEL, LE_STATION, LE_DEST,
req.line, req.station, req.destination,
req.direction, req.hour, req.day_of_week
)
risk_info = get_risk_info(result["crowd_level"])
reasons = build_reasoning(
req.line, req.station, req.destination,
req.direction, req.hour, req.day_of_week,
result["crowd_level"]
)
tip = get_travel_tip(result["crowd_level"], req.direction, req.hour)
return {
"crowd_level": result["crowd_level"],
"crowd_code": result["crowd_code"],
"confidence": result["confidence"],
"probabilities": result["probabilities"],
"risk_score": risk_info["score"],
"risk_color": risk_info["color"],
"risk_bg": risk_info["bg"],
"risk_emoji": risk_info["emoji"],
"risk_badge": risk_info["badge"],
"advice": risk_info["advice"],
"travel_tip": tip,
"reasons": reasons,
"input": {
"line": req.line,
"station": req.station,
"destination": req.destination,
"direction": req.direction,
"hour": req.hour,
"day": req.day_of_week
}
}
@app.post("/api/hourly-forecast")
async def hourly_forecast(req: HourlyRequest):
if MODEL is None:
raise HTTPException(status_code=503, detail="Model not loaded yet")
results = get_best_hours(
MODEL, LE_STATION, LE_DEST,
req.line, req.station, req.destination,
req.direction, req.day_of_week,
predict_crowd
)
# Enrich with colors
for r in results:
info = RISK_CONFIG[r["crowd_level"]]
r["color"] = info["color"]
r["bg"] = info["bg"]
r["emoji"] = info["emoji"]
r["score"] = info["score"]
best = sorted(results, key=lambda x: x["risk_score"])[:3]
return {
"hourly": results,
"best_hours": best,
"peak_hour": max(results, key=lambda x: x["risk_score"])
}
@app.get("/api/stats")
async def get_stats():
if DF is None:
raise HTTPException(status_code=503, detail="Data not loaded")
total = len(DF)
by_line = DF.groupby("line").size().to_dict()
by_crowd = DF.groupby("crowd_level").size().to_dict()
return {
"total_records": total,
"by_line": by_line,
"by_crowd": by_crowd,
"model_accuracy": round(MODEL_ACCURACY * 100, 2) if MODEL_ACCURACY else None,
"lines": list(STATIONS_BY_LINE.keys()),
"total_stations": sum(len(v) for v in STATIONS_BY_LINE.values())
}
@app.get("/api/heatmap/{line}")
async def get_heatmap(line: str, direction: str = "Southbound", day: str = "Monday"):
if MODEL is None or line not in STATIONS_BY_LINE:
raise HTTPException(status_code=404)
stations = STATIONS_BY_LINE[line]
hours = list(range(5, 23))
heatmap = []
for station in stations[:6]: # Limit for performance
row = {"station": station, "hours": {}}
for h in hours:
dest = stations[-1] if station != stations[-1] else stations[0]
try:
pred = predict_crowd(MODEL, LE_STATION, LE_DEST, line, station, dest, direction, h, day)
row["hours"][h] = {
"level": pred["crowd_level"],
"score": RISK_CONFIG[pred["crowd_level"]]["score"],
"color": RISK_CONFIG[pred["crowd_level"]]["color"]
}
except Exception:
row["hours"][h] = {"level": "Low", "score": 15, "color": "#10b981"}
heatmap.append(row)
return {"heatmap": heatmap, "hours": hours, "stations": stations[:6]}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)