import os import traceback from flask import Flask, request, jsonify from flask_cors import CORS import joblib import pandas as pd app = Flask(__name__) CORS(app) BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_PATH = os.path.join(BASE_DIR, 'model_rekomendasi_15mapel.pkl') ENCODER_PATH = os.path.join(BASE_DIR, 'encoders_15mapel.pkl') FEATURE_COLUMNS_PATH = os.path.join(BASE_DIR, 'feature_columns_15mapel.pkl') model = None encoders = None feature_columns = None def load_artifacts(): global model, encoders, feature_columns model = joblib.load(MODEL_PATH) encoders = joblib.load(ENCODER_PATH) feature_columns = joblib.load(FEATURE_COLUMNS_PATH) if not isinstance(feature_columns, list): raise ValueError("feature_columns_15mapel.pkl harus berisi list nama kolom.") required_encoder_keys = ['ekonomi_orang_tua', 'rekomendasi_jurusan'] for key in required_encoder_keys: if key not in encoders: raise KeyError(f"Encoder '{key}' tidak ditemukan pada encoders_15mapel.pkl") def safe_float(value, default=0.0): try: if value is None or value == '': return float(default) return float(value) except (ValueError, TypeError): return float(default) def validate_required_features(data): if not isinstance(data, dict): return False, "Payload JSON tidak valid." missing = [] for col in feature_columns: if col == 'ekonomi_orang_tua': continue if col not in data: missing.append(col) if 'ekonomi_orang_tua' not in data: missing.append('ekonomi_orang_tua') if missing: return False, f"Fitur berikut belum dikirim: {', '.join(missing)}" return True, None try: load_artifacts() print("✅ API Flask siap. Model, encoder, dan feature columns berhasil dimuat.") except Exception as e: print("❌ Gagal memuat aset machine learning.") print(str(e)) @app.route('/', methods=['GET']) def home(): return jsonify({ 'success': True, 'message': 'Flask API Sistem Rekomendasi Jurusan aktif.' }) @app.route('/health', methods=['GET']) def health(): artifacts_ready = all([ model is not None, encoders is not None, feature_columns is not None ]) return jsonify({ 'success': artifacts_ready, 'model_loaded': model is not None, 'encoders_loaded': encoders is not None, 'feature_columns_loaded': feature_columns is not None }), 200 if artifacts_ready else 500 @app.route('/predict', methods=['POST']) def predict(): global model, encoders, feature_columns try: if model is None or encoders is None or feature_columns is None: return jsonify({ 'success': False, 'message': 'Aset model belum berhasil dimuat. Periksa file .pkl dan path-nya.' }), 500 data = request.get_json(silent=True) if data is None: return jsonify({ 'success': False, 'message': 'Request harus berformat JSON.' }), 400 is_valid, error_message = validate_required_features(data) if not is_valid: return jsonify({ 'success': False, 'message': error_message }), 400 eko_text = str(data.get('ekonomi_orang_tua', 'Mampu')).strip() ekonomi_classes = list(encoders['ekonomi_orang_tua'].classes_) if eko_text not in ekonomi_classes: return jsonify({ 'success': False, 'message': f"Nilai ekonomi_orang_tua tidak valid. Gunakan salah satu: {', '.join(ekonomi_classes)}" }), 400 encoded_eko = encoders['ekonomi_orang_tua'].transform([eko_text])[0] input_dict = {} for col in feature_columns: if col == 'ekonomi_orang_tua': input_dict[col] = encoded_eko else: input_dict[col] = safe_float(data.get(col), 0) df_input = pd.DataFrame([input_dict], columns=feature_columns) pred_idx = model.predict(df_input)[0] nama_jurusan = encoders['rekomendasi_jurusan'].inverse_transform([pred_idx])[0] probabilities = None if hasattr(model, 'predict_proba'): try: proba = model.predict_proba(df_input)[0] class_labels = encoders['rekomendasi_jurusan'].inverse_transform(model.classes_) probabilities = [ { 'jurusan': str(label), 'probabilitas': round(float(score), 4) } for label, score in sorted( zip(class_labels, proba), key=lambda x: x[1], reverse=True ) ] except Exception: probabilities = None return jsonify({ 'success': True, 'prediksi_jurusan': str(nama_jurusan), 'top_predictions': probabilities }), 200 except Exception as e: traceback.print_exc() return jsonify({ 'success': False, 'message': f'Terjadi kesalahan saat melakukan prediksi: {str(e)}' }), 500 if __name__ == '__main__': app.run(host='127.0.0.1', port=5000, debug=True)