Enhance ImageObject schema with attribution and license metadata in views and tests
All checks were successful
Deploy to Server / deploy (push) Successful in 11s
All checks were successful
Deploy to Server / deploy (push) Successful in 11s
This commit is contained in:
parent
f8236034a3
commit
28cd62b161
2 changed files with 99 additions and 14 deletions
|
|
@ -1730,7 +1730,7 @@ class TestImageObjectStructuredData:
|
||||||
org: Organization,
|
org: Organization,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""VideoGame ImageObject should carry creditText and copyrightNotice."""
|
"""VideoGame ImageObject should carry attribution and license metadata."""
|
||||||
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
|
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
|
||||||
response: _MonkeyPatchedWSGIResponse = client.get(url)
|
response: _MonkeyPatchedWSGIResponse = client.get(url)
|
||||||
|
|
||||||
|
|
@ -1738,6 +1738,19 @@ class TestImageObjectStructuredData:
|
||||||
img: dict[str, Any] = schema["image"]
|
img: dict[str, Any] = schema["image"]
|
||||||
assert img["creditText"] == org.name
|
assert img["creditText"] == org.name
|
||||||
assert org.name in img["copyrightNotice"]
|
assert org.name in img["copyrightNotice"]
|
||||||
|
assert img["creator"] == {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": org.name,
|
||||||
|
"url": f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}",
|
||||||
|
}
|
||||||
|
assert (
|
||||||
|
img["license"]
|
||||||
|
== f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
img["acquireLicensePage"]
|
||||||
|
== f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}"
|
||||||
|
)
|
||||||
|
|
||||||
def test_game_schema_no_image_when_no_box_art(
|
def test_game_schema_no_image_when_no_box_art(
|
||||||
self,
|
self,
|
||||||
|
|
@ -1837,7 +1850,7 @@ class TestImageObjectStructuredData:
|
||||||
org: Organization,
|
org: Organization,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Event ImageObject should carry creditText and copyrightNotice."""
|
"""Event ImageObject should carry attribution and license metadata."""
|
||||||
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
|
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
|
||||||
response: _MonkeyPatchedWSGIResponse = client.get(url)
|
response: _MonkeyPatchedWSGIResponse = client.get(url)
|
||||||
|
|
||||||
|
|
@ -1845,6 +1858,19 @@ class TestImageObjectStructuredData:
|
||||||
img: dict[str, Any] = schema["image"]
|
img: dict[str, Any] = schema["image"]
|
||||||
assert img["creditText"] == org.name
|
assert img["creditText"] == org.name
|
||||||
assert org.name in img["copyrightNotice"]
|
assert org.name in img["copyrightNotice"]
|
||||||
|
assert img["creator"] == {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": org.name,
|
||||||
|
"url": f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}",
|
||||||
|
}
|
||||||
|
assert (
|
||||||
|
img["license"]
|
||||||
|
== f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
img["acquireLicensePage"]
|
||||||
|
== f"http://testserver{reverse('twitch:organization_detail', args=[org.twitch_id])}"
|
||||||
|
)
|
||||||
|
|
||||||
def test_campaign_schema_no_image_when_no_image_url(
|
def test_campaign_schema_no_image_when_no_image_url(
|
||||||
self,
|
self,
|
||||||
|
|
@ -1916,6 +1942,13 @@ class TestImageObjectStructuredData:
|
||||||
|
|
||||||
schema: dict[str, Any] = json.loads(response.context["schema_data"])
|
schema: dict[str, Any] = json.loads(response.context["schema_data"])
|
||||||
assert schema["image"]["creditText"] == "Twitch"
|
assert schema["image"]["creditText"] == "Twitch"
|
||||||
|
assert schema["image"]["creator"] == {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Twitch",
|
||||||
|
"url": "https://www.twitch.tv/",
|
||||||
|
}
|
||||||
|
assert schema["image"]["license"] == "https://www.twitch.tv/"
|
||||||
|
assert schema["image"]["acquireLicensePage"] == "https://www.twitch.tv/"
|
||||||
assert "organizer" not in schema
|
assert "organizer" not in schema
|
||||||
|
|
||||||
# --- _pick_owner / Twitch Gaming skipping ---
|
# --- _pick_owner / Twitch Gaming skipping ---
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,42 @@ def _pick_owner(owners: list[Organization]) -> Organization | None:
|
||||||
return preferred[0] if preferred else owners[0]
|
return preferred[0] if preferred else owners[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _build_image_object(
|
||||||
|
request: HttpRequest,
|
||||||
|
image_url: str,
|
||||||
|
creator_name: str,
|
||||||
|
creator_url: str,
|
||||||
|
*,
|
||||||
|
copyright_notice: str | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Build a Schema.org ImageObject with attribution and license metadata.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The HTTP request used for absolute URL building.
|
||||||
|
image_url: Relative or absolute image URL.
|
||||||
|
creator_name: Human-readable creator/owner name.
|
||||||
|
creator_url: URL for the creator organization or fallback owner page.
|
||||||
|
copyright_notice: Optional copyright text.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with ImageObject fields used in structured data.
|
||||||
|
"""
|
||||||
|
creator: dict[str, str] = {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": creator_name,
|
||||||
|
"url": creator_url,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"contentUrl": request.build_absolute_uri(image_url),
|
||||||
|
"creditText": creator_name,
|
||||||
|
"copyrightNotice": copyright_notice or creator_name,
|
||||||
|
"creator": creator,
|
||||||
|
"license": creator_url,
|
||||||
|
"acquireLicensePage": creator_url,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _truncate_description(text: str, max_length: int = 160) -> str:
|
def _truncate_description(text: str, max_length: int = 160) -> str:
|
||||||
"""Truncate text to a reasonable description length (for meta tags).
|
"""Truncate text to a reasonable description length (for meta tags).
|
||||||
|
|
||||||
|
|
@ -610,13 +646,21 @@ def drop_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespo
|
||||||
if campaign_owner
|
if campaign_owner
|
||||||
else "Twitch"
|
else "Twitch"
|
||||||
)
|
)
|
||||||
|
campaign_owner_url: str = (
|
||||||
|
request.build_absolute_uri(
|
||||||
|
reverse("twitch:organization_detail", args=[campaign_owner.twitch_id]),
|
||||||
|
)
|
||||||
|
if campaign_owner
|
||||||
|
else "https://www.twitch.tv/"
|
||||||
|
)
|
||||||
if campaign_image:
|
if campaign_image:
|
||||||
campaign_event["image"] = {
|
campaign_event["image"] = _build_image_object(
|
||||||
"@type": "ImageObject",
|
request,
|
||||||
"contentUrl": request.build_absolute_uri(campaign_image),
|
campaign_image,
|
||||||
"creditText": campaign_owner_name,
|
campaign_owner_name,
|
||||||
"copyrightNotice": campaign_owner_name,
|
campaign_owner_url,
|
||||||
}
|
copyright_notice=campaign_owner_name,
|
||||||
|
)
|
||||||
if campaign_owner:
|
if campaign_owner:
|
||||||
campaign_event["organizer"] = {
|
campaign_event["organizer"] = {
|
||||||
"@type": "Organization",
|
"@type": "Organization",
|
||||||
|
|
@ -927,13 +971,21 @@ class GameDetailView(DetailView):
|
||||||
if preferred_owner
|
if preferred_owner
|
||||||
else "Twitch"
|
else "Twitch"
|
||||||
)
|
)
|
||||||
|
owner_url: str = (
|
||||||
|
self.request.build_absolute_uri(
|
||||||
|
reverse("twitch:organization_detail", args=[preferred_owner.twitch_id]),
|
||||||
|
)
|
||||||
|
if preferred_owner
|
||||||
|
else "https://www.twitch.tv/"
|
||||||
|
)
|
||||||
if game.box_art_best_url:
|
if game.box_art_best_url:
|
||||||
game_schema["image"] = {
|
game_schema["image"] = _build_image_object(
|
||||||
"@type": "ImageObject",
|
self.request,
|
||||||
"contentUrl": self.request.build_absolute_uri(game.box_art_best_url),
|
game.box_art_best_url,
|
||||||
"creditText": owner_name,
|
owner_name,
|
||||||
"copyrightNotice": owner_name,
|
owner_url,
|
||||||
}
|
copyright_notice=owner_name,
|
||||||
|
)
|
||||||
if owners:
|
if owners:
|
||||||
game_schema["publisher"] = {
|
game_schema["publisher"] = {
|
||||||
"@type": "Organization",
|
"@type": "Organization",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue