Skip to content

Commit 867c5ed

Browse files
committed
Merge branch 'main' into enable-more-phases-for-budget
2 parents f3b03e4 + 87877da commit 867c5ed

166 files changed

Lines changed: 7689 additions & 3251 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CITATION.cff

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,70 @@ title: Grand-Challenge.org
44
abstract: A platform for end-to-end development of machine learning solutions in biomedical imaging
55
license: Apache-2.0
66
authors:
7-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
7+
# Primary author(s)
8+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
89
family-names: Meakin
910
given-names: James
10-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
11+
12+
# RSEs, alphabetical order
13+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
1114
family-names: Gerke
1215
given-names: Paul K.
13-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
16+
orcid: https://orcid.org/0000-0003-1473-5705
17+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
1418
family-names: Kerkstra
1519
given-names: Sjoerd
16-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
17-
family-names: Groeneveld
18-
given-names: Miriam
19-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
20-
family-names: van Leeuwen
21-
given-names: Kicky
22-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
20+
- affiliation: Informatics Institute, University of Amsterdam, The Netherlands
21+
family-names: Koopman
22+
given-names: Thomas
23+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
2324
family-names: Mickan
2425
given-names: Anne
25-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
26-
family-names: Overkamp
27-
given-names: Mike
28-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
26+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
2927
family-names: van Run
3028
given-names: Chris
31-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
29+
- affiliation: Department of Pathology, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
3230
family-names: van Zeeland
3331
given-names: Harm
34-
- affiliation: Department of Radiology, Nuclear Medicine and Anatomy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
35-
family-names: van Ginneken
36-
given-names: Bram
37-
orcid: https://orcid.org/0000-0003-2028-8972
32+
33+
# Primary stakeholders, alphabetical order
34+
- affiliation: Department of Pathology, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
35+
family-names: Ciompi
36+
given-names: Francesco
37+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
38+
family-names: Hering
39+
given-names: Alessa
40+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
41+
family-names: Jacobs
42+
given-names: Colin
43+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
44+
family-names: Khalili
45+
given-names: Nadieh
46+
- affiliation: Department of Radiotherapy, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
47+
family-names: Koopmans
48+
given-names: Peter
49+
- affiliation: Department of Pathology, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
50+
family-names: van der Laak
51+
given-names: Jeroen
52+
- affiliation: Department of Pathology, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
53+
family-names: Litjens
54+
given-names: Geert
55+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
56+
family-names: Quax
57+
given-names: Silvan
58+
- affiliation: Informatics Institute, University of Amsterdam, The Netherlands
59+
family-names: Sánchez
60+
given-names: Clara I.
61+
- affiliation: Department of Cardiology, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
62+
family-names: Tannhauser
63+
given-names: Jos
64+
65+
# Product owners
66+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
67+
family-names: Groeneveld
68+
given-names: Miriam
69+
- affiliation: Department of Medical Imaging, Radboud University Medical Center, P.O. Box 9101, 6500 HB Nijmegen, the Netherlands
70+
family-names: Huisman
71+
given-names: Henkjan
3872
doi: 10.5281/zenodo.3356819
3973
repository-code: https://github.com/comic/grand-challenge.org

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Build Status](https://github.com/comic/grand-challenge.org/workflows/CI/badge.svg)](https://github.com/comic/grand-challenge.org/actions?query=workflow%3ACI+branch%3Amain)
44
[![Documentation](https://img.shields.io/badge/docs-published-success)](https://comic.github.io/grand-challenge.org/)
55
[![Black Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
6-
[![Cite Us with Zenodo](https://zenodo.org/badge/4557968.svg)](https://zenodo.org/badge/latestdoi/4557968)
6+
[![Cite Us with Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.3356819.svg)](https://doi.org/10.5281/zenodo.3356819)
77

88
In the era of Deep Learning, developing robust machine learning solutions to problems in biomedical imaging requires access to large amounts of annotated training data, fair comparisons of state of the art machine learning solutions, and clinical validation using real world data. Grand Challenge can assist Researchers, Data Scientists, and Clinicians in collaborating to develop these solutions by providing:
99

amass.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"lock_version": "1.0",
3-
"content_hash": "sha256:dea343037bf43507bc0ebc532063f62a4ede582c20086903f0409b15118c433c",
3+
"content_hash": "sha256:ff51ab7b46995da8cedc3a5926630bbaa79c98c9166144c0d879b0dc6a7fa74e",
44
"dependencies": [
55
{
66
"name": "@diagnijmegen/rse-grand-challenge-dicom-de-id-procedure",
7-
"version": "2025.8.5",
7+
"version": "2025.10.0",
88
"provider": "unpkg",
99
"assets": [
1010
{
1111
"name": "dist/grand-challenge-dicom-de-id-procedure.umd.min.js",
12-
"sri": "sha256-77UMyT680wXKkQmsQxLkpXoFIrIokFff81G5PlK4YTQ="
12+
"sri": "sha256-up3MoFUrSVqnKhJjbQ/5Lm9pdFBNirwx1sJSUcSbf1s="
1313
},
1414
{
1515
"name": "dist/grand-challenge-dicom-de-id-procedure.umd.min.js.map",

app/config/settings.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
}
9999

100100
EMAIL_BACKEND = "grandchallenge.emails.backends.CelerySESBackend"
101+
SUPPORT_EMAIL = os.environ.get("SUPPORT_EMAIL", "grandchallenge@localhost")
101102
DEFAULT_FROM_EMAIL = os.environ.get(
102103
"DEFAULT_FROM_EMAIL", "grandchallenge@localhost"
103104
)
@@ -201,18 +202,13 @@
201202
# - sha256 sums are not implemented
202203
USING_MINIO = strtobool(os.environ.get("USING_MINIO", "False"))
203204

204-
AWS_S3_FILE_OVERWRITE = False
205-
# Note: deprecated in django storages 2.0
206-
AWS_BUCKET_ACL = "private"
205+
# Django storages settings, see https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
207206
AWS_DEFAULT_ACL = "private"
207+
AWS_S3_FILE_OVERWRITE = False
208208
AWS_S3_MAX_MEMORY_SIZE = 1_048_576 # 100 MB
209209
AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL")
210-
AWS_DEFAULT_REGION = os.environ.get("AWS_DEFAULT_REGION", "eu-central-1")
211210
AWS_S3_REGION_NAME = os.environ.get("AWS_S3_REGION_NAME")
212211
AWS_S3_URL_PROTOCOL = os.environ.get("AWS_S3_URL_PROTOCOL", "https:")
213-
AWS_CLOUDWATCH_REGION_NAME = os.environ.get("AWS_CLOUDWATCH_REGION_NAME")
214-
AWS_CODEBUILD_REGION_NAME = os.environ.get("AWS_CODEBUILD_REGION_NAME")
215-
AWS_SES_REGION_NAME = os.environ.get("AWS_SES_REGION_NAME")
216212

217213
# This is for storing files that should not be served to the public
218214
PRIVATE_S3_STORAGE_KWARGS = {
@@ -281,6 +277,26 @@
281277
os.environ.get("CLOUDFRONT_URL_EXPIRY_SECONDS", "300") # 5 mins
282278
)
283279

280+
##############################################################################
281+
#
282+
# AWS
283+
#
284+
##############################################################################
285+
286+
AWS_DEFAULT_REGION = os.environ.get("AWS_DEFAULT_REGION", "eu-central-1")
287+
AWS_CLOUDWATCH_REGION_NAME = os.environ.get("AWS_CLOUDWATCH_REGION_NAME")
288+
AWS_CODEBUILD_REGION_NAME = os.environ.get("AWS_CODEBUILD_REGION_NAME")
289+
AWS_SES_REGION_NAME = os.environ.get("AWS_SES_REGION_NAME")
290+
AWS_HEALTH_IMAGING_DATASTORE_ID = os.environ.get(
291+
"AWS_HEALTH_IMAGING_DATASTORE_ID"
292+
)
293+
AWS_HEALTH_IMAGING_BUCKET_NAME = os.environ.get(
294+
"AWS_HEALTH_IMAGING_BUCKET_NAME"
295+
)
296+
AWS_HEALTH_IMAGING_IMPORT_ROLE_ARN = os.environ.get(
297+
"AWS_HEALTH_IMAGING_IMPORT_ROLE_ARN"
298+
)
299+
284300
##############################################################################
285301
#
286302
# Caching
@@ -422,8 +438,7 @@ def get_private_ip():
422438
"django.template.context_processors.request",
423439
"django.contrib.messages.context_processors.messages",
424440
"grandchallenge.core.context_processors.challenge",
425-
"grandchallenge.core.context_processors.deployment_info",
426-
"grandchallenge.core.context_processors.debug",
441+
"grandchallenge.core.context_processors.django_settings",
427442
"grandchallenge.core.context_processors.sentry_dsn",
428443
"grandchallenge.core.context_processors.footer_links",
429444
"grandchallenge.core.context_processors.about_page",
@@ -1372,6 +1387,7 @@ def sentry_before_send(event, hint):
13721387
"zl.edu.kg",
13731388
"lw.edu.kg",
13741389
"edumail.edu.pl",
1390+
"zorrag.com",
13751391
*blocklist,
13761392
}
13771393

app/grandchallenge/algorithms/forms.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@
7878
from grandchallenge.components.tasks import assign_tarball_from_upload
7979
from grandchallenge.core.forms import (
8080
PermissionRequestUpdateForm,
81+
PhaseMixin,
8182
SaveFormInitMixin,
83+
UserMixin,
8284
WorkstationUserFilterMixin,
8385
)
8486
from grandchallenge.core.guardian import filter_by_permission
@@ -106,7 +108,7 @@ class ModelFactsTextField(Field):
106108
template = "algorithms/model_facts_field.html"
107109

108110

109-
class JobCreateForm(AdditionalInputsMixin, Form):
111+
class JobCreateForm(SaveFormInitMixin, AdditionalInputsMixin, Form):
110112
algorithm_image = ModelChoiceField(
111113
queryset=None, disabled=True, required=True, widget=HiddenInput
112114
)
@@ -123,12 +125,15 @@ class JobCreateForm(AdditionalInputsMixin, Form):
123125
widget=HiddenInput,
124126
)
125127

126-
def __init__(self, *args, algorithm, user, interface, **kwargs):
127-
super().__init__(*args, **kwargs)
128-
128+
def __init__(self, *args, algorithm, interface, **kwargs):
129129
self._algorithm = algorithm
130130

131-
self._user = user
131+
super().__init__(
132+
*args,
133+
additional_inputs=interface.inputs.all(),
134+
**kwargs,
135+
)
136+
132137
self.fields["creator"].queryset = get_user_model().objects.filter(
133138
pk=self._user.pk
134139
)
@@ -155,11 +160,6 @@ def __init__(self, *args, algorithm, user, interface, **kwargs):
155160
)
156161
self.fields["algorithm_model"].initial = active_model
157162

158-
self.init_additional_inputs(inputs=interface.inputs.all())
159-
160-
self.helper = FormHelper(self)
161-
self.helper.layout.append(Submit("save", "Save"))
162-
163163
@cached_property
164164
def jobs_limit(self):
165165
if self._algorithm_image:
@@ -187,10 +187,8 @@ def clean(self):
187187
"please try again after they have completed"
188188
)
189189

190-
cleaned_data["inputs"] = self.clean_additional_inputs()
191-
192190
if Job.objects.get_jobs_with_same_inputs(
193-
inputs=cleaned_data["inputs"],
191+
inputs=cleaned_data["additional_inputs"],
194192
algorithm_image=cleaned_data["algorithm_image"],
195193
algorithm_model=cleaned_data["algorithm_model"],
196194
):
@@ -212,19 +210,17 @@ def clean(self):
212210
]
213211

214212

215-
class PhaseSelectForm(Form):
213+
class PhaseSelectForm(UserMixin, Form):
216214
phase = ModelChoiceField(
217215
label="Please select the phase for which you are creating an algorithm",
218216
queryset=Phase.objects.none(),
219217
required=True,
220218
widget=Select2Widget,
221219
)
222220

223-
def __init__(self, *args, user, **kwargs):
221+
def __init__(self, *args, **kwargs):
224222
super().__init__(*args, **kwargs)
225223

226-
self._user = user
227-
228224
self.fields["phase"].queryset = filter_by_permission(
229225
queryset=Phase.objects.filter(
230226
submission_kind=SubmissionKindChoices.ALGORITHM
@@ -256,8 +252,8 @@ def clean_phase(self):
256252

257253

258254
class AlgorithmForm(
259-
WorkstationUserFilterMixin,
260255
SaveFormInitMixin,
256+
WorkstationUserFilterMixin,
261257
ViewContentExampleMixin,
262258
ModelForm,
263259
):
@@ -346,9 +342,8 @@ class Meta:
346342
"workstation_config": "Viewer Configuration",
347343
}
348344

349-
def __init__(self, *args, user, **kwargs):
350-
super().__init__(*args, user=user, **kwargs)
351-
self._user = user
345+
def __init__(self, *args, **kwargs):
346+
super().__init__(*args, **kwargs)
352347

353348
self.fields["contact_email"].required = True
354349
self.fields["display_editors"].required = True
@@ -440,11 +435,7 @@ def maximum_settable_memory_gb(self):
440435
return value
441436

442437

443-
class UserAlgorithmsForPhaseMixin:
444-
def __init__(self, *args, user, phase, **kwargs):
445-
super().__init__(*args, **kwargs)
446-
self._user = user
447-
self._phase = phase
438+
class UserAlgorithmsForPhaseMixin(UserMixin, PhaseMixin):
448439

449440
@cached_property
450441
def user_algorithms_for_phase(self):
@@ -567,6 +558,7 @@ def __init__(
567558
**kwargs,
568559
):
569560
super().__init__(*args, user=user, phase=phase, **kwargs)
561+
570562
self.fields["workstation_config"].initial = workstation_config
571563
self.fields["workstation_config"].disabled = True
572564
self.fields["hanging_protocol"].initial = hanging_protocol
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Generated by Django 4.2.24 on 2025-10-02 13:06
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("algorithms", "0079_alter_algorithm_time_limit_alter_job_time_limit"),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name="algorithmimage",
15+
options={
16+
"ordering": ("created",),
17+
"permissions": [
18+
("download_algorithmimage", "Can download algorithm image")
19+
],
20+
},
21+
),
22+
migrations.AlterModelOptions(
23+
name="algorithmmodel",
24+
options={
25+
"ordering": ("created",),
26+
"permissions": [
27+
("download_algorithmmodel", "Can download algorithm model")
28+
],
29+
},
30+
),
31+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 4.2.25 on 2025-10-20 14:20
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("algorithms", "0080_alter_algorithmimage_options_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="job",
15+
name="signing_key",
16+
field=models.BinaryField(
17+
help_text="The key used to sign the inference result file",
18+
max_length=32,
19+
null=True,
20+
),
21+
),
22+
]

0 commit comments

Comments
 (0)