churnsight-ai / frontend.py
Hasitha16's picture
Update frontend.py
b6cdcfd verified
raw
history blame
10.2 kB
import streamlit as st
import requests
import pandas as pd
import tempfile
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,
"use_aspects": False,
"use_explain_bulk": False
}
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)
api_token = st.text_input("πŸ” API Token", value="my-secret-key", type="password")
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"])
st.session_state.use_aspects = st.checkbox("πŸ” Detect Pain Points", value=st.session_state.get("use_aspects", False))
st.session_state.use_explain_bulk = st.checkbox("🧠 Generate PM Insight (Bulk)", value=st.session_state.get("use_explain_bulk", False))
verbosity = st.radio("πŸ—£οΈ Response Style", ["Brief", "Detailed"])
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
analyze = False
col1, col2, col3 = st.columns(3)
with col1:
analyze = st.button("πŸ” Analyze")
with col2:
if st.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()
with col3:
if st.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.get("trigger_example_analysis")):
with st.spinner("Analyzing feedback..."):
try:
model_used = None if sentiment_model == "Auto-detect" else sentiment_model
payload = {
"text": st.session_state.review,
"model": model_used or "distilbert-base-uncased-finetuned-sst-2-english",
"industry": industry,
"product_category": product_category,
"verbosity": verbosity,
"aspects": st.session_state.use_aspects,
"intelligence": st.session_state.get("intelligence_mode", False)
}
headers = {"x-api-key": st.session_state.get("api_token", "my-secret-key")}
res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers)
if res.ok:
st.session_state.last_response = res.json()
else:
try:
err_detail = res.json().get("detail", "No detail provided.")
except Exception:
err_detail = res.text
st.error(f"❌ Backend Error ({res.status_code}): {err_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.progress(data["sentiment"]["score"])
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 st.session_state.use_aspects:
if data.get("pain_points"):
st.error("πŸ” Pain Points: " + ", ".join(data["pain_points"]))
else:
st.info("βœ… No specific pain points were detected.")
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.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:
try:
follow_payload = {
"text": st.session_state.review,
"question": q_input,
"verbosity": verbosity
}
headers = {"x-api-key": api_token}
res = requests.post(f"{backend_url}/followup/", json=follow_payload, headers=headers)
if res.ok:
st.success(res.json().get("answer"))
else:
try:
err_detail = res.json().get("detail", "No detail provided.")
except Exception:
err_detail = res.text
st.error(f"❌ Follow-up API Error ({res.status_code}): {err_detail}")
except Exception as e:
st.error(f"⚠️ Follow-up error: {e}")
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()
y_columns = [col for col in trend.columns if col != "date"]
st.markdown("#### πŸ“… Daily Churn Trend")
fig = px.bar(trend, x="date", y=y_columns, 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": st.session_state.use_aspects,
"intelligence": st.session_state.intelligence_mode
"explain_bulk": st.session_state.use_explain_bulk
}
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:
try:
err_detail = res.json().get("detail", "No detail provided.")
except Exception:
err_detail = res.text
st.error(f"❌ Bulk API Error ({res.status_code}): {err_detail}")
except Exception as e:
st.error(f"Bulk analysis failed: {e}")