import streamlit as st import pandas as pd from datetime import datetime import json import os from utils import predict_sentiment HISTORY_FILE = 'data/riwayat_analisis.json' def load_history(): if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, 'r', encoding='utf-8') as f: try: return json.load(f) except: return [] return [] def save_history(data): os.makedirs('data', exist_ok=True) with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4) # ============================================================================== # Fungsi Clear sekarang menghapus Teks DAN Hasil Prediksi # ============================================================================== def clear_input(): st.session_state['input_teks_analisis'] = "" st.session_state['latest_result'] = None # ============================================================================== # RENDER HALAMAN UTAMA # ============================================================================== def render_analisis_teks(model, tokenizer): st.title("๐Ÿ’ฌ Analisis Sentimen (Single Text)") st.markdown("Ketikkan kalimat opini terkait kebijakan efisiensi anggaran pendidikan, dan biarkan AI memprediksi sentimennya secara *real-time*.") # 1. INISIALISASI SESSION STATE if 'history_analisis' not in st.session_state: st.session_state['history_analisis'] = load_history() if 'latest_result' not in st.session_state: st.session_state['latest_result'] = None if 'show_confirm' not in st.session_state: st.session_state['show_confirm'] = False if 'rows_to_delete' not in st.session_state: st.session_state['rows_to_delete'] = [] if 'input_teks_analisis' not in st.session_state: st.session_state['input_teks_analisis'] = "" # ============================================================================== # 2. AREA INPUT TEKS & TOMBOL # ============================================================================== input_text = st.text_area( "Masukkan Teks Opini di sini:", height=150, placeholder="Contoh: Sangat kecewa anggaran KIP Kuliah dipotong...", key='input_teks_analisis' ) # ============================================================================== # Deteksi Hapus Manual (Backspace) # ============================================================================== if not input_text.strip(): st.session_state['latest_result'] = None col_btn1, col_btn2, col_spacer = st.columns([2, 2, 6]) with col_btn1: btn_analisis = st.button("๐Ÿ” Analisis Sekarang", type="primary", use_container_width=True) with col_btn2: st.button("๐Ÿงน Bersihkan Teks", on_click=clear_input, use_container_width=True) if btn_analisis: if input_text.strip(): with st.spinner('๐Ÿค– Model LSTM sedang memproses teks...'): label, confidence, probs, clean_txt = predict_sentiment(input_text, model, tokenizer) probabilitas_bersih = [float(p) for p in probs] st.session_state['latest_result'] = { "label": label, "confidence": confidence, "probs": probabilitas_bersih, "clean_txt": clean_txt } waktu_sekarang = datetime.now().strftime("%Y-%m-%d %H:%M:%S") new_entry = { "Waktu": waktu_sekarang, "Teks Asli": input_text, "Teks Bersih": clean_txt, "Label": label, "Keyakinan (%)": round(confidence, 2) } st.session_state['history_analisis'].append(new_entry) save_history(st.session_state['history_analisis']) else: st.warning("โš ๏ธ Mohon masukkan teks terlebih dahulu.") # ============================================================================== # 3. AREA HASIL PREDIKSI # ============================================================================== if st.session_state['latest_result']: res = st.session_state['latest_result'] st.markdown("---") col_res1, col_res2 = st.columns([1, 2]) with col_res1: st.subheader("๐ŸŽฏ Hasil Prediksi") if res['label'] == "Positif": st.success(f"**๐ŸŸข SENTIMEN POSITIF**") elif res['label'] == "Negatif": st.error(f"**๐Ÿ”ด SENTIMEN NEGATIF**") else: st.warning(f"**โšช SENTIMEN NETRAL**") st.metric("Tingkat Keyakinan (Confidence)", f"{res['confidence']:.2f}%") with col_res2: st.subheader("๐Ÿ“Š Distribusi Probabilitas") st.caption("Detail perhitungan matematis model (Total 100%)") st.write(f"๐Ÿ”ด **Negatif:** {res['probs'][0]*100:.1f}%") st.progress(res['probs'][0]) st.write(f"โšช **Netral:** {res['probs'][1]*100:.1f}%") st.progress(res['probs'][1]) st.write(f"๐ŸŸข **Positif:** {res['probs'][2]*100:.1f}%") st.progress(res['probs'][2]) st.markdown("#### ๐Ÿ” Teks Hasil Preprocessing (Cleaning & Normalisasi)") st.info(f"{res['clean_txt']}") st.markdown("---") # ============================================================================== # 4. AREA HISTORY # ============================================================================== st.subheader("๐Ÿ“š Riwayat Analisis") if len(st.session_state['history_analisis']) > 0: # 1. Siapkan Data df_history = pd.DataFrame(st.session_state['history_analisis']) df_display = df_history.iloc[::-1].reset_index(drop=True) if 'Pilih' not in df_display.columns: df_display.insert(0, "Pilih", False) # 2. Filter & Select All c_search, c_all = st.columns([3, 1]) with c_search: q = st.text_input("Cari:", placeholder="Filter riwayat...", label_visibility="collapsed") with c_all: if st.checkbox("Pilih Semua"): df_display['Pilih'] = True if q: df_display = df_display[df_display['Teks Asli'].str.contains(q, case=False, na=False)] # 3. Tabel Editor with st.container(): edited_df = st.data_editor( df_display, column_config={ "Pilih": st.column_config.CheckboxColumn("Hapus?", width="small", default=False), "Waktu": st.column_config.TextColumn("Waktu", disabled=True), "Teks Asli": st.column_config.TextColumn("Teks Tweet", disabled=True), "Label": st.column_config.TextColumn("Prediksi", disabled=True), "Keyakinan (%)": st.column_config.NumberColumn("Score", format="%.2f%%", disabled=True) }, hide_index=True, use_container_width=True, key="history_editor" ) # 4. Tombol Aksi (LAYOUT BARU) selected_rows = edited_df[edited_df['Pilih'] == True] count = len(selected_rows) popup_placeholder = st.empty() # --- BARIS 1: TOMBOL HAPUS (Merah & Primary) --- col_del_1, col_del_2 = st.columns(2) with col_del_1: if st.button(f"๐Ÿ—‘๏ธ Hapus ({count}) Item", type="primary", disabled=count==0, use_container_width=True): st.session_state['rows_to_delete'] = selected_rows['Waktu'].tolist() st.session_state['show_confirm'] = True with col_del_2: if st.button("๐Ÿšจ Hapus Semua", type="secondary", use_container_width=True): st.session_state['rows_to_delete'] = "ALL" st.session_state['show_confirm'] = True # --- BARIS 2: TOMBOL DOWNLOAD (Hijau/Standar - Di Bawah) --- st.write("") csv_data = df_display.drop(columns=['Pilih']).to_csv(index=False).encode('utf-8') st.download_button( label="๐Ÿ“ฅ Download CSV (Backup Data Riwayat)", data=csv_data, file_name="Riwayat_Analisis.csv", mime="text/csv", use_container_width=True ) # --- 5. LOGIKA POP-UP KONFIRMASI --- if st.session_state.get('show_confirm', False): with popup_placeholder.container(): st.markdown("---") msg = "SEMUA DATA" if st.session_state['rows_to_delete'] == "ALL" else f"{len(st.session_state['rows_to_delete'])} DATA TERPILIH" with st.chat_message("assistant", avatar="โš ๏ธ"): st.write(f"**KONFIRMASI:** Apakah Anda yakin ingin menghapus **{msg}**?") col_yes, col_no = st.columns([1, 4]) with col_yes: if st.button("โœ… Ya, Hapus"): if st.session_state['rows_to_delete'] == "ALL": st.session_state['history_analisis'] = [] else: targets = st.session_state['rows_to_delete'] st.session_state['history_analisis'] = [ item for item in st.session_state['history_analisis'] if item['Waktu'] not in targets ] save_history(st.session_state['history_analisis']) st.session_state['show_confirm'] = False st.session_state['rows_to_delete'] = [] st.success("Berhasil dihapus!") st.rerun() with col_no: if st.button("โŒ Batal"): st.session_state['show_confirm'] = False st.session_state['rows_to_delete'] = [] st.rerun() st.markdown("---") else: st.info("๐Ÿ“ Belum ada riwayat analisis.")