Fix HTML entities in GWR advance ticket dates
This commit is contained in:
parent
e75acc3364
commit
69992bebb7
2 changed files with 25 additions and 5 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
from html import unescape
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -12,6 +13,7 @@ url = "https://www.gwr.com/your-tickets/choosing-your-ticket/advance-tickets"
|
||||||
|
|
||||||
def parse_date_string(date_str: str) -> date:
|
def parse_date_string(date_str: str) -> date:
|
||||||
"""Parse date string from HTML."""
|
"""Parse date string from HTML."""
|
||||||
|
date_str = " ".join(unescape(date_str).split())
|
||||||
if not date_str[-1].isdigit(): # If the year is missing, use the current year
|
if not date_str[-1].isdigit(): # If the year is missing, use the current year
|
||||||
date_str += f" {date.today().year}"
|
date_str += f" {date.today().year}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import typing
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import Any
|
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -20,6 +20,16 @@ from agenda.gwr import (
|
||||||
class TestParseDateString:
|
class TestParseDateString:
|
||||||
"""Tests for parse_date_string function."""
|
"""Tests for parse_date_string function."""
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("space", [" ", " ", " ", "\u00a0"])
|
||||||
|
@pytest.mark.parametrize("include_year", [True, False])
|
||||||
|
def test_parse_html_whitespace(self, space: str, include_year: bool) -> None:
|
||||||
|
"""Decode HTML spaces before parsing dates or inferring the year."""
|
||||||
|
date_str = f"Thursday 25{space}March"
|
||||||
|
if include_year:
|
||||||
|
date_str += " 2027"
|
||||||
|
result = parse_date_string(f" {space}{date_str}{space} ")
|
||||||
|
assert result == date(2027 if include_year else date.today().year, 3, 25)
|
||||||
|
|
||||||
def test_parse_date_with_year(self) -> None:
|
def test_parse_date_with_year(self) -> None:
|
||||||
"""Test parsing date string with year included."""
|
"""Test parsing date string with year included."""
|
||||||
result = parse_date_string("Monday 25 December 2023")
|
result = parse_date_string("Monday 25 December 2023")
|
||||||
|
|
@ -152,7 +162,7 @@ class TestAdvanceTicketsPageHtml:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("httpx.AsyncClient")
|
@patch("httpx.AsyncClient")
|
||||||
async def test_fetch_from_web(self, mock_client: Any) -> None:
|
async def test_fetch_from_web(self, mock_client: typing.Any) -> None:
|
||||||
"""Test fetching from web when cache is stale."""
|
"""Test fetching from web when cache is stale."""
|
||||||
mock_response = AsyncMock()
|
mock_response = AsyncMock()
|
||||||
mock_response.text = "<html>fresh content</html>"
|
mock_response.text = "<html>fresh content</html>"
|
||||||
|
|
@ -176,7 +186,7 @@ class TestAdvanceTicketDate:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("agenda.gwr.advance_tickets_page_html")
|
@patch("agenda.gwr.advance_tickets_page_html")
|
||||||
async def test_advance_ticket_date_success(self, mock_html: Any) -> None:
|
async def test_advance_ticket_date_success(self, mock_html: typing.Any) -> None:
|
||||||
"""Test successfully extracting advance ticket date."""
|
"""Test successfully extracting advance ticket date."""
|
||||||
mock_html.return_value = """
|
mock_html.return_value = """
|
||||||
<tr><td>Weekdays</td><td>Monday 25 December 2023</td></tr>
|
<tr><td>Weekdays</td><td>Monday 25 December 2023</td></tr>
|
||||||
|
|
@ -188,7 +198,7 @@ class TestAdvanceTicketDate:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("agenda.gwr.advance_tickets_page_html")
|
@patch("agenda.gwr.advance_tickets_page_html")
|
||||||
async def test_advance_ticket_date_no_match(self, mock_html: Any) -> None:
|
async def test_advance_ticket_date_no_match(self, mock_html: typing.Any) -> None:
|
||||||
"""Test when no weekday date can be extracted."""
|
"""Test when no weekday date can be extracted."""
|
||||||
mock_html.return_value = "<p>No relevant data</p>"
|
mock_html.return_value = "<p>No relevant data</p>"
|
||||||
|
|
||||||
|
|
@ -197,7 +207,7 @@ class TestAdvanceTicketDate:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("agenda.gwr.advance_tickets_page_html")
|
@patch("agenda.gwr.advance_tickets_page_html")
|
||||||
async def test_advance_ticket_date_force_cache(self, mock_html: Any) -> None:
|
async def test_advance_ticket_date_force_cache(self, mock_html: typing.Any) -> None:
|
||||||
"""Test advance_ticket_date with force_cache parameter."""
|
"""Test advance_ticket_date with force_cache parameter."""
|
||||||
mock_html.return_value = """
|
mock_html.return_value = """
|
||||||
<tr><td>Weekdays</td><td>Tuesday 26 December 2023</td></tr>
|
<tr><td>Weekdays</td><td>Tuesday 26 December 2023</td></tr>
|
||||||
|
|
@ -206,3 +216,11 @@ class TestAdvanceTicketDate:
|
||||||
result = await advance_ticket_date("/fake/dir", force_cache=True)
|
result = await advance_ticket_date("/fake/dir", force_cache=True)
|
||||||
assert result == date(2023, 12, 26)
|
assert result == date(2023, 12, 26)
|
||||||
mock_html.assert_called_once_with("/fake/dir", force_cache=True)
|
mock_html.assert_called_once_with("/fake/dir", force_cache=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("suffix", ["", "**"])
|
||||||
|
def test_extract_html_entity_date(suffix: str) -> None:
|
||||||
|
"""Both extraction paths accept the date that caused the update crash."""
|
||||||
|
html = f"<tr><td>Weekdays</td><td>Thursday 25 March 2027{suffix}</td></tr>"
|
||||||
|
assert extract_dates(html) == {"Weekdays": date(2027, 3, 25)}
|
||||||
|
assert extract_weekday_date(html) == date(2027, 3, 25)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue