Update main.py
Browse filescoba tanpa coreq
main.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# ======================================================================
|
| 2 |
-
# --- main.py
|
| 3 |
# ======================================================================
|
| 4 |
|
| 5 |
import os
|
|
@@ -15,9 +15,9 @@ from typing import List, Dict, Any
|
|
| 15 |
# 1. Inisialisasi Aplikasi FastAPI
|
| 16 |
# ======================================================================
|
| 17 |
app = FastAPI(
|
| 18 |
-
title="
|
| 19 |
-
description="
|
| 20 |
-
version="1.
|
| 21 |
)
|
| 22 |
|
| 23 |
# ======================================================================
|
|
@@ -75,7 +75,6 @@ MODEL_FEATURES = [
|
|
| 75 |
G = nx.DiGraph()
|
| 76 |
course_details_map = {}
|
| 77 |
prereq_map = {}
|
| 78 |
-
coreq_map = {}
|
| 79 |
out_degree_map = {}
|
| 80 |
|
| 81 |
# --- Fungsi Pemuatan (dipanggil saat startup) ---
|
|
@@ -96,14 +95,16 @@ def load_ml_model():
|
|
| 96 |
print("Model ML berhasil dimuat.")
|
| 97 |
except Exception as e:
|
| 98 |
print(f"ERROR: Gagal memuat model ML dari {MODEL_PATH}: {e}")
|
| 99 |
-
# Di produksi, Anda mungkin ingin ini menghentikan server
|
| 100 |
-
# raise RuntimeError(f"Gagal memuat model dari {MODEL_PATH}: {e}")
|
| 101 |
|
| 102 |
def load_graph_data():
|
| 103 |
"""Memuat dan memproses data graf kurikulum dari JSON"""
|
| 104 |
-
|
|
|
|
| 105 |
JSON_PATH = os.path.join(os.path.dirname(__file__), "OK_matkul_graph.json")
|
| 106 |
print(f"Mencoba memuat data graf dari: {JSON_PATH}")
|
|
|
|
|
|
|
|
|
|
| 107 |
try:
|
| 108 |
with open(JSON_PATH, "r") as f:
|
| 109 |
data = json.load(f)
|
|
@@ -114,19 +115,20 @@ def load_graph_data():
|
|
| 114 |
|
| 115 |
for edge in data["edges"]:
|
| 116 |
if edge["type"] == "prereq":
|
|
|
|
| 117 |
G.add_edge(edge["from"], edge["to"])
|
| 118 |
if edge["to"] not in prereq_map:
|
| 119 |
prereq_map[edge["to"]] = []
|
| 120 |
prereq_map[edge["to"]].append(edge["from"])
|
| 121 |
-
|
| 122 |
-
if edge["to"] not in coreq_map:
|
| 123 |
-
coreq_map[edge["to"]] = []
|
| 124 |
-
coreq_map[edge["to"]].append(edge["from"])
|
| 125 |
|
| 126 |
for node_code in G.nodes():
|
| 127 |
out_degree_map[node_code] = G.out_degree(node_code)
|
| 128 |
|
| 129 |
-
print(f"Data graf berhasil dimuat.
|
|
|
|
|
|
|
|
|
|
| 130 |
except FileNotFoundError:
|
| 131 |
print(f"ERROR: {JSON_PATH} tidak ditemukan!")
|
| 132 |
except Exception as e:
|
|
@@ -143,7 +145,7 @@ def on_startup():
|
|
| 143 |
# ======================================================================
|
| 144 |
|
| 145 |
def get_recommendations_logic(current_semester: int, courses_passed_list: List[str]) -> List[Dict[str, Any]]:
|
| 146 |
-
"""Inti dari logika rekomendasi, dengan validasi prereq
|
| 147 |
passed_set = set(courses_passed_list)
|
| 148 |
all_courses_set = set(course_details_map.keys())
|
| 149 |
not_passed_courses = all_courses_set - passed_set
|
|
@@ -165,34 +167,19 @@ def get_recommendations_logic(current_semester: int, courses_passed_list: List[s
|
|
| 165 |
candidate_data["priority_score"] = priority_score
|
| 166 |
prereq_valid_candidates.append(candidate_data)
|
| 167 |
|
| 168 |
-
# Tahap 2: Cek Ko-requisite (Coreq)
|
| 169 |
-
prereq_valid_codes = {c['code'] for c in prereq_valid_candidates}
|
| 170 |
-
final_valid_candidates = []
|
| 171 |
|
| 172 |
-
for candidate in prereq_valid_candidates:
|
| 173 |
-
course_code = candidate['code']
|
| 174 |
-
coreqs = coreq_map.get(course_code, [])
|
| 175 |
-
|
| 176 |
-
if not coreqs:
|
| 177 |
-
final_valid_candidates.append(candidate)
|
| 178 |
-
continue
|
| 179 |
-
|
| 180 |
-
is_coreq_met = True
|
| 181 |
-
for coreq_code in coreqs:
|
| 182 |
-
if (coreq_code not in passed_set) and (coreq_code not in prereq_valid_codes):
|
| 183 |
-
is_coreq_met = False
|
| 184 |
-
break
|
| 185 |
-
|
| 186 |
-
if is_coreq_met:
|
| 187 |
-
final_valid_candidates.append(candidate)
|
| 188 |
-
|
| 189 |
# Tahap 3: Urutkan berdasarkan prioritas
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
| 193 |
|
| 194 |
sorted_catch_up = sorted(catch_up_courses, key=lambda x: (x["semester_plan"], -x["priority_score"]))
|
|
|
|
| 195 |
sorted_current = sorted(current_semester_courses, key=lambda x: -x["priority_score"], reverse=False)
|
|
|
|
|
|
|
| 196 |
sorted_future = sorted(future_courses, key=lambda x: (x["semester_plan"], -x["priority_score"]))
|
| 197 |
|
| 198 |
final_ranked_list = sorted_catch_up + sorted_current + sorted_future
|
|
|
|
| 1 |
# ======================================================================
|
| 2 |
+
# --- main.py---
|
| 3 |
# ======================================================================
|
| 4 |
|
| 5 |
import os
|
|
|
|
| 15 |
# 1. Inisialisasi Aplikasi FastAPI
|
| 16 |
# ======================================================================
|
| 17 |
app = FastAPI(
|
| 18 |
+
title="GCOMPRO",
|
| 19 |
+
description="API Prediksi Risiko Akademik dan Rekomendasi Mata Kuliah.",
|
| 20 |
+
version="1.2.0" # Versi dinaikkan
|
| 21 |
)
|
| 22 |
|
| 23 |
# ======================================================================
|
|
|
|
| 75 |
G = nx.DiGraph()
|
| 76 |
course_details_map = {}
|
| 77 |
prereq_map = {}
|
|
|
|
| 78 |
out_degree_map = {}
|
| 79 |
|
| 80 |
# --- Fungsi Pemuatan (dipanggil saat startup) ---
|
|
|
|
| 95 |
print("Model ML berhasil dimuat.")
|
| 96 |
except Exception as e:
|
| 97 |
print(f"ERROR: Gagal memuat model ML dari {MODEL_PATH}: {e}")
|
|
|
|
|
|
|
| 98 |
|
| 99 |
def load_graph_data():
|
| 100 |
"""Memuat dan memproses data graf kurikulum dari JSON"""
|
| 101 |
+
# Menghapus 'coreq_map' dari global
|
| 102 |
+
global G, course_details_map, prereq_map, out_degree_map
|
| 103 |
JSON_PATH = os.path.join(os.path.dirname(__file__), "OK_matkul_graph.json")
|
| 104 |
print(f"Mencoba memuat data graf dari: {JSON_PATH}")
|
| 105 |
+
|
| 106 |
+
prereq_edge_count = 0 # Ditambahkan untuk log yang lebih baik
|
| 107 |
+
|
| 108 |
try:
|
| 109 |
with open(JSON_PATH, "r") as f:
|
| 110 |
data = json.load(f)
|
|
|
|
| 115 |
|
| 116 |
for edge in data["edges"]:
|
| 117 |
if edge["type"] == "prereq":
|
| 118 |
+
prereq_edge_count += 1
|
| 119 |
G.add_edge(edge["from"], edge["to"])
|
| 120 |
if edge["to"] not in prereq_map:
|
| 121 |
prereq_map[edge["to"]] = []
|
| 122 |
prereq_map[edge["to"]].append(edge["from"])
|
| 123 |
+
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
for node_code in G.nodes():
|
| 126 |
out_degree_map[node_code] = G.out_degree(node_code)
|
| 127 |
|
| 128 |
+
print(f"Data graf berhasil dimuat.")
|
| 129 |
+
print(f" - Total Relasi Prasyarat (Edges): {prereq_edge_count}")
|
| 130 |
+
print(f" - (Info) MK unik dgn Prasyarat: {len(prereq_map)}")
|
| 131 |
+
|
| 132 |
except FileNotFoundError:
|
| 133 |
print(f"ERROR: {JSON_PATH} tidak ditemukan!")
|
| 134 |
except Exception as e:
|
|
|
|
| 145 |
# ======================================================================
|
| 146 |
|
| 147 |
def get_recommendations_logic(current_semester: int, courses_passed_list: List[str]) -> List[Dict[str, Any]]:
|
| 148 |
+
"""Inti dari logika rekomendasi, HANYA dengan validasi prereq."""
|
| 149 |
passed_set = set(courses_passed_list)
|
| 150 |
all_courses_set = set(course_details_map.keys())
|
| 151 |
not_passed_courses = all_courses_set - passed_set
|
|
|
|
| 167 |
candidate_data["priority_score"] = priority_score
|
| 168 |
prereq_valid_candidates.append(candidate_data)
|
| 169 |
|
| 170 |
+
# Tahap 2: Cek Ko-requisite (Coreq) GAJADI
|
|
|
|
|
|
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
# Tahap 3: Urutkan berdasarkan prioritas
|
| 173 |
+
# Menggunakan 'prereq_valid_candidates' sebagai sumber
|
| 174 |
+
catch_up_courses = [c for c in prereq_valid_candidates if c["semester_plan"] < current_semester]
|
| 175 |
+
current_semester_courses = [c for c in prereq_valid_candidates if c["semester_plan"] == current_semester]
|
| 176 |
+
future_courses = [c for c in prereq_valid_candidates if c["semester_plan"] > current_semester]
|
| 177 |
|
| 178 |
sorted_catch_up = sorted(catch_up_courses, key=lambda x: (x["semester_plan"], -x["priority_score"]))
|
| 179 |
+
|
| 180 |
sorted_current = sorted(current_semester_courses, key=lambda x: -x["priority_score"], reverse=False)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
sorted_future = sorted(future_courses, key=lambda x: (x["semester_plan"], -x["priority_score"]))
|
| 184 |
|
| 185 |
final_ranked_list = sorted_catch_up + sorted_current + sorted_future
|