Skip to content

feat(#1612): add rss feed for latest downloads #2569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions downloads/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
path('release/<slug:release_slug>/', views.DownloadReleaseDetail.as_view(), name='download_release_detail'),
path('<slug:slug>/', views.DownloadOSList.as_view(), name='download_os_list'),
path('', views.DownloadHome.as_view(), name='download'),
path("feed.rss", views.ReleaseFeed(), name="feed"),
]
77 changes: 77 additions & 0 deletions downloads/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
from typing import Any

import requests
from datetime import datetime

from django.contrib.sites.shortcuts import get_current_site
from django.core.handlers.wsgi import WSGIRequest
from django.db.models import Prefetch
from django.urls import reverse
from django.utils import timezone
from django.views.generic import DetailView, TemplateView, ListView, RedirectView
from django.http import Http404
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed

from .models import OS, Release, ReleaseFile

Expand Down Expand Up @@ -147,3 +157,70 @@ def get_context_data(self, **kwargs):
)

return context


class ReleaseFeed(Feed):
"""Generate an RSS feed of the latest Python releases.

.. note:: It may seem like these are unused methods, but the superclass uses them
using Django's Syndication framework.
Docs: https://docs.djangoproject.com/en/4.2/ref/contrib/syndication/
"""

feed_type = Rss201rev2Feed
title = "Python Releases"
description = "Latest Python releases from Python.org"

@staticmethod
def link() -> str:
"""Return the URL to the main downloads page."""
return reverse("downloads:download")

def get_feed(self, obj: Any, request: WSGIRequest) -> Feed:
"""Store the request object for later use."""
self.request = request
return super().get_feed(obj, request)

def items(self) -> list[dict[str, Any]]:
"""Return the latest Python releases."""
return Release.objects.filter(is_published=True).order_by("-release_date")[:10]

@staticmethod
def _fetch_releases(url: str) -> list[dict[str, Any]]:
"""Grabs the latest Python releases from API."""
response = requests.get(url, timeout=10)
response.raise_for_status()
data = response.json()

sorted_releases = sorted(data, key=lambda x: x["release_date"], reverse=True)
return sorted_releases[:10]

def item_title(self, item: Release) -> str:
"""Return the release name as the item title."""
return item.name

def item_description(self, item: Release) -> str:
"""Return the release version and release date as the item description."""
return f"Version: {item.version}, Release Date: {item.release_date}"

def item_link(self, item: Release) -> str:
"""Return the URL to the release page on python.org."""
return reverse("downloads:download_release_detail", args=[item.slug])

def item_pubdate(self, item: Release) -> datetime | None:
"""Return the release date as the item publication date."""
if item.release_date:
if timezone.is_naive(item.release_date):
return timezone.make_aware(item.release_date)
return item.release_date
return None

def item_guid(self, item: Release) -> str:
"""Return a unique ID for the item based on DB record."""
return str(item.pk)

def create_url(self, path: str) -> str:
"""Create a full URL using the current site domain."""
current_site = get_current_site(self.request)
scheme = "https" if self.request.is_secure() else "http"
return f"{scheme}://{current_site.domain}{path}"