samithcs commited on
Commit
e6590aa
·
verified ·
1 Parent(s): 50933c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -69
app.py CHANGED
@@ -4,24 +4,103 @@ import pandas as pd
4
  from datetime import datetime
5
  from typing import Literal, Annotated
6
  from pydantic import BaseModel, Field
7
- from huggingface_hub import hf_hub_download
8
- import warnings
9
- from sklearn.exceptions import InconsistentVersionWarning
10
 
11
- warnings.filterwarnings("ignore", category=InconsistentVersionWarning)
12
 
13
- # ===============================
14
- # Hugging Face model config
15
- # ===============================
 
16
  HF_REPO = "samithcs/heart-rate-models"
17
  HEART_MODEL_FILENAME = "Heart_Rate_Predictor_model.joblib"
18
  ANOMALY_MODEL_FILENAME = "Anomaly_Detector_model.joblib"
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # ===============================
21
- # Load models directly from HF
22
  # ===============================
23
- HEART_MODEL_PATH = hf_hub_download(repo_id=HF_REPO, filename=HEART_MODEL_FILENAME)
24
- ANOMALY_MODEL_PATH = hf_hub_download(repo_id=HF_REPO, filename=ANOMALY_MODEL_FILENAME)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  heart_model_artifacts = joblib.load(HEART_MODEL_PATH)
27
  heart_model = heart_model_artifacts['model']
@@ -32,7 +111,7 @@ anomaly_model = anomaly_model_artifacts['model']
32
  anomaly_features = anomaly_model_artifacts['feature_columns']
33
 
34
  # ===============================
35
- # FastAPI app
36
  # ===============================
37
  app = FastAPI(title="Health Monitoring API")
38
 
@@ -41,68 +120,36 @@ def home():
41
  return {"message": "Health Monitoring API is running!"}
42
 
43
  # ===============================
44
- # Request schemas
45
- # ===============================
46
- class HeartRateInput(BaseModel):
47
- age: Annotated[int, Field(..., gt=0, lt=120)]
48
- gender: Annotated[Literal['M', 'F'], Field(...)]
49
- weight_kg: Annotated[float, Field(..., gt=0)]
50
- height_cm: Annotated[float, Field(..., gt=0, lt=250)]
51
- bmi: Annotated[float, Field(..., gt=0, lt=100)]
52
- fitness_level: Annotated[Literal['lightly_active','fairly_active','sedentary','very_active'], Field(...)]
53
- performance_level: Annotated[Literal['low','moderate','high'], Field(...)]
54
- resting_hr: Annotated[int, Field(..., gt=0, lt=120)]
55
- max_hr: Annotated[int, Field(..., gt=0, lt=220)]
56
- activity_type: Annotated[Literal['sleeping','walking','resting','light','commuting','exercise'], Field(...)]
57
- activity_intensity: Annotated[float, Field(..., gt=0.0)]
58
- steps_5min: Annotated[int, Field(..., gt=0)]
59
- calories_5min: Annotated[float, Field(..., gt=0)]
60
- hrv_rmssd: Annotated[float, Field(..., gt=0)]
61
- stress_score: Annotated[int, Field(..., gt=0, lt=100)]
62
- signal_quality: Annotated[float, Field(..., gt=0)]
63
- skin_temperature: Annotated[float, Field(..., gt=0)]
64
- device_battery: Annotated[int, Field(..., gt=0)]
65
- elevation_gain: Annotated[int, Field(..., ge=0)]
66
- sleep_stage: Annotated[Literal['light_sleep','deep_sleep','rem_sleep'], Field(...)]
67
- date: Annotated[datetime, Field(...)]
68
-
69
- class AnomalyInput(BaseModel):
70
- heart_rate: Annotated[float, Field(..., gt=0.0)]
71
- resting_hr_baseline: Annotated[int, Field(..., gt=0, lt=120)]
72
- activity_type: Annotated[Literal['sleeping','walking','resting','light','commuting','exercise'], Field(...)]
73
- activity_intensity: Annotated[float, Field(..., gt=0)]
74
- steps_5min: Annotated[int, Field(..., gt=0)]
75
- calories_5min: Annotated[float, Field(..., gt=0)]
76
- hrv_rmssd: Annotated[float, Field(..., gt=0)]
77
- stress_score: Annotated[int, Field(..., gt=0, lt=100)]
78
- confidence_score: Annotated[float, Field(..., gt=0.0)]
79
- signal_quality: Annotated[float, Field(..., gt=0)]
80
- skin_temperature: Annotated[float, Field(..., gt=0)]
81
- device_battery: Annotated[int, Field(..., gt=0)]
82
- elevation_gain: Annotated[int, Field(..., ge=0)]
83
- sleep_stage: Annotated[Literal['light_sleep','deep_sleep','rem_sleep'], Field(...)]
84
- date: Annotated[datetime, Field(...)]
85
-
86
- # ===============================
87
- # Utility functions
88
  # ===============================
89
  def preprocess_heart_features(data_dict: dict) -> pd.DataFrame:
 
90
  data_dict['date_encoded'] = data_dict['date'].timestamp()
91
- data_dict['gender_M'] = 1 if data_dict['gender']=='M' else 0
92
- data_dict['gender_F'] = 1 if data_dict['gender']=='F' else 0
93
- for act in ['sleeping','walking','resting','light','commuting','exercise']:
94
- data_dict[f"activity_type_{act}"] = 1 if data_dict['activity_type']==act else 0
95
- for stage in ['light_sleep','deep_sleep','rem_sleep']:
96
- data_dict[f"sleep_stage_{stage}"] = 1 if data_dict['sleep_stage']==stage else 0
97
- return pd.DataFrame([{f: data_dict.get(f,0) for f in heart_features}])
 
 
 
 
 
 
 
98
 
99
  def preprocess_anomaly_features(data_dict: dict) -> pd.DataFrame:
100
  data_dict['date_encoded'] = data_dict['date'].timestamp()
101
- for act in ['sleeping','walking','resting','light','commuting','exercise']:
102
- data_dict[f"activity_type_{act}"] = 1 if data_dict['activity_type']==act else 0
103
- for stage in ['light_sleep','deep_sleep','rem_sleep']:
104
- data_dict[f"sleep_stage_{stage}"] = 1 if data_dict['sleep_stage']==stage else 0
105
- return pd.DataFrame([{f: data_dict.get(f,0) for f in anomaly_features}])
 
 
 
106
 
107
  # ===============================
108
  # Endpoints
@@ -110,16 +157,19 @@ def preprocess_anomaly_features(data_dict: dict) -> pd.DataFrame:
110
  @app.post("/predict_heart_rate")
111
  def predict_heart_rate(input_data: HeartRateInput):
112
  try:
113
- X = preprocess_heart_features(input_data.model_dump())
 
114
  prediction = heart_model.predict(X)[0]
115
  return {"heart_rate_prediction": float(prediction)}
116
  except Exception as e:
117
  return {"error": str(e)}
118
 
 
119
  @app.post("/detect_anomaly")
120
  def detect_anomaly(input_data: AnomalyInput):
121
  try:
122
- X = preprocess_anomaly_features(input_data.model_dump())
 
123
  prediction = anomaly_model.predict(X)[0]
124
  return {"anomaly_detected": bool(prediction)}
125
  except Exception as e:
 
4
  from datetime import datetime
5
  from typing import Literal, Annotated
6
  from pydantic import BaseModel, Field
 
 
 
7
 
 
8
 
9
+
10
+ import os
11
+ import requests
12
+
13
  HF_REPO = "samithcs/heart-rate-models"
14
  HEART_MODEL_FILENAME = "Heart_Rate_Predictor_model.joblib"
15
  ANOMALY_MODEL_FILENAME = "Anomaly_Detector_model.joblib"
16
 
17
+
18
+ MODEL_DIR = os.path.join("artifacts", "model_trainer")
19
+ os.makedirs(MODEL_DIR, exist_ok=True)
20
+
21
+ def download_from_hf(filename):
22
+ local_path = os.path.join(MODEL_DIR, filename)
23
+
24
+
25
+ if os.path.exists(local_path):
26
+ print(f"✅ {filename} already exists at {local_path}")
27
+ return local_path
28
+
29
+
30
+ url = f"https://huggingface.co/{HF_REPO}/resolve/main/{filename}"
31
+ print(f"⬇️ Downloading {filename} from {url} ...")
32
+ with requests.get(url, stream=True) as r:
33
+ r.raise_for_status()
34
+ with open(local_path, "wb") as f:
35
+ for chunk in r.iter_content(chunk_size=8192):
36
+ f.write(chunk)
37
+ print(f"✅ Downloaded {filename} to {local_path}")
38
+ return local_path
39
+
40
+
41
+
42
+ download_from_hf(HEART_MODEL_FILENAME)
43
+ download_from_hf(ANOMALY_MODEL_FILENAME)
44
+
45
+
46
+
47
+
48
+
49
  # ===============================
50
+ # Define request schemas
51
  # ===============================
52
+
53
+ class HeartRateInput(BaseModel):
54
+ age: Annotated[int, Field(..., gt=0, lt=120, description="The age of the user")]
55
+ gender: Annotated[Literal['M', 'F'], Field(..., description="Gender of the user")]
56
+ weight_kg: Annotated[float, Field(..., gt=0, description='Weight of the user')]
57
+ height_cm: Annotated[float, Field(..., gt=0, lt=250, description='Height of the user')]
58
+ bmi: Annotated[float, Field(..., gt=0, lt=100, description='BMI of the user')]
59
+ fitness_level: Annotated[Literal['lightly_active', 'fairly_active', 'sedentary', 'very_active'], Field(..., description="Fitness level")]
60
+ performance_level: Annotated[Literal['low', 'moderate', 'high'], Field(..., description="Performance level")]
61
+ resting_hr: Annotated[int, Field(..., gt=0, lt=120, description="Resting HR")]
62
+ max_hr: Annotated[int, Field(..., gt=0, lt=220, description="Max HR")]
63
+ activity_type: Annotated[Literal['sleeping', 'walking', 'resting', 'light', 'commuting', 'exercise'], Field(..., description="Activity type")]
64
+ activity_intensity: Annotated[float, Field(..., gt=0.0, description="Activity intensity")]
65
+ steps_5min: Annotated[int, Field(..., gt=0, description="Steps in 5 min")]
66
+ calories_5min: Annotated[float, Field(..., gt=0, description="Calories in 5 min")]
67
+ hrv_rmssd: Annotated[float, Field(..., gt=0, description="Heart rate variability RMSSD")]
68
+ stress_score: Annotated[int, Field(..., gt=0, lt=100, description="Stress score")]
69
+ signal_quality: Annotated[float, Field(..., gt=0, description="Signal quality")]
70
+ skin_temperature: Annotated[float, Field(..., gt=0, description="Skin temperature")]
71
+ device_battery: Annotated[int, Field(..., gt=0, description="Device battery")]
72
+ elevation_gain: Annotated[int, Field(..., ge=0, description="Elevation gain")]
73
+ sleep_stage: Annotated[Literal['light_sleep', 'deep_sleep', 'rem_sleep'], Field(..., description="Sleep stage")]
74
+ date: Annotated[datetime, Field(..., description="Timestamp")]
75
+
76
+
77
+ class AnomalyInput(BaseModel):
78
+ heart_rate: Annotated[float, Field(..., gt=0.0, description="Heart rate")]
79
+ resting_hr_baseline: Annotated[int, Field(..., gt=0, lt=120, description="Resting HR baseline")]
80
+ activity_type: Annotated[Literal['sleeping', 'walking', 'resting', 'light', 'commuting', 'exercise'], Field(..., description="Activity type")]
81
+ activity_intensity: Annotated[float, Field(..., gt=0, description="Activity intensity")]
82
+ steps_5min: Annotated[int, Field(..., gt=0, description="Steps in 5 min")]
83
+ calories_5min: Annotated[float, Field(..., gt=0, description="Calories in 5 min")]
84
+ hrv_rmssd: Annotated[float, Field(..., gt=0, description="Heart rate variability RMSSD")]
85
+ stress_score: Annotated[int, Field(..., gt=0, lt=100, description="Stress score")]
86
+ confidence_score: Annotated[float, Field(..., gt=0.0, description="Confidence score")]
87
+ signal_quality: Annotated[float, Field(..., gt=0, description="Signal quality")]
88
+ skin_temperature: Annotated[float, Field(..., gt=0, description="Skin temperature")]
89
+ device_battery: Annotated[int, Field(..., gt=0, description="Device battery")]
90
+ elevation_gain: Annotated[int, Field(..., ge=0, description="Elevation gain")]
91
+ sleep_stage: Annotated[Literal['light_sleep', 'deep_sleep', 'rem_sleep'], Field(..., description="Sleep stage")]
92
+ date: Annotated[datetime, Field(..., description="Timestamp")]
93
+
94
+ # ===============================
95
+ # Load models
96
+ # ===============================
97
+
98
+ MODEL_DIR = os.path.join("artifacts", "model_trainer")
99
+
100
+
101
+ HEART_MODEL_PATH = os.path.join(MODEL_DIR, "Heart_Rate_Predictor_model.joblib")
102
+ ANOMALY_MODEL_PATH = os.path.join(MODEL_DIR, "Anomaly_Detector_model.joblib")
103
+
104
 
105
  heart_model_artifacts = joblib.load(HEART_MODEL_PATH)
106
  heart_model = heart_model_artifacts['model']
 
111
  anomaly_features = anomaly_model_artifacts['feature_columns']
112
 
113
  # ===============================
114
+ # Create FastAPI app
115
  # ===============================
116
  app = FastAPI(title="Health Monitoring API")
117
 
 
120
  return {"message": "Health Monitoring API is running!"}
121
 
122
  # ===============================
123
+ # Utility: preprocess features
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  # ===============================
125
  def preprocess_heart_features(data_dict: dict) -> pd.DataFrame:
126
+ # Encode datetime
127
  data_dict['date_encoded'] = data_dict['date'].timestamp()
128
+
129
+ # One-hot categorical encodings
130
+ data_dict['gender_M'] = 1 if data_dict['gender'] == 'M' else 0
131
+ data_dict['gender_F'] = 1 if data_dict['gender'] == 'F' else 0
132
+
133
+ for act in ['sleeping', 'walking', 'resting', 'light', 'commuting', 'exercise']:
134
+ data_dict[f"activity_type_{act}"] = 1 if data_dict['activity_type'] == act else 0
135
+
136
+ for stage in ['light_sleep', 'deep_sleep', 'rem_sleep']:
137
+ data_dict[f"sleep_stage_{stage}"] = 1 if data_dict['sleep_stage'] == stage else 0
138
+
139
+ # Restrict to model features only
140
+ return pd.DataFrame([{f: data_dict.get(f, 0) for f in heart_features}])
141
+
142
 
143
  def preprocess_anomaly_features(data_dict: dict) -> pd.DataFrame:
144
  data_dict['date_encoded'] = data_dict['date'].timestamp()
145
+
146
+ for act in ['sleeping', 'walking', 'resting', 'light', 'commuting', 'exercise']:
147
+ data_dict[f"activity_type_{act}"] = 1 if data_dict['activity_type'] == act else 0
148
+
149
+ for stage in ['light_sleep', 'deep_sleep', 'rem_sleep']:
150
+ data_dict[f"sleep_stage_{stage}"] = 1 if data_dict['sleep_stage'] == stage else 0
151
+
152
+ return pd.DataFrame([{f: data_dict.get(f, 0) for f in anomaly_features}])
153
 
154
  # ===============================
155
  # Endpoints
 
157
  @app.post("/predict_heart_rate")
158
  def predict_heart_rate(input_data: HeartRateInput):
159
  try:
160
+ data_dict = input_data.model_dump()
161
+ X = preprocess_heart_features(data_dict)
162
  prediction = heart_model.predict(X)[0]
163
  return {"heart_rate_prediction": float(prediction)}
164
  except Exception as e:
165
  return {"error": str(e)}
166
 
167
+
168
  @app.post("/detect_anomaly")
169
  def detect_anomaly(input_data: AnomalyInput):
170
  try:
171
+ data_dict = input_data.model_dump()
172
+ X = preprocess_anomaly_features(data_dict)
173
  prediction = anomaly_model.predict(X)[0]
174
  return {"anomaly_detected": bool(prediction)}
175
  except Exception as e: