Commit graph

30 commits

Author SHA1 Message Date
71be0dd8cf Move inline styles to CSS classes; update README
Extract repeated inline styles from templates into named CSS classes in
base.html: layout helpers, buttons, form groups, alert boxes, results table
rules, row highlight classes, typography utilities, and empty-state styles.
Remove the per-page <style> block from results.html.

Update README to reflect current destinations, GraphQL data source, Circle
Line timetable, configurable connection range, and GWR fare table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 15:39:04 +01:00
e6f310f517 Add Cologne Hbf destination; use coin emoji for cheapest journey
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 15:02:01 +01:00
c22a3ea0fc Consolidate to single GraphQL call; show indirect trains; fix price formatting
Replace two-step Eurostar fetch (HTML timetable + GraphQL prices) with a
single GraphQL call that returns timing, train numbers, prices, and seats.
Support indirect services (e.g. Amsterdam) by joining multi-leg train numbers
with ' + ' and keeping the earliest arrival per departure time.
Fix half-pound prices by casting displayPrice to float instead of int.
Wrap each train number segment in white-space:nowrap so 'ES 9132 + ER 9363'
never breaks mid-segment.
Format Eurostar prices with two decimal places.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 14:46:22 +01:00
05eec29b7d Show Eurostar seat availability and no-prices notice
fetch_prices now returns {'price': ..., 'seats': ...} per departure.
Seat count (labelled "N at this price") is shown below the fare — it
reflects price-band depth rather than total remaining seats. A yellow
notice is shown when the API returns journeys but all prices are null
(tickets not yet on sale).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 14:12:54 +01:00
cd37f0619b Add config system with TFL_DATA_DIR and CACHE_DIR
config/default.py holds defaults using ~/lib/data/tfl (expanduser, so safe
to commit). app.py loads it then overlays config/local.py if present, pushing
paths into cache and circle_line modules. config/local.py is gitignored for
machine-specific absolute paths (e.g. on the server where www-data runs).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 13:37:44 +01:00
c215456620 Use real Circle Line timetable; add Eurostar duration
Parse Circle Line times from TransXChange XML (output_txc_01CIR_.xml) with
separate weekday/Saturday/Sunday schedules, replacing the approximated
every-10-minutes pattern. Subtract 1 hour timezone offset (CET/CEST vs
GMT/BST) when computing Eurostar journey duration, shown for both viable
and unreachable services.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 13:26:35 +01:00
60674fe663 Add Circle Line timetable info. 2026-04-04 12:58:19 +01:00
cdee44ea3f Adjust wording, add nowrap. 2026-04-04 12:45:43 +01:00
d089d3d716 Add price emoji indicators and hover text to results table
Show 💰 on total prices within £10 of the cheapest journey and 💸
within £10 of the most expensive, mirroring the /🐢 logic for journey
time. Only applied when more than one priced trip exists.

Add title attributes to ⚠️ ("Tight connection"),  ("Fastest journey"),
and 🐢 ("Slowest journey") for accessibility and discoverability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 12:28:12 +01:00
e7695a5e49 Drive search form dropdowns from VALID_MIN/MAX_CONNECTIONS; warn on short transfers
Index page connection time dropdowns now iterate over valid_min_connections
and valid_max_connections passed from the view, so any change to the sets
in app.py is reflected automatically (also adds the missing 45 min option).

Add ⚠️ next to transfer times under 80 minutes in the results table;
store connection_minutes in each trip dict to support the comparison.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:52:32 +01:00
19656f412a Prevent GWR duration from wrapping on small screens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:48:49 +01:00
06afd57957 Link to Eurostar search page with pricing; add Bristol departures RTT link
Replace the Eurostar timetable link with the search URL
(eurostar.com/search/uk-en?adult=1&origin=…&destination=…&outbound=…)
so the footer links directly to the page that shows prices for the
specific date and destination.

Add a Bristol Temple Meads → Paddington departures link on RTT alongside
the existing Paddington arrivals link.

Also update "morning service unavailable" badge and tests to reflect the
removal of the morning-only cutoff filter from find_unreachable_morning_eurostars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:47:46 +01:00
4e4202e220 Drop MORNING_CUTOFF_HOUR, consider all services. 2026-04-04 10:43:47 +01:00
df94e822ae Try 45 minute min connection time. 2026-04-04 10:43:31 +01:00
6b044b9493 Add 24-hour TTL to Eurostar price cache
Cache reads now accept an optional ttl (seconds). get_cached checks the
file mtime and returns None if the entry is older than the TTL, triggering
a fresh fetch. Eurostar prices use a 24-hour TTL; timetable caches remain
indefinite (date-scoped keys become irrelevant once the date passes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:38:47 +01:00
0dee942e16 Show Eurostar Standard prices and total journey cost on results page
Fetches prices via the site-api.eurostar.com GraphQL gateway
(NewBookingSearch operation, discovered with Playwright). Adds
fetch_prices() to scraper/eurostar.py using requests, caches results,
annotates each trip with eurostar_price and total_price, and shows an
ES Std column plus total cost (duration + price) in the results table.
The Transfer column is hidden on small screens for mobile usability.

Closes #4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:38:09 +01:00
804fcedfad Show cheapest GWR fare per journey and flag unreachable morning Eurostars
Add cheapest_gwr_ticket() to trip_planner.py encoding the SSS/SVS/SDS
walk-on single restrictions for Bristol Temple Meads → Paddington: on
weekdays, Super Off-Peak (£45) is valid before 05:05 or from 09:58,
Off-Peak (£63.60) from 08:26, and Anytime (£138.70) covers the gap.
Weekends have no restrictions. The fare is included in each trip dict
and displayed in a new GWR Fare column on the results page.

Also wire up find_unreachable_morning_eurostars() into the results view
so early Eurostar services unreachable from Bristol appear in the table,
with tests covering both features.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:22:47 +01:00
b88d23a270 Use destination-specific result page metadata 2026-04-01 15:33:07 +01:00
4de4c1d556 Add app route tests and fix stale assertions 2026-04-01 14:33:10 +01:00
143887d482 Add same-day destination switcher to results 2026-04-01 12:50:43 +01:00
c3d289e809 Show Bristol departure on search form 2026-04-01 12:46:30 +01:00
a9d5a0589d Show destinations as one-click cards 2026-04-01 12:38:54 +01:00
945d028c13 Generate Eurostar timetable URLs from station IDs 2026-04-01 12:17:50 +01:00
f75c1e2db3 Add Rotterdam Centraal 2026-04-01 12:13:27 +01:00
1faa0a1909 Include link to git repo. 2026-03-31 15:13:11 +01:00
cce630d922 Fix favicon 2026-03-31 14:58:49 +01:00
1fa2e68b31 Various improvements 2026-03-31 12:59:44 +01:00
876eb6a759 Various improvements 2026-03-31 10:42:30 +01:00
2090268754 Various improvements 2026-03-30 20:55:15 +01:00
a8e0bd39e5 Initial commit. 2026-03-30 19:34:46 +01:00