Repository: comic/grand-challenge.org

Issue:
Include archive information for admins on submission page



Discussion:
Also include the number of valid archive items for that phase.

Resolving patch:
diff --git a/app/grandchallenge/evaluation/models.py b/app/grandchallenge/evaluation/models.py
--- a/app/grandchallenge/evaluation/models.py
+++ b/app/grandchallenge/evaluation/models.py
@@ -9,6 +9,7 @@
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
+from django.db.models import Count, Q
 from django.db.transaction import on_commit
 from django.utils import timezone
 from django.utils.functional import cached_property
@@ -19,7 +20,7 @@
 from guardian.shortcuts import assign_perm, remove_perm
 
 from grandchallenge.algorithms.models import AlgorithmImage
-from grandchallenge.archives.models import Archive
+from grandchallenge.archives.models import Archive, ArchiveItem
 from grandchallenge.components.models import (
     ComponentImage,
     ComponentInterface,
@@ -755,6 +756,25 @@ def active_image(self):
         except ObjectDoesNotExist:
             return None
 
+    @cached_property
+    def valid_archive_items(self):
+        """Returns the archive items that are valid for this phase"""
+        if self.archive and self.algorithm_inputs:
+            return self.archive.items.annotate(
+                interface_match_count=Count(
+                    "values",
+                    filter=Q(
+                        values__interface__in={*self.algorithm_inputs.all()}
+                    ),
+                )
+            ).filter(interface_match_count=len(self.algorithm_inputs.all()))
+        else:
+            return ArchiveItem.objects.none()
+
+    @cached_property
+    def count_valid_archive_items(self):
+        return self.valid_archive_items.count()
+
 
 class PhaseUserObjectPermission(UserObjectPermissionBase):
     content_object = models.ForeignKey(Phase, on_delete=models.CASCADE)


Test patch:
diff --git a/app/tests/evaluation_tests/test_models.py b/app/tests/evaluation_tests/test_models.py
--- a/app/tests/evaluation_tests/test_models.py
+++ b/app/tests/evaluation_tests/test_models.py
@@ -610,3 +610,46 @@ def test_combined_leaderboard_ranks(combined_ranks, expected_ranks):
     combined_ranks = [{"combined_rank": cr} for cr in combined_ranks]
     CombinedLeaderboard._rank_combined_rank_scores(combined_ranks)
     assert [cr["rank"] for cr in combined_ranks] == expected_ranks
+
+
+@pytest.mark.django_db
+def test_count_valid_archive_items():
+    archive = ArchiveFactory()
+    phase = PhaseFactory(archive=archive)
+    i1, i2, i3 = ComponentInterfaceFactory.create_batch(3)
+
+    phase.algorithm_inputs.set([i1, i2])
+
+    # Valid archive item
+    ai1 = ArchiveItemFactory(archive=archive)
+    ai1.values.add(ComponentInterfaceValueFactory(interface=i1))
+    ai1.values.add(ComponentInterfaceValueFactory(interface=i2))
+
+    # Valid, but with extra value
+    ai2 = ArchiveItemFactory(archive=archive)
+    ai2.values.add(ComponentInterfaceValueFactory(interface=i1))
+    ai2.values.add(ComponentInterfaceValueFactory(interface=i2))
+    ai2.values.add(ComponentInterfaceValueFactory(interface=i3))
+
+    # Invalid, missing all values
+    _ = ArchiveItemFactory(archive=archive)
+
+    # Invalid, incomplete
+    ai4 = ArchiveItemFactory(archive=archive)
+    ai4.values.add(ComponentInterfaceValueFactory(interface=i1))
+
+    # Invalid, incomplete but right number of values
+    ai5 = ArchiveItemFactory(archive=archive)
+    ai5.values.add(ComponentInterfaceValueFactory(interface=i1))
+    ai5.values.add(ComponentInterfaceValueFactory(interface=i3))
+
+    cciv1 = ComponentInterfaceValueFactory(interface=i1)
+    cciv2 = ComponentInterfaceValueFactory(interface=i2)
+
+    # Valid, reusing interfaces
+    ai6 = ArchiveItemFactory(archive=archive)
+    ai6.values.set([cciv1, cciv2])
+    ai7 = ArchiveItemFactory(archive=archive)
+    ai7.values.set([cciv1, cciv2])
+
+    assert {*phase.valid_archive_items} == {ai1, ai2, ai6, ai7}