Skip to content

Commit 8261cf5

Browse files
authored
Add invocation REST API viewset (#4733)
See DIAGNijmegen/rse-roadmap#481
1 parent 9836085 commit 8261cf5

5 files changed

Lines changed: 371 additions & 2 deletions

File tree

app/grandchallenge/algorithms/apps.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,23 @@ def init_job_permissions(*_, **__):
2323
assign_perm("algorithms.add_job", g)
2424

2525

26+
def init_invocation_permissions(*_, **__):
27+
from django.contrib.auth.models import Group
28+
from guardian.shortcuts import assign_perm
29+
30+
g, _ = Group.objects.get_or_create(
31+
name=settings.REGISTERED_USERS_GROUP_NAME
32+
)
33+
assign_perm("algorithms.add_invocation", g)
34+
35+
2636
class AlgorithmsConfig(AppConfig):
2737
name = "grandchallenge.algorithms"
2838

2939
def ready(self):
3040
post_migrate.connect(init_algorithm_creators_group, sender=self)
3141
post_migrate.connect(init_job_permissions, sender=self)
42+
post_migrate.connect(init_invocation_permissions, sender=self)
3243
# noinspection PyUnresolvedReferences
3344
import grandchallenge.algorithms.signals # noqa: F401
3445
from grandchallenge.algorithms.models import AlgorithmImage

app/grandchallenge/algorithms/filters.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
from django_filters import BooleanFilter, FilterSet, filters
2+
from rest_framework.filters import BaseFilterBackend
23

3-
from grandchallenge.algorithms.models import Algorithm, Endpoint, Job
4+
from grandchallenge.algorithms.models import (
5+
Algorithm,
6+
Endpoint,
7+
Invocation,
8+
Job,
9+
)
410
from grandchallenge.cases.models import Image
511
from grandchallenge.core.filters import TitleDescriptionModalityStructureFilter
12+
from grandchallenge.core.guardian import filter_by_permission
613

714

815
class EndpointFilter(FilterSet):
@@ -28,6 +35,35 @@ def filter_status(self, queryset, name, value):
2835
return queryset.none()
2936

3037

38+
class InvocationObjectPermissionsFilter(BaseFilterBackend):
39+
def filter_queryset(self, request, queryset, view):
40+
endpoints = filter_by_permission(
41+
queryset=Endpoint.objects.all(),
42+
user=request.user,
43+
codename="view_endpoint",
44+
)
45+
return queryset.filter(endpoint__in=endpoints)
46+
47+
48+
class InvocationFilter(FilterSet):
49+
status = filters.CharFilter(method="filter_status")
50+
51+
class Meta:
52+
model = Invocation
53+
fields = ["endpoint", "status"]
54+
55+
def filter_status(self, queryset, name, value):
56+
display_to_value = {
57+
label.lower(): db_value
58+
for db_value, label in Invocation.StatusChoices.choices
59+
}
60+
61+
try:
62+
return queryset.filter(status=display_to_value[value.lower()])
63+
except KeyError:
64+
return queryset.none()
65+
66+
3167
class JobViewsetFilter(FilterSet):
3268
input_image = filters.ModelMultipleChoiceFilter(
3369
field_name="inputs__image", queryset=Image.objects.all()

app/grandchallenge/algorithms/views.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from grandchallenge.algorithms.filters import (
4040
AlgorithmFilter,
4141
EndpointFilter,
42+
InvocationFilter,
43+
InvocationObjectPermissionsFilter,
4244
JobViewsetFilter,
4345
)
4446
from grandchallenge.algorithms.forms import (
@@ -71,13 +73,16 @@
7173
AlgorithmModel,
7274
AlgorithmPermissionRequest,
7375
Endpoint,
76+
Invocation,
7477
Job,
7578
)
7679
from grandchallenge.algorithms.serializers import (
7780
AlgorithmImageSerializer,
7881
AlgorithmSerializer,
7982
EndpointSerializer,
83+
HyperlinkedInvocationSerializer,
8084
HyperlinkedJobSerializer,
85+
InvocationPostSerializer,
8186
JobPostSerializer,
8287
)
8388
from grandchallenge.components.backends.exceptions import (
@@ -914,6 +919,21 @@ class EndpointViewSet(ReadOnlyModelViewSet):
914919
filterset_class = EndpointFilter
915920

916921

922+
class InvocationViewSet(
923+
CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet
924+
):
925+
queryset = Invocation.objects.all()
926+
permission_classes = [DjangoObjectPermissions]
927+
filter_backends = [DjangoFilterBackend, InvocationObjectPermissionsFilter]
928+
filterset_class = InvocationFilter
929+
930+
def get_serializer_class(self):
931+
if self.action == "create":
932+
return InvocationPostSerializer
933+
else:
934+
return HyperlinkedInvocationSerializer
935+
936+
917937
class AlgorithmPermissionRequestCreate(
918938
LoginRequiredMixin, SuccessMessageMixin, CreateView
919939
):

app/grandchallenge/api/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
AlgorithmImageViewSet,
77
AlgorithmViewSet,
88
EndpointViewSet,
9+
InvocationViewSet,
910
JobViewSet,
1011
)
1112
from grandchallenge.api.views import GCAPIView
@@ -50,6 +51,11 @@
5051
router.register(
5152
r"algorithms/endpoints", EndpointViewSet, basename="algorithms-endpoint"
5253
)
54+
router.register(
55+
r"algorithms/invocations",
56+
InvocationViewSet,
57+
basename="algorithms-invocation",
58+
)
5359
router.register(r"algorithms", AlgorithmViewSet, basename="algorithm")
5460

5561
# Archives

0 commit comments

Comments
 (0)