Update
This commit is contained in:
parent
c26383c9c0
commit
a5195cba1a
7 changed files with 263 additions and 71 deletions
271
main.py
271
main.py
|
|
@ -6,22 +6,46 @@ import json
|
|||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import dateutil.parser
|
||||
import flask
|
||||
import pytz
|
||||
import werkzeug.exceptions
|
||||
from werkzeug.debug.tbtools import get_current_traceback
|
||||
import werkzeug
|
||||
from werkzeug.debug.tbtools import DebugTraceback
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
import ferry
|
||||
from ferry.api import get_accommodations, get_prices
|
||||
from ferry.read_config import ferry_config, vehicle_from_config
|
||||
|
||||
# import werkzeug.exceptions
|
||||
# from werkzeug.debug.tbtools import DebugTraceback
|
||||
# from werkzeug.debug.tbtools import get_current_traceback
|
||||
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.debug = False
|
||||
app.debug = True
|
||||
|
||||
routes = {
|
||||
"outbound": [
|
||||
("PORTSMOUTH", "CAEN"),
|
||||
("PORTSMOUTH", "CHERBOURG"),
|
||||
("PORTSMOUTH", "ST MALO"),
|
||||
("PORTSMOUTH", "LE HAVRE"),
|
||||
("POOLE", "CHERBOURG"),
|
||||
],
|
||||
"return": [
|
||||
("CAEN", "PORTSMOUTH"),
|
||||
("CHERBOURG", "PORTSMOUTH"),
|
||||
("ST MALO", "PORTSMOUTH"),
|
||||
("LE HAVRE", "PORTSMOUTH"),
|
||||
("CHERBOURG", "POOLE"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def cache_location() -> str:
|
||||
|
|
@ -60,14 +84,26 @@ def get_duration(depart: str, arrive: str, time_delta: int) -> str:
|
|||
|
||||
|
||||
@app.errorhandler(werkzeug.exceptions.InternalServerError)
|
||||
def exception_handler(e):
|
||||
tb = get_current_traceback()
|
||||
last_frame = next(frame for frame in reversed(tb.frames) if not frame.is_library)
|
||||
last_frame_args = inspect.getargs(last_frame.code)
|
||||
def exception_handler(e: Exception) -> tuple[str, int]:
|
||||
"""Show error page."""
|
||||
exec_type, exc_value, current_traceback = sys.exc_info()
|
||||
assert exc_value
|
||||
tb = DebugTraceback(exc_value)
|
||||
|
||||
summary = tb.render_traceback_html(include_title=False)
|
||||
exc_lines = list(tb._te.format_exception_only())
|
||||
|
||||
frames = [f for f, lineno in traceback.walk_tb(current_traceback)]
|
||||
last_frame = frames[-1]
|
||||
last_frame_args = inspect.getargs(last_frame.f_code)
|
||||
|
||||
return (
|
||||
flask.render_template(
|
||||
"show_error.html",
|
||||
tb=tb,
|
||||
plaintext=tb.render_traceback_text(),
|
||||
exception="".join(exc_lines),
|
||||
exception_type=tb._te.exc_type.__name__,
|
||||
summary=summary,
|
||||
last_frame=last_frame,
|
||||
last_frame_args=last_frame_args,
|
||||
),
|
||||
|
|
@ -75,6 +111,23 @@ def exception_handler(e):
|
|||
)
|
||||
|
||||
|
||||
# @app.errorhandler(werkzeug.exceptions.InternalServerError)
|
||||
# def exception_handler(e):
|
||||
# # tb = get_current_traceback()
|
||||
# tb = DebugTraceback()
|
||||
# last_frame = next(frame for frame in reversed(tb.frames) if not frame.is_library)
|
||||
# last_frame_args = inspect.getargs(last_frame.code)
|
||||
# return (
|
||||
# flask.render_template(
|
||||
# "show_error.html",
|
||||
# tb=tb,
|
||||
# last_frame=last_frame,
|
||||
# last_frame_args=last_frame_args,
|
||||
# ),
|
||||
# 500,
|
||||
# )
|
||||
|
||||
|
||||
def parse_date(d: str) -> date:
|
||||
"""Parse a date from a string in ISO format."""
|
||||
return datetime.strptime(d, "%Y-%m-%d").date()
|
||||
|
|
@ -93,6 +146,7 @@ def cabins_url(dep: str, arr: str, crossing: dict[str, Any], ticket_tier: str) -
|
|||
|
||||
return flask.url_for(
|
||||
"cabins",
|
||||
sailing_id=int(crossing["sailingId"]),
|
||||
departure_port=ferry.ports[dep],
|
||||
arrival_port=ferry.ports[arr],
|
||||
departure_date=utc_dt.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
|
||||
|
|
@ -106,7 +160,7 @@ def get_days_until_start() -> int:
|
|||
return (start - date.today()).days
|
||||
|
||||
|
||||
PriceData = list[tuple[str, str, dict[str, Any]]]
|
||||
PriceData = list[tuple[str, str, list[dict[str, Any]]]]
|
||||
|
||||
|
||||
def check_cache_for_prices(params: str) -> PriceData | None:
|
||||
|
|
@ -130,14 +184,16 @@ def check_cache_for_prices(params: str) -> PriceData | None:
|
|||
|
||||
|
||||
def get_prices_with_cache(
|
||||
name: str,
|
||||
direction: str,
|
||||
start: str,
|
||||
end: str,
|
||||
selection: list[tuple[str, str]],
|
||||
refresh: bool = False,
|
||||
adults: int = 2,
|
||||
small_dogs: int = 1,
|
||||
) -> PriceData:
|
||||
"""Get price data using cache."""
|
||||
params = f"{name}_{start}_{end}"
|
||||
params = f"{direction}_{start}_{end}_{adults}_{small_dogs}"
|
||||
if not refresh:
|
||||
data = check_cache_for_prices(params)
|
||||
if data:
|
||||
|
|
@ -149,9 +205,15 @@ def get_prices_with_cache(
|
|||
(
|
||||
dep,
|
||||
arr,
|
||||
get_prices(ferry.ports[dep], ferry.ports[arr], start, end, vehicle)[
|
||||
"crossings"
|
||||
],
|
||||
get_prices(
|
||||
ferry.ports[dep],
|
||||
ferry.ports[arr],
|
||||
start,
|
||||
end,
|
||||
vehicle,
|
||||
adults,
|
||||
small_dogs,
|
||||
)["crossings"],
|
||||
)
|
||||
for dep, arr in selection
|
||||
]
|
||||
|
|
@ -164,18 +226,21 @@ def get_prices_with_cache(
|
|||
|
||||
def build_outbound(section: str) -> str:
|
||||
"""Show all routes on one page."""
|
||||
selection = [
|
||||
("PORTSMOUTH", "CAEN"),
|
||||
("PORTSMOUTH", "CHERBOURG"),
|
||||
("PORTSMOUTH", "ST MALO"),
|
||||
# ("POOLE", "CHERBOURG"),
|
||||
]
|
||||
|
||||
start = ferry_config.get(section, "from")
|
||||
end = ferry_config.get(section, "to")
|
||||
refresh = bool(flask.request.args.get("refresh"))
|
||||
|
||||
all_data = get_prices_with_cache(section, start, end, selection, refresh)
|
||||
adults_str = flask.request.args.get("adults")
|
||||
adults = int(adults_str) if adults_str else 2
|
||||
|
||||
small_dogs_str = flask.request.args.get("small_dogs")
|
||||
small_dogs = int(small_dogs_str) if small_dogs_str else 2
|
||||
|
||||
direction = section[:-1] if section[-1].isdigit() else section
|
||||
|
||||
all_data = get_prices_with_cache(
|
||||
direction, start, end, routes["outbound"], refresh, adults, small_dogs
|
||||
)
|
||||
|
||||
return flask.render_template(
|
||||
"all_routes.html",
|
||||
|
|
@ -192,43 +257,23 @@ def build_outbound(section: str) -> str:
|
|||
)
|
||||
|
||||
|
||||
@app.route("/outbound1")
|
||||
def outbound1_page() -> str:
|
||||
return build_outbound("outbound1")
|
||||
|
||||
|
||||
@app.route("/outbound2")
|
||||
def outbound2_page() -> str:
|
||||
return build_outbound("outbound2")
|
||||
|
||||
|
||||
def format_pet_options(o: dict[str, bool]) -> list[str]:
|
||||
ret = []
|
||||
if o.get("petCabinAvailable"):
|
||||
ret.append("pet cabins")
|
||||
if o.get("smallKennelAvailable"):
|
||||
ret.append("small kennel")
|
||||
if o.get("stayInCarAvailable"):
|
||||
ret.append("pets stay in car")
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@app.route("/return")
|
||||
def return_page() -> str:
|
||||
def build_return(section: str) -> str:
|
||||
"""Show all routes on one page."""
|
||||
selection = [
|
||||
("CAEN", "PORTSMOUTH"),
|
||||
("CHERBOURG", "PORTSMOUTH"),
|
||||
("ST MALO", "PORTSMOUTH"),
|
||||
# ("CHERBOURG", "POOLE"),
|
||||
]
|
||||
|
||||
start = ferry_config.get("return", "from")
|
||||
end = ferry_config.get("return", "to")
|
||||
start = ferry_config.get(section, "from")
|
||||
end = ferry_config.get(section, "to")
|
||||
refresh = bool(flask.request.args.get("refresh"))
|
||||
|
||||
all_data = get_prices_with_cache("return", start, end, selection, refresh)
|
||||
direction = section[:-1] if section[-1].isdigit() else section
|
||||
|
||||
adults_str = flask.request.args.get("adults")
|
||||
adults = int(adults_str) if adults_str else 2
|
||||
|
||||
small_dogs_str = flask.request.args.get("small_dogs")
|
||||
small_dogs = int(small_dogs_str) if small_dogs_str else 2
|
||||
|
||||
all_data = get_prices_with_cache(
|
||||
direction, start, end, routes["return"], refresh, adults, small_dogs
|
||||
)
|
||||
|
||||
return flask.render_template(
|
||||
"all_routes.html",
|
||||
|
|
@ -245,6 +290,44 @@ def return_page() -> str:
|
|||
)
|
||||
|
||||
|
||||
@app.route("/outbound1")
|
||||
def outbound1_page() -> str:
|
||||
return build_outbound("outbound1")
|
||||
|
||||
|
||||
@app.route("/outbound2")
|
||||
def outbound2_page() -> str:
|
||||
return build_outbound("outbound2")
|
||||
|
||||
|
||||
@app.route("/outbound3")
|
||||
def outbound3_page() -> str:
|
||||
return build_outbound("outbound3")
|
||||
|
||||
|
||||
@app.route("/return1")
|
||||
def return1_page() -> str:
|
||||
return build_return("return1")
|
||||
|
||||
|
||||
@app.route("/return2")
|
||||
def return2_page() -> str:
|
||||
return build_return("return2")
|
||||
|
||||
|
||||
def format_pet_options(o: dict[str, bool]) -> list[str]:
|
||||
"""Read pet options from API and format for display."""
|
||||
ret = []
|
||||
if o.get("petCabinAvailable"):
|
||||
ret.append("pet cabins")
|
||||
if o.get("smallKennelAvailable"):
|
||||
ret.append("small kennel")
|
||||
if o.get("stayInCarAvailable"):
|
||||
ret.append("pets stay in car")
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_accommodations_with_cache(
|
||||
dep: str, arr: str, d: str, ticket_tier: str, refresh: bool = False
|
||||
) -> dict[str, list[dict[str, Any]]]:
|
||||
|
|
@ -274,10 +357,77 @@ def get_accommodations_with_cache(
|
|||
return data
|
||||
|
||||
|
||||
@app.route("/cabins/<departure_port>/<arrival_port>/<departure_date>/<ticket_tier>")
|
||||
def get_start_end(section: str) -> tuple[str, str]:
|
||||
start = ferry_config.get(section, "from")
|
||||
end = ferry_config.get(section, "to")
|
||||
return start, end
|
||||
|
||||
|
||||
def get_return_section(departure_date: str) -> tuple[str, str, str]:
|
||||
section = "return1"
|
||||
start, end = get_start_end(section)
|
||||
dep = departure_date[:10]
|
||||
if start <= dep <= end:
|
||||
return section, start, end
|
||||
|
||||
section = "return2"
|
||||
start, end = get_start_end(section)
|
||||
if start <= dep <= end:
|
||||
return section, start, end
|
||||
|
||||
|
||||
def get_outbound_section(departure_date: str) -> tuple[str, str, str]:
|
||||
section = "outbound1"
|
||||
start, end = get_start_end(section)
|
||||
dep = departure_date[:10]
|
||||
if start <= dep <= end:
|
||||
return section, start, end
|
||||
|
||||
section = "outbound2"
|
||||
start, end = get_start_end(section)
|
||||
if start <= dep <= end:
|
||||
return section, start, end
|
||||
|
||||
section = "outbound3"
|
||||
start, end = get_start_end(section)
|
||||
if start <= dep <= end:
|
||||
return section, start, end
|
||||
|
||||
|
||||
def lookup_sailing_id(prices: PriceData, sailing_id: int) -> dict[str, Any] | None:
|
||||
for dep, arr, price_list in prices:
|
||||
for i in price_list:
|
||||
for j in i["prices"]:
|
||||
crossing: dict[str, Any] = j["crossingPrices"]
|
||||
if int(crossing["sailingId"]) == sailing_id:
|
||||
return crossing
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@app.route(
|
||||
"/cabins/<int:sailing_id>/<departure_port>/<arrival_port>/<departure_date>/<ticket_tier>"
|
||||
)
|
||||
def cabins(
|
||||
departure_port: str, arrival_port: str, departure_date: str, ticket_tier: str
|
||||
sailing_id: int,
|
||||
departure_port: str,
|
||||
arrival_port: str,
|
||||
departure_date: str,
|
||||
ticket_tier: str,
|
||||
) -> str:
|
||||
"""Get details of cabins."""
|
||||
direction = {"GB": "outbound", "FR": "return"}[departure_port[:2]]
|
||||
if direction == "return":
|
||||
section, start, end = get_return_section(departure_date[:10])
|
||||
start, end = get_start_end(section)
|
||||
time_delta = 60
|
||||
else:
|
||||
section, start, end = get_outbound_section(departure_date[:10])
|
||||
time_delta = -60
|
||||
|
||||
prices = get_prices_with_cache(direction, start, end, routes[direction])
|
||||
crossing = lookup_sailing_id(prices, sailing_id)
|
||||
|
||||
cabin_data = get_accommodations_with_cache(
|
||||
departure_port, arrival_port, departure_date, ticket_tier
|
||||
)
|
||||
|
|
@ -299,6 +449,11 @@ def cabins(
|
|||
ticket_tier=ticket_tier,
|
||||
accommodations=accommodations,
|
||||
pet_accommodations=cabin_data["petAccommodations"],
|
||||
crossing=crossing,
|
||||
get_duration=get_duration,
|
||||
time_delta=time_delta,
|
||||
format_pet_options=format_pet_options,
|
||||
section=section,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue