Remove debug JSON from templates and views

This commit is contained in:
Joakim Hellsén 2026-03-17 05:01:49 +01:00
commit 92ca0404a6
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
13 changed files with 4 additions and 329 deletions

View file

@ -7,13 +7,11 @@
<h1>{{ badge_sets.count }} Twitch Chat Badges</h1>
{% if badge_sets %}
{% for data in badge_data %}
<!-- {{ data.set.set_id }} - {{ data.badges|length }} version{% if data.badges|length > 1 %}s{% endif %} -->
<h2>
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">{{ data.set.set_id }}</a>
</h2>
<table>
{% for badge in data.badges %}
<!-- {{ badge.title }} {% if badge.description != badge.title %}- {{ badge.description }}{% else %}{% endif %} -->
<tr>
<td style="width: 40px;">
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">

View file

@ -9,7 +9,6 @@
<table>
<tbody>
{% for badge in badges %}
<!-- {{ badge.title }} {% if badge.description != badge.title %}- {{ badge.description }}{% else %}{% endif %} -->
<tr>
<td>
<code>{{ badge.badge_id }}</code>
@ -48,5 +47,4 @@
{% else %}
<p>No badges found in this set.</p>
{% endif %}
{{ set_data|safe }}
{% endblock content %}

View file

@ -114,7 +114,6 @@
<table style="border-collapse: collapse; width: 100%;">
<tbody>
{% for drop in drops %}
<!-- {{ drop.drop.name }} - {{ drop.drop.benefits.all|join:", " }} -->
<tr>
<td>
{% for benefit in drop.drop.benefits.all %}
@ -177,7 +176,6 @@
<h5>Allowed Channels</h5>
<div>
{% for channel in allowed_channels %}
<!-- {{ channel.display_name }} https://www.twitch.tv/{{ channel.display_name }} -->
<a href="{% url 'twitch:channel_detail' channel.twitch_id %}">{{ channel.display_name }}</a>
{% endfor %}
</div>
@ -189,6 +187,4 @@
Go to a participating live channel
</a>
{% endif %}
<!-- Campaign JSON -->
{{ campaign_data|safe }}
{% endblock content %}

View file

@ -1,7 +1,6 @@
{% extends "base.html" %}
{% load static %}
{% load image_tags %}
{% load image_tags %}
{% block title %}
Drop Campaigns
{% endblock title %}
@ -47,7 +46,6 @@
<select name="game">
<option value="">All Games</option>
{% for game in games %}
<!-- Game option with Twitch ID {{ game.twitch_id }} and display name "{{ game.display_name }}" -->
<option value="{{ game.twitch_id }}"
{% if selected_game == game.twitch_id %}selected{% endif %}>
{{ game.display_name|default:game.name|default:game.slug|default:game.twitch_id }}
@ -58,7 +56,6 @@
<select id="status" name="status">
<option value="">All Statuses</option>
{% for status in status_options %}
<!-- Status option "{{ status }}" -->
<option value="{{ status }}"
{% if selected_status == status %}selected{% endif %}>{{ status|title }}</option>
{% endfor %}
@ -71,7 +68,6 @@
{% if campaigns %}
{% regroup campaigns by game as campaigns_by_game %}
{% for game_group in campaigns_by_game %}
<!-- Game group for game "{{ game_group.grouper.display_name }}" with {{ game_group.list|length }} campaigns -->
<section>
<div style="display: flex; gap: 1rem;">
<div>
@ -108,9 +104,6 @@
<div style="overflow-x: auto;">
<div style="display: flex; gap: 1rem; min-width: max-content;">
{% for campaign in game_group.list %}
<!-- Campaign "{{ campaign.clean_name }}" with Twitch ID {{ campaign.twitch_id }} -->
<!-- https://ttvdrops.lovinator.space{% url 'twitch:campaign_detail' campaign.twitch_id %} -->
<!-- https://ttvdrops.lovinator.space{{ campaign.image_best_url }} -->
<article style="display: flex;
flex-direction: column;
align-items: flex-start;
@ -165,7 +158,6 @@
{% endif %}
<!-- Pagination -->
{% if is_paginated %}
<!-- {{ page_obj.paginator.count }} total campaigns, showing {{ page_obj.start_index }} to {{ page_obj.end_index }} on page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} -->
<nav style="text-align: center;">
<div>
{% if page_obj.has_previous %}

View file

@ -33,17 +33,13 @@
<table>
<tbody>
{% for campaign in active_campaigns %}
<!-- Campaign {{ campaign.name }} ({{ campaign.twitch_id }}) -->
<tr>
<td>
<a href="{% url 'twitch:campaign_detail' campaign.twitch_id %}">{{ campaign.clean_name }}</a>
{% if campaign.time_based_drops.all %}
<!-- If the campaign has time-based drops, show the benefits in a nested div -->
<div>
<!-- swag swag swag {{campaign.sorted_benefits}} -->
{% for benefit in campaign.sorted_benefits %}
<!-- Benefit {{ benefit.name }} ({{ benefit.twitch_id }}) -->
<!-- {{ benefit.image_best_url }} -->
<span title="{{ benefit.name }}">
{% if benefit.image_best_url or benefit.image_asset_url %}
<!-- Show the benefit image if available -->
@ -85,7 +81,6 @@
<table>
<tbody>
{% for campaign in upcoming_campaigns %}
<!-- Campaign {{ campaign.name }} ({{ campaign.twitch_id }}) -->
<tr>
<td>
<a href="{% url 'twitch:campaign_detail' campaign.twitch_id %}">{{ campaign.clean_name }}</a>
@ -93,7 +88,6 @@
<!-- If the campaign has time-based drops, show the benefits in a nested div -->
<div>
{% for benefit in campaign.sorted_benefits %}
<!-- Benefit {{ benefit.name }} ({{ benefit.twitch_id }}) -->
<span title="{{ benefit.name }}">
{% if benefit.image_best_url or benefit.image_asset_url %}
<!-- Show the benefit image if available -->
@ -135,7 +129,6 @@
<table>
<tbody>
{% for campaign in expired_campaigns %}
<!-- Campaign {{ campaign.name }} ({{ campaign.twitch_id }}) -->
<tr>
<td>
<a href="{% url 'twitch:campaign_detail' campaign.twitch_id %}">{{ campaign.clean_name }}</a>
@ -143,7 +136,6 @@
<!-- If the campaign has time-based drops, show the benefits in a nested div -->
<div>
{% for benefit in campaign.sorted_benefits %}
<!-- Benefit {{ benefit.name }} ({{ benefit.twitch_id }}) -->
<span title="{{ benefit.name }}">
{% if benefit.image_best_url or benefit.image_asset_url %}
<img src="{{ benefit.image_best_url|default:benefit.image_asset_url }}"
@ -181,5 +173,4 @@
{% if not active_campaigns and not upcoming_campaigns and not expired_campaigns %}
<p>No campaigns found for this channel.</p>
{% endif %}
{{ channel_data|safe }}
{% endblock content %}

View file

@ -63,7 +63,6 @@
<div style="flex: 1; overflow-x: auto;">
<div style="display: flex; gap: 1rem; min-width: max-content;">
{% for campaign_data in game_data.campaigns %}
<!-- {{ campaign_data.campaign.name }} -->
<article style="display: flex;
flex-direction: column;
align-items: center;
@ -106,7 +105,6 @@
{% if campaign_data.campaign.allow_is_enabled %}
{% if campaign_data.allowed_channels %}
{% for channel in campaign_data.allowed_channels|slice:":5" %}
<!-- {{ channel.name }} -->
<li style="margin-bottom: 0.1rem;">
<a href="https://twitch.tv/{{ channel.name }}"
rel="nofollow ugc"
@ -132,14 +130,12 @@
{% endif %}
{% endif %}
{% if campaign_data.allowed_channels|length > 5 %}
<!-- {{ campaign_data.allowed_channels|length }} allowed channels -->
<li style="margin-bottom: 0.1rem; color: #666; font-style: italic;">
... and {{ campaign_data.allowed_channels|length|add:"-5" }} more
</li>
{% endif %}
{% else %}
{% if campaign_data.campaign.game.twitch_directory_url %}
<!--{{ campaign_data.campaign.game.display_name }} Twitch directory URL: {{ campaign_data.campaign.game.twitch_directory_url }} -->
<li>
<a href="{{ campaign_data.campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
@ -148,7 +144,6 @@
</a>
</li>
{% else %}
<!-- {{ campaign_data.campaign.game.display_name }} Twitch directory URL not available -->
<li>Failed to get Twitch directory URL :(</li>
{% endif %}
{% endif %}

View file

@ -6,8 +6,6 @@
{% block content %}
<h1>Emotes</h1>
{% for emote in emotes %}
<!-- Emote from campaign {{ emote.campaign.name }} -->
<!-- https://ttvdrops.lovinator.space{{ emote.image_url }} -->
<a href="{% url 'twitch:campaign_detail' emote.campaign.twitch_id %}"
title="{{ emote.campaign.name }}"
style="display: inline-block">

View file

@ -158,5 +158,4 @@
{% if not active_campaigns and not upcoming_campaigns and not expired_campaigns %}
<p id="no-campaigns-message">No campaigns found for this game.</p>
{% endif %}
{{ game_data|safe }}
{% endblock content %}

View file

@ -27,5 +27,4 @@
{% else %}
<p>No organizations found.</p>
{% endif %}
{{ orgs_data|safe }}
{% endblock content %}

View file

@ -44,6 +44,4 @@
{% endfor %}
</tbody>
</table>
<hr />
{{ org_data|safe }}
{% endblock content %}

View file

@ -164,6 +164,4 @@
</tr>
</tbody>
</table>
<hr />
{{ campaign_data|safe }}
{% endblock content %}

View file

@ -958,7 +958,7 @@ class TestChannelListView:
client: Client,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Game detail JSON payload should use `owners` (M2M), not stale `owner`."""
"""Game detail view should no longer expose debug JSON payload in context."""
org: Organization = Organization.objects.create(
twitch_id="org-game-detail",
name="Org Game Detail",
@ -970,17 +970,10 @@ class TestChannelListView:
)
game.owners.add(org)
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
assert response.status_code == 200
game_data: dict[str, Any] = response.context["game_data"]
fields: dict[str, Any] = game_data["fields"]
assert "owners" in fields
assert fields["owners"] == [org.pk]
assert "owner" not in fields
assert "game_data" not in response.context
@pytest.mark.django_db
def test_org_list_view(self, client: Client) -> None:
@ -1703,7 +1696,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""VideoGame schema image should be an ImageObject, not a plain URL."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
assert response.status_code == 200
@ -1724,7 +1716,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""VideoGame ImageObject should carry creditText and copyrightNotice."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1739,7 +1730,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""VideoGame schema should omit image key when box_art is empty."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
game_no_art: Game = Game.objects.create(
twitch_id="game-no-art",
name="no_art_game",
@ -1760,7 +1750,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""VideoGame schema publisher name should match the owning organization."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1775,7 +1764,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""publisher.name and image.creditText should be the same value."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:game_detail", args=[game.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1788,7 +1776,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When owner.name is empty, twitch_id is used as credit fallback."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
nameless_org: Organization = Organization.objects.create(
twitch_id="org-nameless",
name="",
@ -1816,7 +1803,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Event schema image should be an ImageObject, not a plain URL string."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
assert response.status_code == 200
@ -1837,7 +1823,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Event ImageObject should carry creditText and copyrightNotice."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1853,7 +1838,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Event schema should omit image key when campaign has no image."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
campaign_no_img: DropCampaign = DropCampaign.objects.create(
twitch_id="camp-no-img",
name="No Image Campaign",
@ -1875,7 +1859,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Event schema organizer name should match the owning organization."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1889,7 +1872,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""organizer.name and image.creditText should be the same value."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id])
response: _MonkeyPatchedWSGIResponse = client.get(url)
@ -1902,7 +1884,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When campaign has no owning org, creditText falls back to 'Twitch'."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
game_no_owner: Game = Game.objects.create(
twitch_id="game-no-owner",
name="no_owner_game",
@ -1930,7 +1911,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When one owner is 'Twitch Gaming' and another is not, the non-generic one is used."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
twitch_gaming: Organization = Organization.objects.create(
twitch_id="twitch-gaming",
name="Twitch Gaming",
@ -1960,7 +1940,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When the only owner is 'Twitch Gaming', it is still used (no other choice)."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
twitch_gaming: Organization = Organization.objects.create(
twitch_id="twitch-gaming-solo",
name="Twitch Gaming",
@ -1985,7 +1964,6 @@ class TestImageObjectStructuredData:
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Campaign schema prefers a non-generic publisher over 'Twitch Gaming'."""
monkeypatch.setattr("twitch.views.format_and_color_json", lambda data: data)
twitch_gaming: Organization = Organization.objects.create(
twitch_id="twitch-gaming-camp",
name="Twitch Gaming",

View file

@ -14,7 +14,6 @@ from django.core.paginator import EmptyPage
from django.core.paginator import Page
from django.core.paginator import PageNotAnInteger
from django.core.paginator import Paginator
from django.core.serializers import serialize
from django.db.models import Case
from django.db.models import Count
from django.db.models import Prefetch
@ -28,9 +27,6 @@ from django.urls import reverse
from django.utils import timezone
from django.views.generic import DetailView
from django.views.generic import ListView
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers.data import JsonLexer
from twitch.models import Channel
from twitch.models import ChatBadge
@ -264,14 +260,6 @@ def org_list_view(request: HttpRequest) -> HttpResponse:
"""
orgs: QuerySet[Organization] = Organization.objects.all().order_by("name")
# Serialize all organizations
serialized_orgs: str = serialize(
"json",
orgs,
fields=("twitch_id", "name", "added_at", "updated_at"),
)
orgs_data: list[dict] = json.loads(serialized_orgs)
# CollectionPage schema for organizations list
collection_schema: dict[str, str] = {
"@context": "https://schema.org",
@ -288,7 +276,6 @@ def org_list_view(request: HttpRequest) -> HttpResponse:
)
context: dict[str, Any] = {
"orgs": orgs,
"orgs_data": format_and_color_json(orgs_data),
**seo_context,
}
@ -296,7 +283,7 @@ def org_list_view(request: HttpRequest) -> HttpResponse:
# MARK: /organizations/<twitch_id>/
def organization_detail_view(request: HttpRequest, twitch_id: str) -> HttpResponse: # noqa: PLR0914
def organization_detail_view(request: HttpRequest, twitch_id: str) -> HttpResponse:
"""Function-based view for organization detail.
Args:
@ -317,30 +304,6 @@ def organization_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespon
games: QuerySet[Game] = organization.games.all() # pyright: ignore[reportAttributeAccessIssue]
serialized_org: str = serialize(
"json",
[organization],
fields=("twitch_id", "name", "added_at", "updated_at"),
)
org_data: list[dict] = json.loads(serialized_org)
if games.exists():
serialized_games: str = serialize(
"json",
games,
fields=(
"twitch_id",
"slug",
"name",
"display_name",
"box_art",
"added_at",
"updated_at",
),
)
games_data: list[dict] = json.loads(serialized_games)
org_data[0]["fields"]["games"] = games_data
org_name: str = organization.name or organization.twitch_id
games_count: int = games.count()
s: Literal["", "s"] = "" if games_count == 1 else "s"
@ -394,7 +357,6 @@ def organization_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespon
context: dict[str, Any] = {
"organization": organization,
"games": games,
"org_data": format_and_color_json(org_data[0]),
**seo_context,
}
@ -505,22 +467,6 @@ def drop_campaign_list_view(request: HttpRequest) -> HttpResponse: # noqa: PLR0
return render(request, "twitch/campaign_list.html", context)
def format_and_color_json(data: dict[str, Any] | list[dict] | str) -> str:
"""Format and color a JSON string for HTML display.
Args:
data: Either a dictionary, list of dictionaries, or a JSON string to format.
Returns:
str: The formatted code with HTML styles.
"""
if isinstance(data, (dict, list)):
formatted_code: str = json.dumps(data, indent=4)
else:
formatted_code = data
return highlight(formatted_code, JsonLexer(), HtmlFormatter())
def _enhance_drops_with_context(
drops: QuerySet[TimeBasedDrop],
now: datetime.datetime,
@ -564,7 +510,7 @@ def _enhance_drops_with_context(
# MARK: /campaigns/<twitch_id>/
def drop_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpResponse: # noqa: PLR0914, PLR0915
def drop_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpResponse: # noqa: PLR0914
"""Function-based view for a drop campaign detail.
Args:
@ -598,96 +544,6 @@ def drop_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespo
.order_by("required_minutes_watched")
)
serialized_campaign: str = serialize(
"json",
[campaign],
fields=(
"twitch_id",
"name",
"description",
"details_url",
"account_link_url",
"image_url",
"start_at",
"end_at",
"allow_is_enabled",
"operation_names",
"game",
"created_at",
"updated_at",
),
)
campaign_data: list[dict[str, Any]] = json.loads(serialized_campaign)
if drops.exists():
badge_benefit_names: set[str] = {
benefit.name
for drop in drops
for benefit in drop.benefits.all()
if benefit.distribution_type == "BADGE" and benefit.name
}
badge_descriptions_by_title: dict[str, str] = dict(
ChatBadge.objects.filter(title__in=badge_benefit_names).values_list(
"title",
"description",
),
)
serialized_drops = serialize(
"json",
drops,
fields=(
"twitch_id",
"name",
"required_minutes_watched",
"required_subs",
"start_at",
"end_at",
"added_at",
"updated_at",
),
)
drops_data: list[dict[str, Any]] = json.loads(serialized_drops)
for i, drop in enumerate(drops):
drop_benefits: list[DropBenefit] = list(drop.benefits.all())
if drop_benefits:
serialized_benefits: str = serialize(
"json",
drop_benefits,
fields=(
"twitch_id",
"name",
"image_asset_url",
"added_at",
"updated_at",
"created_at",
"entitlement_limit",
"is_ios_available",
"distribution_type",
),
)
benefits_data: list[dict[str, Any]] = json.loads(serialized_benefits)
for benefit_data in benefits_data:
fields: dict[str, Any] = benefit_data.get("fields", {})
if fields.get("distribution_type") != "BADGE":
continue
# DropBenefit doesn't have a description field; fetch it from ChatBadge when possible.
if fields.get("description"):
continue
badge_description: str | None = badge_descriptions_by_title.get(
fields.get("name", ""),
)
if badge_description:
fields["description"] = badge_description
drops_data[i]["fields"]["benefits"] = benefits_data
campaign_data[0]["fields"]["drops"] = drops_data
now: datetime.datetime = timezone.now()
enhanced_drops: list[dict[str, Any]] = _enhance_drops_with_context(drops, now)
# Attach awarded_badge to each drop in enhanced_drops
@ -706,7 +562,6 @@ def drop_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespo
"campaign": campaign,
"now": now,
"drops": enhanced_drops,
"campaign_data": format_and_color_json(campaign_data[0]),
"owners": list(campaign.game.owners.all()),
"allowed_channels": getattr(campaign, "channels_ordered", []),
}
@ -1043,45 +898,6 @@ class GameDetailView(DetailView):
if campaign.end_at is not None and campaign.end_at < now
]
serialized_game: str = serialize(
"json",
[game],
fields=(
"twitch_id",
"slug",
"name",
"display_name",
"box_art",
"owners",
"added_at",
"updated_at",
),
)
game_data: list[dict[str, Any]] = json.loads(serialized_game)
if campaigns_list:
serialized_campaigns = serialize(
"json",
campaigns_list,
fields=(
"twitch_id",
"name",
"description",
"details_url",
"account_link_url",
"image_url",
"start_at",
"end_at",
"allow_is_enabled",
"game",
"operation_names",
"added_at",
"updated_at",
),
)
campaigns_data: list[dict[str, Any]] = json.loads(serialized_campaigns)
game_data[0]["fields"]["campaigns"] = campaigns_data
owners: list[Organization] = list(game.owners.all())
game_name: str = game.display_name or game.name or game.twitch_id
@ -1159,7 +975,6 @@ class GameDetailView(DetailView):
"owners": owners,
"drop_awarded_badges": drop_awarded_badges,
"now": now,
"game_data": format_and_color_json(game_data[0]),
**seo_context,
})
@ -1379,28 +1194,6 @@ def reward_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpRes
msg = "No reward campaign found matching the query"
raise Http404(msg) from exc
serialized_campaign: str = serialize(
"json",
[reward_campaign],
fields=(
"twitch_id",
"name",
"brand",
"summary",
"instructions",
"external_url",
"about_url",
"reward_value_url_param",
"starts_at",
"ends_at",
"is_sitewide",
"game",
"added_at",
"updated_at",
),
)
campaign_data: list[dict[str, Any]] = json.loads(serialized_campaign)
now: datetime.datetime = timezone.now()
campaign_name: str = reward_campaign.name or reward_campaign.twitch_id
@ -1480,7 +1273,6 @@ def reward_campaign_detail_view(request: HttpRequest, twitch_id: str) -> HttpRes
context: dict[str, Any] = {
"reward_campaign": reward_campaign,
"now": now,
"campaign_data": format_and_color_json(campaign_data[0]),
"is_active": reward_campaign.is_active,
**seo_context,
}
@ -1672,33 +1464,6 @@ class ChannelDetailView(DetailView):
if campaign.end_at is not None and campaign.end_at < now
]
serialized_channel: str = serialize(
"json",
[channel],
fields=("twitch_id", "name", "display_name", "added_at", "updated_at"),
)
channel_data: list[dict[str, Any]] = json.loads(serialized_channel)
if campaigns_list:
serialized_campaigns: str = serialize(
"json",
campaigns_list,
fields=(
"twitch_id",
"name",
"description",
"details_url",
"account_link_url",
"image_url",
"start_at",
"end_at",
"added_at",
"updated_at",
),
)
campaigns_data: list[dict[str, Any]] = json.loads(serialized_campaigns)
channel_data[0]["fields"]["campaigns"] = campaigns_data
name: str = channel.display_name or channel.name or channel.twitch_id
total_campaigns: int = len(campaigns_list)
description: str = f"{name} participates in {total_campaigns} drop campaign"
@ -1761,7 +1526,6 @@ class ChannelDetailView(DetailView):
"upcoming_campaigns": upcoming_campaigns,
"expired_campaigns": expired_campaigns,
"now": now,
"channel_data": format_and_color_json(channel_data[0]),
**seo_context,
})
@ -1878,34 +1642,6 @@ def badge_set_detail_view(request: HttpRequest, set_id: str) -> HttpResponse:
).distinct()
badge.award_campaigns = list(campaigns) # pyright: ignore[reportAttributeAccessIssue]
# Serialize for JSON display
serialized_set: str = serialize(
"json",
[badge_set],
fields=("set_id", "added_at", "updated_at"),
)
set_data: list[dict[str, Any]] = json.loads(serialized_set)
if badges:
serialized_badges: str = serialize(
"json",
badges,
fields=(
"badge_id",
"image_url_1x",
"image_url_2x",
"image_url_4x",
"title",
"description",
"click_action",
"click_url",
"added_at",
"updated_at",
),
)
badges_data: list[dict[str, Any]] = json.loads(serialized_badges)
set_data[0]["fields"]["badges"] = badges_data
badge_set_name: str = badge_set.set_id
badge_set_description: str = f"Twitch chat badge set {badge_set_name} with {len(badges)} badge{'s' if len(badges) != 1 else ''} awarded through drop campaigns."
@ -1927,7 +1663,6 @@ def badge_set_detail_view(request: HttpRequest, set_id: str) -> HttpResponse:
context: dict[str, Any] = {
"badge_set": badge_set,
"badges": badges,
"set_data": format_and_color_json(set_data[0]),
**seo_context,
}