Add section markers

This commit is contained in:
Joakim Hellsén 2025-09-13 23:17:17 +02:00
commit 007b8f7ec6
7 changed files with 44 additions and 4 deletions

View file

@ -1,12 +1,17 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
from django.urls import path from django.urls import path
from accounts import views from accounts import views
if TYPE_CHECKING:
from django.urls.resolvers import URLPattern
app_name = "accounts" app_name = "accounts"
urlpatterns = [ urlpatterns: list[URLPattern] = [
path("login/", views.CustomLoginView.as_view(), name="login"), path("login/", views.CustomLoginView.as_view(), name="login"),
path("logout/", views.CustomLogoutView.as_view(), name="logout"), path("logout/", views.CustomLogoutView.as_view(), name="logout"),
path("signup/", views.SignUpView.as_view(), name="signup"), path("signup/", views.SignUpView.as_view(), name="signup"),

View file

@ -30,13 +30,13 @@ class CustomLoginView(LoginView):
Returns: Returns:
str: URL to redirect to after successful login. str: URL to redirect to after successful login.
""" """
return reverse_lazy("twitch:dashboard") return reverse_lazy("twitch:dashboard") # pyright: ignore[reportReturnType]
class CustomLogoutView(LogoutView): class CustomLogoutView(LogoutView):
"""Custom logout view.""" """Custom logout view."""
next_page = reverse_lazy("twitch:dashboard") next_page = reverse_lazy("twitch:dashboard") # pyright: ignore[reportAssignmentType]
http_method_names = ["get", "post", "options"] http_method_names = ["get", "post", "options"]
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:

View file

@ -5,6 +5,7 @@ from django.contrib import admin
from twitch.models import DropBenefit, DropBenefitEdge, DropCampaign, Game, Organization, TimeBasedDrop from twitch.models import DropBenefit, DropBenefitEdge, DropCampaign, Game, Organization, TimeBasedDrop
# MARK: Game
@admin.register(Game) @admin.register(Game)
class GameAdmin(admin.ModelAdmin): class GameAdmin(admin.ModelAdmin):
"""Admin configuration for Game model.""" """Admin configuration for Game model."""
@ -14,6 +15,7 @@ class GameAdmin(admin.ModelAdmin):
readonly_fields = ("added_at", "updated_at") readonly_fields = ("added_at", "updated_at")
# MARK: Organization
@admin.register(Organization) @admin.register(Organization)
class OrganizationAdmin(admin.ModelAdmin): class OrganizationAdmin(admin.ModelAdmin):
"""Admin configuration for Organization model.""" """Admin configuration for Organization model."""
@ -30,6 +32,7 @@ class TimeBasedDropInline(admin.TabularInline):
extra = 0 extra = 0
# MARK: DropCampaign
@admin.register(DropCampaign) @admin.register(DropCampaign)
class DropCampaignAdmin(admin.ModelAdmin): class DropCampaignAdmin(admin.ModelAdmin):
"""Admin configuration for DropCampaign model.""" """Admin configuration for DropCampaign model."""
@ -48,6 +51,7 @@ class DropBenefitEdgeInline(admin.TabularInline):
extra = 0 extra = 0
# MARK: TimeBasedDrop
@admin.register(TimeBasedDrop) @admin.register(TimeBasedDrop)
class TimeBasedDropAdmin(admin.ModelAdmin): class TimeBasedDropAdmin(admin.ModelAdmin):
"""Admin configuration for TimeBasedDrop model.""" """Admin configuration for TimeBasedDrop model."""
@ -68,6 +72,7 @@ class TimeBasedDropAdmin(admin.ModelAdmin):
inlines = [DropBenefitEdgeInline] inlines = [DropBenefitEdgeInline]
# MARK: DropBenefit
@admin.register(DropBenefit) @admin.register(DropBenefit)
class DropBenefitAdmin(admin.ModelAdmin): class DropBenefitAdmin(admin.ModelAdmin):
"""Admin configuration for DropBenefit model.""" """Admin configuration for DropBenefit model."""

View file

@ -12,6 +12,7 @@ if TYPE_CHECKING:
import datetime import datetime
# MARK: /rss/organizations/
class OrganizationFeed(Feed): class OrganizationFeed(Feed):
"""RSS feed for latest organizations.""" """RSS feed for latest organizations."""
@ -36,6 +37,7 @@ class OrganizationFeed(Feed):
return reverse("twitch:organization_detail", args=[item.pk]) return reverse("twitch:organization_detail", args=[item.pk])
# MARK: /rss/games/
class GameFeed(Feed): class GameFeed(Feed):
"""RSS feed for latest games.""" """RSS feed for latest games."""
@ -60,6 +62,7 @@ class GameFeed(Feed):
return reverse("twitch:game_detail", args=[item.pk]) return reverse("twitch:game_detail", args=[item.pk])
# MARK: /rss/campaigns/
class DropCampaignFeed(Feed): class DropCampaignFeed(Feed):
"""RSS feed for latest drop campaigns.""" """RSS feed for latest drop campaigns."""

View file

@ -19,6 +19,7 @@ if TYPE_CHECKING:
logger: logging.Logger = logging.getLogger("ttvdrops") logger: logging.Logger = logging.getLogger("ttvdrops")
# MARK: Organization
class Organization(auto_prefetch.Model): class Organization(auto_prefetch.Model):
"""Represents an organization on Twitch that can own drop campaigns.""" """Represents an organization on Twitch that can own drop campaigns."""
@ -63,6 +64,7 @@ class Organization(auto_prefetch.Model):
return self.name or self.id return self.name or self.id
# MARK: Game
class Game(auto_prefetch.Model): class Game(auto_prefetch.Model):
"""Represents a game on Twitch.""" """Represents a game on Twitch."""
@ -204,6 +206,7 @@ class Game(auto_prefetch.Model):
return "" return ""
# MARK: Channel
class Channel(auto_prefetch.Model): class Channel(auto_prefetch.Model):
"""Represents a Twitch channel that can participate in drop campaigns.""" """Represents a Twitch channel that can participate in drop campaigns."""
@ -248,6 +251,7 @@ class Channel(auto_prefetch.Model):
return self.display_name or self.name or self.id return self.display_name or self.name or self.id
# MARK: DropCampaign
class DropCampaign(auto_prefetch.Model): class DropCampaign(auto_prefetch.Model):
"""Represents a Twitch drop campaign.""" """Represents a Twitch drop campaign."""
@ -409,6 +413,7 @@ class DropCampaign(auto_prefetch.Model):
return self.image_url or "" return self.image_url or ""
# MARK: DropBenefit
class DropBenefit(auto_prefetch.Model): class DropBenefit(auto_prefetch.Model):
"""Represents a benefit that can be earned from a drop.""" """Represents a benefit that can be earned from a drop."""
@ -501,6 +506,7 @@ class DropBenefit(auto_prefetch.Model):
return self.image_asset_url or "" return self.image_asset_url or ""
# MARK: TimeBasedDrop
class TimeBasedDrop(auto_prefetch.Model): class TimeBasedDrop(auto_prefetch.Model):
"""Represents a time-based drop in a drop campaign.""" """Represents a time-based drop in a drop campaign."""
@ -593,6 +599,7 @@ class TimeBasedDrop(auto_prefetch.Model):
return self.name return self.name
# MARK: DropBenefitEdge
class DropBenefitEdge(auto_prefetch.Model): class DropBenefitEdge(auto_prefetch.Model):
"""Represents the relationship between a TimeBasedDrop and a DropBenefit.""" """Represents the relationship between a TimeBasedDrop and a DropBenefit."""
@ -634,6 +641,7 @@ class DropBenefitEdge(auto_prefetch.Model):
return f"{self.drop.name} - {self.benefit.name}" return f"{self.drop.name} - {self.benefit.name}"
# MARK: NotificationSubscription
class NotificationSubscription(auto_prefetch.Model): class NotificationSubscription(auto_prefetch.Model):
"""Users can subscribe to games to get notified.""" """Users can subscribe to games to get notified."""

View file

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
from django.urls import path from django.urls import path
from twitch import views from twitch import views
@ -9,9 +11,12 @@ from twitch.feeds import (
OrganizationFeed, OrganizationFeed,
) )
if TYPE_CHECKING:
from django.urls.resolvers import URLPattern
app_name = "twitch" app_name = "twitch"
urlpatterns = [ urlpatterns: list[URLPattern] = [
path("", views.dashboard, name="dashboard"), path("", views.dashboard, name="dashboard"),
path("search/", views.search_view, name="search"), path("search/", views.search_view, name="search"),
path("debug/", views.debug_view, name="debug"), path("debug/", views.debug_view, name="debug"),

View file

@ -35,6 +35,7 @@ MIN_QUERY_LENGTH_FOR_FTS = 3
MIN_SEARCH_RANK = 0.05 MIN_SEARCH_RANK = 0.05
# MARK: /search/
def search_view(request: HttpRequest) -> HttpResponse: def search_view(request: HttpRequest) -> HttpResponse:
"""Search view for all models. """Search view for all models.
@ -95,6 +96,7 @@ def search_view(request: HttpRequest) -> HttpResponse:
return render(request, "twitch/search_results.html", {"query": query, "results": results}) return render(request, "twitch/search_results.html", {"query": query, "results": results})
# MARK: /organizations/
class OrgListView(ListView): class OrgListView(ListView):
"""List view for organization.""" """List view for organization."""
@ -103,6 +105,7 @@ class OrgListView(ListView):
context_object_name = "orgs" context_object_name = "orgs"
# MARK: /organizations/<pk>/
class OrgDetailView(DetailView): class OrgDetailView(DetailView):
"""Detail view for organization.""" """Detail view for organization."""
@ -157,6 +160,7 @@ class OrgDetailView(DetailView):
return context return context
# MARK: /campaigns/
class DropCampaignListView(ListView): class DropCampaignListView(ListView):
"""List view for drop campaigns.""" """List view for drop campaigns."""
@ -213,6 +217,7 @@ def format_and_color_json(code: str) -> str:
return highlight(formatted_code, JsonLexer(), HtmlFormatter()) return highlight(formatted_code, JsonLexer(), HtmlFormatter())
# MARK: /campaigns/<pk>/
class DropCampaignDetailView(DetailView): class DropCampaignDetailView(DetailView):
"""Detail view for a drop campaign.""" """Detail view for a drop campaign."""
@ -342,6 +347,7 @@ class DropCampaignDetailView(DetailView):
return context return context
# MARK: /games/
class GamesGridView(ListView): class GamesGridView(ListView):
"""List view for games grouped by organization.""" """List view for games grouped by organization."""
@ -412,6 +418,7 @@ class GamesGridView(ListView):
return context return context
# MARK: /games/<pk>/
class GameDetailView(DetailView): class GameDetailView(DetailView):
"""Detail view for a game.""" """Detail view for a game."""
@ -574,6 +581,7 @@ def dashboard(request: HttpRequest) -> HttpResponse:
) )
# MARK: /debug/
@login_required @login_required
def debug_view(request: HttpRequest) -> HttpResponse: def debug_view(request: HttpRequest) -> HttpResponse:
"""Debug view showing potentially broken or inconsistent data. """Debug view showing potentially broken or inconsistent data.
@ -635,6 +643,7 @@ def debug_view(request: HttpRequest) -> HttpResponse:
return render(request, "twitch/debug.html", context) return render(request, "twitch/debug.html", context)
# MARK: /games/<pk>/subscribe/
@login_required @login_required
def subscribe_game_notifications(request: HttpRequest, game_id: str) -> HttpResponseRedirect: def subscribe_game_notifications(request: HttpRequest, game_id: str) -> HttpResponseRedirect:
"""Update Game notification for a user. """Update Game notification for a user.
@ -678,6 +687,7 @@ def subscribe_game_notifications(request: HttpRequest, game_id: str) -> HttpResp
return redirect("twitch:game_detail", pk=game.id) return redirect("twitch:game_detail", pk=game.id)
# MARK: /organizations/<pk>/subscribe/
@login_required @login_required
def subscribe_org_notifications(request: HttpRequest, org_id: str) -> HttpResponseRedirect: def subscribe_org_notifications(request: HttpRequest, org_id: str) -> HttpResponseRedirect:
"""Update Organization notification for a user. """Update Organization notification for a user.
@ -722,12 +732,14 @@ def subscribe_org_notifications(request: HttpRequest, org_id: str) -> HttpRespon
return redirect("twitch:organization_detail", pk=organization.id) return redirect("twitch:organization_detail", pk=organization.id)
# MARK: /games/list/
class GamesListView(GamesGridView): class GamesListView(GamesGridView):
"""List view for games in simple list format.""" """List view for games in simple list format."""
template_name = "twitch/games_list.html" template_name = "twitch/games_list.html"
# MARK: /docs/rss/
def docs_rss_view(request: HttpRequest) -> HttpResponse: def docs_rss_view(request: HttpRequest) -> HttpResponse:
"""View for /docs/rss that lists all available RSS feeds. """View for /docs/rss that lists all available RSS feeds.
@ -757,6 +769,7 @@ def docs_rss_view(request: HttpRequest) -> HttpResponse:
return render(request, "twitch/docs_rss.html", {"feeds": feeds}) return render(request, "twitch/docs_rss.html", {"feeds": feeds})
# MARK: /channels/
class ChannelListView(ListView): class ChannelListView(ListView):
"""List view for channels.""" """List view for channels."""
@ -793,6 +806,7 @@ class ChannelListView(ListView):
return context return context
# MARK: /channels/<pk>/
class ChannelDetailView(DetailView): class ChannelDetailView(DetailView):
"""Detail view for a channel.""" """Detail view for a channel."""