Refactor debug view
This commit is contained in:
parent
6d5f014134
commit
e9f6b4489a
2 changed files with 76 additions and 35 deletions
|
|
@ -6,12 +6,12 @@
|
||||||
<h1>Debug Data Integrity Report</h1>
|
<h1>Debug Data Integrity Report</h1>
|
||||||
<p>Generated at: {{ now }}</p>
|
<p>Generated at: {{ now }}</p>
|
||||||
<section>
|
<section>
|
||||||
<h2>Games Without Campaigns / Orgs ({{ games_without_orgs|length }})</h2>
|
<h2>Games Without an Assigned Owner ({{ games_without_owner|length }})</h2>
|
||||||
{% if games_without_orgs %}
|
{% if games_without_owner %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for game in games_without_orgs %}
|
{% for game in games_without_owner %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'twitch:game_detail' game.id %}">{{ game.display_name }}</a> ({{ game.id }})
|
<a href="{% url 'twitch:game_detail' game.id %}">{{ game.display_name }}</a> (ID: {{ game.id }})
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -25,7 +25,9 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for c in broken_image_campaigns %}
|
{% for c in broken_image_campaigns %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a> - {{ c.image_url|default:'(empty)' }}
|
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a>
|
||||||
|
(Game: <a href="{% url 'twitch:game_detail' c.game.id %}">{{ c.game.display_name }}</a>)
|
||||||
|
- URL: {{ c.image_url|default:'(empty)' }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -33,6 +35,31 @@
|
||||||
<p>None ✅</p>
|
<p>None ✅</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>Benefits With Broken Image URLs ({{ broken_benefit_images|length }})</h2>
|
||||||
|
{% if broken_benefit_images %}
|
||||||
|
<ul>
|
||||||
|
{% for b in broken_benefit_images %}
|
||||||
|
{# A benefit is linked to a game via a drop and a campaign. #}
|
||||||
|
{# We use the 'with' tag to get the first drop for cleaner access. #}
|
||||||
|
{% with first_drop=b.drops.all.0 %}
|
||||||
|
<li>
|
||||||
|
{{ b.name }}
|
||||||
|
{# Check if the relationship path to the game exists #}
|
||||||
|
{% if first_drop and first_drop.campaign and first_drop.campaign.game %}
|
||||||
|
(Game: <a href="{% url 'twitch:game_detail' first_drop.campaign.game.id %}">{{ first_drop.campaign.game.display_name }}</a>)
|
||||||
|
{% else %}
|
||||||
|
(Game: Not linked)
|
||||||
|
{% endif %}
|
||||||
|
- URL: {{ b.image_asset_url|default:'(empty)' }}
|
||||||
|
</li>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>None ✅</p>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Active Campaigns Missing Image ({{ active_missing_image|length }})</h2>
|
<h2>Active Campaigns Missing Image ({{ active_missing_image|length }})</h2>
|
||||||
{% if active_missing_image %}
|
{% if active_missing_image %}
|
||||||
|
|
@ -40,20 +67,7 @@
|
||||||
{% for c in active_missing_image %}
|
{% for c in active_missing_image %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a>
|
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a>
|
||||||
</li>
|
(Game: <a href="{% url 'twitch:game_detail' c.game.id %}">{{ c.game.display_name }}</a>)
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p>None ✅</p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>Benefits With Broken Image URLs ({{ broken_benefit_images|length }})</h2>
|
|
||||||
{% if broken_benefit_images %}
|
|
||||||
<ul>
|
|
||||||
{% for b in broken_benefit_images %}
|
|
||||||
<li>
|
|
||||||
{{ b.name }} (Game: <a href="{% url 'twitch:game_detail' b.game.id %}">{{ b.game.display_name }}</a>) - {{ b.image_asset_url|default:'(empty)' }}
|
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -67,7 +81,9 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for d in drops_without_benefits %}
|
{% for d in drops_without_benefits %}
|
||||||
<li>
|
<li>
|
||||||
{{ d.name }} (Campaign: <a href="{% url 'twitch:campaign_detail' d.campaign.id %}">{{ d.campaign.name }}</a>)
|
{{ d.name }}
|
||||||
|
(Campaign: <a href="{% url 'twitch:campaign_detail' d.campaign.id %}">{{ d.campaign.name }}</a>
|
||||||
|
in Game: <a href="{% url 'twitch:game_detail' d.campaign.game.id %}">{{ d.campaign.game.display_name }}</a>)
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -81,7 +97,9 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for c in invalid_date_campaigns %}
|
{% for c in invalid_date_campaigns %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a> - Start: {{ c.start_at|default:'(none)' }} / End: {{ c.end_at|default:'(none)' }}
|
<a href="{% url 'twitch:campaign_detail' c.id %}">{{ c.name }}</a>
|
||||||
|
(Game: <a href="{% url 'twitch:game_detail' c.game.id %}">{{ c.game.display_name }}</a>)
|
||||||
|
- Start: {{ c.start_at|default:'(none)' }} / End: {{ c.end_at|default:'(none)' }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -96,14 +114,16 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Game</th>
|
<th>Game</th>
|
||||||
<th>Name</th>
|
<th>Campaign Name</th>
|
||||||
<th>Count</th>
|
<th>Count</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for row in duplicate_name_campaigns %}
|
{% for row in duplicate_name_campaigns %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ row.game_id }}</td>
|
<td>
|
||||||
|
<a href="{% url 'twitch:game_detail' row.game_id %}">{{ row.game__display_name }}</a>
|
||||||
|
</td>
|
||||||
<td>{{ row.name }}</td>
|
<td>{{ row.name }}</td>
|
||||||
<td>{{ row.name_count }}</td>
|
<td>{{ row.name_count }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ from typing import TYPE_CHECKING, Any, cast
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db import models
|
from django.db.models import Count, F, Prefetch, Q
|
||||||
from django.db.models import Count, Prefetch, Q
|
from django.db.models.functions import Trim
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
@ -351,36 +352,56 @@ def debug_view(request: HttpRequest) -> HttpResponse:
|
||||||
"""
|
"""
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
||||||
# Games with no organizations (no campaigns linking to an org)
|
# Games with no assigned owner organization
|
||||||
games_without_orgs: QuerySet[Game, Game] = Game.objects.filter(drop_campaigns__isnull=True).order_by("display_name")
|
games_without_owner: QuerySet[Game] = Game.objects.filter(owner__isnull=True).order_by("display_name")
|
||||||
|
|
||||||
# Campaigns with missing or obviously broken images (empty or very short or not http)
|
# Campaigns with missing or obviously broken images (empty or not starting with http)
|
||||||
broken_image_campaigns: QuerySet[DropCampaign, DropCampaign] = DropCampaign.objects.filter(
|
broken_image_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
||||||
Q(image_url__isnull=True) | Q(image_url__exact="") | ~Q(image_url__startswith="http")
|
Q(image_url__isnull=True) | Q(image_url__exact="") | ~Q(image_url__startswith="http")
|
||||||
).select_related("game")
|
).select_related("game")
|
||||||
|
|
||||||
# Benefits with missing images
|
# Benefits with missing images
|
||||||
broken_benefit_images: QuerySet[DropBenefit, DropBenefit] = DropBenefit.objects.filter(
|
broken_benefit_images: QuerySet[DropBenefit] = (
|
||||||
Q(image_asset_url__isnull=True) | Q(image_asset_url__exact="") | ~Q(image_asset_url__startswith="http")
|
DropBenefit.objects.annotate(
|
||||||
).prefetch_related(Prefetch("drops", queryset=TimeBasedDrop.objects.select_related("campaign__game")))
|
trimmed_url=Trim("image_asset_url") # Create a temporary field with no whitespace
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
Q(image_asset_url__isnull=True)
|
||||||
|
| Q(trimmed_url__exact="") # Check the trimmed URL
|
||||||
|
| ~Q(image_asset_url__startswith="http")
|
||||||
|
)
|
||||||
|
.prefetch_related(
|
||||||
|
# Prefetch the path to the game to avoid N+1 queries in the template
|
||||||
|
Prefetch("drops", queryset=TimeBasedDrop.objects.select_related("campaign__game"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Time-based drops without any benefits
|
# Time-based drops without any benefits
|
||||||
drops_without_benefits: QuerySet[TimeBasedDrop, TimeBasedDrop] = TimeBasedDrop.objects.filter(benefits__isnull=True).select_related("campaign")
|
drops_without_benefits: QuerySet[TimeBasedDrop] = TimeBasedDrop.objects.filter(benefits__isnull=True).select_related("campaign__game")
|
||||||
|
|
||||||
# Campaigns with invalid dates (start after end or missing either)
|
# Campaigns with invalid dates (start after end or missing either)
|
||||||
invalid_date_campaigns: QuerySet[DropCampaign, DropCampaign] = DropCampaign.objects.filter(
|
invalid_date_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
||||||
Q(start_at__gt=models.F("end_at")) | Q(start_at__isnull=True) | Q(end_at__isnull=True)
|
Q(start_at__gt=F("end_at")) | Q(start_at__isnull=True) | Q(end_at__isnull=True)
|
||||||
).select_related("game")
|
).select_related("game")
|
||||||
|
|
||||||
# Duplicate campaign names per game
|
# Duplicate campaign names per game. We retrieve the game's name for user-friendly display.
|
||||||
duplicate_name_campaigns = DropCampaign.objects.values("game_id", "name").annotate(name_count=Count("id")).filter(name_count__gt=1).order_by("-name_count")
|
duplicate_name_campaigns = (
|
||||||
|
DropCampaign.objects.values("game_id", "game__display_name", "name")
|
||||||
|
.annotate(name_count=Count("id"))
|
||||||
|
.filter(name_count__gt=1)
|
||||||
|
.order_by("game__display_name", "name")
|
||||||
|
)
|
||||||
|
|
||||||
# Campaigns currently active but image missing
|
# Campaigns currently active but image missing
|
||||||
active_missing_image = DropCampaign.objects.filter(start_at__lte=now, end_at__gte=now).filter(Q(image_url__isnull=True) | Q(image_url__exact=""))
|
active_missing_image: QuerySet[DropCampaign] = (
|
||||||
|
DropCampaign.objects.filter(start_at__lte=now, end_at__gte=now)
|
||||||
|
.filter(Q(image_url__isnull=True) | Q(image_url__exact="") | ~Q(image_url__startswith="http"))
|
||||||
|
.select_related("game")
|
||||||
|
)
|
||||||
|
|
||||||
context: dict[str, Any] = {
|
context: dict[str, Any] = {
|
||||||
"now": now,
|
"now": now,
|
||||||
"games_without_orgs": games_without_orgs,
|
"games_without_owner": games_without_owner,
|
||||||
"broken_image_campaigns": broken_image_campaigns,
|
"broken_image_campaigns": broken_image_campaigns,
|
||||||
"broken_benefit_images": broken_benefit_images,
|
"broken_benefit_images": broken_benefit_images,
|
||||||
"drops_without_benefits": drops_without_benefits,
|
"drops_without_benefits": drops_without_benefits,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue