Files
personal-portfolio/portfolio_app/figures/summary_cards.py
lmiranda b3fb94c7cb feat: Add floating sidebar navigation and dark theme support
- Add floating pill-shaped sidebar with navigation icons
- Implement dark/light theme toggle with localStorage persistence
- Update all figure factories for transparent backgrounds
- Use carto-darkmatter map style for choropleths
- Add methodology link button to Toronto dashboard header
- Add back to dashboard button on methodology page
- Remove social links from home page (now in sidebar)
- Update CLAUDE.md to Sprint 7

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 11:53:13 -05:00

108 lines
3.2 KiB
Python

"""Summary card figure factories for KPI display."""
from typing import Any
import plotly.graph_objects as go
def create_metric_card_figure(
value: float | int | str,
title: str,
delta: float | None = None,
delta_suffix: str = "%",
prefix: str = "",
suffix: str = "",
format_spec: str = ",.0f",
positive_is_good: bool = True,
) -> go.Figure:
"""Create a KPI indicator figure.
Args:
value: The main metric value.
title: Card title.
delta: Optional change value (for delta indicator).
delta_suffix: Suffix for delta value (e.g., '%').
prefix: Prefix for main value (e.g., '$').
suffix: Suffix for main value.
format_spec: Python format specification for the value.
positive_is_good: Whether positive delta is good (green) or bad (red).
Returns:
Plotly Figure object.
"""
# Determine numeric value for indicator
if isinstance(value, int | float):
number_value: float | None = float(value)
else:
number_value = None
fig = go.Figure()
# Add indicator trace
indicator_config: dict[str, Any] = {
"mode": "number",
"value": number_value if number_value is not None else 0,
"title": {"text": title, "font": {"size": 14}},
"number": {
"font": {"size": 32},
"prefix": prefix,
"suffix": suffix,
"valueformat": format_spec,
},
}
# Add delta if provided
if delta is not None:
indicator_config["mode"] = "number+delta"
indicator_config["delta"] = {
"reference": number_value - delta if number_value else 0,
"relative": False,
"valueformat": ".1f",
"suffix": delta_suffix,
"increasing": {"color": "green" if positive_is_good else "red"},
"decreasing": {"color": "red" if positive_is_good else "green"},
}
fig.add_trace(go.Indicator(**indicator_config))
fig.update_layout(
height=120,
margin={"l": 20, "r": 20, "t": 40, "b": 20},
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
font={"family": "Inter, sans-serif", "color": "#c9c9c9"},
)
return fig
def create_summary_metrics(
metrics: dict[str, dict[str, Any]],
) -> list[go.Figure]:
"""Create multiple metric card figures.
Args:
metrics: Dictionary of metric configurations.
Key: metric name
Value: dict with 'value', 'title', 'delta' (optional), etc.
Returns:
List of Plotly Figure objects.
"""
figures = []
for metric_config in metrics.values():
fig = create_metric_card_figure(
value=metric_config.get("value", 0),
title=metric_config.get("title", ""),
delta=metric_config.get("delta"),
delta_suffix=metric_config.get("delta_suffix", "%"),
prefix=metric_config.get("prefix", ""),
suffix=metric_config.get("suffix", ""),
format_spec=metric_config.get("format_spec", ",.0f"),
positive_is_good=metric_config.get("positive_is_good", True),
)
figures.append(fig)
return figures