Repository: comic/grand-challenge.org

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. 




Discussion:
This would make the `/challenges/` page so so much simpler, there are a lot of hacks especially around the pagination to get this to work with two querysets of different types. Downside is that there would not be a way to search all the challenges together.
Hm, that's true, although we might be able to add a full text search that looks at fields in both models. Not sure how easy that would be and how efficient, but I'm sure it's possible. 

Resolving patch:
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 @@ def get_form_kwargs(self, *args, **kwargs):
         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):


Test patch:
diff --git a/app/tests/challenges_tests/test_views.py b/app/tests/challenges_tests/test_views.py
--- a/app/tests/challenges_tests/test_views.py
+++ b/app/tests/challenges_tests/test_views.py
@@ -11,7 +11,9 @@ def test_external_challenge_buttons(client):
     create_url = reverse("challenges:external-create")
     list_url = reverse("challenges:external-list")
 
-    response = get_view_for_user(client=client, viewname="challenges:list")
+    response = get_view_for_user(
+        client=client, viewname="challenges:combined-list"
+    )
 
     assert create_url not in response.rendered_content
     assert list_url not in response.rendered_content
@@ -19,7 +21,7 @@ def test_external_challenge_buttons(client):
     user = UserFactory()
 
     response = get_view_for_user(
-        client=client, viewname="challenges:list", user=user
+        client=client, viewname="challenges:combined-list", user=user
     )
 
     assert create_url not in response.rendered_content
@@ -28,7 +30,7 @@ def test_external_challenge_buttons(client):
     assign_perm("challenges.change_externalchallenge", user)
 
     response = get_view_for_user(
-        client=client, viewname="challenges:list", user=user
+        client=client, viewname="challenges:combined-list", user=user
     )
 
     assert create_url in response.rendered_content