File size: 10,181 Bytes
fc94552
 
 
82211db
fc94552
 
5224787
 
fc94552
05dad7c
5224787
 
05dad7c
8e27b3a
fc94552
 
 
 
 
 
 
 
 
 
 
b6cdcfd
 
 
 
fc94552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05dad7c
fc94552
9aa56c6
fc94552
 
9da624c
90267c3
1a945f1
 
fc94552
05dad7c
fc94552
 
b6cdcfd
 
fc94552
5094d9c
9aa56c6
fc94552
05dad7c
fc94552
8e27b3a
 
4ce1c62
9aa56c6
fc94552
 
82b3c12
fc94552
4ce1c62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05dad7c
82b3c12
9aa56c6
fc94552
c88afb5
fc94552
b6cdcfd
fc94552
4ce1c62
fc94552
 
 
b6cdcfd
82b3c12
fc94552
4ce1c62
fc94552
05dad7c
fc94552
 
1959778
 
 
 
 
fc94552
 
 
 
 
9aa56c6
fc94552
9aa56c6
fc94552
571f6f2
fc94552
33c45f1
 
 
 
b6cdcfd
 
 
 
 
 
05dad7c
5224787
 
 
 
 
 
63a3c84
05dad7c
 
5224787
 
9aa56c6
 
68804d2
 
 
 
05dad7c
 
 
68804d2
05dad7c
c88afb5
05dad7c
 
c88afb5
05dad7c
c88afb5
 
 
 
 
 
571f6f2
c88afb5
 
 
 
1959778
 
 
 
 
c88afb5
 
05dad7c
 
 
 
 
 
c88afb5
05dad7c
727b706
05dad7c
 
 
 
 
 
 
 
 
 
 
 
 
0f55ff6
05dad7c
 
 
b6cdcfd
05dad7c
b6cdcfd
 
05dad7c
 
 
 
 
 
 
 
 
1959778
 
 
 
 
05dad7c
1959778
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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}")