mktgtech commited on
Commit
2cf7206
Β·
verified Β·
1 Parent(s): 0de841a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -57
app.py CHANGED
@@ -34,6 +34,7 @@ CAMPAIGN_CONFIG_FILE = "campaign_rules.json"
34
  # =====================================================
35
  # 1. CAMPAIGN MANAGER & BUSINESS LOGIC
36
  # =====================================================
 
37
  DEFAULT_CAMPAIGNS = {
38
  "MS Stack": [
39
  "microsoft", "power", "dynamics", "crm", "365", "xamarin", "sharepoint",
@@ -115,19 +116,16 @@ def apply_business_logic(df):
115
  # 2. PRESETS
116
  # =====================================================
117
  DASHBOARD_PRESETS = {
118
- "All Campaigns (Multi-Chart)": ("SPECIAL_MULTI", None, None), # DYNAMIC ALL CAMPAIGNS
 
 
 
119
 
120
  "Visits by Campaign": ("Campaign", "total_visits", "sum"),
121
  "Leads by Campaign": ("Campaign", "company_name", "count"),
122
  "Leads by Quality Group": ("Quality_Group", "company_name", "count"),
123
-
124
  "Total Visits by Industry": ("primary_industry", "total_visits", "sum"),
125
- "Total Visits by Country": ("country", "total_visits", "sum"),
126
- "Avg Quality by Industry": ("primary_industry", "lead_quality_score", "mean"),
127
  "Top Accounts by Visits": ("company_name", "total_visits", "sum"),
128
- "Companies by Country": ("country", "company_name", "count"),
129
- "Visits by City": ("city", "total_visits", "sum"),
130
- "Accounts by Assignee": ("assignee", "company_name", "count"),
131
  }
132
 
133
  TREND_PRESETS = {
@@ -137,14 +135,10 @@ TREND_PRESETS = {
137
 
138
  "Total Visits Trend": ("total_visits", "sum", None),
139
  "Active Accounts Trend": ("company_name", "count", None),
140
- "Avg Lead Quality Trend": ("lead_quality_score", "mean", None),
141
- "Visits Trend by Industry": ("total_visits", "sum", "primary_industry"),
142
- "Visits Trend by Country": ("total_visits", "sum", "country"),
143
- "Visits Trend by Assignee": ("total_visits", "sum", "assignee"),
144
  }
145
 
146
  # =====================================================
147
- # 3. API HANDLING (FULL DATA RESTORED)
148
  # =====================================================
149
  def make_request(url, params=None):
150
  retries = 3
@@ -160,7 +154,7 @@ def make_request(url, params=None):
160
  raise Exception("Max retries exceeded")
161
 
162
  def fetch_basic_leads(start_date, end_date):
163
- """ Fetch ALL company details (Restored all original fields). """
164
  page = 1
165
  rows = []
166
  print(f"πŸš€ Fetching full company list for {start_date} to {end_date}...")
@@ -237,6 +231,7 @@ def enrich_leads_with_visits(df, start_date, end_date, max_rows=None, progress=g
237
  print(f"πŸ•΅οΈ Deep enriching {total} rows ({start_date} to {end_date})...")
238
 
239
  for index, row in target_df.iterrows():
 
240
  if row.get("landing_page_path") and row.get("exit_page_path"): continue
241
 
242
  lead_id = row["lead_id"]
@@ -299,13 +294,13 @@ def load_preview(start, end):
299
  df = fetch_basic_leads(start, end)
300
  if df.empty: return df, pd.DataFrame(), "⚠️ No data found."
301
 
302
- # Enrich top 20 for preview speed
303
  df_preview = df.copy()
304
- df_preview = enrich_leads_with_visits(df_preview, start, end, max_rows=20)
305
  df_preview = apply_business_logic(df_preview)
306
 
307
- status = f"βœ… Loaded **{len(df):,} companies**. Preview shows top 20 rows enriched."
308
- return df, df_preview.head(20), status
309
 
310
  def download_full_excel(df, start, end):
311
  if df is None or df.empty: return None
@@ -329,7 +324,7 @@ def inspect_raw_json(lead_id, start_date, end_date):
329
  return str(e)
330
 
331
  # =====================================================
332
- # 5. CHART ENGINES
333
  # =====================================================
334
  def build_kpis(df):
335
  if df is None or df.empty: return 0, 0, 0, 0, 0, 0, 0
@@ -343,56 +338,67 @@ def build_kpis(df):
343
  int(df["total_visits"].sum()),
344
  round(df["lead_quality_score"].mean(), 2),
345
  round(df["crm_organization_id"].notna().mean() * 100, 1),
346
- round(df["linkedin_url"].notna().mean() * 100, 1), # Restored LinkedIn %
347
  df[df["Quality_Group"] == "High Quality (8-10)"].shape[0],
348
  )
349
 
350
  def build_dashboard(df, preset, top_n):
351
  if df is None or df.empty: return px.bar(title="No Data")
352
-
353
  if "Campaign" not in df.columns: df = apply_business_logic(df)
354
 
355
- # --- SPECIAL MULTI-CHART FOR ALL CAMPAIGNS ---
356
- if preset == "All Campaigns (Multi-Chart)":
 
 
 
 
357
 
358
- # 1. Get ALL campaigns present in data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  campaigns = df["Campaign"].dropna().unique().tolist()
360
  if not campaigns: return px.bar(title="No Campaigns Found")
361
 
362
- # Sort them (maybe put 'Other' last)
363
- campaigns.sort(key=lambda x: (x == "Other", x))
364
 
365
  num_cams = len(campaigns)
366
- cols = 2 # 2 columns wide grid
367
  rows = math.ceil(num_cams / cols)
368
 
369
- fig = make_subplots(
370
- rows=rows, cols=cols,
371
- subplot_titles=campaigns,
372
- vertical_spacing=0.1
373
- )
374
 
375
  for i, camp in enumerate(campaigns):
376
- # Calculate grid position
377
  r = (i // cols) + 1
378
  c = (i % cols) + 1
379
 
380
- # Filter and Get Top Companies for this campaign
381
  camp_df = df[df["Campaign"] == camp]
382
  grouped = camp_df.groupby("company_name")["total_visits"].sum().reset_index()
383
  grouped = grouped.sort_values("total_visits", ascending=False).head(top_n)
384
 
385
- # Add Bar Trace
386
- fig.add_trace(
387
- go.Bar(x=grouped["company_name"], y=grouped["total_visits"], name=camp),
388
- row=r, col=c
389
- )
390
 
391
- fig.update_layout(
392
- title_text=f"Top Companies by Campaign (All {num_cams})",
393
- height=300 * rows, # Dynamic height
394
- showlegend=False
395
- )
396
  return fig
397
 
398
  # --- STANDARD CHARTS ---
@@ -409,11 +415,14 @@ def get_trend_filter_options(df, preset):
409
 
410
  metric, agg, segment = TREND_PRESETS[preset]
411
  if not segment: return gr.update(choices=[], value=None, visible=False)
412
-
413
  if segment not in df.columns: df = apply_business_logic(df)
414
 
415
  options = sorted(df[segment].astype(str).unique().tolist())
416
- return gr.update(choices=options, value=options[:5], visible=True, label=f"Filter {segment}")
 
 
 
 
417
 
418
  def build_trend(df, preset, grain, filter_values):
419
  if df is None or df.empty: return px.line(title="No Data")
@@ -440,13 +449,13 @@ def build_trend(df, preset, grain, filter_values):
440
 
441
  if segment:
442
  ts = df_t.groupby(["period", segment])[val].agg(agg).reset_index()
443
- return px.line(ts, x="period", y=val, color=segment, title=preset)
444
 
445
  ts = df_t.groupby("period")[val].agg(agg).reset_index()
446
  return px.line(ts, x="period", y=val, markers=True, title=preset)
447
 
448
  # =====================================================
449
- # 6. UI LAYOUT
450
  # =====================================================
451
  with gr.Blocks(title="Leadfeeder Campaign Pro") as demo:
452
  gr.Markdown("## πŸš€ Leadfeeder Analytics & Campaign Manager")
@@ -470,18 +479,21 @@ with gr.Blocks(title="Leadfeeder Campaign Pro") as demo:
470
  btn_dl = gr.Button("2. Enrich & Download Full Excel")
471
  file_dl = gr.File(label="Download Excel")
472
 
473
- table = gr.Dataframe(label="Preview (Top 20 Enriched)", interactive=True)
474
  btn_load.click(load_preview, [start, end], [df_state, table, status])
475
  btn_dl.click(download_full_excel, [df_state, start, end], file_dl)
476
 
477
  # --- TAB 2: DASHBOARD ---
478
  with gr.Tab("πŸ“Š Dashboard"):
479
- kpis = [gr.Number(label=l) for l in ["Total Companies", "Active Companies", "Total Visits", "Avg Quality", "CRM %", "LinkedIn %", "High Quality (8-10)"]]
 
 
480
  gr.Button("Refresh KPIs").click(build_kpis, df_state, kpis)
481
 
 
482
  with gr.Row():
483
- preset = gr.Dropdown(choices=list(DASHBOARD_PRESETS.keys()), label="Chart", value="All Campaigns (Multi-Chart)")
484
- top_n = gr.Slider(5, 50, value=5, label="Top N per Chart")
485
 
486
  chart = gr.Plot()
487
  gr.Button("Build View").click(build_dashboard, [df_state, preset, top_n], chart)
@@ -500,7 +512,7 @@ with gr.Blocks(title="Leadfeeder Campaign Pro") as demo:
500
 
501
  # --- TAB 4: SETTINGS ---
502
  with gr.Tab("βš™οΈ Campaign Settings"):
503
- gr.Markdown("### Manage Campaign Groups & Keywords")
504
  current_rules = json.dumps(load_campaign_rules(), indent=4)
505
  config_editor = gr.Code(value=current_rules, language="json", label="Campaign Configuration")
506
  save_config_btn = gr.Button("πŸ’Ύ Save Configuration")
@@ -509,11 +521,7 @@ with gr.Blocks(title="Leadfeeder Campaign Pro") as demo:
509
 
510
  # --- TAB 5: DEBUGGER ---
511
  with gr.Tab("πŸ› οΈ Debugger"):
512
- gr.Markdown("Inspect raw API data for a specific lead.")
513
  dbg_id = gr.Textbox(label="Lead ID")
514
  dbg_btn = gr.Button("Inspect Raw JSON")
515
  dbg_out = gr.Code(language="json")
516
- dbg_btn.click(inspect_raw_json, [dbg_id, start, end], dbg_out)
517
-
518
- if __name__ == "__main__":
519
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
34
  # =====================================================
35
  # 1. CAMPAIGN MANAGER & BUSINESS LOGIC
36
  # =====================================================
37
+ # UPDATED: Renamed "MS Stack" to "MS Tech" as requested
38
  DEFAULT_CAMPAIGNS = {
39
  "MS Stack": [
40
  "microsoft", "power", "dynamics", "crm", "365", "xamarin", "sharepoint",
 
116
  # 2. PRESETS
117
  # =====================================================
118
  DASHBOARD_PRESETS = {
119
+ # -- NEW: TARGETED COMPARISON --
120
+ "Key Campaigns Overview (MS Tech, Fintech, Adtech)": ("SPECIAL_KEY_CAMPAIGNS", None, None),
121
+
122
+ "All Campaigns (Multi-Chart)": ("SPECIAL_MULTI", None, None),
123
 
124
  "Visits by Campaign": ("Campaign", "total_visits", "sum"),
125
  "Leads by Campaign": ("Campaign", "company_name", "count"),
126
  "Leads by Quality Group": ("Quality_Group", "company_name", "count"),
 
127
  "Total Visits by Industry": ("primary_industry", "total_visits", "sum"),
 
 
128
  "Top Accounts by Visits": ("company_name", "total_visits", "sum"),
 
 
 
129
  }
130
 
131
  TREND_PRESETS = {
 
135
 
136
  "Total Visits Trend": ("total_visits", "sum", None),
137
  "Active Accounts Trend": ("company_name", "count", None),
 
 
 
 
138
  }
139
 
140
  # =====================================================
141
+ # 3. API HANDLING (FULL DATA PRESERVED)
142
  # =====================================================
143
  def make_request(url, params=None):
144
  retries = 3
 
154
  raise Exception("Max retries exceeded")
155
 
156
  def fetch_basic_leads(start_date, end_date):
157
+ """ Fetch ALL company details (No data points missed). """
158
  page = 1
159
  rows = []
160
  print(f"πŸš€ Fetching full company list for {start_date} to {end_date}...")
 
231
  print(f"πŸ•΅οΈ Deep enriching {total} rows ({start_date} to {end_date})...")
232
 
233
  for index, row in target_df.iterrows():
234
+ # Skip if already enriched
235
  if row.get("landing_page_path") and row.get("exit_page_path"): continue
236
 
237
  lead_id = row["lead_id"]
 
294
  df = fetch_basic_leads(start, end)
295
  if df.empty: return df, pd.DataFrame(), "⚠️ No data found."
296
 
297
+ # Enrich top 50 for better preview categorization
298
  df_preview = df.copy()
299
+ df_preview = enrich_leads_with_visits(df_preview, start, end, max_rows=50)
300
  df_preview = apply_business_logic(df_preview)
301
 
302
+ status = f"βœ… Loaded **{len(df):,} companies**. Preview shows top 50 rows enriched."
303
+ return df, df_preview.head(50), status
304
 
305
  def download_full_excel(df, start, end):
306
  if df is None or df.empty: return None
 
324
  return str(e)
325
 
326
  # =====================================================
327
+ # 5. CHART ENGINES (IMPROVED UI)
328
  # =====================================================
329
  def build_kpis(df):
330
  if df is None or df.empty: return 0, 0, 0, 0, 0, 0, 0
 
338
  int(df["total_visits"].sum()),
339
  round(df["lead_quality_score"].mean(), 2),
340
  round(df["crm_organization_id"].notna().mean() * 100, 1),
341
+ round(df["linkedin_url"].notna().mean() * 100, 1),
342
  df[df["Quality_Group"] == "High Quality (8-10)"].shape[0],
343
  )
344
 
345
  def build_dashboard(df, preset, top_n):
346
  if df is None or df.empty: return px.bar(title="No Data")
 
347
  if "Campaign" not in df.columns: df = apply_business_logic(df)
348
 
349
+ # --- 1. NEW KEY CAMPAIGNS COMPARISON (MS Tech, Fintech, Adtech) ---
350
+ if preset == "Key Campaigns Overview (MS Tech, Fintech, Adtech)":
351
+ target_campaigns = ["MS Tech", "Fintech", "Adtech"]
352
+
353
+ # Filter Data
354
+ filtered_df = df[df["Campaign"].isin(target_campaigns)].copy()
355
 
356
+ if filtered_df.empty:
357
+ return px.bar(title="No data found for MS Tech, Fintech, or Adtech. Try 'Download Excel' to enrich more rows.")
358
+
359
+ # Create Aggregates
360
+ visits = filtered_df.groupby("Campaign")["total_visits"].sum().reset_index()
361
+ leads = filtered_df.groupby("Campaign")["company_name"].count().reset_index()
362
+
363
+ # Subplots: Left=Visits, Right=Leads
364
+ fig = make_subplots(rows=1, cols=2, subplot_titles=("Total Visits", "Total Leads (Count)"))
365
+
366
+ fig.add_trace(
367
+ go.Bar(x=visits["Campaign"], y=visits["total_visits"], name="Visits", marker_color='indigo'),
368
+ row=1, col=1
369
+ )
370
+ fig.add_trace(
371
+ go.Bar(x=leads["Campaign"], y=leads["company_name"], name="Leads", marker_color='teal'),
372
+ row=1, col=2
373
+ )
374
+ fig.update_layout(title_text="Key Campaigns Comparison", showlegend=False)
375
+ return fig
376
+
377
+ # --- 2. ALL CAMPAIGNS MULTI-CHART ---
378
+ if preset == "All Campaigns (Multi-Chart)":
379
  campaigns = df["Campaign"].dropna().unique().tolist()
380
  if not campaigns: return px.bar(title="No Campaigns Found")
381
 
382
+ # Sort so key campaigns appear first if possible, or A-Z
383
+ campaigns.sort()
384
 
385
  num_cams = len(campaigns)
386
+ cols = 2
387
  rows = math.ceil(num_cams / cols)
388
 
389
+ fig = make_subplots(rows=rows, cols=cols, subplot_titles=campaigns, vertical_spacing=0.15)
 
 
 
 
390
 
391
  for i, camp in enumerate(campaigns):
 
392
  r = (i // cols) + 1
393
  c = (i % cols) + 1
394
 
 
395
  camp_df = df[df["Campaign"] == camp]
396
  grouped = camp_df.groupby("company_name")["total_visits"].sum().reset_index()
397
  grouped = grouped.sort_values("total_visits", ascending=False).head(top_n)
398
 
399
+ fig.add_trace(go.Bar(x=grouped["company_name"], y=grouped["total_visits"], name=camp), row=r, col=c)
 
 
 
 
400
 
401
+ fig.update_layout(title_text=f"Top Companies by Campaign (All {num_cams})", height=350 * rows, showlegend=False)
 
 
 
 
402
  return fig
403
 
404
  # --- STANDARD CHARTS ---
 
415
 
416
  metric, agg, segment = TREND_PRESETS[preset]
417
  if not segment: return gr.update(choices=[], value=None, visible=False)
 
418
  if segment not in df.columns: df = apply_business_logic(df)
419
 
420
  options = sorted(df[segment].astype(str).unique().tolist())
421
+ # Default select key campaigns if available
422
+ defaults = [o for o in options if o in ["MS Tech", "Fintech", "Adtech"]]
423
+ if not defaults: defaults = options[:5]
424
+
425
+ return gr.update(choices=options, value=defaults, visible=True, label=f"Filter {segment}")
426
 
427
  def build_trend(df, preset, grain, filter_values):
428
  if df is None or df.empty: return px.line(title="No Data")
 
449
 
450
  if segment:
451
  ts = df_t.groupby(["period", segment])[val].agg(agg).reset_index()
452
+ return px.line(ts, x="period", y=val, color=segment, title=preset, markers=True)
453
 
454
  ts = df_t.groupby("period")[val].agg(agg).reset_index()
455
  return px.line(ts, x="period", y=val, markers=True, title=preset)
456
 
457
  # =====================================================
458
+ # 6. UI LAYOUT (STABLE & CLEAN)
459
  # =====================================================
460
  with gr.Blocks(title="Leadfeeder Campaign Pro") as demo:
461
  gr.Markdown("## πŸš€ Leadfeeder Analytics & Campaign Manager")
 
479
  btn_dl = gr.Button("2. Enrich & Download Full Excel")
480
  file_dl = gr.File(label="Download Excel")
481
 
482
+ table = gr.Dataframe(label="Preview (Top 50 Enriched)", interactive=True, height=400)
483
  btn_load.click(load_preview, [start, end], [df_state, table, status])
484
  btn_dl.click(download_full_excel, [df_state, start, end], file_dl)
485
 
486
  # --- TAB 2: DASHBOARD ---
487
  with gr.Tab("πŸ“Š Dashboard"):
488
+ with gr.Row():
489
+ # KPIs in a row
490
+ kpis = [gr.Number(label=l) for l in ["Companies", "Active", "Visits", "Avg Quality", "CRM %", "LinkedIn %", "High Quality (8-10)"]]
491
  gr.Button("Refresh KPIs").click(build_kpis, df_state, kpis)
492
 
493
+ gr.Markdown("### πŸ“Š Charts")
494
  with gr.Row():
495
+ preset = gr.Dropdown(choices=list(DASHBOARD_PRESETS.keys()), label="Chart View", value="Key Campaigns Overview (MS Tech, Fintech, Adtech)")
496
+ top_n = gr.Slider(5, 50, value=10, label="Top N Items")
497
 
498
  chart = gr.Plot()
499
  gr.Button("Build View").click(build_dashboard, [df_state, preset, top_n], chart)
 
512
 
513
  # --- TAB 4: SETTINGS ---
514
  with gr.Tab("βš™οΈ Campaign Settings"):
515
+ gr.Markdown("### Manage Campaign Groups")
516
  current_rules = json.dumps(load_campaign_rules(), indent=4)
517
  config_editor = gr.Code(value=current_rules, language="json", label="Campaign Configuration")
518
  save_config_btn = gr.Button("πŸ’Ύ Save Configuration")
 
521
 
522
  # --- TAB 5: DEBUGGER ---
523
  with gr.Tab("πŸ› οΈ Debugger"):
 
524
  dbg_id = gr.Textbox(label="Lead ID")
525
  dbg_btn = gr.Button("Inspect Raw JSON")
526
  dbg_out = gr.Code(language="json")
527
+ dbg_btn.click(inspect_