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 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()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue