diff --git a/main.py b/main.py index e2aef86..31fc79a 100755 --- a/main.py +++ b/main.py @@ -354,6 +354,7 @@ class CategoryResult: articles: list[ArticleWithoutImage] gcmcontinue: str | None + error: str | None = None # Common non-content images to ignore when checking if an article has images @@ -435,6 +436,7 @@ def get_articles_without_images( articles_without_images: list[ArticleWithoutImage] = [] seen_pageids: set[int] = set() next_gcmcontinue: str | None = None + error: str | None = None # Build initial continue params from the external pagination token continue_params: dict[str, str] = {} @@ -451,8 +453,21 @@ def get_articles_without_images( ) response.raise_for_status() data = response.json() + if api_error := data.get("error"): + raise requests.RequestException( + f"{api_error.get('code', 'unknown')}: " + f"{api_error.get('info', 'unknown Wikipedia API error')}" + ) except (requests.RequestException, json.JSONDecodeError) as e: print(f"Wikipedia API error: {e}") + if articles_without_images: + error = ( + "Wikipedia stopped responding before the whole category could " + "be checked. Showing partial results. " + f"Wikipedia API error: {e}" + ) + else: + error = f"Unable to load articles. Wikipedia API error: {e}" break pages = data.get("query", {}).get("pages", {}) @@ -491,6 +506,7 @@ def get_articles_without_images( return CategoryResult( articles=articles_without_images, gcmcontinue=next_gcmcontinue, + error=error, ) @@ -758,6 +774,7 @@ def start() -> str: "combined.html", name=name, enwp=enwp, + wikipedia_url=wikipedia_url, search_result=search_result, cat=cat, flickr_search=flickr_search, @@ -890,6 +907,7 @@ def category_search() -> str: category_name=category_name, articles=result.articles, gcmcontinue=result.gcmcontinue, + error=result.error, ) diff --git a/templates/category.html b/templates/category.html index 62fcb1a..2fe8df8 100644 --- a/templates/category.html +++ b/templates/category.html @@ -50,7 +50,7 @@ {% endif %} - {% else %} + {% elif not error %}
All articles in this category have images!
diff --git a/templates/combined.html b/templates/combined.html index 1f4ee7f..1b17450 100644 --- a/templates/combined.html +++ b/templates/combined.html @@ -62,7 +62,7 @@ {% if cat %}

← Back to category

{% endif %} -

Wikipedia article: {{ name }}

+

Wikipedia article: {{ name }}

{% if wikipedia_extract %}

{{ wikipedia_extract }}

{% endif %}
@@ -151,7 +151,7 @@ {% if cat %}

← Back to category

{% endif %} -

Wikipedia article: {{ name }}

+

Wikipedia article: {{ name }}

{% if wikipedia_extract %}

{{ wikipedia_extract }}

{% endif %} diff --git a/tests/test_category_search.py b/tests/test_category_search.py new file mode 100644 index 0000000..6a6072a --- /dev/null +++ b/tests/test_category_search.py @@ -0,0 +1,89 @@ +"""Tests for Wikipedia category searches.""" + +from unittest.mock import patch + +import requests + +from main import CategoryResult, app, get_articles_without_images + + +def test_api_failure_is_not_reported_as_an_empty_success(): + with patch("main.requests.get", side_effect=requests.ConnectionError("offline")): + result = get_articles_without_images("Category:Example") + + assert result.articles == [] + assert result.error == "Unable to load articles. Wikipedia API error: offline" + + +def test_api_error_response_is_not_reported_as_an_empty_success(): + error_response = type( + "Response", + (), + { + "raise_for_status": lambda self: None, + "json": lambda self: { + "error": {"code": "readonly", "info": "Wikipedia is read-only"} + }, + }, + )() + + with patch("main.requests.get", return_value=error_response): + result = get_articles_without_images("Category:Example") + + assert result.articles == [] + assert result.error == ( + "Unable to load articles. Wikipedia API error: " + "readonly: Wikipedia is read-only" + ) + + +def test_api_failure_after_results_marks_them_as_partial(): + first_response = type( + "Response", + (), + { + "raise_for_status": lambda self: None, + "json": lambda self: { + "query": { + "pages": { + "1": {"pageid": 1, "title": "No image", "images": []} + } + }, + "continue": { + "gcmcontinue": "next-page", + "continue": "gcmcontinue||", + }, + }, + }, + )() + + with patch( + "main.requests.get", + side_effect=[first_response, requests.ConnectionError("offline")], + ): + result = get_articles_without_images("Category:Example") + + assert [article.title for article in result.articles] == ["No image"] + assert result.error is not None + assert "partial results" in result.error + assert "offline" in result.error + + +def test_category_page_does_not_claim_success_after_api_failure(): + result = CategoryResult( + articles=[], + gcmcontinue=None, + error="Unable to load articles. Wikipedia API error: test failure", + ) + + with ( + patch("main.get_articles_without_images", return_value=result), + patch("main.log_interaction"), + app.test_client() as client, + ): + response = client.get("/category?cat=Category:Example") + + page = response.get_data(as_text=True) + assert response.status_code == 200 + assert "Wikipedia API error: test failure" in page + assert "All articles in this category have images" not in page