Spaces:
Sleeping
Sleeping
| from flask import Flask, request, jsonify | |
| from flask_cors import CORS | |
| import joblib | |
| import pandas as pd | |
| import os | |
| import psycopg2 | |
| from datetime import datetime | |
| import uuid | |
| import jwt | |
| # ================= CONFIG ================= | |
| DATABASE_URL = os.environ.get("DATABASE_URL") | |
| JWT_SECRET = os.environ.get("JWT_SECRET") | |
| if not DATABASE_URL: | |
| raise RuntimeError("DATABASE_URL is not set in environment variables") | |
| if not JWT_SECRET: | |
| raise RuntimeError("JWT_SECRET is not set in environment variables") | |
| # ================= LOAD MODEL ================= | |
| if not os.path.exists("diabetes_model_tuned2.pkl"): | |
| import gdown | |
| url = "https://drive.google.com/uc?id=1Zo-xI1y3Y64HU6aiwXVUIHXfu84bBphx" | |
| gdown.download(url, "diabetes_model_tuned2.pkl", quiet=False) | |
| model = joblib.load("diabetes_model_tuned2.pkl") | |
| # ================= APP ================= | |
| app = Flask(__name__) | |
| # 🔹 Konfigurasi CORS yang lebih lengkap | |
| CORS(app, | |
| resources={r"/*": { | |
| "origins": [ | |
| "http://localhost:5173", | |
| "http://localhost:3000", | |
| "https://capstone-dbs-react.vercel.app", | |
| r"https://.*\.ngrok-free\.app" | |
| ], | |
| "methods": ["GET", "POST", "OPTIONS"], | |
| "allow_headers": ["Content-Type", "Authorization"], | |
| "supports_credentials": True | |
| }}) | |
| def get_db_connection(): | |
| try: | |
| return psycopg2.connect(DATABASE_URL) | |
| except Exception as e: | |
| print(f"Database connection error: {e}") | |
| return None | |
| # ================= JWT HELPER ================= | |
| def get_user_id_from_token(request): | |
| auth_header = request.headers.get("Authorization") | |
| if not auth_header: | |
| return None | |
| try: | |
| scheme, token = auth_header.split() | |
| if scheme.lower() != "bearer": | |
| return None | |
| decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) | |
| return decoded.get("userId") or decoded.get("id") | |
| except Exception as e: | |
| print("JWT decode error:", e) | |
| return None | |
| # ================= ROUTES ================= | |
| def home(): | |
| return "API is running" | |
| def predict_history(): | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({'error': 'No data provided'}), 400 | |
| required_fields = [ | |
| 'hypertension', 'heart_disease', 'bmi', 'blood_glucose_level', | |
| 'HbA1c_level', 'smoking_history', 'gender', 'age' | |
| ] | |
| for field in required_fields: | |
| if field not in data: | |
| return jsonify({'error': f'Missing field: {field}'}), 400 | |
| # 🔹 Ambil userId dari JWT (boleh None) | |
| current_user_id = get_user_id_from_token(request) | |
| input_data = pd.DataFrame([{ | |
| 'hypertension': int(data['hypertension']), | |
| 'heart_disease': int(data['heart_disease']), | |
| 'bmi': float(data['bmi']), | |
| 'blood_glucose_level': float(data['blood_glucose_level']), | |
| 'HbA1c_level': float(data['HbA1c_level']), | |
| 'smoking_history': data['smoking_history'], | |
| 'gender': data['gender'], | |
| 'age': float(data['age']) | |
| }]) | |
| prediction = model.predict(input_data)[0] | |
| proba = model.predict_proba(input_data)[0] | |
| confidence = float(proba[1]) if prediction == 1 else float(proba[0]) | |
| prediction_result = "positive" if prediction == 1 else "negative" | |
| conn = get_db_connection() | |
| if not conn: | |
| return jsonify({'error': 'Database connection failed'}), 500 | |
| try: | |
| cursor = conn.cursor() | |
| insert_query = """ | |
| INSERT INTO form_check_history | |
| (id, "userId", hypertension, "heartDisease", bmi, "bloodGlucoseLevel", | |
| "hba1cLevel", "smokingHistory", gender, age, "predictionResult", "createdAt", "updatedAt") | |
| VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) | |
| RETURNING id | |
| """ | |
| record_id = str(uuid.uuid4()) | |
| current_time = datetime.now() | |
| cursor.execute(insert_query, ( | |
| record_id, | |
| current_user_id, | |
| bool(int(data['hypertension'])), | |
| bool(int(data['heart_disease'])), | |
| float(data['bmi']), | |
| float(data['blood_glucose_level']), | |
| float(data['HbA1c_level']), | |
| data['smoking_history'], | |
| data['gender'], | |
| float(data['age']), | |
| prediction_result, | |
| current_time, | |
| current_time | |
| )) | |
| conn.commit() | |
| cursor.close() | |
| conn.close() | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Prediction completed and saved successfully', | |
| 'recordId': record_id, | |
| 'prediction': { | |
| 'result': int(prediction), | |
| 'resultText': prediction_result, | |
| 'confidence': round(confidence, 4) | |
| }, | |
| 'inputData': data, | |
| 'timestamp': current_time.isoformat(), | |
| 'userId': current_user_id | |
| }), 200 | |
| except Exception as db_error: | |
| conn.rollback() | |
| cursor.close() | |
| conn.close() | |
| return jsonify({'error': f'Database error: {str(db_error)}'}), 500 | |
| except Exception as e: | |
| return jsonify({'error': f'Prediction error: {str(e)}'}), 500 | |
| # ================= RUN ================= | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)), debug=False) | |