Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions apps/agent/src/trip_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@
def fetch_card(title: str, client: httpx.Client) -> dict:
summary = client.get("https://en.wikipedia.org/api/rest_v1/page/summary/" + quote(title, safe=""))
summary.raise_for_status()
data = summary.json()
image = data.get("thumbnail", {}).get("source", "")
original = data.get("originalimage", {}).get("source", "")
def mapping(value):
if not isinstance(value, dict):
raise ValueError("The photo source returned invalid metadata.")
return value

data = mapping(summary.json())
original = mapping(data.get("originalimage") or {}).get("source", "")
image = mapping(data.get("thumbnail") or {}).get("source") or original
if not isinstance(image, str) or not isinstance(original, str):
raise ValueError("The photo source returned invalid image URLs.")
if any(urlsplit(url).scheme != "https" or urlsplit(url).hostname not in IMAGE_HOSTS
for url in [image, original]):
raise ValueError("No supported destination image was available.")
Expand All @@ -33,10 +40,17 @@ def commons_filename(url):
"iiprop": "extmetadata", "titles": "File:" + filename,
})
metadata.raise_for_status()
pages = metadata.json().get("query", {}).get("pages", {})
info = next(iter(pages.values()), {}).get("imageinfo", [{}])[0].get("extmetadata", {})
pages = mapping(mapping(mapping(metadata.json()).get("query")).get("pages"))
page = mapping(next(iter(pages.values()), None))
image_info = page.get("imageinfo")
if not isinstance(image_info, list) or not image_info:
raise ValueError("The photo source returned invalid image metadata.")
info = mapping(mapping(image_info[0]).get("extmetadata"))
def plain(key):
return unescape(re.sub(r"<[^>]*>", "", info.get(key, {}).get("value", ""))).strip()[:500]
value = mapping(info.get(key) or {}).get("value", "")
if not isinstance(value, str):
raise ValueError("The photo source returned invalid attribution.")
return unescape(re.sub(r"<[^>]*>", "", value)).strip()[:500]
license_name = plain("LicenseShortName")
artist = plain("Artist")
if not artist or not license_name or not (license_name.startswith("CC") or license_name == "Public domain"):
Expand Down
52 changes: 52 additions & 0 deletions apps/agent/tests/test_trip_image_resilience_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import httpx
import pytest

import src.trip_images as images


IMAGE = 'https://upload.wikimedia.org/wikipedia/commons/a/ab/Test.jpg'
METADATA = {'query': {'pages': {'1': {'imageinfo': [{'extmetadata': {
'Artist': {'value': 'A Photographer'}, 'LicenseShortName': {'value': 'CC BY-SA 4.0'},
}}]}}}}


def test_original_image_is_usable_when_summary_has_no_thumbnail():
def handle(request):
payload = {'originalimage': {'source': IMAGE}} if 'summary' in request.url.path else METADATA
return httpx.Response(200, json=payload)

with httpx.Client(transport=httpx.MockTransport(handle)) as client:
card = images.fetch_card('Coast', client)
assert card['status'] == 'available'
assert card['image_url'] == IMAGE
assert card['credit_url'].endswith('File:Test.jpg')


@pytest.mark.parametrize(('summary', 'metadata'), [
([], METADATA),
({'thumbnail': {'source': IMAGE}, 'originalimage': {'source': IMAGE}}, {'query': []}),
({'thumbnail': {'source': IMAGE}, 'originalimage': {'source': IMAGE}}, {'query': {'pages': []}}),
({'thumbnail': {'source': IMAGE}, 'originalimage': {'source': IMAGE}}, {'query': {'pages': {'1': None}}}),
({'thumbnail': {'source': IMAGE}, 'originalimage': {'source': IMAGE}}, {'query': {'pages': {'1': {'imageinfo': [{'extmetadata': None}]}}}}),
])
def test_malformed_stop_does_not_abort_other_destination_cards(monkeypatch, summary, metadata):
real_client = httpx.Client
def make_client(**kwargs):
current_title = None

def handle(request):
nonlocal current_title
if 'summary' in request.url.path:
current_title = request.url.path.rsplit('/', 1)[-1]
return httpx.Response(200, json=summary if current_title == 'Broken' else {
'thumbnail': {'source': IMAGE}, 'originalimage': {'source': IMAGE},
})
return httpx.Response(200, json=metadata if current_title == 'Broken' else METADATA)

return real_client(transport=httpx.MockTransport(handle))

monkeypatch.setattr(images.httpx, 'Client', make_client)
result = images.get_trip_stop_images.invoke({'wikipedia_titles': ['Broken', 'Healthy']})
assert [stop['status'] for stop in result['stops']] == ['unavailable', 'available']
assert result['stops'][0]['place'] == 'Broken'
assert result['stops'][1]['image_url'] == IMAGE