Tidy results table layout and centralise connection defaults
- Redesign results table from 8 columns to 4 (National Rail, Transfer, Eurostar, Total), making GWR and Eurostar legs consistent with each other - Move CET label next to Paris arrival time; show duration · train number on one line below - Move "Too early" label into the National Rail column for unreachable rows - Remove horizontal scrollbar (drop card-scroll / overflow-x: auto) - Add DEFAULT_MIN_CONNECTION / DEFAULT_MAX_CONNECTION to config/default.py (70 / 150 min); remove all hardcoded fallback values from app.py and templates - Redirect to clean URL when both connection params equal their defaults; omit params from all generated links when at default values Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6ab4534051
commit
8433252cae
5 changed files with 109 additions and 87 deletions
75
app.py
75
app.py
|
|
@ -65,11 +65,14 @@ DESTINATIONS = {
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
today = date.today().isoformat()
|
today = date.today().isoformat()
|
||||||
|
default_min, default_max = _get_defaults()
|
||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
destinations=DESTINATIONS,
|
destinations=DESTINATIONS,
|
||||||
today=today,
|
today=today,
|
||||||
stations=STATIONS,
|
stations=STATIONS,
|
||||||
|
default_min_connection=default_min,
|
||||||
|
default_max_connection=default_max,
|
||||||
valid_min_connections=sorted(VALID_MIN_CONNECTIONS),
|
valid_min_connections=sorted(VALID_MIN_CONNECTIONS),
|
||||||
valid_max_connections=sorted(VALID_MAX_CONNECTIONS),
|
valid_max_connections=sorted(VALID_MAX_CONNECTIONS),
|
||||||
)
|
)
|
||||||
|
|
@ -79,6 +82,21 @@ VALID_MIN_CONNECTIONS = {45, 50, 60, 70, 80, 90, 100, 110, 120}
|
||||||
VALID_MAX_CONNECTIONS = {60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180}
|
VALID_MAX_CONNECTIONS = {60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_defaults():
|
||||||
|
return (
|
||||||
|
app.config["DEFAULT_MIN_CONNECTION"],
|
||||||
|
app.config["DEFAULT_MAX_CONNECTION"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_connection(raw, default, valid_set):
|
||||||
|
try:
|
||||||
|
val = int(raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return default
|
||||||
|
return val if val in valid_set else default
|
||||||
|
|
||||||
|
|
||||||
@app.route("/search")
|
@app.route("/search")
|
||||||
def search():
|
def search():
|
||||||
slug = request.args.get("destination", "")
|
slug = request.args.get("destination", "")
|
||||||
|
|
@ -86,18 +104,13 @@ def search():
|
||||||
station_crs = request.args.get("station_crs", "BRI")
|
station_crs = request.args.get("station_crs", "BRI")
|
||||||
if station_crs not in STATION_BY_CRS:
|
if station_crs not in STATION_BY_CRS:
|
||||||
station_crs = "BRI"
|
station_crs = "BRI"
|
||||||
try:
|
default_min, default_max = _get_defaults()
|
||||||
min_conn = int(request.args.get("min_connection", 50))
|
min_conn = _parse_connection(
|
||||||
except ValueError:
|
request.args.get("min_connection"), default_min, VALID_MIN_CONNECTIONS
|
||||||
min_conn = 50
|
)
|
||||||
if min_conn not in VALID_MIN_CONNECTIONS:
|
max_conn = _parse_connection(
|
||||||
min_conn = 50
|
request.args.get("max_connection"), default_max, VALID_MAX_CONNECTIONS
|
||||||
try:
|
)
|
||||||
max_conn = int(request.args.get("max_connection", 110))
|
|
||||||
except ValueError:
|
|
||||||
max_conn = 110
|
|
||||||
if max_conn not in VALID_MAX_CONNECTIONS:
|
|
||||||
max_conn = 110
|
|
||||||
if slug in DESTINATIONS and travel_date:
|
if slug in DESTINATIONS and travel_date:
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
|
|
@ -105,8 +118,8 @@ def search():
|
||||||
station_crs=station_crs,
|
station_crs=station_crs,
|
||||||
slug=slug,
|
slug=slug,
|
||||||
travel_date=travel_date,
|
travel_date=travel_date,
|
||||||
min_connection=min_conn,
|
min_connection=None if min_conn == default_min else min_conn,
|
||||||
max_connection=max_conn,
|
max_connection=None if max_conn == default_max else max_conn,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
@ -121,18 +134,21 @@ def results(station_crs, slug, travel_date):
|
||||||
if not destination or not travel_date:
|
if not destination or not travel_date:
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
try:
|
default_min, default_max = _get_defaults()
|
||||||
min_connection = int(request.args.get("min_connection", 50))
|
min_connection = _parse_connection(
|
||||||
except ValueError:
|
request.args.get("min_connection"), default_min, VALID_MIN_CONNECTIONS
|
||||||
min_connection = 70
|
)
|
||||||
if min_connection not in VALID_MIN_CONNECTIONS:
|
max_connection = _parse_connection(
|
||||||
min_connection = 70
|
request.args.get("max_connection"), default_max, VALID_MAX_CONNECTIONS
|
||||||
try:
|
)
|
||||||
max_connection = int(request.args.get("max_connection", 110))
|
|
||||||
except ValueError:
|
# Redirect to clean URL when both params are at their defaults
|
||||||
max_connection = 150
|
if (
|
||||||
if max_connection not in VALID_MAX_CONNECTIONS:
|
"min_connection" in request.args or "max_connection" in request.args
|
||||||
max_connection = 150
|
) and min_connection == default_min and max_connection == default_max:
|
||||||
|
return redirect(
|
||||||
|
url_for("results", station_crs=station_crs, slug=slug, travel_date=travel_date)
|
||||||
|
)
|
||||||
|
|
||||||
user_agent = request.headers.get("User-Agent", rtt_scraper.DEFAULT_UA)
|
user_agent = request.headers.get("User-Agent", rtt_scraper.DEFAULT_UA)
|
||||||
|
|
||||||
|
|
@ -254,6 +270,9 @@ def results(station_crs, slug, travel_date):
|
||||||
rtt_url = RTT_PADDINGTON_URL.format(crs=station_crs, date=travel_date)
|
rtt_url = RTT_PADDINGTON_URL.format(crs=station_crs, date=travel_date)
|
||||||
rtt_station_url = RTT_STATION_URL.format(crs=station_crs, date=travel_date)
|
rtt_station_url = RTT_STATION_URL.format(crs=station_crs, date=travel_date)
|
||||||
|
|
||||||
|
url_min = None if min_connection == default_min else min_connection
|
||||||
|
url_max = None if max_connection == default_max else max_connection
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"results.html",
|
"results.html",
|
||||||
trips=trips,
|
trips=trips,
|
||||||
|
|
@ -278,6 +297,10 @@ def results(station_crs, slug, travel_date):
|
||||||
rtt_station_url=rtt_station_url,
|
rtt_station_url=rtt_station_url,
|
||||||
min_connection=min_connection,
|
min_connection=min_connection,
|
||||||
max_connection=max_connection,
|
max_connection=max_connection,
|
||||||
|
default_min_connection=default_min,
|
||||||
|
default_max_connection=default_max,
|
||||||
|
url_min_connection=url_min,
|
||||||
|
url_max_connection=url_max,
|
||||||
valid_min_connections=sorted(VALID_MIN_CONNECTIONS),
|
valid_min_connections=sorted(VALID_MIN_CONNECTIONS),
|
||||||
valid_max_connections=sorted(VALID_MAX_CONNECTIONS),
|
valid_max_connections=sorted(VALID_MAX_CONNECTIONS),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,7 @@ CACHE_DIR = os.path.expanduser('~/lib/data/tfl/cache')
|
||||||
|
|
||||||
# TransXChange timetable file for the Circle Line
|
# TransXChange timetable file for the Circle Line
|
||||||
CIRCLE_LINE_XML = os.path.join(TFL_DATA_DIR, 'output_txc_01CIR_.xml')
|
CIRCLE_LINE_XML = os.path.join(TFL_DATA_DIR, 'output_txc_01CIR_.xml')
|
||||||
|
|
||||||
|
# Default connection window (minutes) between Paddington arrival and St Pancras departure
|
||||||
|
DEFAULT_MIN_CONNECTION = 70
|
||||||
|
DEFAULT_MAX_CONNECTION = 150
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,6 @@
|
||||||
|
|
||||||
/* Card helpers */
|
/* Card helpers */
|
||||||
.card > h2:first-child { margin-top: 0; }
|
.card > h2:first-child { margin-top: 0; }
|
||||||
.card-scroll { overflow-x: auto; }
|
|
||||||
|
|
||||||
/* Form groups */
|
/* Form groups */
|
||||||
.form-group { margin-bottom: 1.2rem; }
|
.form-group { margin-bottom: 1.2rem; }
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
</label>
|
</label>
|
||||||
<select id="min_connection" name="min_connection" class="form-control">
|
<select id="min_connection" name="min_connection" class="form-control">
|
||||||
{% for mins in valid_min_connections %}
|
{% for mins in valid_min_connections %}
|
||||||
<option value="{{ mins }}" {% if mins == 70 %}selected{% endif %}>{{ mins }} min</option>
|
<option value="{{ mins }}" {% if mins == default_min_connection %}selected{% endif %}>{{ mins }} min</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
</label>
|
</label>
|
||||||
<select id="max_connection" name="max_connection" class="form-control">
|
<select id="max_connection" name="max_connection" class="form-control">
|
||||||
{% for mins in valid_max_connections %}
|
{% for mins in valid_max_connections %}
|
||||||
<option value="{{ mins }}" {% if mins == 150 %}selected{% endif %}>{{ mins }} min</option>
|
<option value="{{ mins }}" {% if mins == default_max_connection %}selected{% endif %}>{{ mins }} min</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
{{ departure_station_name }} → {{ destination }}
|
{{ departure_station_name }} → {{ destination }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="date-nav">
|
<div class="date-nav">
|
||||||
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=prev_date, min_connection=min_connection, max_connection=max_connection) }}"
|
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=prev_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
|
||||||
class="btn-nav">← Prev</a>
|
class="btn-nav">← Prev</a>
|
||||||
<strong>{{ travel_date_display }}</strong>
|
<strong>{{ travel_date_display }}</strong>
|
||||||
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=next_date, min_connection=min_connection, max_connection=max_connection) }}"
|
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=next_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
|
||||||
class="btn-nav">Next →</a>
|
class="btn-nav">Next →</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="switcher-section">
|
<div class="switcher-section">
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<a
|
<a
|
||||||
class="chip-link"
|
class="chip-link"
|
||||||
href="{{ url_for('results', station_crs=station_crs, slug=destination_slug, travel_date=travel_date, min_connection=min_connection, max_connection=max_connection) }}"
|
href="{{ url_for('results', station_crs=station_crs, slug=destination_slug, travel_date=travel_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
|
||||||
>{{ destination_name }}</a>
|
>{{ destination_name }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
@ -64,9 +64,13 @@
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function applyConnectionFilter() {
|
function applyConnectionFilter() {
|
||||||
var min = document.getElementById('min_conn_select').value;
|
var min = parseInt(document.getElementById('min_conn_select').value);
|
||||||
var max = document.getElementById('max_conn_select').value;
|
var max = parseInt(document.getElementById('max_conn_select').value);
|
||||||
window.location = '{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=travel_date) }}?min_connection=' + min + '&max_connection=' + max;
|
var base = '{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=travel_date) }}';
|
||||||
|
var params = [];
|
||||||
|
if (min !== {{ default_min_connection }}) params.push('min_connection=' + min);
|
||||||
|
if (max !== {{ default_max_connection }}) params.push('max_connection=' + max);
|
||||||
|
window.location = params.length ? base + '?' + params.join('&') : base;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<p class="card-meta">
|
<p class="card-meta">
|
||||||
|
|
@ -90,17 +94,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if trips or unreachable_morning_services %}
|
{% if trips or unreachable_morning_services %}
|
||||||
<div class="card card-scroll">
|
<div class="card">
|
||||||
<table class="results-table">
|
<table class="results-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="nowrap">{{ departure_station_name }}</th>
|
<th class="nowrap">National Rail</th>
|
||||||
<th class="nowrap">Paddington</th>
|
|
||||||
<th class="nowrap">GWR Fare</th>
|
|
||||||
<th class="col-transfer nowrap">Transfer</th>
|
<th class="col-transfer nowrap">Transfer</th>
|
||||||
<th class="nowrap">Depart STP</th>
|
<th class="nowrap">Eurostar</th>
|
||||||
<th>{{ destination }}</th>
|
|
||||||
<th class="nowrap">ES Std</th>
|
|
||||||
<th class="nowrap">Total</th>
|
<th class="nowrap">Total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -128,51 +128,50 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr class="{{ row_class }}">
|
<tr class="{{ row_class }}">
|
||||||
{% if row.row_type == 'trip' %}
|
{% if row.row_type == 'trip' %}
|
||||||
<td class="font-bold">
|
|
||||||
{{ row.depart_bristol }}
|
|
||||||
{% if row.headcode %}<br><span class="text-xs font-normal text-muted">{{ row.headcode }}</span>{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
{{ row.arrive_paddington }}
|
<span class="font-bold nowrap">{{ row.depart_bristol }} → {{ row.arrive_paddington }}</span>
|
||||||
<span class="text-sm text-muted nowrap">({{ row.gwr_duration }})</span>
|
<span class="text-sm text-muted nowrap">({{ row.gwr_duration }})</span>
|
||||||
{% if row.arrive_platform %}<br><span class="text-xs text-muted">Plat {{ row.arrive_platform }}</span>{% endif %}
|
{% if row.headcode or row.arrive_platform %}
|
||||||
</td>
|
<br><span class="text-xs text-muted">
|
||||||
<td class="nowrap">
|
{%- if row.headcode %}{{ row.headcode }}{% endif %}
|
||||||
|
{%- if row.headcode and row.arrive_platform %} · {% endif %}
|
||||||
|
{%- if row.arrive_platform %}Plat {{ row.arrive_platform }}{% endif %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
{% if row.ticket_price is not none %}
|
{% if row.ticket_price is not none %}
|
||||||
£{{ "%.2f"|format(row.ticket_price) }}
|
<br><span class="text-sm font-bold">£{{ "%.2f"|format(row.ticket_price) }}</span>
|
||||||
<br><span class="text-xs text-muted">{{ row.ticket_name }}</span>
|
<span class="text-xs text-muted">{{ row.ticket_name }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">–</span>
|
<br><span class="text-sm text-muted">–</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="col-transfer nowrap" style="color:#4a5568">
|
<td class="col-transfer" style="color:#4a5568">
|
||||||
{{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %}
|
<span class="nowrap">{{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %}</span>
|
||||||
{% if row.circle_services %}
|
{% if row.circle_services %}
|
||||||
{% set c = row.circle_services[0] %}
|
{% set c = row.circle_services[0] %}
|
||||||
<br><span class="text-xs text-muted">Circle {{ c.depart }} → KX {{ c.arrive_kx }}</span>
|
<br><span class="text-xs text-muted nowrap">Circle {{ c.depart }} → KX {{ c.arrive_kx }}</span>
|
||||||
{% if row.circle_services | length > 1 %}
|
{% if row.circle_services | length > 1 %}
|
||||||
{% set c2 = row.circle_services[1] %}
|
{% set c2 = row.circle_services[1] %}
|
||||||
<br><span class="text-xs text-muted" style="opacity:0.7">next {{ c2.depart }} → KX {{ c2.arrive_kx }}</span>
|
<br><span class="text-xs text-muted nowrap" style="opacity:0.7">next {{ c2.depart }} → KX {{ c2.arrive_kx }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="font-bold">
|
|
||||||
{{ row.depart_st_pancras }}
|
|
||||||
{% if row.train_number %}<br><span class="text-xs font-normal text-muted">{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}</span>{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
{{ row.arrive_destination }}
|
<span class="font-bold nowrap">{{ row.depart_st_pancras }} → {{ row.arrive_destination }} <span class="font-normal text-muted" style="font-size:0.85em">(CET)</span></span>
|
||||||
<span class="font-normal text-muted" style="font-size:0.85em">(CET)</span>
|
{% if row.eurostar_duration or row.train_number %}
|
||||||
{% if row.eurostar_duration %}<br><span class="text-sm text-muted nowrap">({{ row.eurostar_duration }})</span>{% endif %}
|
<br><span class="text-xs text-muted">
|
||||||
</td>
|
{%- if row.eurostar_duration %}<span class="nowrap">({{ row.eurostar_duration }})</span>{% endif %}
|
||||||
<td class="nowrap">
|
{%- if row.eurostar_duration and row.train_number %} · {% endif %}
|
||||||
|
{%- if row.train_number %}{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}{% endif %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
{% if row.eurostar_price is not none %}
|
{% if row.eurostar_price is not none %}
|
||||||
£{{ "%.2f"|format(row.eurostar_price) }}
|
<br><span class="text-sm font-bold">£{{ "%.2f"|format(row.eurostar_price) }}</span>
|
||||||
{% if row.eurostar_seats is not none %}
|
{% if row.eurostar_seats is not none %}
|
||||||
<br><span class="text-xs text-muted">{{ row.eurostar_seats }} at this price</span>
|
<span class="text-xs text-muted">{{ row.eurostar_seats }} at this price</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">–</span>
|
<br><span class="text-sm text-muted">–</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="font-bold nowrap">
|
<td class="font-bold nowrap">
|
||||||
|
|
@ -188,32 +187,29 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="font-bold">—</td>
|
|
||||||
<td>—</td>
|
|
||||||
<td>—</td>
|
|
||||||
<td class="col-transfer">—</td>
|
|
||||||
<td class="font-bold">
|
|
||||||
{{ row.depart_st_pancras }}
|
|
||||||
{% if row.train_number %}<br><span class="text-xs font-normal text-dimmed">{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}</span>{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
{{ row.arrive_destination }}
|
<span class="text-dimmed text-sm" title="Too early to reach from {{ departure_station_name }}">Too early</span>
|
||||||
<span class="font-normal text-dimmed" style="font-size:0.85em">(CET)</span>
|
|
||||||
{% if row.eurostar_duration %}<br><span class="text-sm text-dimmed nowrap">({{ row.eurostar_duration }})</span>{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
<td class="nowrap">
|
<td class="col-transfer text-dimmed">—</td>
|
||||||
|
<td>
|
||||||
|
<span class="font-bold nowrap text-dimmed">{{ row.depart_st_pancras }} → {{ row.arrive_destination }} <span class="font-normal" style="font-size:0.85em">(CET)</span></span>
|
||||||
|
{% if row.eurostar_duration or row.train_number %}
|
||||||
|
<br><span class="text-xs text-dimmed">
|
||||||
|
{%- if row.eurostar_duration %}<span class="nowrap">({{ row.eurostar_duration }})</span>{% endif %}
|
||||||
|
{%- if row.eurostar_duration and row.train_number %} · {% endif %}
|
||||||
|
{%- if row.train_number %}{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}{% endif %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
{% if row.eurostar_price is not none %}
|
{% if row.eurostar_price is not none %}
|
||||||
<span class="text-dimmed">£{{ "%.2f"|format(row.eurostar_price) }}</span>
|
<br><span class="text-sm text-dimmed">£{{ "%.2f"|format(row.eurostar_price) }}</span>
|
||||||
{% if row.eurostar_seats is not none %}
|
{% if row.eurostar_seats is not none %}
|
||||||
<br><span class="text-xs text-dimmed">{{ row.eurostar_seats }} at this price</span>
|
<span class="text-xs text-dimmed">{{ row.eurostar_seats }} at this price</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-dimmed">–</span>
|
<br><span class="text-sm text-dimmed">–</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="font-bold">
|
<td class="text-dimmed">—</td>
|
||||||
<span title="Too early to reach from {{ departure_station_name }}" class="text-dimmed nowrap">Too early</span>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue