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>
This commit is contained in:
parent
b88d23a270
commit
804fcedfad
5 changed files with 428 additions and 44 deletions
|
|
@ -75,6 +75,12 @@
|
|||
{{ gwr_count }} GWR service{{ 's' if gwr_count != 1 }}
|
||||
·
|
||||
{{ eurostar_count }} Eurostar service{{ 's' if eurostar_count != 1 }}
|
||||
{% if unreachable_morning_services %}
|
||||
·
|
||||
<span style="color:#718096">
|
||||
{{ unreachable_morning_services | length }} morning service{{ 's' if unreachable_morning_services | length != 1 }} unavailable from Bristol
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if from_cache %}
|
||||
· <span style="color:#718096;font-size:0.85rem">(cached)</span>
|
||||
{% endif %}
|
||||
|
|
@ -86,13 +92,14 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if trips %}
|
||||
{% if trips or unreachable_morning_services %}
|
||||
<div class="card" style="overflow-x:auto">
|
||||
<table style="width:100%;border-collapse:collapse;font-size:0.95rem">
|
||||
<thead>
|
||||
<tr style="border-bottom:2px solid #e2e8f0;text-align:left">
|
||||
<th style="padding:0.6rem 0.8rem;white-space:nowrap">Bristol</th>
|
||||
<th style="padding:0.6rem 0.8rem;white-space:nowrap">Paddington</th>
|
||||
<th style="padding:0.6rem 0.8rem;white-space:nowrap">GWR Fare</th>
|
||||
<th style="padding:0.6rem 0.8rem;white-space:nowrap">Transfer</th>
|
||||
<th style="padding:0.6rem 0.8rem;white-space:nowrap">Depart St Pancras</th>
|
||||
<th style="padding:0.6rem 0.8rem">{{ destination }}
|
||||
|
|
@ -101,47 +108,73 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if trips %}
|
||||
{% set best_mins = trips | map(attribute='total_minutes') | min %}
|
||||
{% set worst_mins = trips | map(attribute='total_minutes') | max %}
|
||||
{% for trip in trips %}
|
||||
{% if trip.total_minutes == best_mins and trips | length > 1 %}
|
||||
{% endif %}
|
||||
{% for row in result_rows %}
|
||||
{% if row.row_type == 'trip' and row.total_minutes <= best_mins + 5 and trips | length > 1 %}
|
||||
{% set row_bg = 'background:#f0fff4' %}
|
||||
{% elif trip.total_minutes == worst_mins and trips | length > 1 %}
|
||||
{% elif row.row_type == 'trip' and row.total_minutes >= worst_mins - 5 and trips | length > 1 %}
|
||||
{% set row_bg = 'background:#fff5f5' %}
|
||||
{% elif row.row_type == 'unreachable' %}
|
||||
{% set row_bg = 'background:#f7fafc;color:#a0aec0' %}
|
||||
{% elif loop.index is odd %}
|
||||
{% set row_bg = 'background:#f7fafc' %}
|
||||
{% else %}
|
||||
{% set row_bg = '' %}
|
||||
{% endif %}
|
||||
<tr style="border-bottom:1px solid #e2e8f0;{{ row_bg }}">
|
||||
{% if row.row_type == 'trip' %}
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">
|
||||
{{ trip.depart_bristol }}
|
||||
{% if trip.headcode %}<br><span style="font-size:0.75rem;font-weight:400;color:#718096">{{ trip.headcode }}</span>{% endif %}
|
||||
{{ row.depart_bristol }}
|
||||
{% if row.headcode %}<br><span style="font-size:0.75rem;font-weight:400;color:#718096">{{ row.headcode }}</span>{% endif %}
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem">
|
||||
{{ trip.arrive_paddington }}
|
||||
<span style="font-size:0.8rem;color:#718096">({{ trip.gwr_duration }})</span>
|
||||
{{ row.arrive_paddington }}
|
||||
<span style="font-size:0.8rem;color:#718096">({{ row.gwr_duration }})</span>
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem;white-space:nowrap">
|
||||
£{{ "%.2f"|format(row.ticket_price) }}
|
||||
<br><span style="font-size:0.75rem;color:#718096">{{ row.ticket_name }}</span>
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem;color:#4a5568">
|
||||
{{ trip.connection_duration }}
|
||||
{{ row.connection_duration }}
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">
|
||||
{{ trip.depart_st_pancras }}
|
||||
{% if trip.train_number %}<br><span style="font-size:0.75rem;font-weight:400;color:#718096">{{ trip.train_number }}</span>{% endif %}
|
||||
{{ row.depart_st_pancras }}
|
||||
{% if row.train_number %}<br><span style="font-size:0.75rem;font-weight:400;color:#718096">{{ row.train_number }}</span>{% endif %}
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem">
|
||||
{{ trip.arrive_destination }}
|
||||
{{ row.arrive_destination }}
|
||||
<span style="font-weight:400;color:#718096;font-size:0.85em">(CET)</span>
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">
|
||||
{% if trip.total_minutes == best_mins and trips | length > 1 %}
|
||||
<span style="color:#276749" title="Fastest option">{{ trip.total_duration }} ⚡</span>
|
||||
{% elif trip.total_minutes == worst_mins and trips | length > 1 %}
|
||||
<span style="color:#c53030" title="Slowest option">{{ trip.total_duration }} 🐢</span>
|
||||
{% if row.total_minutes <= best_mins + 5 and trips | length > 1 %}
|
||||
<span style="color:#276749" title="Fastest option">{{ row.total_duration }} ⚡</span>
|
||||
{% elif row.total_minutes >= worst_mins - 5 and trips | length > 1 %}
|
||||
<span style="color:#c53030" title="Slowest option">{{ row.total_duration }} 🐢</span>
|
||||
{% else %}
|
||||
<span style="color:#00539f">{{ trip.total_duration }}</span>
|
||||
<span style="color:#00539f">{{ row.total_duration }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">—</td>
|
||||
<td style="padding:0.6rem 0.8rem">—</td>
|
||||
<td style="padding:0.6rem 0.8rem">—</td>
|
||||
<td style="padding:0.6rem 0.8rem">Unavailable</td>
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">
|
||||
{{ row.depart_st_pancras }}
|
||||
{% if row.train_number %}<br><span style="font-size:0.75rem;font-weight:400;color:#a0aec0">{{ row.train_number }}</span>{% endif %}
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem">
|
||||
{{ row.arrive_destination }}
|
||||
<span style="font-weight:400;color:#a0aec0;font-size:0.85em">(CET)</span>
|
||||
</td>
|
||||
<td style="padding:0.6rem 0.8rem;font-weight:600">
|
||||
<span title="No same-day Bristol connection">Unavailable from Bristol</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
@ -150,6 +183,9 @@
|
|||
|
||||
<p style="margin-top:1rem;font-size:0.82rem;color:#718096">
|
||||
Paddington → St Pancras connection: {{ min_connection }}–{{ max_connection }} min.
|
||||
{% if unreachable_morning_services %}
|
||||
Morning means Eurostar departures before 12:00 from St Pancras.
|
||||
{% endif %}
|
||||
Eurostar times are from the general timetable and may vary; always check
|
||||
<a href="{{ eurostar_url }}" target="_blank" rel="noopener">eurostar.com</a> to book.
|
||||
·
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue