diff --git a/core/base_url.py b/core/base_url.py index 66efa86..7eb763d 100644 --- a/core/base_url.py +++ b/core/base_url.py @@ -7,6 +7,8 @@ from urllib.parse import urlsplit from django.conf import settings if TYPE_CHECKING: + from urllib.parse import SplitResult + from django.http import HttpRequest @@ -16,7 +18,7 @@ def _get_base_url() -> str: Returns: str: The configured BASE_URL without trailing slash. """ - base_url = getattr(settings, "BASE_URL", "") + base_url: str = getattr(settings, "BASE_URL", "") return base_url.rstrip("/") if base_url else "" @@ -33,7 +35,7 @@ def build_absolute_uri( Returns: str: Fully resolved absolute URL. """ - base_url = _get_base_url() + base_url: str = _get_base_url() if location is None: if request is not None: @@ -41,7 +43,7 @@ def build_absolute_uri( else: return f"{base_url}/" if base_url else "/" - parsed = urlsplit(location) + parsed: SplitResult = urlsplit(location) if parsed.scheme and parsed.netloc: return location @@ -58,7 +60,7 @@ def build_absolute_uri( def is_secure() -> bool: """Return whether the configured BASE_URL uses HTTPS.""" - base_url = _get_base_url() + base_url: str = _get_base_url() return base_url.startswith("https://") if base_url else False @@ -69,9 +71,9 @@ class _TTVDropsSite: def get_current_site(request: object) -> _TTVDropsSite: """Return a site-like object with domain derived from BASE_URL.""" - base_url = _get_base_url() - parts = urlsplit(base_url) - domain = parts.netloc or parts.path + base_url: str = _get_base_url() + parts: SplitResult = urlsplit(base_url) + domain: str = parts.netloc or parts.path return _TTVDropsSite(domain=domain) diff --git a/core/context_processors.py b/core/context_processors.py index 66476b8..a359ca2 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -12,6 +12,4 @@ def base_url(request: HttpRequest) -> dict[str, str]: Returns: dict[str, str]: A dictionary containing the BASE_URL. """ - return { - "BASE_URL": getattr(settings, "BASE_URL", ""), - } + return {"BASE_URL": getattr(settings, "BASE_URL", "")} diff --git a/core/tests/test_views.py b/core/tests/test_views.py index bf9327c..e97b8a7 100644 --- a/core/tests/test_views.py +++ b/core/tests/test_views.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + from django.test import RequestFactory from django.test import TestCase from django.test.utils import override_settings @@ -5,6 +7,9 @@ from django.urls import reverse from core.views import _build_base_url +if TYPE_CHECKING: + from django.test.client import _MonkeyPatchedWSGIResponse + @override_settings(ALLOWED_HOSTS=["example.com"]) class TestBuildBaseUrl(TestCase): @@ -16,7 +21,7 @@ class TestBuildBaseUrl(TestCase): def test_valid_base_url(self) -> None: """Test that the base URL is built correctly.""" - base_url = _build_base_url() + base_url: str = _build_base_url() assert base_url == "https://ttvdrops.lovinator.space" @@ -25,12 +30,16 @@ class TestSitemapViews(TestCase): def test_sitemap_twitch_channels_view(self) -> None: """Test that the Twitch channels sitemap view returns a valid XML response.""" - response = self.client.get(reverse("sitemap-twitch-channels")) + response: _MonkeyPatchedWSGIResponse = self.client.get( + reverse("sitemap-twitch-channels"), + ) assert response.status_code == 200 assert " None: """Test that the Twitch drops sitemap view returns a valid XML response.""" - response = self.client.get(reverse("sitemap-twitch-drops")) + response: _MonkeyPatchedWSGIResponse = self.client.get( + reverse("sitemap-twitch-drops"), + ) assert response.status_code == 200 assert "/ path( - "datasets/download//", - dataset_backup_download_view, + route="datasets/download//", + view=dataset_backup_download_view, name="dataset_backup_download", ), # /docs/rss/ - path("docs/rss/", docs_rss_view, name="docs_rss"), + path( + route="docs/rss/", + view=docs_rss_view, + name="docs_rss", + ), # RSS feeds # /rss/campaigns/ - all active campaigns - path("rss/campaigns/", DropCampaignFeed(), name="campaign_feed"), + path( + route="rss/campaigns/", + view=DropCampaignFeed(), + name="campaign_feed", + ), # /rss/games/ - newly added games - path("rss/games/", GameFeed(), name="game_feed"), + path( + route="rss/games/", + view=GameFeed(), + name="game_feed", + ), # /rss/games//campaigns/ - active campaigns for a specific game path( - "rss/games//campaigns/", - GameCampaignFeed(), + route="rss/games//campaigns/", + view=GameCampaignFeed(), name="game_campaign_feed", ), # /rss/organizations/ - newly added organizations path( - "rss/organizations/", - OrganizationRSSFeed(), + route="rss/organizations/", + view=OrganizationRSSFeed(), name="organization_feed", ), # /rss/reward-campaigns/ - all active reward campaigns path( - "rss/reward-campaigns/", - RewardCampaignFeed(), + route="rss/reward-campaigns/", + view=RewardCampaignFeed(), name="reward_campaign_feed", ), # Atom feeds (added alongside RSS to preserve backward compatibility) - path("atom/campaigns/", DropCampaignAtomFeed(), name="campaign_feed_atom"), - path("atom/games/", GameAtomFeed(), name="game_feed_atom"), path( - "atom/games//campaigns/", + route="atom/campaigns/", + view=DropCampaignAtomFeed(), + name="campaign_feed_atom", + ), + path( + route="atom/games/", + view=GameAtomFeed(), + name="game_feed_atom", + ), + path( + route="atom/games//campaigns/", view=GameCampaignAtomFeed(), name="game_campaign_feed_atom", ), path( - "atom/organizations/", - OrganizationAtomFeed(), + route="atom/organizations/", + view=OrganizationAtomFeed(), name="organization_feed_atom", ), path( - "atom/reward-campaigns/", - RewardCampaignAtomFeed(), + route="atom/reward-campaigns/", + view=RewardCampaignAtomFeed(), name="reward_campaign_feed_atom", ), # Discord feeds (Atom feeds with Discord relative timestamps) path( - "discord/campaigns/", - DropCampaignDiscordFeed(), + route="discord/campaigns/", + view=DropCampaignDiscordFeed(), name="campaign_feed_discord", ), - path("discord/games/", GameDiscordFeed(), name="game_feed_discord"), path( - "discord/games//campaigns/", - GameCampaignDiscordFeed(), + route="discord/games/", + view=GameDiscordFeed(), + name="game_feed_discord", + ), + path( + route="discord/games//campaigns/", + view=GameCampaignDiscordFeed(), name="game_campaign_feed_discord", ), path( - "discord/organizations/", - OrganizationDiscordFeed(), + route="discord/organizations/", + view=OrganizationDiscordFeed(), name="organization_feed_discord", ), path( - "discord/reward-campaigns/", - RewardCampaignDiscordFeed(), + route="discord/reward-campaigns/", + view=RewardCampaignDiscordFeed(), name="reward_campaign_feed_discord", ), ] diff --git a/core/utils.py b/core/utils.py index f28a4ec..2adc70f 100644 --- a/core/utils.py +++ b/core/utils.py @@ -24,14 +24,14 @@ def _render_urlset_xml(sitemap_urls: list[dict[str, Any]]) -> str: Returns: str: Serialized XML for a containing the provided URLs. """ - urlset = Element("urlset") + urlset: Element[str] = Element("urlset") urlset.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9") for url_entry in sitemap_urls: - url = SubElement(urlset, "url") - loc = url_entry.get("loc") + url: Element[str] = SubElement(urlset, "url") + loc: str | None = url_entry.get("loc") if loc: - child = SubElement(url, "loc") + child: Element[str] = SubElement(url, "loc") child.text = loc return tostring(urlset, encoding="unicode") diff --git a/core/views.py b/core/views.py index c040c4e..6d26d1f 100644 --- a/core/views.py +++ b/core/views.py @@ -141,7 +141,9 @@ def _render_urlset_xml( xml += '\n' for url_entry in url_entries: xml += " \n" - loc = url_entry.get("loc") or url_entry.get("url") # Handle both keys + loc: str | None = url_entry.get("loc") or url_entry.get( + "url", + ) # Handle both keys if loc: xml += f" {loc}\n" if "lastmod" in url_entry: