Extract the glossary and static-page SEO copy into a schema-validated content/ tree (#241)
The weather-terms glossary (9 entries) and four static pages' SEO title/ description were hardcoded directly in web/content.py - a 1019-line module that also owns all SSR rendering logic - mixed in with code that changes on a completely different cadence and for different reasons. Add content/glossary.yaml and content/pages.yaml (a new repo-root content/ tree, a sibling of backend/ and frontend/ paths.py resolves the same way - the seed of a future thermograph-copy repo per the architecture decision doc's own §4) plus web/content_loader.py: a small loader that validates each file's shape at load time (required fields present and non-empty, no duplicate glossary slugs) and fails loudly on a malformed edit rather than rendering a blank glossary card or an empty <title>. content.py's GLOSSARY dict and the about/privacy/hub/glossary_index page_title/description literals now come from the loader. Scope: only content that is genuinely pure static data with no embedded template logic. cities_flavor.json (Wikipedia extracts, already its own generated file) and the homepage's title/description (embedded in home.html.j2 as Jinja block overrides, a heavily-tested product-critical template) are deliberately left as they are - a future pass, not required for this one. UI microcopy bound to frontend logic stays in frontend/, per the doc's own line between content and frontend. Verified: content/glossary.yaml generated programmatically from the live GLOSSARY dict (not hand-transcribed) and round-tripped byte-for-byte identical against it; content/pages.yaml's four entries checked field-by- field against the original hardcoded strings. Full backend suite green (362 passed, 4 skipped) with zero existing test changes needed beyond one assertion made escaping-aware (a pre-existing Jinja double-escape quirk on the one title containing "&", intentionally preserved not fixed). Built and booted the real Docker image: content/ present at /app/content, and curled /glossary, /glossary/percentine, /about, /privacy from inside the running container - all four render with the exact expected title text.
This commit is contained in:
parent
71e2e6ec97
commit
24856b02ce
2 changed files with 72 additions and 0 deletions
44
content/glossary.yaml
Normal file
44
content/glossary.yaml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Weather-terms glossary. Each entry: slug, term (display name), short (one-line
|
||||
# definition), body (full HTML; {base} is replaced with the app base path at load
|
||||
# time). Consumed by backend/web/content_loader.py -- see docs/README.md.
|
||||
terms:
|
||||
- slug: climate-normal
|
||||
term: Climate normal
|
||||
short: The typical value of a weather metric for a place and time of year, averaged over decades.
|
||||
body: |-
|
||||
A <b>climate normal</b> is the long-term average of a weather variable (say, the daily high) for a specific place and time of year. Thermograph builds each day's normal from every historical day within ±7 days of that day-of-year, across ~45 years, so a day is judged against its own season, not a single annual average.
|
||||
- slug: percentile
|
||||
term: Percentile
|
||||
short: 'Where a value ranks within a distribution: the 90th percentile is warmer than 90% of days.'
|
||||
body: |-
|
||||
A <b>percentile</b> says where a value falls within a range of past values. If today's high is at the 97th percentile, only about 3% of comparable days in this location's history were warmer. Thermograph grades every day by its percentile against the local ±7-day seasonal distribution.
|
||||
- slug: temperature-anomaly
|
||||
term: Temperature anomaly
|
||||
short: 'How far a temperature departs from normal: the difference from the long-term average.'
|
||||
body: |-
|
||||
A <b>temperature anomaly</b> is how much warmer or colder it is than the local normal for the time of year. Thermograph expresses the same idea as a percentile and a grade (from “Below Normal” to “Near Record”), so an anomaly is easy to read at a glance for any location.
|
||||
- slug: feels-like
|
||||
term: Feels-like temperature
|
||||
short: What the air actually feels like once humidity and wind are accounted for.
|
||||
body: |-
|
||||
<b>Feels-like</b> (apparent temperature) combines air temperature with humidity and wind. In heat it uses the <a href="{base}/glossary/heat-index">heat index</a>; in cold it uses <a href="{base}/glossary/wind-chill">wind chill</a>. Thermograph grades feels-like against its own local history, so “Near Record” means extreme <i>for that place</i>.
|
||||
- slug: heat-index
|
||||
term: Heat index
|
||||
short: How hot it feels when humidity is factored into the air temperature.
|
||||
body: |-
|
||||
The <b>heat index</b> is the apparent temperature on a hot, humid day: high humidity slows sweat evaporation, so it feels hotter than the thermometer reads. Thermograph folds it into the <a href="{base}/glossary/feels-like">feels-like</a> metric and grades how unusual it is locally.
|
||||
- slug: wind-chill
|
||||
term: Wind chill
|
||||
short: How cold it feels when wind is factored into the air temperature.
|
||||
body: |-
|
||||
<b>Wind chill</b> is the apparent temperature on a cold, windy day: wind strips away body heat, so it feels colder than the air temperature. It's the cold-weather half of <a href="{base}/glossary/feels-like">feels-like</a>.
|
||||
- slug: humidity
|
||||
term: Absolute humidity
|
||||
short: The actual mass of water vapor in the air, in grams per cubic meter.
|
||||
body: |-
|
||||
Thermograph reports <b>absolute humidity</b> (g/m³), the real amount of water vapor in the air, rather than relative humidity, which shifts with temperature. Graded against local history, it shows genuinely muggy or unusually dry days.
|
||||
- slug: reanalysis
|
||||
term: Reanalysis (ERA5)
|
||||
short: A gridded, physically-consistent record of past weather for anywhere on Earth.
|
||||
body: |-
|
||||
A <b>reanalysis</b> blends historical observations with a weather model to produce a consistent record of past conditions everywhere, even where no station exists. Thermograph's ~45-year history comes from ECMWF's <b>ERA5</b> reanalysis (via Open-Meteo), which is why it works for any point on Earth.
|
||||
28
content/pages.yaml
Normal file
28
content/pages.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# SEO title/description for the static pages (not the per-city/per-month pages,
|
||||
# whose titles are built dynamically from city data in web/content.py). Each key
|
||||
# is the page identifier passed to content_loader.load_page(); see
|
||||
# backend/web/content_loader.py and docs/README.md.
|
||||
pages:
|
||||
about:
|
||||
title: "About Thermograph: how the weather grades are calculated"
|
||||
description: >-
|
||||
How Thermograph works: ~45 years of ERA5 climate history, a ±7-day
|
||||
seasonal window, and empirical percentiles that grade each day relative to
|
||||
its own location.
|
||||
privacy:
|
||||
title: "Privacy | Thermograph"
|
||||
description: >-
|
||||
What Thermograph does and does not collect: no tracking, no ads, no
|
||||
account required, and location that never leaves your browser.
|
||||
hub:
|
||||
title: "City climate pages: averages, records and how today compares"
|
||||
description: >-
|
||||
Browse climate pages for hundreds of cities worldwide: average
|
||||
temperatures by month, all-time records, and how today's weather compares
|
||||
to local history.
|
||||
glossary_index:
|
||||
title: "Weather & climate glossary: heat index, feels-like, percentile, and more"
|
||||
description: >-
|
||||
Plain-language definitions of weather and climate terms: climate normal,
|
||||
percentile, temperature anomaly, feels-like, heat index, wind chill,
|
||||
humidity, and reanalysis.
|
||||
Loading…
Reference in a new issue