Repository: comic/grand-challenge.org

Issue:
Failing partial update of display set does not return validation error.
**Describe the bug**
A clear and concise description of what the bug is.

A partial update of a reader study display set providing a combination of images and annotations using the gcapi client does not work but also gives no errors. Although the reason that the partial update didn't work lies in the gcapi client, it is also a problem that the validation error that occurs in Grand Challenge does not get returned to the user. 

**To Reproduce**
Steps to reproduce the behavior:
While fixing this [issue](https://github.com/DIAGNijmegen/rse-gcapi/issues/143), I noticed that the partial update call does not return any error if the request can't be validated. [This if statement](https://github.com/comic/grand-challenge.org/blob/663437267636baee52ce5705a94f863df95ecf79/app/grandchallenge/reader_studies/views.py#L1128) maybe needs an else?

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Browser [e.g. stock browser, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.



Discussion:

Resolving patch:
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
@@ -1121,11 +1121,7 @@ def partial_update(self, request, pk=None):
                 "This display set cannot be changed, "
                 "as answers for it already exist."
             )
-        assigned_civs = []
         values = request.data.pop("values", None)
-        civs = instance.reader_study.display_sets.values_list(
-            "values", flat=True
-        )
         assigned_civs = []
         if values:
             serialized_data = ComponentInterfaceValuePostSerializer(
@@ -1133,12 +1129,10 @@ def partial_update(self, request, pk=None):
             )
             if serialized_data.is_valid():
                 civs = []
-                interfaces = []
                 for value in serialized_data.validated_data:
                     interface = value.get("interface", None)
                     user_upload = value.get("user_upload", None)
                     if interface.requires_file and user_upload:
-                        interfaces.append(interface)
                         transaction.on_commit(
                             add_file_to_display_set.signature(
                                 kwargs={
@@ -1156,6 +1150,8 @@ def partial_update(self, request, pk=None):
                 )
                 instance.values.remove(*assigned_civs)
                 instance.values.add(*civs)
+            else:
+                raise DRFValidationError(serialized_data.errors)
 
         # Create a new display set for any civs that have been replaced by a
         # new value in this display set, to ensure it remains connected to
@@ -1168,7 +1164,6 @@ def partial_update(self, request, pk=None):
                     reader_study=instance.reader_study
                 )
                 ds.values.add(assigned)
-            instance.values.remove(assigned)
         return super().partial_update(request, pk)
 
     def destroy(self, request, *args, **kwargs):


Test patch:
diff --git a/app/tests/reader_studies_tests/test_api.py b/app/tests/reader_studies_tests/test_api.py
--- a/app/tests/reader_studies_tests/test_api.py
+++ b/app/tests/reader_studies_tests/test_api.py
@@ -1838,6 +1838,27 @@ def test_display_set_add_and_edit(client, settings):
     )
 
 
+@pytest.mark.django_db
+def test_display_set_partial_update_errors_returned(client):
+    ds = DisplaySetFactory()
+    user = UserFactory()
+    ds.reader_study.add_editor(user)
+    ci = ComponentInterfaceFactory(
+        kind=InterfaceKind.InterfaceKindChoices.STRING
+    )
+    response = get_view_for_user(
+        viewname="api:reader-studies-display-set-detail",
+        reverse_kwargs={"pk": ds.pk},
+        user=user,
+        client=client,
+        method=client.patch,
+        content_type="application/json",
+        data={"values": [{"interface": ci.slug, "value": 11}]},
+    )
+    assert response.status_code == 400
+    assert "JSON does not fulfill schema" in response.content.decode("utf-8")
+
+
 @pytest.mark.django_db
 def test_display_sets_shuffled_per_user(client):
     n_display_sets = 10