Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -156,6 +156,10 @@ def enrich_leads_with_visits(df, start_date, end_date, max_rows=None, progress=g
|
|
| 156 |
print(f"๐ต๏ธ Deep enriching {total} rows ({start_date} to {end_date})...")
|
| 157 |
|
| 158 |
for index, row in target_df.iterrows():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
lead_id = row["lead_id"]
|
| 160 |
try:
|
| 161 |
visit_data = make_request(
|
|
@@ -208,7 +212,7 @@ def enrich_leads_with_visits(df, start_date, end_date, max_rows=None, progress=g
|
|
| 208 |
except Exception as e:
|
| 209 |
print(f"Failed to enrich lead {lead_id}: {e}")
|
| 210 |
|
| 211 |
-
if index % 5 == 0:
|
| 212 |
progress(index / total, desc="Enriching...")
|
| 213 |
|
| 214 |
return df
|
|
@@ -315,42 +319,51 @@ def build_trend(df, preset, grain, filter_values):
|
|
| 315 |
return px.line(ts, x="period", y=val, markers=True, template="plotly_white", title=preset)
|
| 316 |
|
| 317 |
# =====================================================
|
| 318 |
-
# ADVANCED ANALYTICS (WITH
|
| 319 |
# =====================================================
|
| 320 |
-
def build_advanced_plots(df, search_lead_id
|
| 321 |
if df is None or df.empty:
|
| 322 |
return None, None
|
| 323 |
|
| 324 |
-
# ---
|
| 325 |
filtered_df = df.copy()
|
| 326 |
-
|
| 327 |
title_suffix = ""
|
| 328 |
|
| 329 |
-
# 1. Filter by Lead ID (Exact Match)
|
| 330 |
if search_lead_id and search_lead_id.strip():
|
| 331 |
filtered_df = filtered_df[filtered_df["lead_id"] == search_lead_id.strip()]
|
| 332 |
title_suffix = f" (Lead: {search_lead_id})"
|
| 333 |
|
| 334 |
-
# 2. Filter by Company Name (Partial Match, Case Insensitive)
|
| 335 |
if search_company and search_company.strip():
|
| 336 |
-
# Handle NA values before string search
|
| 337 |
filtered_df = filtered_df[filtered_df["company_name"].notna()]
|
| 338 |
filtered_df = filtered_df[filtered_df["company_name"].str.contains(search_company, case=False, na=False)]
|
| 339 |
title_suffix = f" (Company: {search_company})"
|
| 340 |
|
| 341 |
if filtered_df.empty:
|
| 342 |
-
# Return empty figures or message logic handled by returning None
|
| 343 |
return go.Figure(layout=dict(title="No matching data found")), go.Figure(layout=dict(title="No matching data found"))
|
| 344 |
|
| 345 |
-
# ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
flow_df = filtered_df.dropna(subset=["landing_page_path", "exit_page_path"])
|
| 347 |
|
| 348 |
if flow_df.empty:
|
| 349 |
-
sankey_fig = go.Figure(layout=dict(title="No
|
| 350 |
else:
|
| 351 |
flow_counts = flow_df.groupby(["landing_page_path", "exit_page_path"]).size().reset_index(name="count")
|
| 352 |
|
| 353 |
-
# If searching specific company, we show ALL their flows. Otherwise top 20.
|
| 354 |
if not search_lead_id and not search_company:
|
| 355 |
flow_counts = flow_counts.sort_values("count", ascending=False).head(20)
|
| 356 |
|
|
@@ -367,10 +380,10 @@ def build_advanced_plots(df, search_lead_id=None, search_company=None):
|
|
| 367 |
)])
|
| 368 |
sankey_fig.update_layout(title_text=f"User Flow: Landing โ Exit{title_suffix}", font_size=10)
|
| 369 |
|
| 370 |
-
# ---
|
| 371 |
temp_df = filtered_df.dropna(subset=["last_visit_date"]).copy()
|
| 372 |
if temp_df.empty:
|
| 373 |
-
heatmap_fig = go.Figure(layout=dict(title="No visit dates found
|
| 374 |
else:
|
| 375 |
temp_df["day_name"] = temp_df["last_visit_date"].dt.day_name()
|
| 376 |
temp_df["hour"] = temp_df["last_visit_date"].dt.hour
|
|
@@ -449,7 +462,7 @@ with gr.Blocks(title="Leadfeeder Pro Analytics") as demo:
|
|
| 449 |
plot
|
| 450 |
)
|
| 451 |
|
| 452 |
-
# --- TAB 4: ADVANCED ANALYTICS
|
| 453 |
with gr.Tab("๐ฌ Advanced Analytics"):
|
| 454 |
gr.Markdown("Visualize user behavior flows. Leave search blank to see aggregate Top 20 flows.")
|
| 455 |
|
|
@@ -463,8 +476,8 @@ with gr.Blocks(title="Leadfeeder Pro Analytics") as demo:
|
|
| 463 |
sankey_plot = gr.Plot(label="User Flow (Sankey)")
|
| 464 |
heatmap_plot = gr.Plot(label="Activity Heatmap")
|
| 465 |
|
| 466 |
-
#
|
| 467 |
-
btn_adv.click(build_advanced_plots, [df_state, search_lead, search_comp], [sankey_plot, heatmap_plot])
|
| 468 |
|
| 469 |
# --- TAB 5: DEBUGGER ---
|
| 470 |
with gr.Tab("๐ ๏ธ Debugger"):
|
|
|
|
| 156 |
print(f"๐ต๏ธ Deep enriching {total} rows ({start_date} to {end_date})...")
|
| 157 |
|
| 158 |
for index, row in target_df.iterrows():
|
| 159 |
+
# Only enrich if data is missing
|
| 160 |
+
if row.get("landing_page_path") and row.get("exit_page_path"):
|
| 161 |
+
continue
|
| 162 |
+
|
| 163 |
lead_id = row["lead_id"]
|
| 164 |
try:
|
| 165 |
visit_data = make_request(
|
|
|
|
| 212 |
except Exception as e:
|
| 213 |
print(f"Failed to enrich lead {lead_id}: {e}")
|
| 214 |
|
| 215 |
+
if max_rows and index % 5 == 0:
|
| 216 |
progress(index / total, desc="Enriching...")
|
| 217 |
|
| 218 |
return df
|
|
|
|
| 319 |
return px.line(ts, x="period", y=val, markers=True, template="plotly_white", title=preset)
|
| 320 |
|
| 321 |
# =====================================================
|
| 322 |
+
# ADVANCED ANALYTICS (WITH AUTO-ENRICHMENT)
|
| 323 |
# =====================================================
|
| 324 |
+
def build_advanced_plots(df, search_lead_id, search_company, start_date, end_date):
|
| 325 |
if df is None or df.empty:
|
| 326 |
return None, None
|
| 327 |
|
| 328 |
+
# --- 1. FILTER ---
|
| 329 |
filtered_df = df.copy()
|
|
|
|
| 330 |
title_suffix = ""
|
| 331 |
|
|
|
|
| 332 |
if search_lead_id and search_lead_id.strip():
|
| 333 |
filtered_df = filtered_df[filtered_df["lead_id"] == search_lead_id.strip()]
|
| 334 |
title_suffix = f" (Lead: {search_lead_id})"
|
| 335 |
|
|
|
|
| 336 |
if search_company and search_company.strip():
|
|
|
|
| 337 |
filtered_df = filtered_df[filtered_df["company_name"].notna()]
|
| 338 |
filtered_df = filtered_df[filtered_df["company_name"].str.contains(search_company, case=False, na=False)]
|
| 339 |
title_suffix = f" (Company: {search_company})"
|
| 340 |
|
| 341 |
if filtered_df.empty:
|
|
|
|
| 342 |
return go.Figure(layout=dict(title="No matching data found")), go.Figure(layout=dict(title="No matching data found"))
|
| 343 |
|
| 344 |
+
# --- 2. AUTO-ENRICHMENT ---
|
| 345 |
+
# Check if the filtered rows are missing landing/exit data.
|
| 346 |
+
# If so, fetch it immediately!
|
| 347 |
+
missing_data = filtered_df[filtered_df["landing_page_path"].isnull()]
|
| 348 |
+
|
| 349 |
+
if not missing_data.empty:
|
| 350 |
+
# We pass the subset to the enrich function
|
| 351 |
+
print(f"๐ Auto-enriching {len(missing_data)} rows for visualization...")
|
| 352 |
+
|
| 353 |
+
# We need to update the original filtered_df with the new data
|
| 354 |
+
enriched_subset = enrich_leads_with_visits(missing_data, start_date, end_date)
|
| 355 |
+
|
| 356 |
+
# Update the rows in filtered_df using the enriched data
|
| 357 |
+
filtered_df.update(enriched_subset)
|
| 358 |
+
|
| 359 |
+
# --- 3. SANKEY DIAGRAM ---
|
| 360 |
flow_df = filtered_df.dropna(subset=["landing_page_path", "exit_page_path"])
|
| 361 |
|
| 362 |
if flow_df.empty:
|
| 363 |
+
sankey_fig = go.Figure(layout=dict(title="No visit data available for selected lead(s)"))
|
| 364 |
else:
|
| 365 |
flow_counts = flow_df.groupby(["landing_page_path", "exit_page_path"]).size().reset_index(name="count")
|
| 366 |
|
|
|
|
| 367 |
if not search_lead_id and not search_company:
|
| 368 |
flow_counts = flow_counts.sort_values("count", ascending=False).head(20)
|
| 369 |
|
|
|
|
| 380 |
)])
|
| 381 |
sankey_fig.update_layout(title_text=f"User Flow: Landing โ Exit{title_suffix}", font_size=10)
|
| 382 |
|
| 383 |
+
# --- 4. HEATMAP ---
|
| 384 |
temp_df = filtered_df.dropna(subset=["last_visit_date"]).copy()
|
| 385 |
if temp_df.empty:
|
| 386 |
+
heatmap_fig = go.Figure(layout=dict(title="No visit dates found"))
|
| 387 |
else:
|
| 388 |
temp_df["day_name"] = temp_df["last_visit_date"].dt.day_name()
|
| 389 |
temp_df["hour"] = temp_df["last_visit_date"].dt.hour
|
|
|
|
| 462 |
plot
|
| 463 |
)
|
| 464 |
|
| 465 |
+
# --- TAB 4: ADVANCED ANALYTICS ---
|
| 466 |
with gr.Tab("๐ฌ Advanced Analytics"):
|
| 467 |
gr.Markdown("Visualize user behavior flows. Leave search blank to see aggregate Top 20 flows.")
|
| 468 |
|
|
|
|
| 476 |
sankey_plot = gr.Plot(label="User Flow (Sankey)")
|
| 477 |
heatmap_plot = gr.Plot(label="Activity Heatmap")
|
| 478 |
|
| 479 |
+
# NOTE: Passed start/end dates so we can auto-enrich on the fly!
|
| 480 |
+
btn_adv.click(build_advanced_plots, [df_state, search_lead, search_comp, start, end], [sankey_plot, heatmap_plot])
|
| 481 |
|
| 482 |
# --- TAB 5: DEBUGGER ---
|
| 483 |
with gr.Tab("๐ ๏ธ Debugger"):
|