varundevmishra09 commited on
Commit
6d9b502
·
verified ·
1 Parent(s): 6b370f6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -0
app.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ from fastapi import FastAPI, HTTPException
3
+ from pydantic import BaseModel
4
+ from typing import Dict, Any
5
+ import joblib, os, time, math
6
+ import numpy as np
7
+
8
+ APP_PORT = int(os.environ.get("PORT", 7860)) # HF maps to 7860
9
+
10
+ app = FastAPI(title="Transit Tracker API")
11
+
12
+ # In-memory store for vehicles
13
+ VEHICLES: Dict[str, Dict[str, Any]] = {}
14
+
15
+ MODEL = None
16
+ MODEL_PATH = "models/model.joblib"
17
+
18
+ # -- Utilities
19
+ def haversine(lat1, lon1, lat2, lon2):
20
+ # returns meters
21
+ R = 6371000
22
+ phi1, phi2 = math.radians(lat1), math.radians(lat2)
23
+ dphi = math.radians(lat2-lat1)
24
+ dlambda = math.radians(lon2-lon1)
25
+ a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2
26
+ return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
27
+
28
+ def load_model():
29
+ global MODEL
30
+ if MODEL is None:
31
+ if not os.path.exists(MODEL_PATH):
32
+ raise RuntimeError("Model file not found: " + MODEL_PATH)
33
+ MODEL = joblib.load(MODEL_PATH)
34
+ return MODEL
35
+
36
+ # -- Request models
37
+ class VehicleUpdate(BaseModel):
38
+ vehicle_id: str
39
+ lat: float
40
+ lon: float
41
+ speed: float = 0.0 # meters/sec
42
+ bearing: float = None
43
+ timestamp: str = None # ISO
44
+
45
+ class PredictRequest(BaseModel):
46
+ distance_to_stop_m: float
47
+ speed_mps: float = 3.0
48
+ timestamp_iso: str = None # optional
49
+
50
+ # -- Endpoints
51
+ @app.get("/health")
52
+ def health():
53
+ ok = os.path.exists(MODEL_PATH)
54
+ return {"status": "ok" if ok else "model-missing", "model_path": MODEL_PATH, "time": time.time()}
55
+
56
+ @app.post("/vehicle/update")
57
+ def vehicle_update(v: VehicleUpdate):
58
+ VEHICLES[v.vehicle_id] = {
59
+ "lat": v.lat,
60
+ "lon": v.lon,
61
+ "speed": v.speed,
62
+ "bearing": v.bearing,
63
+ "timestamp": v.timestamp or time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
64
+ }
65
+ return {"ok": True, "vehicle_id": v.vehicle_id}
66
+
67
+ @app.get("/vehicles")
68
+ def get_vehicles():
69
+ return VEHICLES
70
+
71
+ @app.post("/predict")
72
+ def predict(req: PredictRequest):
73
+ # safety checks
74
+ if req.distance_to_stop_m < 0:
75
+ raise HTTPException(status_code=400, detail="distance must be >= 0")
76
+
77
+ model = load_model()
78
+ # compute TOD features (simple UTC seconds)
79
+ if req.timestamp_iso:
80
+ try:
81
+ import datetime
82
+ dt = datetime.datetime.fromisoformat(req.timestamp_iso.replace("Z",""))
83
+ sec = dt.hour*3600 + dt.minute*60 + dt.second
84
+ dow = dt.weekday()
85
+ except:
86
+ sec = time.gmtime().tm_hour*3600
87
+ dow = time.gmtime().tm_wday
88
+ else:
89
+ sec = time.gmtime().tm_hour*3600 + time.gmtime().tm_min*60 + time.gmtime().tm_sec
90
+ dow = time.gmtime().tm_wday
91
+
92
+ tod_sin = np.sin(2*np.pi*sec/86400)
93
+ tod_cos = np.cos(2*np.pi*sec/86400)
94
+
95
+ X = [[float(req.distance_to_stop_m), float(req.speed_mps), float(tod_sin), float(tod_cos), int(dow)]]
96
+ eta_seconds = float(model.predict(X)[0])
97
+ return {"eta_seconds": eta_seconds, "eta_minutes": eta_seconds/60.0}