prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  The Phase (algorithm) input and output selects are annoying to use in the admin
  A select 2 widget would be better.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `app/grandchallenge/evaluation/admin.py`
  Content:
  ```
  1 from django.contrib import admin
  2 from django.core.exceptions import ObjectDoesNotExist, ValidationError
  3 from django.forms import ModelForm
  4 
  5 from grandchallenge.challenges.models import ChallengeRequest
  6 from grandchallenge.components.admin import (
  7     ComponentImageAdmin,
  8     cancel_jobs,
  9     deprovision_jobs,
  10     requeue_jobs,
  11 )
  12 from grandchallenge.core.admin import (
  13     GroupObjectPermissionAdmin,
  14     UserObjectPermissionAdmin,
  15 )
  16 from grandchallenge.core.templatetags.remove_whitespace import oxford_comma
  17 from grandchallenge.evaluation.models import (
  18     Evaluation,
  19     EvaluationGroupObjectPermission,
  20     EvaluationUserObjectPermission,
  21     Method,
  22     MethodGroupObjectPermission,
  23     MethodUserObjectPermission,
  24     Phase,
  25     PhaseGroupObjectPermission,
  26     PhaseUserObjectPermission,
  27     Submission,
  28     SubmissionGroupObjectPermission,
  29     SubmissionUserObjectPermission,
  30 )
  31 from grandchallenge.evaluation.tasks import create_evaluation
  32 from grandchallenge.evaluation.utils import SubmissionKindChoices
  33 
  34 
  35 class PhaseAdminForm(ModelForm):
  36     class Meta:
  37         model = Phase
  38         fields = "__all__"
  39 
  40     def clean(self):
  41         cleaned_data = super().clean()
  42 
  43         duplicate_interfaces = {
  44             *cleaned_data.get("algorithm_inputs", [])
  45         }.intersection({*cleaned_data.get("algorithm_outputs", [])})
  46 
  47         if duplicate_interfaces:
  48             raise ValidationError(
  49                 f"The sets of Algorithm Inputs and Algorithm Outputs must be unique: "
  50                 f"{oxford_comma(duplicate_interfaces)} present in both"
  51             )
  52 
  53         submission_kind = cleaned_data["submission_kind"]
  54         total_number_of_submissions_allowed = cleaned_data[
  55             "total_number_of_submissions_allowed"
  56         ]
  57 
  58         if (
  59             submission_kind == SubmissionKindChoices.ALGORITHM
  60             and not total_number_of_submissions_allowed
  61         ):
  62             try:
  63                 request = ChallengeRequest.objects.get(
  64                     short_name=self.instance.challenge.short_name
  65                 )
  66                 error_addition = f"The corresponding challenge request lists the following limits: Preliminary phase: {request.phase_1_number_of_submissions_per_team * request.expected_number_of_teams} Final test phase: {request.phase_2_number_of_submissions_per_team * request.expected_number_of_teams}. Set the limits according to the phase type. "
  67             except ObjectDoesNotExist:
  68                 error_addition = "There is no corresponding challenge request."
  69             raise ValidationError(
  70                 "For phases that take an algorithm as submission input, "
  71                 "the total_number_of_submissions_allowed needs to be set. "
  72                 + error_addition
  73             )
  74 
  75         return cleaned_data
  76 
  77 
  78 @admin.register(Phase)
  79 class PhaseAdmin(admin.ModelAdmin):
  80     ordering = ("challenge",)
  81     list_display = (
  82         "slug",
  83         "title",
  84         "challenge",
  85         "submission_kind",
  86         "open_for_submissions",
  87         "submissions_open_at",
  88         "submissions_close_at",
  89         "submissions_limit_per_user_per_period",
  90     )
  91     search_fields = ("pk", "title", "slug", "challenge__short_name")
  92     list_filter = (
  93         "submission_kind",
  94         "challenge__short_name",
  95     )
  96     form = PhaseAdminForm
  97 
  98     @admin.display(boolean=True)
  99     def open_for_submissions(self, instance):
  100         return instance.open_for_submissions
  101 
  102 
  103 @admin.action(
  104     description="Reevaluate selected submissions",
  105     permissions=("change",),
  106 )
  107 def reevaluate_submissions(modeladmin, request, queryset):
  108     """Creates a new evaluation for an existing submission"""
  109     for submission in queryset:
  110         create_evaluation.apply_async(
  111             kwargs={"submission_pk": str(submission.pk)}
  112         )
  113 
  114 
  115 @admin.register(Submission)
  116 class SubmissionAdmin(admin.ModelAdmin):
  117     ordering = ("-created",)
  118     list_display = ("pk", "created", "phase", "creator")
  119     list_filter = ("phase__challenge__short_name",)
  120     search_fields = ("pk", "creator__username", "phase__slug")
  121     readonly_fields = (
  122         "creator",
  123         "phase",
  124         "predictions_file",
  125         "algorithm_image",
  126     )
  127     actions = (reevaluate_submissions,)
  128 
  129 
  130 @admin.register(Evaluation)
  131 class EvaluationAdmin(admin.ModelAdmin):
  132     ordering = ("-created",)
  133     list_display = ("pk", "created", "submission", "status", "error_message")
  134     list_filter = ("submission__phase__challenge__short_name", "status")
  135     list_select_related = (
  136         "submission__phase__challenge",
  137         "submission__creator",
  138     )
  139     search_fields = (
  140         "pk",
  141         "submission__pk",
  142         "submission__phase__challenge__short_name",
  143         "submission__creator__username",
  144     )
  145     readonly_fields = (
  146         "status",
  147         "submission",
  148         "method",
  149         "inputs",
  150         "outputs",
  151         "attempt",
  152         "stdout",
  153         "stderr",
  154         "error_message",
  155         "input_prefixes",
  156         "task_on_success",
  157         "task_on_failure",
  158         "runtime_metrics",
  159     )
  160     actions = (requeue_jobs, cancel_jobs, deprovision_jobs)
  161 
  162 
  163 admin.site.register(PhaseUserObjectPermission, UserObjectPermissionAdmin)
  164 admin.site.register(PhaseGroupObjectPermission, GroupObjectPermissionAdmin)
  165 admin.site.register(Method, ComponentImageAdmin)
  166 admin.site.register(MethodUserObjectPermission, UserObjectPermissionAdmin)
  167 admin.site.register(MethodGroupObjectPermission, GroupObjectPermissionAdmin)
  168 admin.site.register(SubmissionUserObjectPermission, UserObjectPermissionAdmin)
  169 admin.site.register(
  170     SubmissionGroupObjectPermission, GroupObjectPermissionAdmin
  171 )
  172 admin.site.register(EvaluationUserObjectPermission, UserObjectPermissionAdmin)
  173 admin.site.register(
  174     EvaluationGroupObjectPermission, GroupObjectPermissionAdmin
  175 )
  176
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/app/grandchallenge/evaluation/admin.py b/app/grandchallenge/evaluation/admin.py
  --- a/app/grandchallenge/evaluation/admin.py
  +++ b/app/grandchallenge/evaluation/admin.py
  @@ -93,6 +93,13 @@
           "submission_kind",
           "challenge__short_name",
       )
  +    autocomplete_fields = (
  +        "inputs",
  +        "outputs",
  +        "algorithm_inputs",
  +        "algorithm_outputs",
  +        "archive",
  +    )
       form = PhaseAdminForm
   
       @admin.display(boolean=True)
  
verification_info: {"golden_diff": "diff --git a/app/grandchallenge/evaluation/admin.py b/app/grandchallenge/evaluation/admin.py\n--- a/app/grandchallenge/evaluation/admin.py\n+++ b/app/grandchallenge/evaluation/admin.py\n@@ -93,6 +93,13 @@\n         \"submission_kind\",\n         \"challenge__short_name\",\n     )\n+    autocomplete_fields = (\n+        \"inputs\",\n+        \"outputs\",\n+        \"algorithm_inputs\",\n+        \"algorithm_outputs\",\n+        \"archive\",\n+    )\n     form = PhaseAdminForm\n \n     @admin.display(boolean=True)\n", "issue": "The Phase (algorithm) input and output selects are annoying to use in the admin\nA select 2 widget would be better.\n", "before_files": [{"content": "from django.contrib import admin\nfrom django.core.exceptions import ObjectDoesNotExist, ValidationError\nfrom django.forms import ModelForm\n\nfrom grandchallenge.challenges.models import ChallengeRequest\nfrom grandchallenge.components.admin import (\n    ComponentImageAdmin,\n    cancel_jobs,\n    deprovision_jobs,\n    requeue_jobs,\n)\nfrom grandchallenge.core.admin import (\n    GroupObjectPermissionAdmin,\n    UserObjectPermissionAdmin,\n)\nfrom grandchallenge.core.templatetags.remove_whitespace import oxford_comma\nfrom grandchallenge.evaluation.models import (\n    Evaluation,\n    EvaluationGroupObjectPermission,\n    EvaluationUserObjectPermission,\n    Method,\n    MethodGroupObjectPermission,\n    MethodUserObjectPermission,\n    Phase,\n    PhaseGroupObjectPermission,\n    PhaseUserObjectPermission,\n    Submission,\n    SubmissionGroupObjectPermission,\n    SubmissionUserObjectPermission,\n)\nfrom grandchallenge.evaluation.tasks import create_evaluation\nfrom grandchallenge.evaluation.utils import SubmissionKindChoices\n\n\nclass PhaseAdminForm(ModelForm):\n    class Meta:\n        model = Phase\n        fields = \"__all__\"\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        duplicate_interfaces = {\n            *cleaned_data.get(\"algorithm_inputs\", [])\n        }.intersection({*cleaned_data.get(\"algorithm_outputs\", [])})\n\n        if duplicate_interfaces:\n            raise ValidationError(\n                f\"The sets of Algorithm Inputs and Algorithm Outputs must be unique: \"\n                f\"{oxford_comma(duplicate_interfaces)} present in both\"\n            )\n\n        submission_kind = cleaned_data[\"submission_kind\"]\n        total_number_of_submissions_allowed = cleaned_data[\n            \"total_number_of_submissions_allowed\"\n        ]\n\n        if (\n            submission_kind == SubmissionKindChoices.ALGORITHM\n            and not total_number_of_submissions_allowed\n        ):\n            try:\n                request = ChallengeRequest.objects.get(\n                    short_name=self.instance.challenge.short_name\n                )\n                error_addition = f\"The corresponding challenge request lists the following limits: Preliminary phase: {request.phase_1_number_of_submissions_per_team * request.expected_number_of_teams} Final test phase: {request.phase_2_number_of_submissions_per_team * request.expected_number_of_teams}. Set the limits according to the phase type. \"\n            except ObjectDoesNotExist:\n                error_addition = \"There is no corresponding challenge request.\"\n            raise ValidationError(\n                \"For phases that take an algorithm as submission input, \"\n                \"the total_number_of_submissions_allowed needs to be set. \"\n                + error_addition\n            )\n\n        return cleaned_data\n\n\n@admin.register(Phase)\nclass PhaseAdmin(admin.ModelAdmin):\n    ordering = (\"challenge\",)\n    list_display = (\n        \"slug\",\n        \"title\",\n        \"challenge\",\n        \"submission_kind\",\n        \"open_for_submissions\",\n        \"submissions_open_at\",\n        \"submissions_close_at\",\n        \"submissions_limit_per_user_per_period\",\n    )\n    search_fields = (\"pk\", \"title\", \"slug\", \"challenge__short_name\")\n    list_filter = (\n        \"submission_kind\",\n        \"challenge__short_name\",\n    )\n    form = PhaseAdminForm\n\n    @admin.display(boolean=True)\n    def open_for_submissions(self, instance):\n        return instance.open_for_submissions\n\n\n@admin.action(\n    description=\"Reevaluate selected submissions\",\n    permissions=(\"change\",),\n)\ndef reevaluate_submissions(modeladmin, request, queryset):\n    \"\"\"Creates a new evaluation for an existing submission\"\"\"\n    for submission in queryset:\n        create_evaluation.apply_async(\n            kwargs={\"submission_pk\": str(submission.pk)}\n        )\n\n\n@admin.register(Submission)\nclass SubmissionAdmin(admin.ModelAdmin):\n    ordering = (\"-created\",)\n    list_display = (\"pk\", \"created\", \"phase\", \"creator\")\n    list_filter = (\"phase__challenge__short_name\",)\n    search_fields = (\"pk\", \"creator__username\", \"phase__slug\")\n    readonly_fields = (\n        \"creator\",\n        \"phase\",\n        \"predictions_file\",\n        \"algorithm_image\",\n    )\n    actions = (reevaluate_submissions,)\n\n\n@admin.register(Evaluation)\nclass EvaluationAdmin(admin.ModelAdmin):\n    ordering = (\"-created\",)\n    list_display = (\"pk\", \"created\", \"submission\", \"status\", \"error_message\")\n    list_filter = (\"submission__phase__challenge__short_name\", \"status\")\n    list_select_related = (\n        \"submission__phase__challenge\",\n        \"submission__creator\",\n    )\n    search_fields = (\n        \"pk\",\n        \"submission__pk\",\n        \"submission__phase__challenge__short_name\",\n        \"submission__creator__username\",\n    )\n    readonly_fields = (\n        \"status\",\n        \"submission\",\n        \"method\",\n        \"inputs\",\n        \"outputs\",\n        \"attempt\",\n        \"stdout\",\n        \"stderr\",\n        \"error_message\",\n        \"input_prefixes\",\n        \"task_on_success\",\n        \"task_on_failure\",\n        \"runtime_metrics\",\n    )\n    actions = (requeue_jobs, cancel_jobs, deprovision_jobs)\n\n\nadmin.site.register(PhaseUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(PhaseGroupObjectPermission, GroupObjectPermissionAdmin)\nadmin.site.register(Method, ComponentImageAdmin)\nadmin.site.register(MethodUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(MethodGroupObjectPermission, GroupObjectPermissionAdmin)\nadmin.site.register(SubmissionUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(\n    SubmissionGroupObjectPermission, GroupObjectPermissionAdmin\n)\nadmin.site.register(EvaluationUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(\n    EvaluationGroupObjectPermission, GroupObjectPermissionAdmin\n)\n", "path": "app/grandchallenge/evaluation/admin.py"}], "after_files": [{"content": "from django.contrib import admin\nfrom django.core.exceptions import ObjectDoesNotExist, ValidationError\nfrom django.forms import ModelForm\n\nfrom grandchallenge.challenges.models import ChallengeRequest\nfrom grandchallenge.components.admin import (\n    ComponentImageAdmin,\n    cancel_jobs,\n    deprovision_jobs,\n    requeue_jobs,\n)\nfrom grandchallenge.core.admin import (\n    GroupObjectPermissionAdmin,\n    UserObjectPermissionAdmin,\n)\nfrom grandchallenge.core.templatetags.remove_whitespace import oxford_comma\nfrom grandchallenge.evaluation.models import (\n    Evaluation,\n    EvaluationGroupObjectPermission,\n    EvaluationUserObjectPermission,\n    Method,\n    MethodGroupObjectPermission,\n    MethodUserObjectPermission,\n    Phase,\n    PhaseGroupObjectPermission,\n    PhaseUserObjectPermission,\n    Submission,\n    SubmissionGroupObjectPermission,\n    SubmissionUserObjectPermission,\n)\nfrom grandchallenge.evaluation.tasks import create_evaluation\nfrom grandchallenge.evaluation.utils import SubmissionKindChoices\n\n\nclass PhaseAdminForm(ModelForm):\n    class Meta:\n        model = Phase\n        fields = \"__all__\"\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        duplicate_interfaces = {\n            *cleaned_data.get(\"algorithm_inputs\", [])\n        }.intersection({*cleaned_data.get(\"algorithm_outputs\", [])})\n\n        if duplicate_interfaces:\n            raise ValidationError(\n                f\"The sets of Algorithm Inputs and Algorithm Outputs must be unique: \"\n                f\"{oxford_comma(duplicate_interfaces)} present in both\"\n            )\n\n        submission_kind = cleaned_data[\"submission_kind\"]\n        total_number_of_submissions_allowed = cleaned_data[\n            \"total_number_of_submissions_allowed\"\n        ]\n\n        if (\n            submission_kind == SubmissionKindChoices.ALGORITHM\n            and not total_number_of_submissions_allowed\n        ):\n            try:\n                request = ChallengeRequest.objects.get(\n                    short_name=self.instance.challenge.short_name\n                )\n                error_addition = f\"The corresponding challenge request lists the following limits: Preliminary phase: {request.phase_1_number_of_submissions_per_team * request.expected_number_of_teams} Final test phase: {request.phase_2_number_of_submissions_per_team * request.expected_number_of_teams}. Set the limits according to the phase type. \"\n            except ObjectDoesNotExist:\n                error_addition = \"There is no corresponding challenge request.\"\n            raise ValidationError(\n                \"For phases that take an algorithm as submission input, \"\n                \"the total_number_of_submissions_allowed needs to be set. \"\n                + error_addition\n            )\n\n        return cleaned_data\n\n\n@admin.register(Phase)\nclass PhaseAdmin(admin.ModelAdmin):\n    ordering = (\"challenge\",)\n    list_display = (\n        \"slug\",\n        \"title\",\n        \"challenge\",\n        \"submission_kind\",\n        \"open_for_submissions\",\n        \"submissions_open_at\",\n        \"submissions_close_at\",\n        \"submissions_limit_per_user_per_period\",\n    )\n    search_fields = (\"pk\", \"title\", \"slug\", \"challenge__short_name\")\n    list_filter = (\n        \"submission_kind\",\n        \"challenge__short_name\",\n    )\n    autocomplete_fields = (\n        \"inputs\",\n        \"outputs\",\n        \"algorithm_inputs\",\n        \"algorithm_outputs\",\n        \"archive\",\n    )\n    form = PhaseAdminForm\n\n    @admin.display(boolean=True)\n    def open_for_submissions(self, instance):\n        return instance.open_for_submissions\n\n\n@admin.action(\n    description=\"Reevaluate selected submissions\",\n    permissions=(\"change\",),\n)\ndef reevaluate_submissions(modeladmin, request, queryset):\n    \"\"\"Creates a new evaluation for an existing submission\"\"\"\n    for submission in queryset:\n        create_evaluation.apply_async(\n            kwargs={\"submission_pk\": str(submission.pk)}\n        )\n\n\n@admin.register(Submission)\nclass SubmissionAdmin(admin.ModelAdmin):\n    ordering = (\"-created\",)\n    list_display = (\"pk\", \"created\", \"phase\", \"creator\")\n    list_filter = (\"phase__challenge__short_name\",)\n    search_fields = (\"pk\", \"creator__username\", \"phase__slug\")\n    readonly_fields = (\n        \"creator\",\n        \"phase\",\n        \"predictions_file\",\n        \"algorithm_image\",\n    )\n    actions = (reevaluate_submissions,)\n\n\n@admin.register(Evaluation)\nclass EvaluationAdmin(admin.ModelAdmin):\n    ordering = (\"-created\",)\n    list_display = (\"pk\", \"created\", \"submission\", \"status\", \"error_message\")\n    list_filter = (\"submission__phase__challenge__short_name\", \"status\")\n    list_select_related = (\n        \"submission__phase__challenge\",\n        \"submission__creator\",\n    )\n    search_fields = (\n        \"pk\",\n        \"submission__pk\",\n        \"submission__phase__challenge__short_name\",\n        \"submission__creator__username\",\n    )\n    readonly_fields = (\n        \"status\",\n        \"submission\",\n        \"method\",\n        \"inputs\",\n        \"outputs\",\n        \"attempt\",\n        \"stdout\",\n        \"stderr\",\n        \"error_message\",\n        \"input_prefixes\",\n        \"task_on_success\",\n        \"task_on_failure\",\n        \"runtime_metrics\",\n    )\n    actions = (requeue_jobs, cancel_jobs, deprovision_jobs)\n\n\nadmin.site.register(PhaseUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(PhaseGroupObjectPermission, GroupObjectPermissionAdmin)\nadmin.site.register(Method, ComponentImageAdmin)\nadmin.site.register(MethodUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(MethodGroupObjectPermission, GroupObjectPermissionAdmin)\nadmin.site.register(SubmissionUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(\n    SubmissionGroupObjectPermission, GroupObjectPermissionAdmin\n)\nadmin.site.register(EvaluationUserObjectPermission, UserObjectPermissionAdmin)\nadmin.site.register(\n    EvaluationGroupObjectPermission, GroupObjectPermissionAdmin\n)\n", "path": "app/grandchallenge/evaluation/admin.py"}]}