diff --git a/downloads/tests/base.py b/downloads/tests/base.py index bcb7905c4..2b5e2c905 100644 --- a/downloads/tests/base.py +++ b/downloads/tests/base.py @@ -1,4 +1,4 @@ -import datetime +import datetime as dt from django.test import TestCase from django.utils import timezone @@ -32,7 +32,7 @@ def setUp(self): is_latest=True, is_published=True, release_page=self.release_275_page, - release_date=timezone.now() - datetime.timedelta(days=-1) + release_date=dt.datetime.fromisoformat("2013-05-15T00:00Z"), ) self.release_275_windows_32bit = ReleaseFile.objects.create( os=self.windows, @@ -102,9 +102,31 @@ def setUp(self): self.python_3 = Release.objects.create( version=Release.PYTHON3, - name='Python 3.10', + name="Python 3.10.19", is_latest=True, is_published=True, show_on_download_page=True, - release_page=self.release_275_page + release_page=self.release_275_page, + release_date=dt.datetime.fromisoformat("2025-10-09T00:00Z"), + ) + + self.python_3_10_18 = Release.objects.create( + version=Release.PYTHON3, + name="Python 3.10.18", + is_published=True, + release_date=dt.datetime.fromisoformat("2025-06-03T00:00Z"), + ) + + self.python_3_8_20 = Release.objects.create( + version=Release.PYTHON3, + name="Python 3.8.20", + is_published=True, + release_date=dt.datetime.fromisoformat("2024-09-06T00:00Z"), + ) + + self.python_3_8_19 = Release.objects.create( + version=Release.PYTHON3, + name="Python 3.8.19", + is_published=True, + release_date=dt.datetime.fromisoformat("2024-03-19T00:00Z"), ) diff --git a/downloads/tests/test_models.py b/downloads/tests/test_models.py index bd26c3b58..b89b2ecce 100644 --- a/downloads/tests/test_models.py +++ b/downloads/tests/test_models.py @@ -10,14 +10,14 @@ def test_stringification(self): def test_published(self): published_releases = Release.objects.published() - self.assertEqual(len(published_releases), 4) + self.assertEqual(len(published_releases), 7) self.assertIn(self.release_275, published_releases) self.assertIn(self.hidden_release, published_releases) self.assertNotIn(self.draft_release, published_releases) def test_release(self): released_versions = Release.objects.released() - self.assertEqual(len(released_versions), 3) + self.assertEqual(len(released_versions), 6) self.assertIn(self.release_275, released_versions) self.assertIn(self.hidden_release, released_versions) self.assertNotIn(self.draft_release, released_versions) @@ -37,7 +37,7 @@ def test_draft(self): def test_downloads(self): downloads = Release.objects.downloads() - self.assertEqual(len(downloads), 2) + self.assertEqual(len(downloads), 5) self.assertIn(self.release_275, downloads) self.assertNotIn(self.hidden_release, downloads) self.assertNotIn(self.draft_release, downloads) @@ -50,7 +50,7 @@ def test_python2(self): def test_python3(self): versions = Release.objects.python3() - self.assertEqual(len(versions), 3) + self.assertEqual(len(versions), 6) self.assertNotIn(self.release_275, versions) self.assertNotIn(self.draft_release, versions) self.assertIn(self.hidden_release, versions) diff --git a/downloads/tests/test_views.py b/downloads/tests/test_views.py index b559a2adc..c42572d13 100644 --- a/downloads/tests/test_views.py +++ b/downloads/tests/test_views.py @@ -56,6 +56,21 @@ def test_download(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) + def test_download_releases_ordered_by_version(self): + url = reverse("download:download") + response = self.client.get(url) + releases = response.context["releases"] + self.assertEqual( + releases, + [ + self.python_3, + self.python_3_10_18, + self.python_3_8_20, + self.python_3_8_19, + self.release_275, + ], + ) + def test_latest_redirects(self): latest_python2 = Release.objects.released().python2().latest() url = reverse('download:download_latest_python2') @@ -218,13 +233,13 @@ def test_get_release(self): self.assertEqual(response.status_code, 200) content = self.get_json(response) # 'self.draft_release' won't shown here. - self.assertEqual(len(content), 4) + self.assertEqual(len(content), 7) # Login to get all releases. response = self.client.get(url, headers={"authorization": self.Authorization}) self.assertEqual(response.status_code, 200) content = self.get_json(response) - self.assertEqual(len(content), 5) + self.assertEqual(len(content), 8) self.assertFalse(content[0]['is_latest']) def test_post_release(self): @@ -594,5 +609,5 @@ def test_feed_item_count(self) -> None: response = self.client.get(self.url) content = response.content.decode() - # In BaseDownloadTests, we create 5 releases, 4 of which are published, 1 of those published are hidden.. - self.assertEqual(content.count(""), 4) + # In BaseDownloadTests, we create 8 releases, 7 of which are published, 1 of those published are hidden.. + self.assertEqual(content.count(""), 7) diff --git a/downloads/views.py b/downloads/views.py index d19f0e868..5d8da9461 100644 --- a/downloads/views.py +++ b/downloads/views.py @@ -124,8 +124,17 @@ def get_context_data(self, **kwargs): data['pymanager'] = latest_pymanager.download_file_for_os(o.slug) python_files.append(data) + def version_key(release: Release) -> tuple[int, ...]: + try: + return tuple(int(x) for x in release.get_version().split(".")) + except ValueError: + return (0,) + + releases = list(Release.objects.downloads()) + releases.sort(key=version_key, reverse=True) + context.update({ - 'releases': Release.objects.downloads(), + 'releases': releases, 'latest_python2': latest_python2, 'latest_python3': latest_python3, 'python_files': python_files, diff --git a/templates/downloads/index.html b/templates/downloads/index.html index f527be031..303fad87b 100644 --- a/templates/downloads/index.html +++ b/templates/downloads/index.html @@ -50,7 +50,7 @@

Download the latest version of Python

{% render_active_banner %} -

Active Python Releases

+

Active Python releases

For more information visit the Python Developer's Guide.

{% box 'downloads-active-releases' %} @@ -74,7 +74,7 @@

Looking for a specific release?

{{ r.name }} {{ r.release_date|date }} Download - Release Notes + Release notes {% endfor %}