import asyncio from flask import Flask, render_template, redirect, url_for, request from datetime import date, timedelta from cache import get_cached, set_cached import scraper.eurostar as eurostar_scraper import scraper.realtime_trains as rtt_scraper from trip_planner import combine_trips app = Flask(__name__) DESTINATIONS = { 'paris': 'Paris Gare du Nord', 'brussels': 'Brussels Midi', 'lille': 'Lille Europe', 'amsterdam': 'Amsterdam Centraal', } async def _fetch_both(destination: str, travel_date: str, user_agent: str): """Fetch GWR trains and Eurostar times simultaneously.""" gwr, es = await asyncio.gather( rtt_scraper.fetch(travel_date, user_agent), eurostar_scraper.fetch(destination, travel_date, user_agent), return_exceptions=True, ) return gwr, es @app.route('/') def index(): today = date.today().isoformat() return render_template('index.html', destinations=DESTINATIONS, today=today) @app.route('/search') def search(): slug = request.args.get('destination', '') travel_date = request.args.get('travel_date', '') if slug in DESTINATIONS and travel_date: return redirect(url_for('results', slug=slug, travel_date=travel_date)) return redirect(url_for('index')) @app.route('/results//') def results(slug, travel_date): destination = DESTINATIONS.get(slug) if not destination or not travel_date: return redirect(url_for('index')) user_agent = request.headers.get('User-Agent', rtt_scraper.DEFAULT_UA) cache_key = f"{travel_date}_{destination}" cached = get_cached(cache_key) error = None if cached: gwr_trains = cached['gwr'] eurostar_trains = cached['eurostar'] from_cache = True else: from_cache = False gwr_result, es_result = asyncio.run(_fetch_both(destination, travel_date, user_agent)) if isinstance(gwr_result, Exception): gwr_trains = [] error = f"Could not fetch GWR trains: {gwr_result}" else: gwr_trains = gwr_result if isinstance(es_result, Exception): eurostar_trains = [] msg = f"Could not fetch Eurostar times: {es_result}" error = f"{error}; {msg}" if error else msg else: eurostar_trains = es_result if gwr_trains or eurostar_trains: set_cached(cache_key, {'gwr': gwr_trains, 'eurostar': eurostar_trains}) trips = combine_trips(gwr_trains, eurostar_trains, travel_date) dt = date.fromisoformat(travel_date) prev_date = (dt - timedelta(days=1)).isoformat() next_date = (dt + timedelta(days=1)).isoformat() travel_date_display = dt.strftime('%A %-d %B %Y') return render_template( 'results.html', trips=trips, destination=destination, travel_date=travel_date, slug=slug, prev_date=prev_date, next_date=next_date, travel_date_display=travel_date_display, gwr_count=len(gwr_trains), eurostar_count=len(eurostar_trains), from_cache=from_cache, error=error, ) if __name__ == '__main__': app.run(debug=True)