import gradio as gr import torch from transformers import pipeline import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from datetime import datetime # --- SETUP MODEL --- # Menggunakan model RoBERTa Bahasa Indonesia untuk analisis sentimen MODEL_NAME = "w11wo/indonesian-roberta-base-sentiment-classifier" device = 0 if torch.cuda.is_available() else -1 sentiment_pipeline = pipeline("sentiment-analysis", model=MODEL_NAME, device=device) # --- DATABASE SEDERHANA (In-Memory) --- all_messages = [] # Mapping Label untuk Tampilan UI agar lebih mudah dipahami manusia label_map = { "POSITIVE": "Pujian/Apresiasi", "NEGATIVE": "Keluhan/Kritik", "NEUTRAL": "Pertanyaan/Info" } def process_submission(text): if not text or text.strip() == "": return "⚠️ Mohon isi komentar Anda terlebih dahulu.", gr.update() # 1. Analisis Sentimen menggunakan Pipeline Hugging Face result = sentiment_pipeline(text)[0] label = result['label'].upper() # 2. Simpan ke Database Lokal dengan Timestamp new_entry = { "Waktu": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "Pesan": text.strip(), "Sentimen": label } all_messages.append(new_entry) # 3. Respons untuk User thanks_msg = "### 🙏 Terima kasih atas pesan Anda!\nMasukan Anda telah kami terima dan akan segera ditinjau oleh tim admin posko." return thanks_msg, gr.update(value="") def get_admin_dashboard(filter_val): if not all_messages: return None, pd.DataFrame(columns=["Waktu", "Pesan", "Sentimen"]), "Belum ada data." df_all = pd.DataFrame(all_messages) # --- LOGIKA FILTER --- if filter_val != "SEMUA": # Balik mapping untuk mencari label asli (POSITIVE/NEGATIVE/NEUTRAL) rev_map = {v: k for k, v in label_map.items()} target = rev_map.get(filter_val) df_filtered = df_all[df_all['Sentimen'] == target] else: df_filtered = df_all if df_filtered.empty: return None, pd.DataFrame(columns=["Waktu", "Pesan", "Sentimen"]), f"Tidak ada data untuk kategori: {filter_val}" # --- VISUALISASI TOTAL (Pie Chart atau Bar Plot) --- fig, ax = plt.subplots(figsize=(8, 5)) counts = df_all['Sentimen'].value_counts() # Mengubah index angka/label asli ke label buatan kita (Pujian/Keluhan/dll) counts.index = [label_map.get(i, i) for i in counts.index] sns.barplot(x=counts.index, y=counts.values, palette="viridis", ax=ax) ax.set_title("Proporsi Pesan Masuk (Total)", fontsize=12, fontweight='bold') ax.set_ylabel("Jumlah Pesan") # --- TABEL DENGAN KOLOM WAKTU/TANGGAL --- display_df = df_filtered[["Waktu", "Pesan", "Sentimen"]].copy() display_df['Sentimen'] = display_df['Sentimen'].map(label_map) # Percantik label di tabel display_df = display_df.sort_values(by="Waktu", ascending=False) # Urutkan: Terbaru di atas return fig, display_df, f"Menampilkan {len(df_filtered)} pesan ({filter_val})" # --- INTERFACE GRADIO --- with gr.Blocks(theme=gr.themes.Soft(primary_hue="emerald"), title="PoskoLog Dashboard") as demo: gr.Markdown("# 📦 PoskoLog: Suara Pengungsi") gr.Markdown("Sistem analisis sentimen otomatis untuk memprioritaskan laporan darurat pasca-bencana.") with gr.Tabs(): # --- TAB USER --- with gr.Tab("📝 Sampaikan Pesan"): with gr.Column(variant="panel"): gr.Markdown("### Laporkan kondisi atau berikan masukan Anda") user_input = gr.Textbox( label="Komentar Anda", placeholder="Contoh: Bantuan air bersih belum sampai di tenda C...", lines=4 ) submit_btn = gr.Button("Kirim Pesan", variant="primary") user_feedback = gr.Markdown("") # --- TAB ADMIN --- with gr.Tab("📊 Dashboard Admin"): with gr.Row(): sentiment_filter = gr.Dropdown( choices=["SEMUA"] + list(label_map.values()), value="SEMUA", label="Filter Sentimen" ) refresh_btn = gr.Button("🔄 Refresh & Filter Data", variant="secondary") with gr.Row(): with gr.Column(scale=1): plot_output = gr.Plot(label="Grafik Distribusi") with gr.Column(scale=2): gr.Markdown("#### Daftar Laporan Masuk") # Tabel diperbarui dengan kolom Waktu table_output = gr.Dataframe( headers=["Waktu", "Pesan", "Sentimen"], interactive=False, wrap=True ) status_txt = gr.Markdown("Klik 'Refresh' untuk memuat data terbaru.") # --- BINDING EVENTS --- # Saat klik kirim: proses teks, beri feedback, dan kosongkan textbox submit_btn.click( fn=process_submission, inputs=user_input, outputs=[user_feedback, user_input] ) # Saat klik refresh: update grafik dan tabel berdasarkan filter refresh_btn.click( fn=get_admin_dashboard, inputs=sentiment_filter, outputs=[plot_output, table_output, status_txt] ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)