diff --git a/app.py b/app.py index 1527c00..086a631 100644 --- a/app.py +++ b/app.py @@ -389,9 +389,15 @@ def _results_url( **params, ) if journey_type == "inbound": - params["journey_type"] = "inbound" + return url_for( + "back_results", + station_crs=station_crs, + slug=slug, + travel_date=travel_date, + **params, + ) return url_for( - "results", + "out_results", station_crs=station_crs, slug=slug, travel_date=travel_date, @@ -463,18 +469,17 @@ def search() -> ResponseReturnValue: return redirect(url_for("index")) -@app.route("/results///") -def results(station_crs: str, slug: str, travel_date: str) -> ResponseReturnValue: - return _results( - station_crs, - slug, - travel_date, - request.args.get("journey_type", "outbound"), - request.args.get("return_date"), - ) +@app.route("/results///out/") +def out_results(station_crs: str, slug: str, travel_date: str) -> ResponseReturnValue: + return _results(station_crs, slug, travel_date, "outbound", None) -@app.route("/results////return/") +@app.route("/results///back/") +def back_results(station_crs: str, slug: str, travel_date: str) -> ResponseReturnValue: + return _results(station_crs, slug, travel_date, "inbound", None) + + +@app.route("/results///return//") def return_results( station_crs: str, slug: str, travel_date: str, return_date: str ) -> ResponseReturnValue: @@ -1108,7 +1113,7 @@ def _results( station_crs=station_crs, slug=slug, travel_date=travel_date, - journey_type=journey_type if journey_type == "inbound" else None, + path_journey_type="back" if journey_type == "inbound" else "out", ) summary_html = _build_summary_html( @@ -1331,7 +1336,7 @@ def _results( station_crs=station_crs, slug=slug, travel_date=travel_date, - journey_type=journey_type if journey_type == "inbound" else None, + path_journey_type="back" if journey_type == "inbound" else "out", ) return render_template( @@ -1480,15 +1485,20 @@ def api_walkon_fares( return jsonify({"error": str(e)}), 500 -@app.route("/api/results_refresh///") -def api_results_refresh(station_crs: str, slug: str, travel_date: str) -> Response: - return _api_results_refresh( - station_crs, slug, travel_date, request.args.get("return_date") - ) +@app.route( + "/api/results_refresh////" +) +def api_results_refresh( + station_crs: str, slug: str, path_journey_type: str, travel_date: str +) -> Response: + if path_journey_type not in {"out", "back"}: + abort(404) + journey_type = "inbound" if path_journey_type == "back" else "outbound" + return _api_results_refresh(station_crs, slug, travel_date, None, journey_type) @app.route( - "/api/results_refresh////return/" + "/api/results_refresh///return//" ) def api_return_results_refresh( station_crs: str, slug: str, travel_date: str, return_date: str diff --git a/tests/test_app.py b/tests/test_app.py index b484e8b..5a17560 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -78,7 +78,7 @@ def test_search_redirects_to_results_with_selected_params() -> None: assert resp.status_code == 302 assert resp.headers["Location"].endswith( - "/results/BRI/rotterdam/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/rotterdam/out/2026-04-10?min_connection=60&max_connection=120" ) @@ -91,10 +91,21 @@ def test_search_redirects_return_with_return_date() -> None: assert resp.status_code == 302 assert resp.headers["Location"].endswith( - "/results/BRI/paris/2026-04-10/return/2026-04-17" + "/results/BRI/paris/return/2026-04-10/2026-04-17" ) +def test_search_redirects_inbound_to_back_path() -> None: + client = _client() + + resp = client.get( + "/search?journey_type=inbound&destination=paris&travel_date=2026-04-10&station_crs=BRI" + ) + + assert resp.status_code == 302 + assert resp.headers["Location"].endswith("/results/BRI/paris/back/2026-04-10") + + def test_nr_weekday_cache_key_includes_timetable_period() -> None: key = app_module._nr_weekday_cache_key("to_paddington", "BRI", "2026-06-22") @@ -106,7 +117,7 @@ def test_results_shows_same_day_destination_switcher(monkeypatch: Any) -> None: client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -114,11 +125,11 @@ def test_results_shows_same_day_destination_switcher(monkeypatch: Any) -> None: assert "Switch destination for Friday 10 April 2026" in html assert 'Paris Gare du Nord' in html assert ( - "/results/BRI/brussels/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/brussels/out/2026-04-10?min_connection=60&max_connection=120" in html ) assert ( - "/results/BRI/rotterdam/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/rotterdam/out/2026-04-10?min_connection=60&max_connection=120" in html ) assert "ES 9014" in html @@ -171,7 +182,7 @@ def test_results_can_render_from_weekday_timetable_cache(monkeypatch: Any) -> No client = _client() resp = client.get( - "/results/BRI/paris/2026-06-22?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-06-22?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -180,7 +191,7 @@ def test_results_can_render_from_weekday_timetable_cache(monkeypatch: Any) -> No assert "10:01 → 13:34" in html assert "ES 9014" in html assert "checking exact timetable" in html - assert "/api/results_refresh/BRI/paris/2026-06-22" in html + assert "/api/results_refresh/BRI/paris/out/2026-06-22" in html assert "refreshFullResults()" in html assert "window.location.reload()" not in html assert "Checking Eurostar price" in html @@ -268,7 +279,7 @@ def test_results_refresh_reloads_when_exact_timetable_differs(monkeypatch: Any) ) client = _client() - resp = client.get("/api/results_refresh/BRI/paris/2026-06-22") + resp = client.get("/api/results_refresh/BRI/paris/out/2026-06-22") body = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -341,7 +352,7 @@ def test_results_refresh_streams_prices_when_timetable_matches( ) client = _client() - resp = client.get("/api/results_refresh/BRI/paris/2026-06-22") + resp = client.get("/api/results_refresh/BRI/paris/out/2026-06-22") body = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -362,7 +373,7 @@ def test_results_progressive_shell_loads_without_scraping(monkeypatch: Any) -> N monkeypatch.setattr(gwr_fares_scraper, "fetch", fail_fetch) client = _client() - resp = client.get("/results/BRI/paris/2026-04-10?progressive=1") + resp = client.get("/results/BRI/paris/out/2026-04-10?progressive=1") html = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -379,7 +390,7 @@ def test_return_progressive_shell_formats_return_date(monkeypatch: Any) -> None: monkeypatch.setattr(gwr_fares_scraper, "fetch", fail_fetch) client = _client() - resp = client.get("/results/BRI/paris/2026-04-10/return/2026-04-17?progressive=1") + resp = client.get("/results/BRI/paris/return/2026-04-10/2026-04-17?progressive=1") html = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -392,7 +403,7 @@ def test_results_title_and_social_meta_include_destination(monkeypatch: Any) -> client = _client() resp = client.get( - "/results/BRI/lille/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/lille/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -407,7 +418,7 @@ def test_results_title_and_social_meta_include_destination(monkeypatch: Any) -> 'to Lille Europe on Friday 10 April 2026 via Paddington, St Pancras, and Eurostar.">' ) in html assert ( - '' + '' in html ) @@ -498,7 +509,7 @@ def test_results_marks_trips_within_five_minutes_of_fastest_and_slowest( client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -567,7 +578,7 @@ def test_results_shows_only_pre_first_reachable_unreachable_services( client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -583,7 +594,7 @@ def test_results_shows_eurostar_price_and_total(monkeypatch: Any) -> None: client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -640,7 +651,7 @@ def test_results_uses_unique_row_keys_for_same_eurostar(monkeypatch: Any) -> Non client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -685,7 +696,7 @@ def test_results_shows_unreachable_service_when_no_trips(monkeypatch: Any) -> No client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -703,7 +714,7 @@ def test_results_shows_eurostar_plus_price(monkeypatch: Any) -> None: client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -718,7 +729,7 @@ def test_results_selectors_present(monkeypatch: Any) -> None: client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -775,7 +786,7 @@ def test_results_preloads_cached_advance_fares(monkeypatch: Any) -> None: client = _client() resp = client.get( - "/results/BRI/paris/2026-04-10?min_connection=60&max_connection=120" + "/results/BRI/paris/out/2026-04-10?min_connection=60&max_connection=120" ) html = resp.get_data(as_text=True) @@ -827,7 +838,7 @@ def test_results_inbound_uses_reverse_legs(monkeypatch: Any) -> None: ) client = _client() - resp = client.get("/results/BRI/paris/2026-04-10?journey_type=inbound") + resp = client.get("/results/BRI/paris/back/2026-04-10") html = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -917,7 +928,7 @@ def test_results_return_renders_outbound_and_inbound_tables(monkeypatch: Any) -> ) client = _client() - resp = client.get("/results/BRI/paris/2026-04-10/return/2026-04-17") + resp = client.get("/results/BRI/paris/return/2026-04-10/2026-04-17") html = resp.get_data(as_text=True) assert resp.status_code == 200 @@ -925,11 +936,11 @@ def test_results_return_renders_outbound_and_inbound_tables(monkeypatch: Any) -> assert "Return: Paris Gare du Nord → Bristol Temple Meads" in html assert "Friday 10 April 2026" in html assert "Friday 17 April 2026" in html - assert "/results/BRI/paris/2026-04-09/return/2026-04-17" in html - assert "/results/BRI/paris/2026-04-11/return/2026-04-17" in html - assert "/results/BRI/paris/2026-04-10/return/2026-04-16" in html - assert "/results/BRI/paris/2026-04-10/return/2026-04-18" in html - assert "/results/BRI/paris/2026-04-10/return/2026-04-17" in html + assert "/results/BRI/paris/return/2026-04-09/2026-04-17" in html + assert "/results/BRI/paris/return/2026-04-11/2026-04-17" in html + assert "/results/BRI/paris/return/2026-04-10/2026-04-16" in html + assert "/results/BRI/paris/return/2026-04-10/2026-04-18" in html + assert "/results/BRI/paris/return/2026-04-10/2026-04-17" in html assert "journey_type=return" not in html assert "return_date=2026-04-17" not in html assert "Circle 09:10 → KX 09:25" in html diff --git a/tests/test_playwright_return_fares.py b/tests/test_playwright_return_fares.py index 01205ff..3c89489 100644 --- a/tests/test_playwright_return_fares.py +++ b/tests/test_playwright_return_fares.py @@ -244,7 +244,7 @@ def test_single_advance_standard_totals_after_click(single_server: str) -> None: browser = _launch_browser(p) page = browser.new_page() page.goto( - f"{single_server}/results/BRI/paris/2026-07-20", + f"{single_server}/results/BRI/paris/out/2026-07-20", wait_until="domcontentloaded", ) @@ -344,7 +344,7 @@ def test_single_next_date_advance_standard_labels_unreachable_rows( page = browser.new_page() page.goto( f"http://127.0.0.1:{server.server_port}" - "/results/BRI/brussels/2026-06-16", + "/results/BRI/brussels/out/2026-06-16", wait_until="domcontentloaded", ) page.get_by_role("link", name="Next →").click() @@ -372,7 +372,7 @@ def test_single_advance_standard_premier_totals_on_initial_url( browser = _launch_browser(p) page = browser.new_page() page.goto( - f"{single_server}/results/BRI/paris/2026-07-20" + f"{single_server}/results/BRI/paris/out/2026-07-20" "?nr_class=advance_std&es_class=plus", wait_until="domcontentloaded", ) @@ -452,7 +452,7 @@ def test_single_advance_first_falls_back_to_walkon_when_unavailable( page = browser.new_page() page.goto( f"http://127.0.0.1:{server.server_port}" - "/results/BRI/paris/2026-07-20?nr_class=advance_1st&es_class=standard", + "/results/BRI/paris/out/2026-07-20?nr_class=advance_1st&es_class=standard", wait_until="domcontentloaded", ) @@ -496,7 +496,7 @@ def test_return_advance_first_standard_premier_totals(local_server: str) -> None timeout=10000, ) - assert "/results/BRI/paris/2026-07-20/return/2026-07-27" in page.url + assert "/results/BRI/paris/return/2026-07-20/2026-07-27" in page.url assert "journey_type=return" not in page.url assert "return_date=2026-07-27" not in page.url assert "nr_class=advance_1st" in page.url @@ -528,7 +528,7 @@ def test_return_calendar_selects_outbound_before_return(local_server: str) -> No assert "Return: Wed 17 Jun" in page.locator("#cal-hint").inner_text() page.locator('button[type="submit"]').click() - page.wait_for_url("**/results/BRI/paris/2026-06-10/return/2026-06-17", timeout=10000) + page.wait_for_url("**/results/BRI/paris/return/2026-06-10/2026-06-17", timeout=10000) browser.close() @@ -539,7 +539,7 @@ def test_return_advance_first_standard_premier_totals_on_initial_url( browser = _launch_browser(p) page = browser.new_page() page.goto( - f"{local_server}/results/BRI/paris/2026-07-20/return/2026-07-27" + f"{local_server}/results/BRI/paris/return/2026-07-20/2026-07-27" "?nr_class=advance_1st&es_class=plus", wait_until="domcontentloaded", )