Clarify missing Eurostar price states
This commit is contained in:
parent
ed8a5626a4
commit
2b475aa726
4 changed files with 122 additions and 6 deletions
30
app.py
30
app.py
|
|
@ -261,15 +261,29 @@ def _eurostar_prices_by_row(
|
||||||
if service.get("price") is not None
|
if service.get("price") is not None
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
|
"es_standard_status": _eurostar_price_status(
|
||||||
|
service.get("price"), service.get("seats")
|
||||||
|
),
|
||||||
"es_plus": (
|
"es_plus": (
|
||||||
{"price": service.get("plus_price"), "seats": service.get("plus_seats")}
|
{"price": service.get("plus_price"), "seats": service.get("plus_seats")}
|
||||||
if service.get("plus_price") is not None
|
if service.get("plus_price") is not None
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
|
"es_plus_status": _eurostar_price_status(
|
||||||
|
service.get("plus_price"), service.get("plus_seats")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return prices
|
return prices
|
||||||
|
|
||||||
|
|
||||||
|
def _eurostar_price_status(price: Any, seats: Any) -> str | None:
|
||||||
|
if price is not None:
|
||||||
|
return None
|
||||||
|
if seats == 0:
|
||||||
|
return "sold_out"
|
||||||
|
return "price_not_returned"
|
||||||
|
|
||||||
|
|
||||||
def _get_defaults() -> tuple[int, int]:
|
def _get_defaults() -> tuple[int, int]:
|
||||||
return (
|
return (
|
||||||
app.config["DEFAULT_MIN_CONNECTION"],
|
app.config["DEFAULT_MIN_CONNECTION"],
|
||||||
|
|
@ -300,6 +314,9 @@ def _section_trip_fares(section: dict[str, Any]) -> dict[str, Any]:
|
||||||
if row.get("eurostar_price") is not None
|
if row.get("eurostar_price") is not None
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
es_std_status = _eurostar_price_status(
|
||||||
|
row.get("eurostar_price"), row.get("eurostar_seats")
|
||||||
|
)
|
||||||
es_plus = (
|
es_plus = (
|
||||||
{
|
{
|
||||||
"price": row["eurostar_plus_price"],
|
"price": row["eurostar_plus_price"],
|
||||||
|
|
@ -308,13 +325,18 @@ def _section_trip_fares(section: dict[str, Any]) -> dict[str, Any]:
|
||||||
if row.get("eurostar_plus_price") is not None
|
if row.get("eurostar_plus_price") is not None
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
es_plus_status = _eurostar_price_status(
|
||||||
|
row.get("eurostar_plus_price"), row.get("eurostar_plus_seats")
|
||||||
|
)
|
||||||
trip_fares[row["row_key"]] = {
|
trip_fares[row["row_key"]] = {
|
||||||
"section": section["id"],
|
"section": section["id"],
|
||||||
"eurostar_key": row.get("eurostar_key"),
|
"eurostar_key": row.get("eurostar_key"),
|
||||||
"advance_key": row.get("depart_bristol") or row.get("depart_paddington"),
|
"advance_key": row.get("depart_bristol") or row.get("depart_paddington"),
|
||||||
"walkon": walkon,
|
"walkon": walkon,
|
||||||
"es_standard": es_std,
|
"es_standard": es_std,
|
||||||
|
"es_standard_status": es_std_status,
|
||||||
"es_plus": es_plus,
|
"es_plus": es_plus,
|
||||||
|
"es_plus_status": es_plus_status,
|
||||||
"circle_fare": circle_fare,
|
"circle_fare": circle_fare,
|
||||||
}
|
}
|
||||||
return trip_fares
|
return trip_fares
|
||||||
|
|
@ -1268,6 +1290,9 @@ def _results(
|
||||||
if row.get("eurostar_price") is not None
|
if row.get("eurostar_price") is not None
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
es_std_status = _eurostar_price_status(
|
||||||
|
row.get("eurostar_price"), row.get("eurostar_seats")
|
||||||
|
)
|
||||||
es_plus = (
|
es_plus = (
|
||||||
{
|
{
|
||||||
"price": row["eurostar_plus_price"],
|
"price": row["eurostar_plus_price"],
|
||||||
|
|
@ -1276,6 +1301,9 @@ def _results(
|
||||||
if row.get("eurostar_plus_price") is not None
|
if row.get("eurostar_plus_price") is not None
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
es_plus_status = _eurostar_price_status(
|
||||||
|
row.get("eurostar_plus_price"), row.get("eurostar_plus_seats")
|
||||||
|
)
|
||||||
trip_fares[row["row_key"]] = {
|
trip_fares[row["row_key"]] = {
|
||||||
"section": section["id"],
|
"section": section["id"],
|
||||||
"eurostar_key": row.get("eurostar_key"),
|
"eurostar_key": row.get("eurostar_key"),
|
||||||
|
|
@ -1283,7 +1311,9 @@ def _results(
|
||||||
or row.get("depart_paddington"),
|
or row.get("depart_paddington"),
|
||||||
"walkon": walkon,
|
"walkon": walkon,
|
||||||
"es_standard": es_std,
|
"es_standard": es_std,
|
||||||
|
"es_standard_status": es_std_status,
|
||||||
"es_plus": es_plus,
|
"es_plus": es_plus,
|
||||||
|
"es_plus_status": es_plus_status,
|
||||||
"circle_fare": circle_fare,
|
"circle_fare": circle_fare,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@
|
||||||
let ADVANCE_STREAM_URLS = {{ advance_stream_urls_json | safe }};
|
let ADVANCE_STREAM_URLS = {{ advance_stream_urls_json | safe }};
|
||||||
const TIMETABLE_REFRESH_URL = {{ timetable_refresh_url|tojson }};
|
const TIMETABLE_REFRESH_URL = {{ timetable_refresh_url|tojson }};
|
||||||
const HAS_PROVISIONAL_TIMETABLE = {{ 'true' if provisional_timetable else 'false' }};
|
const HAS_PROVISIONAL_TIMETABLE = {{ 'true' if provisional_timetable else 'false' }};
|
||||||
|
let eurostarRefreshPending = HAS_PROVISIONAL_TIMETABLE && !!TIMETABLE_REFRESH_URL && !!window.EventSource;
|
||||||
let cachedAdvanceFares = ADVANCE_FARES;
|
let cachedAdvanceFares = ADVANCE_FARES;
|
||||||
let currentNrClasses = {{ nr_classes_json | safe }};
|
let currentNrClasses = {{ nr_classes_json | safe }};
|
||||||
let currentEsClasses = {{ es_classes_json | safe }};
|
let currentEsClasses = {{ es_classes_json | safe }};
|
||||||
|
|
@ -260,11 +261,34 @@
|
||||||
var row = TRIP_FARES[rowKey];
|
var row = TRIP_FARES[rowKey];
|
||||||
if (rowKey !== key && row.eurostar_key !== key) continue;
|
if (rowKey !== key && row.eurostar_key !== key) continue;
|
||||||
if (prices[key].es_standard) row.es_standard = prices[key].es_standard;
|
if (prices[key].es_standard) row.es_standard = prices[key].es_standard;
|
||||||
|
if (prices[key].es_standard_status !== undefined) row.es_standard_status = prices[key].es_standard_status;
|
||||||
if (prices[key].es_plus) row.es_plus = prices[key].es_plus;
|
if (prices[key].es_plus) row.es_plus = prices[key].es_plus;
|
||||||
|
if (prices[key].es_plus_status !== undefined) row.es_plus_status = prices[key].es_plus_status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function eurostarMissingText(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (status === 'sold_out') return 'Eurostar sold out';
|
||||||
|
if (status === 'price_not_returned') return 'No Eurostar price returned';
|
||||||
|
return 'No Eurostar price returned';
|
||||||
|
}
|
||||||
|
|
||||||
|
function eurostarMissingTitle(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (status === 'sold_out') return 'Eurostar returned 0 seats at this price for the selected class.';
|
||||||
|
if (status === 'price_not_returned') return 'Eurostar returned this service without a price for the selected class.';
|
||||||
|
return 'Eurostar did not return a price for the selected class. Check eurostar.com.';
|
||||||
|
}
|
||||||
|
|
||||||
|
function eurostarMissingFareHtml(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (eurostarRefreshPending && !status) return '<span class="text-sm text-muted">checking</span>';
|
||||||
|
if (status === 'sold_out') return '<span class="text-sm text-muted">sold out</span>';
|
||||||
|
return '<span class="text-sm text-muted">\u2013</span>';
|
||||||
|
}
|
||||||
|
|
||||||
function sectionNeedsAdvance(sectionId) {
|
function sectionNeedsAdvance(sectionId) {
|
||||||
var nrClass = currentNrClasses[sectionId] || 'walkon';
|
var nrClass = currentNrClasses[sectionId] || 'walkon';
|
||||||
for (var key in TRIP_FARES) {
|
for (var key in TRIP_FARES) {
|
||||||
|
|
@ -401,11 +425,11 @@
|
||||||
var esStdEl = tr.querySelector('.es-standard');
|
var esStdEl = tr.querySelector('.es-standard');
|
||||||
var esPlusEl = tr.querySelector('.es-plus');
|
var esPlusEl = tr.querySelector('.es-plus');
|
||||||
if (esStdEl) {
|
if (esStdEl) {
|
||||||
esStdEl.innerHTML = '<span class="text-xs text-muted">Std</span> ' + (row.es_standard ? fareHtml(row.es_standard) : '<span class="text-sm text-muted">\u2013</span>');
|
esStdEl.innerHTML = '<span class="text-xs text-muted">Std</span> ' + (row.es_standard ? fareHtml(row.es_standard) : eurostarMissingFareHtml(row, 'standard'));
|
||||||
esStdEl.classList.toggle('fare-inactive', esClass !== 'standard');
|
esStdEl.classList.toggle('fare-inactive', esClass !== 'standard');
|
||||||
}
|
}
|
||||||
if (esPlusEl) {
|
if (esPlusEl) {
|
||||||
esPlusEl.innerHTML = '<span class="text-xs text-muted">SP</span> ' + (row.es_plus ? fareHtml(row.es_plus) : '<span class="text-sm text-muted">\u2013</span>');
|
esPlusEl.innerHTML = '<span class="text-xs text-muted">SP</span> ' + (row.es_plus ? fareHtml(row.es_plus) : eurostarMissingFareHtml(row, 'plus'));
|
||||||
esPlusEl.classList.toggle('fare-inactive', esClass !== 'plus');
|
esPlusEl.classList.toggle('fare-inactive', esClass !== 'plus');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,8 +450,10 @@
|
||||||
totalSpan.innerHTML = '';
|
totalSpan.innerHTML = '';
|
||||||
} else if (!nrFare) {
|
} else if (!nrFare) {
|
||||||
totalSpan.innerHTML = '<span class="text-xs text-muted" title="National Rail fare data is not available for this service">NR fare not available</span>';
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="National Rail fare data is not available for this service">NR fare not available</span>';
|
||||||
|
} else if (eurostarRefreshPending) {
|
||||||
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="Checking exact Eurostar data for this service">Checking Eurostar price</span>';
|
||||||
} else {
|
} else {
|
||||||
totalSpan.innerHTML = '<span class="text-xs text-muted" title="No Eurostar price found for this service — it may be sold out in the selected class. Check eurostar.com.">No Eurostar price</span>';
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="' + eurostarMissingTitle(row, esClass) + ' Check eurostar.com.">' + eurostarMissingText(row, esClass) + '</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -620,6 +646,8 @@
|
||||||
|
|
||||||
function startTimetableRefresh() {
|
function startTimetableRefresh() {
|
||||||
if (!HAS_PROVISIONAL_TIMETABLE || !TIMETABLE_REFRESH_URL || !window.EventSource) return;
|
if (!HAS_PROVISIONAL_TIMETABLE || !TIMETABLE_REFRESH_URL || !window.EventSource) return;
|
||||||
|
eurostarRefreshPending = true;
|
||||||
|
updateDisplay();
|
||||||
var source = new EventSource(TIMETABLE_REFRESH_URL);
|
var source = new EventSource(TIMETABLE_REFRESH_URL);
|
||||||
source.onmessage = function(event) {
|
source.onmessage = function(event) {
|
||||||
var msg = JSON.parse(event.data);
|
var msg = JSON.parse(event.data);
|
||||||
|
|
@ -633,11 +661,15 @@
|
||||||
mergeWalkonFares(msg.section, msg.fares);
|
mergeWalkonFares(msg.section, msg.fares);
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
} else if (msg.type === 'done' || msg.type === 'error') {
|
} else if (msg.type === 'done' || msg.type === 'error') {
|
||||||
|
eurostarRefreshPending = false;
|
||||||
source.close();
|
source.close();
|
||||||
|
updateDisplay();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
source.onerror = function() {
|
source.onerror = function() {
|
||||||
|
eurostarRefreshPending = false;
|
||||||
source.close();
|
source.close();
|
||||||
|
updateDisplay();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@
|
||||||
let ADVANCE_STREAM_URLS = {};
|
let ADVANCE_STREAM_URLS = {};
|
||||||
let TIMETABLE_REFRESH_URL = null;
|
let TIMETABLE_REFRESH_URL = null;
|
||||||
let HAS_PROVISIONAL_TIMETABLE = false;
|
let HAS_PROVISIONAL_TIMETABLE = false;
|
||||||
|
let eurostarRefreshPending = false;
|
||||||
let cachedAdvanceFares = null;
|
let cachedAdvanceFares = null;
|
||||||
let currentNrClasses = {{ nr_classes_json | safe }};
|
let currentNrClasses = {{ nr_classes_json | safe }};
|
||||||
let currentEsClasses = {{ es_classes_json | safe }};
|
let currentEsClasses = {{ es_classes_json | safe }};
|
||||||
|
|
@ -260,11 +261,34 @@
|
||||||
var row = TRIP_FARES[rowKey];
|
var row = TRIP_FARES[rowKey];
|
||||||
if (rowKey !== key && row.eurostar_key !== key) continue;
|
if (rowKey !== key && row.eurostar_key !== key) continue;
|
||||||
if (prices[key].es_standard) row.es_standard = prices[key].es_standard;
|
if (prices[key].es_standard) row.es_standard = prices[key].es_standard;
|
||||||
|
if (prices[key].es_standard_status !== undefined) row.es_standard_status = prices[key].es_standard_status;
|
||||||
if (prices[key].es_plus) row.es_plus = prices[key].es_plus;
|
if (prices[key].es_plus) row.es_plus = prices[key].es_plus;
|
||||||
|
if (prices[key].es_plus_status !== undefined) row.es_plus_status = prices[key].es_plus_status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function eurostarMissingText(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (status === 'sold_out') return 'Eurostar sold out';
|
||||||
|
if (status === 'price_not_returned') return 'No Eurostar price returned';
|
||||||
|
return 'No Eurostar price returned';
|
||||||
|
}
|
||||||
|
|
||||||
|
function eurostarMissingTitle(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (status === 'sold_out') return 'Eurostar returned 0 seats at this price for the selected class.';
|
||||||
|
if (status === 'price_not_returned') return 'Eurostar returned this service without a price for the selected class.';
|
||||||
|
return 'Eurostar did not return a price for the selected class. Check eurostar.com.';
|
||||||
|
}
|
||||||
|
|
||||||
|
function eurostarMissingFareHtml(row, esClass) {
|
||||||
|
var status = esClass === 'standard' ? row.es_standard_status : row.es_plus_status;
|
||||||
|
if (eurostarRefreshPending && !status) return '<span class="text-sm text-muted">checking</span>';
|
||||||
|
if (status === 'sold_out') return '<span class="text-sm text-muted">sold out</span>';
|
||||||
|
return '<span class="text-sm text-muted">–</span>';
|
||||||
|
}
|
||||||
|
|
||||||
function sectionNeedsAdvance(sectionId) {
|
function sectionNeedsAdvance(sectionId) {
|
||||||
var nrClass = currentNrClasses[sectionId] || 'walkon';
|
var nrClass = currentNrClasses[sectionId] || 'walkon';
|
||||||
for (var key in TRIP_FARES) {
|
for (var key in TRIP_FARES) {
|
||||||
|
|
@ -401,11 +425,11 @@
|
||||||
var esStdEl = tr.querySelector('.es-standard');
|
var esStdEl = tr.querySelector('.es-standard');
|
||||||
var esPlusEl = tr.querySelector('.es-plus');
|
var esPlusEl = tr.querySelector('.es-plus');
|
||||||
if (esStdEl) {
|
if (esStdEl) {
|
||||||
esStdEl.innerHTML = '<span class="text-xs text-muted">Std</span> ' + (row.es_standard ? fareHtml(row.es_standard) : '<span class="text-sm text-muted">–</span>');
|
esStdEl.innerHTML = '<span class="text-xs text-muted">Std</span> ' + (row.es_standard ? fareHtml(row.es_standard) : eurostarMissingFareHtml(row, 'standard'));
|
||||||
esStdEl.classList.toggle('fare-inactive', esClass !== 'standard');
|
esStdEl.classList.toggle('fare-inactive', esClass !== 'standard');
|
||||||
}
|
}
|
||||||
if (esPlusEl) {
|
if (esPlusEl) {
|
||||||
esPlusEl.innerHTML = '<span class="text-xs text-muted">SP</span> ' + (row.es_plus ? fareHtml(row.es_plus) : '<span class="text-sm text-muted">–</span>');
|
esPlusEl.innerHTML = '<span class="text-xs text-muted">SP</span> ' + (row.es_plus ? fareHtml(row.es_plus) : eurostarMissingFareHtml(row, 'plus'));
|
||||||
esPlusEl.classList.toggle('fare-inactive', esClass !== 'plus');
|
esPlusEl.classList.toggle('fare-inactive', esClass !== 'plus');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,8 +450,10 @@
|
||||||
totalSpan.innerHTML = '';
|
totalSpan.innerHTML = '';
|
||||||
} else if (!nrFare) {
|
} else if (!nrFare) {
|
||||||
totalSpan.innerHTML = '<span class="text-xs text-muted" title="National Rail fare data is not available for this service">NR fare not available</span>';
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="National Rail fare data is not available for this service">NR fare not available</span>';
|
||||||
|
} else if (eurostarRefreshPending) {
|
||||||
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="Checking exact Eurostar data for this service">Checking Eurostar price</span>';
|
||||||
} else {
|
} else {
|
||||||
totalSpan.innerHTML = '<span class="text-xs text-muted" title="No Eurostar price found for this service — it may be sold out in the selected class. Check eurostar.com.">No Eurostar price</span>';
|
totalSpan.innerHTML = '<span class="text-xs text-muted" title="' + eurostarMissingTitle(row, esClass) + ' Check eurostar.com.">' + eurostarMissingText(row, esClass) + '</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -618,6 +644,8 @@
|
||||||
|
|
||||||
function startTimetableRefresh() {
|
function startTimetableRefresh() {
|
||||||
if (!HAS_PROVISIONAL_TIMETABLE || !TIMETABLE_REFRESH_URL || !window.EventSource) return;
|
if (!HAS_PROVISIONAL_TIMETABLE || !TIMETABLE_REFRESH_URL || !window.EventSource) return;
|
||||||
|
eurostarRefreshPending = true;
|
||||||
|
updateDisplay();
|
||||||
var source = new EventSource(TIMETABLE_REFRESH_URL);
|
var source = new EventSource(TIMETABLE_REFRESH_URL);
|
||||||
source.onmessage = function(event) {
|
source.onmessage = function(event) {
|
||||||
var msg = JSON.parse(event.data);
|
var msg = JSON.parse(event.data);
|
||||||
|
|
@ -631,11 +659,15 @@
|
||||||
mergeWalkonFares(msg.section, msg.fares);
|
mergeWalkonFares(msg.section, msg.fares);
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
} else if (msg.type === 'done' || msg.type === 'error') {
|
} else if (msg.type === 'done' || msg.type === 'error') {
|
||||||
|
eurostarRefreshPending = false;
|
||||||
source.close();
|
source.close();
|
||||||
|
updateDisplay();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
source.onerror = function() {
|
source.onerror = function() {
|
||||||
|
eurostarRefreshPending = false;
|
||||||
source.close();
|
source.close();
|
||||||
|
updateDisplay();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -655,6 +687,7 @@
|
||||||
function finaliseResults(msg) {
|
function finaliseResults(msg) {
|
||||||
TIMETABLE_REFRESH_URL = msg.timetable_refresh_url || null;
|
TIMETABLE_REFRESH_URL = msg.timetable_refresh_url || null;
|
||||||
HAS_PROVISIONAL_TIMETABLE = msg.provisional_timetable || false;
|
HAS_PROVISIONAL_TIMETABLE = msg.provisional_timetable || false;
|
||||||
|
eurostarRefreshPending = HAS_PROVISIONAL_TIMETABLE && !!TIMETABLE_REFRESH_URL && !!window.EventSource;
|
||||||
var summaryEl = document.getElementById('results-summary');
|
var summaryEl = document.getElementById('results-summary');
|
||||||
if (summaryEl && msg.summary_html) summaryEl.innerHTML = msg.summary_html;
|
if (summaryEl && msg.summary_html) summaryEl.innerHTML = msg.summary_html;
|
||||||
initialiseResultsPage();
|
initialiseResultsPage();
|
||||||
|
|
|
||||||
|
|
@ -183,9 +183,30 @@ def test_results_can_render_from_weekday_timetable_cache(monkeypatch: Any) -> No
|
||||||
assert "/api/results_refresh/BRI/paris/2026-06-22" in html
|
assert "/api/results_refresh/BRI/paris/2026-06-22" in html
|
||||||
assert "refreshFullResults()" in html
|
assert "refreshFullResults()" in html
|
||||||
assert "window.location.reload()" not in html
|
assert "window.location.reload()" not in html
|
||||||
|
assert "Checking Eurostar price" in html
|
||||||
assert "Eurostar prices not yet available" not in html
|
assert "Eurostar prices not yet available" not in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_eurostar_price_status_distinguishes_sold_out() -> None:
|
||||||
|
prices = app_module._eurostar_prices_by_row(
|
||||||
|
"outbound",
|
||||||
|
"outbound",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"depart_st_pancras": "10:01",
|
||||||
|
"price": None,
|
||||||
|
"seats": 0,
|
||||||
|
"plus_price": None,
|
||||||
|
"plus_seats": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert prices["outbound:10:01"]["es_standard"] is None
|
||||||
|
assert prices["outbound:10:01"]["es_standard_status"] == "sold_out"
|
||||||
|
assert prices["outbound:10:01"]["es_plus_status"] == "price_not_returned"
|
||||||
|
|
||||||
|
|
||||||
def test_results_refresh_reloads_when_exact_timetable_differs(monkeypatch: Any) -> None:
|
def test_results_refresh_reloads_when_exact_timetable_differs(monkeypatch: Any) -> None:
|
||||||
travel_date = "2026-06-22"
|
travel_date = "2026-06-22"
|
||||||
cache: dict[str, Any] = {
|
cache: dict[str, Any] = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue