We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
proposal list - url params are not remembered correctly after adding a token
**URL:** https://meinberlin-dev.liqd.net/projekte/module/burgerinnenhaushalt-3-phasen-23/ 
**user:** 
**expected behaviour:** filtering is kept after posting a token
**behaviour:** filtering gets lost if it was applied on react list
**important screensize:**
**device & browser:** 
**Comment/Question:** The filters/query params are passed via a tempatetag [here](https://github.com/liqd/a4-meinberlin/blob/7c6f2c3add38af5a4383a871a1d04d3c3adb3c63/meinberlin/apps/budgeting/templates/meinberlin_budgeting/proposal_list.html#L26). The problem is that request.GET does not get updated unless the page is reloaded, so it will not reflect any filters that have been changed by react. Instead, it will keep the filters from last page reload (e.g. when coming from map with filters, those filters will be passed, no matter what was changed in the meantime).

Screenshot?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `meinberlin/apps/budgeting/views.py`
Content:
```
1 import datetime
2 from urllib.parse import parse_qs
3 from urllib.parse import urlparse
4 
5 import django_filters
6 from django.urls import resolve
7 from django.urls import reverse
8 from django.utils.translation import gettext_lazy as _
9 
10 from adhocracy4.categories import filters as category_filters
11 from adhocracy4.exports.views import DashboardExportView
12 from adhocracy4.filters import filters as a4_filters
13 from adhocracy4.labels import filters as label_filters
14 from adhocracy4.modules.predicates import module_is_between_phases
15 from adhocracy4.projects.mixins import DisplayProjectOrModuleMixin
16 from meinberlin.apps.ideas import views as idea_views
17 from meinberlin.apps.moderatorremark.forms import ModeratorRemarkForm
18 from meinberlin.apps.projects.views import ArchivedWidget
19 from meinberlin.apps.votes.forms import TokenForm
20 from meinberlin.apps.votes.models import VotingToken
21 
22 from . import forms
23 from . import models
24 
25 TOKEN_SESSION_EXPIRE = datetime.timedelta(hours=12)
26 
27 
28 def get_ordering_choices(view):
29     choices = (("-created", _("Most recent")),)
30     if view.module.has_feature("rate", models.Proposal):
31         choices += (("-positive_rating_count", _("Most popular")),)
32     elif view.module.has_feature("support", models.Proposal):
33         choices += (("-positive_rating_count", _("Most support")),)
34     choices += (
35         ("-comment_count", _("Most commented")),
36         ("dailyrandom", _("Random")),
37     )
38     return choices
39 
40 
41 def get_default_ordering(view):
42     if module_is_between_phases(
43         "meinberlin_budgeting:support", "meinberlin_budgeting:voting", view.module
44     ):
45         return "-positive_rating_count"
46     elif (
47         view.module.has_feature("vote", models.Proposal)
48         and view.module.module_has_finished
49     ):
50         return "-token_vote_count"
51     return "dailyrandom"
52 
53 
54 class ProposalFilterSet(a4_filters.DefaultsFilterSet):
55     defaults = {"is_archived": "false"}
56     category = category_filters.CategoryFilter()
57     labels = label_filters.LabelFilter()
58     ordering = a4_filters.DistinctOrderingWithDailyRandomFilter(
59         choices=get_ordering_choices
60     )
61     is_archived = django_filters.BooleanFilter(widget=ArchivedWidget)
62 
63     class Meta:
64         model = models.Proposal
65         fields = ["category", "labels", "is_archived"]
66 
67     def __init__(self, data, *args, **kwargs):
68         self.defaults["ordering"] = get_default_ordering(kwargs["view"])
69         super().__init__(data, *args, **kwargs)
70 
71 
72 class ProposalListView(idea_views.AbstractIdeaListView, DisplayProjectOrModuleMixin):
73     model = models.Proposal
74     filter_set = ProposalFilterSet
75 
76     def has_valid_token_in_session(self, request):
77         """Return whether a valid token is stored in the session.
78 
79         The token is valid if it is valid for the respective module.
80         """
81         if "voting_tokens" in request.session:
82             module_key = str(self.module.id)
83             if module_key in request.session["voting_tokens"]:
84                 return (
85                     VotingToken.get_voting_token_by_hash(
86                         token_hash=request.session["voting_tokens"][module_key],
87                         module=self.module,
88                     )
89                     is not None
90                 )
91         return False
92 
93     def dispatch(self, request, **kwargs):
94         self.mode = request.GET.get("mode", "map")
95         if self.mode == "map":
96             self.paginate_by = 0
97         return super().dispatch(request, **kwargs)
98 
99     def get_queryset(self):
100         return super().get_queryset().filter(module=self.module)
101 
102     def get_context_data(self, **kwargs):
103         if "token_form" not in kwargs:
104             token_form = TokenForm(module_id=self.module.id)
105             kwargs["token_form"] = token_form
106         kwargs["valid_token_present"] = self.has_valid_token_in_session(self.request)
107         return super().get_context_data(**kwargs)
108 
109     def post(self, request, *args, **kwargs):
110         self.object_list = self.get_queryset()
111         token_form = TokenForm(request.POST, module_id=self.module.id)
112         if token_form.is_valid():
113             if "voting_tokens" in request.session:
114                 request.session["voting_tokens"][
115                     str(self.module.id)
116                 ] = token_form.cleaned_data["token"]
117                 request.session.modified = True
118             else:
119                 request.session["voting_tokens"] = {
120                     str(self.module.id): token_form.cleaned_data["token"]
121                 }
122             request.session["token_expire_date"] = (
123                 datetime.datetime.now() + TOKEN_SESSION_EXPIRE
124             ).timestamp()
125             kwargs["valid_token_present"] = True
126         kwargs["token_form"] = token_form
127         context = super().get_context_data(**kwargs)
128         return self.render_to_response(context)
129 
130 
131 class ProposalDetailView(idea_views.AbstractIdeaDetailView):
132     model = models.Proposal
133     queryset = (
134         models.Proposal.objects.annotate_positive_rating_count()
135         .annotate_negative_rating_count()
136         .annotate_token_vote_count()
137     )
138     permission_required = "meinberlin_budgeting.view_proposal"
139 
140     def get_back(self):
141         """
142         Get last page to return to if was project or module view.
143 
144         To make sure all the filters and the display mode (map or list)
145         are remembered when going back, we check if the referer is a
146         module or project detail view and add the appropriate back url.
147         """
148         back_link = self.module.get_detail_url
149         back_string = _("map")
150         if "Referer" in self.request.headers:
151             referer = self.request.headers["Referer"]
152             parsed_url = urlparse(referer)
153             match = resolve(parsed_url.path)
154             if match.url_name == "project-detail" or match.url_name == "module-detail":
155                 if "mode" in parse_qs(parsed_url.query):
156                     back_mode = parse_qs(parsed_url.query)["mode"][0]
157                     if back_mode == "list":
158                         back_string = _("list")
159                 back_link = referer + "#proposal_{}".format(self.object.id)
160                 return back_link, back_string
161             return back_link, back_string
162         return back_link, back_string
163 
164     def has_valid_token_in_session(self, request):
165         """Return whether a valid token is stored in the session.
166 
167         The token is valid if it is valid for the respective module.
168         """
169         if "voting_tokens" in request.session:
170             module_key = str(self.module.id)
171             if module_key in request.session["voting_tokens"]:
172                 return (
173                     VotingToken.get_voting_token_by_hash(
174                         token_hash=request.session["voting_tokens"][module_key],
175                         module=self.module,
176                     )
177                     is not None
178                 )
179         return False
180 
181     def get_context_data(self, **kwargs):
182         context = super().get_context_data(**kwargs)
183         back_link, back_string = self.get_back()
184         context["back"] = back_link
185         context["back_string"] = back_string
186         context["has_valid_token_in_session"] = self.has_valid_token_in_session(
187             self.request
188         )
189         return context
190 
191 
192 class ProposalCreateView(idea_views.AbstractIdeaCreateView):
193     model = models.Proposal
194     form_class = forms.ProposalForm
195     permission_required = "meinberlin_budgeting.add_proposal"
196     template_name = "meinberlin_budgeting/proposal_create_form.html"
197 
198     def get_form_kwargs(self):
199         kwargs = super().get_form_kwargs()
200         kwargs["user"] = self.request.user
201         return kwargs
202 
203 
204 class ProposalUpdateView(idea_views.AbstractIdeaUpdateView):
205     model = models.Proposal
206     form_class = forms.ProposalForm
207     permission_required = "meinberlin_budgeting.change_proposal"
208     template_name = "meinberlin_budgeting/proposal_update_form.html"
209 
210     def get_form_kwargs(self):
211         kwargs = super().get_form_kwargs()
212         kwargs["user"] = self.request.user
213         return kwargs
214 
215 
216 class ProposalDeleteView(idea_views.AbstractIdeaDeleteView):
217     model = models.Proposal
218     success_message = _("Your budget request has been deleted")
219     permission_required = "meinberlin_budgeting.change_proposal"
220     template_name = "meinberlin_budgeting/proposal_confirm_delete.html"
221 
222 
223 class ProposalModerateView(idea_views.AbstractIdeaModerateView):
224     model = models.Proposal
225     permission_required = "meinberlin_budgeting.moderate_proposal"
226     template_name = "meinberlin_budgeting/proposal_moderate_form.html"
227     moderateable_form_class = forms.ProposalModerateForm
228     remark_form_class = ModeratorRemarkForm
229 
230 
231 class ProposalDashboardExportView(DashboardExportView):
232     template_name = "a4exports/export_dashboard.html"
233 
234     def get_context_data(self, **kwargs):
235         context = super().get_context_data(**kwargs)
236         context["export"] = reverse(
237             "a4dashboard:budgeting-export", kwargs={"module_slug": self.module.slug}
238         )
239         context["comment_export"] = reverse(
240             "a4dashboard:budgeting-comment-export",
241             kwargs={"module_slug": self.module.slug},
242         )
243         return context
244
```
--- 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/meinberlin/apps/budgeting/views.py b/meinberlin/apps/budgeting/views.py
--- a/meinberlin/apps/budgeting/views.py
+++ b/meinberlin/apps/budgeting/views.py
@@ -3,6 +3,7 @@
 from urllib.parse import urlparse
 
 import django_filters
+from django.shortcuts import redirect
 from django.urls import resolve
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
@@ -123,6 +124,16 @@
                 datetime.datetime.now() + TOKEN_SESSION_EXPIRE
             ).timestamp()
             kwargs["valid_token_present"] = True
+            # redirect to the list view, but keep filters after successful
+            # token addition
+            list_url = request.path
+            parameter = request.GET.copy()
+            if not parameter.get("mode") or "list" not in parameter.get("mode"):
+                parameter.setlist("mode", ["list"])
+            encoded_parameter = "?" + parameter.urlencode()
+            return redirect(list_url + encoded_parameter)
+        # stay on the same page if the token form has errors,
+        # add the form including errors so that they are displayed.
         kwargs["token_form"] = token_form
         context = super().get_context_data(**kwargs)
         return self.render_to_response(context)

{"golden_diff": "diff --git a/meinberlin/apps/budgeting/views.py b/meinberlin/apps/budgeting/views.py\n--- a/meinberlin/apps/budgeting/views.py\n+++ b/meinberlin/apps/budgeting/views.py\n@@ -3,6 +3,7 @@\n from urllib.parse import urlparse\n \n import django_filters\n+from django.shortcuts import redirect\n from django.urls import resolve\n from django.urls import reverse\n from django.utils.translation import gettext_lazy as _\n@@ -123,6 +124,16 @@\n                 datetime.datetime.now() + TOKEN_SESSION_EXPIRE\n             ).timestamp()\n             kwargs[\"valid_token_present\"] = True\n+            # redirect to the list view, but keep filters after successful\n+            # token addition\n+            list_url = request.path\n+            parameter = request.GET.copy()\n+            if not parameter.get(\"mode\") or \"list\" not in parameter.get(\"mode\"):\n+                parameter.setlist(\"mode\", [\"list\"])\n+            encoded_parameter = \"?\" + parameter.urlencode()\n+            return redirect(list_url + encoded_parameter)\n+        # stay on the same page if the token form has errors,\n+        # add the form including errors so that they are displayed.\n         kwargs[\"token_form\"] = token_form\n         context = super().get_context_data(**kwargs)\n         return self.render_to_response(context)\n", "issue": "proposal list - url params are not remembered correctly after adding a token\n**URL:** https://meinberlin-dev.liqd.net/projekte/module/burgerinnenhaushalt-3-phasen-23/ \r\n**user:** \r\n**expected behaviour:** filtering is kept after posting a token\r\n**behaviour:** filtering gets lost if it was applied on react list\r\n**important screensize:**\r\n**device & browser:** \r\n**Comment/Question:** The filters/query params are passed via a tempatetag [here](https://github.com/liqd/a4-meinberlin/blob/7c6f2c3add38af5a4383a871a1d04d3c3adb3c63/meinberlin/apps/budgeting/templates/meinberlin_budgeting/proposal_list.html#L26). The problem is that request.GET does not get updated unless the page is reloaded, so it will not reflect any filters that have been changed by react. Instead, it will keep the filters from last page reload (e.g. when coming from map with filters, those filters will be passed, no matter what was changed in the meantime).\r\n\r\nScreenshot?\r\n\n", "before_files": [{"content": "import datetime\nfrom urllib.parse import parse_qs\nfrom urllib.parse import urlparse\n\nimport django_filters\nfrom django.urls import resolve\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\n\nfrom adhocracy4.categories import filters as category_filters\nfrom adhocracy4.exports.views import DashboardExportView\nfrom adhocracy4.filters import filters as a4_filters\nfrom adhocracy4.labels import filters as label_filters\nfrom adhocracy4.modules.predicates import module_is_between_phases\nfrom adhocracy4.projects.mixins import DisplayProjectOrModuleMixin\nfrom meinberlin.apps.ideas import views as idea_views\nfrom meinberlin.apps.moderatorremark.forms import ModeratorRemarkForm\nfrom meinberlin.apps.projects.views import ArchivedWidget\nfrom meinberlin.apps.votes.forms import TokenForm\nfrom meinberlin.apps.votes.models import VotingToken\n\nfrom . import forms\nfrom . import models\n\nTOKEN_SESSION_EXPIRE = datetime.timedelta(hours=12)\n\n\ndef get_ordering_choices(view):\n    choices = ((\"-created\", _(\"Most recent\")),)\n    if view.module.has_feature(\"rate\", models.Proposal):\n        choices += ((\"-positive_rating_count\", _(\"Most popular\")),)\n    elif view.module.has_feature(\"support\", models.Proposal):\n        choices += ((\"-positive_rating_count\", _(\"Most support\")),)\n    choices += (\n        (\"-comment_count\", _(\"Most commented\")),\n        (\"dailyrandom\", _(\"Random\")),\n    )\n    return choices\n\n\ndef get_default_ordering(view):\n    if module_is_between_phases(\n        \"meinberlin_budgeting:support\", \"meinberlin_budgeting:voting\", view.module\n    ):\n        return \"-positive_rating_count\"\n    elif (\n        view.module.has_feature(\"vote\", models.Proposal)\n        and view.module.module_has_finished\n    ):\n        return \"-token_vote_count\"\n    return \"dailyrandom\"\n\n\nclass ProposalFilterSet(a4_filters.DefaultsFilterSet):\n    defaults = {\"is_archived\": \"false\"}\n    category = category_filters.CategoryFilter()\n    labels = label_filters.LabelFilter()\n    ordering = a4_filters.DistinctOrderingWithDailyRandomFilter(\n        choices=get_ordering_choices\n    )\n    is_archived = django_filters.BooleanFilter(widget=ArchivedWidget)\n\n    class Meta:\n        model = models.Proposal\n        fields = [\"category\", \"labels\", \"is_archived\"]\n\n    def __init__(self, data, *args, **kwargs):\n        self.defaults[\"ordering\"] = get_default_ordering(kwargs[\"view\"])\n        super().__init__(data, *args, **kwargs)\n\n\nclass ProposalListView(idea_views.AbstractIdeaListView, DisplayProjectOrModuleMixin):\n    model = models.Proposal\n    filter_set = ProposalFilterSet\n\n    def has_valid_token_in_session(self, request):\n        \"\"\"Return whether a valid token is stored in the session.\n\n        The token is valid if it is valid for the respective module.\n        \"\"\"\n        if \"voting_tokens\" in request.session:\n            module_key = str(self.module.id)\n            if module_key in request.session[\"voting_tokens\"]:\n                return (\n                    VotingToken.get_voting_token_by_hash(\n                        token_hash=request.session[\"voting_tokens\"][module_key],\n                        module=self.module,\n                    )\n                    is not None\n                )\n        return False\n\n    def dispatch(self, request, **kwargs):\n        self.mode = request.GET.get(\"mode\", \"map\")\n        if self.mode == \"map\":\n            self.paginate_by = 0\n        return super().dispatch(request, **kwargs)\n\n    def get_queryset(self):\n        return super().get_queryset().filter(module=self.module)\n\n    def get_context_data(self, **kwargs):\n        if \"token_form\" not in kwargs:\n            token_form = TokenForm(module_id=self.module.id)\n            kwargs[\"token_form\"] = token_form\n        kwargs[\"valid_token_present\"] = self.has_valid_token_in_session(self.request)\n        return super().get_context_data(**kwargs)\n\n    def post(self, request, *args, **kwargs):\n        self.object_list = self.get_queryset()\n        token_form = TokenForm(request.POST, module_id=self.module.id)\n        if token_form.is_valid():\n            if \"voting_tokens\" in request.session:\n                request.session[\"voting_tokens\"][\n                    str(self.module.id)\n                ] = token_form.cleaned_data[\"token\"]\n                request.session.modified = True\n            else:\n                request.session[\"voting_tokens\"] = {\n                    str(self.module.id): token_form.cleaned_data[\"token\"]\n                }\n            request.session[\"token_expire_date\"] = (\n                datetime.datetime.now() + TOKEN_SESSION_EXPIRE\n            ).timestamp()\n            kwargs[\"valid_token_present\"] = True\n        kwargs[\"token_form\"] = token_form\n        context = super().get_context_data(**kwargs)\n        return self.render_to_response(context)\n\n\nclass ProposalDetailView(idea_views.AbstractIdeaDetailView):\n    model = models.Proposal\n    queryset = (\n        models.Proposal.objects.annotate_positive_rating_count()\n        .annotate_negative_rating_count()\n        .annotate_token_vote_count()\n    )\n    permission_required = \"meinberlin_budgeting.view_proposal\"\n\n    def get_back(self):\n        \"\"\"\n        Get last page to return to if was project or module view.\n\n        To make sure all the filters and the display mode (map or list)\n        are remembered when going back, we check if the referer is a\n        module or project detail view and add the appropriate back url.\n        \"\"\"\n        back_link = self.module.get_detail_url\n        back_string = _(\"map\")\n        if \"Referer\" in self.request.headers:\n            referer = self.request.headers[\"Referer\"]\n            parsed_url = urlparse(referer)\n            match = resolve(parsed_url.path)\n            if match.url_name == \"project-detail\" or match.url_name == \"module-detail\":\n                if \"mode\" in parse_qs(parsed_url.query):\n                    back_mode = parse_qs(parsed_url.query)[\"mode\"][0]\n                    if back_mode == \"list\":\n                        back_string = _(\"list\")\n                back_link = referer + \"#proposal_{}\".format(self.object.id)\n                return back_link, back_string\n            return back_link, back_string\n        return back_link, back_string\n\n    def has_valid_token_in_session(self, request):\n        \"\"\"Return whether a valid token is stored in the session.\n\n        The token is valid if it is valid for the respective module.\n        \"\"\"\n        if \"voting_tokens\" in request.session:\n            module_key = str(self.module.id)\n            if module_key in request.session[\"voting_tokens\"]:\n                return (\n                    VotingToken.get_voting_token_by_hash(\n                        token_hash=request.session[\"voting_tokens\"][module_key],\n                        module=self.module,\n                    )\n                    is not None\n                )\n        return False\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        back_link, back_string = self.get_back()\n        context[\"back\"] = back_link\n        context[\"back_string\"] = back_string\n        context[\"has_valid_token_in_session\"] = self.has_valid_token_in_session(\n            self.request\n        )\n        return context\n\n\nclass ProposalCreateView(idea_views.AbstractIdeaCreateView):\n    model = models.Proposal\n    form_class = forms.ProposalForm\n    permission_required = \"meinberlin_budgeting.add_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_create_form.html\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user\"] = self.request.user\n        return kwargs\n\n\nclass ProposalUpdateView(idea_views.AbstractIdeaUpdateView):\n    model = models.Proposal\n    form_class = forms.ProposalForm\n    permission_required = \"meinberlin_budgeting.change_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_update_form.html\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user\"] = self.request.user\n        return kwargs\n\n\nclass ProposalDeleteView(idea_views.AbstractIdeaDeleteView):\n    model = models.Proposal\n    success_message = _(\"Your budget request has been deleted\")\n    permission_required = \"meinberlin_budgeting.change_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_confirm_delete.html\"\n\n\nclass ProposalModerateView(idea_views.AbstractIdeaModerateView):\n    model = models.Proposal\n    permission_required = \"meinberlin_budgeting.moderate_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_moderate_form.html\"\n    moderateable_form_class = forms.ProposalModerateForm\n    remark_form_class = ModeratorRemarkForm\n\n\nclass ProposalDashboardExportView(DashboardExportView):\n    template_name = \"a4exports/export_dashboard.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"export\"] = reverse(\n            \"a4dashboard:budgeting-export\", kwargs={\"module_slug\": self.module.slug}\n        )\n        context[\"comment_export\"] = reverse(\n            \"a4dashboard:budgeting-comment-export\",\n            kwargs={\"module_slug\": self.module.slug},\n        )\n        return context\n", "path": "meinberlin/apps/budgeting/views.py"}], "after_files": [{"content": "import datetime\nfrom urllib.parse import parse_qs\nfrom urllib.parse import urlparse\n\nimport django_filters\nfrom django.shortcuts import redirect\nfrom django.urls import resolve\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\n\nfrom adhocracy4.categories import filters as category_filters\nfrom adhocracy4.exports.views import DashboardExportView\nfrom adhocracy4.filters import filters as a4_filters\nfrom adhocracy4.labels import filters as label_filters\nfrom adhocracy4.modules.predicates import module_is_between_phases\nfrom adhocracy4.projects.mixins import DisplayProjectOrModuleMixin\nfrom meinberlin.apps.ideas import views as idea_views\nfrom meinberlin.apps.moderatorremark.forms import ModeratorRemarkForm\nfrom meinberlin.apps.projects.views import ArchivedWidget\nfrom meinberlin.apps.votes.forms import TokenForm\nfrom meinberlin.apps.votes.models import VotingToken\n\nfrom . import forms\nfrom . import models\n\nTOKEN_SESSION_EXPIRE = datetime.timedelta(hours=12)\n\n\ndef get_ordering_choices(view):\n    choices = ((\"-created\", _(\"Most recent\")),)\n    if view.module.has_feature(\"rate\", models.Proposal):\n        choices += ((\"-positive_rating_count\", _(\"Most popular\")),)\n    elif view.module.has_feature(\"support\", models.Proposal):\n        choices += ((\"-positive_rating_count\", _(\"Most support\")),)\n    choices += (\n        (\"-comment_count\", _(\"Most commented\")),\n        (\"dailyrandom\", _(\"Random\")),\n    )\n    return choices\n\n\ndef get_default_ordering(view):\n    if module_is_between_phases(\n        \"meinberlin_budgeting:support\", \"meinberlin_budgeting:voting\", view.module\n    ):\n        return \"-positive_rating_count\"\n    elif (\n        view.module.has_feature(\"vote\", models.Proposal)\n        and view.module.module_has_finished\n    ):\n        return \"-token_vote_count\"\n    return \"dailyrandom\"\n\n\nclass ProposalFilterSet(a4_filters.DefaultsFilterSet):\n    defaults = {\"is_archived\": \"false\"}\n    category = category_filters.CategoryFilter()\n    labels = label_filters.LabelFilter()\n    ordering = a4_filters.DistinctOrderingWithDailyRandomFilter(\n        choices=get_ordering_choices\n    )\n    is_archived = django_filters.BooleanFilter(widget=ArchivedWidget)\n\n    class Meta:\n        model = models.Proposal\n        fields = [\"category\", \"labels\", \"is_archived\"]\n\n    def __init__(self, data, *args, **kwargs):\n        self.defaults[\"ordering\"] = get_default_ordering(kwargs[\"view\"])\n        super().__init__(data, *args, **kwargs)\n\n\nclass ProposalListView(idea_views.AbstractIdeaListView, DisplayProjectOrModuleMixin):\n    model = models.Proposal\n    filter_set = ProposalFilterSet\n\n    def has_valid_token_in_session(self, request):\n        \"\"\"Return whether a valid token is stored in the session.\n\n        The token is valid if it is valid for the respective module.\n        \"\"\"\n        if \"voting_tokens\" in request.session:\n            module_key = str(self.module.id)\n            if module_key in request.session[\"voting_tokens\"]:\n                return (\n                    VotingToken.get_voting_token_by_hash(\n                        token_hash=request.session[\"voting_tokens\"][module_key],\n                        module=self.module,\n                    )\n                    is not None\n                )\n        return False\n\n    def dispatch(self, request, **kwargs):\n        self.mode = request.GET.get(\"mode\", \"map\")\n        if self.mode == \"map\":\n            self.paginate_by = 0\n        return super().dispatch(request, **kwargs)\n\n    def get_queryset(self):\n        return super().get_queryset().filter(module=self.module)\n\n    def get_context_data(self, **kwargs):\n        if \"token_form\" not in kwargs:\n            token_form = TokenForm(module_id=self.module.id)\n            kwargs[\"token_form\"] = token_form\n        kwargs[\"valid_token_present\"] = self.has_valid_token_in_session(self.request)\n        return super().get_context_data(**kwargs)\n\n    def post(self, request, *args, **kwargs):\n        self.object_list = self.get_queryset()\n        token_form = TokenForm(request.POST, module_id=self.module.id)\n        if token_form.is_valid():\n            if \"voting_tokens\" in request.session:\n                request.session[\"voting_tokens\"][\n                    str(self.module.id)\n                ] = token_form.cleaned_data[\"token\"]\n                request.session.modified = True\n            else:\n                request.session[\"voting_tokens\"] = {\n                    str(self.module.id): token_form.cleaned_data[\"token\"]\n                }\n            request.session[\"token_expire_date\"] = (\n                datetime.datetime.now() + TOKEN_SESSION_EXPIRE\n            ).timestamp()\n            kwargs[\"valid_token_present\"] = True\n            # redirect to the list view, but keep filters after successful\n            # token addition\n            list_url = request.path\n            parameter = request.GET.copy()\n            if not parameter.get(\"mode\") or \"list\" not in parameter.get(\"mode\"):\n                parameter.setlist(\"mode\", [\"list\"])\n            encoded_parameter = \"?\" + parameter.urlencode()\n            return redirect(list_url + encoded_parameter)\n        # stay on the same page if the token form has errors,\n        # add the form including errors so that they are displayed.\n        kwargs[\"token_form\"] = token_form\n        context = super().get_context_data(**kwargs)\n        return self.render_to_response(context)\n\n\nclass ProposalDetailView(idea_views.AbstractIdeaDetailView):\n    model = models.Proposal\n    queryset = (\n        models.Proposal.objects.annotate_positive_rating_count()\n        .annotate_negative_rating_count()\n        .annotate_token_vote_count()\n    )\n    permission_required = \"meinberlin_budgeting.view_proposal\"\n\n    def get_back(self):\n        \"\"\"\n        Get last page to return to if was project or module view.\n\n        To make sure all the filters and the display mode (map or list)\n        are remembered when going back, we check if the referer is a\n        module or project detail view and add the appropriate back url.\n        \"\"\"\n        back_link = self.module.get_detail_url\n        back_string = _(\"map\")\n        if \"Referer\" in self.request.headers:\n            referer = self.request.headers[\"Referer\"]\n            parsed_url = urlparse(referer)\n            match = resolve(parsed_url.path)\n            if match.url_name == \"project-detail\" or match.url_name == \"module-detail\":\n                if \"mode\" in parse_qs(parsed_url.query):\n                    back_mode = parse_qs(parsed_url.query)[\"mode\"][0]\n                    if back_mode == \"list\":\n                        back_string = _(\"list\")\n                back_link = referer + \"#proposal_{}\".format(self.object.id)\n                return back_link, back_string\n            return back_link, back_string\n        return back_link, back_string\n\n    def has_valid_token_in_session(self, request):\n        \"\"\"Return whether a valid token is stored in the session.\n\n        The token is valid if it is valid for the respective module.\n        \"\"\"\n        if \"voting_tokens\" in request.session:\n            module_key = str(self.module.id)\n            if module_key in request.session[\"voting_tokens\"]:\n                return (\n                    VotingToken.get_voting_token_by_hash(\n                        token_hash=request.session[\"voting_tokens\"][module_key],\n                        module=self.module,\n                    )\n                    is not None\n                )\n        return False\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        back_link, back_string = self.get_back()\n        context[\"back\"] = back_link\n        context[\"back_string\"] = back_string\n        context[\"has_valid_token_in_session\"] = self.has_valid_token_in_session(\n            self.request\n        )\n        return context\n\n\nclass ProposalCreateView(idea_views.AbstractIdeaCreateView):\n    model = models.Proposal\n    form_class = forms.ProposalForm\n    permission_required = \"meinberlin_budgeting.add_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_create_form.html\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user\"] = self.request.user\n        return kwargs\n\n\nclass ProposalUpdateView(idea_views.AbstractIdeaUpdateView):\n    model = models.Proposal\n    form_class = forms.ProposalForm\n    permission_required = \"meinberlin_budgeting.change_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_update_form.html\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user\"] = self.request.user\n        return kwargs\n\n\nclass ProposalDeleteView(idea_views.AbstractIdeaDeleteView):\n    model = models.Proposal\n    success_message = _(\"Your budget request has been deleted\")\n    permission_required = \"meinberlin_budgeting.change_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_confirm_delete.html\"\n\n\nclass ProposalModerateView(idea_views.AbstractIdeaModerateView):\n    model = models.Proposal\n    permission_required = \"meinberlin_budgeting.moderate_proposal\"\n    template_name = \"meinberlin_budgeting/proposal_moderate_form.html\"\n    moderateable_form_class = forms.ProposalModerateForm\n    remark_form_class = ModeratorRemarkForm\n\n\nclass ProposalDashboardExportView(DashboardExportView):\n    template_name = \"a4exports/export_dashboard.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"export\"] = reverse(\n            \"a4dashboard:budgeting-export\", kwargs={\"module_slug\": self.module.slug}\n        )\n        context[\"comment_export\"] = reverse(\n            \"a4dashboard:budgeting-comment-export\",\n            kwargs={\"module_slug\": self.module.slug},\n        )\n        return context\n", "path": "meinberlin/apps/budgeting/views.py"}]}