Spaces:
Sleeping
Sleeping
Download src/streamlit_app.py from nab3588/customerrates: direct link, hf CLI and curl.
- Browser
- Download file 6.84 kB
-
https://huggingface.co/spaces/nab3588/customerrates/resolve/main/src/streamlit_app.py
- Command line
-
hf download hf://spaces/nab3588/customerrates/src/streamlit_app.py
-
curl -L -o streamlit_app.py https://huggingface.co/spaces/nab3588/customerrates/resolve/main/src/streamlit_app.py
6.84 kB
| import streamlit as st | |
| import numpy as np | |
| import pandas as pd | |
| from statistics import mode, multimode | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline | |
| import plotly.graph_objects as go | |
| from collections import Counter | |
| import re | |
| import math | |
| from google import genai | |
| import os | |
| from dotenv import load_dotenv | |
| # Set a custom cache directory (inside your project) | |
| os.environ["TRANSFORMERS_CACHE"] = "./hf_cache" | |
| os.environ["HF_HOME"] = "./hf_home" | |
| load_dotenv() | |
| client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY")) | |
| def sentiment_to_rating(score, label): | |
| if label == "positive": | |
| scaled = 6 + score * 4 # 6β10 range | |
| elif label == "neutral": | |
| scaled = 4 + (score - 0.5) * 2 # 4β6 range | |
| else: # negative | |
| scaled = 1 + (1 - score) * 4 # 1β5 range | |
| return round(max(1, min(10, scaled)), 2) | |
| # --- PAGE SETUP --- | |
| st.set_page_config(page_title="Warung Sentiment Dashboard", page_icon="π", layout="centered") | |
| st.title("Analisis Kepuasan Pelanggan - Dari Profitly") | |
| st.markdown("Masukkan hingga *15 ulasan pelanggan* untuk mengetahui tingkat kepuasan mereka secara mudah dan visual.") | |
| sentiment_model = pipeline( | |
| "sentiment-analysis", | |
| model="w11wo/indonesian-roberta-base-sentiment-classifier" | |
| ) | |
| if "num_inputs" not in st.session_state: | |
| st.session_state.num_inputs = 1 | |
| def add_input(): | |
| if st.session_state.num_inputs < 15: | |
| st.session_state.num_inputs += 1 | |
| else: | |
| st.warning("β Maksimum 15 ulasan sudah tercapai!") | |
| st.header("π¬ Input Ulasan Pelanggan") | |
| comments = [] | |
| with st.form("customer_reviews"): | |
| for i in range(st.session_state.num_inputs): | |
| comment = st.text_area(f"Ulasan ke-{i+1}", height=80, key=f"comment_{i}") | |
| if comment.strip(): | |
| comments.append(comment.strip()) | |
| col1, col2 = st.columns([1, 2]) | |
| with col1: | |
| st.form_submit_button("Tambah Ulasan Baru", on_click=add_input) | |
| with col2: | |
| submitted = st.form_submit_button("Analisis Semua Ulasan") | |
| # --- ANALYSIS --- | |
| if submitted and len(comments) > 0: | |
| st.header("π Hasil Analisis Kepuasan") | |
| data = [] | |
| for text in comments: | |
| res = sentiment_model(text)[0] | |
| label = res["label"].lower() | |
| score = max(1e-6, min(res["score"], 1)) | |
| rating = sentiment_to_rating(score, label) | |
| data.append({ | |
| "Ulasan": text, | |
| "Sentimen": label.capitalize(), | |
| "Nilai (1-10)": rating, | |
| "Kepercayaan Model": round(score * 100, 2) | |
| }) | |
| df = pd.DataFrame(data) | |
| df = df[["Ulasan", "Sentimen", "Nilai (1-10)", "Kepercayaan Model"]] | |
| st.subheader("π Tabel Hasil Ulasan") | |
| st.dataframe(df.style.format({ | |
| "Kepercayaan Model": "{:.2f}%", | |
| "Nilai (1-10)": "{:.2f}" | |
| }), use_container_width=True) | |
| ratings = df["Nilai (1-10)"].tolist() | |
| avg_rating = round(np.mean(ratings), 2) | |
| sorted_ratings = sorted(ratings) | |
| q1, q2, q3 = np.percentile(sorted_ratings, [25, 50, 75]) | |
| try: | |
| mode_rating = round(mode([round(r) for r in ratings]), 2) | |
| except: | |
| mode_rating = ", ".join(map(str, multimode([round(r) for r in ratings]))) | |
| # Ringkasan cui | |
| st.subheader("Ringkasan Kepuasan") | |
| st.write(f"*Rata-rata kepuasan pelanggan:* {avg_rating}/10") | |
| st.write(f"*Nilai yang paling sering muncul:* {mode_rating}") | |
| st.write(f"*Sebagian besar pelanggan memberi nilai antara:* {q1:.2f} β {q3:.2f}") | |
| if avg_rating >= 8: | |
| st.success("Pelanggan sangat puas! Pertahankan kekuatan produk Anda.") | |
| elif avg_rating >= 6: | |
| st.info("Pelanggan cukup puas, namun masih ada ruang untuk perbaikan.") | |
| else: | |
| st.warning("Banyak pelanggan kurang puas. Evaluasi produk Anda.") | |
| # Quartile | |
| st.subheader("π¦ Persebaran Nilai Kepuasan") | |
| fig_quartile = go.Figure() | |
| # Background full bar | |
| fig_quartile.add_trace(go.Bar( | |
| x=[10], | |
| y=["Distribusi Kepuasan"], | |
| orientation='h', | |
| marker_color='rgba(220, 220, 220, 0.3)', | |
| hoverinfo='none', | |
| showlegend=False | |
| )) | |
| # Add vertical lines for Q1, Median, Q3, and Avg | |
| quartiles = [ | |
| ("Q1", q1, "#63b3ed"), | |
| ("Median", q2, "#48bb78"), | |
| ("Q3", q3, "#38a169"), | |
| ("Rata-rata", avg_rating, "black") | |
| ] | |
| for label, value, color in quartiles: | |
| fig_quartile.add_shape( | |
| type="line", | |
| x0=value, | |
| x1=value, | |
| y0=-0.4, | |
| y1=0.4, | |
| line=dict(color=color, width=3, dash="dot" if label=="Rata-rata" else "solid") | |
| ) | |
| fig_quartile.add_annotation( | |
| x=value, | |
| y=0, | |
| text=label, | |
| showarrow=True, | |
| arrowhead=2, | |
| ax=40, | |
| ay=-30 | |
| ) | |
| # Determine dynamic range | |
| min_rating = min(ratings) | |
| max_rating = max(ratings) | |
| padding = (max_rating - min_rating) * 0.1 # 10% margin | |
| x_min = max(1, min_rating - padding) | |
| x_max = min(10, max_rating + padding) | |
| fig_quartile.update_layout( | |
| xaxis=dict( | |
| title="Nilai Kepuasan (1β10)", | |
| range=[x_min, x_max], | |
| tickmode="linear", | |
| dtick=0.5 | |
| ), | |
| yaxis=dict(showticklabels=False), | |
| template="plotly_white", | |
| height=300, | |
| margin=dict(l=30, r=30, t=40, b=30) | |
| ) | |
| st.plotly_chart(fig_quartile, use_container_width=True) | |
| # FEEDBACK SUMMARIZER | |
| st.subheader("π§ Ringkasan Feedback Otomatis") | |
| # Prepare textual summary for Gemini | |
| summary_prompt = f""" | |
| Kamu adalah seorang konsultan bisnis. | |
| Jawablah secara runtun dan jelas. | |
| Diberikan beberapa ulasan berikut (dalam bahasa Indonesia): | |
| {comments} | |
| Tugasmu: | |
| 1. Sebutkan secara SINGKAT TIGA hal utama yang menjadi *kekuatan bisnis* (berdasarkan ulasan positif). | |
| 2. Sebutkan secara SINGKAT TIGA hal utama yang menjadi *kelemahan atau masalah utama* (berdasarkan ulasan negatif). | |
| 3. Tentukan TIGA cara terelaborasi untuk memperkuatan keunggulan dan mencegah kelemahan bisnis terjadi secara optimal: | |
| *Kekuatan Utama:* (isian singkat) | |
| *Kelemahan Utama:* (isian singkat) | |
| *Saran :* (sedikit penjelasan) | |
| Jawab dalam format markdown terstruktur | |
| """ | |
| try: | |
| with st.spinner("π Menganalisis kekuatan dan kelemahan bisnis..."): | |
| response = client.models.generate_content( | |
| model="gemini-2.5-flash", | |
| contents=summary_prompt | |
| ) | |
| summary_text = response.text.strip() | |
| st.markdown(summary_text) | |
| except Exception as e: | |
| st.error("Gagal menghasilkan ringkasan otomatis. Coba refresh.") | |
| st.exception(e) | |
| elif submitted: | |
| st.warning("β Silakan isi minimal satu ulasan pelanggan terlebih dahulu.") |