diff --git a/templates/twitch/badge_list.html b/templates/twitch/badge_list.html index 49e4016..0a9e41c 100644 --- a/templates/twitch/badge_list.html +++ b/templates/twitch/badge_list.html @@ -7,13 +7,11 @@

{{ badge_sets.count }} Twitch Chat Badges

{% if badge_sets %} {% for data in badge_data %} -

{{ data.set.set_id }}

{% for badge in data.badges %} -
diff --git a/templates/twitch/badge_set_detail.html b/templates/twitch/badge_set_detail.html index 3d203e6..c14bc1d 100644 --- a/templates/twitch/badge_set_detail.html +++ b/templates/twitch/badge_set_detail.html @@ -9,7 +9,6 @@ {% for badge in badges %} -
{{ badge.badge_id }} @@ -48,5 +47,4 @@ {% else %}

No badges found in this set.

{% endif %} - {{ set_data|safe }} {% endblock content %} diff --git a/templates/twitch/campaign_detail.html b/templates/twitch/campaign_detail.html index dc36335..b272668 100644 --- a/templates/twitch/campaign_detail.html +++ b/templates/twitch/campaign_detail.html @@ -114,7 +114,6 @@ {% for drop in drops %} -
{% for benefit in drop.drop.benefits.all %} @@ -177,7 +176,6 @@
Allowed Channels
{% for channel in allowed_channels %} - {{ channel.display_name }} {% endfor %}
@@ -189,6 +187,4 @@ Go to a participating live channel {% endif %} - - {{ campaign_data|safe }} {% endblock content %} diff --git a/templates/twitch/campaign_list.html b/templates/twitch/campaign_list.html index 9e904c0..b9f7bf8 100644 --- a/templates/twitch/campaign_list.html +++ b/templates/twitch/campaign_list.html @@ -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 @@ {% for status in status_options %} - {% endfor %} @@ -71,7 +68,6 @@ {% if campaigns %} {% regroup campaigns by game as campaigns_by_game %} {% for game_group in campaigns_by_game %} -
@@ -108,9 +104,6 @@
{% for campaign in game_group.list %} - - -
{% if page_obj.has_previous %} diff --git a/templates/twitch/channel_detail.html b/templates/twitch/channel_detail.html index c5ad5d7..49a7487 100644 --- a/templates/twitch/channel_detail.html +++ b/templates/twitch/channel_detail.html @@ -33,17 +33,13 @@ {% for campaign in active_campaigns %} -
{{ campaign.clean_name }} {% if campaign.time_based_drops.all %}
- {% for benefit in campaign.sorted_benefits %} - - {% if benefit.image_best_url or benefit.image_asset_url %} @@ -85,7 +81,6 @@ {% for campaign in upcoming_campaigns %} -
{{ campaign.clean_name }} @@ -93,7 +88,6 @@
{% for benefit in campaign.sorted_benefits %} - {% if benefit.image_best_url or benefit.image_asset_url %} @@ -135,7 +129,6 @@ {% for campaign in expired_campaigns %} -
{{ campaign.clean_name }} @@ -143,7 +136,6 @@
{% for benefit in campaign.sorted_benefits %} - {% if benefit.image_best_url or benefit.image_asset_url %} No campaigns found for this channel.

{% endif %} - {{ channel_data|safe }} {% endblock content %} diff --git a/templates/twitch/dashboard.html b/templates/twitch/dashboard.html index 134e050..d9e1434 100644 --- a/templates/twitch/dashboard.html +++ b/templates/twitch/dashboard.html @@ -63,7 +63,6 @@
{% for campaign_data in game_data.campaigns %} -
  • 5 %} -
  • ... and {{ campaign_data.allowed_channels|length|add:"-5" }} more
  • {% endif %} {% else %} {% if campaign_data.campaign.game.twitch_directory_url %} -
  • {% else %} -
  • Failed to get Twitch directory URL :(
  • {% endif %} {% endif %} diff --git a/templates/twitch/emote_gallery.html b/templates/twitch/emote_gallery.html index b42b6d6..83178a1 100644 --- a/templates/twitch/emote_gallery.html +++ b/templates/twitch/emote_gallery.html @@ -6,8 +6,6 @@ {% block content %}

    Emotes

    {% for emote in emotes %} - -
    diff --git a/templates/twitch/game_detail.html b/templates/twitch/game_detail.html index 1c871f1..a29a20f 100644 --- a/templates/twitch/game_detail.html +++ b/templates/twitch/game_detail.html @@ -158,5 +158,4 @@ {% if not active_campaigns and not upcoming_campaigns and not expired_campaigns %}

    No campaigns found for this game.

    {% endif %} - {{ game_data|safe }} {% endblock content %} diff --git a/templates/twitch/org_list.html b/templates/twitch/org_list.html index 838acae..2004f8d 100644 --- a/templates/twitch/org_list.html +++ b/templates/twitch/org_list.html @@ -27,5 +27,4 @@ {% else %}

    No organizations found.

    {% endif %} - {{ orgs_data|safe }} {% endblock content %} diff --git a/templates/twitch/organization_detail.html b/templates/twitch/organization_detail.html index 0464252..f460edc 100644 --- a/templates/twitch/organization_detail.html +++ b/templates/twitch/organization_detail.html @@ -44,6 +44,4 @@ {% endfor %}
    -
    - {{ org_data|safe }} {% endblock content %} diff --git a/templates/twitch/reward_campaign_detail.html b/templates/twitch/reward_campaign_detail.html index 234c6b5..1bbe29f 100644 --- a/templates/twitch/reward_campaign_detail.html +++ b/templates/twitch/reward_campaign_detail.html @@ -164,6 +164,4 @@
    -
    - {{ campaign_data|safe }} {% endblock content %} diff --git a/twitch/tests/test_views.py b/twitch/tests/test_views.py index 29b89fb..7586914 100644 --- a/twitch/tests/test_views.py +++ b/twitch/tests/test_views.py @@ -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", diff --git a/twitch/views.py b/twitch/views.py index b21cc71..78ccc9a 100644 --- a/twitch/views.py +++ b/twitch/views.py @@ -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// -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// -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, }