Fix /place 500s, /day rate-limit handling, calendar listener leak, error surfacing (#40)

- /api/v2/place still called grid.in_north_america(), which the worldwide-
  coverage change removed — every request raised AttributeError (500). The
  failure was invisible because compare.js treats the call as best-effort,
  so the instant chip-naming feature was silently dead. Drop the dead guard;
  the lat/lon Query validators already bound the inputs.
- /api/v2/day was the only data route not wrapping get_history in
  _weather_fetch_error, so a rate-limited cold cell returned a raw 500
  instead of the clean 503 the other routes emit.
- calendar: the #calendar pointerleave handler was re-registered inside
  attachHover on every render — and the comfort slider re-renders per input
  tick, stacking dozens of copies. Register it once at module scope next to
  the matching document-level dismiss handler.
- getJSON: check res.ok before parsing the body as JSON and fall back to the
  status line, so a non-JSON error body (a proxy 502 HTML page) reads as
  "Request failed (502 Bad Gateway)" instead of a JSON parse error.
This commit is contained in:
Emi Griffith 2026-07-11 12:26:42 -07:00 committed by GitHub
parent 09e96a2eaf
commit 686ac7afaf

5
app.py
View file

@ -419,8 +419,6 @@ def api_place(
endpoints expose as ``place`` (snapped to the grid cell, reverse-geocoded and
cached). Resolved on its own so the compare page can show a location's name as
soon as it's added, before its full series loads."""
if not grid.in_north_america(lat, lon):
return {"place": None}
cell = grid.snap(lat, lon)
with audit.RunAudit(endpoint="place", lat=round(lat, 4), lon=round(lon, 4),
cell_id=cell["id"]) as run:
@ -567,8 +565,11 @@ def api_day(
lon=round(lon, 4),
cell_id=cell["id"],
) as run:
try:
with run.phase("history"):
history, cache_meta = climate.get_history(cell)
except Exception as e: # noqa: BLE001
raise _weather_fetch_error(e)
if history.empty:
raise HTTPException(status_code=404, detail="No historical data for this cell.")