CI Health Check & Quality Gate / Code, dbt & Live App Health Check (pull_request) Successful in 1m11s
202 lines
6.9 KiB
Python
202 lines
6.9 KiB
Python
import plotly.express as px
|
|
import streamlit as st
|
|
from streamlit_echarts import st_echarts
|
|
|
|
from utils.charts import AMBER, apply_arena_theme
|
|
from utils.db import get_selected_season, query
|
|
|
|
SEASON, SEASON_LABEL = get_selected_season()
|
|
|
|
st.title(f":material/dashboard: League Pulse — {SEASON_LABEL}")
|
|
|
|
# --- KPIs with delta vs prior season ---
|
|
summary = query(
|
|
"""
|
|
SELECT count(*) AS total_games,
|
|
sum(CASE WHEN is_overtime THEN 1 ELSE 0 END) AS ot_games,
|
|
round(avg(total_points), 1) AS avg_pts,
|
|
round(avg(point_differential), 1) AS avg_margin,
|
|
round(100.0 * sum(CASE WHEN is_home_team_winner THEN 1 ELSE 0 END) / count(*), 1) AS home_win_pct,
|
|
min(game_date) AS first_game,
|
|
max(game_date) AS last_game
|
|
FROM main_marts.fct_game_results
|
|
WHERE season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
""",
|
|
[SEASON],
|
|
)
|
|
|
|
prior = query(
|
|
"""
|
|
SELECT round(avg(total_points), 1) AS avg_pts,
|
|
round(avg(point_differential), 1) AS avg_margin,
|
|
round(100.0 * sum(CASE WHEN is_home_team_winner THEN 1 ELSE 0 END) / count(*), 1) AS home_win_pct
|
|
FROM main_marts.fct_game_results
|
|
WHERE season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
""",
|
|
[SEASON - 1],
|
|
)
|
|
|
|
has_prior = not prior.empty and prior["avg_pts"][0] is not None
|
|
c1, c2, c3, c4, c5 = st.columns(5)
|
|
c1.metric("Total Games", f"{summary['total_games'][0]:,}")
|
|
c2.metric(
|
|
"Avg Points/Game",
|
|
summary["avg_pts"][0],
|
|
delta=f"{summary['avg_pts'][0] - prior['avg_pts'][0]:+.1f}" if has_prior else None,
|
|
)
|
|
c3.metric(
|
|
"Avg Margin",
|
|
summary["avg_margin"][0],
|
|
delta=f"{summary['avg_margin'][0] - prior['avg_margin'][0]:+.1f}" if has_prior else None,
|
|
)
|
|
c4.metric(
|
|
"Home Win %",
|
|
f"{summary['home_win_pct'][0]}%",
|
|
delta=f"{summary['home_win_pct'][0] - prior['home_win_pct'][0]:+.1f}%" if has_prior else None,
|
|
)
|
|
c5.metric("OT Games", f"{summary['ot_games'][0]:,}")
|
|
|
|
st.caption(f"Season: {summary['first_game'][0]} → {summary['last_game'][0]} | Deltas vs prior season")
|
|
|
|
# --- Row 2: Competitiveness donut + Top standings ---
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
comp = query(
|
|
"""
|
|
SELECT game_competitiveness_tier AS tier, count(*) AS games
|
|
FROM main_marts.fct_game_results
|
|
WHERE season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
GROUP BY tier ORDER BY games DESC
|
|
""",
|
|
[SEASON],
|
|
)
|
|
fig = px.pie(
|
|
comp,
|
|
names="tier",
|
|
values="games",
|
|
title="Game Competitiveness",
|
|
hole=0.4,
|
|
color_discrete_sequence=px.colors.qualitative.Set2,
|
|
)
|
|
fig.update_traces(textinfo="percent+label")
|
|
apply_arena_theme(fig)
|
|
st.plotly_chart(fig, width="stretch")
|
|
|
|
with col2:
|
|
standings = query(
|
|
"""
|
|
SELECT team, count(*) AS games,
|
|
sum(CASE WHEN game_result = 'W' THEN 1 ELSE 0 END) AS wins,
|
|
sum(CASE WHEN game_result = 'L' THEN 1 ELSE 0 END) AS losses,
|
|
round(100.0 * sum(CASE WHEN game_result = 'W' THEN 1 ELSE 0 END) / count(*), 1) AS win_pct,
|
|
round(avg(net_rating), 1) AS net_rtg
|
|
FROM main_intermediate.int_team_performance
|
|
WHERE season_start_year = ?
|
|
GROUP BY team ORDER BY win_pct DESC LIMIT 8
|
|
""",
|
|
[SEASON],
|
|
)
|
|
st.subheader(":material/emoji_events: Top Teams")
|
|
st.dataframe(
|
|
standings,
|
|
column_config={
|
|
"win_pct": st.column_config.ProgressColumn(
|
|
"Win%", min_value=0, max_value=100, format="%.1f%%"
|
|
),
|
|
},
|
|
hide_index=True,
|
|
width="stretch",
|
|
)
|
|
|
|
# --- Row 3: Weekly scoring trend ---
|
|
weekly = query(
|
|
"""
|
|
SELECT date_trunc('week', game_date)::date::varchar AS week,
|
|
count(*) AS games,
|
|
round(avg(total_points), 1) AS avg_pts,
|
|
round(avg(point_differential), 1) AS avg_margin
|
|
FROM main_marts.fct_game_results
|
|
WHERE season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
GROUP BY week ORDER BY week
|
|
""",
|
|
[SEASON],
|
|
)
|
|
fig_weekly = px.area(
|
|
weekly,
|
|
x="week",
|
|
y="avg_pts",
|
|
title="Weekly Scoring Trend",
|
|
color_discrete_sequence=[AMBER],
|
|
)
|
|
fig_weekly.update_layout(xaxis_title="Week", yaxis_title="Avg Total Points")
|
|
apply_arena_theme(fig_weekly, height=350)
|
|
st.plotly_chart(fig_weekly, width="stretch")
|
|
|
|
# --- Row 4: Calendar heatmap + Season highlights ---
|
|
col1, col2 = st.columns([2, 1])
|
|
|
|
with col1:
|
|
cal_data = query(
|
|
"""
|
|
SELECT game_date::varchar AS day, count(*) AS games
|
|
FROM main_marts.fct_game_results
|
|
WHERE season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
GROUP BY game_date ORDER BY game_date
|
|
""",
|
|
[SEASON],
|
|
)
|
|
if not cal_data.empty:
|
|
cal_list = cal_data.values.tolist()
|
|
cal_option = {
|
|
"tooltip": {"position": "top", "formatter": "{c0} games on {b0}"},
|
|
"visualMap": {
|
|
"min": 0,
|
|
"max": int(cal_data["games"].max()),
|
|
"calculable": True,
|
|
"orient": "horizontal",
|
|
"left": "center",
|
|
"top": "top",
|
|
"inRange": {"color": ["#151D2A", "#9be9a8", "#40c463", "#30a14e", "#10B981"]},
|
|
},
|
|
"calendar": {
|
|
"range": [cal_list[0][0][:7], cal_list[-1][0][:7]],
|
|
"cellSize": ["auto", 15],
|
|
},
|
|
"series": [{"type": "heatmap", "coordinateSystem": "calendar", "data": cal_list}],
|
|
}
|
|
st_echarts(options=cal_option, height="200px")
|
|
|
|
with col2:
|
|
st.subheader(":material/local_fire_department: Top Performances")
|
|
highlights = query(
|
|
"""
|
|
SELECT player_name AS player, team,
|
|
game_date::date::varchar AS date, points AS pts,
|
|
assists AS ast, total_rebounds AS reb
|
|
FROM main_intermediate.int_player_performance
|
|
WHERE season_start_year = ?
|
|
ORDER BY points DESC LIMIT 5
|
|
""",
|
|
[SEASON],
|
|
)
|
|
st.dataframe(highlights, hide_index=True, width="stretch")
|
|
|
|
# --- Row 5: Recent games ---
|
|
st.subheader("Recent Games")
|
|
games = query(
|
|
"""
|
|
SELECT g.game_date, ht.team_abbr AS home, vt.team_abbr AS away,
|
|
g.home_points, g.visitor_points, wt.team_abbr AS winner,
|
|
g.point_differential AS margin, g.game_competitiveness_tier AS type
|
|
FROM main_marts.fct_game_results g
|
|
LEFT JOIN main_marts.dim_teams ht ON g.home_team_key = ht.team_key
|
|
LEFT JOIN main_marts.dim_teams vt ON g.visitor_team_key = vt.team_key
|
|
LEFT JOIN main_marts.dim_teams wt ON g.winning_team_key = wt.team_key
|
|
WHERE g.season_key IN (SELECT season_key FROM main_marts.dim_seasons WHERE season_start_year = ?)
|
|
ORDER BY g.game_date DESC LIMIT 25
|
|
""",
|
|
[SEASON],
|
|
)
|
|
st.dataframe(games, hide_index=True, width="stretch")
|