Compare commits
3 commits
4b8c29a8d0
...
88ccd79cb2
Author | SHA1 | Date | |
---|---|---|---|
|
88ccd79cb2 | ||
|
ea712b2063 | ||
|
412d8c7ba7 |
370
tests/test_conference.py
Normal file
370
tests/test_conference.py
Normal file
|
@ -0,0 +1,370 @@
|
|||
"""Tests for agenda.conference module."""
|
||||
|
||||
import decimal
|
||||
import tempfile
|
||||
from datetime import date, datetime
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from agenda.conference import Conference, get_list
|
||||
from agenda.event import Event
|
||||
|
||||
|
||||
class TestConference:
|
||||
"""Tests for Conference dataclass."""
|
||||
|
||||
def test_conference_creation_minimal(self) -> None:
|
||||
"""Test creating conference with minimal required fields."""
|
||||
conf = Conference(
|
||||
name="PyCon",
|
||||
topic="Python",
|
||||
location="Portland",
|
||||
start=date(2024, 5, 15),
|
||||
end=date(2024, 5, 17),
|
||||
)
|
||||
assert conf.name == "PyCon"
|
||||
assert conf.topic == "Python"
|
||||
assert conf.location == "Portland"
|
||||
assert conf.start == date(2024, 5, 15)
|
||||
assert conf.end == date(2024, 5, 17)
|
||||
assert conf.trip is None
|
||||
assert conf.going is False
|
||||
assert conf.online is False
|
||||
|
||||
def test_conference_creation_full(self) -> None:
|
||||
"""Test creating conference with all fields."""
|
||||
conf = Conference(
|
||||
name="PyCon US",
|
||||
topic="Python",
|
||||
location="Portland",
|
||||
start=date(2024, 5, 15),
|
||||
end=date(2024, 5, 17),
|
||||
trip=date(2024, 5, 14),
|
||||
country="USA",
|
||||
venue="Convention Center",
|
||||
address="123 Main St",
|
||||
url="https://pycon.org",
|
||||
accommodation_booked=True,
|
||||
transport_booked=True,
|
||||
going=True,
|
||||
registered=True,
|
||||
speaking=True,
|
||||
online=False,
|
||||
price=decimal.Decimal("500.00"),
|
||||
currency="USD",
|
||||
latitude=45.5152,
|
||||
longitude=-122.6784,
|
||||
cfp_end=date(2024, 2, 1),
|
||||
cfp_url="https://pycon.org/cfp",
|
||||
free=False,
|
||||
hackathon=True,
|
||||
ticket_type="early_bird",
|
||||
attendees=3000,
|
||||
hashtag="#pycon2024",
|
||||
)
|
||||
assert conf.name == "PyCon US"
|
||||
assert conf.going is True
|
||||
assert conf.price == decimal.Decimal("500.00")
|
||||
assert conf.currency == "USD"
|
||||
assert conf.latitude == 45.5152
|
||||
assert conf.longitude == -122.6784
|
||||
assert conf.cfp_end == date(2024, 2, 1)
|
||||
assert conf.hashtag == "#pycon2024"
|
||||
|
||||
def test_display_name_location_in_name(self) -> None:
|
||||
"""Test display_name when location is already in conference name."""
|
||||
conf = Conference(
|
||||
name="PyCon Portland",
|
||||
topic="Python",
|
||||
location="Portland",
|
||||
start=date(2024, 5, 15),
|
||||
end=date(2024, 5, 17),
|
||||
)
|
||||
assert conf.display_name == "PyCon Portland"
|
||||
|
||||
def test_display_name_location_not_in_name(self) -> None:
|
||||
"""Test display_name when location is not in conference name."""
|
||||
conf = Conference(
|
||||
name="PyCon",
|
||||
topic="Python",
|
||||
location="Portland",
|
||||
start=date(2024, 5, 15),
|
||||
end=date(2024, 5, 17),
|
||||
)
|
||||
assert conf.display_name == "PyCon (Portland)"
|
||||
|
||||
def test_display_name_partial_location_match(self) -> None:
|
||||
"""Test display_name when location is partially in name."""
|
||||
conf = Conference(
|
||||
name="PyConf",
|
||||
topic="Python",
|
||||
location="Conference Center",
|
||||
start=date(2024, 5, 15),
|
||||
end=date(2024, 5, 17),
|
||||
)
|
||||
assert conf.display_name == "PyConf (Conference Center)"
|
||||
|
||||
def test_conference_with_datetime(self) -> None:
|
||||
"""Test conference with datetime objects."""
|
||||
start_dt = datetime(2024, 5, 15, 9, 0)
|
||||
end_dt = datetime(2024, 5, 17, 17, 0)
|
||||
conf = Conference(
|
||||
name="PyCon",
|
||||
topic="Python",
|
||||
location="Portland",
|
||||
start=start_dt,
|
||||
end=end_dt,
|
||||
)
|
||||
assert conf.start == start_dt
|
||||
assert conf.end == end_dt
|
||||
|
||||
|
||||
class TestGetList:
|
||||
"""Tests for get_list function."""
|
||||
|
||||
def test_get_list_single_conference(self) -> None:
|
||||
"""Test reading single conference from YAML."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 17),
|
||||
"url": "https://pycon.org",
|
||||
"going": True,
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 1
|
||||
event = events[0]
|
||||
assert isinstance(event, Event)
|
||||
assert event.name == "conference"
|
||||
assert event.date == date(2024, 5, 15)
|
||||
assert event.end_date == date(2024, 5, 17)
|
||||
assert event.title == "PyCon (Portland)"
|
||||
assert event.url == "https://pycon.org"
|
||||
assert event.going is True
|
||||
|
||||
def test_get_list_conference_with_cfp(self) -> None:
|
||||
"""Test reading conference with CFP end date."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 17),
|
||||
"url": "https://pycon.org",
|
||||
"cfp_end": date(2024, 2, 1),
|
||||
"cfp_url": "https://pycon.org/cfp",
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 2
|
||||
|
||||
# Conference event
|
||||
conf_event = events[0]
|
||||
assert conf_event.name == "conference"
|
||||
assert conf_event.title == "PyCon (Portland)"
|
||||
assert conf_event.url == "https://pycon.org"
|
||||
|
||||
# CFP end event
|
||||
cfp_event = events[1]
|
||||
assert cfp_event.name == "cfp_end"
|
||||
assert cfp_event.date == date(2024, 2, 1)
|
||||
assert cfp_event.title == "CFP end: PyCon (Portland)"
|
||||
assert cfp_event.url == "https://pycon.org/cfp"
|
||||
|
||||
def test_get_list_conference_cfp_no_url(self) -> None:
|
||||
"""Test reading conference with CFP end date but no CFP URL."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 17),
|
||||
"url": "https://pycon.org",
|
||||
"cfp_end": date(2024, 2, 1),
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 2
|
||||
cfp_event = events[1]
|
||||
assert cfp_event.url == "https://pycon.org" # Falls back to conference URL
|
||||
|
||||
def test_get_list_multiple_conferences(self) -> None:
|
||||
"""Test reading multiple conferences from YAML."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 17),
|
||||
},
|
||||
{
|
||||
"name": "EuroPython",
|
||||
"topic": "Python",
|
||||
"location": "Prague",
|
||||
"start": date(2024, 7, 8),
|
||||
"end": date(2024, 7, 14),
|
||||
"cfp_end": date(2024, 3, 15),
|
||||
},
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 3 # 2 conferences + 1 CFP end
|
||||
|
||||
# First conference
|
||||
assert events[0].title == "PyCon (Portland)"
|
||||
assert events[0].date == date(2024, 5, 15)
|
||||
|
||||
# Second conference
|
||||
assert events[1].title == "EuroPython (Prague)"
|
||||
assert events[1].date == date(2024, 7, 8)
|
||||
|
||||
# CFP end event for second conference
|
||||
assert events[2].name == "cfp_end"
|
||||
assert events[2].title == "CFP end: EuroPython (Prague)"
|
||||
|
||||
def test_get_list_location_in_name(self) -> None:
|
||||
"""Test conference where location is already in name."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon Portland",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 17),
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 1
|
||||
assert events[0].title == "PyCon Portland" # No location appended
|
||||
|
||||
def test_get_list_datetime_objects(self) -> None:
|
||||
"""Test reading conferences with datetime objects."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "PyCon",
|
||||
"topic": "Python",
|
||||
"location": "Portland",
|
||||
"start": datetime(2024, 5, 15, 9, 0),
|
||||
"end": datetime(2024, 5, 17, 17, 0),
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 1
|
||||
event = events[0]
|
||||
assert event.date == datetime(2024, 5, 15, 9, 0)
|
||||
assert event.end_date == datetime(2024, 5, 17, 17, 0)
|
||||
|
||||
def test_get_list_invalid_date_order(self) -> None:
|
||||
"""Test that conferences with end before start raise assertion error."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "Invalid Conference",
|
||||
"topic": "Testing",
|
||||
"location": "Nowhere",
|
||||
"start": date(2024, 5, 17),
|
||||
"end": date(2024, 5, 15), # End before start
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
get_list(f.name)
|
||||
|
||||
def test_get_list_too_long_conference(self) -> None:
|
||||
"""Test that conferences longer than MAX_CONF_DAYS raise assertion error."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "Too Long Conference",
|
||||
"topic": "Testing",
|
||||
"location": "Nowhere",
|
||||
"start": date(2024, 5, 1),
|
||||
"end": date(2024, 6, 1), # More than 20 days
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
get_list(f.name)
|
||||
|
||||
def test_get_list_empty_file(self) -> None:
|
||||
"""Test reading empty YAML file."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump([], f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert events == []
|
||||
|
||||
def test_get_list_same_day_conference(self) -> None:
|
||||
"""Test conference that starts and ends on same day."""
|
||||
yaml_data = [
|
||||
{
|
||||
"name": "One Day Conference",
|
||||
"topic": "Testing",
|
||||
"location": "Test City",
|
||||
"start": date(2024, 5, 15),
|
||||
"end": date(2024, 5, 15),
|
||||
}
|
||||
]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
yaml.dump(yaml_data, f)
|
||||
f.flush()
|
||||
|
||||
events = get_list(f.name)
|
||||
|
||||
assert len(events) == 1
|
||||
event = events[0]
|
||||
assert event.date == date(2024, 5, 15)
|
||||
assert event.end_date == date(2024, 5, 15)
|
208
tests/test_gwr.py
Normal file
208
tests/test_gwr.py
Normal file
|
@ -0,0 +1,208 @@
|
|||
"""Tests for agenda.gwr module."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import date
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from agenda.gwr import (
|
||||
advance_ticket_date,
|
||||
advance_tickets_page_html,
|
||||
extract_dates,
|
||||
extract_weekday_date,
|
||||
parse_date_string,
|
||||
)
|
||||
|
||||
|
||||
class TestParseDateString:
|
||||
"""Tests for parse_date_string function."""
|
||||
|
||||
def test_parse_date_with_year(self) -> None:
|
||||
"""Test parsing date string with year included."""
|
||||
result = parse_date_string("Monday 25 December 2023")
|
||||
assert result == date(2023, 12, 25)
|
||||
|
||||
def test_parse_date_without_year(self) -> None:
|
||||
"""Test parsing date string without year (should use current year)."""
|
||||
current_year = date.today().year
|
||||
result = parse_date_string("Monday 25 December")
|
||||
assert result == date(current_year, 12, 25)
|
||||
|
||||
def test_parse_different_formats(self) -> None:
|
||||
"""Test parsing various date formats."""
|
||||
result = parse_date_string("Friday 1 January 2024")
|
||||
assert result == date(2024, 1, 1)
|
||||
|
||||
result = parse_date_string("Saturday 29 February 2024")
|
||||
assert result == date(2024, 2, 29)
|
||||
|
||||
|
||||
class TestExtractDates:
|
||||
"""Tests for extract_dates function."""
|
||||
|
||||
def test_extract_valid_dates(self) -> None:
|
||||
"""Test extracting dates from valid HTML."""
|
||||
html = """
|
||||
<tr><td>Weekdays</td><td>Monday 25 December 2023</td></tr>
|
||||
<tr><td>Saturdays</td><td>Saturday 23 December 2023</td></tr>
|
||||
<tr><td>Sundays</td><td>Sunday 24 December 2023</td></tr>
|
||||
"""
|
||||
result = extract_dates(html)
|
||||
expected = {
|
||||
"Weekdays": date(2023, 12, 25),
|
||||
"Saturdays": date(2023, 12, 23),
|
||||
"Sundays": date(2023, 12, 24),
|
||||
}
|
||||
assert result == expected
|
||||
|
||||
def test_extract_dates_with_asterisks(self) -> None:
|
||||
"""Test extracting dates when HTML contains asterisks."""
|
||||
html = """
|
||||
<tr><td>Weekdays</td><td>Monday 25 December 2023**</td></tr>
|
||||
"""
|
||||
result = extract_dates(html)
|
||||
expected = {"Weekdays": date(2023, 12, 25)}
|
||||
assert result == expected
|
||||
|
||||
def test_extract_dates_no_match(self) -> None:
|
||||
"""Test extracting dates when no pattern matches."""
|
||||
html = "<p>No relevant table data here</p>"
|
||||
result = extract_dates(html)
|
||||
assert result is None
|
||||
|
||||
def test_extract_dates_whitespace_handling(self) -> None:
|
||||
"""Test extracting dates with various whitespace."""
|
||||
html = """
|
||||
<tr> <td>Weekdays</td> <td>Monday 25 December 2023</td> </tr>
|
||||
"""
|
||||
result = extract_dates(html)
|
||||
expected = {"Weekdays": date(2023, 12, 25)}
|
||||
assert result == expected
|
||||
|
||||
|
||||
class TestExtractWeekdayDate:
|
||||
"""Tests for extract_weekday_date function."""
|
||||
|
||||
def test_extract_valid_weekday_date(self) -> None:
|
||||
"""Test extracting weekday date from valid HTML."""
|
||||
html = """
|
||||
<tr><td>Weekdays</td><td>Monday 25 December 2023</td></tr>
|
||||
"""
|
||||
result = extract_weekday_date(html)
|
||||
assert result == date(2023, 12, 25)
|
||||
|
||||
def test_extract_weekday_date_with_asterisks(self) -> None:
|
||||
"""Test extracting weekday date when HTML contains asterisks."""
|
||||
html = """
|
||||
<tr><td>Weekdays</td><td>Monday 25 December 2023**</td></tr>
|
||||
"""
|
||||
result = extract_weekday_date(html)
|
||||
assert result == date(2023, 12, 25)
|
||||
|
||||
def test_extract_weekday_date_no_match(self) -> None:
|
||||
"""Test extracting weekday date when no pattern matches."""
|
||||
html = "<p>No weekday data here</p>"
|
||||
result = extract_weekday_date(html)
|
||||
assert result is None
|
||||
|
||||
def test_extract_weekday_date_multiline(self) -> None:
|
||||
"""Test extracting weekday date with multiline content."""
|
||||
html = """
|
||||
<tr>
|
||||
<td>Weekdays</td>
|
||||
<td>Monday 25 December 2023</td>
|
||||
</tr>
|
||||
"""
|
||||
result = extract_weekday_date(html)
|
||||
assert result == date(2023, 12, 25)
|
||||
|
||||
|
||||
class TestAdvanceTicketsPageHtml:
|
||||
"""Tests for advance_tickets_page_html function."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cache_hit(self) -> None:
|
||||
"""Test using cached HTML when file is fresh."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
cache_file = os.path.join(temp_dir, "advance-tickets.html")
|
||||
test_content = "<html>test content</html>"
|
||||
|
||||
# Create a fresh cache file
|
||||
with open(cache_file, "w") as f:
|
||||
f.write(test_content)
|
||||
|
||||
result = await advance_tickets_page_html(temp_dir, ttl=3600)
|
||||
assert result == test_content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_force_cache(self) -> None:
|
||||
"""Test forcing cache usage even when file doesn't exist."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
cache_file = os.path.join(temp_dir, "advance-tickets.html")
|
||||
test_content = "<html>cached content</html>"
|
||||
|
||||
with open(cache_file, "w") as f:
|
||||
f.write(test_content)
|
||||
|
||||
result = await advance_tickets_page_html(temp_dir, force_cache=True)
|
||||
assert result == test_content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("httpx.AsyncClient")
|
||||
async def test_fetch_from_web(self, mock_client: Any) -> None:
|
||||
"""Test fetching from web when cache is stale."""
|
||||
mock_response = AsyncMock()
|
||||
mock_response.text = "<html>fresh content</html>"
|
||||
mock_client.return_value.__aenter__.return_value.get.return_value = (
|
||||
mock_response
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
result = await advance_tickets_page_html(temp_dir, ttl=0)
|
||||
assert result == "<html>fresh content</html>"
|
||||
|
||||
# Check that content was cached
|
||||
cache_file = os.path.join(temp_dir, "advance-tickets.html")
|
||||
with open(cache_file) as f:
|
||||
cached_content = f.read()
|
||||
assert cached_content == "<html>fresh content</html>"
|
||||
|
||||
|
||||
class TestAdvanceTicketDate:
|
||||
"""Tests for advance_ticket_date function."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("agenda.gwr.advance_tickets_page_html")
|
||||
async def test_advance_ticket_date_success(self, mock_html: Any) -> None:
|
||||
"""Test successfully extracting advance ticket date."""
|
||||
mock_html.return_value = """
|
||||
<tr><td>Weekdays</td><td>Monday 25 December 2023</td></tr>
|
||||
"""
|
||||
|
||||
result = await advance_ticket_date("/fake/dir")
|
||||
assert result == date(2023, 12, 25)
|
||||
mock_html.assert_called_once_with("/fake/dir", force_cache=False)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("agenda.gwr.advance_tickets_page_html")
|
||||
async def test_advance_ticket_date_no_match(self, mock_html: Any) -> None:
|
||||
"""Test when no weekday date can be extracted."""
|
||||
mock_html.return_value = "<p>No relevant data</p>"
|
||||
|
||||
result = await advance_ticket_date("/fake/dir")
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("agenda.gwr.advance_tickets_page_html")
|
||||
async def test_advance_ticket_date_force_cache(self, mock_html: Any) -> None:
|
||||
"""Test advance_ticket_date with force_cache parameter."""
|
||||
mock_html.return_value = """
|
||||
<tr><td>Weekdays</td><td>Tuesday 26 December 2023</td></tr>
|
||||
"""
|
||||
|
||||
result = await advance_ticket_date("/fake/dir", force_cache=True)
|
||||
assert result == date(2023, 12, 26)
|
||||
mock_html.assert_called_once_with("/fake/dir", force_cache=True)
|
132
validate_yaml.py
132
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()
|
||||
|
|
Loading…
Reference in a new issue