sistem_rekomendasi/flask_api/app.py

181 lines
5.3 KiB
Python

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)