| |
| |
| |
|
|
| 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 |
| LIMIT_SKS_LOW_GPA = 20 |
| SAFE_THRESHOLD = 18 |
| |
| |
| sks_needed = TARGET_SKS - total_sks_passed |
| semesters_left = (TARGET_SEMESTER - current_semester) + 1 |
| |
| |
| 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) |
| } |
|
|
| |
| 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 |
| } |
|
|
| |
| required_sks_per_sem = sks_needed / semesters_left |
| stats["required_pace"] = round(required_sks_per_sem, 2) |
| |
| |
| student_capacity = stats["student_capacity"] |
| |
| if required_sks_per_sem > MAX_SKS_REGULAR: |
| |
| 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: |
| |
| 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: |
| |
| 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: |
| |
| 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, |
| passed_courses: List[str] = None |
| ) -> Dict[str, Any]: |
| """ |
| Fungsi utama: Menjalankan logika Naive, lalu melakukan Override jika |
| ditemukan masalah struktural pada graf (rantai prasyarat). |
| """ |
| |
| |
| result = _predict_naive(current_semester, total_sks_passed, last_gpa) |
| |
| |
| if graph_G is None or passed_courses is None: |
| return result |
| |
| if result["color"] == "red" or result["status"] == "Lulus": |
| return result |
|
|
| |
| try: |
| |
| all_courses = set(graph_G.nodes()) |
| passed_set = set(passed_courses) |
| unpassed_courses = list(all_courses - passed_set) |
| |
| if not unpassed_courses: |
| return result |
|
|
| |
| subgraph_remaining = graph_G.subgraph(unpassed_courses) |
| |
| |
| |
| 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"] |
| |
| |
| 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." |
| ) |
| |
| 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}") |
| |
| return result |
|
|
| return result |