Compare commits
No commits in common. "0101a82ddf52e9a7357c1422cbd88d4f8664bb8a" and "768d986556a227a9ab3109dec10b8e5c738d184d" have entirely different histories.
0101a82ddf
...
768d986556
6 changed files with 59 additions and 54 deletions
|
|
@ -8,26 +8,26 @@
|
||||||
{% if badge_sets %}
|
{% if badge_sets %}
|
||||||
{% for data in badge_data %}
|
{% for data in badge_data %}
|
||||||
<h2>
|
<h2>
|
||||||
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">{{ data.set.set_id }}</a>
|
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">[{{ data.set.set_id }}]</a>
|
||||||
</h2>
|
</h2>
|
||||||
<table>
|
{% for badge in data.badges %}
|
||||||
{% for badge in data.badges %}
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 40px;">
|
<td style="width: 40px;">
|
||||||
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">
|
<a href="{% url 'twitch:badge_set_detail' set_id=data.set.set_id %}">
|
||||||
{% picture badge.image_url_4x alt=badge.title width=36 %}
|
{% picture badge.image_url_4x alt=badge.title width=36 height=36 %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>{{ badge.title }}</strong>
|
<strong>{{ badge.title }}</strong>
|
||||||
{% if badge.description != badge.title %}
|
{% if badge.description != badge.title %}
|
||||||
<br />
|
<br>
|
||||||
{{ badge.description }}
|
{{ badge.description }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</table>
|
||||||
</table>
|
{% endfor %}
|
||||||
<br />
|
<br />
|
||||||
{% if data.badges|length > 1 %}<small>versions: {{ data.badges|length }}</small>{% endif %}
|
{% if data.badges|length > 1 %}<small>versions: {{ data.badges|length }}</small>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,25 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ badge_set.set_id }}</h1>
|
<h1>{{ badge_set.set_id }}</h1>
|
||||||
{% if badges %}
|
{% if badges %}
|
||||||
|
<h2>
|
||||||
|
{{ badges.count }}
|
||||||
|
{% if badges.count == 1 %}
|
||||||
|
version
|
||||||
|
{% else %}
|
||||||
|
versions
|
||||||
|
{% endif %}
|
||||||
|
</h2>
|
||||||
<table>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th></th>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Images</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for badge in badges %}
|
{% for badge in badges %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -14,7 +32,7 @@
|
||||||
<code>{{ badge.badge_id }}</code>
|
<code>{{ badge.badge_id }}</code>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% picture badge.image_url_4x alt=badge.title width=72 style="width: 72px !important; height: 72px !important; object-fit: contain" %}
|
{% picture badge.image_url_4x alt=badge.title width=72 height=72 style="width: 72px !important; height: 72px !important; object-fit: contain" %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ badge.title }}</td>
|
<td>{{ badge.title }}</td>
|
||||||
<td>{{ badge.description }}</td>
|
<td>{{ badge.description }}</td>
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,19 @@
|
||||||
{% endblock extra_head %}
|
{% endblock extra_head %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main>
|
<main>
|
||||||
<h1>Active Twitch Drops Campaigns</h1>
|
<h1>Twitch Drops</h1>
|
||||||
|
<pre>
|
||||||
|
Latest drops are shown first within each game. Click on a campaign or game title to see more details.
|
||||||
|
Hover over the end time to see the exact date and time.
|
||||||
|
</pre>
|
||||||
<!-- RSS Feeds -->
|
<!-- RSS Feeds -->
|
||||||
<div>
|
<div style="margin-bottom: 1rem;">
|
||||||
<a href="{% url 'twitch:campaign_feed' %}"
|
<a href="{% url 'twitch:campaign_feed' %}"
|
||||||
title="RSS feed for all campaigns">[rss - all campaigns]</a>
|
style="margin-right: 1rem"
|
||||||
|
title="RSS feed for all campaigns">RSS feed for campaigns</a>
|
||||||
|
|
|
||||||
<a href="{% url 'twitch:campaign_feed_atom' %}"
|
<a href="{% url 'twitch:campaign_feed_atom' %}"
|
||||||
title="Atom feed for campaigns">[atom - all campaigns]</a>
|
title="Atom feed for campaigns">Atom feed for campaigns</a>
|
||||||
</div>
|
</div>
|
||||||
{% if campaigns_by_game %}
|
{% if campaigns_by_game %}
|
||||||
{% for game_id, game_data in campaigns_by_game.items %}
|
{% for game_id, game_data in campaigns_by_game.items %}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,14 @@
|
||||||
{{ game.display_name }}
|
{{ game.display_name }}
|
||||||
{% if game.display_name != game.name and game.name %}<small>({{ game.name }})</small>{% endif %}
|
{% if game.display_name != game.name and game.name %}<small>({{ game.name }})</small>{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
|
<!-- RSS Feeds -->
|
||||||
|
<div>
|
||||||
|
<a href="{% url 'twitch:game_campaign_feed' game.twitch_id %}"
|
||||||
|
title="RSS feed for {{ game.display_name }} campaigns">RSS feed for {{ game.display_name }} campaigns</a>
|
||||||
|
|
|
||||||
|
<a href="{% url 'twitch:game_campaign_feed_atom' game.twitch_id %}"
|
||||||
|
title="Atom feed for {{ game.display_name }} campaigns">Atom feed for {{ game.display_name }} campaigns</a>
|
||||||
|
</div>
|
||||||
<!-- Game image -->
|
<!-- Game image -->
|
||||||
{% if game.box_art_best_url %}
|
{% if game.box_art_best_url %}
|
||||||
{% picture game.box_art_best_url alt=game.name width=160 %}
|
{% picture game.box_art_best_url alt=game.name width=160 %}
|
||||||
|
|
@ -35,13 +43,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</small>
|
</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- RSS Feeds -->
|
|
||||||
<div>
|
|
||||||
<a href="{% url 'twitch:game_campaign_feed' game.twitch_id %}"
|
|
||||||
title="RSS feed for {{ game.display_name }} campaigns">[rss - {{ game.display_name|default:game.name|lower }}]</a>
|
|
||||||
<a href="{% url 'twitch:game_campaign_feed_atom' game.twitch_id %}"
|
|
||||||
title="Atom feed for {{ game.display_name }} campaigns">[atom - {{ game.display_name|default:game.name|lower }}]</a>
|
|
||||||
</div>
|
|
||||||
{% if active_campaigns %}
|
{% if active_campaigns %}
|
||||||
<h5 id="active-campaigns-header">Active Campaigns</h5>
|
<h5 id="active-campaigns-header">Active Campaigns</h5>
|
||||||
<table id="active-campaigns-table">
|
<table id="active-campaigns-table">
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,26 @@
|
||||||
Organizations
|
Organizations
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Organizations</h1>
|
<h1 id="page-title">Organizations</h1>
|
||||||
<div>
|
<!-- RSS Feeds -->
|
||||||
|
<div style="margin-bottom: 1rem;">
|
||||||
<a href="{% url 'twitch:organization_feed' %}"
|
<a href="{% url 'twitch:organization_feed' %}"
|
||||||
title="RSS feed for all organizations">[rss]</a>
|
style="margin-right: 1rem"
|
||||||
|
title="RSS feed for all organizations">RSS feed for organizations</a>
|
||||||
<a href="{% url 'twitch:organization_feed_atom' %}"
|
<a href="{% url 'twitch:organization_feed_atom' %}"
|
||||||
title="Atom feed for all organizations">[atom]</a>
|
title="Atom feed for all organizations">[atom]</a>
|
||||||
|
</div>
|
||||||
|
<!-- Export Options -->
|
||||||
|
<div style="margin-bottom: 1rem; display: flex; gap: 1rem;">
|
||||||
<a href="{% url 'twitch:export_organizations_csv' %}"
|
<a href="{% url 'twitch:export_organizations_csv' %}"
|
||||||
title="Export all organizations as CSV">[csv]</a>
|
title="Export all organizations as CSV">[csv]</a>
|
||||||
<a href="{% url 'twitch:export_organizations_json' %}"
|
<a href="{% url 'twitch:export_organizations_json' %}"
|
||||||
title="Export all organizations as JSON">[json]</a>
|
title="Export all organizations as JSON">[json]</a>
|
||||||
</div>
|
</div>
|
||||||
{% if orgs %}
|
{% if orgs %}
|
||||||
<ul>
|
<ul id="org-list">
|
||||||
{% for organization in orgs %}
|
{% for organization in orgs %}
|
||||||
<li>
|
<li id="org-{{ organization.twitch_id }}">
|
||||||
<a href="{% url 'twitch:organization_detail' organization.twitch_id %}">{{ organization.name }}</a>
|
<a href="{% url 'twitch:organization_detail' organization.twitch_id %}">{{ organization.name }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@ from django.core.paginator import PageNotAnInteger
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.core.serializers import serialize
|
from django.core.serializers import serialize
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.models import Case
|
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.db.models import Exists
|
from django.db.models import Exists
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.db.models import OuterRef
|
from django.db.models import OuterRef
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models import When
|
|
||||||
from django.db.models.functions import Trim
|
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 FileResponse
|
from django.http import FileResponse
|
||||||
|
|
@ -66,9 +64,10 @@ if TYPE_CHECKING:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from debug_toolbar.utils import QueryDict
|
from debug_toolbar.utils import QueryDict
|
||||||
from django.db.models import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
|
|
||||||
logger: logging.Logger = logging.getLogger("ttvdrops.views")
|
logger: logging.Logger = logging.getLogger("ttvdrops.views")
|
||||||
|
|
||||||
MIN_QUERY_LENGTH_FOR_FTS = 3
|
MIN_QUERY_LENGTH_FOR_FTS = 3
|
||||||
|
|
@ -2235,31 +2234,7 @@ def badge_set_detail_view(request: HttpRequest, set_id: str) -> HttpResponse:
|
||||||
msg = "No badge set found matching the query"
|
msg = "No badge set found matching the query"
|
||||||
raise Http404(msg) from exc
|
raise Http404(msg) from exc
|
||||||
|
|
||||||
def get_sorted_badges(badge_set: ChatBadgeSet) -> QuerySet[ChatBadge]:
|
badges: QuerySet[ChatBadge] = badge_set.badges.all() # pyright: ignore[reportAttributeAccessIssue]
|
||||||
badges = badge_set.badges.all() # pyright: ignore[reportAttributeAccessIssue]
|
|
||||||
|
|
||||||
def sort_badges(badge: ChatBadge) -> tuple:
|
|
||||||
"""Sort badges by badge_id, treating numeric IDs as integers.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
badge: The ChatBadge to sort.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A tuple used for sorting, where numeric badge_ids are sorted as integers.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return (int(badge.badge_id),)
|
|
||||||
except ValueError:
|
|
||||||
return (badge.badge_id,)
|
|
||||||
|
|
||||||
sorted_badges: list[ChatBadge] = sorted(badges, key=sort_badges)
|
|
||||||
badge_ids: list[int] = [badge.pk for badge in sorted_badges]
|
|
||||||
preserved_order = Case(
|
|
||||||
*[When(pk=pk, then=pos) for pos, pk in enumerate(badge_ids)],
|
|
||||||
)
|
|
||||||
return ChatBadge.objects.filter(pk__in=badge_ids).order_by(preserved_order)
|
|
||||||
|
|
||||||
badges = get_sorted_badges(badge_set)
|
|
||||||
|
|
||||||
# Attach award_campaigns attribute to each badge for template use
|
# Attach award_campaigns attribute to each badge for template use
|
||||||
for badge in badges:
|
for badge in badges:
|
||||||
|
|
@ -2280,7 +2255,7 @@ def badge_set_detail_view(request: HttpRequest, set_id: str) -> HttpResponse:
|
||||||
)
|
)
|
||||||
set_data: list[dict[str, Any]] = json.loads(serialized_set)
|
set_data: list[dict[str, Any]] = json.loads(serialized_set)
|
||||||
|
|
||||||
if badges:
|
if badges.exists():
|
||||||
serialized_badges: str = serialize(
|
serialized_badges: str = serialize(
|
||||||
"json",
|
"json",
|
||||||
badges,
|
badges,
|
||||||
|
|
@ -2301,7 +2276,7 @@ def badge_set_detail_view(request: HttpRequest, set_id: str) -> HttpResponse:
|
||||||
set_data[0]["fields"]["badges"] = badges_data
|
set_data[0]["fields"]["badges"] = badges_data
|
||||||
|
|
||||||
badge_set_name: str = badge_set.set_id
|
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."
|
badge_set_description: str = f"Twitch chat badge set {badge_set_name} with {badges.count()} badge{'s' if badges.count() != 1 else ''} awarded through drop campaigns."
|
||||||
|
|
||||||
badge_schema: dict[str, Any] = {
|
badge_schema: dict[str, Any] = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue