shot chart and navbar
CI Health Check & Quality Gate / Code, dbt & Live App Health Check (pull_request) Successful in 1m11s
CI Health Check & Quality Gate / Code, dbt & Live App Health Check (pull_request) Successful in 1m11s
This commit is contained in:
1 parent
e7c8e10071
commit
11a5cc0f18
15 files changed
+2277
-1454
No files matched your search
@@ -0,0 +1,201 @@
|
||||
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")
|
||||
Reference in new issue
Block a user