SEO: show both temperature extremes per metric in month/season records (#112)
The month and season records tables showed only the record high (of the daytime high) and the record low (of the overnight low) — one extreme each. Show both ends of both metrics: for the daytime high, its record hottest (▲) and the coldest a day ever stayed (▼, its record-low high); for the overnight low, its mildest night (▲, its record-high low) and its record low (▼). All four already came out of all_time_records; they were just being dropped. Restructure each table to Period · Daytime high · Overnight low, with each metric cell carrying two tinted, dated chips (▲ warmest / ▼ coldest). Fits mobile in three columns; the all-time cards already showed both extremes, so this brings the per-period tables in line.
This commit is contained in:
parent
b3e7f75e50
commit
da94b082c5
3 changed files with 50 additions and 22 deletions
26
content.py
26
content.py
|
|
@ -226,10 +226,22 @@ def _monthly_normals(history) -> list[dict]:
|
|||
return rows
|
||||
|
||||
|
||||
def _extreme(metric_rec, key: str) -> dict | None:
|
||||
"""One extreme of a metric — key 'max' (warmest) or 'min' (coldest) — as the
|
||||
two-unit value, its heat-map tier, and the date it occurred."""
|
||||
if not metric_rec:
|
||||
return None
|
||||
v = metric_rec[key]
|
||||
return {"txt": _temp(v), "f": v, "cls": temp_class(v), "date": metric_rec[f"{key}_date"]}
|
||||
|
||||
|
||||
def _period_records(history, months: list[int]) -> dict:
|
||||
"""Record high (tmax) and record low (tmin), with the dates they occurred, over a
|
||||
set of calendar months — the shared primitive behind the monthly and seasonal
|
||||
records tables. Reuses grading.all_time_records on the month-filtered archive."""
|
||||
"""For a set of calendar months, the record *warmest and coldest* of BOTH the
|
||||
daytime high (tmax) and the overnight low (tmin) — four extremes with dates.
|
||||
|
||||
So each metric shows both ends: the daytime high's hottest day and the coldest a
|
||||
day ever stayed (its record-low high), and the overnight low's mildest night and
|
||||
its record low. Reuses grading.all_time_records on the month-filtered archive."""
|
||||
if len(months) == 1:
|
||||
sub = history.filter(pl.col("date").dt.month() == months[0])
|
||||
else:
|
||||
|
|
@ -237,12 +249,8 @@ def _period_records(history, months: list[int]) -> dict:
|
|||
rec = grading.all_time_records(sub) if not sub.is_empty() else {}
|
||||
tmax, tmin = rec.get("tmax"), rec.get("tmin")
|
||||
return {
|
||||
"high": _temp(tmax["max"]) if tmax else "—",
|
||||
"high_f": tmax["max"] if tmax else None,
|
||||
"high_date": tmax["max_date"] if tmax else "—",
|
||||
"low": _temp(tmin["min"]) if tmin else "—",
|
||||
"low_f": tmin["min"] if tmin else None,
|
||||
"low_date": tmin["min_date"] if tmin else "—",
|
||||
"high": {"warm": _extreme(tmax, "max"), "cold": _extreme(tmax, "min")},
|
||||
"low": {"warm": _extreme(tmin, "max"), "cold": _extreme(tmin, "min")},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@
|
|||
{% block canonical_path %}{{ canonical_path }}{% endblock %}
|
||||
{% block jsonld %}<script type="application/ld+json">{{ jsonld_str | safe }}</script>{% endblock %}
|
||||
{% block content %}
|
||||
{# A metric's two extremes — ▲ warmest and ▼ coldest it has ever been — as tinted
|
||||
chips with the date each occurred. Used for the daytime-high and overnight-low
|
||||
columns of the month and season tables. #}
|
||||
{% macro extremes(metric) %}
|
||||
{% if metric and metric.warm and metric.cold %}
|
||||
<div class="rec-ext"><span class="rec-arrow up" aria-hidden="true">▲</span><span class="t-inline t-{{ metric.warm.cls }}">{{ metric.warm.txt }}</span><span class="rec-on">{{ metric.warm.date }}</span></div>
|
||||
<div class="rec-ext"><span class="rec-arrow down" aria-hidden="true">▼</span><span class="t-inline t-{{ metric.cold.cls }}">{{ metric.cold.txt }}</span><span class="rec-on">{{ metric.cold.date }}</span></div>
|
||||
{% else %}—{% endif %}
|
||||
{% endmacro %}
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||||
{% for label, href in breadcrumb %}{% if href %}<a href="{{ href }}">{{ label }}</a>{% else %}<span>{{ label }}</span>{% endif %}{% if not loop.last %} <span class="sep">›</span> {% endif %}{% endfor %}
|
||||
</nav>
|
||||
|
|
@ -20,17 +29,18 @@
|
|||
|
||||
<section class="climate-records-section">
|
||||
<h2>Records by month</h2>
|
||||
<p>The warmest and coldest {{ name }} has ever been in each calendar month. Tap a month for its full
|
||||
averages and typical range.</p>
|
||||
<p>The warmest (▲) and coldest (▼) both the daytime high and the overnight low have ever been in
|
||||
each calendar month — so each column shows its record high <i>and</i> its record low. Tap a
|
||||
month for its full averages and typical range.</p>
|
||||
<div class="table-wrap">
|
||||
<table class="normals-table records-table">
|
||||
<thead><tr><th>Month</th><th>Record high</th><th>Record low</th></tr></thead>
|
||||
<table class="normals-table records-table records-ext">
|
||||
<thead><tr><th>Month</th><th>Daytime high</th><th>Overnight low</th></tr></thead>
|
||||
<tbody>
|
||||
{% for m in monthly %}
|
||||
<tr>
|
||||
<td><a href="{{ base }}/climate/{{ city.slug }}/{{ m.slug }}">{{ m.name }}</a></td>
|
||||
<td class="t-cell t-{{ temp_class(m.high_f) }}">{{ m.high }}<span class="rec-on">{{ m.high_date }}</span></td>
|
||||
<td class="t-cell t-{{ temp_class(m.low_f) }}">{{ m.low }}<span class="rec-on">{{ m.low_date }}</span></td>
|
||||
<td>{{ extremes(m.high) }}</td>
|
||||
<td>{{ extremes(m.low) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
@ -40,17 +50,17 @@
|
|||
|
||||
<section class="climate-records-section">
|
||||
<h2>Records by season</h2>
|
||||
<p>Meteorological seasons for the {{ hemisphere }} Hemisphere — each a three-month span — and the
|
||||
most extreme {{ name }} has recorded within each.</p>
|
||||
<p>The warmest (▲) and coldest (▼) both the daytime high and the overnight low have reached in each
|
||||
meteorological season ({{ hemisphere }} Hemisphere — each a three-month span).</p>
|
||||
<div class="table-wrap">
|
||||
<table class="normals-table records-table">
|
||||
<thead><tr><th>Season</th><th>Record high</th><th>Record low</th></tr></thead>
|
||||
<table class="normals-table records-table records-ext">
|
||||
<thead><tr><th>Season</th><th>Daytime high</th><th>Overnight low</th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in seasonal %}
|
||||
<tr>
|
||||
<td>{{ s.name }} <span class="season-span">({{ s.span }})</span></td>
|
||||
<td class="t-cell t-{{ temp_class(s.high_f) }}">{{ s.high }}<span class="rec-on">{{ s.high_date }}</span></td>
|
||||
<td class="t-cell t-{{ temp_class(s.low_f) }}">{{ s.low }}<span class="rec-on">{{ s.low_date }}</span></td>
|
||||
<td>{{ extremes(s.high) }}</td>
|
||||
<td>{{ extremes(s.low) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -134,11 +134,21 @@ def test_climate_pages_are_colour_coded(client):
|
|||
assert "t-cell t-" in city # tinted temperature cells in the normals table
|
||||
assert "range-fill" in city and "--c1:var(--" in city # the monthly range strip's gradient bars
|
||||
recs = client.get(f"{B}/climate/{SLUG}/records").text
|
||||
assert "t-cell t-" in recs
|
||||
assert "t-inline t-" in recs # tinted record chips
|
||||
month = client.get(f"{B}/climate/{SLUG}/july").text
|
||||
assert "t-inline t-" in month # tinted hero numbers + records
|
||||
|
||||
|
||||
def test_records_show_both_extremes_per_metric(client):
|
||||
# Each metric column shows BOTH its record warmest (▲) and coldest (▼) — so the
|
||||
# daytime high carries its record-low high, and the overnight low its record-high low.
|
||||
b = client.get(f"{B}/climate/{SLUG}/records").text
|
||||
assert "Daytime high" in b and "Overnight low" in b
|
||||
assert "▲" in b and "▼" in b
|
||||
# 12 months × 2 metric columns × 2 extremes = 48 chips just in the monthly table.
|
||||
assert b.count('class="rec-ext"') >= 48
|
||||
|
||||
|
||||
def test_hub_glossary_about(client):
|
||||
assert client.get(f"{B}/climate").status_code == 200
|
||||
assert client.get(f"{B}/glossary").status_code == 200
|
||||
|
|
|
|||
Loading…
Reference in a new issue