Spaces:
Sleeping
Sleeping
File size: 6,532 Bytes
410b443 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | # ======================================================================
# --- graduation_logic.py ---
# ======================================================================
import networkx as nx
from typing import Dict, Any, List
def _predict_naive(current_semester: int, total_sks_passed: int, last_gpa: float) -> Dict[str, Any]:
"""
Logika perhitungan matematis dasar (Naive) berdasarkan SKS dan IPK.
Menggunakan teks deskripsi custom sesuai permintaan user.
"""
TARGET_SKS = 144
TARGET_SEMESTER = 8
MAX_SKS_REGULAR = 24 # Batas absolut reguler (biasanya IP >= 3.00)
LIMIT_SKS_LOW_GPA = 20 # Batas jika IP < 3.00
SAFE_THRESHOLD = 18 # Batas aman/santai
# 1. Hitung Sisa
sks_needed = TARGET_SKS - total_sks_passed
semesters_left = (TARGET_SEMESTER - current_semester) + 1
# Stats dasar untuk dikembalikan
stats = {
"sks_needed": sks_needed,
"semesters_left": semesters_left,
"required_pace": 0,
"student_capacity": int(MAX_SKS_REGULAR if last_gpa >= 3.00 else LIMIT_SKS_LOW_GPA)
}
# --- LOGIC: Sudah Semester Akhir / Lewat ---
if semesters_left <= 0:
if sks_needed <= 0:
return {
"status": "Lulus",
"color": "green",
"description": "Selamat! Anda telah menyelesaikan kebutuhan SKS minimal.",
"stats": stats
}
else:
stats["required_pace"] = sks_needed
return {
"status": "Terlambat",
"color": "red",
"description": f"Saat ini semester {current_semester} dan SKS belum terpenuhi. Target lulus 8 semester sudah terlewat.",
"stats": stats
}
# 2. Hitung Kecepatan yang Dibutuhkan (Required Pace)
required_sks_per_sem = sks_needed / semesters_left
stats["required_pace"] = round(required_sks_per_sem, 2)
# 3. Evaluasi Status (Teks dari User)
student_capacity = stats["student_capacity"]
if required_sks_per_sem > MAX_SKS_REGULAR:
# KASUS: Mustahil reguler
return {
"status": "🔴 Terlambat",
"color": "red",
"description": f"Target lulus semester 8 tidak memungkinkan. Anda butuh rata-rata {required_sks_per_sem:.1f} SKS yang perlu dipenuhi setiap semester selanjutnya, melebihi batas reguler yaitu 24 SKS.",
"stats": stats
}
elif required_sks_per_sem <= SAFE_THRESHOLD:
# KASUS: Aman
return {
"status": "🟢 Aman",
"color": "green",
"description": f"Posisi aman. Beban ringan, sisa (~{required_sks_per_sem:.1f} SKS yang perlu dipenuhi tiap semester. Pertahankan performa tiap semester!",
"stats": stats
}
elif required_sks_per_sem <= student_capacity:
# KASUS: Padat tapi Masih Mungkin
status_text = "🟡 Jadwal Relatif Padat"
return {
"status": status_text,
"color": "yellow",
"description": f"Diperkirakan anda butuh ~{required_sks_per_sem:.1f} SKS yang perlu dipenuhi untuk semester-semester selanjutnya. Kapasitas SKS Anda ({student_capacity}) Sudah cukup mendukung untuk mengejar target ini.",
"stats": stats
}
else:
# KASUS: Terhambat IPK
return {
"status": "🟠 Rawan Terlambat",
"color": "orange",
"description": f"Hati-hati! Anda butuh {required_sks_per_sem:.1f} SKS tiap semester agar lulus tepat waktu, tapi IPK saat ini membatasi jatah cuma {student_capacity} SKS.",
"stats": stats
}
def predict_graduation_status(
current_semester: int,
total_sks_passed: int,
last_gpa: float,
graph_G: nx.DiGraph = None, # Optional: Graph object (pass by reference)
passed_courses: List[str] = None # Optional: List kode MK lulus
) -> Dict[str, Any]:
"""
Fungsi utama: Menjalankan logika Naive, lalu melakukan Override jika
ditemukan masalah struktural pada graf (rantai prasyarat).
"""
# 1. Jalankan Prediksi Naive (Matematis)
result = _predict_naive(current_semester, total_sks_passed, last_gpa)
# Jika data graf tidak lengkap atau status sudah Critical/Lulus, kembalikan hasil naive
if graph_G is None or passed_courses is None:
return result
if result["color"] == "red" or result["status"] == "Lulus":
return result
# 2. LOGIKA OVERRIDE: Cek Rantai Prasyarat (Critical Path)
try:
# A. Identifikasi MK yang BELUM lulus
all_courses = set(graph_G.nodes())
passed_set = set(passed_courses)
unpassed_courses = list(all_courses - passed_set)
if not unpassed_courses:
return result
# B. Buat Subgraph (Hanya berisi matkul sisa & relasinya)
subgraph_remaining = graph_G.subgraph(unpassed_courses)
# C. Hitung Longest Path (Rantai Terpanjang) di subgraph
# dag_longest_path mengembalikan list node, misal ['A', 'B', 'C'] -> Panjang 3
if nx.is_directed_acyclic_graph(subgraph_remaining):
longest_chain_path = nx.dag_longest_path(subgraph_remaining)
min_semesters_needed_structural = len(longest_chain_path)
semesters_left = result["stats"]["semesters_left"]
# D. Bandingkan dengan Sisa Waktu
if min_semesters_needed_structural > semesters_left:
result["status"] = "🔴 Terlambat (Struktural)"
result["color"] = "red"
result["description"] = (
f"SKS tersisa cukup, namun terdeteksi rantai prasyarat panjang yang terdiri"
f"({min_semesters_needed_structural} Mata Kuliah Beruntun) dan tidak bisa diambil sekaligus dalam sisa waktu."
)
# Tambahkan info debug ke stats
result["stats"]["structural_issue"] = True
result["stats"]["longest_chain_len"] = min_semesters_needed_structural
result["stats"]["longest_chain_path"] = longest_chain_path
except Exception as e:
print(f"Graph Analysis Warning: {e}")
# Jika error graf, fallback ke hasil naive
return result
return result |