Files
fantabeto/dashboard/viz/template.py
T
ramseshk 65f5b66b05 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)
2026-08-11 14:53:51 +08:00

127 lines
3.4 KiB
Python

"""Plotly template "fantabeto_dark" — single source of truth for all chart styling.
Registers once at module import. Every chart uses this template.
"""
import plotly.graph_objects as go
import plotly.io as pio
# ─── Semantic palette ───────────────────────────────────────────────
PITCH_GREEN = "#00D084"
GOLD = "#FFC94D"
RED = "#FF4D5E"
SKY = "#38BDF8"
VIOLET = "#A78BFA"
BG = "#0B0F17"
CARD_BG = "#111827"
BORDER = "#1F2937"
TEXT = "#E5E7EB"
TEXT_SECONDARY = "#9CA3AF"
GRIDLINE = "rgba(31,41,55,0.8)"
WHITE = "#FFFFFF"
# Role colors
ROLE_COLORS = {"P": "#e74c3c", "D": "#3498db", "C": "#2ecc71", "A": "#f39c12"}
ROLE_ICONS = {"P": "🧤", "D": "🛡", "C": "⚙", "A": "⚡"}
# Sequential palette for heatmaps
HEATMAP_COLORS = [
[0.0, "#0B0F17"],
[0.2, "#1F2937"],
[0.4, "#38BDF8"],
[0.6, "#00D084"],
[0.8, "#FFC94D"],
[1.0, "#FF4D5E"],
]
DISCRETE_10 = [
SKY, PITCH_GREEN, GOLD, VIOLET, RED,
"#FB923C", "#34D399", "#60A5FA", "#C084FC", "#F87171",
]
FANTABETO_TEMPLATE = "fantabeto_dark"
def register_template():
"""Register the custom Plotly template. Call once at app startup."""
if FANTABETO_TEMPLATE in pio.templates:
return
t = go.layout.Template()
t.layout.update(
# Canvas
paper_bgcolor=BG,
plot_bgcolor=BG,
font=dict(color=TEXT, family="Inter, sans-serif", size=12),
title=dict(font=dict(family="Space Grotesk, sans-serif", size=18, color=WHITE)),
# Axes
xaxis=dict(
gridcolor=GRIDLINE, zerolinecolor=GRIDLINE,
linecolor=BORDER, tickcolor=BORDER,
title_font=dict(color=TEXT_SECONDARY, size=11),
),
yaxis=dict(
gridcolor=GRIDLINE, zerolinecolor=GRIDLINE,
linecolor=BORDER, tickcolor=BORDER,
title_font=dict(color=TEXT_SECONDARY, size=11),
),
# Legend
legend=dict(
bgcolor="rgba(17,24,39,0.85)", bordercolor=BORDER,
font=dict(color=TEXT_SECONDARY, size=11),
),
# Margins
margin=dict(l=50, r=30, t=60, b=50),
# Hover
hoverlabel=dict(
bgcolor=CARD_BG, bordercolor=BORDER,
font=dict(color=TEXT, family="Inter, sans-serif"),
),
# Annotations default
annotationdefaults=dict(
font=dict(color=TEXT, size=11, family="Inter, sans-serif"),
),
# Colorway
colorway=DISCRETE_10,
)
# Bar trace defaults
t.data.bar = [
go.Bar(marker=dict(line=dict(width=0)), textposition="none"),
]
# Scatter trace defaults
t.data.scatter = [
go.Scatter(
marker=dict(line=dict(width=0)),
line=dict(width=2),
),
]
# Heatmap defaults
t.data.heatmap = [
go.Heatmap(
colorscale=HEATMAP_COLORS,
colorbar=dict(
bgcolor=CARD_BG, bordercolor=BORDER,
tickfont=dict(color=TEXT_SECONDARY),
),
),
]
pio.templates[FANTABETO_TEMPLATE] = t
def insight_caption(text: str) -> str:
"""Generate markdown insight caption for charts."""
return f'<p style="color:{TEXT_SECONDARY};font-size:12px;margin-top:4px;font-style:italic;">{text}</p>'
def stub_render(): # for test import
return True
# Auto-register on import
register_template()