We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
make sure the next-module thing works for the polls
**URL:** https://meinberlin-dev.liqd.net/projekte/this-tests-two-polls-in-one-project/
**user:** 
**expected behaviour:** 
**behaviour:** The poll module from mB uses the get_detail_url (I think) and can thus display the next-module thing, and a back-button. Using the poll app from a4 in mB this breaks.
**important screensize:**
**device & browser:** 
**Comment/Question:** 

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: `adhocracy4/projects/mixins.py`
Content:
```
1 from django.http import Http404
2 from django.http.response import HttpResponseRedirect
3 from django.shortcuts import get_object_or_404
4 from django.urls import resolve
5 from django.utils.functional import cached_property
6 from django.views import generic
7 
8 from adhocracy4.modules.models import Module
9 from adhocracy4.projects.models import Project
10 
11 
12 class PhaseDispatchMixin(generic.DetailView):
13 
14     @cached_property
15     def project(self):
16         return self.get_object()
17 
18     @cached_property
19     def module(self):
20         return self.project.last_active_module
21 
22     def dispatch(self, request, *args, **kwargs):
23         # Choose the appropriate view for the current active phase.
24         kwargs['project'] = self.project
25         kwargs['module'] = self.module
26 
27         return self._view_by_phase()(request, *args, **kwargs)
28 
29     def _view_by_phase(self):
30         """
31         Choose the appropriate view for the current active phase.
32         """
33         if self.module and self.module.last_active_phase:
34             return self.module.last_active_phase.view.as_view()
35         else:
36             return super().dispatch
37 
38 
39 class ModuleDispatchMixin(PhaseDispatchMixin):
40 
41     @cached_property
42     def project(self):
43         return self.module.project
44 
45     @cached_property
46     def module(self):
47         return self.get_object()
48 
49     def dispatch(self, request, *args, **kwargs):
50         # Redirect to the project detail page if the module is shown there
51         if self.module == self.project.last_active_module:
52             return HttpResponseRedirect(self.project.get_absolute_url())
53 
54         return super().dispatch(request, *args, **kwargs)
55 
56 
57 class ProjectMixin(generic.base.ContextMixin):
58     """Add project and module attributes to the view and the template context.
59 
60     This is a counterpart to the Phase- / ModuleDispatcher logic.
61 
62     To consider the object context from get_object() set the
63     get_context_from_object attribute. Enable this only if get_object() does
64     not access the project and module properties.
65     """
66 
67     project_lookup_field = 'slug'
68     project_url_kwarg = 'project_slug'
69     module_lookup_field = 'slug'
70     module_url_kwarg = 'module_slug'
71     get_context_from_object = False
72 
73     @property
74     def module(self):
75         """Get the module from the current object, kwargs or url."""
76         if self.get_context_from_object:
77             return self._get_object(Module, 'module')
78 
79         if 'module' in self.kwargs \
80                 and isinstance(self.kwargs['module'], Module):
81             return self.kwargs['module']
82 
83         if self.module_url_kwarg and self.module_url_kwarg in self.kwargs:
84             lookup = {
85                 self.module_lookup_field: self.kwargs[self.module_url_kwarg]
86             }
87             return get_object_or_404(Module, **lookup)
88 
89     @property
90     def project(self):
91         """Get the project from the module, kwargs, url or current object."""
92         if self.module:
93             return self.module.project
94 
95         if self.get_context_from_object:
96             return self._get_object(Project, 'project')
97 
98         if 'project' in self.kwargs \
99                 and isinstance(self.kwargs['project'], Project):
100             return self.kwargs['project']
101 
102         if self.project_url_kwarg and self.project_url_kwarg in self.kwargs:
103             lookup = {
104                 self.project_lookup_field: self.kwargs[self.project_url_kwarg]
105             }
106             return get_object_or_404(Project, **lookup)
107 
108     def _get_object(self, cls, attr):
109         # CreateView supplies a defect get_object method and has to be excluded
110         if hasattr(self, 'get_object') \
111                 and not isinstance(self, generic.CreateView):
112             try:
113                 object = self.get_object()
114                 if isinstance(object, cls):
115                     return object
116 
117                 if hasattr(object, attr):
118                     return getattr(object, attr)
119             except Http404:
120                 return None
121             except AttributeError:
122                 return None
123 
124         return None
125 
126     def get_context_data(self, **kwargs):
127         """Append project and module to the template context."""
128         if 'project' not in kwargs:
129             kwargs['project'] = self.project
130         if 'module' not in kwargs:
131             kwargs['module'] = self.module
132         return super().get_context_data(**kwargs)
133 
134 
135 class DisplayProjectOrModuleMixin(generic.base.ContextMixin):
136 
137     @cached_property
138     def url_name(self):
139         return resolve(self.request.path_info).url_name
140 
141     @cached_property
142     def extends(self):
143         if self.url_name == 'module-detail':
144             return 'a4modules/module_detail.html'
145         return 'a4projects/project_detail.html'
146 
147     @cached_property
148     def initial_slide(self):
149         initial_slide = self.request.GET.get('initialSlide')
150         if initial_slide:
151             initial_slide = ''.join(i for i in initial_slide if i.isdigit())
152             if initial_slide:
153                 return int(initial_slide)
154         elif self.project.get_current_participation_date():
155             return self.project.get_current_participation_date()
156         return 0
157 
158     def get_current_event(self):
159         idx = self.initial_slide
160         return self.project.get_current_event(idx)
161 
162     def get_current_modules(self):
163         idx = self.initial_slide
164         return self.project.get_current_modules(idx)
165 
166     def get_context_data(self, **kwargs):
167         context = super().get_context_data(**kwargs)
168         context['url_name'] = self.url_name
169         context['extends'] = self.extends
170         if not self.url_name == 'module-detail':
171             context['event'] = self.get_current_event()
172             context['modules'] = self.get_current_modules()
173             context['initial_slide'] = self.initial_slide
174         return context
175 
176 
177 class ProjectModuleDispatchMixin(generic.DetailView):
178 
179     @cached_property
180     def project(self):
181         return self.get_object()
182 
183     @cached_property
184     def module(self):
185         if (self.project.published_modules.count()
186                 == 1 and not self.project.events):
187             return self.project.published_modules.first()
188         elif len(self.get_current_modules()) == 1:
189             return self.get_current_modules()[0]
190 
191     def dispatch(self, request, *args, **kwargs):
192         kwargs['project'] = self.project
193         kwargs['module'] = self.module
194 
195         return self._view_by_phase()(request, *args, **kwargs)
196 
197     def _view_by_phase(self):
198         if self.module and self.module.last_active_phase:
199             return self.module.last_active_phase.view.as_view()
200         elif self.module and self.module.future_phases:
201             return self.module.future_phases.first().view.as_view()
202         else:
203             return super().dispatch
204
```

Path: `adhocracy4/polls/views.py`
Content:
```
1 from django.http import Http404
2 from django.shortcuts import get_object_or_404
3 from django.shortcuts import render_to_response
4 from django.views import generic
5 
6 from adhocracy4.dashboard import mixins as dashboard_mixins
7 from adhocracy4.projects.mixins import ProjectMixin
8 from adhocracy4.rules import mixins as rules_mixins
9 
10 from . import models
11 
12 
13 class PollDetailView(ProjectMixin,
14                      rules_mixins.PermissionRequiredMixin,
15                      generic.DetailView):
16     model = models.Poll
17     permission_required = 'a4polls.view_poll'
18 
19     def get(self, request, *args, **kwargs):
20         try:
21             self.object = self.get_object()
22             context = self.get_context_data(object=self.object)
23             return self.render_to_response(context)
24 
25         except Http404:
26             self.object = None
27             context = self.get_context_data(object=None, request=self.request,)
28             return render_to_response(
29                 'a4polls/poll_404.html',
30                 context=context,
31                 status=404
32             )
33 
34     def get_object(self):
35         return get_object_or_404(models.Poll, module=self.module)
36 
37     def get_context_data(self, **kwargs):
38         context = super().get_context_data(**kwargs)
39         if self.object is not None:
40             context['question_list'] = \
41                 self.object.questions.annotate_vote_count().all()
42         return context
43 
44     def get_permission_object(self):
45         return self.module
46 
47     @property
48     def phase(self):
49         try:
50             weight = self.request.GET.get('phase')
51             phase = self.project.phases.filter(weight=weight).first()
52         except ValueError:
53             phase = None
54 
55         if phase:
56             return phase
57         else:
58             return self.project.last_active_phase
59 
60 
61 class PollDashboardView(ProjectMixin,
62                         dashboard_mixins.DashboardBaseMixin,
63                         dashboard_mixins.DashboardComponentMixin,
64                         generic.TemplateView):
65     template_name = 'a4polls/poll_dashboard.html'
66     permission_required = 'a4projects.change_project'
67 
68     def get_context_data(self, **kwargs):
69         context = super().get_context_data(**kwargs)
70         context['poll'] = self.get_or_create_poll()
71         return context
72 
73     def get_or_create_poll(self):
74         try:
75             obj = models.Poll.objects.get(module=self.module)
76         except models.Poll.DoesNotExist:
77             obj = models.Poll(module=self.module,
78                               creator=self.request.user)
79             obj.save()
80         return obj
81 
82     def get_permission_object(self):
83         return self.project
84
```
--- 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/adhocracy4/polls/views.py b/adhocracy4/polls/views.py
--- a/adhocracy4/polls/views.py
+++ b/adhocracy4/polls/views.py
@@ -4,6 +4,7 @@
 from django.views import generic
 
 from adhocracy4.dashboard import mixins as dashboard_mixins
+from adhocracy4.projects.mixins import DisplayProjectOrModuleMixin
 from adhocracy4.projects.mixins import ProjectMixin
 from adhocracy4.rules import mixins as rules_mixins
 
@@ -12,7 +13,8 @@
 
 class PollDetailView(ProjectMixin,
                      rules_mixins.PermissionRequiredMixin,
-                     generic.DetailView):
+                     generic.DetailView,
+                     DisplayProjectOrModuleMixin):
     model = models.Poll
     permission_required = 'a4polls.view_poll'
 
diff --git a/adhocracy4/projects/mixins.py b/adhocracy4/projects/mixins.py
--- a/adhocracy4/projects/mixins.py
+++ b/adhocracy4/projects/mixins.py
@@ -133,6 +133,15 @@
 
 
 class DisplayProjectOrModuleMixin(generic.base.ContextMixin):
+    """Use the appropriate project or module view with timeline/cluster logic.
+
+    On platforms with multiple module projects, this should be used
+    with the phase view to display the module instead of the project
+    detail where appropriate. To do that, the template should extend
+    'extends' instead of a specific base template.
+    This mixin also makes sure the project view is shown with the
+    appropriate timeline tile activated.
+    """
 
     @cached_property
     def url_name(self):

{"golden_diff": "diff --git a/adhocracy4/polls/views.py b/adhocracy4/polls/views.py\n--- a/adhocracy4/polls/views.py\n+++ b/adhocracy4/polls/views.py\n@@ -4,6 +4,7 @@\n from django.views import generic\n \n from adhocracy4.dashboard import mixins as dashboard_mixins\n+from adhocracy4.projects.mixins import DisplayProjectOrModuleMixin\n from adhocracy4.projects.mixins import ProjectMixin\n from adhocracy4.rules import mixins as rules_mixins\n \n@@ -12,7 +13,8 @@\n \n class PollDetailView(ProjectMixin,\n                      rules_mixins.PermissionRequiredMixin,\n-                     generic.DetailView):\n+                     generic.DetailView,\n+                     DisplayProjectOrModuleMixin):\n     model = models.Poll\n     permission_required = 'a4polls.view_poll'\n \ndiff --git a/adhocracy4/projects/mixins.py b/adhocracy4/projects/mixins.py\n--- a/adhocracy4/projects/mixins.py\n+++ b/adhocracy4/projects/mixins.py\n@@ -133,6 +133,15 @@\n \n \n class DisplayProjectOrModuleMixin(generic.base.ContextMixin):\n+    \"\"\"Use the appropriate project or module view with timeline/cluster logic.\n+\n+    On platforms with multiple module projects, this should be used\n+    with the phase view to display the module instead of the project\n+    detail where appropriate. To do that, the template should extend\n+    'extends' instead of a specific base template.\n+    This mixin also makes sure the project view is shown with the\n+    appropriate timeline tile activated.\n+    \"\"\"\n \n     @cached_property\n     def url_name(self):\n", "issue": "make sure the next-module thing works for the polls\n**URL:** https://meinberlin-dev.liqd.net/projekte/this-tests-two-polls-in-one-project/\r\n**user:** \r\n**expected behaviour:** \r\n**behaviour:** The poll module from mB uses the get_detail_url (I think) and can thus display the next-module thing, and a back-button. Using the poll app from a4 in mB this breaks.\r\n**important screensize:**\r\n**device & browser:** \r\n**Comment/Question:** \r\n\r\nScreenshot?\r\n\n", "before_files": [{"content": "from django.http import Http404\nfrom django.http.response import HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404\nfrom django.urls import resolve\nfrom django.utils.functional import cached_property\nfrom django.views import generic\n\nfrom adhocracy4.modules.models import Module\nfrom adhocracy4.projects.models import Project\n\n\nclass PhaseDispatchMixin(generic.DetailView):\n\n    @cached_property\n    def project(self):\n        return self.get_object()\n\n    @cached_property\n    def module(self):\n        return self.project.last_active_module\n\n    def dispatch(self, request, *args, **kwargs):\n        # Choose the appropriate view for the current active phase.\n        kwargs['project'] = self.project\n        kwargs['module'] = self.module\n\n        return self._view_by_phase()(request, *args, **kwargs)\n\n    def _view_by_phase(self):\n        \"\"\"\n        Choose the appropriate view for the current active phase.\n        \"\"\"\n        if self.module and self.module.last_active_phase:\n            return self.module.last_active_phase.view.as_view()\n        else:\n            return super().dispatch\n\n\nclass ModuleDispatchMixin(PhaseDispatchMixin):\n\n    @cached_property\n    def project(self):\n        return self.module.project\n\n    @cached_property\n    def module(self):\n        return self.get_object()\n\n    def dispatch(self, request, *args, **kwargs):\n        # Redirect to the project detail page if the module is shown there\n        if self.module == self.project.last_active_module:\n            return HttpResponseRedirect(self.project.get_absolute_url())\n\n        return super().dispatch(request, *args, **kwargs)\n\n\nclass ProjectMixin(generic.base.ContextMixin):\n    \"\"\"Add project and module attributes to the view and the template context.\n\n    This is a counterpart to the Phase- / ModuleDispatcher logic.\n\n    To consider the object context from get_object() set the\n    get_context_from_object attribute. Enable this only if get_object() does\n    not access the project and module properties.\n    \"\"\"\n\n    project_lookup_field = 'slug'\n    project_url_kwarg = 'project_slug'\n    module_lookup_field = 'slug'\n    module_url_kwarg = 'module_slug'\n    get_context_from_object = False\n\n    @property\n    def module(self):\n        \"\"\"Get the module from the current object, kwargs or url.\"\"\"\n        if self.get_context_from_object:\n            return self._get_object(Module, 'module')\n\n        if 'module' in self.kwargs \\\n                and isinstance(self.kwargs['module'], Module):\n            return self.kwargs['module']\n\n        if self.module_url_kwarg and self.module_url_kwarg in self.kwargs:\n            lookup = {\n                self.module_lookup_field: self.kwargs[self.module_url_kwarg]\n            }\n            return get_object_or_404(Module, **lookup)\n\n    @property\n    def project(self):\n        \"\"\"Get the project from the module, kwargs, url or current object.\"\"\"\n        if self.module:\n            return self.module.project\n\n        if self.get_context_from_object:\n            return self._get_object(Project, 'project')\n\n        if 'project' in self.kwargs \\\n                and isinstance(self.kwargs['project'], Project):\n            return self.kwargs['project']\n\n        if self.project_url_kwarg and self.project_url_kwarg in self.kwargs:\n            lookup = {\n                self.project_lookup_field: self.kwargs[self.project_url_kwarg]\n            }\n            return get_object_or_404(Project, **lookup)\n\n    def _get_object(self, cls, attr):\n        # CreateView supplies a defect get_object method and has to be excluded\n        if hasattr(self, 'get_object') \\\n                and not isinstance(self, generic.CreateView):\n            try:\n                object = self.get_object()\n                if isinstance(object, cls):\n                    return object\n\n                if hasattr(object, attr):\n                    return getattr(object, attr)\n            except Http404:\n                return None\n            except AttributeError:\n                return None\n\n        return None\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Append project and module to the template context.\"\"\"\n        if 'project' not in kwargs:\n            kwargs['project'] = self.project\n        if 'module' not in kwargs:\n            kwargs['module'] = self.module\n        return super().get_context_data(**kwargs)\n\n\nclass DisplayProjectOrModuleMixin(generic.base.ContextMixin):\n\n    @cached_property\n    def url_name(self):\n        return resolve(self.request.path_info).url_name\n\n    @cached_property\n    def extends(self):\n        if self.url_name == 'module-detail':\n            return 'a4modules/module_detail.html'\n        return 'a4projects/project_detail.html'\n\n    @cached_property\n    def initial_slide(self):\n        initial_slide = self.request.GET.get('initialSlide')\n        if initial_slide:\n            initial_slide = ''.join(i for i in initial_slide if i.isdigit())\n            if initial_slide:\n                return int(initial_slide)\n        elif self.project.get_current_participation_date():\n            return self.project.get_current_participation_date()\n        return 0\n\n    def get_current_event(self):\n        idx = self.initial_slide\n        return self.project.get_current_event(idx)\n\n    def get_current_modules(self):\n        idx = self.initial_slide\n        return self.project.get_current_modules(idx)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['url_name'] = self.url_name\n        context['extends'] = self.extends\n        if not self.url_name == 'module-detail':\n            context['event'] = self.get_current_event()\n            context['modules'] = self.get_current_modules()\n            context['initial_slide'] = self.initial_slide\n        return context\n\n\nclass ProjectModuleDispatchMixin(generic.DetailView):\n\n    @cached_property\n    def project(self):\n        return self.get_object()\n\n    @cached_property\n    def module(self):\n        if (self.project.published_modules.count()\n                == 1 and not self.project.events):\n            return self.project.published_modules.first()\n        elif len(self.get_current_modules()) == 1:\n            return self.get_current_modules()[0]\n\n    def dispatch(self, request, *args, **kwargs):\n        kwargs['project'] = self.project\n        kwargs['module'] = self.module\n\n        return self._view_by_phase()(request, *args, **kwargs)\n\n    def _view_by_phase(self):\n        if self.module and self.module.last_active_phase:\n            return self.module.last_active_phase.view.as_view()\n        elif self.module and self.module.future_phases:\n            return self.module.future_phases.first().view.as_view()\n        else:\n            return super().dispatch\n", "path": "adhocracy4/projects/mixins.py"}, {"content": "from django.http import Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.shortcuts import render_to_response\nfrom django.views import generic\n\nfrom adhocracy4.dashboard import mixins as dashboard_mixins\nfrom adhocracy4.projects.mixins import ProjectMixin\nfrom adhocracy4.rules import mixins as rules_mixins\n\nfrom . import models\n\n\nclass PollDetailView(ProjectMixin,\n                     rules_mixins.PermissionRequiredMixin,\n                     generic.DetailView):\n    model = models.Poll\n    permission_required = 'a4polls.view_poll'\n\n    def get(self, request, *args, **kwargs):\n        try:\n            self.object = self.get_object()\n            context = self.get_context_data(object=self.object)\n            return self.render_to_response(context)\n\n        except Http404:\n            self.object = None\n            context = self.get_context_data(object=None, request=self.request,)\n            return render_to_response(\n                'a4polls/poll_404.html',\n                context=context,\n                status=404\n            )\n\n    def get_object(self):\n        return get_object_or_404(models.Poll, module=self.module)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        if self.object is not None:\n            context['question_list'] = \\\n                self.object.questions.annotate_vote_count().all()\n        return context\n\n    def get_permission_object(self):\n        return self.module\n\n    @property\n    def phase(self):\n        try:\n            weight = self.request.GET.get('phase')\n            phase = self.project.phases.filter(weight=weight).first()\n        except ValueError:\n            phase = None\n\n        if phase:\n            return phase\n        else:\n            return self.project.last_active_phase\n\n\nclass PollDashboardView(ProjectMixin,\n                        dashboard_mixins.DashboardBaseMixin,\n                        dashboard_mixins.DashboardComponentMixin,\n                        generic.TemplateView):\n    template_name = 'a4polls/poll_dashboard.html'\n    permission_required = 'a4projects.change_project'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['poll'] = self.get_or_create_poll()\n        return context\n\n    def get_or_create_poll(self):\n        try:\n            obj = models.Poll.objects.get(module=self.module)\n        except models.Poll.DoesNotExist:\n            obj = models.Poll(module=self.module,\n                              creator=self.request.user)\n            obj.save()\n        return obj\n\n    def get_permission_object(self):\n        return self.project\n", "path": "adhocracy4/polls/views.py"}], "after_files": [{"content": "from django.http import Http404\nfrom django.http.response import HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404\nfrom django.urls import resolve\nfrom django.utils.functional import cached_property\nfrom django.views import generic\n\nfrom adhocracy4.modules.models import Module\nfrom adhocracy4.projects.models import Project\n\n\nclass PhaseDispatchMixin(generic.DetailView):\n\n    @cached_property\n    def project(self):\n        return self.get_object()\n\n    @cached_property\n    def module(self):\n        return self.project.last_active_module\n\n    def dispatch(self, request, *args, **kwargs):\n        # Choose the appropriate view for the current active phase.\n        kwargs['project'] = self.project\n        kwargs['module'] = self.module\n\n        return self._view_by_phase()(request, *args, **kwargs)\n\n    def _view_by_phase(self):\n        \"\"\"\n        Choose the appropriate view for the current active phase.\n        \"\"\"\n        if self.module and self.module.last_active_phase:\n            return self.module.last_active_phase.view.as_view()\n        else:\n            return super().dispatch\n\n\nclass ModuleDispatchMixin(PhaseDispatchMixin):\n\n    @cached_property\n    def project(self):\n        return self.module.project\n\n    @cached_property\n    def module(self):\n        return self.get_object()\n\n    def dispatch(self, request, *args, **kwargs):\n        # Redirect to the project detail page if the module is shown there\n        if self.module == self.project.last_active_module:\n            return HttpResponseRedirect(self.project.get_absolute_url())\n\n        return super().dispatch(request, *args, **kwargs)\n\n\nclass ProjectMixin(generic.base.ContextMixin):\n    \"\"\"Add project and module attributes to the view and the template context.\n\n    This is a counterpart to the Phase- / ModuleDispatcher logic.\n\n    To consider the object context from get_object() set the\n    get_context_from_object attribute. Enable this only if get_object() does\n    not access the project and module properties.\n    \"\"\"\n\n    project_lookup_field = 'slug'\n    project_url_kwarg = 'project_slug'\n    module_lookup_field = 'slug'\n    module_url_kwarg = 'module_slug'\n    get_context_from_object = False\n\n    @property\n    def module(self):\n        \"\"\"Get the module from the current object, kwargs or url.\"\"\"\n        if self.get_context_from_object:\n            return self._get_object(Module, 'module')\n\n        if 'module' in self.kwargs \\\n                and isinstance(self.kwargs['module'], Module):\n            return self.kwargs['module']\n\n        if self.module_url_kwarg and self.module_url_kwarg in self.kwargs:\n            lookup = {\n                self.module_lookup_field: self.kwargs[self.module_url_kwarg]\n            }\n            return get_object_or_404(Module, **lookup)\n\n    @property\n    def project(self):\n        \"\"\"Get the project from the module, kwargs, url or current object.\"\"\"\n        if self.module:\n            return self.module.project\n\n        if self.get_context_from_object:\n            return self._get_object(Project, 'project')\n\n        if 'project' in self.kwargs \\\n                and isinstance(self.kwargs['project'], Project):\n            return self.kwargs['project']\n\n        if self.project_url_kwarg and self.project_url_kwarg in self.kwargs:\n            lookup = {\n                self.project_lookup_field: self.kwargs[self.project_url_kwarg]\n            }\n            return get_object_or_404(Project, **lookup)\n\n    def _get_object(self, cls, attr):\n        # CreateView supplies a defect get_object method and has to be excluded\n        if hasattr(self, 'get_object') \\\n                and not isinstance(self, generic.CreateView):\n            try:\n                object = self.get_object()\n                if isinstance(object, cls):\n                    return object\n\n                if hasattr(object, attr):\n                    return getattr(object, attr)\n            except Http404:\n                return None\n            except AttributeError:\n                return None\n\n        return None\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Append project and module to the template context.\"\"\"\n        if 'project' not in kwargs:\n            kwargs['project'] = self.project\n        if 'module' not in kwargs:\n            kwargs['module'] = self.module\n        return super().get_context_data(**kwargs)\n\n\nclass DisplayProjectOrModuleMixin(generic.base.ContextMixin):\n    \"\"\"Use the appropriate project or module view with timeline/cluster logic.\n\n    On platforms with multiple module projects, this should be used\n    with the phase view to display the module instead of the project\n    detail where appropriate. To do that, the template should extend\n    'extends' instead of a specific base template.\n    This mixin also makes sure the project view is shown with the\n    appropriate timeline tile activated.\n    \"\"\"\n\n    @cached_property\n    def url_name(self):\n        return resolve(self.request.path_info).url_name\n\n    @cached_property\n    def extends(self):\n        if self.url_name == 'module-detail':\n            return 'a4modules/module_detail.html'\n        return 'a4projects/project_detail.html'\n\n    @cached_property\n    def initial_slide(self):\n        initial_slide = self.request.GET.get('initialSlide')\n        if initial_slide:\n            initial_slide = ''.join(i for i in initial_slide if i.isdigit())\n            if initial_slide:\n                return int(initial_slide)\n        elif self.project.get_current_participation_date():\n            return self.project.get_current_participation_date()\n        return 0\n\n    def get_current_event(self):\n        idx = self.initial_slide\n        return self.project.get_current_event(idx)\n\n    def get_current_modules(self):\n        idx = self.initial_slide\n        return self.project.get_current_modules(idx)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['url_name'] = self.url_name\n        context['extends'] = self.extends\n        if not self.url_name == 'module-detail':\n            context['event'] = self.get_current_event()\n            context['modules'] = self.get_current_modules()\n            context['initial_slide'] = self.initial_slide\n        return context\n\n\nclass ProjectModuleDispatchMixin(generic.DetailView):\n\n    @cached_property\n    def project(self):\n        return self.get_object()\n\n    @cached_property\n    def module(self):\n        if (self.project.published_modules.count()\n                == 1 and not self.project.events):\n            return self.project.published_modules.first()\n        elif len(self.get_current_modules()) == 1:\n            return self.get_current_modules()[0]\n\n    def dispatch(self, request, *args, **kwargs):\n        kwargs['project'] = self.project\n        kwargs['module'] = self.module\n\n        return self._view_by_phase()(request, *args, **kwargs)\n\n    def _view_by_phase(self):\n        if self.module and self.module.last_active_phase:\n            return self.module.last_active_phase.view.as_view()\n        elif self.module and self.module.future_phases:\n            return self.module.future_phases.first().view.as_view()\n        else:\n            return super().dispatch\n", "path": "adhocracy4/projects/mixins.py"}, {"content": "from django.http import Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.shortcuts import render_to_response\nfrom django.views import generic\n\nfrom adhocracy4.dashboard import mixins as dashboard_mixins\nfrom adhocracy4.projects.mixins import DisplayProjectOrModuleMixin\nfrom adhocracy4.projects.mixins import ProjectMixin\nfrom adhocracy4.rules import mixins as rules_mixins\n\nfrom . import models\n\n\nclass PollDetailView(ProjectMixin,\n                     rules_mixins.PermissionRequiredMixin,\n                     generic.DetailView,\n                     DisplayProjectOrModuleMixin):\n    model = models.Poll\n    permission_required = 'a4polls.view_poll'\n\n    def get(self, request, *args, **kwargs):\n        try:\n            self.object = self.get_object()\n            context = self.get_context_data(object=self.object)\n            return self.render_to_response(context)\n\n        except Http404:\n            self.object = None\n            context = self.get_context_data(object=None, request=self.request,)\n            return render_to_response(\n                'a4polls/poll_404.html',\n                context=context,\n                status=404\n            )\n\n    def get_object(self):\n        return get_object_or_404(models.Poll, module=self.module)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        if self.object is not None:\n            context['question_list'] = \\\n                self.object.questions.annotate_vote_count().all()\n        return context\n\n    def get_permission_object(self):\n        return self.module\n\n    @property\n    def phase(self):\n        try:\n            weight = self.request.GET.get('phase')\n            phase = self.project.phases.filter(weight=weight).first()\n        except ValueError:\n            phase = None\n\n        if phase:\n            return phase\n        else:\n            return self.project.last_active_phase\n\n\nclass PollDashboardView(ProjectMixin,\n                        dashboard_mixins.DashboardBaseMixin,\n                        dashboard_mixins.DashboardComponentMixin,\n                        generic.TemplateView):\n    template_name = 'a4polls/poll_dashboard.html'\n    permission_required = 'a4projects.change_project'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['poll'] = self.get_or_create_poll()\n        return context\n\n    def get_or_create_poll(self):\n        try:\n            obj = models.Poll.objects.get(module=self.module)\n        except models.Poll.DoesNotExist:\n            obj = models.Poll(module=self.module,\n                              creator=self.request.user)\n            obj.save()\n        return obj\n\n    def get_permission_object(self):\n        return self.project\n", "path": "adhocracy4/polls/views.py"}]}