Compare commits
3 commits
782b629929
...
15bbbecacf
| Author | SHA1 | Date | |
|---|---|---|---|
| 15bbbecacf | |||
| b7ba24b567 | |||
| 3de78c75c5 |
4 changed files with 110 additions and 3 deletions
18
main.py
18
main.py
|
|
@ -354,6 +354,7 @@ class CategoryResult:
|
||||||
|
|
||||||
articles: list[ArticleWithoutImage]
|
articles: list[ArticleWithoutImage]
|
||||||
gcmcontinue: str | None
|
gcmcontinue: str | None
|
||||||
|
error: str | None = None
|
||||||
|
|
||||||
|
|
||||||
# Common non-content images to ignore when checking if an article has images
|
# 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] = []
|
articles_without_images: list[ArticleWithoutImage] = []
|
||||||
seen_pageids: set[int] = set()
|
seen_pageids: set[int] = set()
|
||||||
next_gcmcontinue: str | None = None
|
next_gcmcontinue: str | None = None
|
||||||
|
error: str | None = None
|
||||||
|
|
||||||
# Build initial continue params from the external pagination token
|
# Build initial continue params from the external pagination token
|
||||||
continue_params: dict[str, str] = {}
|
continue_params: dict[str, str] = {}
|
||||||
|
|
@ -451,8 +453,21 @@ def get_articles_without_images(
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
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:
|
except (requests.RequestException, json.JSONDecodeError) as e:
|
||||||
print(f"Wikipedia API error: {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
|
break
|
||||||
|
|
||||||
pages = data.get("query", {}).get("pages", {})
|
pages = data.get("query", {}).get("pages", {})
|
||||||
|
|
@ -491,6 +506,7 @@ def get_articles_without_images(
|
||||||
return CategoryResult(
|
return CategoryResult(
|
||||||
articles=articles_without_images,
|
articles=articles_without_images,
|
||||||
gcmcontinue=next_gcmcontinue,
|
gcmcontinue=next_gcmcontinue,
|
||||||
|
error=error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -758,6 +774,7 @@ def start() -> str:
|
||||||
"combined.html",
|
"combined.html",
|
||||||
name=name,
|
name=name,
|
||||||
enwp=enwp,
|
enwp=enwp,
|
||||||
|
wikipedia_url=wikipedia_url,
|
||||||
search_result=search_result,
|
search_result=search_result,
|
||||||
cat=cat,
|
cat=cat,
|
||||||
flickr_search=flickr_search,
|
flickr_search=flickr_search,
|
||||||
|
|
@ -890,6 +907,7 @@ def category_search() -> str:
|
||||||
category_name=category_name,
|
category_name=category_name,
|
||||||
articles=result.articles,
|
articles=result.articles,
|
||||||
gcmcontinue=result.gcmcontinue,
|
gcmcontinue=result.gcmcontinue,
|
||||||
|
error=result.error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% else %}
|
{% elif not error %}
|
||||||
<div class="alert alert-success mt-3">
|
<div class="alert alert-success mt-3">
|
||||||
All articles in this category have images!
|
All articles in this category have images!
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
{% if cat %}
|
{% if cat %}
|
||||||
<p><a href="{{ url_for('category_search', cat=cat) }}">← Back to category</a></p>
|
<p><a href="{{ url_for('category_search', cat=cat) }}">← Back to category</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>Wikipedia article: {{ name }}</p>
|
<p>Wikipedia article: <a href="{{ wikipedia_url }}" target="_blank">{{ name }}</a></p>
|
||||||
{% if wikipedia_extract %}<p class="text-muted">{{ wikipedia_extract }}</p>{% endif %}
|
{% if wikipedia_extract %}<p class="text-muted">{{ wikipedia_extract }}</p>{% endif %}
|
||||||
<form action="{{ url_for(request.endpoint) }}" class="mb-3 d-flex align-items-center gap-2">
|
<form action="{{ url_for(request.endpoint) }}" class="mb-3 d-flex align-items-center gap-2">
|
||||||
<input type="hidden" name="enwp" value="{{ enwp }}">
|
<input type="hidden" name="enwp" value="{{ enwp }}">
|
||||||
|
|
@ -151,7 +151,7 @@
|
||||||
{% if cat %}
|
{% if cat %}
|
||||||
<p><a href="{{ url_for('category_search', cat=cat) }}">← Back to category</a></p>
|
<p><a href="{{ url_for('category_search', cat=cat) }}">← Back to category</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>Wikipedia article: {{ name }}</p>
|
<p>Wikipedia article: <a href="{{ wikipedia_url }}" target="_blank">{{ name }}</a></p>
|
||||||
{% if wikipedia_extract %}<p class="text-muted">{{ wikipedia_extract }}</p>{% endif %}
|
{% if wikipedia_extract %}<p class="text-muted">{{ wikipedia_extract }}</p>{% endif %}
|
||||||
<form action="{{ url_for(request.endpoint) }}" class="mb-3 d-flex align-items-center gap-2">
|
<form action="{{ url_for(request.endpoint) }}" class="mb-3 d-flex align-items-center gap-2">
|
||||||
<input type="hidden" name="enwp" value="{{ enwp }}">
|
<input type="hidden" name="enwp" value="{{ enwp }}">
|
||||||
|
|
|
||||||
89
tests/test_category_search.py
Normal file
89
tests/test_category_search.py
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue