Rizwan9 commited on
Commit
f981030
·
verified ·
1 Parent(s): a272c83

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -84
app.py CHANGED
@@ -1,118 +1,74 @@
1
  from flask import Flask, request, jsonify, make_response
2
- import joblib
3
- import pandas as pd
4
- import numpy as np
5
- import os
6
- from typing import Union, List, Dict
7
 
 
8
  app = Flask(__name__)
9
 
10
- # -----------------------------
11
- # Config / Model Load
12
- # -----------------------------
13
- MODEL_PATH = os.getenv("MODEL_PATH", "best_model_random_forest.joblib")
14
  PORT = int(os.getenv("PORT", "5000"))
15
 
 
 
16
  def load_pipeline(path: str):
 
17
  if not os.path.exists(path):
18
- raise FileNotFoundError(
19
- f"Model file not found: {path}. "
20
- "Ensure the artifact exists in the Space root and Dockerfile COPY line matches."
21
- )
22
  bundle = joblib.load(path)
23
- # Support both a dict bundle {"pipeline": pipe} and a bare fitted pipeline
24
- return bundle["pipeline"] if isinstance(bundle, dict) and "pipeline" in bundle else bundle
 
 
 
 
 
 
25
 
26
  try:
27
  pipe = load_pipeline(MODEL_PATH)
28
  MODEL_READY = True
29
  LOAD_ERROR = None
30
  except Exception as e:
 
31
  pipe = None
32
  MODEL_READY = False
33
  LOAD_ERROR = str(e)
34
 
35
- # -----------------------------
36
- # CORS (no external dependency)
37
- # -----------------------------
38
  @app.after_request
39
- def add_cors_headers(response):
40
- # Allow Streamlit frontend from a different domain to call this API
41
- response.headers["Access-Control-Allow-Origin"] = "*"
42
- response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
43
- response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
44
- return response
45
 
46
- @app.route("/", methods=["GET"])
47
  def root():
48
- return jsonify({
49
- "service": "SuperKart Sales Forecast API",
50
- "health": "/health",
51
- "predict": "/predict",
52
- "model_path": MODEL_PATH
53
- })
54
 
55
- @app.route("/health", methods=["GET"])
56
  def health():
57
- status = "ok" if MODEL_READY else "error"
58
- payload = {"status": status, "model_path": MODEL_PATH}
59
- if LOAD_ERROR:
60
- payload["error"] = LOAD_ERROR
61
- return jsonify(payload), (200 if MODEL_READY else 500)
62
 
63
  @app.route("/predict", methods=["OPTIONS"])
64
  def predict_options():
65
- # Handle CORS preflight cleanly
66
  return make_response(("", 204))
67
 
68
- # -----------------------------
69
- # Helpers
70
- # -----------------------------
71
- def _to_frame(payload: Union[Dict, List[Dict]]) -> pd.DataFrame:
72
- # Accept a single dict or a list of dicts
73
- if isinstance(payload, dict):
74
- return pd.DataFrame([payload])
75
- if isinstance(payload, list):
76
- if not all(isinstance(x, dict) for x in payload):
77
- raise ValueError("If sending a list, it must be a list of JSON objects.")
78
- return pd.DataFrame(payload)
79
- raise ValueError("Input JSON must be an object or a list of objects.")
80
-
81
- def _to_pyfloat(x):
82
- # Ensure JSON-serializable floats
83
- if isinstance(x, (np.floating,)):
84
- return float(x)
85
- return float(x)
86
-
87
- # -----------------------------
88
- # Prediction
89
- # -----------------------------
90
- @app.route("/predict", methods=["POST"])
91
  def predict():
92
  if not MODEL_READY or pipe is None:
93
  return jsonify({"error": "Model not loaded", "details": LOAD_ERROR}), 503
 
 
 
 
 
 
 
 
 
 
94
 
95
- try:
96
- data = request.get_json(force=True, silent=False)
97
- if data is None:
98
- return jsonify({"error": "No JSON payload received"}), 400
99
-
100
- df = _to_frame(data)
101
-
102
- # Let the sklearn pipeline handle all preprocessing (encoders, etc.)
103
- preds = pipe.predict(df)
104
-
105
- # Cast to built-in float for clean JSON
106
- preds = [_to_pyfloat(p) for p in preds]
107
- return jsonify({"predictions": preds})
108
-
109
- except ValueError as ve:
110
- return jsonify({"error": "Invalid input", "details": str(ve)}), 400
111
- except Exception as e:
112
- return jsonify({"error": "Prediction failed", "details": str(e)}), 500
113
-
114
- # -----------------------------
115
- # Local dev entrypoint (prod uses gunicorn)
116
- # -----------------------------
117
  if __name__ == "__main__":
118
- app.run(host="0.0.0.0", port=PORT)
 
 
1
  from flask import Flask, request, jsonify, make_response
2
+ import joblib, pandas as pd, numpy as np, os, time, sys
 
 
 
 
3
 
4
+ print("==> [BOOT] Starting app.py", flush=True)
5
  app = Flask(__name__)
6
 
7
+ MODEL_PATH = os.getenv("MODEL_PATH", "superkart_sales_forecast.joblib")
 
 
 
8
  PORT = int(os.getenv("PORT", "5000"))
9
 
10
+ print(f"==> [BOOT] MODEL_PATH={MODEL_PATH}", flush=True)
11
+
12
  def load_pipeline(path: str):
13
+ t0 = time.time()
14
  if not os.path.exists(path):
15
+ raise FileNotFoundError(f"Model file not found: {path}")
16
+ print(f"==> [LOAD] Loading model from {path} ...", flush=True)
 
 
17
  bundle = joblib.load(path)
18
+ if isinstance(bundle, dict) and "pipeline" in bundle:
19
+ pipe = bundle["pipeline"]
20
+ print("==> [LOAD] Loaded dict bundle with 'pipeline'", flush=True)
21
+ else:
22
+ pipe = bundle
23
+ print("==> [LOAD] Loaded pipeline object", flush=True)
24
+ print(f"==> [LOAD] Done in {time.time()-t0:.2f}s", flush=True)
25
+ return pipe
26
 
27
  try:
28
  pipe = load_pipeline(MODEL_PATH)
29
  MODEL_READY = True
30
  LOAD_ERROR = None
31
  except Exception as e:
32
+ print("==> [ERROR] Model load failed:", e, file=sys.stderr, flush=True)
33
  pipe = None
34
  MODEL_READY = False
35
  LOAD_ERROR = str(e)
36
 
 
 
 
37
  @app.after_request
38
+ def cors(resp):
39
+ resp.headers["Access-Control-Allow-Origin"] = "*"
40
+ resp.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
41
+ resp.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
42
+ return resp
 
43
 
44
+ @app.get("/")
45
  def root():
46
+ return jsonify({"service": "SuperKart API", "health": "/health", "predict": "/predict", "model_path": MODEL_PATH})
 
 
 
 
 
47
 
48
+ @app.get("/health")
49
  def health():
50
+ return (jsonify({"status": "ok", "model_path": MODEL_PATH}), 200) if MODEL_READY \
51
+ else (jsonify({"status": "error", "model_path": MODEL_PATH, "error": LOAD_ERROR}), 500)
 
 
 
52
 
53
  @app.route("/predict", methods=["OPTIONS"])
54
  def predict_options():
 
55
  return make_response(("", 204))
56
 
57
+ @app.post("/predict")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  def predict():
59
  if not MODEL_READY or pipe is None:
60
  return jsonify({"error": "Model not loaded", "details": LOAD_ERROR}), 503
61
+ data = request.get_json(force=True)
62
+ if isinstance(data, dict):
63
+ df = pd.DataFrame([data])
64
+ elif isinstance(data, list):
65
+ df = pd.DataFrame(data)
66
+ else:
67
+ return jsonify({"error": "Payload must be an object or list of objects"}), 400
68
+ preds = pipe.predict(df)
69
+ preds = [float(x) if isinstance(x, (np.floating, float, int)) else x for x in preds]
70
+ return jsonify({"predictions": preds})
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  if __name__ == "__main__":
73
+ print("==> [RUN] Flask dev server starting on 0.0.0.0:5000", flush=True)
74
+ app.run(host="0.0.0.0", port=PORT)