thermograph/cities.py
Emi Griffith 0e16ad2a93 SEO: climate hub — collapse all countries by default, order alphabetically (#104)
Drop the open-by-default on the first three countries (every <details> starts
collapsed) and order the country sections alphabetically instead of by largest
city's population.
2026-07-16 03:22:47 +00:00

68 lines
2.2 KiB
Python

"""Access to the curated city set (backend/cities.json) that gets crawlable
climate pages. Loaded once, lazily; regenerate the JSON with gen_cities.py."""
import json
import os
_PATH = os.path.join(os.path.dirname(__file__), "cities.json")
_FLAVOR_PATH = os.path.join(os.path.dirname(__file__), "cities_flavor.json")
_CITIES: list[dict] | None = None
_BY_SLUG: dict[str, dict] | None = None
_FLAVOR: dict[str, dict] | None = None
def _load() -> list[dict]:
global _CITIES, _BY_SLUG
if _CITIES is None:
with open(_PATH, encoding="utf-8") as f:
_CITIES = json.load(f)
_BY_SLUG = {c["slug"]: c for c in _CITIES}
return _CITIES
def all_cities() -> list[dict]:
return _load()
def all_slugs() -> list[str]:
return [c["slug"] for c in _load()]
def get(slug: str) -> dict | None:
"""The city for a slug, or None (→ 404)."""
_load()
return _BY_SLUG.get(slug)
def flavor(slug: str) -> dict | None:
"""A city's descriptive blurb {extract, url, title} from cities_flavor.json, or
None when we have no confident match (the page renders fine without it)."""
global _FLAVOR
if _FLAVOR is None:
try:
with open(_FLAVOR_PATH, encoding="utf-8") as f:
_FLAVOR = json.load(f)
except (OSError, ValueError):
_FLAVOR = {}
return _FLAVOR.get(slug)
def display_name(city: dict) -> str:
"""Human label: 'Seattle, Washington, United States' (drops repeated admin1)."""
parts = [city["name"]]
if city.get("admin1") and city["admin1"] != city["name"]:
parts.append(city["admin1"])
if city.get("country"):
parts.append(city["country"])
return ", ".join(parts)
def by_country() -> dict[str, list[dict]]:
"""Cities grouped by country (population-descending within each), country keys
ordered alphabetically — for the /climate hub's crawlable link graph."""
groups: dict[str, list[dict]] = {}
for c in _load():
key = c.get("country") or c.get("country_code") or "Other"
groups.setdefault(key, []).append(c)
for v in groups.values():
v.sort(key=lambda x: -x["population"])
return dict(sorted(groups.items(), key=lambda kv: kv[0].lower()))