Update main.py
Browse files
main.py
CHANGED
|
@@ -21,7 +21,7 @@ from .explanation_builder import build_explanation_from_rules
|
|
| 21 |
app = FastAPI(
|
| 22 |
title="GCOMPRO",
|
| 23 |
description="API Prediksi Risiko Akademik dan Rekomendasi Mata Kuliah.",
|
| 24 |
-
version="1.
|
| 25 |
)
|
| 26 |
|
| 27 |
# ======================================================================
|
|
@@ -60,7 +60,7 @@ class PredictionResponse(BaseModel):
|
|
| 60 |
class RecommendationRequest(BaseModel):
|
| 61 |
current_semester: int
|
| 62 |
courses_passed: List[str]
|
| 63 |
-
mk_pilihan_failed: List[str] = []
|
| 64 |
|
| 65 |
class PrerequisiteInfo(BaseModel):
|
| 66 |
code: str
|
|
@@ -81,6 +81,38 @@ class CourseRecommendation(BaseModel):
|
|
| 81 |
# 3. Variabel Global & Pemuatan Model/Data
|
| 82 |
# ======================================================================
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
# --- Variabel Global untuk API Prediksi Risiko (App 1) ---
|
| 85 |
ml_model = None
|
| 86 |
MODEL_FEATURES = [
|
|
@@ -160,11 +192,9 @@ def on_startup():
|
|
| 160 |
# 4. Helper Function (Untuk API Rekomendasi)
|
| 161 |
# ======================================================================
|
| 162 |
|
| 163 |
-
# <--- [UPDATE] Parameter mk_pilihan_failed_list ditambahkan
|
| 164 |
def get_recommendations_logic(current_semester: int, courses_passed_list: List[str], mk_pilihan_failed_list: List[str]) -> List[Dict[str, Any]]:
|
| 165 |
"""
|
| 166 |
-
Logika rekomendasi dengan
|
| 167 |
-
MK Gagal akan 'membajak' slot MK_PILIHAN yang tersedia di graf.
|
| 168 |
"""
|
| 169 |
passed_set = set(courses_passed_list)
|
| 170 |
all_courses_set = set(course_details_map.keys())
|
|
@@ -191,6 +221,7 @@ def get_recommendations_logic(current_semester: int, courses_passed_list: List[s
|
|
| 191 |
|
| 192 |
raw_candidates.append(candidate_data)
|
| 193 |
|
|
|
|
| 194 |
elective_slots = []
|
| 195 |
regular_candidates = []
|
| 196 |
|
|
@@ -204,18 +235,29 @@ def get_recommendations_logic(current_semester: int, courses_passed_list: List[s
|
|
| 204 |
# Urutkan slot pilihan agar mengisi dari semester terkecil (misal sem 7 dulu)
|
| 205 |
elective_slots.sort(key=lambda x: x["semester_plan"])
|
| 206 |
|
|
|
|
| 207 |
processed_electives = []
|
| 208 |
failed_idx = 0
|
| 209 |
|
| 210 |
# Prioritaskan mengisi slot dengan MK yang gagal
|
| 211 |
while failed_idx < len(mk_pilihan_failed_list) and len(elective_slots) > 0:
|
| 212 |
-
slot = elective_slots.pop(0) # Ambil slot
|
| 213 |
failed_code = mk_pilihan_failed_list[failed_idx]
|
| 214 |
|
| 215 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
slot["code"] = failed_code
|
| 217 |
-
slot["name"] = f"
|
| 218 |
-
slot["
|
|
|
|
| 219 |
slot["is_retake_elective"] = True
|
| 220 |
|
| 221 |
processed_electives.append(slot)
|
|
@@ -343,10 +385,9 @@ async def recommend_courses(request: RecommendationRequest):
|
|
| 343 |
elif course["semester_plan"] > request.current_semester:
|
| 344 |
reason = f"Akselerasi (Semester {course['semester_plan']})"
|
| 345 |
|
| 346 |
-
# Note: Untuk MK Pilihan Gagal, kode sudah berubah jadi spesifik (misal EL4092).
|
| 347 |
-
# Prereq map mungkin return kosong ([]) jika kode spesifik tidak ada di graf JSON.
|
| 348 |
-
# Ini aman, karena biasanya MK Pilihan prasyaratnya umum/tidak strict di graf.
|
| 349 |
prereq_codes = prereq_map.get(course["code"], [])
|
|
|
|
|
|
|
| 350 |
|
| 351 |
prereq_details_list = []
|
| 352 |
for p_code in prereq_codes:
|
|
|
|
| 21 |
app = FastAPI(
|
| 22 |
title="GCOMPRO",
|
| 23 |
description="API Prediksi Risiko Akademik dan Rekomendasi Mata Kuliah.",
|
| 24 |
+
version="1.6.0"
|
| 25 |
)
|
| 26 |
|
| 27 |
# ======================================================================
|
|
|
|
| 60 |
class RecommendationRequest(BaseModel):
|
| 61 |
current_semester: int
|
| 62 |
courses_passed: List[str]
|
| 63 |
+
mk_pilihan_failed: List[str] = [] # <--- Berisi kode spesifik (misal: "AAK4ABB3")
|
| 64 |
|
| 65 |
class PrerequisiteInfo(BaseModel):
|
| 66 |
code: str
|
|
|
|
| 81 |
# 3. Variabel Global & Pemuatan Model/Data
|
| 82 |
# ======================================================================
|
| 83 |
|
| 84 |
+
# <--- [UPDATE] DATABASE HARDCODE MATA KULIAH PILIHAN
|
| 85 |
+
# Ini digunakan untuk lookup nama resmi saat user mengirim kode MK Pilihan yang gagal
|
| 86 |
+
ELECTIVE_COURSES_DB = {
|
| 87 |
+
"AAK4ABB3": {"name": "New Generation Network", "sks": 3},
|
| 88 |
+
"AAK4BBB3": {"name": "Software Defined Network", "sks": 3},
|
| 89 |
+
"AAK4CBB3": {"name": "Rekayasa Jaringan", "sks": 3},
|
| 90 |
+
"AAK4DBB3": {"name": "Aplikasi Cyber Security", "sks": 3},
|
| 91 |
+
"AAK4EBB3": {"name": "Manajemen Telekomunikasi dan Transformasi Digital", "sks": 3},
|
| 92 |
+
"AAK4FBB3": {"name": "Adaptive Network", "sks": 3},
|
| 93 |
+
"AAK4GBB3": {"name": "Cloud Computing", "sks": 3},
|
| 94 |
+
"AAK4HBB3": {"name": "Koding dan Kompresi", "sks": 3},
|
| 95 |
+
"AAK4IBB3": {"name": "Steganografi dan Watermarking", "sks": 3},
|
| 96 |
+
"AAK4JBB3": {"name": "Mobile Application", "sks": 3},
|
| 97 |
+
"AAK4KBB3": {"name": "Speech Signal Processing", "sks": 3},
|
| 98 |
+
"AAK4LBB3": {"name": "Komunikasi Akses Wireless", "sks": 3},
|
| 99 |
+
"AAK4MBB3": {"name": "Wireless Optical Communication", "sks": 3},
|
| 100 |
+
"AAK4NBB3": {"name": "Broadband Optical Network", "sks": 3},
|
| 101 |
+
"AAK4OBB3": {"name": "Sistem Komunikasi Satelit", "sks": 3},
|
| 102 |
+
"AAK4PBB3": {"name": "Rekayasa Radio", "sks": 3},
|
| 103 |
+
"AAK4QBB3": {"name": "Radar, Navigasi dan Remote Sensing", "sks": 3},
|
| 104 |
+
"AAK4RBB3": {"name": "5G and Beyond", "sks": 3},
|
| 105 |
+
"AAK4SBB3": {"name": "Software Defined Radio", "sks": 3},
|
| 106 |
+
"AAK4TBB3": {"name": "Robotic Process Automation", "sks": 3},
|
| 107 |
+
"AAK4UBB3": {"name": "Rekayasa Frekuensi Radio dalam Komunikasi Selular", "sks": 3},
|
| 108 |
+
"AAK4VBB3": {"name": "Teknologi Radio Access Network (RAN)", "sks": 3},
|
| 109 |
+
"AAK4WBB3": {"name": "Internet of Things: Protokol, Platform, dan AI", "sks": 3},
|
| 110 |
+
"AAK4XBB3": {"name": "Jaringan Core Telekomunikasi", "sks": 3},
|
| 111 |
+
"AAK4YBB3": {"name": "Ethical Hacking", "sks": 3},
|
| 112 |
+
"AAK4ZBB3": {"name": "Keamanan Komunikasi Data", "sks": 3},
|
| 113 |
+
"AAK47BB3": {"name": "Rekayasa Penyiaran Digital", "sks": 3}
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
# --- Variabel Global untuk API Prediksi Risiko (App 1) ---
|
| 117 |
ml_model = None
|
| 118 |
MODEL_FEATURES = [
|
|
|
|
| 192 |
# 4. Helper Function (Untuk API Rekomendasi)
|
| 193 |
# ======================================================================
|
| 194 |
|
|
|
|
| 195 |
def get_recommendations_logic(current_semester: int, courses_passed_list: List[str], mk_pilihan_failed_list: List[str]) -> List[Dict[str, Any]]:
|
| 196 |
"""
|
| 197 |
+
Logika rekomendasi dengan injeksi data MK Pilihan dari Database Hardcode.
|
|
|
|
| 198 |
"""
|
| 199 |
passed_set = set(courses_passed_list)
|
| 200 |
all_courses_set = set(course_details_map.keys())
|
|
|
|
| 221 |
|
| 222 |
raw_candidates.append(candidate_data)
|
| 223 |
|
| 224 |
+
# Tahap 2: Pisahkan Slot MK Pilihan vs MK Wajib
|
| 225 |
elective_slots = []
|
| 226 |
regular_candidates = []
|
| 227 |
|
|
|
|
| 235 |
# Urutkan slot pilihan agar mengisi dari semester terkecil (misal sem 7 dulu)
|
| 236 |
elective_slots.sort(key=lambda x: x["semester_plan"])
|
| 237 |
|
| 238 |
+
# <--- [UPDATE] Tahap 3: Suntikkan MK Pilihan Gagal + Lookup Database
|
| 239 |
processed_electives = []
|
| 240 |
failed_idx = 0
|
| 241 |
|
| 242 |
# Prioritaskan mengisi slot dengan MK yang gagal
|
| 243 |
while failed_idx < len(mk_pilihan_failed_list) and len(elective_slots) > 0:
|
| 244 |
+
slot = elective_slots.pop(0) # Ambil slot template (misal MK_PILIHAN1)
|
| 245 |
failed_code = mk_pilihan_failed_list[failed_idx]
|
| 246 |
|
| 247 |
+
# <--- [UPDATE] Lookup detail mata kuliah dari ELECTIVE_COURSES_DB
|
| 248 |
+
if failed_code in ELECTIVE_COURSES_DB:
|
| 249 |
+
real_name = ELECTIVE_COURSES_DB[failed_code]["name"]
|
| 250 |
+
real_sks = ELECTIVE_COURSES_DB[failed_code]["sks"]
|
| 251 |
+
else:
|
| 252 |
+
# Fallback jika kode tidak ada di DB (safety net)
|
| 253 |
+
real_name = "Mata Kuliah Pilihan (Unknown)"
|
| 254 |
+
real_sks = 3
|
| 255 |
+
|
| 256 |
+
# Modifikasi slot menjadi MK spesifik dengan data asli
|
| 257 |
slot["code"] = failed_code
|
| 258 |
+
slot["name"] = f"{real_name} (Mengulang)" # Ubah nama jadi nama asli
|
| 259 |
+
slot["sks"] = real_sks
|
| 260 |
+
slot["priority_score"] += 100.0 # Boost score agar prioritas #1
|
| 261 |
slot["is_retake_elective"] = True
|
| 262 |
|
| 263 |
processed_electives.append(slot)
|
|
|
|
| 385 |
elif course["semester_plan"] > request.current_semester:
|
| 386 |
reason = f"Akselerasi (Semester {course['semester_plan']})"
|
| 387 |
|
|
|
|
|
|
|
|
|
|
| 388 |
prereq_codes = prereq_map.get(course["code"], [])
|
| 389 |
+
# Jika kode MK Pilihan diganti (misal AAK4ABB3), prereq_map.get("AAK4ABB3") akan None/Empty
|
| 390 |
+
# Ini benar karena MK Pilihan umumnya tidak punya prereq di graf ini (hanya placeholder)
|
| 391 |
|
| 392 |
prereq_details_list = []
|
| 393 |
for p_code in prereq_codes:
|