For each conference, calculate the number of free days available
before and after, relative to the trip boundary or adjacent
conference. Multi-conference trips show the gap between conferences
as "after" the first and "before" the second.
Closes#189
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Web view was calling the OpenWeatherMap API directly on every request
(home + all trip locations), causing >1000 calls/day. Now web_view.py
uses cache_only=True everywhere so it never triggers live API calls —
update.py (cron) is the sole API caller. Also warms home location cache
in update_weather(), and increases TTL from 6h to 24h.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents hitting the SpaceDevs rate limit by skipping the API call
when the launches cache is still fresh (< 2 hours old), matching the
same TTL pattern already used for the active crewed flights cache.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Don't write rate-limit/error responses to disk in next_launch_api_data,
so they can't become the "most recent" cache file and cause KeyError crashes
in read_cached_launches. Add defensive results-list checks in
read_cached_launches and get_launches to handle any existing bad files.
Drop refresh=True from the updater's get_active_crewed_flights call so the
2-hour TTL applies; the paginated spacecraft/flight crawl was running on
every hourly cron job and likely causing the burst that triggered throttling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show 8-day Bristol home weather on the index and weekends pages.
Show destination weather per day on the trip list and trip page.
Cache forecasts in ~/lib/data/weather/ and refresh via update.py.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows recording when you arrive late or leave early at a multi-day
conference. Both fields accept a plain date or datetime with time.
Trip pages display the attendance dates instead of the official
conference dates when these fields are set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract `load_road_transport()` as a shared helper for bus and coach,
combining the near-identical route rendering blocks in `get_trip_routes`
and `Trip.elements()` into single `in ("coach", "bus")` branches.
Document transport type patterns in AGENTS.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add case "bus" to get_locations() and bus route handling to
get_trip_routes() in trip.py. Coach stations now populate a dedicated
"coach_station" dict instead of the train station list. Add
"coach_station" (🚌) and "bus_stop" (🚏) emoji types to map.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aggregate yearly stats into overall totals so the trip stats page
shows flight segments by airline, airports used, and stations used
in the summary section at the top.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously Trip.total_distance returned None if any travel leg lacked a
distance, which led to undercounted trip stats (less than flight + train
totals).
Now it accumulates only distances that are present and ignores
missing/falsy values. The method returns a float and yields 0.0 when no
distances are available. Docstring updated accordingly.
Fixes#185