Update app.py
Browse files
app.py
CHANGED
|
@@ -398,22 +398,54 @@ class VisualizationEngine:
|
|
| 398 |
return fig
|
| 399 |
|
| 400 |
@staticmethod
|
| 401 |
-
def create_revenue_trend(df: pd.DataFrame):
|
| 402 |
-
"""Create revenue trend visualization"""
|
| 403 |
df_copy = df.copy()
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
|
| 408 |
fig = px.line(
|
| 409 |
-
|
| 410 |
-
x='
|
| 411 |
y='amount',
|
| 412 |
-
title=
|
| 413 |
-
labels={'amount': 'Revenue ($)', '
|
| 414 |
)
|
| 415 |
fig.update_traces(line_color=COLORS['primary'], line_width=3)
|
| 416 |
fig.update_layout(height=400, title={'x': 0.5, 'xanchor': 'center'})
|
|
|
|
| 417 |
return fig
|
| 418 |
|
| 419 |
@staticmethod
|
|
@@ -582,6 +614,21 @@ class B2BCustomerAnalytics:
|
|
| 582 |
|
| 583 |
return segment_chart, rfm_chart, churn_chart, revenue_chart
|
| 584 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
def get_customer_table(self) -> Optional[pd.DataFrame]:
|
| 586 |
"""Get formatted customer table"""
|
| 587 |
if self.customer_metrics is None:
|
|
@@ -800,6 +847,22 @@ class B2BCustomerAnalytics:
|
|
| 800 |
|
| 801 |
return " • ".join(recommendations) if recommendations else "Continue monitoring customer engagement patterns."
|
| 802 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 803 |
def create_gradio_interface():
|
| 804 |
"""Create the enhanced Gradio interface"""
|
| 805 |
|
|
@@ -832,7 +895,7 @@ def create_gradio_interface():
|
|
| 832 |
|
| 833 |
with gr.Tabs():
|
| 834 |
|
| 835 |
-
with gr.Tab("
|
| 836 |
with gr.Row():
|
| 837 |
with gr.Column():
|
| 838 |
file_input = gr.File(
|
|
@@ -854,7 +917,7 @@ def create_gradio_interface():
|
|
| 854 |
summary_display = gr.HTML()
|
| 855 |
data_preview = gr.DataFrame(label="Data Preview (First 20 Rows)")
|
| 856 |
|
| 857 |
-
with gr.Tab("
|
| 858 |
with gr.Row():
|
| 859 |
with gr.Column():
|
| 860 |
segment_chart = gr.Plot(label="Customer Segments Distribution")
|
|
@@ -875,7 +938,7 @@ def create_gradio_interface():
|
|
| 875 |
</div>
|
| 876 |
""")
|
| 877 |
|
| 878 |
-
with gr.Tab("
|
| 879 |
train_btn = gr.Button(
|
| 880 |
"Train Churn Prediction Model",
|
| 881 |
variant="primary",
|
|
@@ -899,19 +962,36 @@ def create_gradio_interface():
|
|
| 899 |
</div>
|
| 900 |
""")
|
| 901 |
|
| 902 |
-
with gr.Tab("
|
| 903 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 904 |
|
|
|
|
| 905 |
gr.HTML("""
|
| 906 |
<div style="background: #ecfdf5; padding: 1rem; border-radius: 8px; border-left: 4px solid #10b981; margin-top: 1rem;">
|
| 907 |
-
<h4 style="color: #065f46; margin: 0 0 0.5rem 0;">Revenue
|
| 908 |
<p style="margin: 0; color: #1f2937; font-size: 0.9rem;">
|
| 909 |
-
|
|
|
|
| 910 |
</p>
|
| 911 |
</div>
|
| 912 |
""")
|
| 913 |
|
| 914 |
-
with gr.Tab("
|
| 915 |
with gr.Row():
|
| 916 |
customer_id_input = gr.Textbox(
|
| 917 |
label="Customer ID",
|
|
@@ -926,7 +1006,7 @@ def create_gradio_interface():
|
|
| 926 |
|
| 927 |
customer_insights = gr.HTML()
|
| 928 |
|
| 929 |
-
with gr.Tab("
|
| 930 |
with gr.Row():
|
| 931 |
with gr.Column():
|
| 932 |
gr.HTML("""
|
|
@@ -953,20 +1033,23 @@ def create_gradio_interface():
|
|
| 953 |
def safe_load_data(analytics_instance, file):
|
| 954 |
try:
|
| 955 |
if file is None:
|
| 956 |
-
return analytics_instance, "Please upload a CSV file", "", None, None, None, None, None, None
|
| 957 |
|
| 958 |
status, dashboard, preview = analytics_instance.load_data(file)
|
| 959 |
|
| 960 |
if "successfully" in status:
|
| 961 |
charts = analytics_instance.get_visualizations()
|
| 962 |
table = analytics_instance.get_customer_table()
|
| 963 |
-
|
|
|
|
|
|
|
|
|
|
| 964 |
else:
|
| 965 |
-
return analytics_instance, status, "", None, None, None, None, None, None
|
| 966 |
|
| 967 |
except Exception as e:
|
| 968 |
error_msg = f"Error loading data: {str(e)}"
|
| 969 |
-
return analytics_instance, error_msg, "", None, None, None, None, None, None
|
| 970 |
|
| 971 |
def safe_train_model(analytics_instance):
|
| 972 |
try:
|
|
@@ -1006,7 +1089,27 @@ def create_gradio_interface():
|
|
| 1006 |
fn=safe_load_data,
|
| 1007 |
inputs=[analytics, file_input],
|
| 1008 |
outputs=[analytics, load_status, summary_display, data_preview,
|
| 1009 |
-
segment_chart, rfm_chart, churn_distribution_chart, revenue_chart, customer_table]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1010 |
)
|
| 1011 |
|
| 1012 |
train_btn.click(
|
|
|
|
| 398 |
return fig
|
| 399 |
|
| 400 |
@staticmethod
|
| 401 |
+
def create_revenue_trend(df: pd.DataFrame, time_granularity='month', customer_filter='all'):
|
| 402 |
+
"""Create revenue trend visualization with filters"""
|
| 403 |
df_copy = df.copy()
|
| 404 |
+
|
| 405 |
+
# Filter by customer if specified
|
| 406 |
+
if customer_filter != 'all' and customer_filter:
|
| 407 |
+
df_copy = df_copy[df_copy['customer_id'] == customer_filter]
|
| 408 |
+
if df_copy.empty:
|
| 409 |
+
# Return empty chart with message
|
| 410 |
+
fig = go.Figure()
|
| 411 |
+
fig.add_annotation(text=f"No data found for customer {customer_filter}",
|
| 412 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 413 |
+
fig.update_layout(title=f"Revenue Trend - {customer_filter}", height=400)
|
| 414 |
+
return fig
|
| 415 |
+
|
| 416 |
+
# Group by time granularity
|
| 417 |
+
if time_granularity == 'day':
|
| 418 |
+
df_copy['time_period'] = df_copy['order_date'].dt.date
|
| 419 |
+
title_suffix = "Daily"
|
| 420 |
+
elif time_granularity == 'week':
|
| 421 |
+
df_copy['time_period'] = df_copy['order_date'].dt.to_period('W')
|
| 422 |
+
title_suffix = "Weekly"
|
| 423 |
+
elif time_granularity == 'year':
|
| 424 |
+
df_copy['time_period'] = df_copy['order_date'].dt.to_period('Y')
|
| 425 |
+
title_suffix = "Yearly"
|
| 426 |
+
else: # default to month
|
| 427 |
+
df_copy['time_period'] = df_copy['order_date'].dt.to_period('M')
|
| 428 |
+
title_suffix = "Monthly"
|
| 429 |
+
|
| 430 |
+
revenue_data = df_copy.groupby('time_period')['amount'].sum().reset_index()
|
| 431 |
+
revenue_data['time_period'] = revenue_data['time_period'].astype(str)
|
| 432 |
+
|
| 433 |
+
# Create title
|
| 434 |
+
if customer_filter == 'all':
|
| 435 |
+
title = f"{title_suffix} Revenue Trends - All Customers"
|
| 436 |
+
else:
|
| 437 |
+
title = f"{title_suffix} Revenue Trends - {customer_filter}"
|
| 438 |
|
| 439 |
fig = px.line(
|
| 440 |
+
revenue_data,
|
| 441 |
+
x='time_period',
|
| 442 |
y='amount',
|
| 443 |
+
title=title,
|
| 444 |
+
labels={'amount': 'Revenue ($)', 'time_period': 'Time Period'}
|
| 445 |
)
|
| 446 |
fig.update_traces(line_color=COLORS['primary'], line_width=3)
|
| 447 |
fig.update_layout(height=400, title={'x': 0.5, 'xanchor': 'center'})
|
| 448 |
+
|
| 449 |
return fig
|
| 450 |
|
| 451 |
@staticmethod
|
|
|
|
| 614 |
|
| 615 |
return segment_chart, rfm_chart, churn_chart, revenue_chart
|
| 616 |
|
| 617 |
+
def get_revenue_chart_with_filters(self, time_granularity='month', customer_filter='all'):
|
| 618 |
+
"""Get revenue chart with time and customer filters"""
|
| 619 |
+
if self.raw_data is None:
|
| 620 |
+
return None
|
| 621 |
+
|
| 622 |
+
return VisualizationEngine.create_revenue_trend(
|
| 623 |
+
self.raw_data, time_granularity, customer_filter
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
def get_customer_list(self):
|
| 627 |
+
"""Get list of customer IDs for dropdown"""
|
| 628 |
+
if self.raw_data is None:
|
| 629 |
+
return []
|
| 630 |
+
return ['all'] + sorted(self.raw_data['customer_id'].unique().tolist())
|
| 631 |
+
|
| 632 |
def get_customer_table(self) -> Optional[pd.DataFrame]:
|
| 633 |
"""Get formatted customer table"""
|
| 634 |
if self.customer_metrics is None:
|
|
|
|
| 847 |
|
| 848 |
return " • ".join(recommendations) if recommendations else "Continue monitoring customer engagement patterns."
|
| 849 |
|
| 850 |
+
def update_revenue_chart(analytics_instance, time_gran, customer_id):
|
| 851 |
+
"""Update revenue chart based on filters"""
|
| 852 |
+
try:
|
| 853 |
+
chart = analytics_instance.get_revenue_chart_with_filters(time_gran, customer_id)
|
| 854 |
+
return chart
|
| 855 |
+
except Exception as e:
|
| 856 |
+
return None
|
| 857 |
+
|
| 858 |
+
def update_customer_dropdown(analytics_instance):
|
| 859 |
+
"""Update customer dropdown options"""
|
| 860 |
+
try:
|
| 861 |
+
customers = analytics_instance.get_customer_list()
|
| 862 |
+
return gr.Dropdown(choices=customers, value='all')
|
| 863 |
+
except:
|
| 864 |
+
return gr.Dropdown(choices=['all'], value='all')
|
| 865 |
+
|
| 866 |
def create_gradio_interface():
|
| 867 |
"""Create the enhanced Gradio interface"""
|
| 868 |
|
|
|
|
| 895 |
|
| 896 |
with gr.Tabs():
|
| 897 |
|
| 898 |
+
with gr.Tab("Data Upload & Dashboard"):
|
| 899 |
with gr.Row():
|
| 900 |
with gr.Column():
|
| 901 |
file_input = gr.File(
|
|
|
|
| 917 |
summary_display = gr.HTML()
|
| 918 |
data_preview = gr.DataFrame(label="Data Preview (First 20 Rows)")
|
| 919 |
|
| 920 |
+
with gr.Tab("Customer Segmentation"):
|
| 921 |
with gr.Row():
|
| 922 |
with gr.Column():
|
| 923 |
segment_chart = gr.Plot(label="Customer Segments Distribution")
|
|
|
|
| 938 |
</div>
|
| 939 |
""")
|
| 940 |
|
| 941 |
+
with gr.Tab("Churn Prediction"):
|
| 942 |
train_btn = gr.Button(
|
| 943 |
"Train Churn Prediction Model",
|
| 944 |
variant="primary",
|
|
|
|
| 962 |
</div>
|
| 963 |
""")
|
| 964 |
|
| 965 |
+
with gr.Tab("Revenue Analytics"):
|
| 966 |
+
with gr.Row():
|
| 967 |
+
with gr.Column(scale=1):
|
| 968 |
+
time_granularity = gr.Radio(
|
| 969 |
+
choices=['day', 'week', 'month', 'year'],
|
| 970 |
+
value='month',
|
| 971 |
+
label="Time Granularity"
|
| 972 |
+
)
|
| 973 |
+
customer_filter = gr.Dropdown(
|
| 974 |
+
choices=[], # Will be populated dynamically
|
| 975 |
+
value='all',
|
| 976 |
+
label="Customer Filter"
|
| 977 |
+
)
|
| 978 |
+
update_chart_btn = gr.Button("Update Chart", variant="primary")
|
| 979 |
+
|
| 980 |
+
with gr.Column(scale=3):
|
| 981 |
+
revenue_chart = gr.Plot(label="Revenue Trends")
|
| 982 |
|
| 983 |
+
# Add the info box
|
| 984 |
gr.HTML("""
|
| 985 |
<div style="background: #ecfdf5; padding: 1rem; border-radius: 8px; border-left: 4px solid #10b981; margin-top: 1rem;">
|
| 986 |
+
<h4 style="color: #065f46; margin: 0 0 0.5rem 0;">Interactive Revenue Analysis</h4>
|
| 987 |
<p style="margin: 0; color: #1f2937; font-size: 0.9rem;">
|
| 988 |
+
Select time granularity (day/week/month/year) and specific customers to analyze revenue patterns.
|
| 989 |
+
Use "all" to view aggregate trends across all customers.
|
| 990 |
</p>
|
| 991 |
</div>
|
| 992 |
""")
|
| 993 |
|
| 994 |
+
with gr.Tab("Customer Insights"):
|
| 995 |
with gr.Row():
|
| 996 |
customer_id_input = gr.Textbox(
|
| 997 |
label="Customer ID",
|
|
|
|
| 1006 |
|
| 1007 |
customer_insights = gr.HTML()
|
| 1008 |
|
| 1009 |
+
with gr.Tab("Reports"):
|
| 1010 |
with gr.Row():
|
| 1011 |
with gr.Column():
|
| 1012 |
gr.HTML("""
|
|
|
|
| 1033 |
def safe_load_data(analytics_instance, file):
|
| 1034 |
try:
|
| 1035 |
if file is None:
|
| 1036 |
+
return analytics_instance, "Please upload a CSV file", "", None, None, None, None, None, None, gr.Dropdown(choices=['all'], value='all')
|
| 1037 |
|
| 1038 |
status, dashboard, preview = analytics_instance.load_data(file)
|
| 1039 |
|
| 1040 |
if "successfully" in status:
|
| 1041 |
charts = analytics_instance.get_visualizations()
|
| 1042 |
table = analytics_instance.get_customer_table()
|
| 1043 |
+
# Update customer dropdown
|
| 1044 |
+
customers = analytics_instance.get_customer_list()
|
| 1045 |
+
customer_dropdown = gr.Dropdown(choices=customers, value='all')
|
| 1046 |
+
return analytics_instance, status, dashboard, preview, *charts, table, customer_dropdown
|
| 1047 |
else:
|
| 1048 |
+
return analytics_instance, status, "", None, None, None, None, None, None, gr.Dropdown(choices=['all'], value='all')
|
| 1049 |
|
| 1050 |
except Exception as e:
|
| 1051 |
error_msg = f"Error loading data: {str(e)}"
|
| 1052 |
+
return analytics_instance, error_msg, "", None, None, None, None, None, None, gr.Dropdown(choices=['all'], value='all')
|
| 1053 |
|
| 1054 |
def safe_train_model(analytics_instance):
|
| 1055 |
try:
|
|
|
|
| 1089 |
fn=safe_load_data,
|
| 1090 |
inputs=[analytics, file_input],
|
| 1091 |
outputs=[analytics, load_status, summary_display, data_preview,
|
| 1092 |
+
segment_chart, rfm_chart, churn_distribution_chart, revenue_chart, customer_table, customer_filter]
|
| 1093 |
+
)
|
| 1094 |
+
|
| 1095 |
+
# Update chart when filters change
|
| 1096 |
+
update_chart_btn.click(
|
| 1097 |
+
fn=update_revenue_chart,
|
| 1098 |
+
inputs=[analytics, time_granularity, customer_filter],
|
| 1099 |
+
outputs=[revenue_chart]
|
| 1100 |
+
)
|
| 1101 |
+
|
| 1102 |
+
# Auto-update on filter change
|
| 1103 |
+
time_granularity.change(
|
| 1104 |
+
fn=update_revenue_chart,
|
| 1105 |
+
inputs=[analytics, time_granularity, customer_filter],
|
| 1106 |
+
outputs=[revenue_chart]
|
| 1107 |
+
)
|
| 1108 |
+
|
| 1109 |
+
customer_filter.change(
|
| 1110 |
+
fn=update_revenue_chart,
|
| 1111 |
+
inputs=[analytics, time_granularity, customer_filter],
|
| 1112 |
+
outputs=[revenue_chart]
|
| 1113 |
)
|
| 1114 |
|
| 1115 |
train_btn.click(
|