Dashboard: Stadium Night design system, 5-page Streamlit app, warehouse exporter
- src/export/warehouse.py: reads scattered pipeline artifacts → 6 Parquet files (players, fixtures, predictions, lineups, votes, model_metrics) - dashboard/warehouse.py: read-only cached Parquet loader - .streamlit/config.toml: dark theme base, server config - dashboard/viz/template.py: Plotly 'fantabeto_dark' template (single source of truth) - Semantic palette: pitch_green #00D084, gold #FFC94D, red #FF4D5E, sky #38BDF8 - Space Grotesk headers, Inter body, tabular numerals - All 10 chart colors banned from default palette - dashboard/viz/components.py: KPI cards, role chips, section headers, CSS injection - dashboard/viz/charts.py: 10 pure chart functions (df → Figure) - percentile radar, fixture heatmap, regression comparison, bonus donut - card risk gauge, budget waterfall, value scatter, error violins - dashboard/viz/pitch.py: SVG pitch component — dark turf gradient, player badges sized by FV, gold captain ring, bench strip, formation label - 5 pages: - 01_matchday: KPI sparklines, fixture heatmap, start/sit grid, bump chart - 02_players: search, radar, regression, bonus/malus, card risk, news feed - 03_auction: budget slider, waterfall, value scatter, grid auction heatmap - 04_lineup: SVG pitch, what-if toggles, opponent mirror, MCTS captain - 05_lab: error violins, feature importance, calibration curve, backtest - dashboard/app.py: multi-page Streamlit entry with sidebar navigation - dashboard/tests/test_dashboard.py: 18 unit tests (warehouse, template, charts, pitch) - DASHBOARD.md: full architecture docs, design system reference - 49 total tests passing (31 existing + 18 dashboard)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
"""Read-only warehouse layer. Dashboard reads ONLY from data/warehouse/.
|
||||
Cached with @st.cache_data. No imports from ML code.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
WAREHOUSE = ROOT / "data" / "warehouse"
|
||||
|
||||
|
||||
def _cache_key():
|
||||
"""Bust cache when parquet files change."""
|
||||
files = sorted(WAREHOUSE.glob("*.parquet"))
|
||||
mtimes = tuple(f.stat().st_mtime for f in files)
|
||||
return (len(files), mtimes)
|
||||
|
||||
|
||||
def load_players() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "players.parquet")
|
||||
|
||||
|
||||
def load_fixtures() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "fixtures.parquet")
|
||||
|
||||
|
||||
def load_predictions() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "predictions.parquet")
|
||||
|
||||
|
||||
def load_lineups() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "lineups.parquet")
|
||||
|
||||
|
||||
def load_votes() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "votes.parquet")
|
||||
|
||||
|
||||
def load_model_metrics() -> pd.DataFrame:
|
||||
return pd.read_parquet(WAREHOUSE / "model_metrics.parquet")
|
||||
Reference in New Issue
Block a user