-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Addons: sorting algorithm for versions customizable on flyout #11069
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
Changes from 18 commits
ea93e9e
5fb9b47
f329103
fd016d6
5d40efb
83805a6
2a2b6f5
10ac07a
30455a2
cea2052
4ca07f9
6701538
51e1bd7
cdbe804
4d3dcb2
e2ebaa1
eeb0727
6cdbe42
6288537
f0c74a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Generated by Django 4.2.10 on 2024-03-04 13:32 | ||
|
||
from django.db import migrations, models | ||
from django_safemigrate import Safe | ||
|
||
|
||
class Migration(migrations.Migration): | ||
safe = Safe.before_deploy | ||
|
||
dependencies = [ | ||
("projects", "0117_remove_old_fields"), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name="addonsconfig", | ||
name="flyout_sorting", | ||
field=models.CharField( | ||
choices=[ | ||
("alphabetically", "Alphabetically"), | ||
("semver-readthedocs-compatible", "SemVer (Read the Docs)"), | ||
("python-packaging", "Python Packaging (PEP 440 and PEP 425)"), | ||
("calver", "CalVer (YYYY.0M.0M)"), | ||
("custom-pattern", "Define your own pattern"), | ||
], | ||
default="alphabetically", | ||
max_length=64, | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="addonsconfig", | ||
name="flyout_sorting_custom_pattern", | ||
field=models.CharField( | ||
blank=True, | ||
default=None, | ||
help_text='Sorting pattern supported by BumpVer (<a href="https://github.com/mbarkhau/bumpver#pattern-examples">See examples</a>', | ||
max_length=32, | ||
null=True, | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="addonsconfig", | ||
name="flyout_sorting_latest_stable_at_beginning", | ||
field=models.BooleanField( | ||
default=True, | ||
help_text="Show <code>latest</code> and <code>stable</code> at the beginning", | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="historicaladdonsconfig", | ||
name="flyout_sorting", | ||
field=models.CharField( | ||
choices=[ | ||
("alphabetically", "Alphabetically"), | ||
("semver-readthedocs-compatible", "SemVer (Read the Docs)"), | ||
("python-packaging", "Python Packaging (PEP 440 and PEP 425)"), | ||
("calver", "CalVer (YYYY.0M.0M)"), | ||
("custom-pattern", "Define your own pattern"), | ||
], | ||
default="alphabetically", | ||
max_length=64, | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="historicaladdonsconfig", | ||
name="flyout_sorting_custom_pattern", | ||
field=models.CharField( | ||
blank=True, | ||
default=None, | ||
help_text='Sorting pattern supported by BumpVer (<a href="https://github.com/mbarkhau/bumpver#pattern-examples">See examples</a>', | ||
max_length=32, | ||
null=True, | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="historicaladdonsconfig", | ||
name="flyout_sorting_latest_stable_at_beginning", | ||
field=models.BooleanField( | ||
default=True, | ||
help_text="Show <code>latest</code> and <code>stable</code> at the beginning", | ||
), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
import django_dynamic_fixture as fixture | ||
import pytest | ||
|
||
from readthedocs.builds.models import Build, Version | ||
from readthedocs.projects.models import AddonsConfig, Project | ||
from readthedocs.projects.version_handling import ( | ||
sort_versions_calver, | ||
sort_versions_custom_pattern, | ||
sort_versions_python_packaging, | ||
) | ||
|
||
|
||
@pytest.mark.django_db(databases="__all__") | ||
class TestVersionHandling: | ||
@pytest.fixture(autouse=True) | ||
def setup(self, requests_mock): | ||
# Save the reference to query it from inside the test | ||
self.requests_mock = requests_mock | ||
|
||
self.project = fixture.get(Project, slug="project") | ||
self.addons = fixture.get(AddonsConfig, project=self.project) | ||
self.version = self.project.versions.get(slug="latest") | ||
self.build = fixture.get( | ||
Build, | ||
version=self.version, | ||
commit="a1b2c3", | ||
) | ||
|
||
def test_sort_versions_python_packaging(self): | ||
slugs = [ | ||
"v1.0", | ||
"1.1", | ||
"invalid", | ||
"2.5.3", | ||
"1.1.0", | ||
"another-invalid", | ||
] | ||
|
||
expected = [ | ||
# `latest` and `stable` are at the beginning | ||
"latest", | ||
"v1.0", | ||
"1.1", | ||
"1.1.0", | ||
"2.5.3", | ||
# Invalid versions are at the end sorted alphabetically. | ||
"another-invalid", | ||
"invalid", | ||
] | ||
|
||
for slug in slugs: | ||
fixture.get( | ||
Version, | ||
slug=slug, | ||
project=self.project, | ||
) | ||
|
||
sorted_versions = sort_versions_python_packaging( | ||
self.project.versions.all(), | ||
latest_stable_at_beginning=True, | ||
) | ||
assert expected == [version.slug for version in sorted_versions] | ||
|
||
def test_sort_versions_python_packaging_latest_stable_not_at_beginning(self): | ||
slugs = [ | ||
"v1.0", | ||
"1.1", | ||
"invalid", | ||
"2.5.3", | ||
"1.1.0", | ||
"another-invalid", | ||
] | ||
|
||
expected = [ | ||
"v1.0", | ||
"1.1", | ||
"1.1.0", | ||
"2.5.3", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to invert this to show the newest version first in the flyout? Now it will display:
but maybe what we want is:
This is how the old flyout sorts the versions (the newest first). Example: https://docs.godotengine.org/en/stable/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pinging @astrojuanlu since you are one of the users wanting this feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think descending order makes more sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, the result for this case using Python Packaging sorting will be:
Then, the final result with Read the Docs special versions and invalid ones will be:
The pattern is:
|
||
# Invalid versions are at the end sorted alphabetically. | ||
"another-invalid", | ||
"invalid", | ||
"latest", | ||
] | ||
|
||
for slug in slugs: | ||
fixture.get( | ||
Version, | ||
slug=slug, | ||
project=self.project, | ||
) | ||
|
||
sorted_versions = sort_versions_python_packaging( | ||
self.project.versions.all(), | ||
latest_stable_at_beginning=False, | ||
) | ||
assert expected == [version.slug for version in sorted_versions] | ||
|
||
def test_sort_versions_calver(self): | ||
slugs = [ | ||
"2022.01.22", | ||
"2023.04.22", | ||
"2021.01.22", | ||
"2022.05.02", | ||
# invalid ones | ||
"2001.16.32", | ||
"2001.02.2", | ||
"2001-02-27", | ||
"1.1", | ||
"invalid", | ||
"2.5.3", | ||
"1.1.0", | ||
"another-invalid", | ||
] | ||
|
||
expected = [ | ||
# `latest` and `stable` are at the beginning | ||
"latest", | ||
"stable", | ||
"2021.01.22", | ||
"2022.01.22", | ||
"2022.05.02", | ||
"2023.04.22", | ||
# invalid ones (alphabetically) | ||
"1.1", | ||
"1.1.0", | ||
"2.5.3", | ||
"2001-02-27", | ||
"2001.02.2", | ||
"2001.16.32", | ||
"another-invalid", | ||
"invalid", | ||
] | ||
|
||
for slug in slugs: | ||
fixture.get( | ||
Version, | ||
slug=slug, | ||
project=self.project, | ||
) | ||
|
||
fixture.get( | ||
Version, | ||
slug="stable", | ||
machine=True, | ||
project=self.project, | ||
) | ||
|
||
sorted_versions = sort_versions_calver( | ||
self.project.versions.all(), | ||
latest_stable_at_beginning=True, | ||
) | ||
|
||
assert expected == [version.slug for version in sorted_versions] | ||
|
||
def test_sort_versions_custom_pattern(self): | ||
slugs = [ | ||
"v1.0", | ||
"v1.1", | ||
"v2.3", | ||
# invalid ones | ||
"v1.1.0", | ||
"v2.3rc1", | ||
"invalid", | ||
"2.5.3", | ||
"2022.01.22", | ||
"1.1", | ||
"another-invalid", | ||
] | ||
|
||
expected = [ | ||
# `latest` and `stable` are at the beginning | ||
"latest", | ||
"stable", | ||
"v1.0", | ||
"v1.1", | ||
"v2.3", | ||
# invalid ones (alphabetically) | ||
"1.1", | ||
"2.5.3", | ||
"2022.01.22", | ||
"another-invalid", | ||
"invalid", | ||
"v1.1.0", | ||
"v2.3rc1", | ||
] | ||
|
||
for slug in slugs: | ||
fixture.get( | ||
Version, | ||
slug=slug, | ||
project=self.project, | ||
) | ||
|
||
fixture.get( | ||
Version, | ||
slug="stable", | ||
machine=True, | ||
project=self.project, | ||
) | ||
|
||
sorted_versions = sort_versions_custom_pattern( | ||
self.project.versions.all(), | ||
raw_pattern="vMAJOR.MINOR", | ||
latest_stable_at_beginning=True, | ||
) | ||
|
||
assert expected == [version.slug for version in sorted_versions] |
Uh oh!
There was an error while loading. Please reload this page.