churnsight-ai / frontend.py
Hasitha16's picture
Update frontend.py
996d2c2 verified
raw
history blame
12.5 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?",
"What benefits did they mention?",
"What made their experience smooth?"
]
elif churn == "High Risk":
suggestions = [
"What made the user upset?",
"Is this user likely to churn?",
"What were the major complaints?",
"What could improve their experience?"
]
else:
suggestions = [
"What are the key takeaways?",
"Is there any concern raised?",
"Did the user express dissatisfaction?",
"Is this feedback actionable?"
]
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")
st.markdown("#### πŸ“₯ Upload CSV or Paste Reviews")
uploaded_file = st.file_uploader("Upload a CSV with a 'review' column", type=["csv"])
bulk_input = st.text_area("Or paste multiple reviews (one per line)", height=180)
reviews = []
if uploaded_file is not None:
try:
df_csv = pd.read_csv(uploaded_file)
if "review" in df_csv.columns:
reviews = df_csv["review"].dropna().astype(str).tolist()
else:
st.warning("CSV must contain a 'review' column.")
except Exception as e:
st.error(f"CSV error: {e}")
elif bulk_input.strip():
reviews = [line.strip() for line in bulk_input.split("\\n") if line.strip()]
st.markdown("#### 🧠 Bulk Analysis Configuration")
explain_bulk = st.checkbox("🧠 Generate Explanations", value=st.session_state.get("use_explain_bulk", False))
enable_followups = st.checkbox("πŸ’¬ Generate Follow-Up Q&A", value=True)
if st.button("πŸš€ Analyze Bulk") and reviews:
payload = {
"reviews": reviews,
"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": explain_bulk,
"follow_up": [["What is the issue here?", "What could be improved?"]] * len(reviews) if enable_followups else None
}
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)
if any("follow_up" in r for r in results):
st.markdown("### πŸ’¬ Follow-Up Answers")
for r in results:
st.markdown(f"**Review:** {r['review']}")
if isinstance(r.get("follow_up"), list):
for ans in r["follow_up"]:
st.info(ans)
elif "follow_up" in r:
st.info(r["follow_up"])
if "churn_risk" in df.columns:
st.markdown("### πŸ“ˆ Churn Risk Chart")
churn_summary = df["churn_risk"].value_counts().reset_index()
churn_summary.columns = ["Churn Risk", "Count"]
fig = px.pie(churn_summary, names="Churn Risk", values="Count", title="Churn Risk Distribution")
st.plotly_chart(fig, use_container_width=True)
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}")