diff --git a/validate_yaml.py b/validate_yaml.py index ccbdd0d..6cf4028 100755 --- a/validate_yaml.py +++ b/validate_yaml.py @@ -4,7 +4,7 @@ import os import sys import typing -from datetime import date, timedelta +from datetime import date, timedelta, datetime import yaml from rich.pretty import pprint @@ -33,7 +33,26 @@ def check_currency(item: agenda.types.StrDict) -> 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) 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: - """Check trains.""" + """Check trains and ensure they are in chronological order.""" 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") def check_conferences() -> None: - """Check conferences.""" + """Check conferences and ensure they are in chronological order.""" filepath = os.path.join(data_dir, "conferences.yaml") - conferences = [ - agenda.conference.Conference(**conf) - for conf in yaml.safe_load(open(filepath, "r")) - ] - for conf in conferences: + conferences_data = yaml.safe_load(open(filepath, "r")) + conferences = [agenda.conference.Conference(**conf) for conf in conferences_data] + + prev_start = None + prev_conf_data = None + for i, conf_data in enumerate(conferences_data): + conf = conferences[i] if not conf.currency or conf.currency in currencies: - continue - pprint(conf) - print(f"currency {conf.currency!r} not in {currencies!r}") - sys.exit(-1) + pass + else: + pprint(conf) + 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") @@ -118,12 +180,14 @@ def check_coordinates(item: agenda.types.StrDict) -> None: def check_accommodation() -> None: - """Check accommodation.""" + """Check accommodation and ensure they are in chronological order.""" filepath = os.path.join(data_dir, "accommodation.yaml") accommodation_list = yaml.safe_load(open(filepath)) required_fields = ["type", "name", "country", "location", "trip", "from", "to"] + prev_from = None + prev_stay = None for stay in accommodation_list: try: assert all(field in stay for field in required_fields) @@ -134,6 +198,21 @@ def check_accommodation() -> None: 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") @@ -157,6 +236,30 @@ def check_stations() -> None: 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]: """Check airlines.""" airlines = agenda.travel.parse_yaml("airlines", data_dir) @@ -185,6 +288,7 @@ def check() -> None: check_trips() check_flights({airline["iata"] for airline in airlines}) check_trains() + check_ferries() check_conferences() check_events() check_accommodation()