Syntrex Claude Sonnet 4.6 commited on
Commit
d3d0fef
·
1 Parent(s): b70ab2f

Debug page: lazy-load 7 unconditional DB queries behind session-state buttons

Browse files

Eliminates 7 uncached DB queries that fired on every page render:
- 5 audit expanders moved inside "Load Data Library" gate (session state persisted)
- Section 9 eval metrics gated behind new "Load Evaluation Metrics" button
- Both buttons use st.session_state so content survives Streamlit reruns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. visualization/debug_page.py +91 -79
visualization/debug_page.py CHANGED
@@ -641,7 +641,12 @@ def render_debug(
641
  # ------------------------------------------------------------------
642
  st.subheader("Data Inventory")
643
 
 
 
644
  if st.button("Load Data Library", key="dbg_load_inventory"):
 
 
 
645
  with st.expander("All database tables (row counts)", expanded=True):
646
  inv_df = _query_db_inventory(conn)
647
  if inv_df.empty:
@@ -672,7 +677,54 @@ def render_debug(
672
  elif overlap and "error" in overlap:
673
  st.warning(f"Overlap query error: {overlap['error']}")
674
 
675
- st.caption("Please allow a few moments to load the data library..")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
676
 
677
  # --- Simulator raw rows ---
678
  with st.expander("Simulator raw rows", expanded=False):
@@ -711,52 +763,6 @@ def render_debug(
711
  else:
712
  st.info("No prepared live games.")
713
 
714
- # --- Batter prop outcomes ---
715
- with st.expander("Batter prop outcomes", expanded=False):
716
- batter_prop_outcomes_df = read_batter_prop_outcomes(conn)
717
- st.write(f"Rows: {len(batter_prop_outcomes_df)}")
718
- if not batter_prop_outcomes_df.empty:
719
- display_cols = [c for c in [
720
- "created_at", "graded_at", "game_pk", "slot", "batter_name",
721
- "fair_hr_odds", "book_hr_odds", "adjusted_edge", "confidence",
722
- "recommendation_tier", "realized_hit", "realized_hr", "realized_tb2p",
723
- "grade_status", "outcome_source",
724
- ] if c in batter_prop_outcomes_df.columns]
725
- st.dataframe(batter_prop_outcomes_df[display_cols].tail(30), use_container_width=True, hide_index=True)
726
-
727
- # --- Game outcomes ---
728
- with st.expander("Game outcomes", expanded=False):
729
- game_outcomes_df = read_game_outcomes(conn)
730
- st.write(f"Rows: {len(game_outcomes_df)}")
731
- if not game_outcomes_df.empty:
732
- st.dataframe(game_outcomes_df.tail(20), use_container_width=True, hide_index=True)
733
-
734
- # --- Recommendation logs ---
735
- with st.expander("Recommendation logs", expanded=False):
736
- rec_logs_df = read_table(conn, "recommendation_logs")
737
- st.write(f"Rows: {len(rec_logs_df)}")
738
- if not rec_logs_df.empty:
739
- st.dataframe(rec_logs_df.tail(20), use_container_width=True, hide_index=True)
740
-
741
- # --- Recommendation audit ---
742
- with st.expander("Recommendation audit", expanded=False):
743
- audit_df = read_recommendation_audit_view(conn)
744
- st.write(f"Rows: {len(audit_df)}")
745
- if not audit_df.empty:
746
- audit_display_cols = [c for c in [
747
- "created_at", "game_pk", "away_team", "home_team", "slot", "batter_name",
748
- "fair_hr_odds", "book_hr_odds", "adjusted_edge", "confidence",
749
- "recommendation_tier", "realized_hr", "graded_at", "outcome_source",
750
- ] if c in audit_df.columns]
751
- st.dataframe(audit_df[audit_display_cols].tail(20), use_container_width=True, hide_index=True)
752
-
753
- # --- Batter prop audit ---
754
- with st.expander("Batter prop audit", expanded=False):
755
- batter_audit_df = read_batter_prop_audit_view(conn)
756
- st.write(f"Rows: {len(batter_audit_df)}")
757
- if not batter_audit_df.empty:
758
- st.dataframe(batter_audit_df.tail(20), use_container_width=True, hide_index=True)
759
-
760
  # ------------------------------------------------------------------
761
  # SECTION 7 — Export
762
  # ------------------------------------------------------------------
@@ -807,43 +813,49 @@ def render_debug(
807
  # ------------------------------------------------------------------
808
  st.subheader("Model Evaluation Metrics")
809
 
810
- try:
811
- audit_df = read_recommendation_audit_view(conn)
812
- except Exception:
813
- audit_df = pd.DataFrame()
814
-
815
- eval_tables = [
816
- ("HR Probability Calibration", build_hr_calibration_table(audit_df)),
817
- ("Edge Bucket Performance", build_edge_bucket_table(audit_df)),
818
- ("Confidence Bucket", build_confidence_table(audit_df)),
819
- ("Recommendation Tier", build_tier_performance_table(audit_df)),
820
- ("Global ERE", build_ere_table(audit_df)),
821
- ("ERE by Edge Bucket", build_ere_by_edge_bucket_table(audit_df)),
822
- ("ERE by Confidence", build_ere_by_confidence_bucket_table(audit_df)),
823
- ("ERE by Tier", build_ere_by_tier_table(audit_df)),
824
- ("CLV Summary", build_clv_table(audit_df)),
825
- ("CLV by Tier", build_clv_by_tier_table(audit_df)),
826
- ]
827
 
828
- for title, tbl in eval_tables:
829
- if not tbl.empty:
830
- st.write(title)
831
- st.dataframe(tbl, use_container_width=True, hide_index=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
832
 
833
- # Batter-specific metrics
834
- try:
835
- batter_audit_df_eval = read_batter_prop_audit_view(conn)
836
- for title, fn in [
837
- ("Batter HR Rate by Tier", build_batter_hr_tier_table),
838
- ("Batter HR Rate by Confidence", build_batter_hr_confidence_table),
839
- ("Batter HR Rate by Edge", build_batter_hr_edge_table),
840
- ]:
841
- tbl = fn(batter_audit_df_eval)
842
  if not tbl.empty:
843
  st.write(title)
844
  st.dataframe(tbl, use_container_width=True, hide_index=True)
845
- except Exception:
846
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
847
 
848
  # Scores raw status (diagnostic)
849
  if not scores_df.empty and "status" in scores_df.columns:
 
641
  # ------------------------------------------------------------------
642
  st.subheader("Data Inventory")
643
 
644
+ if "dbg_lib_loaded" not in st.session_state:
645
+ st.session_state["dbg_lib_loaded"] = False
646
  if st.button("Load Data Library", key="dbg_load_inventory"):
647
+ st.session_state["dbg_lib_loaded"] = True
648
+
649
+ if st.session_state["dbg_lib_loaded"]:
650
  with st.expander("All database tables (row counts)", expanded=True):
651
  inv_df = _query_db_inventory(conn)
652
  if inv_df.empty:
 
677
  elif overlap and "error" in overlap:
678
  st.warning(f"Overlap query error: {overlap['error']}")
679
 
680
+ # --- Batter prop outcomes ---
681
+ with st.expander("Batter prop outcomes", expanded=False):
682
+ batter_prop_outcomes_df = read_batter_prop_outcomes(conn)
683
+ st.write(f"Rows: {len(batter_prop_outcomes_df)}")
684
+ if not batter_prop_outcomes_df.empty:
685
+ display_cols = [c for c in [
686
+ "created_at", "graded_at", "game_pk", "slot", "batter_name",
687
+ "fair_hr_odds", "book_hr_odds", "adjusted_edge", "confidence",
688
+ "recommendation_tier", "realized_hit", "realized_hr", "realized_tb2p",
689
+ "grade_status", "outcome_source",
690
+ ] if c in batter_prop_outcomes_df.columns]
691
+ st.dataframe(batter_prop_outcomes_df[display_cols].tail(30), use_container_width=True, hide_index=True)
692
+
693
+ # --- Game outcomes ---
694
+ with st.expander("Game outcomes", expanded=False):
695
+ game_outcomes_df = read_game_outcomes(conn)
696
+ st.write(f"Rows: {len(game_outcomes_df)}")
697
+ if not game_outcomes_df.empty:
698
+ st.dataframe(game_outcomes_df.tail(20), use_container_width=True, hide_index=True)
699
+
700
+ # --- Recommendation logs ---
701
+ with st.expander("Recommendation logs", expanded=False):
702
+ rec_logs_df = read_table(conn, "recommendation_logs")
703
+ st.write(f"Rows: {len(rec_logs_df)}")
704
+ if not rec_logs_df.empty:
705
+ st.dataframe(rec_logs_df.tail(20), use_container_width=True, hide_index=True)
706
+
707
+ # --- Recommendation audit ---
708
+ with st.expander("Recommendation audit", expanded=False):
709
+ audit_df = read_recommendation_audit_view(conn)
710
+ st.write(f"Rows: {len(audit_df)}")
711
+ if not audit_df.empty:
712
+ audit_display_cols = [c for c in [
713
+ "created_at", "game_pk", "away_team", "home_team", "slot", "batter_name",
714
+ "fair_hr_odds", "book_hr_odds", "adjusted_edge", "confidence",
715
+ "recommendation_tier", "realized_hr", "graded_at", "outcome_source",
716
+ ] if c in audit_df.columns]
717
+ st.dataframe(audit_df[audit_display_cols].tail(20), use_container_width=True, hide_index=True)
718
+
719
+ # --- Batter prop audit ---
720
+ with st.expander("Batter prop audit", expanded=False):
721
+ batter_audit_df = read_batter_prop_audit_view(conn)
722
+ st.write(f"Rows: {len(batter_audit_df)}")
723
+ if not batter_audit_df.empty:
724
+ st.dataframe(batter_audit_df.tail(20), use_container_width=True, hide_index=True)
725
+
726
+ else:
727
+ st.caption("Please allow a few moments to load the data library..")
728
 
729
  # --- Simulator raw rows ---
730
  with st.expander("Simulator raw rows", expanded=False):
 
763
  else:
764
  st.info("No prepared live games.")
765
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  # ------------------------------------------------------------------
767
  # SECTION 7 — Export
768
  # ------------------------------------------------------------------
 
813
  # ------------------------------------------------------------------
814
  st.subheader("Model Evaluation Metrics")
815
 
816
+ if "dbg_eval_metrics_loaded" not in st.session_state:
817
+ st.session_state["dbg_eval_metrics_loaded"] = False
818
+ if st.button("Load Evaluation Metrics", key="dbg_load_eval_metrics"):
819
+ st.session_state["dbg_eval_metrics_loaded"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
820
 
821
+ if st.session_state["dbg_eval_metrics_loaded"]:
822
+ try:
823
+ audit_df = read_recommendation_audit_view(conn)
824
+ except Exception:
825
+ audit_df = pd.DataFrame()
826
+
827
+ eval_tables = [
828
+ ("HR Probability Calibration", build_hr_calibration_table(audit_df)),
829
+ ("Edge Bucket Performance", build_edge_bucket_table(audit_df)),
830
+ ("Confidence Bucket", build_confidence_table(audit_df)),
831
+ ("Recommendation Tier", build_tier_performance_table(audit_df)),
832
+ ("Global ERE", build_ere_table(audit_df)),
833
+ ("ERE by Edge Bucket", build_ere_by_edge_bucket_table(audit_df)),
834
+ ("ERE by Confidence", build_ere_by_confidence_bucket_table(audit_df)),
835
+ ("ERE by Tier", build_ere_by_tier_table(audit_df)),
836
+ ("CLV Summary", build_clv_table(audit_df)),
837
+ ("CLV by Tier", build_clv_by_tier_table(audit_df)),
838
+ ]
839
 
840
+ for title, tbl in eval_tables:
 
 
 
 
 
 
 
 
841
  if not tbl.empty:
842
  st.write(title)
843
  st.dataframe(tbl, use_container_width=True, hide_index=True)
844
+
845
+ # Batter-specific metrics
846
+ try:
847
+ batter_audit_df_eval = read_batter_prop_audit_view(conn)
848
+ for title, fn in [
849
+ ("Batter HR Rate by Tier", build_batter_hr_tier_table),
850
+ ("Batter HR Rate by Confidence", build_batter_hr_confidence_table),
851
+ ("Batter HR Rate by Edge", build_batter_hr_edge_table),
852
+ ]:
853
+ tbl = fn(batter_audit_df_eval)
854
+ if not tbl.empty:
855
+ st.write(title)
856
+ st.dataframe(tbl, use_container_width=True, hide_index=True)
857
+ except Exception:
858
+ pass
859
 
860
  # Scores raw status (diagnostic)
861
  if not scores_df.empty and "status" in scores_df.columns: