ps1811 commited on
Commit
86f30b4
·
1 Parent(s): 9eaecc1
Files changed (1) hide show
  1. app.py +91 -8
app.py CHANGED
@@ -484,6 +484,9 @@ footer,
484
  }
485
 
486
  .ai-button-card {
 
 
 
487
  color: var(--on-surface) !important;
488
  text-align: left !important;
489
  font-size: 14px !important;
@@ -504,10 +507,11 @@ button.ai-button-card {
504
  justify-content: flex-start !important;
505
  text-align: left !important;
506
  white-space: pre-line !important;
507
- background: transparent !important;
508
  color: var(--on-surface) !important;
509
- border: 0 !important;
510
- box-shadow: none !important;
 
511
  }
512
 
513
  .ai-button-card:hover {
@@ -566,6 +570,10 @@ button.ai-button-card {
566
  color: #ffffff !important;
567
  }
568
 
 
 
 
 
569
  .advisor-intel-title .material-symbols-outlined {
570
  color: #ffffff !important;
571
  font-size: 20px;
@@ -584,12 +592,12 @@ button.ai-button-card {
584
  }
585
 
586
  .intel-label {
587
- color: var(--custom-text-muted);
588
  font-size: 10px;
589
  font-weight: 700;
590
  text-transform: uppercase;
591
  letter-spacing: 0.12em;
592
- opacity: 0.5;
593
  margin-bottom: 12px;
594
  }
595
 
@@ -603,6 +611,10 @@ button.ai-button-card {
603
  line-height: 1.5;
604
  }
605
 
 
 
 
 
606
  .text-primary {
607
  color: #ffffff !important;
608
  font-weight: 700;
@@ -684,6 +696,23 @@ def format_money(value):
684
  return "£0.00"
685
 
686
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  def build_hero_html(spend, leads, cpl, count):
688
  return f"""
689
  <section class="hero-section">
@@ -741,6 +770,59 @@ def build_kpi_html(spend, leads, cpl, count):
741
  """
742
 
743
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
  SIGNAL_FLOW_HTML = """
745
  <div class="signal-card">
746
  <div class="signal-header">
@@ -843,9 +925,10 @@ def initial_data_load():
843
 
844
  def campaign_selected(campaign_name, full_state):
845
  if not campaign_name:
846
- return None
847
 
848
- return on_campaign_select(full_state, campaign_name)
 
849
 
850
 
851
  # ==================================================
@@ -930,7 +1013,7 @@ with gr.Blocks(fill_height=True, fill_width=True, css=CSS) as demo:
930
  campaign_picker.change(
931
  fn=campaign_selected,
932
  inputs=[campaign_picker, full_state],
933
- outputs=[campaign_state],
934
  )
935
 
936
  ads_card.click(
 
484
  }
485
 
486
  .ai-button-card {
487
+ background: var(--custom-card-bg) !important;
488
+ border: 1px solid var(--custom-divider) !important;
489
+ border-radius: 8px !important;
490
  color: var(--on-surface) !important;
491
  text-align: left !important;
492
  font-size: 14px !important;
 
507
  justify-content: flex-start !important;
508
  text-align: left !important;
509
  white-space: pre-line !important;
510
+ background: var(--custom-card-bg) !important;
511
  color: var(--on-surface) !important;
512
+ border: 1px solid var(--custom-divider) !important;
513
+ border-radius: 8px !important;
514
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08) !important;
515
  }
516
 
517
  .ai-button-card:hover {
 
570
  color: #ffffff !important;
571
  }
572
 
573
+ .advisor-intel-title * {
574
+ color: #ffffff !important;
575
+ }
576
+
577
  .advisor-intel-title .material-symbols-outlined {
578
  color: #ffffff !important;
579
  font-size: 20px;
 
592
  }
593
 
594
  .intel-label {
595
+ color: #c7c9d1 !important;
596
  font-size: 10px;
597
  font-weight: 700;
598
  text-transform: uppercase;
599
  letter-spacing: 0.12em;
600
+ opacity: 1;
601
  margin-bottom: 12px;
602
  }
603
 
 
611
  line-height: 1.5;
612
  }
613
 
614
+ .intel-box * {
615
+ color: #ffffff !important;
616
+ }
617
+
618
  .text-primary {
619
  color: #ffffff !important;
620
  font-weight: 700;
 
696
  return "&pound;0.00"
697
 
698
 
699
+ def format_number(value):
700
+ try:
701
+ return f"{float(value):,.0f}"
702
+ except (TypeError, ValueError):
703
+ return "0"
704
+
705
+
706
+ def format_percent(value):
707
+ try:
708
+ numeric = float(value)
709
+ if abs(numeric) <= 1:
710
+ numeric *= 100
711
+ return f"{numeric:.2f}%"
712
+ except (TypeError, ValueError):
713
+ return "0.00%"
714
+
715
+
716
  def build_hero_html(spend, leads, cpl, count):
717
  return f"""
718
  <section class="hero-section">
 
770
  """
771
 
772
 
773
+ def build_campaign_kpi_html(campaign_df):
774
+ if campaign_df is None or campaign_df.empty:
775
+ return build_kpi_html(0, 0, 0, 0)
776
+
777
+ spend = campaign_df["cost"].sum() if "cost" in campaign_df else 0
778
+ clicks = campaign_df["clicks"].sum() if "clicks" in campaign_df else 0
779
+ conversions = campaign_df["conversions"].sum() if "conversions" in campaign_df else 0
780
+ cpl = spend / conversions if conversions else 0
781
+
782
+ if "ctr" in campaign_df:
783
+ ctr = campaign_df["ctr"].mean()
784
+ else:
785
+ impressions = campaign_df["impressions"].sum() if "impressions" in campaign_df else 0
786
+ ctr = clicks / impressions if impressions else 0
787
+
788
+ return f"""
789
+ <div class="kpi-grid">
790
+ <div class="kpi-card">
791
+ <div class="kpi-top">
792
+ <div class="kpi-label">Spend</div>
793
+ <div class="kpi-status">[CAMPAIGN]</div>
794
+ </div>
795
+ <div class="kpi-value">{format_money(spend)}</div>
796
+ <div class="kpi-note note-sky">Selected campaign spend</div>
797
+ </div>
798
+ <div class="kpi-card">
799
+ <div class="kpi-top">
800
+ <div class="kpi-label">Clicks</div>
801
+ <div class="kpi-status">[TRAFFIC]</div>
802
+ </div>
803
+ <div class="kpi-value">{format_number(clicks)}</div>
804
+ <div class="kpi-note note-brown">Campaign traffic</div>
805
+ </div>
806
+ <div class="kpi-card">
807
+ <div class="kpi-top">
808
+ <div class="kpi-label">CPL</div>
809
+ <div class="kpi-status">[EFFICIENCY]</div>
810
+ </div>
811
+ <div class="kpi-value">{format_money(cpl)}</div>
812
+ <div class="kpi-note note-olive">{format_number(conversions)} leads recorded</div>
813
+ </div>
814
+ <div class="kpi-card">
815
+ <div class="kpi-top">
816
+ <div class="kpi-label">CTR</div>
817
+ <div class="kpi-status">[RESPONSE]</div>
818
+ </div>
819
+ <div class="kpi-value">{format_percent(ctr)}</div>
820
+ <div class="kpi-note note-violet">Click-through rate</div>
821
+ </div>
822
+ </div>
823
+ """
824
+
825
+
826
  SIGNAL_FLOW_HTML = """
827
  <div class="signal-card">
828
  <div class="signal-header">
 
925
 
926
  def campaign_selected(campaign_name, full_state):
927
  if not campaign_name:
928
+ return None, gr.update()
929
 
930
+ campaign_state = on_campaign_select(full_state, campaign_name)
931
+ return campaign_state, build_campaign_kpi_html(campaign_state.get("campaign_df"))
932
 
933
 
934
  # ==================================================
 
1013
  campaign_picker.change(
1014
  fn=campaign_selected,
1015
  inputs=[campaign_picker, full_state],
1016
+ outputs=[campaign_state, kpi_html],
1017
  )
1018
 
1019
  ads_card.click(