open-model-evolution / helpers.py
emsesc's picture
cleanup app.py into modularized components
927a4de
Raw
History Blame Contribute Delete
2.85 kB
import pandas as pd
from dash import html
def ordinal(n: int) -> str:
"""Return the ordinal suffix for a day (e.g., 1 -> 1st)."""
if 10 <= n % 100 <= 20:
suffix = "th"
else:
suffix = {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
return f"{n}{suffix}"
def format_date(dt: pd.Timestamp) -> str:
"""Format a pandas Timestamp into a readable string."""
return dt.strftime("%b") + f" {ordinal(dt.day)}, {dt.year}"
def build_slider_marks(start_dt: pd.Timestamp, end_dt: pd.Timestamp):
"""Create slider marks for the range and all-time sliders."""
return [
{"value": int(start_dt.timestamp()), "label": start_dt.strftime("%b %Y")},
{"value": int(end_dt.timestamp()), "label": end_dt.strftime("%b %Y")},
]
def get_thumb_labels(values):
"""Generate thumb labels for the range slider."""
distance = abs(values[1] - values[0])
close = distance < 4 * 30 * 86400 # 4 months
label_style = {
"background": "#fff",
"color": "#082030",
"fontWeight": "bold",
"fontSize": "13px",
"borderRadius": "8px",
"padding": "2px 8px",
"boxShadow": "0 1px 4px rgba(8,32,48,0.10)",
"position": "absolute",
"left": "50%",
"transform": "translateX(-50%)",
"whiteSpace": "nowrap",
"zIndex": 100,
}
if close:
style_top_1 = label_style.copy()
style_top_1["top"] = "-38px"
style_top_2 = label_style.copy()
style_top_2["top"] = "14px"
else:
style_top_1 = label_style.copy()
style_top_1["top"] = "14px"
style_top_2 = label_style.copy()
style_top_2["top"] = "14px"
return [
html.Div(pd.to_datetime(values[0], unit="s").strftime("%b %d, %Y"), style=style_top_1),
html.Div(pd.to_datetime(values[1], unit="s").strftime("%b %d, %Y"), style=style_top_2),
]
def get_thumb_label_single(value):
"""Generate thumb label for the all-time slider."""
label_style = {
"background": "#fff",
"color": "#082030",
"fontWeight": "bold",
"fontSize": "13px",
"borderRadius": "8px",
"padding": "2px 8px",
"boxShadow": "0 1px 4px rgba(8,32,48,0.10)",
"position": "absolute",
"left": "50%",
"transform": "translateX(-50%)",
"whiteSpace": "nowrap",
"zIndex": 100,
"top": "14px",
}
return [html.Div(pd.to_datetime(value, unit="s").strftime("%b %d, %Y"), style=label_style)]
def format_large_number(n: int) -> str:
"""Shorten large numbers, e.g. 5,000,000 -> '5 million'."""
if n >= 1_000_000_000:
return f"{n / 1_000_000_000:.1f} billion"
if n >= 1_000_000:
return f"{n / 1_000_000:.1f} million"
if n >= 1_000:
return f"{n / 1_000:.1f}k"
return str(int(n))