Chronological order validation for YAML

This commit is contained in:
Edward Betts 2025-07-20 01:10:56 +02:00
parent 4b8c29a8d0
commit 412d8c7ba7

View file

@ -4,7 +4,7 @@
import os import os
import sys import sys
import typing import typing
from datetime import date, timedelta from datetime import date, timedelta, datetime
import yaml import yaml
from rich.pretty import pprint from rich.pretty import pprint
@ -33,7 +33,26 @@ def check_currency(item: agenda.types.StrDict) -> None:
def check_trips() -> None: def check_trips() -> None:
"""Check trips.""" """Check trips and ensure they are in chronological order."""
filepath = os.path.join(data_dir, "trips.yaml")
trips_data = yaml.safe_load(open(filepath, "r"))
prev_trip = None
prev_trip_data = None
for trip_data in trips_data:
current_trip = normalize_datetime(trip_data["trip"])
if prev_trip and current_trip < prev_trip:
print(f"Out of order trip found:")
print(
f" Previous: {prev_trip_data.get('trip')} - {prev_trip_data.get('name', 'No name')}"
)
print(
f" Current: {trip_data.get('trip')} - {trip_data.get('name', 'No name')}"
)
assert False, "Trips are not in chronological order by trip date."
prev_trip = current_trip
prev_trip_data = trip_data
trip_list = agenda.trip.build_trip_list(data_dir) trip_list = agenda.trip.build_trip_list(data_dir)
print(len(trip_list), "trips") print(len(trip_list), "trips")
@ -76,25 +95,68 @@ def check_flights(airlines: set[str]) -> None:
) )
def normalize_datetime(dt_value):
"""Convert date or datetime to datetime for comparison, removing timezone info."""
if isinstance(dt_value, date) and not isinstance(dt_value, datetime):
return datetime.combine(dt_value, datetime.min.time())
elif isinstance(dt_value, datetime):
# Remove timezone info to allow comparison between naive and aware datetimes
return dt_value.replace(tzinfo=None)
return dt_value
def check_trains() -> None: def check_trains() -> None:
"""Check trains.""" """Check trains and ensure they are in chronological order."""
trains = agenda.travel.parse_yaml("trains", data_dir) trains = agenda.travel.parse_yaml("trains", data_dir)
prev_depart = None
prev_train = None
for train in trains:
current_depart = normalize_datetime(train["depart"])
if prev_depart and current_depart < prev_depart:
print(f"Out of order train found:")
print(
f" Previous: {prev_train.get('depart')} {prev_train.get('from', '')} -> {prev_train.get('to', '')}"
)
print(
f" Current: {train.get('depart')} {train.get('from', '')} -> {train.get('to', '')}"
)
assert False, "Trains are not in chronological order by departure time."
prev_depart = current_depart
prev_train = train
print(len(trains), "trains") print(len(trains), "trains")
def check_conferences() -> None: def check_conferences() -> None:
"""Check conferences.""" """Check conferences and ensure they are in chronological order."""
filepath = os.path.join(data_dir, "conferences.yaml") filepath = os.path.join(data_dir, "conferences.yaml")
conferences = [ conferences_data = yaml.safe_load(open(filepath, "r"))
agenda.conference.Conference(**conf) conferences = [agenda.conference.Conference(**conf) for conf in conferences_data]
for conf in yaml.safe_load(open(filepath, "r"))
] prev_start = None
for conf in conferences: prev_conf_data = None
for i, conf_data in enumerate(conferences_data):
conf = conferences[i]
if not conf.currency or conf.currency in currencies: if not conf.currency or conf.currency in currencies:
continue pass
pprint(conf) else:
print(f"currency {conf.currency!r} not in {currencies!r}") pprint(conf)
sys.exit(-1) print(f"currency {conf.currency!r} not in {currencies!r}")
sys.exit(-1)
current_start = normalize_datetime(conf_data["start"])
if prev_start and current_start < prev_start:
print(f"Out of order conference found:")
print(
f" Previous: {prev_conf_data.get('start')} - {prev_conf_data.get('name', 'No name')}"
)
print(
f" Current: {conf_data.get('start')} - {conf_data.get('name', 'No name')}"
)
assert False, "Conferences are not in chronological order by start time."
prev_start = current_start
prev_conf_data = conf_data
print(len(conferences), "conferences") print(len(conferences), "conferences")
@ -118,12 +180,14 @@ def check_coordinates(item: agenda.types.StrDict) -> None:
def check_accommodation() -> None: def check_accommodation() -> None:
"""Check accommodation.""" """Check accommodation and ensure they are in chronological order."""
filepath = os.path.join(data_dir, "accommodation.yaml") filepath = os.path.join(data_dir, "accommodation.yaml")
accommodation_list = yaml.safe_load(open(filepath)) accommodation_list = yaml.safe_load(open(filepath))
required_fields = ["type", "name", "country", "location", "trip", "from", "to"] required_fields = ["type", "name", "country", "location", "trip", "from", "to"]
prev_from = None
prev_stay = None
for stay in accommodation_list: for stay in accommodation_list:
try: try:
assert all(field in stay for field in required_fields) assert all(field in stay for field in required_fields)
@ -134,6 +198,21 @@ def check_accommodation() -> None:
check_currency(stay) check_currency(stay)
current_from = normalize_datetime(stay["from"])
if prev_from and current_from < prev_from:
print(f"Out of order accommodation found:")
print(
f" Previous: {prev_stay.get('from')} - {prev_stay.get('name', 'No name')} ({prev_stay.get('location', '')})"
)
print(
f" Current: {stay.get('from')} - {stay.get('name', 'No name')} ({stay.get('location', '')})"
)
assert (
False
), "Accommodation is not in chronological order by check-in time."
prev_from = current_from
prev_stay = stay
print(len(accommodation_list), "stays") print(len(accommodation_list), "stays")
@ -157,6 +236,30 @@ def check_stations() -> None:
assert agenda.get_country(station["country"]) assert agenda.get_country(station["country"])
def check_ferries() -> None:
"""Check ferries and ensure they are in chronological order."""
ferries = agenda.travel.parse_yaml("ferries", data_dir)
prev_depart = None
prev_ferry = None
for ferry in ferries:
current_depart = normalize_datetime(ferry["depart"])
if prev_depart and current_depart < prev_depart:
print(f"Out of order ferry found:")
print(
f" Previous: {prev_ferry.get('depart')} {prev_ferry.get('from', '')} -> {prev_ferry.get('to', '')}"
)
print(
f" Current: {ferry.get('depart')} {ferry.get('from', '')} -> {ferry.get('to', '')}"
)
assert False, "Ferries are not in chronological order by departure time."
prev_depart = current_depart
prev_ferry = ferry
check_currency(ferry)
print(len(ferries), "ferries")
def check_airlines() -> list[agenda.types.StrDict]: def check_airlines() -> list[agenda.types.StrDict]:
"""Check airlines.""" """Check airlines."""
airlines = agenda.travel.parse_yaml("airlines", data_dir) airlines = agenda.travel.parse_yaml("airlines", data_dir)
@ -185,6 +288,7 @@ def check() -> None:
check_trips() check_trips()
check_flights({airline["iata"] for airline in airlines}) check_flights({airline["iata"] for airline in airlines})
check_trains() check_trains()
check_ferries()
check_conferences() check_conferences()
check_events() check_events()
check_accommodation() check_accommodation()