import os import json import pandas as pd import streamlit as st # Configure page settings st.set_page_config( page_title="AI Trend Summarization & Monitoring Dashboard", page_icon="📈", layout="wide", initial_sidebar_state="expanded" ) # Custom premium styling using HTML/CSS st.markdown(""" """, unsafe_allow_html=True) # Path definition SUMMARY_PATH = "data/final_summary_report.json" FEEDBACK_PATH = "data/user_feedback.json" EVAL_PATH = "data/evaluation_results.json" def load_json_data(file_path): if os.path.exists(file_path): try: with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: st.error(f"Error loading {file_path}: {e}") return None # Load dataset report = load_json_data(SUMMARY_PATH) eval_results = load_json_data(EVAL_PATH) # Title & Description st.markdown('

Trend Summarization & Insights Dashboard

', unsafe_allow_html=True) st.markdown("##### *Monitoring & Peringkasan AI Tren Industri Retail, E-Commerce, dan FMCG Indonesia* 🇮🇩") st.markdown("---") if not report: st.warning("⚠️ Laporan ringkasan akhir (`data/final_summary_report.json`) belum tersedia. Harap jalankan pipeline terlebih dahulu.") else: # Sidebar st.sidebar.image("https://img.icons8.com/nolan/96/combo-chart.png", width=80) st.sidebar.markdown("### Navigasi Dashboard") page = st.sidebar.radio("Pilih Halaman:", ["Analisis Tren & Ringkasan", "Evaluasi Model (ROUGE)", "Umpan Balik Pengguna"]) # Extract all articles for global count and metrics all_articles = [] category_metrics = {} for cat_name, cat_data in report.items(): count = cat_data.get("article_count", 0) category_metrics[cat_name] = count for art in cat_data.get("articles", []): art["trend_category"] = cat_name all_articles.append(art) df_articles = pd.DataFrame(all_articles) if page == "Analisis Tren & Ringkasan": # Section 1: KPI Metrics col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Total Artikel Terkumpul", len(df_articles)) with col2: st.metric("Tren Digital Marketing", category_metrics.get("Digital Marketing", 0)) with col3: st.metric("Tren Sustainability", category_metrics.get("Sustainability", 0)) with col4: st.metric("Tren Consumer Behavior", category_metrics.get("Consumer Behavior Shift", 0)) st.markdown("
", unsafe_allow_html=True) # Section 2: Distribution Chart & Keywords col_left, col_right = st.columns([1, 1]) with col_left: st.markdown('

Distribusi Topik Tren

', unsafe_allow_html=True) # Create a simple horizontal bar chart chart_data = pd.DataFrame({ 'Kategori': list(category_metrics.keys()), 'Jumlah Artikel': list(category_metrics.values()) }) st.bar_chart(data=chart_data, x='Kategori', y='Jumlah Artikel', color='#FF4B4B') with col_right: st.markdown('

Kata Kunci Tren Dominan

', unsafe_allow_html=True) # Display keyword pills per category for cat_name, cat_data in report.items(): st.write(f"**{cat_name}:**") keywords_html = "".join([f'{kw}' for kw in cat_data.get("top_keywords", [])]) st.markdown(keywords_html, unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) # Section 3: Dynamic Summarization Panel st.markdown('

Executive Summary Briefs

', unsafe_allow_html=True) selected_cat = st.selectbox("Pilih Kategori Tren Untuk Menampilkan Ringkasan:", list(report.keys())) if selected_cat: cat_data = report[selected_cat] col_ext, col_gen = st.columns([1, 1]) with col_ext: st.subheader("📌 Extractive Summary (Poin Kunci)") st.info("Algoritma TextRank (PageRank) memilih kalimat-kalimat paling representatif dari kluster artikel.") # Render bullet points nicely sentences = [s.strip() for s in cat_data.get("extractive_brief", "").split('.') if s.strip()] for s in sentences: st.markdown(f"- {s}.") with col_gen: st.subheader("✍️ Generative Summary (Naratif Eksekutif)") st.success("Ringkasan naratif eksekutif terstruktur yang disintesis secara cerdas.") st.markdown(f"
{cat_data.get('generative_brief', '')}
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) # Section 4: Articles Table st.markdown('

Industry Insight Monitoring Table

', unsafe_allow_html=True) st.write("Cari dan filter artikel pendukung hasil scraping:") search_query = st.text_input("🔍 Cari berdasarkan judul atau sumber:") filter_cat = st.multiselect("Filter Kategori:", list(report.keys()), default=list(report.keys())) # Filter dataframe filtered_df = df_articles.copy() if search_query: filtered_df = filtered_df[ filtered_df['title'].str.contains(search_query, case=False, na=False) | filtered_df['source'].str.contains(search_query, case=False, na=False) ] if filter_cat: filtered_df = filtered_df[filtered_df['trend_category'].isin(filter_cat)] # Format table representation if not filtered_df.empty: # Reorder and rename columns display_df = filtered_df[['id', 'title', 'source', 'publish_date', 'trend_category', 'url']] display_df.columns = ['ID', 'Judul Artikel', 'Sumber', 'Tanggal Terbit', 'Kategori Tren', 'Link URL'] st.dataframe(display_df, use_container_width=True, hide_index=True) else: st.write("Tidak ada artikel yang cocok dengan filter pencarian.") elif page == "Evaluasi Model (ROUGE)": st.markdown('

Evaluasi Kualitas Ringkasan AI

', unsafe_allow_html=True) st.write( "Mengukur performa ringkasan otomatis (Extractive & Generative) terhadap ringkasan acuan manusia " "(*ground truth summaries*) menggunakan metrik ROUGE (Recall-Oriented Understudy for Gisting Evaluation)." ) st.markdown("---") if not eval_results: st.warning("⚠️ Berkas evaluasi (`data/evaluation_results.json`) belum tersedia. Harap jalankan script evaluasi.") else: col_left, col_right = st.columns([1.2, 1]) with col_left: st.markdown('

Tabel Hasil ROUGE Score

', unsafe_allow_html=True) # Transform eval results to dataframe for visualization eval_rows = [] for category, types in eval_results.items(): for type_name, metrics in types.items(): for rouge_metric, scores in metrics.items(): eval_rows.append({ "Kategori": category, "Tipe Ringkasan": type_name.capitalize(), "Metrik": rouge_metric.upper(), "Precision": round(scores["precision"], 4), "Recall": round(scores["recall"], 4), "F1-Score": round(scores["fmeasure"], 4) }) df_eval = pd.DataFrame(eval_rows) st.dataframe(df_eval, use_container_width=True, hide_index=True) with col_right: st.markdown('

Interpretasi Metrik

', unsafe_allow_html=True) st.markdown(""" * **ROUGE-1**: Mengukur kecocokan kata tunggal (*unigram*). Menunjukkan tingkat pemeliharaan konten informasi penting. * **ROUGE-2**: Mengukur kecocokan pasangan kata berurutan (*bigram*). Menunjukkan tingkat kelancaran dan kesinambungan struktur kalimat. * **ROUGE-L**: Mengukur Subsekuen Terpanjang Bersama (*Longest Common Subsequence*). Menunjukkan kesamaan struktur tata bahasa dan susunan kalimat. * **F1-Score**: Rata-rata harmonis antara *Precision* (seberapa banyak kata hasil AI yang relevan) dan *Recall* (seberapa banyak kata acuan manusia yang tertangkap oleh AI). """) # Plot average F1-scores st.markdown("**Rata-rata F1-Score per Tipe Ringkasan:**") avg_f1 = df_eval.groupby("Tipe Ringkasan")["F1-Score"].mean().reset_index() st.bar_chart(avg_f1, x="Tipe Ringkasan", y="F1-Score", color="#FF8533") elif page == "Umpan Balik Pengguna": st.markdown('

Human Review Log

', unsafe_allow_html=True) st.write("Berikan rating dan feedback Anda terhadap hasil ringkasan AI untuk membantu menyempurnakan performa model.") st.markdown("---") # Load existing feedback list feedback_list = [] if os.path.exists(FEEDBACK_PATH): try: with open(FEEDBACK_PATH, 'r', encoding='utf-8') as f: feedback_list = json.load(f) except Exception: feedback_list = [] # Feedback entry form st.subheader("Kirim Review Baru") with st.form("feedback_form", clear_on_submit=True): category_selection = st.selectbox("Pilih Kategori Tren yang Dinilai:", list(report.keys())) rating = st.radio("Rating Kualitas Ringkasan:", ["Sangat Baik 👍", "Cukup Baik 👌", "Perlu Perbaikan 👎"]) comments = st.text_area("Masukkan komentar/masukan spesifik Anda:") submitted = st.form_submit_submit_button("Simpan Umpan Balik") if submitted: new_feedback = { "timestamp": pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S"), "kategori_tren": category_selection, "rating": rating, "komentar": comments } feedback_list.append(new_feedback) try: with open(FEEDBACK_PATH, 'w', encoding='utf-8') as f: json.dump(feedback_list, f, indent=4, ensure_ascii=False) st.success("Umpan balik berhasil disimpan! Terima kasih atas kontribusi Anda.") except Exception as e: st.error(f"Gagal menyimpan feedback: {e}") # Display historical feedback st.markdown("
", unsafe_allow_html=True) st.markdown('

Riwayat Umpan Balik Pengguna

', unsafe_allow_html=True) if feedback_list: df_fb = pd.DataFrame(feedback_list) # Reorder columns df_fb.columns = ['Waktu', 'Kategori Tren', 'Rating Kualitas', 'Komentar Pengguna'] st.dataframe(df_fb.sort_values(by='Waktu', ascending=False), use_container_width=True, hide_index=True) else: st.write("Belum ada umpan balik yang terekam.")