Repository: comic/grand-challenge.org

Issue:
Ability to remove reader studies
To avoid clutter, this is a something that Editors should be able to do.


Discussion:

Resolving patch:
diff --git a/app/grandchallenge/reader_studies/urls.py b/app/grandchallenge/reader_studies/urls.py
--- a/app/grandchallenge/reader_studies/urls.py
+++ b/app/grandchallenge/reader_studies/urls.py
@@ -6,6 +6,7 @@
     EditorsUpdate,
     QuestionUpdate,
     ReaderStudyCreate,
+    ReaderStudyDelete,
     ReaderStudyDetail,
     ReaderStudyList,
     ReaderStudyUpdate,
@@ -25,6 +26,7 @@
     ),
     path("<slug>/", ReaderStudyDetail.as_view(), name="detail"),
     path("<slug>/update/", ReaderStudyUpdate.as_view(), name="update"),
+    path("<slug>/delete/", ReaderStudyDelete.as_view(), name="delete"),
     path(
         "<slug>/images/add/",
         AddImagesToReaderStudy.as_view(),
diff --git a/app/grandchallenge/reader_studies/views.py b/app/grandchallenge/reader_studies/views.py
--- a/app/grandchallenge/reader_studies/views.py
+++ b/app/grandchallenge/reader_studies/views.py
@@ -11,8 +11,10 @@
 )
 from django.contrib.messages.views import SuccessMessageMixin
 from django.http import Http404, HttpResponse
+from django.urls import reverse
 from django.views.generic import (
     CreateView,
+    DeleteView,
     DetailView,
     FormView,
     ListView,
@@ -133,6 +135,24 @@ def get_form_kwargs(self):
         return kwargs
 
 
+class ReaderStudyDelete(
+    LoginRequiredMixin, ObjectPermissionRequiredMixin, DeleteView
+):
+    model = ReaderStudy
+    permission_required = (
+        f"{ReaderStudy._meta.app_label}.change_{ReaderStudy._meta.model_name}"
+    )
+    raise_exception = True
+    success_message = "Reader study was successfully deleted"
+
+    def get_success_url(self):
+        return reverse("reader-studies:list")
+
+    def delete(self, request, *args, **kwargs):
+        messages.success(self.request, self.success_message)
+        return super().delete(request, *args, **kwargs)
+
+
 class QuestionUpdate(
     LoginRequiredMixin, ObjectPermissionRequiredMixin, UpdateView
 ):


Test patch:
diff --git a/app/tests/reader_studies_tests/test_forms.py b/app/tests/reader_studies_tests/test_forms.py
--- a/app/tests/reader_studies_tests/test_forms.py
+++ b/app/tests/reader_studies_tests/test_forms.py
@@ -308,3 +308,50 @@ def test_image_port_only_with_bounding_box(
         assert response.status_code == 200
 
     assert Question.objects.all().count() == questions_created
+
+
+@pytest.mark.django_db
+def test_reader_study_delete(client):
+    rs = ReaderStudyFactory()
+    editor = UserFactory()
+    reader = UserFactory()
+    rs.editors_group.user_set.add(editor)
+    rs.readers_group.user_set.add(reader)
+
+    assert ReaderStudy.objects.count() == 1
+
+    response = get_view_for_user(
+        viewname="reader-studies:delete",
+        client=client,
+        method=client.get,
+        reverse_kwargs={"slug": rs.slug},
+        follow=True,
+        user=reader,
+    )
+
+    assert response.status_code == 403
+    assert ReaderStudy.objects.count() == 1
+
+    response = get_view_for_user(
+        viewname="reader-studies:delete",
+        client=client,
+        method=client.get,
+        reverse_kwargs={"slug": rs.slug},
+        follow=True,
+        user=editor,
+    )
+
+    assert response.status_code == 200
+    assert "Confirm Deletion" in response.rendered_content
+
+    response = get_view_for_user(
+        viewname="reader-studies:delete",
+        client=client,
+        method=client.post,
+        reverse_kwargs={"slug": rs.slug},
+        follow=True,
+        user=editor,
+    )
+
+    assert response.status_code == 200
+    assert ReaderStudy.objects.count() == 0