Add a search box to /climate that filters to a flat, alphabetical list of matching cities (by city or country name), shown with their country; clearing it restores the collapsible directory. The index is built from the already-crawlable links, so it's a pure progressive enhancement (no SEO impact). Also sort cities alphabetically within each country in the directory (were population-ordered).
68 lines
2.2 KiB
Python
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, both the countries and the cities within each
|
|
ordered alphabetically — for the /climate hub's crawlable link graph + search."""
|
|
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["name"].lower())
|
|
return dict(sorted(groups.items(), key=lambda kv: kv[0].lower()))
|