We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Project version filter returns 500
It seems the `ProjectVersionListFilterSet.get_visibility()` is receiving the wrong amount of arguments.

To reproduce, just hit https://beta.readthedocs.org/projects/bigo-live-hack/?privacy=&sort=&visibility=hidden

Sentry issue: https://read-the-docs.sentry.io/issues/4721614191/?project=148442&query=is%3Aunresolved&referrer=issue-stream&statsPeriod=7d&stream_index=7

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/projects/filters.py`
Content:
```
1 """Filters used in project dashboard."""
2 
3 import structlog
4 from django.db.models import Count, F, Max
5 from django.utils.translation import gettext_lazy as _
6 from django_filters import ChoiceFilter, OrderingFilter
7 
8 from readthedocs.core.filters import FilteredModelChoiceFilter, ModelFilterSet
9 from readthedocs.projects.models import Project
10 
11 log = structlog.get_logger(__name__)
12 
13 
14 class VersionSortOrderingFilter(OrderingFilter):
15 
16     """
17     Version list sort ordering django_filters filter.
18 
19     Django-filter is highly opionated, and the default model filters do not work
20     well with empty/null values in the filter choices. In our case, empty/null
21     values are used for a default query. So, to make this work, we will use a
22     custom filter, instead of an automated model filter.
23 
24     The empty/None value is used to provide both a default value to the filter
25     (when there is no ``sort`` query param), but also provide an option that is
26     manually selectable (``?sort=relevance``). We can't do this with the default
27     filter, because result would be params like ``?sort=None``.
28     """
29 
30     SORT_BUILD_COUNT = "build_count"
31     SORT_BUILD_DATE = "build_date"
32     SORT_NAME = "name"
33 
34     def __init__(self, *args, **kwargs):
35         # The default filtering operation will be `-recent`, so we omit it
36         # from choices to avoid showing it on the list twice.
37         kwargs.setdefault("empty_label", _("Recently built"))
38         kwargs.setdefault(
39             "choices",
40             (
41                 ("-" + self.SORT_BUILD_DATE, _("Least recently built")),
42                 ("-" + self.SORT_BUILD_COUNT, _("Frequently built")),
43                 (self.SORT_BUILD_COUNT, _("Least frequently built")),
44                 (self.SORT_NAME, _("Name")),
45                 ("-" + self.SORT_NAME, _("Name (descending)")),
46             ),
47         )
48         super().__init__(*args, **kwargs)
49 
50     def filter(self, qs, value):
51         # This is where we use the None value for this custom filter. This
52         # doesn't work with a standard model filter. Note: ``value`` is always
53         # an iterable, but can be empty.
54 
55         if not value:
56             value = [self.SORT_BUILD_DATE]
57 
58         annotations = {}
59         order_bys = []
60         for field_ordered in value:
61             field = field_ordered.lstrip("-")
62 
63             if field == self.SORT_BUILD_DATE:
64                 annotations[self.SORT_BUILD_DATE] = Max("builds__date")
65             elif field == self.SORT_BUILD_COUNT:
66                 annotations[self.SORT_BUILD_COUNT] = Count("builds")
67             elif field == self.SORT_NAME:
68                 # Alias field name here, as ``OrderingFilter`` was having trouble
69                 # doing this with it's native field mapping
70                 annotations[self.SORT_NAME] = F("verbose_name")
71 
72             if field_ordered == self.SORT_BUILD_DATE:
73                 order_bys.append(F(field).desc(nulls_last=True))
74             elif field_ordered == "-" + self.SORT_BUILD_DATE:
75                 order_bys.append(F(field).asc(nulls_first=True))
76             else:
77                 order_bys.append(field_ordered)
78 
79         return qs.annotate(**annotations).order_by(*order_bys)
80 
81 
82 class ProjectSortOrderingFilter(OrderingFilter):
83 
84     """
85     Project list sort ordering django_filters filter.
86 
87     Django-filter is highly opionated, and the default model filters do not work
88     well with empty/null values in the filter choices. In our case, empty/null
89     values are used for a default query. So, to make this work, we will use a
90     custom filter, instead of an automated model filter.
91     """
92 
93     SORT_NAME = "name"
94     SORT_MODIFIED_DATE = "modified_date"
95     SORT_BUILD_DATE = "build_date"
96     SORT_BUILD_COUNT = "build_count"
97 
98     def __init__(self, *args, **kwargs):
99         # The default filtering operation will be `name`, so we omit it
100         # from choices to avoid showing it on the list twice.
101         kwargs.setdefault("empty_label", _("Recently built"))
102         kwargs.setdefault(
103             "choices",
104             (
105                 ("-" + self.SORT_BUILD_DATE, _("Least recently built")),
106                 ("-" + self.SORT_BUILD_COUNT, _("Frequently built")),
107                 (self.SORT_BUILD_COUNT, _("Least frequently built")),
108                 ("-" + self.SORT_MODIFIED_DATE, _("Recently modified")),
109                 (self.SORT_MODIFIED_DATE, _("Least recently modified")),
110                 (self.SORT_NAME, _("Name")),
111                 ("-" + self.SORT_NAME, _("Name (descending)")),
112             ),
113         )
114         super().__init__(*args, **kwargs)
115 
116     def filter(self, qs, value):
117         # This is where we use the None value from the custom filter
118         if not value:
119             value = [self.SORT_BUILD_DATE]
120 
121         annotations = {}
122         order_bys = []
123         for field_ordered in value:
124             field = field_ordered.lstrip("-")
125 
126             if field == self.SORT_BUILD_DATE:
127                 annotations[self.SORT_BUILD_DATE] = Max("builds__date")
128             elif field == self.SORT_BUILD_COUNT:
129                 annotations[self.SORT_BUILD_COUNT] = Count("builds")
130 
131             if field_ordered == self.SORT_BUILD_DATE:
132                 order_bys.append(F(field).desc(nulls_last=True))
133             elif field_ordered == "-" + self.SORT_BUILD_DATE:
134                 order_bys.append(F(field).asc(nulls_first=True))
135             else:
136                 order_bys.append(field_ordered)
137 
138         return qs.annotate(**annotations).order_by(*order_bys)
139 
140 
141 class ProjectListFilterSet(ModelFilterSet):
142 
143     """
144     Project list filter set for project list view.
145 
146     This filter set enables list view sorting using a custom filter, and
147     provides search-as-you-type lookup filter as well.
148     """
149 
150     slug = FilteredModelChoiceFilter(
151         label=_("Project"),
152         empty_label=_("All projects"),
153         to_field_name="slug",
154         queryset_method="get_project_queryset",
155         method="get_project",
156         label_attribute="name",
157     )
158 
159     sort = ProjectSortOrderingFilter(
160         field_name="sort",
161         label=_("Sort by"),
162     )
163 
164     def get_project_queryset(self):
165         return Project.objects.for_user(user=self.request.user)
166 
167     def get_project(self, queryset, field_name, project):
168         return queryset.filter(slug=project.slug)
169 
170 
171 class ProjectVersionListFilterSet(ModelFilterSet):
172 
173     """
174     Filter and sorting for project version listing page.
175 
176     This is used from the project versions list view page to provide filtering
177     and sorting to the version list and search UI. It is normally instantiated
178     with an included queryset, which provides user project authorization.
179     """
180 
181     VISIBILITY_HIDDEN = "hidden"
182     VISIBILITY_VISIBLE = "visible"
183 
184     VISIBILITY_CHOICES = (
185         ("hidden", _("Hidden versions")),
186         ("visible", _("Visible versions")),
187     )
188 
189     PRIVACY_CHOICES = (
190         ("public", _("Public versions")),
191         ("private", _("Private versions")),
192     )
193 
194     # Attribute filter fields
195     slug = FilteredModelChoiceFilter(
196         label=_("Version"),
197         empty_label=_("All versions"),
198         to_field_name="slug",
199         queryset_method="get_version_queryset",
200         method="get_version",
201         label_attribute="verbose_name",
202     )
203 
204     privacy = ChoiceFilter(
205         field_name="privacy_level",
206         label=_("Privacy"),
207         choices=PRIVACY_CHOICES,
208         empty_label=_("Any"),
209     )
210     # This field looks better as ``visibility=hidden`` than it does
211     # ``hidden=true``, otherwise we could use a BooleanFilter instance here
212     # instead
213     visibility = ChoiceFilter(
214         field_name="hidden",
215         label=_("Visibility"),
216         choices=VISIBILITY_CHOICES,
217         method="get_visibility",
218         empty_label=_("Any"),
219     )
220 
221     sort = VersionSortOrderingFilter(
222         field_name="sort",
223         label=_("Sort by"),
224     )
225 
226     def __init__(self, *args, project=None, **kwargs):
227         self.project = project
228         super().__init__(*args, **kwargs)
229 
230     def get_version(self, queryset, field_name, version):
231         return queryset.filter(slug=version.slug)
232 
233     def get_version_queryset(self):
234         # This query is passed in at instantiation
235         return self.queryset
236 
237     def get_visibility(self, queryset, *, value):
238         if value == self.VISIBILITY_HIDDEN:
239             return queryset.filter(hidden=True)
240         if value == self.VISIBILITY_VISIBLE:
241             return queryset.filter(hidden=False)
242         return queryset
243
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/readthedocs/projects/filters.py b/readthedocs/projects/filters.py
--- a/readthedocs/projects/filters.py
+++ b/readthedocs/projects/filters.py
@@ -234,7 +234,7 @@
         # This query is passed in at instantiation
         return self.queryset
 
-    def get_visibility(self, queryset, *, value):
+    def get_visibility(self, queryset, field_name, value):
         if value == self.VISIBILITY_HIDDEN:
             return queryset.filter(hidden=True)
         if value == self.VISIBILITY_VISIBLE:

{"golden_diff": "diff --git a/readthedocs/projects/filters.py b/readthedocs/projects/filters.py\n--- a/readthedocs/projects/filters.py\n+++ b/readthedocs/projects/filters.py\n@@ -234,7 +234,7 @@\n         # This query is passed in at instantiation\n         return self.queryset\n \n-    def get_visibility(self, queryset, *, value):\n+    def get_visibility(self, queryset, field_name, value):\n         if value == self.VISIBILITY_HIDDEN:\n             return queryset.filter(hidden=True)\n         if value == self.VISIBILITY_VISIBLE:\n", "issue": "Project version filter returns 500\nIt seems the `ProjectVersionListFilterSet.get_visibility()` is receiving the wrong amount of arguments.\r\n\r\nTo reproduce, just hit https://beta.readthedocs.org/projects/bigo-live-hack/?privacy=&sort=&visibility=hidden\r\n\r\nSentry issue: https://read-the-docs.sentry.io/issues/4721614191/?project=148442&query=is%3Aunresolved&referrer=issue-stream&statsPeriod=7d&stream_index=7\n", "before_files": [{"content": "\"\"\"Filters used in project dashboard.\"\"\"\n\nimport structlog\nfrom django.db.models import Count, F, Max\nfrom django.utils.translation import gettext_lazy as _\nfrom django_filters import ChoiceFilter, OrderingFilter\n\nfrom readthedocs.core.filters import FilteredModelChoiceFilter, ModelFilterSet\nfrom readthedocs.projects.models import Project\n\nlog = structlog.get_logger(__name__)\n\n\nclass VersionSortOrderingFilter(OrderingFilter):\n\n    \"\"\"\n    Version list sort ordering django_filters filter.\n\n    Django-filter is highly opionated, and the default model filters do not work\n    well with empty/null values in the filter choices. In our case, empty/null\n    values are used for a default query. So, to make this work, we will use a\n    custom filter, instead of an automated model filter.\n\n    The empty/None value is used to provide both a default value to the filter\n    (when there is no ``sort`` query param), but also provide an option that is\n    manually selectable (``?sort=relevance``). We can't do this with the default\n    filter, because result would be params like ``?sort=None``.\n    \"\"\"\n\n    SORT_BUILD_COUNT = \"build_count\"\n    SORT_BUILD_DATE = \"build_date\"\n    SORT_NAME = \"name\"\n\n    def __init__(self, *args, **kwargs):\n        # The default filtering operation will be `-recent`, so we omit it\n        # from choices to avoid showing it on the list twice.\n        kwargs.setdefault(\"empty_label\", _(\"Recently built\"))\n        kwargs.setdefault(\n            \"choices\",\n            (\n                (\"-\" + self.SORT_BUILD_DATE, _(\"Least recently built\")),\n                (\"-\" + self.SORT_BUILD_COUNT, _(\"Frequently built\")),\n                (self.SORT_BUILD_COUNT, _(\"Least frequently built\")),\n                (self.SORT_NAME, _(\"Name\")),\n                (\"-\" + self.SORT_NAME, _(\"Name (descending)\")),\n            ),\n        )\n        super().__init__(*args, **kwargs)\n\n    def filter(self, qs, value):\n        # This is where we use the None value for this custom filter. This\n        # doesn't work with a standard model filter. Note: ``value`` is always\n        # an iterable, but can be empty.\n\n        if not value:\n            value = [self.SORT_BUILD_DATE]\n\n        annotations = {}\n        order_bys = []\n        for field_ordered in value:\n            field = field_ordered.lstrip(\"-\")\n\n            if field == self.SORT_BUILD_DATE:\n                annotations[self.SORT_BUILD_DATE] = Max(\"builds__date\")\n            elif field == self.SORT_BUILD_COUNT:\n                annotations[self.SORT_BUILD_COUNT] = Count(\"builds\")\n            elif field == self.SORT_NAME:\n                # Alias field name here, as ``OrderingFilter`` was having trouble\n                # doing this with it's native field mapping\n                annotations[self.SORT_NAME] = F(\"verbose_name\")\n\n            if field_ordered == self.SORT_BUILD_DATE:\n                order_bys.append(F(field).desc(nulls_last=True))\n            elif field_ordered == \"-\" + self.SORT_BUILD_DATE:\n                order_bys.append(F(field).asc(nulls_first=True))\n            else:\n                order_bys.append(field_ordered)\n\n        return qs.annotate(**annotations).order_by(*order_bys)\n\n\nclass ProjectSortOrderingFilter(OrderingFilter):\n\n    \"\"\"\n    Project list sort ordering django_filters filter.\n\n    Django-filter is highly opionated, and the default model filters do not work\n    well with empty/null values in the filter choices. In our case, empty/null\n    values are used for a default query. So, to make this work, we will use a\n    custom filter, instead of an automated model filter.\n    \"\"\"\n\n    SORT_NAME = \"name\"\n    SORT_MODIFIED_DATE = \"modified_date\"\n    SORT_BUILD_DATE = \"build_date\"\n    SORT_BUILD_COUNT = \"build_count\"\n\n    def __init__(self, *args, **kwargs):\n        # The default filtering operation will be `name`, so we omit it\n        # from choices to avoid showing it on the list twice.\n        kwargs.setdefault(\"empty_label\", _(\"Recently built\"))\n        kwargs.setdefault(\n            \"choices\",\n            (\n                (\"-\" + self.SORT_BUILD_DATE, _(\"Least recently built\")),\n                (\"-\" + self.SORT_BUILD_COUNT, _(\"Frequently built\")),\n                (self.SORT_BUILD_COUNT, _(\"Least frequently built\")),\n                (\"-\" + self.SORT_MODIFIED_DATE, _(\"Recently modified\")),\n                (self.SORT_MODIFIED_DATE, _(\"Least recently modified\")),\n                (self.SORT_NAME, _(\"Name\")),\n                (\"-\" + self.SORT_NAME, _(\"Name (descending)\")),\n            ),\n        )\n        super().__init__(*args, **kwargs)\n\n    def filter(self, qs, value):\n        # This is where we use the None value from the custom filter\n        if not value:\n            value = [self.SORT_BUILD_DATE]\n\n        annotations = {}\n        order_bys = []\n        for field_ordered in value:\n            field = field_ordered.lstrip(\"-\")\n\n            if field == self.SORT_BUILD_DATE:\n                annotations[self.SORT_BUILD_DATE] = Max(\"builds__date\")\n            elif field == self.SORT_BUILD_COUNT:\n                annotations[self.SORT_BUILD_COUNT] = Count(\"builds\")\n\n            if field_ordered == self.SORT_BUILD_DATE:\n                order_bys.append(F(field).desc(nulls_last=True))\n            elif field_ordered == \"-\" + self.SORT_BUILD_DATE:\n                order_bys.append(F(field).asc(nulls_first=True))\n            else:\n                order_bys.append(field_ordered)\n\n        return qs.annotate(**annotations).order_by(*order_bys)\n\n\nclass ProjectListFilterSet(ModelFilterSet):\n\n    \"\"\"\n    Project list filter set for project list view.\n\n    This filter set enables list view sorting using a custom filter, and\n    provides search-as-you-type lookup filter as well.\n    \"\"\"\n\n    slug = FilteredModelChoiceFilter(\n        label=_(\"Project\"),\n        empty_label=_(\"All projects\"),\n        to_field_name=\"slug\",\n        queryset_method=\"get_project_queryset\",\n        method=\"get_project\",\n        label_attribute=\"name\",\n    )\n\n    sort = ProjectSortOrderingFilter(\n        field_name=\"sort\",\n        label=_(\"Sort by\"),\n    )\n\n    def get_project_queryset(self):\n        return Project.objects.for_user(user=self.request.user)\n\n    def get_project(self, queryset, field_name, project):\n        return queryset.filter(slug=project.slug)\n\n\nclass ProjectVersionListFilterSet(ModelFilterSet):\n\n    \"\"\"\n    Filter and sorting for project version listing page.\n\n    This is used from the project versions list view page to provide filtering\n    and sorting to the version list and search UI. It is normally instantiated\n    with an included queryset, which provides user project authorization.\n    \"\"\"\n\n    VISIBILITY_HIDDEN = \"hidden\"\n    VISIBILITY_VISIBLE = \"visible\"\n\n    VISIBILITY_CHOICES = (\n        (\"hidden\", _(\"Hidden versions\")),\n        (\"visible\", _(\"Visible versions\")),\n    )\n\n    PRIVACY_CHOICES = (\n        (\"public\", _(\"Public versions\")),\n        (\"private\", _(\"Private versions\")),\n    )\n\n    # Attribute filter fields\n    slug = FilteredModelChoiceFilter(\n        label=_(\"Version\"),\n        empty_label=_(\"All versions\"),\n        to_field_name=\"slug\",\n        queryset_method=\"get_version_queryset\",\n        method=\"get_version\",\n        label_attribute=\"verbose_name\",\n    )\n\n    privacy = ChoiceFilter(\n        field_name=\"privacy_level\",\n        label=_(\"Privacy\"),\n        choices=PRIVACY_CHOICES,\n        empty_label=_(\"Any\"),\n    )\n    # This field looks better as ``visibility=hidden`` than it does\n    # ``hidden=true``, otherwise we could use a BooleanFilter instance here\n    # instead\n    visibility = ChoiceFilter(\n        field_name=\"hidden\",\n        label=_(\"Visibility\"),\n        choices=VISIBILITY_CHOICES,\n        method=\"get_visibility\",\n        empty_label=_(\"Any\"),\n    )\n\n    sort = VersionSortOrderingFilter(\n        field_name=\"sort\",\n        label=_(\"Sort by\"),\n    )\n\n    def __init__(self, *args, project=None, **kwargs):\n        self.project = project\n        super().__init__(*args, **kwargs)\n\n    def get_version(self, queryset, field_name, version):\n        return queryset.filter(slug=version.slug)\n\n    def get_version_queryset(self):\n        # This query is passed in at instantiation\n        return self.queryset\n\n    def get_visibility(self, queryset, *, value):\n        if value == self.VISIBILITY_HIDDEN:\n            return queryset.filter(hidden=True)\n        if value == self.VISIBILITY_VISIBLE:\n            return queryset.filter(hidden=False)\n        return queryset\n", "path": "readthedocs/projects/filters.py"}], "after_files": [{"content": "\"\"\"Filters used in project dashboard.\"\"\"\n\nimport structlog\nfrom django.db.models import Count, F, Max\nfrom django.utils.translation import gettext_lazy as _\nfrom django_filters import ChoiceFilter, OrderingFilter\n\nfrom readthedocs.core.filters import FilteredModelChoiceFilter, ModelFilterSet\nfrom readthedocs.projects.models import Project\n\nlog = structlog.get_logger(__name__)\n\n\nclass VersionSortOrderingFilter(OrderingFilter):\n\n    \"\"\"\n    Version list sort ordering django_filters filter.\n\n    Django-filter is highly opionated, and the default model filters do not work\n    well with empty/null values in the filter choices. In our case, empty/null\n    values are used for a default query. So, to make this work, we will use a\n    custom filter, instead of an automated model filter.\n\n    The empty/None value is used to provide both a default value to the filter\n    (when there is no ``sort`` query param), but also provide an option that is\n    manually selectable (``?sort=relevance``). We can't do this with the default\n    filter, because result would be params like ``?sort=None``.\n    \"\"\"\n\n    SORT_BUILD_COUNT = \"build_count\"\n    SORT_BUILD_DATE = \"build_date\"\n    SORT_NAME = \"name\"\n\n    def __init__(self, *args, **kwargs):\n        # The default filtering operation will be `-recent`, so we omit it\n        # from choices to avoid showing it on the list twice.\n        kwargs.setdefault(\"empty_label\", _(\"Recently built\"))\n        kwargs.setdefault(\n            \"choices\",\n            (\n                (\"-\" + self.SORT_BUILD_DATE, _(\"Least recently built\")),\n                (\"-\" + self.SORT_BUILD_COUNT, _(\"Frequently built\")),\n                (self.SORT_BUILD_COUNT, _(\"Least frequently built\")),\n                (self.SORT_NAME, _(\"Name\")),\n                (\"-\" + self.SORT_NAME, _(\"Name (descending)\")),\n            ),\n        )\n        super().__init__(*args, **kwargs)\n\n    def filter(self, qs, value):\n        # This is where we use the None value for this custom filter. This\n        # doesn't work with a standard model filter. Note: ``value`` is always\n        # an iterable, but can be empty.\n\n        if not value:\n            value = [self.SORT_BUILD_DATE]\n\n        annotations = {}\n        order_bys = []\n        for field_ordered in value:\n            field = field_ordered.lstrip(\"-\")\n\n            if field == self.SORT_BUILD_DATE:\n                annotations[self.SORT_BUILD_DATE] = Max(\"builds__date\")\n            elif field == self.SORT_BUILD_COUNT:\n                annotations[self.SORT_BUILD_COUNT] = Count(\"builds\")\n            elif field == self.SORT_NAME:\n                # Alias field name here, as ``OrderingFilter`` was having trouble\n                # doing this with it's native field mapping\n                annotations[self.SORT_NAME] = F(\"verbose_name\")\n\n            if field_ordered == self.SORT_BUILD_DATE:\n                order_bys.append(F(field).desc(nulls_last=True))\n            elif field_ordered == \"-\" + self.SORT_BUILD_DATE:\n                order_bys.append(F(field).asc(nulls_first=True))\n            else:\n                order_bys.append(field_ordered)\n\n        return qs.annotate(**annotations).order_by(*order_bys)\n\n\nclass ProjectSortOrderingFilter(OrderingFilter):\n\n    \"\"\"\n    Project list sort ordering django_filters filter.\n\n    Django-filter is highly opionated, and the default model filters do not work\n    well with empty/null values in the filter choices. In our case, empty/null\n    values are used for a default query. So, to make this work, we will use a\n    custom filter, instead of an automated model filter.\n    \"\"\"\n\n    SORT_NAME = \"name\"\n    SORT_MODIFIED_DATE = \"modified_date\"\n    SORT_BUILD_DATE = \"build_date\"\n    SORT_BUILD_COUNT = \"build_count\"\n\n    def __init__(self, *args, **kwargs):\n        # The default filtering operation will be `name`, so we omit it\n        # from choices to avoid showing it on the list twice.\n        kwargs.setdefault(\"empty_label\", _(\"Recently built\"))\n        kwargs.setdefault(\n            \"choices\",\n            (\n                (\"-\" + self.SORT_BUILD_DATE, _(\"Least recently built\")),\n                (\"-\" + self.SORT_BUILD_COUNT, _(\"Frequently built\")),\n                (self.SORT_BUILD_COUNT, _(\"Least frequently built\")),\n                (\"-\" + self.SORT_MODIFIED_DATE, _(\"Recently modified\")),\n                (self.SORT_MODIFIED_DATE, _(\"Least recently modified\")),\n                (self.SORT_NAME, _(\"Name\")),\n                (\"-\" + self.SORT_NAME, _(\"Name (descending)\")),\n            ),\n        )\n        super().__init__(*args, **kwargs)\n\n    def filter(self, qs, value):\n        # This is where we use the None value from the custom filter\n        if not value:\n            value = [self.SORT_BUILD_DATE]\n\n        annotations = {}\n        order_bys = []\n        for field_ordered in value:\n            field = field_ordered.lstrip(\"-\")\n\n            if field == self.SORT_BUILD_DATE:\n                annotations[self.SORT_BUILD_DATE] = Max(\"builds__date\")\n            elif field == self.SORT_BUILD_COUNT:\n                annotations[self.SORT_BUILD_COUNT] = Count(\"builds\")\n\n            if field_ordered == self.SORT_BUILD_DATE:\n                order_bys.append(F(field).desc(nulls_last=True))\n            elif field_ordered == \"-\" + self.SORT_BUILD_DATE:\n                order_bys.append(F(field).asc(nulls_first=True))\n            else:\n                order_bys.append(field_ordered)\n\n        return qs.annotate(**annotations).order_by(*order_bys)\n\n\nclass ProjectListFilterSet(ModelFilterSet):\n\n    \"\"\"\n    Project list filter set for project list view.\n\n    This filter set enables list view sorting using a custom filter, and\n    provides search-as-you-type lookup filter as well.\n    \"\"\"\n\n    slug = FilteredModelChoiceFilter(\n        label=_(\"Project\"),\n        empty_label=_(\"All projects\"),\n        to_field_name=\"slug\",\n        queryset_method=\"get_project_queryset\",\n        method=\"get_project\",\n        label_attribute=\"name\",\n    )\n\n    sort = ProjectSortOrderingFilter(\n        field_name=\"sort\",\n        label=_(\"Sort by\"),\n    )\n\n    def get_project_queryset(self):\n        return Project.objects.for_user(user=self.request.user)\n\n    def get_project(self, queryset, field_name, project):\n        return queryset.filter(slug=project.slug)\n\n\nclass ProjectVersionListFilterSet(ModelFilterSet):\n\n    \"\"\"\n    Filter and sorting for project version listing page.\n\n    This is used from the project versions list view page to provide filtering\n    and sorting to the version list and search UI. It is normally instantiated\n    with an included queryset, which provides user project authorization.\n    \"\"\"\n\n    VISIBILITY_HIDDEN = \"hidden\"\n    VISIBILITY_VISIBLE = \"visible\"\n\n    VISIBILITY_CHOICES = (\n        (\"hidden\", _(\"Hidden versions\")),\n        (\"visible\", _(\"Visible versions\")),\n    )\n\n    PRIVACY_CHOICES = (\n        (\"public\", _(\"Public versions\")),\n        (\"private\", _(\"Private versions\")),\n    )\n\n    # Attribute filter fields\n    slug = FilteredModelChoiceFilter(\n        label=_(\"Version\"),\n        empty_label=_(\"All versions\"),\n        to_field_name=\"slug\",\n        queryset_method=\"get_version_queryset\",\n        method=\"get_version\",\n        label_attribute=\"verbose_name\",\n    )\n\n    privacy = ChoiceFilter(\n        field_name=\"privacy_level\",\n        label=_(\"Privacy\"),\n        choices=PRIVACY_CHOICES,\n        empty_label=_(\"Any\"),\n    )\n    # This field looks better as ``visibility=hidden`` than it does\n    # ``hidden=true``, otherwise we could use a BooleanFilter instance here\n    # instead\n    visibility = ChoiceFilter(\n        field_name=\"hidden\",\n        label=_(\"Visibility\"),\n        choices=VISIBILITY_CHOICES,\n        method=\"get_visibility\",\n        empty_label=_(\"Any\"),\n    )\n\n    sort = VersionSortOrderingFilter(\n        field_name=\"sort\",\n        label=_(\"Sort by\"),\n    )\n\n    def __init__(self, *args, project=None, **kwargs):\n        self.project = project\n        super().__init__(*args, **kwargs)\n\n    def get_version(self, queryset, field_name, version):\n        return queryset.filter(slug=version.slug)\n\n    def get_version_queryset(self):\n        # This query is passed in at instantiation\n        return self.queryset\n\n    def get_visibility(self, queryset, field_name, value):\n        if value == self.VISIBILITY_HIDDEN:\n            return queryset.filter(hidden=True)\n        if value == self.VISIBILITY_VISIBLE:\n            return queryset.filter(hidden=False)\n        return queryset\n", "path": "readthedocs/projects/filters.py"}]}