Rizwan9 commited on
Commit
e2f1a0e
·
verified ·
1 Parent(s): 27022b1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -20
app.py CHANGED
@@ -1,32 +1,123 @@
1
-
2
- from flask import Flask, request, jsonify
3
- import joblib
4
- import pandas as pd
5
- import os
6
 
7
  app = Flask(__name__)
8
 
9
  MODEL_PATH = os.getenv("MODEL_PATH", "best_model_random_forest.joblib")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- if not os.path.exists(MODEL_PATH):
12
- raise FileNotFoundError(f"Model file not found: {MODEL_PATH}. Please ensure it's uploaded to the Space.")
 
 
 
 
13
 
14
- model_bundle = joblib.load(MODEL_PATH)
15
- if isinstance(model_bundle, dict) and "pipeline" in model_bundle:
16
- pipe = model_bundle["pipeline"]
17
- else:
18
- pipe = model_bundle
 
 
 
 
19
 
20
- @app.route("/health", methods=["GET"])
21
  def health():
22
- return jsonify({"status": "ok", "model": MODEL_PATH})
 
23
 
24
- @app.route("/predict", methods=["POST"])
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def predict():
26
- data = request.get_json(force=True)
27
- df = pd.DataFrame([data]) if isinstance(data, dict) else pd.DataFrame(data)
28
- preds = pipe.predict(df)
29
- return jsonify({"predictions": [float(p) for p in preds]})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  if __name__ == "__main__":
32
- app.run(host="0.0.0.0", port=5000)
 
 
1
+ from flask import Flask, request, jsonify, make_response
2
+ import joblib, pandas as pd, numpy as np, os, sys, time, traceback
3
+ from sklearn.pipeline import Pipeline
 
 
4
 
5
  app = Flask(__name__)
6
 
7
  MODEL_PATH = os.getenv("MODEL_PATH", "best_model_random_forest.joblib")
8
+ PORT = int(os.getenv("PORT", "7860"))
9
+
10
+ print(f"==> [BOOT] Starting Flask app on port {PORT}")
11
+ print(f"==> [BOOT] MODEL_PATH={MODEL_PATH}", flush=True)
12
+
13
+ def load_pipeline(path: str):
14
+ t0 = time.time()
15
+ if not os.path.exists(path):
16
+ raise FileNotFoundError(f"Model file not found: {path}")
17
+ print(f"==> [LOAD] Loading model from {path} ...", flush=True)
18
+ obj = joblib.load(path)
19
+ if isinstance(obj, dict) and "pipeline" in obj:
20
+ pipe = obj["pipeline"]
21
+ print("==> [LOAD] Loaded dict bundle with 'pipeline'", flush=True)
22
+ else:
23
+ pipe = obj
24
+ print("==> [LOAD] Loaded pipeline object (no bundle key)", flush=True)
25
+ print(f"==> [LOAD] Done in {time.time()-t0:.2f}s", flush=True)
26
+ return pipe
27
+
28
+ try:
29
+ pipe = load_pipeline(MODEL_PATH)
30
+ MODEL_READY, LOAD_ERROR = True, None
31
+ except Exception as e:
32
+ pipe, MODEL_READY, LOAD_ERROR = None, False, str(e)
33
+ print("==> [ERROR] Model load failed:", LOAD_ERROR, file=sys.stderr, flush=True)
34
+
35
+ def sanitize_inputs(df: pd.DataFrame) -> pd.DataFrame:
36
+ df = df.copy()
37
+ for col in df.select_dtypes(include="object").columns:
38
+ df[col] = df[col].astype(str).str.strip().str.title()
39
+ for col in df.columns:
40
+ if df[col].dtype.kind in "biufc":
41
+ df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0)
42
+ return df
43
+
44
+ def expected_feature_names():
45
+ names = getattr(pipe, "feature_names_in_", None)
46
+ if names is not None:
47
+ return list(names)
48
+ if isinstance(pipe, Pipeline):
49
+ first = pipe.steps[0][1]
50
+ names = getattr(first, "feature_names_in_", None)
51
+ if names is not None:
52
+ return list(names)
53
+ return None
54
 
55
+ @app.after_request
56
+ def add_cors_headers(resp):
57
+ resp.headers["Access-Control-Allow-Origin"] = "*"
58
+ resp.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
59
+ resp.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
60
+ return resp
61
 
62
+ @app.get("/")
63
+ def root():
64
+ return jsonify({
65
+ "service": "SuperKart Sales Forecast API",
66
+ "health": "/health",
67
+ "predict": "/predict",
68
+ "schema": "/schema",
69
+ "model_path": MODEL_PATH
70
+ })
71
 
72
+ @app.get("/health")
73
  def health():
74
+ return (jsonify({"status": "ok", "model_path": MODEL_PATH}), 200) if MODEL_READY \
75
+ else (jsonify({"status": "error", "error": LOAD_ERROR, "model_path": MODEL_PATH}), 500)
76
 
77
+ @app.get("/schema")
78
+ def schema():
79
+ return jsonify({
80
+ "model_ready": MODEL_READY,
81
+ "model_path": MODEL_PATH,
82
+ "estimator_type": type(pipe).__name__ if pipe is not None else None,
83
+ "expected_feature_names": expected_feature_names()
84
+ }), 200 if MODEL_READY else 500
85
+
86
+ @app.route("/predict", methods=["OPTIONS"])
87
+ def predict_options():
88
+ return make_response(("", 204))
89
+
90
+ @app.post("/predict")
91
  def predict():
92
+ if not MODEL_READY or pipe is None:
93
+ return jsonify({"error": "Model not loaded", "details": LOAD_ERROR}), 503
94
+ try:
95
+ payload = request.get_json(force=True)
96
+ if payload is None:
97
+ return jsonify({"error": "No JSON received"}), 400
98
+
99
+ df = pd.DataFrame([payload]) if isinstance(payload, dict) else pd.DataFrame(payload)
100
+ df = sanitize_inputs(df)
101
+
102
+ expected = expected_feature_names()
103
+ if expected:
104
+ missing = [c for c in expected if c not in df.columns]
105
+ if missing:
106
+ return jsonify({
107
+ "error": "Missing required columns",
108
+ "missing": missing,
109
+ "expected": expected,
110
+ "received": list(df.columns)
111
+ }), 400
112
+ df = df[expected]
113
+
114
+ preds = pipe.predict(df)
115
+ preds = [float(p) if isinstance(p, (np.floating, float, int)) else p for p in preds]
116
+ return jsonify({"predictions": preds, "rows_received": len(df)})
117
+ except Exception as e:
118
+ print("==> [ERROR] Prediction failed:\n", traceback.format_exc(), flush=True)
119
+ return jsonify({"error": "Prediction failed", "details": str(e)}), 500
120
 
121
  if __name__ == "__main__":
122
+ print("==> [RUN] Flask dev server starting...", flush=True)
123
+ app.run(host="0.0.0.0", port=PORT)