Improvements
This commit is contained in:
parent
7599f655ad
commit
b6f0c88320
|
@ -19,6 +19,7 @@ ports = {
|
||||||
"CHERBOURG": "FRCER",
|
"CHERBOURG": "FRCER",
|
||||||
"ST MALO": "FRSML",
|
"ST MALO": "FRSML",
|
||||||
"LE HAVRE": "FRLEH",
|
"LE HAVRE": "FRLEH",
|
||||||
|
"ROSCOFF": "FRROS",
|
||||||
}
|
}
|
||||||
|
|
||||||
port_lookup = {code: name for name, code in ports.items()}
|
port_lookup = {code: name for name, code in ports.items()}
|
||||||
|
|
21
ferry/api.py
21
ferry/api.py
|
@ -3,6 +3,7 @@
|
||||||
from typing import Any, TypedDict
|
from typing import Any, TypedDict
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
from . import Vehicle
|
from . import Vehicle
|
||||||
|
|
||||||
|
@ -19,17 +20,18 @@ class VehicleDict(TypedDict):
|
||||||
registrations: list[str]
|
registrations: list[str]
|
||||||
height: int
|
height: int
|
||||||
length: int
|
length: int
|
||||||
extras: dict[str, None]
|
extras: dict[str, None | bool]
|
||||||
|
|
||||||
|
|
||||||
def vehicle_dict(v: Vehicle) -> VehicleDict:
|
def vehicle_dict(v: Vehicle, rear_mounted_bike_carrier: bool = False) -> VehicleDict:
|
||||||
"""Return vehicle detail in the format for the Brittany Ferries API."""
|
"""Return vehicle detail in the format for the Brittany Ferries API."""
|
||||||
|
rmbc = True if rear_mounted_bike_carrier else None
|
||||||
return {
|
return {
|
||||||
"type": v.type,
|
"type": v.type,
|
||||||
"registrations": [v.registration],
|
"registrations": [v.registration],
|
||||||
"height": v.height,
|
"height": v.height,
|
||||||
"length": v.length,
|
"length": v.length,
|
||||||
"extras": {"rearMountedBikeCarrier": None},
|
"extras": {"rearMountedBikeCarrier": rmbc},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@ def get_prices(
|
||||||
vehicle: Vehicle,
|
vehicle: Vehicle,
|
||||||
adults: int = 2,
|
adults: int = 2,
|
||||||
small_dogs: int = 1,
|
small_dogs: int = 1,
|
||||||
|
rear_mounted_bike_carrier: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Call Brittany Ferries API to get details of crossings."""
|
"""Call Brittany Ferries API to get details of crossings."""
|
||||||
url = api_root_url + "crossing/prices"
|
url = api_root_url + "crossing/prices"
|
||||||
|
@ -49,7 +52,7 @@ def get_prices(
|
||||||
"bookingReference": None,
|
"bookingReference": None,
|
||||||
"pets": {"smallDogs": small_dogs, "largeDogs": 0, "cats": 0},
|
"pets": {"smallDogs": small_dogs, "largeDogs": 0, "cats": 0},
|
||||||
"passengers": {"adults": adults, "children": 0, "infants": 0},
|
"passengers": {"adults": adults, "children": 0, "infants": 0},
|
||||||
"vehicle": vehicle_dict(vehicle),
|
"vehicle": vehicle_dict(vehicle, rear_mounted_bike_carrier),
|
||||||
"departurePort": departure_port,
|
"departurePort": departure_port,
|
||||||
"arrivalPort": arrival_port,
|
"arrivalPort": arrival_port,
|
||||||
"disability": None,
|
"disability": None,
|
||||||
|
@ -60,6 +63,8 @@ def get_prices(
|
||||||
|
|
||||||
r = requests.post(url, json=post_data, headers=headers)
|
r = requests.post(url, json=post_data, headers=headers)
|
||||||
data: dict[str, Any] = r.json()
|
data: dict[str, Any] = r.json()
|
||||||
|
if "crossings" not in data:
|
||||||
|
print(json.dumps(data, indent=2))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +76,7 @@ def get_accommodations(
|
||||||
vehicle: Vehicle,
|
vehicle: Vehicle,
|
||||||
adults: int,
|
adults: int,
|
||||||
small_dogs: int,
|
small_dogs: int,
|
||||||
|
rear_mounted_bike_carrier: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Grab cabin details."""
|
"""Grab cabin details."""
|
||||||
url = api_root_url + "crossing/accommodations"
|
url = api_root_url + "crossing/accommodations"
|
||||||
|
@ -81,7 +87,7 @@ def get_accommodations(
|
||||||
"departureDate": departure_date,
|
"departureDate": departure_date,
|
||||||
"passengers": {"adults": adults, "children": 0, "infants": 0},
|
"passengers": {"adults": adults, "children": 0, "infants": 0},
|
||||||
"disability": None,
|
"disability": None,
|
||||||
"vehicle": vehicle_dict(vehicle),
|
"vehicle": vehicle_dict(vehicle, rear_mounted_bike_carrier),
|
||||||
"petCabinsNeeded": True,
|
"petCabinsNeeded": True,
|
||||||
"ticketTier": ticket_tier,
|
"ticketTier": ticket_tier,
|
||||||
"pets": {"smallDogs": small_dogs, "largeDogs": 0, "cats": 0},
|
"pets": {"smallDogs": small_dogs, "largeDogs": 0, "cats": 0},
|
||||||
|
@ -92,4 +98,9 @@ def get_accommodations(
|
||||||
json_data: dict[str, Any] = requests.post(
|
json_data: dict[str, Any] = requests.post(
|
||||||
url, json=post_data, headers=headers
|
url, json=post_data, headers=headers
|
||||||
).json()
|
).json()
|
||||||
|
if "crossings" not in json_data:
|
||||||
|
print()
|
||||||
|
print(json.dumps(post_data, indent=2))
|
||||||
|
print()
|
||||||
|
print(json.dumps(json_data, indent=2))
|
||||||
return json_data
|
return json_data
|
||||||
|
|
96
main.py
96
main.py
|
@ -8,6 +8,7 @@ import os.path
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import collections
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ routes = {
|
||||||
("PORTSMOUTH", "ST MALO"),
|
("PORTSMOUTH", "ST MALO"),
|
||||||
("PORTSMOUTH", "LE HAVRE"),
|
("PORTSMOUTH", "LE HAVRE"),
|
||||||
("POOLE", "CHERBOURG"),
|
("POOLE", "CHERBOURG"),
|
||||||
|
("PLYMOUTH", "ROSCOFF"),
|
||||||
],
|
],
|
||||||
"return": [
|
"return": [
|
||||||
("CAEN", "PORTSMOUTH"),
|
("CAEN", "PORTSMOUTH"),
|
||||||
|
@ -44,6 +46,7 @@ routes = {
|
||||||
("ST MALO", "PORTSMOUTH"),
|
("ST MALO", "PORTSMOUTH"),
|
||||||
("LE HAVRE", "PORTSMOUTH"),
|
("LE HAVRE", "PORTSMOUTH"),
|
||||||
("CHERBOURG", "POOLE"),
|
("CHERBOURG", "POOLE"),
|
||||||
|
("ROSCOFF", "PLYMOUTH"),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +142,13 @@ def start() -> Response | str:
|
||||||
return flask.render_template("index.html")
|
return flask.render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
def cabins_url(dep: str, arr: str, crossing: dict[str, Any], ticket_tier: str) -> str:
|
def cabins_url(
|
||||||
|
dep: str,
|
||||||
|
arr: str,
|
||||||
|
crossing: dict[str, Any],
|
||||||
|
ticket_tier: str,
|
||||||
|
rear_mounted_bike_carrier: bool,
|
||||||
|
) -> str:
|
||||||
"""Generate a URL for the cabins on a given crossing."""
|
"""Generate a URL for the cabins on a given crossing."""
|
||||||
dt = datetime.fromisoformat(crossing["departureDateTime"]["iso"])
|
dt = datetime.fromisoformat(crossing["departureDateTime"]["iso"])
|
||||||
utc_dt = dt.astimezone(pytz.utc)
|
utc_dt = dt.astimezone(pytz.utc)
|
||||||
|
@ -155,6 +164,7 @@ def cabins_url(dep: str, arr: str, crossing: dict[str, Any], ticket_tier: str) -
|
||||||
ticket_tier=ticket_tier,
|
ticket_tier=ticket_tier,
|
||||||
adults=adults_str,
|
adults=adults_str,
|
||||||
small_dogs=small_dogs_str,
|
small_dogs=small_dogs_str,
|
||||||
|
rear_mounted_bike_carrier=("true" if rear_mounted_bike_carrier else None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,9 +205,12 @@ def get_prices_with_cache(
|
||||||
adults: int,
|
adults: int,
|
||||||
small_dogs: int,
|
small_dogs: int,
|
||||||
refresh: bool = False,
|
refresh: bool = False,
|
||||||
|
rear_mounted_bike_carrier: bool = False,
|
||||||
) -> PriceData:
|
) -> PriceData:
|
||||||
"""Get price data using cache."""
|
"""Get price data using cache."""
|
||||||
params = f"{direction}_{start}_{end}_{adults}_{small_dogs}"
|
params = (
|
||||||
|
f"{direction}_{start}_{end}_{adults}_{small_dogs}_{rear_mounted_bike_carrier}"
|
||||||
|
)
|
||||||
if not refresh:
|
if not refresh:
|
||||||
data = check_cache_for_prices(params)
|
data = check_cache_for_prices(params)
|
||||||
if data:
|
if data:
|
||||||
|
@ -217,6 +230,7 @@ def get_prices_with_cache(
|
||||||
vehicle,
|
vehicle,
|
||||||
adults,
|
adults,
|
||||||
small_dogs,
|
small_dogs,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)["crossings"],
|
)["crossings"],
|
||||||
)
|
)
|
||||||
for dep, arr in selection
|
for dep, arr in selection
|
||||||
|
@ -228,7 +242,10 @@ def get_prices_with_cache(
|
||||||
return all_data
|
return all_data
|
||||||
|
|
||||||
|
|
||||||
def read_pax() -> dict[str, int]:
|
Pax = collections.namedtuple("Pax", ["adults", "small_dogs"])
|
||||||
|
|
||||||
|
|
||||||
|
def read_pax() -> Pax:
|
||||||
"""Get the number of adults and dogs that are travelling."""
|
"""Get the number of adults and dogs that are travelling."""
|
||||||
config_adults = int(ferry_config.get("pax", "adults"))
|
config_adults = int(ferry_config.get("pax", "adults"))
|
||||||
config_dogs = int(ferry_config.get("pax", "dogs"))
|
config_dogs = int(ferry_config.get("pax", "dogs"))
|
||||||
|
@ -239,7 +256,14 @@ def read_pax() -> dict[str, int]:
|
||||||
small_dogs_str = flask.request.args.get("small_dogs")
|
small_dogs_str = flask.request.args.get("small_dogs")
|
||||||
small_dogs = int(small_dogs_str) if small_dogs_str else config_dogs
|
small_dogs = int(small_dogs_str) if small_dogs_str else config_dogs
|
||||||
|
|
||||||
return {"adults": adults, "small_dogs": small_dogs}
|
return Pax(adults=adults, small_dogs=small_dogs)
|
||||||
|
|
||||||
|
|
||||||
|
pax_options = [
|
||||||
|
{"pax": Pax(adults=1, small_dogs=0), "label": "1 adult"},
|
||||||
|
{"pax": Pax(adults=2, small_dogs=0), "label": "2 adults"},
|
||||||
|
{"pax": Pax(adults=2, small_dogs=1), "label": "2 adults and a dog"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def build_outbound(section: str) -> str:
|
def build_outbound(section: str) -> str:
|
||||||
|
@ -247,6 +271,9 @@ def build_outbound(section: str) -> str:
|
||||||
start = ferry_config.get(section, "from")
|
start = ferry_config.get(section, "from")
|
||||||
end = ferry_config.get(section, "to")
|
end = ferry_config.get(section, "to")
|
||||||
refresh = bool(flask.request.args.get("refresh"))
|
refresh = bool(flask.request.args.get("refresh"))
|
||||||
|
rear_mounted_bike_carrier = (
|
||||||
|
flask.request.args.get("rear_mounted_bike_carrier") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
pax = read_pax()
|
pax = read_pax()
|
||||||
|
|
||||||
|
@ -257,9 +284,10 @@ def build_outbound(section: str) -> str:
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
routes["outbound"],
|
routes["outbound"],
|
||||||
pax["adults"],
|
pax.adults,
|
||||||
pax["small_dogs"],
|
pax.small_dogs,
|
||||||
refresh,
|
refresh,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
|
@ -274,6 +302,9 @@ def build_outbound(section: str) -> str:
|
||||||
get_duration=get_duration,
|
get_duration=get_duration,
|
||||||
time_delta=-60,
|
time_delta=-60,
|
||||||
format_pet_options=format_pet_options,
|
format_pet_options=format_pet_options,
|
||||||
|
pax=pax,
|
||||||
|
pax_options=pax_options,
|
||||||
|
rear_mounted_bike_carrier=rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,6 +314,9 @@ def build_return(section: str) -> str:
|
||||||
end = ferry_config.get(section, "to")
|
end = ferry_config.get(section, "to")
|
||||||
refresh = bool(flask.request.args.get("refresh"))
|
refresh = bool(flask.request.args.get("refresh"))
|
||||||
pax = read_pax()
|
pax = read_pax()
|
||||||
|
rear_mounted_bike_carrier = (
|
||||||
|
flask.request.args.get("rear_mounted_bike_carrier") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
direction = section[:-1] if section[-1].isdigit() else section
|
direction = section[:-1] if section[-1].isdigit() else section
|
||||||
|
|
||||||
|
@ -291,9 +325,10 @@ def build_return(section: str) -> str:
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
routes["return"],
|
routes["return"],
|
||||||
pax["adults"],
|
pax.adults,
|
||||||
pax["small_dogs"],
|
pax.small_dogs,
|
||||||
refresh,
|
refresh,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
|
@ -308,6 +343,9 @@ def build_return(section: str) -> str:
|
||||||
get_duration=get_duration,
|
get_duration=get_duration,
|
||||||
time_delta=60,
|
time_delta=60,
|
||||||
format_pet_options=format_pet_options,
|
format_pet_options=format_pet_options,
|
||||||
|
pax=pax,
|
||||||
|
pax_options=pax_options,
|
||||||
|
rear_mounted_bike_carrier=rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -350,11 +388,16 @@ def format_pet_options(o: dict[str, bool]) -> list[str]:
|
||||||
|
|
||||||
|
|
||||||
def get_accommodations_with_cache(
|
def get_accommodations_with_cache(
|
||||||
dep: str, arr: str, d: str, ticket_tier: str, refresh: bool = False
|
dep: str,
|
||||||
|
arr: str,
|
||||||
|
d: str,
|
||||||
|
ticket_tier: str,
|
||||||
|
refresh: bool = False,
|
||||||
|
rear_mounted_bike_carrier: bool = False,
|
||||||
) -> dict[str, list[dict[str, Any]]]:
|
) -> dict[str, list[dict[str, Any]]]:
|
||||||
pax = read_pax()
|
pax = read_pax()
|
||||||
|
|
||||||
params = f"{dep}_{arr}_{d}_{ticket_tier}_{pax['adults']}_{pax['small_dogs']}"
|
params = f"{dep}_{arr}_{d}_{ticket_tier}_{pax.adults}_{pax.small_dogs}_{rear_mounted_bike_carrier}"
|
||||||
existing_files = os.listdir(cache_location())
|
existing_files = os.listdir(cache_location())
|
||||||
existing = [f for f in existing_files if f.endswith(params + ".json")]
|
existing = [f for f in existing_files if f.endswith(params + ".json")]
|
||||||
if not refresh and existing:
|
if not refresh and existing:
|
||||||
|
@ -372,7 +415,14 @@ def get_accommodations_with_cache(
|
||||||
vehicle = vehicle_from_config(ferry_config)
|
vehicle = vehicle_from_config(ferry_config)
|
||||||
filename = cache_filename(params)
|
filename = cache_filename(params)
|
||||||
data = get_accommodations(
|
data = get_accommodations(
|
||||||
dep, arr, d, ticket_tier, vehicle, pax["adults"], pax["small_dogs"]
|
dep,
|
||||||
|
arr,
|
||||||
|
d,
|
||||||
|
ticket_tier,
|
||||||
|
vehicle,
|
||||||
|
pax.adults,
|
||||||
|
pax.small_dogs,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(filename, "w") as out:
|
with open(filename, "w") as out:
|
||||||
|
@ -451,14 +501,29 @@ def cabins(
|
||||||
time_delta = -60
|
time_delta = -60
|
||||||
|
|
||||||
pax = read_pax()
|
pax = read_pax()
|
||||||
|
rear_mounted_bike_carrier = (
|
||||||
|
flask.request.args.get("rear_mounted_bike_carrier") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
prices = get_prices_with_cache(
|
prices = get_prices_with_cache(
|
||||||
direction, start, end, routes[direction], pax["adults"], pax["small_dogs"]
|
direction,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
routes[direction],
|
||||||
|
pax.adults,
|
||||||
|
pax.small_dogs,
|
||||||
|
False,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
crossing = lookup_sailing_id(prices, sailing_id)
|
crossing = lookup_sailing_id(prices, sailing_id)
|
||||||
|
|
||||||
cabin_data = get_accommodations_with_cache(
|
cabin_data = get_accommodations_with_cache(
|
||||||
departure_port, arrival_port, departure_date, ticket_tier
|
departure_port,
|
||||||
|
arrival_port,
|
||||||
|
departure_date,
|
||||||
|
ticket_tier,
|
||||||
|
False,
|
||||||
|
rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
accommodations = [
|
accommodations = [
|
||||||
a
|
a
|
||||||
|
@ -467,6 +532,8 @@ def cabins(
|
||||||
# and "Inside" not in a["description"]
|
# and "Inside" not in a["description"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
pax_labels = {i["pax"]: i["label"] for i in pax_options}
|
||||||
|
|
||||||
dep = dateutil.parser.isoparse(departure_date)
|
dep = dateutil.parser.isoparse(departure_date)
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
|
@ -483,6 +550,9 @@ def cabins(
|
||||||
time_delta=time_delta,
|
time_delta=time_delta,
|
||||||
format_pet_options=format_pet_options,
|
format_pet_options=format_pet_options,
|
||||||
section=section,
|
section=section,
|
||||||
|
pax=pax,
|
||||||
|
pax_label=pax_labels[pax],
|
||||||
|
rear_mounted_bike_carrier=rear_mounted_bike_carrier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,27 @@ a:link {
|
||||||
<p><a href="{{ url_for(other + "_page") }}">{{ other }}</a></p>
|
<p><a href="{{ url_for(other + "_page") }}">{{ other }}</a></p>
|
||||||
#}
|
#}
|
||||||
|
|
||||||
<ul>
|
<p>
|
||||||
<li><a href="?adults=2&small_dogs=1">2 adults and a dog</a></li>
|
Passengers:
|
||||||
<li><a href="?adults=2&small_dogs=0">2 adults</a></li>
|
{% for o in pax_options %}
|
||||||
<li><a href="?adults=1&small_dogs=0">1 adult</a></li>
|
{% if o.pax == pax %}
|
||||||
</ul>
|
<strong>{{ o.label }}</strong>
|
||||||
|
{% else %}
|
||||||
|
<a href="?adults={{ o.pax.adults}}&small_dogs={{ o.pax.small_dogs }}">{{ o.label }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if not loop.last %}|{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if rear_mounted_bike_carrier %}
|
||||||
|
<p>Bike carrier: <strong>yes</strong> | <a href="{{ url_for(request.endpoint) }}">no</a></p>
|
||||||
|
{% else %}
|
||||||
|
<p>Bike carrier:
|
||||||
|
<a href="{{ url_for(request.endpoint, rear_mounted_bike_carrier='true') }}">yes</a>
|
||||||
|
|
|
||||||
|
<strong>no</strong>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if extra_routes %}
|
{% if extra_routes %}
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -34,7 +34,19 @@ a:link {
|
||||||
|
|
||||||
<p>{{ departure_date.strftime("%A, %d %B %Y %H:%M UTC") }} {{ ticket_tier }}</p>
|
<p>{{ departure_date.strftime("%A, %d %B %Y %H:%M UTC") }} {{ ticket_tier }}</p>
|
||||||
|
|
||||||
<p><a href="{{ url_for(section + "_page") }}">back to sailings</a></p>
|
<p><a href="{{ url_for(section + "_page", rear_mounted_bike_carrier=request.args.get("rear_mounted_bike_carrier")) }}">back to sailings</a></p>
|
||||||
|
|
||||||
|
<p>Passengers: {{ pax_label }}</p>
|
||||||
|
|
||||||
|
{% if rear_mounted_bike_carrier %}
|
||||||
|
<p>Bike carrier: <strong>yes</strong> | <a href="{{ url_for(request.endpoint, rear_mounted_bike_carrier=None, **request.view_args) }}">no</a></p>
|
||||||
|
{% else %}
|
||||||
|
<p>Bike carrier:
|
||||||
|
<a href="{{ url_for(request.endpoint, rear_mounted_bike_carrier='true', **request.view_args) }}">yes</a>
|
||||||
|
|
|
||||||
|
<strong>no</strong>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<table class="table w-auto">
|
<table class="table w-auto">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ url_for("outbound1_page") }}">Outbound: 29 September</a>
|
<li><a href="{{ url_for("outbound1_page") }}">Outbound: 29 September</a>
|
||||||
|
<li><a href="{{ url_for("outbound2_page") }}">Outbound 9 October</a>
|
||||||
<li><a href="{{ url_for("return1_page") }}">Return: 6 October</a>
|
<li><a href="{{ url_for("return1_page") }}">Return: 6 October</a>
|
||||||
|
<li><a href="{{ url_for("return2_page") }}">Return: 13 October</a>
|
||||||
{#
|
{#
|
||||||
<li><a href="{{ url_for("outbound3_page") }}">Outbound: 29 September</a>
|
<li><a href="{{ url_for("outbound3_page") }}">Outbound: 29 September</a>
|
||||||
<li><a href="{{ url_for("return2_page") }}">Return: </a>
|
<li><a href="{{ url_for("return2_page") }}">Return: </a>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="text-nowrap">
|
<td class="text-nowrap">
|
||||||
{% if crossing.economyPrice %}
|
{% if crossing.economyPrice %}
|
||||||
<a href="{{ cabins_url(dep, arr, crossing, "ECONOMY") }}">
|
<a href="{{ cabins_url(dep, arr, crossing, "ECONOMY", rear_mounted_bike_carrier) }}">
|
||||||
£{{ crossing.economyPrice.amount }}
|
£{{ crossing.economyPrice.amount }}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -52,12 +52,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-nowrap">
|
<td class="text-nowrap">
|
||||||
<a href="{{ cabins_url(dep, arr, crossing, "STANDARD") }}">
|
<a href="{{ cabins_url(dep, arr, crossing, "STANDARD", rear_mounted_bike_carrier) }}">
|
||||||
£{{ crossing.standardPrice.amount }}
|
£{{ crossing.standardPrice.amount }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-nowrap">
|
<td class="text-nowrap">
|
||||||
<a href="{{ cabins_url(dep, arr, crossing, "FLEXI") }}">
|
<a href="{{ cabins_url(dep, arr, crossing, "FLEXI", rear_mounted_bike_carrier) }}">
|
||||||
£{{ crossing.flexiPrice.amount }}
|
£{{ crossing.flexiPrice.amount }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
Loading…
Reference in a new issue