Files
nprasad2077 11a5cc0f18
CI Health Check & Quality Gate / Code, dbt & Live App Health Check (pull_request) Successful in 1m11s
shot chart and navbar
2026-10-28 02:37:56 -05:00

156 lines
5.2 KiB
Python

import plotly.express as px
import streamlit as st
from utils.charts import AMBER, MADE_EMERALD, MISS_CRIMSON, apply_arena_theme
from utils.db import get_selected_season, query
SEASON, SEASON_LABEL = get_selected_season()
st.title(f":material/timer: Momentum — {SEASON_LABEL}")
tab_overview, tab_teams, tab_comebacks = st.tabs(
[
":material/bar_chart: Quarter Scoring",
":material/emoji_events: Team by Quarter",
":material/replay: Comebacks",
]
)
QUARTER_COLORS = {
"Q1": "#38BDF8",
"Q2": MADE_EMERALD,
"Q3": AMBER,
"Q4": MISS_CRIMSON,
}
with tab_overview:
quarter_avg = query(
"""
SELECT q.period,
round(avg(q.points_scored), 1) AS avg_pts,
round(avg(abs(q.period_point_differential)), 1) AS avg_abs_diff,
round(stddev(q.points_scored), 1) AS scoring_volatility
FROM main_marts.fct_quarter_scoring q
JOIN main_marts.dim_seasons s ON q.season_key = s.season_key
WHERE s.season_start_year = ? AND q.period IN ('Q1','Q2','Q3','Q4')
GROUP BY q.period ORDER BY q.period
""",
[SEASON],
)
col1, col2 = st.columns(2)
with col1:
fig = px.bar(
quarter_avg,
x="period",
y="avg_pts",
title="Avg Points per Quarter",
color="period",
color_discrete_map=QUARTER_COLORS,
labels={"period": "Quarter", "avg_pts": "Avg Points"},
)
fig.update_layout(showlegend=False)
apply_arena_theme(fig)
st.plotly_chart(fig, width="stretch")
with col2:
fig2 = px.bar(
quarter_avg,
x="period",
y="avg_abs_diff",
title="Avg Quarter Lopsidedness (|differential|)",
color="period",
color_discrete_map=QUARTER_COLORS,
labels={"period": "Quarter", "avg_abs_diff": "Avg |Differential|"},
)
fig2.update_layout(showlegend=False)
apply_arena_theme(fig2)
st.plotly_chart(fig2, width="stretch")
st.caption("Q4 has lower scoring and tighter margins — games tighten up in crunch time.")
with tab_teams:
quarter_sel = (
st.segmented_control(
"Quarter",
["Q1", "Q2", "Q3", "Q4"],
default="Q1",
)
or "Q1"
)
team_quarter = query(
"""
SELECT t.team_abbr AS team,
round(avg(q.points_scored), 1) AS avg_pts,
round(avg(q.period_point_differential), 2) AS avg_diff,
count(*) AS games
FROM main_marts.fct_quarter_scoring q
JOIN main_marts.dim_teams t ON q.team_key = t.team_key
JOIN main_marts.dim_seasons s ON q.season_key = s.season_key
WHERE s.season_start_year = ? AND q.period = ?
GROUP BY t.team_abbr HAVING count(*) >= 10
ORDER BY avg_diff DESC
""",
[SEASON, quarter_sel],
)
fig = px.bar(
team_quarter,
x="avg_pts",
y="team",
orientation="h",
color="avg_diff",
color_continuous_scale="RdYlGn",
title=f"{quarter_sel} Leaders — sorted by differential (color = +/-)",
)
fig.update_layout(yaxis=dict(autorange="reversed"))
apply_arena_theme(fig, height=600)
st.plotly_chart(fig, width="stretch")
with tab_comebacks:
comebacks = query(
"""
WITH q3_trailing AS (
SELECT q.game_id, q.team_key,
sum(q.period_point_differential) AS through_q3_diff
FROM main_marts.fct_quarter_scoring q
JOIN main_marts.dim_seasons s ON q.season_key = s.season_key
WHERE s.season_start_year = ? AND q.period IN ('Q1','Q2','Q3')
GROUP BY q.game_id, q.team_key
HAVING sum(q.period_point_differential) < -10
),
q4_results AS (
SELECT q.game_id, q.team_key, q.period_point_differential AS q4_diff
FROM main_marts.fct_quarter_scoring q
JOIN main_marts.dim_seasons s ON q.season_key = s.season_key
WHERE s.season_start_year = ? AND q.period = 'Q4'
)
SELECT t.team_abbr AS team,
count(*) AS trailing_10_after_q3,
sum(CASE WHEN c.through_q3_diff + r.q4_diff > 0 THEN 1 ELSE 0 END) AS comebacks,
round(100.0 * sum(CASE WHEN c.through_q3_diff + r.q4_diff > 0 THEN 1 ELSE 0 END) / count(*), 1) AS comeback_pct
FROM q3_trailing c
JOIN q4_results r ON c.game_id = r.game_id AND c.team_key = r.team_key
JOIN main_marts.dim_teams t ON c.team_key = t.team_key
GROUP BY t.team_abbr HAVING count(*) >= 3
ORDER BY comeback_pct DESC
""",
[SEASON, SEASON],
)
if not comebacks.empty:
fig = px.bar(
comebacks,
x="team",
y="comeback_pct",
color="comebacks",
color_continuous_scale="Greens",
title="Comeback Rate (Down 10+ After Q3)",
)
fig.update_layout(xaxis_title="", yaxis_title="Comeback %")
apply_arena_theme(fig)
st.plotly_chart(fig, width="stretch")
st.dataframe(comebacks, hide_index=True, width="stretch")
else:
st.info("No comeback data available for this season.")