Repository: comic/grand-challenge.org

Issue:
It is no longer possible to have an image with multiple overlays defined per viewport.
**Describe the bug**
A clear and concise description of what the bug is.
It is no longer possible to have an image with multiple overlays defined per viewport.

Introduced in this pr:
https://github.com/comic/grand-challenge.org/pull/3188

**To Reproduce**
Steps to reproduce the behavior:
1. Go to https://grand-challenge.org/archives/cirrus-core-release-test-archive/
2. Click on 'Update Settings'
3. Click on Save (no need to make changes)
4. See error:
![image](https://github.com/comic/grand-challenge.org/assets/51329283/6c27107b-ccfc-4ec4-a218-b52cbb6942ac)


**Expected behavior**
A clear and concise description of what you expected to happen.
The validation should be done on the number of interfaces of Interface Kind Image, not all image types. Having multiple segmentations or heatmaps per viewport is a supported feature in Cirrus.

**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:
That was not my understanding from the Roadmap item:

> Cirrus already ignores additional images when multiple images are added to a single viewport.

https://github.com/DIAGNijmegen/rse-roadmap/issues/282
That wasn't worded accurately then, additional CIV of kind `Image` are ignored. Multiple CIVs of kind `Segmentation` and/or `Heatmap` can be added to a single viewport. 
Okay - so the limitations are:

- There can only be at most one non-segmentation or non-heatmap
- There is no limitation on the number of segmentations or heatmaps

Resolving patch:
diff --git a/app/grandchallenge/hanging_protocols/models.py b/app/grandchallenge/hanging_protocols/models.py
--- a/app/grandchallenge/hanging_protocols/models.py
+++ b/app/grandchallenge/hanging_protocols/models.py
@@ -351,12 +351,12 @@ def check_all_interfaces_in_view_content_exist(self):
             image_interfaces = [
                 i
                 for i in viewport_interfaces
-                if i.kind in InterfaceKind.interface_type_image()
+                if i.kind == InterfaceKind.InterfaceKindChoices.IMAGE
             ]
 
-            if len(image_interfaces) > 2:
+            if len(image_interfaces) > 1:
                 raise ValidationError(
-                    "Maximum of two image interfaces are allowed per viewport, "
+                    "Maximum of one image interface is allowed per viewport, "
                     f"got {len(image_interfaces)} for viewport {viewport}: "
                     f"{', '.join(i.slug for i in image_interfaces)}"
                 )


Test patch:
diff --git a/app/tests/hanging_protocols_tests/test_models.py b/app/tests/hanging_protocols_tests/test_models.py
--- a/app/tests/hanging_protocols_tests/test_models.py
+++ b/app/tests/hanging_protocols_tests/test_models.py
@@ -479,6 +479,7 @@ def test_view_content_validation():
 @pytest.mark.django_db
 def test_at_most_two_images():
     image = ComponentInterfaceFactory(kind=InterfaceKindChoices.IMAGE)
+    image2 = ComponentInterfaceFactory(kind=InterfaceKindChoices.IMAGE)
     heatmap = ComponentInterfaceFactory(kind=InterfaceKindChoices.HEAT_MAP)
     segmentation = ComponentInterfaceFactory(
         kind=InterfaceKindChoices.SEGMENTATION
@@ -509,12 +510,19 @@ def test_at_most_two_images():
     hp = HangingProtocolTestModel(
         view_content={"main": [image.slug, heatmap.slug, segmentation.slug]}
     )
+    hp.full_clean()
+
+    hp = HangingProtocolTestModel(
+        view_content={
+            "main": [image.slug, image2.slug, heatmap.slug, segmentation.slug]
+        }
+    )
 
     with pytest.raises(ValidationError) as err:
         hp.full_clean()
 
     assert (
-        "Maximum of two image interfaces are allowed per viewport, got 3 for viewport main:"
+        "Maximum of one image interface is allowed per viewport, got 2 for viewport main:"
         in str(err.value)
     )