churnsight-ai / frontend.py
Hasitha16's picture
Update frontend.py
1a945f1 verified
raw
history blame
9.15 kB
import streamlit as st
import requests
import pandas as pd
from gtts import gTTS
import base64
from io import BytesIO
import os
import plotly.express as px
from datetime import datetime
import uuid
# Simulated in-memory storage for churn log
if "churn_log" not in st.session_state:
st.session_state.churn_log = []
st.set_page_config(page_title="ChurnSight AI", page_icon="🧠", layout="wide")
if os.path.exists("logo.png"):
st.image("logo.png", width=180)
# Session state setup
defaults = {
"review": "",
"dark_mode": False,
"intelligence_mode": True,
"trigger_example_analysis": False,
"last_response": None,
"followup_answer": None
}
for k, v in defaults.items():
if k not in st.session_state:
st.session_state[k] = v
# Dark mode styling
if st.session_state.dark_mode:
st.markdown("""
<style>
html, body, [class*="st-"] {
background-color: #121212;
color: #f5f5f5;
}
</style>
""", unsafe_allow_html=True)
# Sidebar config
with st.sidebar:
st.header("βš™οΈ PM Config")
st.session_state.dark_mode = st.toggle("πŸŒ™ Dark Mode", value=st.session_state.dark_mode)
st.session_state.intelligence_mode = st.toggle("🧠 Intelligence Mode", value=st.session_state.intelligence_mode)
if api_token.strip() == "my-secret-key":
st.warning("πŸ§ͺ Demo Mode β€” Not all features are active. Add your API token to unlock full features.")
backend_url = st.text_input("🌐 Backend URL", value="http://localhost:8000")
sentiment_model = st.selectbox("πŸ“Š Sentiment Model", ["Auto-detect", "distilbert-base-uncased-finetuned-sst-2-english"])
industry = st.selectbox("🏭 Industry", ["Auto-detect", "Generic", "E-commerce", "Healthcare", "Education"])
product_category = st.selectbox("🧩 Product Category", ["Auto-detect", "General", "Mobile Devices", "Laptops"])
use_aspects = st.checkbox("πŸ” Detect Pain Points")
use_explain_bulk = st.checkbox("🧠 Generate PM Insight (Bulk)")
verbosity = st.radio("πŸ—£οΈ Response Style", ["Brief", "Detailed"])
voice_lang = st.selectbox("πŸ”ˆ Voice Language", ["en", "fr", "es", "de", "hi", "zh"])
# Text-to-Speech
def speak(text, lang='en'):
tts = gTTS(text, lang=lang)
mp3 = BytesIO()
tts.write_to_fp(mp3)
b64 = base64.b64encode(mp3.getvalue()).decode()
st.markdown(f'<audio controls><source src="data:audio/mp3;base64,{b64}" type="audio/mp3"></audio>', unsafe_allow_html=True)
mp3.seek(0)
return mp3
tab1, tab2 = st.tabs(["🧠 Analyze Review", "πŸ“š Bulk Reviews"])
# === SINGLE REVIEW ANALYSIS ===
with tab1:
st.title("πŸ“Š ChurnSight AI β€” Product Feedback Assistant")
st.markdown("Analyze feedback to detect churn risk, extract pain points, and support product decisions.")
review = st.text_area("πŸ“ Enter Customer Feedback", value=st.session_state.review, height=180)
st.session_state.review = review
col1, col2, col3 = st.columns(3)
if col1.button("πŸ” Analyze"):
st.session_state.trigger_example_analysis = False
if col2.button("🎲 Example"):
st.session_state.review = (
"The app crashes every time I try to checkout. It's so slow and unresponsive. "
"Customer support never replied. I'm switching to another brand."
)
st.session_state.trigger_example_analysis = True
st.rerun()
if col3.button("🧹 Clear"):
for key in ["review", "last_response", "followup_answer"]:
st.session_state[key] = ""
st.rerun()
if st.session_state.review and (analyze or st.session_state.trigger_example_analysis):
with st.spinner("Analyzing feedback..."):
try:
payload = {
"text": st.session_state.review,
"model": "distilbert-base-uncased-finetuned-sst-2-english" if sentiment_model == "Auto-detect" else sentiment_model,
"industry": industry,
"product_category": product_category,
"verbosity": verbosity,
"aspects": use_aspects,
"intelligence": st.session_state.intelligence_mode
}
headers = {"x-api-key": api_token}
res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers)
if res.ok:
st.session_state.last_response = res.json()
else:
st.error(f"Error: {res.status_code} - {res.json().get('detail')}")
except Exception as e:
st.error(f"🚫 Exception: {e}")
data = st.session_state.last_response
if data:
st.subheader("πŸ“Œ PM Insight Summary")
st.info(data["summary"])
st.markdown(f"**Industry:** `{data['industry']}` | **Category:** `{data['product_category']}` | **Device:** Web")
st.metric("πŸ“Š Sentiment", data["sentiment"]["label"], delta=f"{data['sentiment']['score']:.2%}")
st.info(f"πŸ’’ Emotion: {data['emotion']}")
if "churn_risk" in data:
risk = data["churn_risk"]
color = "πŸ”΄" if risk == "High Risk" else "🟒"
st.metric("🚨 Churn Risk", f"{color} {risk}")
if data.get("pain_points"):
st.error("πŸ” Pain Points: " + ", ".join(data["pain_points"]))
# Add to churn log
try:
st.session_state.churn_log.append({
"timestamp": datetime.now(),
"product": data.get("product_category", "General"),
"churn_risk": data.get("churn_risk", "Unknown"),
"session_id": str(uuid.uuid4())
})
if len(st.session_state.churn_log) > 1000:
st.session_state.churn_log = st.session_state.churn_log[-1000:]
except Exception as e:
st.warning(f"πŸ§ͺ Logging failed: {e}")
st.subheader("πŸ”Š Audio Summary")
audio = speak(data["summary"], lang=voice_lang)
st.download_button("⬇️ Download Audio", audio.read(), "summary.mp3")
st.markdown("### πŸ” Ask a Follow-Up")
sentiment = data["sentiment"]["label"].lower()
churn = data.get("churn_risk", "")
pain = data.get("pain_points", [])
if sentiment == "positive" and churn == "Low Risk":
suggestions = ["What features impressed the user?", "Would they recommend the product?"]
elif churn == "High Risk":
suggestions = ["What made the user upset?", "Is this user likely to churn?"]
else:
suggestions = ["What are the key takeaways?", "Is there any concern raised?"]
selected_q = st.selectbox("πŸ’‘ Suggested Questions", ["Type your own..."] + suggestions)
q_input = st.text_input("πŸ” Your Question") if selected_q == "Type your own..." else selected_q
if q_input:
follow_payload = {"text": st.session_state.review, "question": q_input, "verbosity": verbosity}
res = requests.post(f"{backend_url}/followup/", json=follow_payload, headers=headers)
if res.ok:
st.success(res.json().get("answer"))
else:
st.error("Failed to answer.")
if st.checkbox("πŸ“Š Show Churn Risk Trends"):
try:
df = pd.DataFrame(st.session_state.churn_log)
df["date"] = pd.to_datetime(df["timestamp"]).dt.date
trend = df.groupby(["date", "churn_risk"]).size().unstack(fill_value=0).reset_index()
st.markdown("#### πŸ“… Daily Churn Trend")
fig = px.bar(trend, x="date", y=["High Risk", "Low Risk"], barmode="group")
st.plotly_chart(fig, use_container_width=True)
st.download_button("⬇️ Export Trend CSV", trend.to_csv(index=False), "churn_trend.csv")
except Exception as e:
st.error(f"Trend error: {e}")
# === BULK REVIEW ANALYSIS ===
with tab2:
st.title("πŸ“š Bulk Feedback Analysis")
bulk_input = st.text_area("πŸ“₯ Paste multiple reviews (one per line)", height=250)
if st.button("πŸš€ Analyze Bulk"):
lines = [l.strip() for l in bulk_input.strip().splitlines() if l.strip()]
payload = {
"reviews": lines,
"model": "distilbert-base-uncased-finetuned-sst-2-english" if sentiment_model == "Auto-detect" else sentiment_model,
"industry": None,
"product_category": None,
"device": None,
"aspects": use_aspects,
"intelligence": st.session_state.intelligence_mode
}
try:
res = requests.post(f"{backend_url}/bulk/?token={api_token}", json=payload)
if res.ok:
results = res.json().get("results", [])
df = pd.DataFrame(results)
st.dataframe(df)
st.download_button("⬇️ Export Results CSV", df.to_csv(index=False), "bulk_results.csv")
else:
st.error(f"API Error: {res.status_code}")
except Exception as e:
st.error(f"Bulk analysis failed: {e}")