Simplify the location tracking function by extracting travel data directly from trip objects instead of requiring separate YAML file parameters. Changes: - Remove airport, train, and ferry location helper functions that required separate YAML data lookups - Update get_location_for_date signature to only take target_date and trips - Extract flight/train/ferry details directly from trip.travel items - Use embedded airport/station/terminal objects from trip data - Remove YAML file parsing from weekends function - Update test calls to use new simplified signature This eliminates duplicate data loading and simplifies the API while maintaining all existing functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
215 lines
6.8 KiB
Python
215 lines
6.8 KiB
Python
from datetime import date, datetime
|
|
|
|
import agenda.busy
|
|
import agenda.travel as travel
|
|
import agenda.trip
|
|
import pytest
|
|
from agenda.busy import _parse_datetime_field
|
|
from agenda.event import Event
|
|
from web_view import app
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def app_context():
|
|
"""Set up Flask app context for tests."""
|
|
app.config["SERVER_NAME"] = "test"
|
|
with app.app_context():
|
|
yield
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def trips(app_context):
|
|
"""Load trip list once for all tests."""
|
|
return agenda.trip.build_trip_list()
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def travel_data(app_context):
|
|
"""Load travel data (bookings, accommodations, airports) once for all tests."""
|
|
data_dir = app.config["PERSONAL_DATA"]
|
|
return {
|
|
"bookings": travel.parse_yaml("flights", data_dir),
|
|
"accommodations": travel.parse_yaml("accommodation", data_dir),
|
|
"airports": travel.parse_yaml("airports", data_dir),
|
|
"data_dir": data_dir,
|
|
}
|
|
|
|
|
|
def test_weekend_location_consistency(app_context, trips, travel_data):
|
|
"""Test that weekend locations are consistent with events (free=home, events=away)."""
|
|
for year in range(2023, 2025):
|
|
start = date(2023, 1, 1)
|
|
busy_events = agenda.busy.get_busy_events(start, app.config, trips)
|
|
weekends = agenda.busy.weekends(
|
|
start, busy_events, trips, travel_data["data_dir"]
|
|
)
|
|
|
|
for weekend in weekends:
|
|
for day in "saturday", "sunday":
|
|
# When free (no events), should be home (None)
|
|
# When traveling (events), should be away (City name)
|
|
location_exists = bool(weekend[day + "_location"][0])
|
|
has_events = bool(weekend[day])
|
|
assert location_exists == has_events, (
|
|
f"Weekend {weekend['date']} {day}: "
|
|
f"location_exists={location_exists}, has_events={has_events}"
|
|
)
|
|
|
|
|
|
def test_specific_home_dates(travel_data):
|
|
"""Test specific dates that should return home (None)."""
|
|
trips = agenda.trip.build_trip_list()
|
|
|
|
home_dates = [
|
|
date(2023, 4, 29),
|
|
date(2025, 7, 1),
|
|
date(2023, 12, 2),
|
|
date(2023, 10, 7),
|
|
date(2023, 2, 18),
|
|
date(2025, 8, 2),
|
|
]
|
|
|
|
for test_date in home_dates:
|
|
location = agenda.busy.get_location_for_date(
|
|
test_date,
|
|
trips,
|
|
)
|
|
assert not location[
|
|
0
|
|
], f"Expected home (None) for {test_date}, got {location[0]}"
|
|
|
|
|
|
def test_specific_away_dates(travel_data):
|
|
"""Test specific dates that should return away locations."""
|
|
trips = agenda.trip.build_trip_list()
|
|
|
|
away_cases = [
|
|
(date(2025, 2, 15), "Hackettstown"),
|
|
]
|
|
|
|
for test_date, expected_city in away_cases:
|
|
location = agenda.busy.get_location_for_date(
|
|
test_date,
|
|
trips,
|
|
)
|
|
assert (
|
|
location[0] == expected_city
|
|
), f"Expected {expected_city} for {test_date}, got {location[0]}"
|
|
|
|
|
|
def test_get_location_for_date_basic(travel_data):
|
|
"""Test basic functionality of get_location_for_date function."""
|
|
trips = agenda.trip.build_trip_list()
|
|
test_date = date(2023, 1, 1)
|
|
|
|
location = agenda.busy.get_location_for_date(
|
|
test_date,
|
|
trips,
|
|
)
|
|
|
|
# Should return a tuple with (city|None, country)
|
|
assert isinstance(location, tuple)
|
|
assert len(location) == 2
|
|
assert location[1] is not None # Should always have a country
|
|
|
|
|
|
def test_busy_event_classification():
|
|
"""Test the busy_event function for different event types."""
|
|
|
|
# Busy event types
|
|
busy_events = [
|
|
Event(name="event", title="Test Event", date=date(2023, 1, 1)),
|
|
Event(
|
|
name="conference",
|
|
title="Test Conference",
|
|
date=date(2023, 1, 1),
|
|
going=True,
|
|
),
|
|
Event(name="accommodation", title="Hotel", date=date(2023, 1, 1)),
|
|
Event(name="transport", title="Flight", date=date(2023, 1, 1)),
|
|
]
|
|
|
|
for event in busy_events:
|
|
assert agenda.busy.busy_event(event), f"Event {event.name} should be busy"
|
|
|
|
# Non-busy events
|
|
non_busy_events = [
|
|
Event(
|
|
name="conference",
|
|
title="Test Conference",
|
|
date=date(2023, 1, 1),
|
|
going=False,
|
|
),
|
|
Event(name="other", title="Other Event", date=date(2023, 1, 1)),
|
|
Event(name="event", title="LHG Run Club", date=date(2023, 1, 1)),
|
|
Event(name="event", title="IA UK board meeting", date=date(2023, 1, 1)),
|
|
]
|
|
|
|
for event in non_busy_events:
|
|
assert not agenda.busy.busy_event(
|
|
event
|
|
), f"Event {event.name}/{event.title} should not be busy"
|
|
|
|
|
|
def test_parse_datetime_field():
|
|
"""Test the _parse_datetime_field helper function."""
|
|
|
|
# Test with datetime object
|
|
dt = datetime(2023, 1, 1, 12, 0, 0)
|
|
parsed_dt, parsed_date = _parse_datetime_field(dt)
|
|
assert parsed_dt == dt
|
|
assert parsed_date == date(2023, 1, 1)
|
|
|
|
# Test with ISO string
|
|
iso_string = "2023-01-01T12:00:00Z"
|
|
parsed_dt, parsed_date = _parse_datetime_field(iso_string)
|
|
assert parsed_date == date(2023, 1, 1)
|
|
assert parsed_dt.year == 2023
|
|
assert parsed_dt.month == 1
|
|
assert parsed_dt.day == 1
|
|
|
|
|
|
def test_get_busy_events(app_context, trips):
|
|
"""Test get_busy_events function."""
|
|
start_date = date(2023, 1, 1)
|
|
busy_events = agenda.busy.get_busy_events(start_date, app.config, trips)
|
|
|
|
# Should return a list
|
|
assert isinstance(busy_events, list)
|
|
|
|
# All events should be Event objects
|
|
for event in busy_events:
|
|
assert hasattr(event, "name")
|
|
assert hasattr(event, "as_date")
|
|
|
|
# Events should be sorted by date
|
|
dates = [event.as_date for event in busy_events]
|
|
assert dates == sorted(dates), "Events should be sorted by date"
|
|
|
|
|
|
def test_weekends_function(app_context, trips, travel_data):
|
|
"""Test the weekends function."""
|
|
start_date = date(2023, 1, 1)
|
|
busy_events = agenda.busy.get_busy_events(start_date, app.config, trips)
|
|
weekends = agenda.busy.weekends(
|
|
start_date, busy_events, trips, travel_data["data_dir"]
|
|
)
|
|
|
|
# Should return a list of weekend info
|
|
assert isinstance(weekends, list)
|
|
assert len(weekends) == 52 # Should return 52 weekends
|
|
|
|
# Each weekend should have the required keys
|
|
for weekend in weekends[:5]: # Check first 5 weekends
|
|
assert "date" in weekend
|
|
assert "saturday" in weekend
|
|
assert "sunday" in weekend
|
|
assert "saturday_location" in weekend
|
|
assert "sunday_location" in weekend
|
|
|
|
# Locations should be tuples
|
|
assert isinstance(weekend["saturday_location"], tuple)
|
|
assert isinstance(weekend["sunday_location"], tuple)
|
|
assert len(weekend["saturday_location"]) == 2
|
|
assert len(weekend["sunday_location"]) == 2
|