diff --git a/content.py b/content.py index 391a361..b010014 100644 --- a/content.py +++ b/content.py @@ -14,6 +14,7 @@ import polars as pl from fastapi import HTTPException, Request, Response from fastapi.responses import PlainTextResponse from jinja2 import Environment, FileSystemLoader, select_autoescape +from markupsafe import Markup import cities import city_events @@ -61,11 +62,22 @@ def _c(f: float) -> int: return round((f - 32) * 5 / 9) -def _temp(f) -> str: - """A Fahrenheit value shown in both units: '72°F (22°C)'.""" +def _temp(f): + """A Fahrenheit temperature as a client-convertible span, e.g. + '72°F'. Renders °F by default + (so crawlers and no-JS visitors see a real value); climate.js rewrites it to + the active unit on load and on toggle. Returns '—' for a missing value.""" if f is None: return "—" - return f"{round(f)}°F ({_c(f)}°C)" + return Markup('{}°F').format(f, round(f)) + + +def _temp_bare(f): + """Like _temp() but with no unit letter ('72°'), for the range strip where the + axis already implies the unit. Still carries data-temp-f so it converts.""" + if f is None: + return "—" + return Markup('{}°').format(f, round(f)) def _fmt(metric: str, v) -> str: @@ -119,6 +131,8 @@ def _range_bar(low_f, high_f) -> dict | None: _env.globals["temp_class"] = temp_class +_env.globals["temp"] = _temp +_env.globals["temp_bare"] = _temp_bare def _month_doy(month_idx: int) -> int: @@ -402,10 +416,10 @@ def _month_context(request, city, history, month_idx: int) -> dict: stats = [] if tmax: stats.append(("Average high", _temp(tmax["mean"]))) - stats.append(("Typical high range", f"{_temp(tmax['p10'])} to {_temp(tmax['p90'])}")) + stats.append(("Typical high range", _temp(tmax['p10']) + " to " + _temp(tmax['p90']))) if tmin: stats.append(("Average low", _temp(tmin["mean"]))) - stats.append(("Typical low range", f"{_temp(tmin['p10'])} to {_temp(tmin['p90'])}")) + stats.append(("Typical low range", _temp(tmin['p10']) + " to " + _temp(tmin['p90']))) if precip: stats.append(("Average daily precipitation", f"{precip['mean']:.2f} in")) # Record high and low for every metric in this calendar month (mirrors the diff --git a/templates/about.html.j2 b/templates/about.html.j2 index 3467de2..5d20af3 100644 --- a/templates/about.html.j2 +++ b/templates/about.html.j2 @@ -23,7 +23,7 @@ within ±7 days of that day-of-year — a 15-day seasonal window across all years. The observed value is placed on that distribution as an empirical percentile, then mapped to a grade from “Below Normal” through “High” to “Near Record”.
-Everything is relative to the location: a 60°F day can be “Above Normal” in one place and +
Everything is relative to the location: a {{ temp(60) }} day can be “Above Normal” in one place and “Below Normal” in another. Precipitation is graded only among days that actually rained, so “Heavy” means heavy for a rainy day here. See the grade guide for the full scale.
diff --git a/templates/base.html.j2 b/templates/base.html.j2 index b5125be..e7a5615 100644 --- a/templates/base.html.j2 +++ b/templates/base.html.j2 @@ -60,5 +60,10 @@Thermograph grades weather by its percentile against ~45 years of local climate history. Climate data via Open-Meteo (ERA5 reanalysis).
+ + + +