import pandas as pd import plotly.express as px import plotly.graph_objects as go import gradio as gr import re custom_theme = gr.themes.Base().set( body_background_fill="linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%)", block_background_fill="white", block_shadow="0px 5px 15px rgba(0,0,0,0.06)", block_border_width="1px", block_border_color="#e6e6e6", ) # Run predictions results_df = run_batch_predictions(df, pipe) # Create a new DataFrame without 'Clusters' and 'Recommendation' export_df = results_df.drop(columns=[col for col in ['Clusters', 'Recommendation'] if col in results_df.columns]) # Save to CSV export_df.to_csv('results_summary.csv', index=False) # Add the constant column results_df["RecordCount"] = 1 # Extract actions '''' def extract_actions(html_text): if not isinstance(html_text, str): return ["No action"] text = html_text.replace("
", "\n") actions = re.findall(r"• (.+)", text) return actions if actions else ["No action"] ''' # Drilldown DataFrame drilldown_data = [] for _, row in results_df.iterrows(): churn_pred = row["Churn Prediction"] prob= row["Probability"] clusters = row["Clusters"] if isinstance(clusters, dict): for cluster_name, rec_list in clusters.items(): for rec in rec_list: drilldown_data.append({ "Churn Prediction": churn_pred, "Cluster": cluster_name, "Recommendation": rec, "Probability": prob }) drilldown_df = pd.DataFrame(drilldown_data) # Chart defaults px.defaults.template = "plotly_white" px.defaults.color_discrete_sequence = px.colors.qualitative.Set2 # Helper for numeric KPI cards def make_numeric_card(title, value, suffix=""): fig = go.Figure(go.Indicator( mode="number", value=value, number={'font': {'size': 36}, 'suffix': suffix}, title={'text': title, 'font': {'size': 16}} )) fig.update_layout( height=140, width=220, margin=dict(l=10, r=10, t=25, b=10), paper_bgcolor="white" ) return fig # Build Analytics Figures def generate_analytics_figs(df): figs = {} figs["avg_tenure"] = make_numeric_card("Avg Tenure (Months)", df["Tenure in Months"].mean()) figs["avg_charge"] = make_numeric_card("Avg Monthly Charge ($)", df["Monthly Charge"].mean(), suffix="$") figs["avg_data"] = make_numeric_card("Avg Monthly GB Download", df["Avg Monthly GB Download"].mean()) figs["tenure_hist"] = px.histogram(df, x="Tenure in Months", nbins=20, title="Tenure Distribution") figs["age_hist"] = px.histogram(df, x="Age", nbins=20, title="Age Distribution") figs["contract_pie"] = px.pie(df, names="Contract", hole=0.4, title="Contract Types") figs["dependents_donut"] = px.pie(df, names="Number of Dependents", hole=0.4, title="Dependents Distribution") figs["senior_pie"] = px.pie(df, names="Senior Citizen", hole=0.4, title="Senior Citizen Ratio") figs["streaming_pie"] = px.pie(df, names="Streaming Movies", hole=0.4, title="Streaming Movies Adoption") figs["security_pie"] = px.pie(df, names="Online Security", hole=0.4, title="Online Security Subscription") figs["unlimited_pie"] = px.pie(df, names="Unlimited Data", hole=0.4, title="Unlimited Data Adoption") referrals_df = df["Number of Referrals"].value_counts().reset_index() referrals_df.columns = ["Number of Referrals", "count"] figs["referrals_bar"] = px.bar( referrals_df, x="Number of Referrals", y="count", title="Referrals Count Distribution", labels={"Number of Referrals": "Number of Referrals", "count": "Count"} ) for f in figs.values(): f.update_layout(margin=dict(l=20, r=20, t=40, b=20)) return figs analytics_figs = generate_analytics_figs(results_df) # Drilldown Chart def generate_drilldown_chart(): if drilldown_df.empty: fig = px.treemap(names=["No data"], parents=[""], values=[1], title="No recommendations yet") return fig fig = px.treemap( drilldown_df, path=['Churn Prediction', 'Cluster', 'Recommendation'], values=None, title="Recommendations by Churn Prediction & Cluster", ) fig.update_traces( textinfo='label+percent parent', hovertemplate="%{label}
Customers: %{value}
Percentage of parent: %{percentParent:.1%}" ) fig.update_layout( margin=dict(l=20, r=20, t=50, b=20), title_font=dict(size=18) ) return fig # Customer Details def get_customer_details(customer_id): row = results_df[results_df["Customer ID"] == customer_id].iloc[0] prediction = row["Churn Prediction"] clusters = row["Clusters"] prob= row["Probability"] # Convert decimal probability → percentage pct_value = float(prob) * 100 # numeric percentage, e.g. 82.3 pct = f"{pct_value:.0f}%" # string percentage, e.g. "82%" # Categorize risk if pct_value < 30: risk_level = "Low" elif pct_value < 60: risk_level = "Medium" else: risk_level = "High" # Corrected line churn_risk = f"{risk_level} ({pct})" # Build recommendations with cluster names if isinstance(clusters, dict): rec_lines = [] for cluster_name, rec_list in clusters.items(): for rec in rec_list: rec_lines.append(f"{cluster_name}: {rec}") recommendations_html = "
• ".join(rec_lines) if rec_lines else "No recommendations" else: recommendations_html = "No recommendations" selected_features = [ 'Contract', 'Tenure in Months', 'Age', 'Number of Referrals', 'Monthly Charge', 'Number of Dependents', 'Senior Citizen', 'Streaming Movies', 'Online Security', 'Avg Monthly GB Download', 'Unlimited Data' ] # Table with customer features table_html = "" table_html += "" for col in selected_features: table_html += f"" table_html += f"" table_html += "
{col}{row[col]}
" # Build full HTML with recommendations **outside** the table full_html = ( f"Churn Risk: {churn_risk}

" f"Recommendations:
• {recommendations_html}

" f"Customer Details:
{table_html}" ) return full_html # Gradio App with gr.Blocks( theme=custom_theme, title="CRM Churn Dashboard", css=""" /* FORCE LIGHT MODE */ :root { --color-background: #ffffff !important; --color-border: #e6e6e6 !important; --color-text: #000000 !important; --body-background-fill: linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%) !important; /* Override dark mode variables */ --dark-mode: 0 !important; --neutral-900: #000000 !important; --neutral-800: #1a1a1a !important; --neutral-700: #2a2a2a !important; --neutral-600: #3a3a3a !important; --neutral-500: #777 !important; --neutral-400: #aaa !important; --neutral-300: #ccc !important; --neutral-200: #eee !important; --neutral-100: #fafafa !important; } body { background: linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%) !important; } /* Force blocks to be light */ .gr-block, .gr-panel, .gr-box, .gr-container { background: white !important; color: black !important; } /* Dropdowns, inputs, etc. */ input, select, textarea { background: white !important; color: black !important; border: 1px solid #ddd !important; } """ ) as demo: gr.Markdown("## 📊 CRM Analytics Dashboard") with gr.Tabs(): # Actions Insights tab with gr.TabItem("Actions Overview"): gr.Markdown("### Drilldown of Recommendations by Cluster") drilldown_plot = gr.Plot(value=generate_drilldown_chart()) # Analytics Dashboard tab (donut style, original font) with gr.TabItem("Analytics Dashboard"): gr.Markdown("### Customer Base Analytics Overview") with gr.Row(): gr.Plot(value=analytics_figs["avg_tenure"]) gr.Plot(value=analytics_figs["avg_charge"]) gr.Plot(value=analytics_figs["avg_data"]) with gr.Row(): gr.Plot(value=analytics_figs["tenure_hist"]) gr.Plot(value=analytics_figs["age_hist"]) with gr.Row(): gr.Plot(value=analytics_figs["contract_pie"]) gr.Plot(value=analytics_figs["dependents_donut"]) with gr.Row(): gr.Plot(value=analytics_figs["senior_pie"]) gr.Plot(value=analytics_figs["streaming_pie"]) with gr.Row(): gr.Plot(value=analytics_figs["security_pie"]) gr.Plot(value=analytics_figs["unlimited_pie"]) gr.Plot(value=analytics_figs["referrals_bar"]) # Customer Insights tab with gr.TabItem("CRM Details"): gr.Markdown("### Customer-Level Predictions & Recommendations") customer_dropdown = gr.Dropdown( choices=results_df["Customer ID"].tolist(), value=results_df["Customer ID"].iloc[0], label="Select Customer" ) customer_output = gr.HTML() customer_dropdown.change( fn=get_customer_details, inputs=customer_dropdown, outputs=customer_output ) demo.launch(share=True)