Update sentiment_app.py
Browse files- sentiment_app.py +52 -29
sentiment_app.py
CHANGED
|
@@ -5,9 +5,9 @@ import pandas as pd
|
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
| 7 |
from datetime import datetime
|
| 8 |
-
import collections
|
| 9 |
|
| 10 |
# --- SETUP MODEL ---
|
|
|
|
| 11 |
MODEL_NAME = "w11wo/indonesian-roberta-base-sentiment-classifier"
|
| 12 |
device = 0 if torch.cuda.is_available() else -1
|
| 13 |
sentiment_pipeline = pipeline("sentiment-analysis", model=MODEL_NAME, device=device)
|
|
@@ -15,7 +15,7 @@ sentiment_pipeline = pipeline("sentiment-analysis", model=MODEL_NAME, device=dev
|
|
| 15 |
# --- DATABASE SEDERHANA (In-Memory) ---
|
| 16 |
all_messages = []
|
| 17 |
|
| 18 |
-
# Mapping Label untuk Tampilan UI
|
| 19 |
label_map = {
|
| 20 |
"POSITIVE": "Pujian/Apresiasi",
|
| 21 |
"NEGATIVE": "Keluhan/Kritik",
|
|
@@ -26,13 +26,13 @@ def process_submission(text):
|
|
| 26 |
if not text or text.strip() == "":
|
| 27 |
return "β οΈ Mohon isi komentar Anda terlebih dahulu.", gr.update()
|
| 28 |
|
| 29 |
-
# 1. Analisis Sentimen
|
| 30 |
result = sentiment_pipeline(text)[0]
|
| 31 |
label = result['label'].upper()
|
| 32 |
|
| 33 |
-
# 2. Simpan ke Database Lokal
|
| 34 |
new_entry = {
|
| 35 |
-
"Waktu": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
| 36 |
"Pesan": text.strip(),
|
| 37 |
"Sentimen": label
|
| 38 |
}
|
|
@@ -44,13 +44,13 @@ def process_submission(text):
|
|
| 44 |
|
| 45 |
def get_admin_dashboard(filter_val):
|
| 46 |
if not all_messages:
|
| 47 |
-
return None, pd.DataFrame(columns=["
|
| 48 |
|
| 49 |
df_all = pd.DataFrame(all_messages)
|
| 50 |
|
| 51 |
# --- LOGIKA FILTER ---
|
| 52 |
if filter_val != "SEMUA":
|
| 53 |
-
# Balik mapping untuk
|
| 54 |
rev_map = {v: k for k, v in label_map.items()}
|
| 55 |
target = rev_map.get(filter_val)
|
| 56 |
df_filtered = df_all[df_all['Sentimen'] == target]
|
|
@@ -58,37 +58,44 @@ def get_admin_dashboard(filter_val):
|
|
| 58 |
df_filtered = df_all
|
| 59 |
|
| 60 |
if df_filtered.empty:
|
| 61 |
-
return None, pd.DataFrame(columns=["
|
| 62 |
|
| 63 |
-
# --- VISUALISASI TOTAL ---
|
| 64 |
fig, ax = plt.subplots(figsize=(8, 5))
|
| 65 |
-
color_map = {"POSITIVE": "#4CAF50", "NEGATIVE": "#F44336", "NEUTRAL": "#FFC107"}
|
| 66 |
counts = df_all['Sentimen'].value_counts()
|
|
|
|
| 67 |
counts.index = [label_map.get(i, i) for i in counts.index]
|
| 68 |
|
| 69 |
sns.barplot(x=counts.index, y=counts.values, palette="viridis", ax=ax)
|
| 70 |
ax.set_title("Proporsi Pesan Masuk (Total)", fontsize=12, fontweight='bold')
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
| 78 |
|
| 79 |
# --- INTERFACE GRADIO ---
|
| 80 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="emerald"), title="PoskoLog Dashboard") as demo:
|
| 81 |
gr.Markdown("# π¦ PoskoLog: Suara Pengungsi")
|
|
|
|
| 82 |
|
| 83 |
with gr.Tabs():
|
| 84 |
-
# TAB USER
|
| 85 |
with gr.Tab("π Sampaikan Pesan"):
|
| 86 |
with gr.Column(variant="panel"):
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
submit_btn = gr.Button("Kirim Pesan", variant="primary")
|
| 89 |
user_feedback = gr.Markdown("")
|
| 90 |
|
| 91 |
-
# TAB ADMIN
|
| 92 |
with gr.Tab("π Dashboard Admin"):
|
| 93 |
with gr.Row():
|
| 94 |
sentiment_filter = gr.Dropdown(
|
|
@@ -96,20 +103,36 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="emerald"), title="PoskoLog Dash
|
|
| 96 |
value="SEMUA",
|
| 97 |
label="Filter Sentimen"
|
| 98 |
)
|
| 99 |
-
refresh_btn = gr.Button("π Refresh & Filter", variant="secondary")
|
| 100 |
|
| 101 |
with gr.Row():
|
| 102 |
-
with gr.Column():
|
| 103 |
plot_output = gr.Plot(label="Grafik Distribusi")
|
| 104 |
-
with gr.Column():
|
| 105 |
-
gr.Markdown("####
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
-
status_txt = gr.Markdown("")
|
| 109 |
|
| 110 |
-
#
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
if __name__ == "__main__":
|
| 115 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
| 7 |
from datetime import datetime
|
|
|
|
| 8 |
|
| 9 |
# --- SETUP MODEL ---
|
| 10 |
+
# Menggunakan model RoBERTa Bahasa Indonesia untuk analisis sentimen
|
| 11 |
MODEL_NAME = "w11wo/indonesian-roberta-base-sentiment-classifier"
|
| 12 |
device = 0 if torch.cuda.is_available() else -1
|
| 13 |
sentiment_pipeline = pipeline("sentiment-analysis", model=MODEL_NAME, device=device)
|
|
|
|
| 15 |
# --- DATABASE SEDERHANA (In-Memory) ---
|
| 16 |
all_messages = []
|
| 17 |
|
| 18 |
+
# Mapping Label untuk Tampilan UI agar lebih mudah dipahami manusia
|
| 19 |
label_map = {
|
| 20 |
"POSITIVE": "Pujian/Apresiasi",
|
| 21 |
"NEGATIVE": "Keluhan/Kritik",
|
|
|
|
| 26 |
if not text or text.strip() == "":
|
| 27 |
return "β οΈ Mohon isi komentar Anda terlebih dahulu.", gr.update()
|
| 28 |
|
| 29 |
+
# 1. Analisis Sentimen menggunakan Pipeline Hugging Face
|
| 30 |
result = sentiment_pipeline(text)[0]
|
| 31 |
label = result['label'].upper()
|
| 32 |
|
| 33 |
+
# 2. Simpan ke Database Lokal dengan Timestamp
|
| 34 |
new_entry = {
|
| 35 |
+
"Waktu": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 36 |
"Pesan": text.strip(),
|
| 37 |
"Sentimen": label
|
| 38 |
}
|
|
|
|
| 44 |
|
| 45 |
def get_admin_dashboard(filter_val):
|
| 46 |
if not all_messages:
|
| 47 |
+
return None, pd.DataFrame(columns=["Waktu", "Pesan", "Sentimen"]), "Belum ada data."
|
| 48 |
|
| 49 |
df_all = pd.DataFrame(all_messages)
|
| 50 |
|
| 51 |
# --- LOGIKA FILTER ---
|
| 52 |
if filter_val != "SEMUA":
|
| 53 |
+
# Balik mapping untuk mencari label asli (POSITIVE/NEGATIVE/NEUTRAL)
|
| 54 |
rev_map = {v: k for k, v in label_map.items()}
|
| 55 |
target = rev_map.get(filter_val)
|
| 56 |
df_filtered = df_all[df_all['Sentimen'] == target]
|
|
|
|
| 58 |
df_filtered = df_all
|
| 59 |
|
| 60 |
if df_filtered.empty:
|
| 61 |
+
return None, pd.DataFrame(columns=["Waktu", "Pesan", "Sentimen"]), f"Tidak ada data untuk kategori: {filter_val}"
|
| 62 |
|
| 63 |
+
# --- VISUALISASI TOTAL (Pie Chart atau Bar Plot) ---
|
| 64 |
fig, ax = plt.subplots(figsize=(8, 5))
|
|
|
|
| 65 |
counts = df_all['Sentimen'].value_counts()
|
| 66 |
+
# Mengubah index angka/label asli ke label buatan kita (Pujian/Keluhan/dll)
|
| 67 |
counts.index = [label_map.get(i, i) for i in counts.index]
|
| 68 |
|
| 69 |
sns.barplot(x=counts.index, y=counts.values, palette="viridis", ax=ax)
|
| 70 |
ax.set_title("Proporsi Pesan Masuk (Total)", fontsize=12, fontweight='bold')
|
| 71 |
+
ax.set_ylabel("Jumlah Pesan")
|
| 72 |
+
|
| 73 |
+
# --- TABEL DENGAN KOLOM WAKTU/TANGGAL ---
|
| 74 |
+
display_df = df_filtered[["Waktu", "Pesan", "Sentimen"]].copy()
|
| 75 |
+
display_df['Sentimen'] = display_df['Sentimen'].map(label_map) # Percantik label di tabel
|
| 76 |
+
display_df = display_df.sort_values(by="Waktu", ascending=False) # Urutkan: Terbaru di atas
|
| 77 |
+
|
| 78 |
+
return fig, display_df, f"Menampilkan {len(df_filtered)} pesan ({filter_val})"
|
| 79 |
|
| 80 |
# --- INTERFACE GRADIO ---
|
| 81 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="emerald"), title="PoskoLog Dashboard") as demo:
|
| 82 |
gr.Markdown("# π¦ PoskoLog: Suara Pengungsi")
|
| 83 |
+
gr.Markdown("Sistem analisis sentimen otomatis untuk memprioritaskan laporan darurat pasca-bencana.")
|
| 84 |
|
| 85 |
with gr.Tabs():
|
| 86 |
+
# --- TAB USER ---
|
| 87 |
with gr.Tab("π Sampaikan Pesan"):
|
| 88 |
with gr.Column(variant="panel"):
|
| 89 |
+
gr.Markdown("### Laporkan kondisi atau berikan masukan Anda")
|
| 90 |
+
user_input = gr.Textbox(
|
| 91 |
+
label="Komentar Anda",
|
| 92 |
+
placeholder="Contoh: Bantuan air bersih belum sampai di tenda C...",
|
| 93 |
+
lines=4
|
| 94 |
+
)
|
| 95 |
submit_btn = gr.Button("Kirim Pesan", variant="primary")
|
| 96 |
user_feedback = gr.Markdown("")
|
| 97 |
|
| 98 |
+
# --- TAB ADMIN ---
|
| 99 |
with gr.Tab("π Dashboard Admin"):
|
| 100 |
with gr.Row():
|
| 101 |
sentiment_filter = gr.Dropdown(
|
|
|
|
| 103 |
value="SEMUA",
|
| 104 |
label="Filter Sentimen"
|
| 105 |
)
|
| 106 |
+
refresh_btn = gr.Button("π Refresh & Filter Data", variant="secondary")
|
| 107 |
|
| 108 |
with gr.Row():
|
| 109 |
+
with gr.Column(scale=1):
|
| 110 |
plot_output = gr.Plot(label="Grafik Distribusi")
|
| 111 |
+
with gr.Column(scale=2):
|
| 112 |
+
gr.Markdown("#### Daftar Laporan Masuk")
|
| 113 |
+
# Tabel diperbarui dengan kolom Waktu
|
| 114 |
+
table_output = gr.Dataframe(
|
| 115 |
+
headers=["Waktu", "Pesan", "Sentimen"],
|
| 116 |
+
interactive=False,
|
| 117 |
+
wrap=True
|
| 118 |
+
)
|
| 119 |
|
| 120 |
+
status_txt = gr.Markdown("Klik 'Refresh' untuk memuat data terbaru.")
|
| 121 |
|
| 122 |
+
# --- BINDING EVENTS ---
|
| 123 |
+
# Saat klik kirim: proses teks, beri feedback, dan kosongkan textbox
|
| 124 |
+
submit_btn.click(
|
| 125 |
+
fn=process_submission,
|
| 126 |
+
inputs=user_input,
|
| 127 |
+
outputs=[user_feedback, user_input]
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
# Saat klik refresh: update grafik dan tabel berdasarkan filter
|
| 131 |
+
refresh_btn.click(
|
| 132 |
+
fn=get_admin_dashboard,
|
| 133 |
+
inputs=sentiment_filter,
|
| 134 |
+
outputs=[plot_output, table_output, status_txt]
|
| 135 |
+
)
|
| 136 |
|
| 137 |
if __name__ == "__main__":
|
| 138 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|