From 1bcd56d473af0ff097b7048616e015be792ecbfe Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Fri, 10 Jul 2026 19:58:56 -0700 Subject: [PATCH] Add comfort-temperature compare view; simplify Feels calendar filter (#9) Compare page (frontend/compare.{html,js} + /thermograph/compare route): line up several places over a date range and rank which best matches a comfort temperature. Per location it pulls the same daily record the Calendar uses and, per day, takes a chosen temperature (daytime high / daily mean / overnight low / feels-like) against the comfort target. A day "hits comfort" when it lands within an adjustable band; otherwise it counts as colder or warmer and the average miss is tracked. Results are ranked by comfort-day share (tie-broken by the smaller typical miss), with a diverging below/comfort/above bar and per-bucket stats. Comfort, band and judged temperature re-rank instantly client-side; only the location set or date range trigger a data load (shared calendar cache). Feels calendar filter: drop the comfort-temperature slider and the client-side felt-high/felt-low re-pick. The Feels metric now colors by the server's combined feels-like value like the other metrics, so its tab matches the rest of the metric selector. --- app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 8ae8175..a5b7ca3 100644 --- a/app.py +++ b/app.py @@ -81,7 +81,7 @@ async def no_store_static(request, call_next): skipping the cache entirely is fine and avoids "my change isn't showing" bugs.""" response = await call_next(request) path = request.url.path - pages = (BASE, f"{BASE}/", f"{BASE}/calendar", f"{BASE}/day", f"{BASE}/legend") + pages = (BASE, f"{BASE}/", f"{BASE}/calendar", f"{BASE}/day", f"{BASE}/compare", f"{BASE}/legend") if path.endswith((".js", ".css", ".html")) or path in pages: response.headers["Cache-Control"] = "no-store, must-revalidate" return response @@ -437,6 +437,7 @@ app.add_api_route(BASE, lambda: RedirectResponse(url=f"{BASE}/"), methods=["GET" app.add_api_route(f"{BASE}/", lambda: _page("index.html"), methods=["GET"], include_in_schema=False) app.add_api_route(f"{BASE}/calendar", lambda: _page("calendar.html"), methods=["GET"], include_in_schema=False) app.add_api_route(f"{BASE}/day", lambda: _page("day.html"), methods=["GET"], include_in_schema=False) +app.add_api_route(f"{BASE}/compare", lambda: _page("compare.html"), methods=["GET"], include_in_schema=False) app.add_api_route(f"{BASE}/legend", lambda: _page("legend.html"), methods=["GET"], include_in_schema=False) # Everything else under BASE (app.js, style.css, nav.js, …) is a static asset.