roshcheeku commited on
Commit
fab5e05
·
verified ·
1 Parent(s): 79a89c5

Upload 12 files

Browse files
Files changed (12) hide show
  1. .env +3 -0
  2. .gitattributes +35 -35
  3. .huggingface.yml +2 -0
  4. Dockerfile +16 -0
  5. Procfile +1 -0
  6. README.md +11 -11
  7. app.py +300 -0
  8. finaliseddiabetes_model.zip +3 -0
  9. finalisedscaler.zip +3 -0
  10. model_loader.py +110 -0
  11. nodiabetes.zip +3 -0
  12. requirements.txt +46 -0
.env ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ DIABETES_MODEL_URL=https://drive.google.com/uc?export=download&id=14H_wPtW4_W1XPFiiJ3tkmsFFACac_jxS
2
+ SCALER_MODEL_URL=https://drive.google.com/uc?export=download&id=1PnILhtH35yVwG1xfd0bNj7jBquX855bk
3
+ NO_DIABETES_MODEL_URL=https://drive.google.com/uc?export=download&id=1cnjaKDyR7AiCojKsm0rZYtQZSCiJVWW6
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.huggingface.yml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ sdk: gradio
2
+ app_file: app.py
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Copy dependencies first
7
+ COPY requirements.txt .
8
+
9
+ # Install dependencies
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ # Copy rest of the code
13
+ COPY . .
14
+
15
+ # Run the app
16
+ CMD ["python", "app.py"]
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ gunicorn -w 4 -k gthread app
README.md CHANGED
@@ -1,11 +1,11 @@
1
- ---
2
- title: Lolback
3
- emoji: 🐨
4
- colorFrom: pink
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Lolback
3
+ emoji: 🐨
4
+ colorFrom: pink
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ ---
10
+
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import joblib
4
+ import logging
5
+ import zipfile
6
+ import pandas as pd
7
+ import numpy as np
8
+ import warnings
9
+ from flask import Flask, request, jsonify
10
+ from flask_cors import CORS
11
+
12
+ # Suppress sklearn warnings
13
+ warnings.filterwarnings('ignore', category=UserWarning, module='sklearn')
14
+
15
+ # Configure logging
16
+ logging.basicConfig(level=logging.INFO)
17
+
18
+ # Get model URLs from environment variables
19
+ DIABETES_MODEL_URL = os.getenv("DIABETES_MODEL_URL")
20
+ SCALER_URL = os.getenv("SCALER_URL")
21
+ MULTI_MODEL_URL = os.getenv("MULTI_MODEL_URL")
22
+
23
+ # Local paths for downloaded models
24
+ MODEL_PATHS = {
25
+ "DIABETES_MODEL": "finaliseddiabetes_model.zip",
26
+ "SCALER": "finalisedscaler.zip",
27
+ "MULTI_MODEL": "nodiabetes.zip",
28
+ }
29
+
30
+ # Extracted model names
31
+ EXTRACTED_MODELS = {
32
+ "DIABETES_MODEL": "finaliseddiabetes_model.joblib",
33
+ "SCALER": "finalisedscaler.joblib",
34
+ "MULTI_MODEL": "nodiabetes.joblib",
35
+ }
36
+
37
+ BASE_DIR = os.getcwd()
38
+
39
+ # Flask app initialization
40
+ app = Flask(__name__)
41
+
42
+ # Enable CORS for all routes
43
+ CORS(app, resources={
44
+ r"/*": {
45
+ "origins": [
46
+ "http://localhost:3000",
47
+ "https://carelog-diabetes-api.onrender.com",
48
+ "https://carelog-diabetes.vercel.app",
49
+ "http://localhost:5000"
50
+ ],
51
+ "methods": ["GET", "POST", "OPTIONS"],
52
+ "allow_headers": ["Content-Type", "Authorization"],
53
+ "supports_credentials": True
54
+ }
55
+ })
56
+
57
+ def download_model(url, zip_filename):
58
+ """Downloads the model zip file from the given URL and saves it locally."""
59
+ zip_path = os.path.join(BASE_DIR, zip_filename)
60
+ if not url:
61
+ logging.error(f"URL for {zip_filename} is missing!")
62
+ return False
63
+ try:
64
+ response = requests.get(url, allow_redirects=True)
65
+ if response.status_code == 200:
66
+ with open(zip_path, 'wb') as f:
67
+ f.write(response.content)
68
+ logging.info(f"Downloaded {zip_filename} successfully.")
69
+ return True
70
+ else:
71
+ logging.error(f"Failed to download {zip_filename}. HTTP Status: {response.status_code}")
72
+ return False
73
+ except Exception as e:
74
+ logging.error(f"Error downloading {zip_filename}: {e}")
75
+ return False
76
+
77
+ def extract_if_needed(zip_filename, extracted_filename):
78
+ """Extracts model file from zip if not already extracted."""
79
+ zip_path = os.path.join(BASE_DIR, zip_filename)
80
+ extracted_path = os.path.join(BASE_DIR, extracted_filename)
81
+ if os.path.exists(extracted_path):
82
+ logging.info(f"{extracted_filename} already exists. Skipping extraction.")
83
+ return True
84
+ if not os.path.exists(zip_path):
85
+ logging.error(f"Zip file missing: {zip_path}")
86
+ return False
87
+ try:
88
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
89
+ zip_ref.extractall(BASE_DIR)
90
+ logging.info(f"Extracted {zip_filename}")
91
+ return True
92
+ except Exception as e:
93
+ logging.error(f"Error extracting {zip_filename}: {e}")
94
+ return False
95
+
96
+ def load_model(model_filename):
97
+ """Loads a model from the given filename."""
98
+ model_path = os.path.join(BASE_DIR, model_filename)
99
+ if not os.path.exists(model_path):
100
+ logging.error(f"Model file not found: {model_path}")
101
+ return None
102
+ try:
103
+ model = joblib.load(model_path)
104
+ logging.info(f"Loaded {model_filename} successfully.")
105
+ return model
106
+ except Exception as e:
107
+ logging.error(f"Error loading {model_filename}: {e}")
108
+ return None
109
+
110
+ def initialize_models():
111
+ """Handles downloading, extraction, and loading of models."""
112
+ models = {}
113
+ for model_key, zip_filename in MODEL_PATHS.items():
114
+ extracted_filename = EXTRACTED_MODELS[model_key]
115
+ if not os.path.exists(os.path.join(BASE_DIR, zip_filename)):
116
+ download_model(globals()[f"{model_key}_URL"], zip_filename)
117
+ extract_if_needed(zip_filename, extracted_filename)
118
+ models[model_key] = load_model(extracted_filename)
119
+ return models
120
+
121
+ models = initialize_models()
122
+
123
+ FEATURE_ORDER = [
124
+ 'Pregnancies', 'Glucose', 'BloodPressure', 'Insulin',
125
+ 'BMI', 'DiabetesPedigreeFunction', 'Age'
126
+ ]
127
+
128
+ def validate_input(value, input_type=float, min_value=0, max_value=None):
129
+ """Enhanced input validation with range checking."""
130
+ try:
131
+ value = input_type(value)
132
+ if value < min_value:
133
+ return None
134
+ if max_value is not None and value > max_value:
135
+ return None
136
+ return value
137
+ except (ValueError, TypeError):
138
+ return None
139
+
140
+ def validate_blood_pressure(systolic, diastolic):
141
+ """Validates blood pressure values within realistic ranges."""
142
+ systolic = validate_input(systolic, float, 0, 300)
143
+ diastolic = validate_input(diastolic, float, 0, 200)
144
+
145
+ if systolic is None or diastolic is None:
146
+ return None, None
147
+ return systolic, diastolic
148
+
149
+ def validate_gender(gender):
150
+ """Validates gender input."""
151
+ if isinstance(gender, str) and gender.lower() in ['male', 'female']:
152
+ return 1 if gender.lower() == 'male' else 0
153
+ return None
154
+
155
+ def calculate_diabetes_pedigree(family_history, first_degree=0, second_degree=0):
156
+ """Calculates diabetes pedigree function based on family history."""
157
+ if not family_history:
158
+ return 0.0
159
+ genetic_contribution = (first_degree * 0.5) + (second_degree * 0.25)
160
+ return min(genetic_contribution, 1.0)
161
+
162
+ def get_multi_condition_predictions(model, df):
163
+ """Get predictions for multiple health conditions."""
164
+ try:
165
+ predictions = model.predict(df)[0]
166
+ probs_list = model.predict_proba(df)
167
+
168
+ return {
169
+ 'hypertension': bool(predictions[0]),
170
+ 'cardiovascular': float(probs_list[1][0][1]),
171
+ 'stroke': float(probs_list[2][0][1]),
172
+ 'diabetes': float(probs_list[3][0][1])
173
+ }
174
+ except Exception as e:
175
+ logging.error(f"Error in multi-condition prediction: {str(e)}")
176
+ return None
177
+
178
+ def get_diabetes_prediction(model, df):
179
+ """Get diabetes-only prediction."""
180
+ try:
181
+ prediction = model.predict(df)[0]
182
+ probability = float(model.predict_proba(df)[0][1] * 100)
183
+ return 'Diabetes' if prediction else 'No Diabetes', probability
184
+ except Exception as e:
185
+ logging.error(f"Error in diabetes prediction: {str(e)}")
186
+ return None, 0.0
187
+
188
+ @app.route('/health', methods=['GET'])
189
+ def health_check():
190
+ """Health check endpoint."""
191
+ return jsonify({
192
+ 'status': 'healthy',
193
+ 'message': 'Service is running'
194
+ })
195
+
196
+ @app.route('/predict', methods=['POST'])
197
+ def predict_health():
198
+ """Main prediction endpoint."""
199
+ try:
200
+ data = request.get_json()
201
+ logging.info(f"Received data: {data}")
202
+ if not data:
203
+ return jsonify({'status': 'error', 'error': 'Invalid JSON payload'}), 400
204
+
205
+ # Validate basic health metrics
206
+ gender = validate_gender(data.get('gender'))
207
+ if gender is None:
208
+ return jsonify({'status': 'error', 'error': 'Invalid gender value. Must be "male" or "female"'}), 400
209
+
210
+ systolic, diastolic = validate_blood_pressure(data.get('systolic'), data.get('diastolic'))
211
+ if systolic is None or diastolic is None:
212
+ return jsonify({'status': 'error', 'error': 'Invalid blood pressure values'}), 400
213
+
214
+ # Validate other common inputs
215
+ age = validate_input(data.get('age'), float, 0, 120)
216
+ glucose = validate_input(data.get('glucose'), float, 0, 1000)
217
+ bmi = validate_input(data.get('bmi'), float, 0, 100)
218
+
219
+ if any(v is None for v in [age, glucose, bmi]):
220
+ return jsonify({'status': 'error', 'error': 'Invalid values for age, glucose, or BMI'}), 400
221
+
222
+ # Determine which model to use based on blood pressure
223
+ use_multi_condition = systolic < 90 or diastolic < 60
224
+
225
+ if use_multi_condition:
226
+ # Multi-condition model input preparation
227
+ df_multi = pd.DataFrame([{
228
+ 'Age': age,
229
+ 'Gender': gender,
230
+ 'Systolic_bp': systolic,
231
+ 'Diastolic_bp': diastolic,
232
+ 'Glucose': glucose,
233
+ 'BMI': bmi
234
+ }])
235
+
236
+ results = get_multi_condition_predictions(models['MULTI_MODEL'], df_multi)
237
+ if results is None:
238
+ return jsonify({'status': 'error', 'error': 'Error in multi-condition prediction'}), 500
239
+
240
+ return jsonify({
241
+ 'status': 'success',
242
+ 'model': 'multi-condition',
243
+ 'predictions': {
244
+ 'hypertension': results['hypertension'],
245
+ 'cardiovascular_risk': results['cardiovascular'],
246
+ 'stroke_risk': results['stroke'],
247
+ 'diabetes_risk': results['diabetes']
248
+ }
249
+ })
250
+ else:
251
+ # Diabetes-specific model handling
252
+ pregnancies = validate_input(data.get('pregnancies', 0 if gender == 1 else None), float, 0, 20)
253
+ insulin = validate_input(data.get('insulin'), float, 0, 1000)
254
+
255
+ # Family history handling
256
+ family_history = data.get('family_history', False)
257
+ first_degree = validate_input(data.get('first_degree_relatives', 0), float, 0, 10)
258
+ second_degree = validate_input(data.get('second_degree_relatives', 0), float, 0, 20)
259
+
260
+ diabetes_pedigree = calculate_diabetes_pedigree(
261
+ family_history,
262
+ first_degree if first_degree is not None else 0,
263
+ second_degree if second_degree is not None else 0
264
+ )
265
+
266
+ if any(v is None for v in [pregnancies, insulin]):
267
+ return jsonify({'status': 'error', 'error': 'Invalid values for pregnancies or insulin'}), 400
268
+
269
+ df_diabetes = pd.DataFrame([{
270
+ 'Pregnancies': pregnancies,
271
+ 'Glucose': glucose,
272
+ 'BloodPressure': systolic,
273
+ 'Insulin': insulin,
274
+ 'BMI': bmi,
275
+ 'DiabetesPedigreeFunction': diabetes_pedigree,
276
+ 'Age': age
277
+ }])
278
+
279
+ # Ensure correct column order
280
+ df_diabetes = df_diabetes[FEATURE_ORDER]
281
+
282
+ # Scale the data
283
+ df_scaled = models['SCALER'].transform(df_diabetes)
284
+
285
+ prediction, probability = get_diabetes_prediction(models['DIABETES_MODEL'], df_scaled)
286
+
287
+ return jsonify({
288
+ 'status': 'success',
289
+ 'model': 'diabetes',
290
+ 'prediction': prediction,
291
+ 'probability': probability,
292
+ 'risk_level': 'HIGH' if probability > 70 else 'MODERATE' if probability > 40 else 'LOW'
293
+ })
294
+
295
+ except Exception as e:
296
+ logging.error(f"Error: {e}")
297
+ return jsonify({'status': 'error', 'error': str(e)}), 500
298
+
299
+ if __name__ == '__main__':
300
+ app.run(host="0.0.0.0", port=7860)
finaliseddiabetes_model.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9c17cf84b9c5d2fd57663e1a6344e54aee9a4e513bb80954a3c4ab144d3c0355
3
+ size 283301
finalisedscaler.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f26ac692e451befa24ad6e53aa65c95f447e41e6bd5e30aa18fc722989e1ee68
3
+ size 1065
model_loader.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import joblib
4
+ import logging
5
+ import zipfile
6
+
7
+ # Configure logging
8
+ logging.basicConfig(level=logging.INFO)
9
+
10
+ # Get model URLs from environment variables
11
+ DIABETES_MODEL_URL = os.getenv("DIABETES_MODEL_URL")
12
+ SCALER_URL = os.getenv("SCALER_URL")
13
+ MULTI_MODEL_URL = os.getenv("MULTI_MODEL_URL")
14
+
15
+ # Local paths for downloaded models
16
+ MODEL_PATHS = {
17
+ "DIABETES_MODEL": "finaliseddiabetes_model.zip",
18
+ "SCALER": "finalisedscaler.zip",
19
+ "MULTI_MODEL": "nodiabetes.zip",
20
+ }
21
+
22
+ # Extracted model names
23
+ EXTRACTED_MODELS = {
24
+ "DIABETES_MODEL": "finaliseddiabetes_model.joblib",
25
+ "SCALER": "finalisedscaler.joblib",
26
+ "MULTI_MODEL": "nodiabetes.joblib",
27
+ }
28
+
29
+ BASE_DIR = os.getcwd() # Get current working directory
30
+
31
+ def download_model(url, zip_filename):
32
+ """Downloads the model zip file from the given URL and saves it locally."""
33
+ zip_path = os.path.join(BASE_DIR, zip_filename)
34
+ if not url:
35
+ logging.error(f"URL for {zip_filename} is missing!")
36
+ return False
37
+
38
+ try:
39
+ response = requests.get(url, allow_redirects=True)
40
+ if response.status_code == 200:
41
+ with open(zip_path, 'wb') as f:
42
+ f.write(response.content)
43
+ logging.info(f"Downloaded {zip_filename} successfully.")
44
+ return True
45
+ else:
46
+ logging.error(f"Failed to download {zip_filename}. HTTP Status: {response.status_code}")
47
+ return False
48
+ except Exception as e:
49
+ logging.error(f"Error downloading {zip_filename}: {e}")
50
+ return False
51
+
52
+ def extract_if_needed(zip_filename, extracted_filename):
53
+ """Extracts model file from zip if not already extracted."""
54
+ zip_path = os.path.join(BASE_DIR, zip_filename)
55
+ extracted_path = os.path.join(BASE_DIR, extracted_filename)
56
+
57
+ if os.path.exists(extracted_path):
58
+ logging.info(f"{extracted_filename} already exists. Skipping extraction.")
59
+ return True
60
+
61
+ if not os.path.exists(zip_path):
62
+ logging.error(f"Zip file missing: {zip_path}")
63
+ return False
64
+
65
+ try:
66
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
67
+ zip_ref.extractall(BASE_DIR)
68
+ extracted_files = zip_ref.namelist()
69
+ logging.info(f"Extracted {zip_filename}, contents: {extracted_files}")
70
+ return True
71
+ except Exception as e:
72
+ logging.error(f"Error extracting {zip_filename}: {e}")
73
+ return False
74
+
75
+ def load_model(model_filename):
76
+ """Loads a model from the given filename."""
77
+ model_path = os.path.join(BASE_DIR, model_filename)
78
+ if not os.path.exists(model_path):
79
+ logging.error(f"Model file not found: {model_path}")
80
+ return None
81
+
82
+ try:
83
+ model = joblib.load(model_path)
84
+ logging.info(f"Loaded {model_filename} successfully.")
85
+ return model
86
+ except Exception as e:
87
+ logging.error(f"Error loading {model_filename}: {e}")
88
+ return None
89
+
90
+ # **Main Execution**
91
+ for model_key, zip_filename in MODEL_PATHS.items():
92
+ extracted_filename = EXTRACTED_MODELS[model_key]
93
+
94
+ # Step 1: Download model if not present
95
+ if not os.path.exists(os.path.join(BASE_DIR, zip_filename)):
96
+ download_model(globals()[f"{model_key}_URL"], zip_filename)
97
+
98
+ # Step 2: Extract model file
99
+ extract_if_needed(zip_filename, extracted_filename)
100
+
101
+ # Step 3: Load models
102
+ diabetes_model = load_model(EXTRACTED_MODELS["DIABETES_MODEL"])
103
+ scaler = load_model(EXTRACTED_MODELS["SCALER"])
104
+ multi_model = load_model(EXTRACTED_MODELS["MULTI_MODEL"])
105
+
106
+ # Final check
107
+ if diabetes_model and scaler and multi_model:
108
+ logging.info("All models loaded successfully! ✅")
109
+ else:
110
+ logging.error("Some models failed to load. ❌ Check logs for details.")
nodiabetes.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:05ae3b2c7b5d0bb6172bf0513d5cd187bfd289b457034d0f368a9bd039d2b044
3
+ size 6388514
requirements.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Web Framework and WSGI
2
+ Flask==3.0.3
3
+ Flask-Cors==4.0.1
4
+ gunicorn==21.2.0
5
+ Werkzeug==3.0.1
6
+
7
+ # Machine Learning and Data Processing
8
+ scikit-learn==1.3.0
9
+ pandas==2.2.2
10
+ numpy==1.26.4
11
+ joblib==1.4.2
12
+ waitress==3.0.2
13
+ # Error Handling and Validation
14
+ marshmallow==3.20.2 # For request/response validation
15
+ pydantic==2.6.1 # For data validation
16
+
17
+ # Security
18
+ python-dotenv==1.0.0
19
+ PyJWT==2.8.0 # For JWT handling if you add authentication
20
+ bcrypt==4.1.2 # For password hashing if needed
21
+
22
+ # Monitoring and Logging
23
+ prometheus-flask-exporter==0.23.0 # For metrics and monitoring
24
+ python-json-logger==2.0.7 # For structured JSON logging
25
+ sentry-sdk[flask]==1.40.4 # For error tracking
26
+
27
+ # HTTP and Networking
28
+ requests==2.32.3
29
+ urllib3==2.2.0 # Required by requests
30
+ certifi==2024.2.2 # For SSL certificate verification
31
+
32
+ # Performance and Caching
33
+ cachetools==5.3.2 # For in-memory caching
34
+ redis==5.0.1 # For distributed caching if needed
35
+
36
+ # Development and Testing
37
+ pytest==8.0.0 # For unit testing
38
+ pytest-cov==4.1.0 # For test coverage
39
+ black==24.1.1 # For code formatting
40
+ flake8==7.0.0 # For code linting
41
+
42
+ # Time zone handling
43
+ pytz==2024.1 # For proper timezone handling
44
+
45
+ # Compression and Performance
46
+ brotli==1.1.0 # For response compression