Skip to content

Commit

Permalink
feat: add rss feed for latest downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobCoffee committed Sep 16, 2024
1 parent 097df08 commit 97624b5
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
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"),
]
101 changes: 101 additions & 0 deletions downloads/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import logging
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.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
import pytz

from .models import OS, Release, ReleaseFile

logger = logging.getLogger(__name__)

class DownloadLatestPython2(RedirectView):
""" Redirect to latest Python 2 release """
Expand Down Expand Up @@ -147,3 +159,92 @@ 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."""
url = self.create_url("/api/v2/downloads/release/")
logger.info(f"Fetching releases from: {url}")
try:
return self._fetch_releases(url)
except requests.RequestException as e:
logger.error(f"Error fetching releases from API: {str(e)}")
except ValueError as e:
logger.error(f"Error parsing JSON from API response: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error in items method: {str(e)}")
return []

@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: dict[str, Any]) -> str:
"""Return the release name as the item title."""
return item.get("name", "Unknown Release")

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

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

@staticmethod
def item_pubdate(item: dict[str, Any]) -> datetime:
"""Return the release date as the item publication date."""
try:
release_date = datetime.strptime(
item.get("release_date", ""), "%Y-%m-%dT%H:%M:%SZ"
)
return pytz.utc.localize(release_date)
except ValueError:
logger.error(
f"Invalid release date format for item: {item.get('name', 'Unknown')}"
)
return pytz.utc.localize(datetime.now())

@staticmethod
def item_guid(item: dict[str, Any]) -> str:
"""Return the release URI as the item GUID."""
return item.get("resource_uri", "")

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}"

0 comments on commit 97624b5

Please sign in to comment.