We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Seperate external challenges from internal challenges
It would be nice to seperate internal from external challenges a little more in the list view. 

The simplest (?) way would be to only include internal challenges in the list view and add a seperate list view for external challenges. This seperate list view could be linked to through a button on the normal challenge list view. 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `app/grandchallenge/challenges/urls.py`
Content:
```
1 from django.urls import path
2 
3 from grandchallenge.challenges.views import (
4     ChallengeCreate,
5     ChallengeList,
6     ExternalChallengeCreate,
7     ExternalChallengeList,
8     ExternalChallengeUpdate,
9     UsersChallengeList,
10 )
11 
12 app_name = "challenges"
13 
14 urlpatterns = [
15     path("", ChallengeList.as_view(), name="list"),
16     path("my-challenges/", UsersChallengeList.as_view(), name="users-list"),
17     path("create/", ChallengeCreate.as_view(), name="create"),
18     path("external/", ExternalChallengeList.as_view(), name="external-list"),
19     path(
20         "external/create/",
21         ExternalChallengeCreate.as_view(),
22         name="external-create",
23     ),
24     path(
25         "external/<slug:short_name>/update/",
26         ExternalChallengeUpdate.as_view(),
27         name="external-update",
28     ),
29 ]
30
```

Path: `app/grandchallenge/challenges/views.py`
Content:
```
1 from django.contrib.auth.mixins import PermissionRequiredMixin
2 from django.contrib.messages.views import SuccessMessageMixin
3 from django.core.paginator import EmptyPage, Paginator
4 from django.db.models import Q
5 from django.utils.html import format_html
6 from django.views.generic import (
7     CreateView,
8     ListView,
9     TemplateView,
10     UpdateView,
11 )
12 from guardian.mixins import (
13     LoginRequiredMixin,
14     PermissionRequiredMixin as ObjectPermissionRequiredMixin,
15 )
16 
17 from grandchallenge.challenges.filters import ChallengeFilter
18 from grandchallenge.challenges.forms import (
19     ChallengeCreateForm,
20     ChallengeUpdateForm,
21     ExternalChallengeUpdateForm,
22 )
23 from grandchallenge.challenges.models import (
24     Challenge,
25     ExternalChallenge,
26 )
27 from grandchallenge.core.templatetags.random_encode import random_encode
28 from grandchallenge.datatables.views import Column, PaginatedTableListView
29 from grandchallenge.subdomains.mixins import ChallengeSubdomainObjectMixin
30 from grandchallenge.subdomains.utils import reverse, reverse_lazy
31 
32 
33 class ChallengeCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):
34     model = Challenge
35     form_class = ChallengeCreateForm
36     success_message = "Challenge successfully created"
37 
38     def form_valid(self, form):
39         form.instance.creator = self.request.user
40         return super().form_valid(form)
41 
42     def get_form_kwargs(self, *args, **kwargs):
43         form_kwargs = super().get_form_kwargs(*args, **kwargs)
44         form_kwargs.update({"creator": self.request.user})
45         return form_kwargs
46 
47 
48 class ChallengeList(TemplateView):
49     paginate_by = 40
50     template_name = "challenges/challenge_list.html"
51 
52     @property
53     def _current_page(self):
54         return int(self.request.GET.get("page", 1))
55 
56     @property
57     def _filters_applied(self):
58         return any(k for k in self.request.GET if k.lower() != "page")
59 
60     def _get_page(self):
61         int_qs = (
62             Challenge.objects.filter(hidden=False)
63             .prefetch_related("phase_set", "publications")
64             .order_by("-created")
65         )
66         self.int_filter = ChallengeFilter(self.request.GET, int_qs,)
67         ext_qs = (
68             ExternalChallenge.objects.filter(hidden=False)
69             .prefetch_related("publications")
70             .order_by("-created")
71         )
72         self.ext_filter = ChallengeFilter(self.request.GET, ext_qs,)
73 
74         total_count = int_qs.count() + ext_qs.count()
75 
76         int_paginator = Paginator(self.int_filter.qs, self.paginate_by // 2)
77         ext_paginator = Paginator(self.ext_filter.qs, self.paginate_by // 2)
78 
79         num_pages = max(int_paginator.num_pages, ext_paginator.num_pages)
80         num_results = int_paginator.count + ext_paginator.count
81 
82         try:
83             int_page = int_paginator.page(self._current_page)
84         except EmptyPage:
85             int_page = []
86 
87         try:
88             ext_page = ext_paginator.page(self._current_page)
89         except EmptyPage:
90             ext_page = []
91 
92         return [*int_page, *ext_page], num_pages, num_results, total_count
93 
94     def get_context_data(self, *, object_list=None, **kwargs):
95         context = super().get_context_data(**kwargs)
96 
97         page_obj, num_pages, num_results, total_count = self._get_page()
98 
99         context.update(
100             {
101                 "filter": self.int_filter,
102                 "filters_applied": self._filters_applied,
103                 "page_obj": page_obj,
104                 "num_pages": num_pages,
105                 "num_results": num_results,
106                 "total_count": total_count,
107                 "current_page": self._current_page,
108                 "next_page": self._current_page + 1,
109                 "previous_page": self._current_page - 1,
110                 "jumbotron_title": "Challenges",
111                 "jumbotron_description": format_html(
112                     (
113                         "Here is an overview of all challenges that have been "
114                         "organised within the area of medical image analysis "
115                         "that we are aware of. Please <a href='{}'>contact "
116                         "us</a> if you want to advertise your challenge or "
117                         "know of any study that would fit in this overview."
118                     ),
119                     random_encode("mailto:support@grand-challenge.org"),
120                 ),
121             }
122         )
123 
124         return context
125 
126 
127 class UsersChallengeList(LoginRequiredMixin, PaginatedTableListView):
128     model = Challenge
129     template_name = "challenges/challenge_users_list.html"
130     row_template = "challenges/challenge_users_row.html"
131     search_fields = [
132         "title",
133         "short_name",
134         "description",
135     ]
136     columns = [
137         Column(title="Name", sort_field="short_name"),
138         Column(title="Created", sort_field="created"),
139         Column(title="Admins", sort_field="created"),
140         Column(title="Description", sort_field="description"),
141         Column(title="Automated Evaluation", sort_field="use_evaluation"),
142     ]
143     default_sort_column = 1
144 
145     def get_queryset(self):
146         queryset = (
147             super()
148             .get_queryset()
149             .prefetch_related(
150                 "admins_group__user_set__user_profile",
151                 "admins_group__user_set__verification",
152             )
153         )
154         if not self.request.user.is_superuser:
155             queryset = queryset.filter(
156                 Q(participants_group__in=self.request.user.groups.all())
157                 | Q(admins_group__in=self.request.user.groups.all())
158             )
159         return queryset
160 
161 
162 class ChallengeUpdate(
163     LoginRequiredMixin,
164     ObjectPermissionRequiredMixin,
165     SuccessMessageMixin,
166     ChallengeSubdomainObjectMixin,
167     UpdateView,
168 ):
169     model = Challenge
170     slug_field = "short_name__iexact"
171     slug_url_kwarg = "challenge_short_name"
172     form_class = ChallengeUpdateForm
173     success_message = "Challenge successfully updated"
174     template_name_suffix = "_update"
175     permission_required = "change_challenge"
176     raise_exception = True
177     login_url = reverse_lazy("account_login")
178 
179     def get_success_url(self):
180         return reverse(
181             "update",
182             kwargs={"challenge_short_name": self.request.challenge.short_name},
183         )
184 
185 
186 class ExternalChallengeCreate(
187     LoginRequiredMixin,
188     PermissionRequiredMixin,
189     SuccessMessageMixin,
190     CreateView,
191 ):
192     model = ExternalChallenge
193     form_class = ExternalChallengeUpdateForm
194     success_message = (
195         "Your challenge has been successfully submitted. "
196         "An admin will review your challenge before it is published."
197     )
198     raise_exception = True
199     permission_required = "challenges.add_externalchallenge"
200 
201     def form_valid(self, form):
202         form.instance.creator = self.request.user
203         return super().form_valid(form)
204 
205     def get_success_url(self):
206         return reverse("challenges:list")
207 
208 
209 class ExternalChallengeUpdate(
210     LoginRequiredMixin,
211     PermissionRequiredMixin,
212     SuccessMessageMixin,
213     UpdateView,
214 ):
215     model = ExternalChallenge
216     slug_field = "short_name__iexact"
217     slug_url_kwarg = "short_name"
218     form_class = ExternalChallengeUpdateForm
219     template_name_suffix = "_update"
220     success_message = "Challenge updated"
221     raise_exception = True
222     permission_required = "challenges.change_externalchallenge"
223 
224     def get_success_url(self):
225         return reverse("challenges:list")
226 
227 
228 class ExternalChallengeList(
229     LoginRequiredMixin, PermissionRequiredMixin, ListView
230 ):
231     model = ExternalChallenge
232     raise_exception = True
233     permission_required = "challenges.view_externalchallenge"
234
```
--- 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/app/grandchallenge/challenges/urls.py b/app/grandchallenge/challenges/urls.py
--- a/app/grandchallenge/challenges/urls.py
+++ b/app/grandchallenge/challenges/urls.py
@@ -3,6 +3,7 @@
 from grandchallenge.challenges.views import (
     ChallengeCreate,
     ChallengeList,
+    CombinedChallengeList,
     ExternalChallengeCreate,
     ExternalChallengeList,
     ExternalChallengeUpdate,
@@ -13,6 +14,11 @@
 
 urlpatterns = [
     path("", ChallengeList.as_view(), name="list"),
+    path(
+        "all-challenges/",
+        CombinedChallengeList.as_view(),
+        name="combined-list",
+    ),
     path("my-challenges/", UsersChallengeList.as_view(), name="users-list"),
     path("create/", ChallengeCreate.as_view(), name="create"),
     path("external/", ExternalChallengeList.as_view(), name="external-list"),
diff --git a/app/grandchallenge/challenges/views.py b/app/grandchallenge/challenges/views.py
--- a/app/grandchallenge/challenges/views.py
+++ b/app/grandchallenge/challenges/views.py
@@ -24,6 +24,7 @@
     Challenge,
     ExternalChallenge,
 )
+from grandchallenge.core.filters import FilterMixin
 from grandchallenge.core.templatetags.random_encode import random_encode
 from grandchallenge.datatables.views import Column, PaginatedTableListView
 from grandchallenge.subdomains.mixins import ChallengeSubdomainObjectMixin
@@ -45,9 +46,44 @@
         return form_kwargs
 
 
-class ChallengeList(TemplateView):
+class ChallengeList(FilterMixin, ListView):
+    model = Challenge
+    ordering = "-created"
+    filter_class = ChallengeFilter
+    paginate_by = 40
+
+    def get_queryset(self):
+        return (
+            super()
+            .get_queryset()
+            .filter(hidden=False)
+            .prefetch_related("phase_set", "publications")
+            .order_by("-created")
+        )
+
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
+
+        context.update(
+            {
+                "jumbotron_title": "Challenges",
+                "jumbotron_description": format_html(
+                    (
+                        "Here is an overview over the medical image analysis"
+                        " challenges that have been hosted on Grand Challenge."
+                        "<br>Please <a href='{}'>contact us</a> if you would like "
+                        "to host your own challenge."
+                    ),
+                    random_encode("mailto:support@grand-challenge.org"),
+                ),
+            }
+        )
+        return context
+
+
+class CombinedChallengeList(TemplateView):
     paginate_by = 40
-    template_name = "challenges/challenge_list.html"
+    template_name = "challenges/combined_challenge_list.html"
 
     @property
     def _current_page(self):

{"golden_diff": "diff --git a/app/grandchallenge/challenges/urls.py b/app/grandchallenge/challenges/urls.py\n--- a/app/grandchallenge/challenges/urls.py\n+++ b/app/grandchallenge/challenges/urls.py\n@@ -3,6 +3,7 @@\n from grandchallenge.challenges.views import (\n     ChallengeCreate,\n     ChallengeList,\n+    CombinedChallengeList,\n     ExternalChallengeCreate,\n     ExternalChallengeList,\n     ExternalChallengeUpdate,\n@@ -13,6 +14,11 @@\n \n urlpatterns = [\n     path(\"\", ChallengeList.as_view(), name=\"list\"),\n+    path(\n+        \"all-challenges/\",\n+        CombinedChallengeList.as_view(),\n+        name=\"combined-list\",\n+    ),\n     path(\"my-challenges/\", UsersChallengeList.as_view(), name=\"users-list\"),\n     path(\"create/\", ChallengeCreate.as_view(), name=\"create\"),\n     path(\"external/\", ExternalChallengeList.as_view(), name=\"external-list\"),\ndiff --git a/app/grandchallenge/challenges/views.py b/app/grandchallenge/challenges/views.py\n--- a/app/grandchallenge/challenges/views.py\n+++ b/app/grandchallenge/challenges/views.py\n@@ -24,6 +24,7 @@\n     Challenge,\n     ExternalChallenge,\n )\n+from grandchallenge.core.filters import FilterMixin\n from grandchallenge.core.templatetags.random_encode import random_encode\n from grandchallenge.datatables.views import Column, PaginatedTableListView\n from grandchallenge.subdomains.mixins import ChallengeSubdomainObjectMixin\n@@ -45,9 +46,44 @@\n         return form_kwargs\n \n \n-class ChallengeList(TemplateView):\n+class ChallengeList(FilterMixin, ListView):\n+    model = Challenge\n+    ordering = \"-created\"\n+    filter_class = ChallengeFilter\n+    paginate_by = 40\n+\n+    def get_queryset(self):\n+        return (\n+            super()\n+            .get_queryset()\n+            .filter(hidden=False)\n+            .prefetch_related(\"phase_set\", \"publications\")\n+            .order_by(\"-created\")\n+        )\n+\n+    def get_context_data(self, *args, **kwargs):\n+        context = super().get_context_data(*args, **kwargs)\n+\n+        context.update(\n+            {\n+                \"jumbotron_title\": \"Challenges\",\n+                \"jumbotron_description\": format_html(\n+                    (\n+                        \"Here is an overview over the medical image analysis\"\n+                        \" challenges that have been hosted on Grand Challenge.\"\n+                        \"<br>Please <a href='{}'>contact us</a> if you would like \"\n+                        \"to host your own challenge.\"\n+                    ),\n+                    random_encode(\"mailto:support@grand-challenge.org\"),\n+                ),\n+            }\n+        )\n+        return context\n+\n+\n+class CombinedChallengeList(TemplateView):\n     paginate_by = 40\n-    template_name = \"challenges/challenge_list.html\"\n+    template_name = \"challenges/combined_challenge_list.html\"\n \n     @property\n     def _current_page(self):\n", "issue": "Seperate external challenges from internal challenges\nIt would be nice to seperate internal from external challenges a little more in the list view. \r\n\r\nThe simplest (?) way would be to only include internal challenges in the list view and add a seperate list view for external challenges. This seperate list view could be linked to through a button on the normal challenge list view. \r\n\r\n\n", "before_files": [{"content": "from django.urls import path\n\nfrom grandchallenge.challenges.views import (\n    ChallengeCreate,\n    ChallengeList,\n    ExternalChallengeCreate,\n    ExternalChallengeList,\n    ExternalChallengeUpdate,\n    UsersChallengeList,\n)\n\napp_name = \"challenges\"\n\nurlpatterns = [\n    path(\"\", ChallengeList.as_view(), name=\"list\"),\n    path(\"my-challenges/\", UsersChallengeList.as_view(), name=\"users-list\"),\n    path(\"create/\", ChallengeCreate.as_view(), name=\"create\"),\n    path(\"external/\", ExternalChallengeList.as_view(), name=\"external-list\"),\n    path(\n        \"external/create/\",\n        ExternalChallengeCreate.as_view(),\n        name=\"external-create\",\n    ),\n    path(\n        \"external/<slug:short_name>/update/\",\n        ExternalChallengeUpdate.as_view(),\n        name=\"external-update\",\n    ),\n]\n", "path": "app/grandchallenge/challenges/urls.py"}, {"content": "from django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.contrib.messages.views import SuccessMessageMixin\nfrom django.core.paginator import EmptyPage, Paginator\nfrom django.db.models import Q\nfrom django.utils.html import format_html\nfrom django.views.generic import (\n    CreateView,\n    ListView,\n    TemplateView,\n    UpdateView,\n)\nfrom guardian.mixins import (\n    LoginRequiredMixin,\n    PermissionRequiredMixin as ObjectPermissionRequiredMixin,\n)\n\nfrom grandchallenge.challenges.filters import ChallengeFilter\nfrom grandchallenge.challenges.forms import (\n    ChallengeCreateForm,\n    ChallengeUpdateForm,\n    ExternalChallengeUpdateForm,\n)\nfrom grandchallenge.challenges.models import (\n    Challenge,\n    ExternalChallenge,\n)\nfrom grandchallenge.core.templatetags.random_encode import random_encode\nfrom grandchallenge.datatables.views import Column, PaginatedTableListView\nfrom grandchallenge.subdomains.mixins import ChallengeSubdomainObjectMixin\nfrom grandchallenge.subdomains.utils import reverse, reverse_lazy\n\n\nclass ChallengeCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):\n    model = Challenge\n    form_class = ChallengeCreateForm\n    success_message = \"Challenge successfully created\"\n\n    def form_valid(self, form):\n        form.instance.creator = self.request.user\n        return super().form_valid(form)\n\n    def get_form_kwargs(self, *args, **kwargs):\n        form_kwargs = super().get_form_kwargs(*args, **kwargs)\n        form_kwargs.update({\"creator\": self.request.user})\n        return form_kwargs\n\n\nclass ChallengeList(TemplateView):\n    paginate_by = 40\n    template_name = \"challenges/challenge_list.html\"\n\n    @property\n    def _current_page(self):\n        return int(self.request.GET.get(\"page\", 1))\n\n    @property\n    def _filters_applied(self):\n        return any(k for k in self.request.GET if k.lower() != \"page\")\n\n    def _get_page(self):\n        int_qs = (\n            Challenge.objects.filter(hidden=False)\n            .prefetch_related(\"phase_set\", \"publications\")\n            .order_by(\"-created\")\n        )\n        self.int_filter = ChallengeFilter(self.request.GET, int_qs,)\n        ext_qs = (\n            ExternalChallenge.objects.filter(hidden=False)\n            .prefetch_related(\"publications\")\n            .order_by(\"-created\")\n        )\n        self.ext_filter = ChallengeFilter(self.request.GET, ext_qs,)\n\n        total_count = int_qs.count() + ext_qs.count()\n\n        int_paginator = Paginator(self.int_filter.qs, self.paginate_by // 2)\n        ext_paginator = Paginator(self.ext_filter.qs, self.paginate_by // 2)\n\n        num_pages = max(int_paginator.num_pages, ext_paginator.num_pages)\n        num_results = int_paginator.count + ext_paginator.count\n\n        try:\n            int_page = int_paginator.page(self._current_page)\n        except EmptyPage:\n            int_page = []\n\n        try:\n            ext_page = ext_paginator.page(self._current_page)\n        except EmptyPage:\n            ext_page = []\n\n        return [*int_page, *ext_page], num_pages, num_results, total_count\n\n    def get_context_data(self, *, object_list=None, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        page_obj, num_pages, num_results, total_count = self._get_page()\n\n        context.update(\n            {\n                \"filter\": self.int_filter,\n                \"filters_applied\": self._filters_applied,\n                \"page_obj\": page_obj,\n                \"num_pages\": num_pages,\n                \"num_results\": num_results,\n                \"total_count\": total_count,\n                \"current_page\": self._current_page,\n                \"next_page\": self._current_page + 1,\n                \"previous_page\": self._current_page - 1,\n                \"jumbotron_title\": \"Challenges\",\n                \"jumbotron_description\": format_html(\n                    (\n                        \"Here is an overview of all challenges that have been \"\n                        \"organised within the area of medical image analysis \"\n                        \"that we are aware of. Please <a href='{}'>contact \"\n                        \"us</a> if you want to advertise your challenge or \"\n                        \"know of any study that would fit in this overview.\"\n                    ),\n                    random_encode(\"mailto:support@grand-challenge.org\"),\n                ),\n            }\n        )\n\n        return context\n\n\nclass UsersChallengeList(LoginRequiredMixin, PaginatedTableListView):\n    model = Challenge\n    template_name = \"challenges/challenge_users_list.html\"\n    row_template = \"challenges/challenge_users_row.html\"\n    search_fields = [\n        \"title\",\n        \"short_name\",\n        \"description\",\n    ]\n    columns = [\n        Column(title=\"Name\", sort_field=\"short_name\"),\n        Column(title=\"Created\", sort_field=\"created\"),\n        Column(title=\"Admins\", sort_field=\"created\"),\n        Column(title=\"Description\", sort_field=\"description\"),\n        Column(title=\"Automated Evaluation\", sort_field=\"use_evaluation\"),\n    ]\n    default_sort_column = 1\n\n    def get_queryset(self):\n        queryset = (\n            super()\n            .get_queryset()\n            .prefetch_related(\n                \"admins_group__user_set__user_profile\",\n                \"admins_group__user_set__verification\",\n            )\n        )\n        if not self.request.user.is_superuser:\n            queryset = queryset.filter(\n                Q(participants_group__in=self.request.user.groups.all())\n                | Q(admins_group__in=self.request.user.groups.all())\n            )\n        return queryset\n\n\nclass ChallengeUpdate(\n    LoginRequiredMixin,\n    ObjectPermissionRequiredMixin,\n    SuccessMessageMixin,\n    ChallengeSubdomainObjectMixin,\n    UpdateView,\n):\n    model = Challenge\n    slug_field = \"short_name__iexact\"\n    slug_url_kwarg = \"challenge_short_name\"\n    form_class = ChallengeUpdateForm\n    success_message = \"Challenge successfully updated\"\n    template_name_suffix = \"_update\"\n    permission_required = \"change_challenge\"\n    raise_exception = True\n    login_url = reverse_lazy(\"account_login\")\n\n    def get_success_url(self):\n        return reverse(\n            \"update\",\n            kwargs={\"challenge_short_name\": self.request.challenge.short_name},\n        )\n\n\nclass ExternalChallengeCreate(\n    LoginRequiredMixin,\n    PermissionRequiredMixin,\n    SuccessMessageMixin,\n    CreateView,\n):\n    model = ExternalChallenge\n    form_class = ExternalChallengeUpdateForm\n    success_message = (\n        \"Your challenge has been successfully submitted. \"\n        \"An admin will review your challenge before it is published.\"\n    )\n    raise_exception = True\n    permission_required = \"challenges.add_externalchallenge\"\n\n    def form_valid(self, form):\n        form.instance.creator = self.request.user\n        return super().form_valid(form)\n\n    def get_success_url(self):\n        return reverse(\"challenges:list\")\n\n\nclass ExternalChallengeUpdate(\n    LoginRequiredMixin,\n    PermissionRequiredMixin,\n    SuccessMessageMixin,\n    UpdateView,\n):\n    model = ExternalChallenge\n    slug_field = \"short_name__iexact\"\n    slug_url_kwarg = \"short_name\"\n    form_class = ExternalChallengeUpdateForm\n    template_name_suffix = \"_update\"\n    success_message = \"Challenge updated\"\n    raise_exception = True\n    permission_required = \"challenges.change_externalchallenge\"\n\n    def get_success_url(self):\n        return reverse(\"challenges:list\")\n\n\nclass ExternalChallengeList(\n    LoginRequiredMixin, PermissionRequiredMixin, ListView\n):\n    model = ExternalChallenge\n    raise_exception = True\n    permission_required = \"challenges.view_externalchallenge\"\n", "path": "app/grandchallenge/challenges/views.py"}], "after_files": [{"content": "from django.urls import path\n\nfrom grandchallenge.challenges.views import (\n    ChallengeCreate,\n    ChallengeList,\n    CombinedChallengeList,\n    ExternalChallengeCreate,\n    ExternalChallengeList,\n    ExternalChallengeUpdate,\n    UsersChallengeList,\n)\n\napp_name = \"challenges\"\n\nurlpatterns = [\n    path(\"\", ChallengeList.as_view(), name=\"list\"),\n    path(\n        \"all-challenges/\",\n        CombinedChallengeList.as_view(),\n        name=\"combined-list\",\n    ),\n    path(\"my-challenges/\", UsersChallengeList.as_view(), name=\"users-list\"),\n    path(\"create/\", ChallengeCreate.as_view(), name=\"create\"),\n    path(\"external/\", ExternalChallengeList.as_view(), name=\"external-list\"),\n    path(\n        \"external/create/\",\n        ExternalChallengeCreate.as_view(),\n        name=\"external-create\",\n    ),\n    path(\n        \"external/<slug:short_name>/update/\",\n        ExternalChallengeUpdate.as_view(),\n        name=\"external-update\",\n    ),\n]\n", "path": "app/grandchallenge/challenges/urls.py"}, {"content": "from django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.contrib.messages.views import SuccessMessageMixin\nfrom django.core.paginator import EmptyPage, Paginator\nfrom django.db.models import Q\nfrom django.utils.html import format_html\nfrom django.views.generic import (\n    CreateView,\n    ListView,\n    TemplateView,\n    UpdateView,\n)\nfrom guardian.mixins import (\n    LoginRequiredMixin,\n    PermissionRequiredMixin as ObjectPermissionRequiredMixin,\n)\n\nfrom grandchallenge.challenges.filters import ChallengeFilter\nfrom grandchallenge.challenges.forms import (\n    ChallengeCreateForm,\n    ChallengeUpdateForm,\n    ExternalChallengeUpdateForm,\n)\nfrom grandchallenge.challenges.models import (\n    Challenge,\n    ExternalChallenge,\n)\nfrom grandchallenge.core.filters import FilterMixin\nfrom grandchallenge.core.templatetags.random_encode import random_encode\nfrom grandchallenge.datatables.views import Column, PaginatedTableListView\nfrom grandchallenge.subdomains.mixins import ChallengeSubdomainObjectMixin\nfrom grandchallenge.subdomains.utils import reverse, reverse_lazy\n\n\nclass ChallengeCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):\n    model = Challenge\n    form_class = ChallengeCreateForm\n    success_message = \"Challenge successfully created\"\n\n    def form_valid(self, form):\n        form.instance.creator = self.request.user\n        return super().form_valid(form)\n\n    def get_form_kwargs(self, *args, **kwargs):\n        form_kwargs = super().get_form_kwargs(*args, **kwargs)\n        form_kwargs.update({\"creator\": self.request.user})\n        return form_kwargs\n\n\nclass ChallengeList(FilterMixin, ListView):\n    model = Challenge\n    ordering = \"-created\"\n    filter_class = ChallengeFilter\n    paginate_by = 40\n\n    def get_queryset(self):\n        return (\n            super()\n            .get_queryset()\n            .filter(hidden=False)\n            .prefetch_related(\"phase_set\", \"publications\")\n            .order_by(\"-created\")\n        )\n\n    def get_context_data(self, *args, **kwargs):\n        context = super().get_context_data(*args, **kwargs)\n\n        context.update(\n            {\n                \"jumbotron_title\": \"Challenges\",\n                \"jumbotron_description\": format_html(\n                    (\n                        \"Here is an overview over the medical image analysis\"\n                        \" challenges that have been hosted on Grand Challenge.\"\n                        \"<br>Please <a href='{}'>contact us</a> if you would like \"\n                        \"to host your own challenge.\"\n                    ),\n                    random_encode(\"mailto:support@grand-challenge.org\"),\n                ),\n            }\n        )\n        return context\n\n\nclass CombinedChallengeList(TemplateView):\n    paginate_by = 40\n    template_name = \"challenges/combined_challenge_list.html\"\n\n    @property\n    def _current_page(self):\n        return int(self.request.GET.get(\"page\", 1))\n\n    @property\n    def _filters_applied(self):\n        return any(k for k in self.request.GET if k.lower() != \"page\")\n\n    def _get_page(self):\n        int_qs = (\n            Challenge.objects.filter(hidden=False)\n            .prefetch_related(\"phase_set\", \"publications\")\n            .order_by(\"-created\")\n        )\n        self.int_filter = ChallengeFilter(self.request.GET, int_qs,)\n        ext_qs = (\n            ExternalChallenge.objects.filter(hidden=False)\n            .prefetch_related(\"publications\")\n            .order_by(\"-created\")\n        )\n        self.ext_filter = ChallengeFilter(self.request.GET, ext_qs,)\n\n        total_count = int_qs.count() + ext_qs.count()\n\n        int_paginator = Paginator(self.int_filter.qs, self.paginate_by // 2)\n        ext_paginator = Paginator(self.ext_filter.qs, self.paginate_by // 2)\n\n        num_pages = max(int_paginator.num_pages, ext_paginator.num_pages)\n        num_results = int_paginator.count + ext_paginator.count\n\n        try:\n            int_page = int_paginator.page(self._current_page)\n        except EmptyPage:\n            int_page = []\n\n        try:\n            ext_page = ext_paginator.page(self._current_page)\n        except EmptyPage:\n            ext_page = []\n\n        return [*int_page, *ext_page], num_pages, num_results, total_count\n\n    def get_context_data(self, *, object_list=None, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        page_obj, num_pages, num_results, total_count = self._get_page()\n\n        context.update(\n            {\n                \"filter\": self.int_filter,\n                \"filters_applied\": self._filters_applied,\n                \"page_obj\": page_obj,\n                \"num_pages\": num_pages,\n                \"num_results\": num_results,\n                \"total_count\": total_count,\n                \"current_page\": self._current_page,\n                \"next_page\": self._current_page + 1,\n                \"previous_page\": self._current_page - 1,\n                \"jumbotron_title\": \"Challenges\",\n                \"jumbotron_description\": format_html(\n                    (\n                        \"Here is an overview of all challenges that have been \"\n                        \"organised within the area of medical image analysis \"\n                        \"that we are aware of. Please <a href='{}'>contact \"\n                        \"us</a> if you want to advertise your challenge or \"\n                        \"know of any study that would fit in this overview.\"\n                    ),\n                    random_encode(\"mailto:support@grand-challenge.org\"),\n                ),\n            }\n        )\n\n        return context\n\n\nclass UsersChallengeList(LoginRequiredMixin, PaginatedTableListView):\n    model = Challenge\n    template_name = \"challenges/challenge_users_list.html\"\n    row_template = \"challenges/challenge_users_row.html\"\n    search_fields = [\n        \"title\",\n        \"short_name\",\n        \"description\",\n    ]\n    columns = [\n        Column(title=\"Name\", sort_field=\"short_name\"),\n        Column(title=\"Created\", sort_field=\"created\"),\n        Column(title=\"Admins\", sort_field=\"created\"),\n        Column(title=\"Description\", sort_field=\"description\"),\n        Column(title=\"Automated Evaluation\", sort_field=\"use_evaluation\"),\n    ]\n    default_sort_column = 1\n\n    def get_queryset(self):\n        queryset = (\n            super()\n            .get_queryset()\n            .prefetch_related(\n                \"admins_group__user_set__user_profile\",\n                \"admins_group__user_set__verification\",\n            )\n        )\n        if not self.request.user.is_superuser:\n            queryset = queryset.filter(\n                Q(participants_group__in=self.request.user.groups.all())\n                | Q(admins_group__in=self.request.user.groups.all())\n            )\n        return queryset\n\n\nclass ChallengeUpdate(\n    LoginRequiredMixin,\n    ObjectPermissionRequiredMixin,\n    SuccessMessageMixin,\n    ChallengeSubdomainObjectMixin,\n    UpdateView,\n):\n    model = Challenge\n    slug_field = \"short_name__iexact\"\n    slug_url_kwarg = \"challenge_short_name\"\n    form_class = ChallengeUpdateForm\n    success_message = \"Challenge successfully updated\"\n    template_name_suffix = \"_update\"\n    permission_required = \"change_challenge\"\n    raise_exception = True\n    login_url = reverse_lazy(\"account_login\")\n\n    def get_success_url(self):\n        return reverse(\n            \"update\",\n            kwargs={\"challenge_short_name\": self.request.challenge.short_name},\n        )\n\n\nclass ExternalChallengeCreate(\n    LoginRequiredMixin,\n    PermissionRequiredMixin,\n    SuccessMessageMixin,\n    CreateView,\n):\n    model = ExternalChallenge\n    form_class = ExternalChallengeUpdateForm\n    success_message = (\n        \"Your challenge has been successfully submitted. \"\n        \"An admin will review your challenge before it is published.\"\n    )\n    raise_exception = True\n    permission_required = \"challenges.add_externalchallenge\"\n\n    def form_valid(self, form):\n        form.instance.creator = self.request.user\n        return super().form_valid(form)\n\n    def get_success_url(self):\n        return reverse(\"challenges:list\")\n\n\nclass ExternalChallengeUpdate(\n    LoginRequiredMixin,\n    PermissionRequiredMixin,\n    SuccessMessageMixin,\n    UpdateView,\n):\n    model = ExternalChallenge\n    slug_field = \"short_name__iexact\"\n    slug_url_kwarg = \"short_name\"\n    form_class = ExternalChallengeUpdateForm\n    template_name_suffix = \"_update\"\n    success_message = \"Challenge updated\"\n    raise_exception = True\n    permission_required = \"challenges.change_externalchallenge\"\n\n    def get_success_url(self):\n        return reverse(\"challenges:list\")\n\n\nclass ExternalChallengeList(\n    LoginRequiredMixin, PermissionRequiredMixin, ListView\n):\n    model = ExternalChallenge\n    raise_exception = True\n    permission_required = \"challenges.view_externalchallenge\"\n", "path": "app/grandchallenge/challenges/views.py"}]}