Spaces:
Sleeping
Sleeping
| # ====================================================================== | |
| # --- 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 |