Spaces:
Sleeping
Sleeping
Update explanation_builder.py
Browse files- explanation_builder.py +142 -48
explanation_builder.py
CHANGED
|
@@ -1,96 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import random
|
| 2 |
from typing import List, Dict, Any
|
| 3 |
|
| 4 |
# ======================================================================
|
| 5 |
-
# 1.
|
| 6 |
# ======================================================================
|
| 7 |
EXPLANATION_TEMPLATES = {
|
| 8 |
"pembuka": {
|
| 9 |
-
"Resiko Tinggi":
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
},
|
| 15 |
"fitur": {
|
|
|
|
| 16 |
"IPS_Terakhir": {
|
| 17 |
"rendah": [
|
| 18 |
-
"
|
| 19 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
],
|
| 21 |
"tinggi": [
|
| 22 |
-
"
|
| 23 |
-
"
|
|
|
|
| 24 |
]
|
| 25 |
},
|
|
|
|
|
|
|
| 26 |
"Jumlah_MK_Gagal": {
|
| 27 |
"rendah": [
|
| 28 |
-
"
|
| 29 |
-
"
|
|
|
|
| 30 |
],
|
| 31 |
"tinggi": [
|
| 32 |
-
"
|
| 33 |
-
"
|
|
|
|
| 34 |
]
|
| 35 |
},
|
| 36 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
"rendah": [
|
| 38 |
-
"
|
| 39 |
-
"
|
| 40 |
],
|
| 41 |
"tinggi": [
|
| 42 |
-
"
|
| 43 |
-
"
|
| 44 |
]
|
| 45 |
},
|
|
|
|
|
|
|
| 46 |
"Tren_IPS_Slope": {
|
| 47 |
"rendah": [
|
| 48 |
-
"
|
| 49 |
-
"
|
| 50 |
],
|
| 51 |
"tinggi": [
|
| 52 |
-
"
|
| 53 |
-
"
|
| 54 |
]
|
| 55 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
"Tren_Menaik": {
|
| 57 |
-
"ya": "
|
| 58 |
-
"tidak": "
|
| 59 |
},
|
| 60 |
"Tren_Menurun": {
|
| 61 |
-
"ya": "
|
| 62 |
-
"tidak": "
|
| 63 |
},
|
| 64 |
"Tren_Stabil": {
|
| 65 |
-
"ya": "
|
| 66 |
-
"tidak": "
|
| 67 |
},
|
|
|
|
|
|
|
| 68 |
"default": {
|
| 69 |
-
"rendah": "Nilai {feature_name}
|
| 70 |
-
"tinggi": "Nilai {feature_name}
|
| 71 |
}
|
| 72 |
}
|
| 73 |
}
|
| 74 |
|
|
|
|
| 75 |
# ======================================================================
|
| 76 |
-
# 2. FUNGSI BUILDER
|
| 77 |
# ======================================================================
|
| 78 |
|
| 79 |
def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: str) -> Dict[str, Any]:
|
| 80 |
"""
|
| 81 |
-
Merakit penjelasan
|
| 82 |
"""
|
| 83 |
try:
|
| 84 |
-
# 1. Pilih
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
explained_rules_list = []
|
| 88 |
-
features_explained = set()
|
| 89 |
-
|
| 90 |
-
# 2. Iterasi aturan SECARA TERBALIK (dari
|
|
|
|
| 91 |
for rule in reversed(structured_rules):
|
| 92 |
feature = rule["feature"]
|
| 93 |
|
|
|
|
|
|
|
| 94 |
if feature in features_explained:
|
| 95 |
continue
|
| 96 |
|
|
@@ -99,20 +182,30 @@ def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: s
|
|
| 99 |
condition = rule["condition"]
|
| 100 |
chosen_template = None
|
| 101 |
|
| 102 |
-
# Logika untuk Fitur
|
| 103 |
if feature in ["Tren_Menaik", "Tren_Menurun", "Tren_Stabil"]:
|
| 104 |
if condition == "tinggi":
|
| 105 |
chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["ya"]
|
| 106 |
else:
|
| 107 |
chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["tidak"]
|
| 108 |
|
| 109 |
-
# Logika untuk Fitur
|
| 110 |
else:
|
|
|
|
| 111 |
feature_templates = EXPLANATION_TEMPLATES["fitur"].get(feature, EXPLANATION_TEMPLATES["fitur"]["default"])
|
|
|
|
|
|
|
| 112 |
condition_templates = feature_templates.get(condition)
|
| 113 |
|
|
|
|
| 114 |
if isinstance(condition_templates, list):
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
elif isinstance(condition_templates, str):
|
| 117 |
chosen_template = condition_templates.format(
|
| 118 |
feature_name=feature,
|
|
@@ -123,19 +216,20 @@ def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: s
|
|
| 123 |
if chosen_template:
|
| 124 |
explained_rules_list.append(chosen_template)
|
| 125 |
|
| 126 |
-
# 3. Balikkan list agar urutannya logis (
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
-
# 4. Kembalikan sebagai DICT alih-alih string
|
| 130 |
return {
|
| 131 |
"opening_line": opening_line,
|
| 132 |
"factors": explained_rules_list
|
| 133 |
}
|
| 134 |
|
| 135 |
except Exception as e:
|
| 136 |
-
# Kembalikan error dalam format yang sama
|
| 137 |
return {
|
| 138 |
-
"opening_line": f"
|
| 139 |
"factors": []
|
| 140 |
-
}
|
| 141 |
-
|
|
|
|
| 1 |
+
# ======================================================================
|
| 2 |
+
# --- explanation_builder.py ---
|
| 3 |
+
# ======================================================================
|
| 4 |
+
|
| 5 |
import random
|
| 6 |
from typing import List, Dict, Any
|
| 7 |
|
| 8 |
# ======================================================================
|
| 9 |
+
# 1. BANK TEMPLATE (Natural Language & Emoji)
|
| 10 |
# ======================================================================
|
| 11 |
EXPLANATION_TEMPLATES = {
|
| 12 |
"pembuka": {
|
| 13 |
+
"Resiko Tinggi": [
|
| 14 |
+
"β οΈ **Perhatian Serius Diperlukan:** Sistem mendeteksi indikator risiko tinggi pada profil akademik Anda. Berikut adalah faktor krusial yang memicunya:",
|
| 15 |
+
"π¨ **Peringatan Dini:** Berdasarkan pola data historis, performa Anda saat ini berada dalam zona 'Resiko Tinggi'. Hal ini didorong oleh faktor-faktor berikut:",
|
| 16 |
+
"π **Analisis Kritis:** Terdapat akumulasi faktor yang menempatkan Anda pada kategori 'Resiko Tinggi'. Mohon perhatikan poin-poin evaluasi ini:"
|
| 17 |
+
],
|
| 18 |
+
"Resiko Sedang": [
|
| 19 |
+
"β οΈ **Waspada:** Profil Anda menunjukkan tanda-tanda 'Resiko Sedang'. Belum kritis, namun perlu perbaikan segera pada aspek berikut:",
|
| 20 |
+
"π‘ **Perlu Evaluasi:** Sistem mendeteksi adanya ketidakstabilan yang memicu status 'Resiko Sedang'. Berikut adalah area yang perlu mendapat perhatian:",
|
| 21 |
+
"π© **Zona Kuning:** Anda berada di kategori 'Resiko Sedang'. Ada keseimbangan antara faktor positif dan negatif, namun poin berikut perlu diwaspadai:"
|
| 22 |
+
],
|
| 23 |
+
"Resiko Rendah": [
|
| 24 |
+
"β
**Cukup Aman:** Profil akademik Anda tergolong 'Resiko Rendah', namun tetap ada beberapa catatan kecil untuk menjaga konsistensi:",
|
| 25 |
+
"π **Progres Baik:** Secara umum performa Anda stabil di zona aman. Sistem hanya menyoroti beberapa hal minor berikut:",
|
| 26 |
+
"π‘οΈ **Terkendali:** Prediksi risiko Anda rendah. Pertahankan momentum ini, sambil memperhatikan sedikit catatan berikut:"
|
| 27 |
+
],
|
| 28 |
+
"Aman": [
|
| 29 |
+
"π **Sangat Baik:** Selamat! Rekam jejak akademik Anda sangat solid sehingga dikategorikan 'Aman'. Faktor pendukung utamanya adalah:",
|
| 30 |
+
"π **Performa Unggul:** Sistem tidak mendeteksi masalah berarti. Prediksi 'Aman' ini didukung oleh pondasi akademik yang kuat berikut ini:",
|
| 31 |
+
"π **Top Performance:** Data menunjukkan stabilitas yang sangat baik. Berikut adalah poin-poin kekuatan profil Anda:"
|
| 32 |
+
],
|
| 33 |
+
"default": "π Berikut adalah faktor-faktor analisis sistem untuk kategori '{prediction_val}':"
|
| 34 |
},
|
| 35 |
"fitur": {
|
| 36 |
+
# --- 1. PERFORMA TERKINI (IPS) ---
|
| 37 |
"IPS_Terakhir": {
|
| 38 |
"rendah": [
|
| 39 |
+
"π **Penurunan Terkini:** IPS semester terakhir Anda tercatat **{value:.2f}**, yang berada di bawah ambang batas ideal.",
|
| 40 |
+
"β οΈ **Warning Semester Lalu:** Performa semester terakhir (**{value:.2f}**) menjadi kontributor utama risiko karena belum memenuhi standar aman.",
|
| 41 |
+
"π» **Butuh Boost:** Capaian IPS terakhir (**{value:.2f}**) terindikasi rendah, menyeret turun profil risiko Anda secara keseluruhan."
|
| 42 |
+
],
|
| 43 |
+
"tinggi": [
|
| 44 |
+
"β
**Momentum Positif:** IPS semester terakhir Anda (**{value:.2f}**) sangat solid, menunjukkan Anda sedang dalam performa yang baik.",
|
| 45 |
+
"π **Finish Kuat:** Capaian semester terakhir yang tinggi (**{value:.2f}**) menjadi sinyal kuat bahwa Anda mampu mengatasi beban studi.",
|
| 46 |
+
"π **Nilai Kompetitif:** IPS terakhir di angka **{value:.2f}** memberikan bobot positif yang signifikan pada prediksi ini."
|
| 47 |
+
]
|
| 48 |
+
},
|
| 49 |
+
|
| 50 |
+
# --- 2. PERFORMA KUMULATIF (IPK) ---
|
| 51 |
+
"IPK_Terakhir": {
|
| 52 |
+
"rendah": [
|
| 53 |
+
"ποΈ **Pondasi Rapuh:** IPK kumulatif saat ini (**{value:.2f}**) terdeteksi di zona yang memerlukan perbaikan segera.",
|
| 54 |
+
"π **Akumulasi Nilai:** Secara keseluruhan, IPK Anda (**{value:.2f}**) masih di bawah ambang batas aman sistem.",
|
| 55 |
+
"π **Perhatian:** IPK Terakhir (**{value:.2f}**) adalah faktor dominan yang menempatkan Anda pada risiko ini."
|
| 56 |
],
|
| 57 |
"tinggi": [
|
| 58 |
+
"ποΈ **Pondasi Kokoh:** IPK kumulatif Anda (**{value:.2f}**) sangat baik dan menjadi penyangga utama profil akademik Anda.",
|
| 59 |
+
"π‘οΈ **Rekam Jejak Solid:** Konsistensi nilai yang tercermin dari IPK (**{value:.2f}**) menjauhkan Anda dari risiko akademik.",
|
| 60 |
+
"π **Prestasi Stabil:** IPK di angka **{value:.2f}** menunjukkan pemahaman materi yang konsisten di atas rata-rata."
|
| 61 |
]
|
| 62 |
},
|
| 63 |
+
|
| 64 |
+
# --- 3. KEGAGALAN MATA KULIAH ---
|
| 65 |
"Jumlah_MK_Gagal": {
|
| 66 |
"rendah": [
|
| 67 |
+
"β¨ **Rekam Jejak Bersih:** Anda memiliki sedikit/tanpa mata kuliah gagal (Total: {value}), yang sangat bagus untuk kelancaran studi.",
|
| 68 |
+
"β
**Efisiensi Studi:** Minimnya mata kuliah yang harus diulang (Total: {value}) adalah indikator positif yang kuat.",
|
| 69 |
+
"π‘οΈ **Bebas Hambatan:** Tidak adanya beban mata kuliah gagal yang signifikan menjaga risiko Anda tetap rendah."
|
| 70 |
],
|
| 71 |
"tinggi": [
|
| 72 |
+
"π **Beban Mengulang:** Terdapat **{value}** mata kuliah gagal. Tumpukan beban ini meningkatkan risiko akademik secara signifikan.",
|
| 73 |
+
"π§ **Hambatan Studi:** Jumlah mata kuliah gagal yang tinggi (**{value} MK**) terdeteksi sebagai 'red flag' utama dalam profil ini.",
|
| 74 |
+
"π¨ **Perlu Perbaikan:** Akumulasi **{value}** mata kuliah yang belum lulus menuntut perhatian ekstra untuk semester depan."
|
| 75 |
]
|
| 76 |
},
|
| 77 |
+
"Total_SKS_Gagal": {
|
| 78 |
+
"rendah": [
|
| 79 |
+
"β
**Minim SKS Hangus:** Total SKS dari mata kuliah gagal sangat minim.",
|
| 80 |
+
"β¨ **Efektif:** Hampir seluruh SKS yang diambil berhasil lulus."
|
| 81 |
+
],
|
| 82 |
+
"tinggi": [
|
| 83 |
+
"β οΈ **SKS Terbuang:** Total SKS gagal yang besar membebani rasio kelulusan Anda.",
|
| 84 |
+
"π **Warning SKS:** Banyak kredit SKS yang harus diulang."
|
| 85 |
+
]
|
| 86 |
+
},
|
| 87 |
+
|
| 88 |
+
# --- 4. PROGRES SKS ---
|
| 89 |
+
"Total_SKS": {
|
| 90 |
"rendah": [
|
| 91 |
+
"β³ **Progres Lambat:** Total SKS yang berhasil dikumpulkan (**{value}**) masih tertinggal dari target ideal tahap ini.",
|
| 92 |
+
"π’ **Perlu Akselerasi:** Jumlah SKS lulus (**{value}**) tergolong sedikit, mengindikasikan perlunya strategi pengambilan SKS yang lebih optimal."
|
| 93 |
],
|
| 94 |
"tinggi": [
|
| 95 |
+
"π **On-Track:** Tabungan SKS Anda (**{value}**) sudah cukup banyak, menandakan progres studi yang lancar.",
|
| 96 |
+
"π― **Target Tercapai:** Jumlah SKS lulus (**{value}**) sudah memenuhi standar progres yang diharapkan."
|
| 97 |
]
|
| 98 |
},
|
| 99 |
+
|
| 100 |
+
# --- 5. TREN & KONSISTENSI ---
|
| 101 |
"Tren_IPS_Slope": {
|
| 102 |
"rendah": [
|
| 103 |
+
"π **Tren Menurun:** Analisis regresi menunjukkan grafik performa Anda cenderung melandai/turun belakangan ini.",
|
| 104 |
+
"β οΈ **Kehilangan Momentum:** Nilai *slope* tren akademik Anda negatif/rendah, waspadai penurunan semangat belajar."
|
| 105 |
],
|
| 106 |
"tinggi": [
|
| 107 |
+
"π **Tren Menanjak:** Grafik nilai Anda menunjukkan tren kenaikan (slope positif). Pertahankan semangat ini!",
|
| 108 |
+
"π **Perbaikan Konsisten:** Sistem mendeteksi pola kenaikan performa yang konsisten dari waktu ke waktu."
|
| 109 |
]
|
| 110 |
},
|
| 111 |
+
"Rentang_IPS": {
|
| 112 |
+
"rendah": [
|
| 113 |
+
"βοΈ **Performa Stabil:** Fluktuasi nilai Anda kecil, menunjukkan konsistensi belajar yang baik.",
|
| 114 |
+
"πΉ **Konsisten:** Tidak ada lonjakan atau penurunan drastis pada sejarah nilai."
|
| 115 |
+
],
|
| 116 |
+
"tinggi": [
|
| 117 |
+
"π’ **Nilai Fluktuatif:** Terdeteksi rentang nilai yang lebar (tidak stabil). Performa Anda cenderung naik-turun drastis.",
|
| 118 |
+
"β οΈ **Inkonsistensi:** Ada semester di mana nilai sangat tinggi dan sangat rendah. Konsistensi perlu ditingkatkan."
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
|
| 122 |
+
# --- 6. FITUR ONE-HOT (TREN KATEGORIKAL) ---
|
| 123 |
"Tren_Menaik": {
|
| 124 |
+
"ya": "π **Grafik Positif:** Pola data Anda secara eksplisit dikategorikan sebagai tren 'Menaik'.",
|
| 125 |
+
"tidak": "πΈ **Tidak Ada Kenaikan:** Pola data saat ini tidak menunjukkan tren kenaikan yang signifikan."
|
| 126 |
},
|
| 127 |
"Tren_Menurun": {
|
| 128 |
+
"ya": "π **Peringatan Penurunan:** Pola data Anda secara eksplisit dikategorikan sebagai tren 'Menurun'. Segera lakukan evaluasi.",
|
| 129 |
+
"tidak": "β
**Aman dari Penurunan:** Untungnya, profil Anda tidak menunjukkan pola tren 'Menurun'."
|
| 130 |
},
|
| 131 |
"Tren_Stabil": {
|
| 132 |
+
"ya": "β‘οΈ **Stagnan/Stabil:** Pola nilai Anda cenderung datar (Stabil). Ini bisa baik atau buruk tergantung nilai rata-ratanya.",
|
| 133 |
+
"tidak": "π **Dinamis:** Profil nilai Anda tidak stagnan, ada pergerakan naik atau turun."
|
| 134 |
},
|
| 135 |
+
|
| 136 |
+
# --- DEFAULT FALLBACK ---
|
| 137 |
"default": {
|
| 138 |
+
"rendah": "πΉ Nilai **{feature_name}** tercatat **{value:.2f}**, lebih rendah dari batas acuan ({threshold:.2f}).",
|
| 139 |
+
"tinggi": "πΈ Nilai **{feature_name}** tercatat **{value:.2f}**, lebih tinggi dari batas acuan ({threshold:.2f})."
|
| 140 |
}
|
| 141 |
}
|
| 142 |
}
|
| 143 |
|
| 144 |
+
|
| 145 |
# ======================================================================
|
| 146 |
+
# 2. FUNGSI BUILDER (With Deduplication & Random Logic)
|
| 147 |
# ======================================================================
|
| 148 |
|
| 149 |
def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: str) -> Dict[str, Any]:
|
| 150 |
"""
|
| 151 |
+
Merakit penjelasan dinamis dengan emoji, anti-duplikat, dan variasi kalimat.
|
| 152 |
"""
|
| 153 |
try:
|
| 154 |
+
# 1. Pilih Kalimat Pembuka (Random Choice dari List)
|
| 155 |
+
opening_templates = EXPLANATION_TEMPLATES["pembuka"].get(prediction_val)
|
| 156 |
+
|
| 157 |
+
if not opening_templates:
|
| 158 |
+
# Fallback jika key prediksi tidak ditemukan di template
|
| 159 |
+
opening_line = EXPLANATION_TEMPLATES["pembuka"]["default"].format(prediction_val=prediction_val)
|
| 160 |
+
elif isinstance(opening_templates, list):
|
| 161 |
+
# Pilih salah satu variasi secara acak
|
| 162 |
+
opening_line = random.choice(opening_templates)
|
| 163 |
+
else:
|
| 164 |
+
# Jika ternyata cuma string tunggal
|
| 165 |
+
opening_line = opening_templates
|
| 166 |
|
| 167 |
explained_rules_list = []
|
| 168 |
+
features_explained = set() # Set untuk mencegah duplikasi faktor
|
| 169 |
+
|
| 170 |
+
# 2. Iterasi aturan SECARA TERBALIK (dari Leaf -> Root)
|
| 171 |
+
# Tujuannya: Mengambil aturan yang paling spesifik (di ujung pohon) terlebih dahulu.
|
| 172 |
for rule in reversed(structured_rules):
|
| 173 |
feature = rule["feature"]
|
| 174 |
|
| 175 |
+
# LOGIKA ANTI-DUPLIKAT
|
| 176 |
+
# Jika fitur sudah dijelaskan oleh node yang lebih spesifik, skip node induknya.
|
| 177 |
if feature in features_explained:
|
| 178 |
continue
|
| 179 |
|
|
|
|
| 182 |
condition = rule["condition"]
|
| 183 |
chosen_template = None
|
| 184 |
|
| 185 |
+
# A. Logika untuk Fitur One-Hot (Tren) -> Ya/Tidak
|
| 186 |
if feature in ["Tren_Menaik", "Tren_Menurun", "Tren_Stabil"]:
|
| 187 |
if condition == "tinggi":
|
| 188 |
chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["ya"]
|
| 189 |
else:
|
| 190 |
chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["tidak"]
|
| 191 |
|
| 192 |
+
# B. Logika untuk Fitur Numerik -> Rendah/Tinggi
|
| 193 |
else:
|
| 194 |
+
# Ambil template fitur, fallback ke default jika nama fitur tidak ada di bank template
|
| 195 |
feature_templates = EXPLANATION_TEMPLATES["fitur"].get(feature, EXPLANATION_TEMPLATES["fitur"]["default"])
|
| 196 |
+
|
| 197 |
+
# Ambil template kondisi (rendah/tinggi)
|
| 198 |
condition_templates = feature_templates.get(condition)
|
| 199 |
|
| 200 |
+
# Jika template berupa LIST (ada variasi kalimat), pilih satu acak
|
| 201 |
if isinstance(condition_templates, list):
|
| 202 |
+
template_str = random.choice(condition_templates)
|
| 203 |
+
chosen_template = template_str.format(
|
| 204 |
+
feature_name=feature,
|
| 205 |
+
value=rule["value"],
|
| 206 |
+
threshold=rule["threshold"]
|
| 207 |
+
)
|
| 208 |
+
# Jika template berupa STRING tunggal
|
| 209 |
elif isinstance(condition_templates, str):
|
| 210 |
chosen_template = condition_templates.format(
|
| 211 |
feature_name=feature,
|
|
|
|
| 216 |
if chosen_template:
|
| 217 |
explained_rules_list.append(chosen_template)
|
| 218 |
|
| 219 |
+
# 3. Balikkan list agar urutannya logis (Penting -> Pendukung)
|
| 220 |
+
# Karena tadi kita iterasi dari bawah (reversed), hasilnya jadi terbalik.
|
| 221 |
+
# Sebenarnya urutan "Leaf first" (Paling spesifik dulu) seringkali lebih baik untuk dibaca.
|
| 222 |
+
# Namun jika ingin urutan flow Decision Tree (Root -> Leaf), uncomment baris di bawah:
|
| 223 |
+
# explained_rules_list.reverse()
|
| 224 |
+
|
| 225 |
|
|
|
|
| 226 |
return {
|
| 227 |
"opening_line": opening_line,
|
| 228 |
"factors": explained_rules_list
|
| 229 |
}
|
| 230 |
|
| 231 |
except Exception as e:
|
|
|
|
| 232 |
return {
|
| 233 |
+
"opening_line": f"β οΈ Maaf, terjadi kesalahan saat menyusun penjelasan: {str(e)}",
|
| 234 |
"factors": []
|
| 235 |
+
}
|
|
|