Compare commits
2 commits
768d986556
...
0101a82ddf
| Author | SHA1 | Date | |
|---|---|---|---|
|
0101a82ddf |
|||
|
719e5d416b |
6 changed files with 54 additions and 59 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>
|
||||||
{% for badge in data.badges %}
|
<table>
|
||||||
<table>
|
{% for badge in data.badges %}
|
||||||
<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 height=36 %}
|
{% picture badge.image_url_4x alt=badge.title width=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>
|
||||||
</table>
|
{% endfor %}
|
||||||
{% endfor %}
|
</table>
|
||||||
<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,25 +6,7 @@
|
||||||
{% 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>
|
||||||
|
|
@ -32,7 +14,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 height=72 style="width: 72px !important; height: 72px !important; object-fit: contain" %}
|
{% picture badge.image_url_4x alt=badge.title width=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,19 +16,13 @@
|
||||||
{% endblock extra_head %}
|
{% endblock extra_head %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main>
|
<main>
|
||||||
<h1>Twitch Drops</h1>
|
<h1>Active Twitch Drops Campaigns</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 style="margin-bottom: 1rem;">
|
<div>
|
||||||
<a href="{% url 'twitch:campaign_feed' %}"
|
<a href="{% url 'twitch:campaign_feed' %}"
|
||||||
style="margin-right: 1rem"
|
title="RSS feed for all campaigns">[rss - all campaigns]</a>
|
||||||
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 feed for campaigns</a>
|
title="Atom feed for campaigns">[atom - all 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,14 +21,6 @@
|
||||||
{{ 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 %}
|
||||||
|
|
@ -43,6 +35,13 @@
|
||||||
{% 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,26 +3,21 @@
|
||||||
Organizations
|
Organizations
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 id="page-title">Organizations</h1>
|
<h1>Organizations</h1>
|
||||||
<!-- RSS Feeds -->
|
<div>
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<a href="{% url 'twitch:organization_feed' %}"
|
<a href="{% url 'twitch:organization_feed' %}"
|
||||||
style="margin-right: 1rem"
|
title="RSS feed for all organizations">[rss]</a>
|
||||||
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 id="org-list">
|
<ul>
|
||||||
{% for organization in orgs %}
|
{% for organization in orgs %}
|
||||||
<li id="org-{{ organization.twitch_id }}">
|
<li>
|
||||||
<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,12 +17,14 @@ 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
|
||||||
|
|
@ -64,10 +66,9 @@ 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.query import QuerySet
|
from django.db.models 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
|
||||||
|
|
@ -2234,7 +2235,31 @@ 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
|
||||||
|
|
||||||
badges: QuerySet[ChatBadge] = badge_set.badges.all() # pyright: ignore[reportAttributeAccessIssue]
|
def get_sorted_badges(badge_set: ChatBadgeSet) -> QuerySet[ChatBadge]:
|
||||||
|
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:
|
||||||
|
|
@ -2255,7 +2280,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.exists():
|
if badges:
|
||||||
serialized_badges: str = serialize(
|
serialized_badges: str = serialize(
|
||||||
"json",
|
"json",
|
||||||
badges,
|
badges,
|
||||||
|
|
@ -2276,7 +2301,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 {badges.count()} badge{'s' if badges.count() != 1 else ''} awarded through drop campaigns."
|
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_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