thermograph/templates/city.html.j2
Emi Griffith c3a11ee994 SEO: per-city Wikipedia blurbs + travel/compare CTA on city pages (#98)
Add unique editorial content so the programmatic pages don't read as templated:
- gen_flavor.py seeds backend/cities_flavor.json with a short descriptive blurb per
  city from Wikipedia's free REST summary API (no key), validating each match by
  comparing article coordinates to the city's lat/lon so the wrong 'Springfield'
  never attaches. Retries + modest concurrency; ~700/750 cities get a blurb, the
  rest render without one. Text is CC BY-SA, attributed with a 'via Wikipedia' link.
- City pages show the blurb under the intro and a travel callout that deep-links to
  the compare page with the city pre-loaded (/compare#loc=lat,lon) — the visitor
  just adds their own city. Month pages get the same seasonal 'visiting in {month}?'
  compare link. Both add unique per-page text and internal links.
- cities.py gains flavor(slug); tests cover the blurb + attribution + compare CTA.
2026-07-16 00:39:47 +00:00

87 lines
4.3 KiB
Django/Jinja
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html.j2" %}
{% block title %}{{ page_title }}{% endblock %}
{% block description %}{{ page_description }}{% endblock %}
{% block canonical_path %}{{ canonical_path }}{% endblock %}
{% block jsonld %}<script type="application/ld+json">{{ jsonld_str | safe }}</script>{% endblock %}
{% block content %}
<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>
<article class="climate-page">
<h1>{{ display }} climate</h1>
<p class="lede">Average temperatures, precipitation and all-time records for <b>{{ display }}</b>,
with every day graded against ~{{ n_years }} years of local climate history
({{ year_range[0] }}{{ year_range[1] }}). See where {% if warmest %}the warmest month is
<b>{{ warmest.name }}</b> ({{ warmest.high }} average high){% endif %}{% if coldest %} and the coldest
is <b>{{ coldest.name }}</b> ({{ coldest.low }} average low){% endif %}.</p>
{% if flavor %}
<p class="city-blurb">{{ flavor.extract }}{% if flavor.url %}
<a class="blurb-src" href="{{ flavor.url }}" rel="nofollow">— via Wikipedia</a>{% endif %}</p>
{% endif %}
{% if today %}
<section class="climate-today">
<h2>How today compares</h2>
<p class="muted">Latest recorded day: {{ today.date }} — each reading placed on {{ display }}'s
own ±7-day seasonal distribution.</p>
<div class="today-cards">
{% for c in today.cards %}
<div class="today-card" style="border-left-color: var(--{{ c.cls }})">
<span class="tc-label">{{ c.label }}</span>
<span class="tc-value">{{ c.value }}</span>
{% if c.percentile is not none %}<span class="tc-grade">{{ c.grade }} · {{ c.percentile }}th pct</span>
{% else %}<span class="tc-grade">{{ c.grade }}</span>{% endif %}
</div>
{% endfor %}
</div>
<p><a class="cta" href="{{ base }}/#{{ tool_hash }}">Open {{ name }} in the live weather grader →</a></p>
</section>
{% endif %}
<section class="climate-normals">
<h2>{{ name }} average temperatures by month</h2>
<p>Typical daily high and low, and average precipitation, for each month in {{ display }},
based on {{ year_range[0] }}{{ year_range[1] }} records.
{% if wettest %}The wettest month is <b>{{ wettest.name }}</b> ({{ wettest.precip }} on an average day).{% endif %}</p>
<div class="table-wrap">
<table class="normals-table">
<thead><tr><th>Month</th><th>Avg high</th><th>Avg low</th><th>Avg precip</th></tr></thead>
<tbody>
{% for m in months %}
<tr>
<td><a href="{{ base }}/climate/{{ city.slug }}/{{ m.slug }}">{{ m.name }}</a></td>
<td>{{ m.high }}</td><td>{{ m.low }}</td><td>{{ m.precip }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="city-travel">
<h2>Thinking of visiting {{ name }}?</h2>
<p>Trips live and die by the weather. See how {{ name }}'s comfort stacks up against where
you live — {{ name }} is pre-loaded, just add your city.</p>
<p><a class="cta" href="{{ compare_url }}">Compare {{ name }}'s comfort with your city →</a></p>
</section>
{% if records.tmax or records.tmin %}
<section class="climate-records">
<h2>Record extremes</h2>
<ul>
{% if records.tmax %}<li><b>Hottest day on record:</b> {{ records.tmax.max | round | int }}°F
({{ ((records.tmax.max - 32) * 5 / 9) | round | int }}°C) on {{ records.tmax.max_date }}</li>{% endif %}
{% if records.tmin %}<li><b>Coldest day on record:</b> {{ records.tmin.min | round | int }}°F
({{ ((records.tmin.min - 32) * 5 / 9) | round | int }}°C) on {{ records.tmin.min_date }}</li>{% endif %}
</ul>
<p><a href="{{ base }}/climate/{{ city.slug }}/records">All {{ name }} weather records →</a></p>
</section>
{% endif %}
<p class="climate-foot muted">Percentiles compare each day to {{ display }}'s own history, so grades are
relative to this location — a “Near Record” day here isn't the same temperature as elsewhere.
<a href="{{ base }}/legend">How the grades work →</a></p>
</article>
{% endblock %}