COCODEDE04 commited on
Commit
dbb37d9
·
verified ·
1 Parent(s): b081063

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -30
app.py CHANGED
@@ -19,12 +19,56 @@ model = tf.keras.models.load_model(MODEL_PATH, compile=False)
19
  with open(STATS_PATH, "r") as f:
20
  stats: Dict[str, Dict[str, float]] = json.load(f)
21
 
 
22
  FEATURES = list(stats.keys())
23
  print("Feature order:", FEATURES)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def _z(val: Any, mean: float, sd: float) -> float:
26
  try:
27
- v = float(val)
28
  except Exception:
29
  return 0.0
30
  if not sd:
@@ -54,10 +98,7 @@ app.add_middleware(
54
 
55
  @app.get("/")
56
  def root():
57
- return {
58
- "message": "Static Fingerprint API is running.",
59
- "try": ["GET /health", "POST /predict"],
60
- }
61
 
62
  @app.get("/health")
63
  def health():
@@ -69,6 +110,11 @@ def health():
69
  "stats_file": STATS_PATH,
70
  }
71
 
 
 
 
 
 
72
  @app.post("/predict")
73
  async def predict(req: Request):
74
  """
@@ -89,14 +135,15 @@ async def predict(req: Request):
89
  z_detail = {}
90
  missing = []
91
  for f in FEATURES:
92
- val = payload.get(f, 0)
93
  mean = stats[f]["mean"]
94
  sd = stats[f]["std"]
95
- zf = _z(val, mean, sd)
 
 
 
 
96
  z.append(zf)
97
  z_detail[f] = zf
98
- if f not in payload:
99
- missing.append(f)
100
 
101
  X = np.array([z], dtype=np.float32)
102
  raw = model.predict(X, verbose=0)
@@ -106,7 +153,6 @@ async def predict(req: Request):
106
  probs = coral_probs_from_logits(raw)[0]
107
  else:
108
  probs = raw[0]
109
- # If it's not normalized, normalize defensively:
110
  s = float(np.sum(probs))
111
  if s > 0:
112
  probs = probs / s
@@ -118,23 +164,4 @@ async def predict(req: Request):
118
  "z_scores": z_detail,
119
  "probabilities": {CLASSES[i]: float(probs[i]) for i in range(len(CLASSES))},
120
  "predicted_state": CLASSES[pred_idx],
121
- }
122
-
123
- # ---- add to app.py ----
124
- from fastapi import FastAPI, HTTPException, Request
125
- from fastapi.responses import JSONResponse
126
- import traceback
127
-
128
- @app.post("/echo")
129
- async def echo(payload: dict):
130
- return {"received": payload}
131
-
132
- @app.post("/predict")
133
- async def predict(payload: dict):
134
- try:
135
- return predict_from_json(payload) # your existing function
136
- except Exception as e:
137
- tb = traceback.format_exc()
138
- print("PREDICT ERROR:", tb)
139
- # Return 400 with details so Excel can read it
140
- return JSONResponse(status_code=400, content={"error": str(e)})
 
19
  with open(STATS_PATH, "r") as f:
20
  stats: Dict[str, Dict[str, float]] = json.load(f)
21
 
22
+ # IMPORTANT: FEATURES order must match training!
23
  FEATURES = list(stats.keys())
24
  print("Feature order:", FEATURES)
25
 
26
+ # ---------- robust numeric coercion ----------
27
+ def coerce_float(val: Any) -> float:
28
+ """
29
+ Accepts numeric, or strings like:
30
+ "49.709,14" -> 49709.14
31
+ "49,709.14" -> 49709.14
32
+ "0,005" -> 0.005
33
+ " 1 234 " -> 1234
34
+ Returns float, or raises ValueError if impossible.
35
+ """
36
+ if isinstance(val, (int, float)):
37
+ return float(val)
38
+
39
+ s = str(val).strip()
40
+ if s == "":
41
+ raise ValueError("empty")
42
+
43
+ # remove spaces
44
+ s = s.replace(" ", "")
45
+
46
+ has_dot = "." in s
47
+ has_comma = "," in s
48
+
49
+ if has_dot and has_comma:
50
+ # Decide which is decimal separator by last occurrence
51
+ last_dot = s.rfind(".")
52
+ last_comma = s.rfind(",")
53
+ if last_comma > last_dot:
54
+ # decimal is comma, thousands is dot
55
+ s = s.replace(".", "")
56
+ s = s.replace(",", ".")
57
+ else:
58
+ # decimal is dot, thousands is comma
59
+ s = s.replace(",", "")
60
+ elif has_comma and not has_dot:
61
+ # likely decimal is comma
62
+ s = s.replace(",", ".")
63
+ else:
64
+ # dots only or pure digits -> leave as is
65
+ pass
66
+
67
+ return float(s)
68
+
69
  def _z(val: Any, mean: float, sd: float) -> float:
70
  try:
71
+ v = coerce_float(val)
72
  except Exception:
73
  return 0.0
74
  if not sd:
 
98
 
99
  @app.get("/")
100
  def root():
101
+ return {"message": "Static Fingerprint API is running.", "try": ["GET /health", "POST /predict"]}
 
 
 
102
 
103
  @app.get("/health")
104
  def health():
 
110
  "stats_file": STATS_PATH,
111
  }
112
 
113
+ @app.post("/echo")
114
+ async def echo(req: Request):
115
+ payload = await req.json()
116
+ return {"received": payload}
117
+
118
  @app.post("/predict")
119
  async def predict(req: Request):
120
  """
 
135
  z_detail = {}
136
  missing = []
137
  for f in FEATURES:
 
138
  mean = stats[f]["mean"]
139
  sd = stats[f]["std"]
140
+ if f in payload:
141
+ zf = _z(payload[f], mean, sd)
142
+ else:
143
+ missing.append(f)
144
+ zf = _z(0.0, mean, sd) # treat missing as 0 input
145
  z.append(zf)
146
  z_detail[f] = zf
 
 
147
 
148
  X = np.array([z], dtype=np.float32)
149
  raw = model.predict(X, verbose=0)
 
153
  probs = coral_probs_from_logits(raw)[0]
154
  else:
155
  probs = raw[0]
 
156
  s = float(np.sum(probs))
157
  if s > 0:
158
  probs = probs / s
 
164
  "z_scores": z_detail,
165
  "probabilities": {CLASSES[i]: float(probs[i]) for i in range(len(CLASSES))},
166
  "predicted_state": CLASSES[pred_idx],
167
+ }