diff --git a/pydotorg/urls.py b/pydotorg/urls.py index 8a70d5790..bd5496fb6 100644 --- a/pydotorg/urls.py +++ b/pydotorg/urls.py @@ -32,6 +32,7 @@ path('getit/', include('downloads.urls', namespace='getit')), path('downloads/', include('downloads.urls', namespace='download')), path('doc/', views.DocumentationIndexView.as_view(), name='documentation'), + path('doc/versions2/', views.DocsByVersionView.as_view(), name='docs-versions'), path('blogs/', include('blogs.urls')), path('inner/', TemplateView.as_view(template_name="python/inner.html"), name='inner'), diff --git a/pydotorg/views.py b/pydotorg/views.py index bbc30ec51..8d1bf7f05 100644 --- a/pydotorg/views.py +++ b/pydotorg/views.py @@ -1,5 +1,9 @@ +import datetime as dt import json import os +import re +from collections import defaultdict + from django.conf import settings from django.http import HttpResponse, JsonResponse from django.views.generic.base import RedirectView, TemplateView @@ -67,3 +71,113 @@ def get_redirect_url(self, *args, **kwargs): settings.AWS_STORAGE_BUCKET_NAME, image_path, ]) + + +class DocsByVersionView(TemplateView): + template_name = "python/versions.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + releases = Release.objects.filter( + is_published=True, + pre_release=False, + ).order_by("-release_date") + + # Some releases have no documentation + no_docs = {"2.3.6", "2.3.7", "2.4.5", "2.4.6", "2.5.5", "2.5.6"} + + # We'll group releases by major.minor version + version_groups = defaultdict(list) + + for release in releases: + # Extract version number from name ("Python 3.14.0" -> "3.14.0") + version_match = re.match(r"Python ([\d.]+)", release.name) + if version_match: + full_version = version_match.group(1) + + if full_version in no_docs: + continue + + # Get major.minor version ("3.14.0" -> "3.14") + version_parts = full_version.split(".") + major_minor = f"{version_parts[0]}.{version_parts[1]}" + + # For 3.2.0 and earlier, use X.Y instead of X.Y.0 + if len(version_parts) == 3: + major, minor, patch = map(int, version_parts) + # For versions <= 3.2.0 where patch is 0 + if (major, minor, patch) <= (3, 2, 0) and patch == 0: + full_version = major_minor + + release_data = { + "stage": full_version, + "date": release.release_date.replace(tzinfo=None), + } + version_groups[major_minor].append(release_data) + + # Add legacy releases not in the database + legacy_releases_data = { + "2.2": [ + {"stage": "2.2p1", "date": dt.datetime(2002, 3, 29)}, + ], + "2.1": [ + {"stage": "2.1.2", "date": dt.datetime(2002, 1, 16)}, + {"stage": "2.1.1", "date": dt.datetime(2001, 7, 20)}, + {"stage": "2.1", "date": dt.datetime(2001, 4, 15)}, + ], + "2.0": [ + {"stage": "2.0", "date": dt.datetime(2000, 10, 16)}, + ], + "1.6": [ + {"stage": "1.6", "date": dt.datetime(2000, 9, 5)}, + ], + "1.5": [ + {"stage": "1.5.2p2", "date": dt.datetime(2000, 3, 22)}, + {"stage": "1.5.2p1", "date": dt.datetime(1999, 7, 6)}, + {"stage": "1.5.2", "date": dt.datetime(1999, 4, 30)}, + {"stage": "1.5.1p1", "date": dt.datetime(1998, 8, 6)}, + {"stage": "1.5.1", "date": dt.datetime(1998, 4, 14)}, + {"stage": "1.5", "date": dt.datetime(1998, 2, 17)}, + ], + "1.4": [ + {"stage": "1.4", "date": dt.datetime(1996, 10, 25)}, + ], + } + + # Merge legacy releases in + for version, items in legacy_releases_data.items(): + version_groups[version].extend(items) + + # Convert to list for template and sort releases within each version + version_list = [] + for version, releases in version_groups.items(): + # Sort x.y.z newest first + releases = sorted( + releases, + key=lambda x: x.get("date", dt.datetime.min), + reverse=True, + ) + for release in releases: + release["date"] = release["date"].strftime("%-d %B %Y") + + version_list.append( + { + "version": version, + "releases": releases, + } + ) + + # Sort x.y versions (newest first) + version_list.sort( + key=lambda x: [ + int(n) if n.isdigit() else n for n in x["version"].split(".") + ], + reverse=True, + ) + + context.update({ + "version_list": version_list, + }) + + return context diff --git a/templates/python/versions.html b/templates/python/versions.html new file mode 100644 index 000000000..ab9c6a222 --- /dev/null +++ b/templates/python/versions.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} +{% load boxes %} +{% load sitetree %} + +{% block page_title %}Python documentation by version | {{ SITE_INFO.site_name }}{% endblock %} + +{% block body_attributes %}class="python pages default-page"{% endblock %} + +{% block breadcrumbs %} +{% sitetree_breadcrumbs from "main" %} +{% endblock breadcrumbs %} + +{% block content_attributes %}with-left-sidebar{% endblock %} + +{% block content %} +
+
+

Python documentation by version

+
+ +

Some previous versions of the documentation remain available online. Use the list below to select a version to view.

+ +

For unreleased (in development) documentation, see In development versions.

+ +

Release versions

+ + {% for version_data in version_list %} +

Python {{ version_data.version }}

+ + {% endfor %} + +

In development versions

+

The latest, and unreleased, documentation for versions of Python still under development:

+ + +
+{% endblock content %} + +{% block left_sidebar %} + +{% endblock left_sidebar %}