Compare commits
No commits in common. "04cb3e8179113553c63330a8bc77b8cbb4a662cb" and "f38c5327ea44f728be47144560a357bf1030cd79" have entirely different histories.
04cb3e8179
...
f38c5327ea
10 changed files with 15 additions and 361 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import typing
|
import typing
|
||||||
from datetime import date, datetime, timedelta, timezone
|
from datetime import date, datetime, timedelta
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
import pycountry
|
import pycountry
|
||||||
|
|
@ -84,20 +84,13 @@ def get_busy_events(
|
||||||
def _parse_datetime_field(datetime_obj: datetime | date | str) -> tuple[datetime, date]:
|
def _parse_datetime_field(datetime_obj: datetime | date | str) -> tuple[datetime, date]:
|
||||||
"""Parse a datetime field that could be datetime object or string."""
|
"""Parse a datetime field that could be datetime object or string."""
|
||||||
if isinstance(datetime_obj, datetime):
|
if isinstance(datetime_obj, datetime):
|
||||||
dt = datetime_obj
|
return datetime_obj, datetime_obj.date()
|
||||||
elif isinstance(datetime_obj, date):
|
if isinstance(datetime_obj, date):
|
||||||
dt = datetime.combine(datetime_obj, datetime.min.time(), tzinfo=timezone.utc)
|
return datetime.combine(datetime_obj, datetime.min.time()), datetime_obj
|
||||||
elif isinstance(datetime_obj, str):
|
if isinstance(datetime_obj, str):
|
||||||
dt = datetime.fromisoformat(datetime_obj.replace("Z", "+00:00"))
|
dt = datetime.fromisoformat(datetime_obj.replace("Z", "+00:00"))
|
||||||
else:
|
return dt, dt.date()
|
||||||
raise ValueError(f"Invalid datetime format: {datetime_obj}")
|
raise ValueError(f"Invalid datetime format: {datetime_obj}")
|
||||||
|
|
||||||
if dt.tzinfo is None:
|
|
||||||
dt = dt.replace(tzinfo=timezone.utc)
|
|
||||||
else:
|
|
||||||
dt = dt.astimezone(timezone.utc)
|
|
||||||
|
|
||||||
return dt, dt.date()
|
|
||||||
|
|
||||||
|
|
||||||
def _get_accommodation_location(
|
def _get_accommodation_location(
|
||||||
|
|
|
||||||
|
|
@ -322,18 +322,6 @@ def summarize_launch(launch: Launch) -> Summary:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def is_launches_cache_fresh(rocket_dir: str) -> bool:
|
|
||||||
"""Return True if the launches cache is younger than the TTL."""
|
|
||||||
now = datetime.now()
|
|
||||||
existing = [
|
|
||||||
x for x in (filename_timestamp(f, "json") for f in os.listdir(rocket_dir)) if x
|
|
||||||
]
|
|
||||||
if not existing:
|
|
||||||
return False
|
|
||||||
existing.sort(reverse=True)
|
|
||||||
return (now - existing[0][0]).total_seconds() <= ttl
|
|
||||||
|
|
||||||
|
|
||||||
def load_cached_launches(rocket_dir: str) -> StrDict | None:
|
def load_cached_launches(rocket_dir: str) -> StrDict | None:
|
||||||
"""Read the most recent cache of launches."""
|
"""Read the most recent cache of launches."""
|
||||||
filename = get_most_recent_file(rocket_dir, "json")
|
filename = get_most_recent_file(rocket_dir, "json")
|
||||||
|
|
|
||||||
|
|
@ -281,9 +281,7 @@
|
||||||
{% set item = e.detail %}
|
{% set item = e.detail %}
|
||||||
{% set full_flight_number = item.airline_code + item.flight_number %}
|
{% set full_flight_number = item.airline_code + item.flight_number %}
|
||||||
{% set radarbox_url = "https://www.radarbox.com/data/flights/" + full_flight_number %}
|
{% set radarbox_url = "https://www.radarbox.com/data/flights/" + full_flight_number %}
|
||||||
{% set depart_date = item.depart.date() if item.depart.hour is defined else item.depart %}
|
{% set is_overnight = item.arrive and item.depart.date() != item.arrive.date() %}
|
||||||
{% set arrive_date = item.arrive.date() if (item.arrive and item.arrive.hour is defined) else item.arrive %}
|
|
||||||
{% set is_overnight = item.arrive and depart_date != arrive_date %}
|
|
||||||
<div class="trip-transport-card my-1">
|
<div class="trip-transport-card my-1">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">
|
<h5 class="card-title">
|
||||||
|
|
@ -319,9 +317,7 @@
|
||||||
|
|
||||||
{% elif e.element_type == "train" %}
|
{% elif e.element_type == "train" %}
|
||||||
{% set item = e.detail %}
|
{% set item = e.detail %}
|
||||||
{% set depart_date = item.depart.date() if item.depart.hour is defined else item.depart %}
|
{% set is_overnight = item.depart.date() != item.arrive.date() %}
|
||||||
{% set arrive_date = item.arrive.date() if item.arrive.hour is defined else item.arrive %}
|
|
||||||
{% set is_overnight = depart_date != arrive_date %}
|
|
||||||
<div class="trip-transport-card my-1">
|
<div class="trip-transport-card my-1">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">
|
<h5 class="card-title">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import date, datetime, timezone
|
from datetime import date, datetime
|
||||||
|
|
||||||
import agenda.busy
|
import agenda.busy
|
||||||
import agenda.travel as travel
|
import agenda.travel as travel
|
||||||
|
|
@ -157,8 +157,7 @@ def test_parse_datetime_field():
|
||||||
# Test with datetime object
|
# Test with datetime object
|
||||||
dt = datetime(2023, 1, 1, 12, 0, 0)
|
dt = datetime(2023, 1, 1, 12, 0, 0)
|
||||||
parsed_dt, parsed_date = _parse_datetime_field(dt)
|
parsed_dt, parsed_date = _parse_datetime_field(dt)
|
||||||
assert parsed_dt == dt.replace(tzinfo=timezone.utc)
|
assert parsed_dt == dt
|
||||||
assert parsed_dt.tzinfo == timezone.utc
|
|
||||||
assert parsed_date == date(2023, 1, 1)
|
assert parsed_date == date(2023, 1, 1)
|
||||||
|
|
||||||
# Test with ISO string
|
# Test with ISO string
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
"""Regression tests for timezone handling in busy location logic."""
|
|
||||||
|
|
||||||
from datetime import date, datetime, timezone
|
|
||||||
|
|
||||||
import agenda.busy
|
|
||||||
from agenda.types import Trip
|
|
||||||
|
|
||||||
|
|
||||||
def test_mixed_naive_and_aware_arrivals_do_not_crash() -> None:
|
|
||||||
"""Most recent travel should compare mixed timezone styles safely."""
|
|
||||||
trips = [
|
|
||||||
Trip(
|
|
||||||
start=date(2099, 12, 30),
|
|
||||||
travel=[
|
|
||||||
{
|
|
||||||
"type": "flight",
|
|
||||||
"arrive": datetime(2100, 1, 1, 10, 0, 0),
|
|
||||||
"to": "CDG",
|
|
||||||
"to_airport": {"country": "fr", "city": "Paris"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "flight",
|
|
||||||
"arrive": datetime(2100, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
|
|
||||||
"to": "AMS",
|
|
||||||
"to_airport": {"country": "nl", "city": "Amsterdam"},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
location = agenda.busy._find_most_recent_travel_before_date(date(2100, 1, 1), trips)
|
|
||||||
assert location is not None
|
|
||||||
assert location[0] == "Amsterdam"
|
|
||||||
assert location[1] is not None
|
|
||||||
assert location[1].alpha_2 == "NL"
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
"""Tests for trip map coordinate assembly."""
|
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
import agenda.trip
|
|
||||||
from agenda.types import Trip
|
|
||||||
from web_view import app
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_coordinates_for_unbooked_flights_adds_missing_airports() -> None:
|
|
||||||
"""Unbooked routes should contribute missing airport pins."""
|
|
||||||
routes = [
|
|
||||||
{
|
|
||||||
"type": "unbooked_flight",
|
|
||||||
"key": "LHR_Paris_fr",
|
|
||||||
"from_iata": "LHR",
|
|
||||||
"to_iata": "CDG",
|
|
||||||
"from": (51.47, -0.45),
|
|
||||||
"to": (49.01, 2.55),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
coordinates = [
|
|
||||||
{
|
|
||||||
"name": "Heathrow Airport",
|
|
||||||
"type": "airport",
|
|
||||||
"latitude": 51.47,
|
|
||||||
"longitude": -0.45,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
airports = {
|
|
||||||
"LHR": {
|
|
||||||
"name": "Heathrow Airport",
|
|
||||||
"latitude": 51.47,
|
|
||||||
"longitude": -0.45,
|
|
||||||
},
|
|
||||||
"CDG": {
|
|
||||||
"name": "Paris Charles de Gaulle Airport",
|
|
||||||
"latitude": 49.01,
|
|
||||||
"longitude": 2.55,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
with app.app_context():
|
|
||||||
original_parse_yaml = agenda.trip.travel.parse_yaml
|
|
||||||
try:
|
|
||||||
agenda.trip.travel.parse_yaml = lambda _name, _data_dir: airports
|
|
||||||
agenda.trip.add_coordinates_for_unbooked_flights(
|
|
||||||
routes, coordinates, app.config["PERSONAL_DATA"]
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
agenda.trip.travel.parse_yaml = original_parse_yaml
|
|
||||||
|
|
||||||
airport_names = {
|
|
||||||
coord["name"] for coord in coordinates if coord["type"] == "airport"
|
|
||||||
}
|
|
||||||
assert airport_names == {"Heathrow Airport", "Paris Charles de Gaulle Airport"}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_coordinates_and_routes_adds_unbooked_flight_airports() -> None:
|
|
||||||
"""Trip list map data should include pins for unbooked flights."""
|
|
||||||
trips = [Trip(start=date(2026, 7, 20))]
|
|
||||||
unbooked_routes = [
|
|
||||||
{
|
|
||||||
"type": "unbooked_flight",
|
|
||||||
"key": "LHR_Paris_fr",
|
|
||||||
"from_iata": "LHR",
|
|
||||||
"to_iata": "CDG",
|
|
||||||
"from": (51.47, -0.45),
|
|
||||||
"to": (49.01, 2.55),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
airports = {
|
|
||||||
"LHR": {
|
|
||||||
"name": "Heathrow Airport",
|
|
||||||
"latitude": 51.47,
|
|
||||||
"longitude": -0.45,
|
|
||||||
},
|
|
||||||
"CDG": {
|
|
||||||
"name": "Paris Charles de Gaulle Airport",
|
|
||||||
"latitude": 49.01,
|
|
||||||
"longitude": 2.55,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
with app.app_context():
|
|
||||||
original_collect_trip_coordinates = agenda.trip.collect_trip_coordinates
|
|
||||||
original_get_trip_routes = agenda.trip.get_trip_routes
|
|
||||||
original_parse_yaml = agenda.trip.travel.parse_yaml
|
|
||||||
try:
|
|
||||||
agenda.trip.collect_trip_coordinates = lambda _trip: []
|
|
||||||
agenda.trip.get_trip_routes = lambda _trip, _data_dir: unbooked_routes
|
|
||||||
agenda.trip.travel.parse_yaml = lambda _name, _data_dir: airports
|
|
||||||
|
|
||||||
coordinates, _routes = agenda.trip.get_coordinates_and_routes(trips)
|
|
||||||
finally:
|
|
||||||
agenda.trip.collect_trip_coordinates = original_collect_trip_coordinates
|
|
||||||
agenda.trip.get_trip_routes = original_get_trip_routes
|
|
||||||
agenda.trip.travel.parse_yaml = original_parse_yaml
|
|
||||||
|
|
||||||
airport_names = {
|
|
||||||
coord["name"] for coord in coordinates if coord["type"] == "airport"
|
|
||||||
}
|
|
||||||
assert airport_names == {"Heathrow Airport", "Paris Charles de Gaulle Airport"}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
"""Regression tests for trip page route wiring and rendering."""
|
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
import typing
|
|
||||||
|
|
||||||
import web_view
|
|
||||||
from agenda.types import Trip
|
|
||||||
|
|
||||||
|
|
||||||
def test_trip_page_passes_data_dir_to_unbooked_flight_helper() -> None:
|
|
||||||
"""Trip page should call helper with routes, coordinates and data_dir."""
|
|
||||||
trip = Trip(start=date(2025, 1, 28))
|
|
||||||
captured: dict[str, str] = {}
|
|
||||||
|
|
||||||
with web_view.app.app_context():
|
|
||||||
original_get_trip_list = web_view.get_trip_list
|
|
||||||
original_add_schengen = (
|
|
||||||
web_view.agenda.trip_schengen.add_schengen_compliance_to_trip
|
|
||||||
)
|
|
||||||
original_collect_trip_coordinates = (
|
|
||||||
web_view.agenda.trip.collect_trip_coordinates
|
|
||||||
)
|
|
||||||
original_get_trip_routes = web_view.agenda.trip.get_trip_routes
|
|
||||||
original_add_coordinates = (
|
|
||||||
web_view.agenda.trip.add_coordinates_for_unbooked_flights
|
|
||||||
)
|
|
||||||
original_get_trip_weather = web_view.agenda.weather.get_trip_weather
|
|
||||||
original_render_template = web_view.flask.render_template
|
|
||||||
try:
|
|
||||||
web_view.get_trip_list = lambda: [trip]
|
|
||||||
web_view.agenda.trip_schengen.add_schengen_compliance_to_trip = lambda t: t
|
|
||||||
web_view.agenda.trip.collect_trip_coordinates = lambda _trip: []
|
|
||||||
web_view.agenda.trip.get_trip_routes = lambda _trip, _data_dir: []
|
|
||||||
|
|
||||||
def fake_add_coordinates(
|
|
||||||
_routes: list[typing.Any],
|
|
||||||
_coordinates: list[typing.Any],
|
|
||||||
data_dir: str,
|
|
||||||
) -> None:
|
|
||||||
captured["data_dir"] = data_dir
|
|
||||||
|
|
||||||
web_view.agenda.trip.add_coordinates_for_unbooked_flights = (
|
|
||||||
fake_add_coordinates
|
|
||||||
)
|
|
||||||
web_view.agenda.weather.get_trip_weather = lambda *_args, **_kwargs: []
|
|
||||||
web_view.flask.render_template = lambda *_args, **_kwargs: "ok"
|
|
||||||
|
|
||||||
with web_view.app.test_request_context("/trip/2025-01-28"):
|
|
||||||
result = web_view.trip_page("2025-01-28")
|
|
||||||
|
|
||||||
assert result == "ok"
|
|
||||||
assert captured["data_dir"] == web_view.app.config["PERSONAL_DATA"]
|
|
||||||
finally:
|
|
||||||
web_view.get_trip_list = original_get_trip_list
|
|
||||||
web_view.agenda.trip_schengen.add_schengen_compliance_to_trip = (
|
|
||||||
original_add_schengen
|
|
||||||
)
|
|
||||||
web_view.agenda.trip.collect_trip_coordinates = (
|
|
||||||
original_collect_trip_coordinates
|
|
||||||
)
|
|
||||||
web_view.agenda.trip.get_trip_routes = original_get_trip_routes
|
|
||||||
web_view.agenda.trip.add_coordinates_for_unbooked_flights = (
|
|
||||||
original_add_coordinates
|
|
||||||
)
|
|
||||||
web_view.agenda.weather.get_trip_weather = original_get_trip_weather
|
|
||||||
web_view.flask.render_template = original_render_template
|
|
||||||
|
|
||||||
|
|
||||||
def test_trip_page_renders_with_date_only_train_leg() -> None:
|
|
||||||
"""Trip page should render when train legs use date values (no time)."""
|
|
||||||
trip = Trip(
|
|
||||||
start=date(2025, 1, 28),
|
|
||||||
travel=[
|
|
||||||
{
|
|
||||||
"type": "train",
|
|
||||||
"depart": date(2025, 1, 28),
|
|
||||||
"from": "A",
|
|
||||||
"to": "B",
|
|
||||||
"from_station": {
|
|
||||||
"name": "A",
|
|
||||||
"country": "gb",
|
|
||||||
"latitude": 51.5,
|
|
||||||
"longitude": -0.1,
|
|
||||||
},
|
|
||||||
"to_station": {
|
|
||||||
"name": "B",
|
|
||||||
"country": "gb",
|
|
||||||
"latitude": 51.6,
|
|
||||||
"longitude": -0.2,
|
|
||||||
},
|
|
||||||
"legs": [
|
|
||||||
{
|
|
||||||
"from": "A",
|
|
||||||
"to": "B",
|
|
||||||
"depart": date(2025, 1, 28),
|
|
||||||
"arrive": date(2025, 1, 28),
|
|
||||||
"from_station": {
|
|
||||||
"name": "A",
|
|
||||||
"country": "gb",
|
|
||||||
"latitude": 51.5,
|
|
||||||
"longitude": -0.1,
|
|
||||||
},
|
|
||||||
"to_station": {
|
|
||||||
"name": "B",
|
|
||||||
"country": "gb",
|
|
||||||
"latitude": 51.6,
|
|
||||||
"longitude": -0.2,
|
|
||||||
},
|
|
||||||
"operator": "Test Rail",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
with web_view.app.app_context():
|
|
||||||
original_get_trip_list = web_view.get_trip_list
|
|
||||||
original_get_trip_weather = web_view.agenda.weather.get_trip_weather
|
|
||||||
try:
|
|
||||||
web_view.get_trip_list = lambda: [trip]
|
|
||||||
web_view.agenda.weather.get_trip_weather = lambda *_args, **_kwargs: []
|
|
||||||
web_view.app.config["TESTING"] = True
|
|
||||||
|
|
||||||
with web_view.app.test_client() as client:
|
|
||||||
response = client.get("/trip/2025-01-28")
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert b"Test Rail" in response.data
|
|
||||||
finally:
|
|
||||||
web_view.get_trip_list = original_get_trip_list
|
|
||||||
web_view.agenda.weather.get_trip_weather = original_get_trip_weather
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
"""Tests for weekends route query validation."""
|
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
import typing
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import web_view
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture # type: ignore[untyped-decorator]
|
|
||||||
def client() -> typing.Any:
|
|
||||||
"""Flask test client."""
|
|
||||||
web_view.app.config["TESTING"] = True
|
|
||||||
with web_view.app.test_client() as c:
|
|
||||||
yield c
|
|
||||||
|
|
||||||
|
|
||||||
def test_weekends_rejects_year_before_2020(client: typing.Any) -> None:
|
|
||||||
"""Years before 2020 should return HTTP 400."""
|
|
||||||
response = client.get("/weekends?year=2019&week=1")
|
|
||||||
assert response.status_code == 400
|
|
||||||
assert b"Year must be between 2020" in response.data
|
|
||||||
|
|
||||||
|
|
||||||
def test_weekends_rejects_year_more_than_five_years_ahead(client: typing.Any) -> None:
|
|
||||||
"""Years beyond current year + 5 should return HTTP 400."""
|
|
||||||
too_far = date.today().year + 6
|
|
||||||
response = client.get(f"/weekends?year={too_far}&week=1")
|
|
||||||
assert response.status_code == 400
|
|
||||||
assert b"Year must be between 2020" in response.data
|
|
||||||
|
|
@ -321,9 +321,6 @@ def update_thespacedevs(config: flask.config.Config) -> None:
|
||||||
existing_data = agenda.thespacedevs.load_cached_launches(rocket_dir)
|
existing_data = agenda.thespacedevs.load_cached_launches(rocket_dir)
|
||||||
assert existing_data
|
assert existing_data
|
||||||
|
|
||||||
if agenda.thespacedevs.is_launches_cache_fresh(rocket_dir):
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update active crewed mission cache used by the launches page.
|
# Update active crewed mission cache used by the launches page.
|
||||||
# Uses the 2-hour TTL; failures are handled internally with cache fallback.
|
# Uses the 2-hour TTL; failures are handled internally with cache fallback.
|
||||||
active_crewed = agenda.thespacedevs.get_active_crewed_flights(rocket_dir)
|
active_crewed = agenda.thespacedevs.get_active_crewed_flights(rocket_dir)
|
||||||
|
|
|
||||||
27
web_view.py
27
web_view.py
|
|
@ -268,16 +268,6 @@ async def gaps_page() -> str:
|
||||||
async def weekends() -> str:
|
async def weekends() -> str:
|
||||||
"""List of available weekends using an optional date, week, or year parameter."""
|
"""List of available weekends using an optional date, week, or year parameter."""
|
||||||
today = datetime.now().date()
|
today = datetime.now().date()
|
||||||
min_year = 2020
|
|
||||||
max_year = today.year + 5
|
|
||||||
|
|
||||||
def validate_year(year: int) -> None:
|
|
||||||
"""Validate year parameter range for weekends page."""
|
|
||||||
if year < min_year or year > max_year:
|
|
||||||
flask.abort(
|
|
||||||
400, description=f"Year must be between {min_year} and {max_year}."
|
|
||||||
)
|
|
||||||
|
|
||||||
date_str = flask.request.args.get("date")
|
date_str = flask.request.args.get("date")
|
||||||
week_str = flask.request.args.get("week")
|
week_str = flask.request.args.get("week")
|
||||||
year_str = flask.request.args.get("year")
|
year_str = flask.request.args.get("year")
|
||||||
|
|
@ -285,14 +275,12 @@ async def weekends() -> str:
|
||||||
if date_str:
|
if date_str:
|
||||||
try:
|
try:
|
||||||
start = datetime.strptime(date_str, "%Y-%m-%d").date()
|
start = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||||||
validate_year(start.year)
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return flask.abort(400, description="Invalid date format. Use YYYY-MM-DD.")
|
return flask.abort(400, description="Invalid date format. Use YYYY-MM-DD.")
|
||||||
elif week_str:
|
elif week_str:
|
||||||
try:
|
try:
|
||||||
week = int(week_str)
|
week = int(week_str)
|
||||||
year = int(year_str) if year_str else today.year
|
year = int(year_str) if year_str else today.year
|
||||||
validate_year(year)
|
|
||||||
if week < 1 or week > 53:
|
if week < 1 or week > 53:
|
||||||
return flask.abort(
|
return flask.abort(
|
||||||
400, description="Week number must be between 1 and 53."
|
400, description="Week number must be between 1 and 53."
|
||||||
|
|
@ -305,13 +293,6 @@ async def weekends() -> str:
|
||||||
return flask.abort(
|
return flask.abort(
|
||||||
400, description="Invalid week or year format. Use integers."
|
400, description="Invalid week or year format. Use integers."
|
||||||
)
|
)
|
||||||
elif year_str:
|
|
||||||
try:
|
|
||||||
year = int(year_str)
|
|
||||||
validate_year(year)
|
|
||||||
start = date(year, 1, 1)
|
|
||||||
except ValueError:
|
|
||||||
return flask.abort(400, description="Invalid year format. Use an integer.")
|
|
||||||
else:
|
else:
|
||||||
start = date(today.year, 1, 1)
|
start = date(today.year, 1, 1)
|
||||||
|
|
||||||
|
|
@ -933,7 +914,9 @@ def get_destination_timezones(trip: Trip) -> list[StrDict]:
|
||||||
if flight_country:
|
if flight_country:
|
||||||
flight_locations.append((city, flight_country))
|
flight_locations.append((city, flight_country))
|
||||||
|
|
||||||
existing_location_keys = {(loc, c.alpha_2.lower()) for loc, c in trip.locations()}
|
existing_location_keys = {
|
||||||
|
(loc, c.alpha_2.lower()) for loc, c in trip.locations()
|
||||||
|
}
|
||||||
all_locations = list(trip.locations()) + [
|
all_locations = list(trip.locations()) + [
|
||||||
(city, country)
|
(city, country)
|
||||||
for city, country in flight_locations
|
for city, country in flight_locations
|
||||||
|
|
@ -1035,9 +1018,7 @@ def trip_page(start: str) -> str:
|
||||||
coordinates = agenda.trip.collect_trip_coordinates(trip)
|
coordinates = agenda.trip.collect_trip_coordinates(trip)
|
||||||
routes = agenda.trip.get_trip_routes(trip, app.config["PERSONAL_DATA"])
|
routes = agenda.trip.get_trip_routes(trip, app.config["PERSONAL_DATA"])
|
||||||
|
|
||||||
agenda.trip.add_coordinates_for_unbooked_flights(
|
agenda.trip.add_coordinates_for_unbooked_flights(routes, coordinates)
|
||||||
routes, coordinates, app.config["PERSONAL_DATA"]
|
|
||||||
)
|
|
||||||
|
|
||||||
for route in routes:
|
for route in routes:
|
||||||
if "geojson_filename" in route:
|
if "geojson_filename" in route:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue