We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Reduce database calls for `update_challenge_results_cache()`


--- 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/challenges/tasks.py`
Content:
```
1 from celery import shared_task
2 from django.core.mail import mail_managers
3 from requests import exceptions, get
4 
5 from grandchallenge.challenges.models import Challenge, ExternalChallenge
6 from grandchallenge.evaluation.models import Evaluation
7 from grandchallenge.subdomains.utils import reverse
8 
9 
10 @shared_task
11 def update_challenge_results_cache():
12     for c in Challenge.objects.all():
13         kwargs = {
14             "cached_num_participants": c.participants_group.user_set.all().count()
15         }
16 
17         challenge_results = Evaluation.objects.filter(
18             submission__phase__challenge=c, published=True
19         ).order_by("-created")
20 
21         try:
22             kwargs.update(
23                 {
24                     "cached_num_results": challenge_results.count(),
25                     "cached_latest_result": challenge_results.first().created,
26                 }
27             )
28         except AttributeError:
29             # No results for this challenge
30             kwargs.update(
31                 {"cached_num_results": 0, "cached_latest_result": None}
32             )
33 
34         Challenge.objects.filter(pk=c.pk).update(**kwargs)
35 
36 
37 @shared_task
38 def check_external_challenge_urls():
39     """
40     Checks that all external challenge urls are reachable.
41 
42     Emails the managers if any of the challenges are not.
43     """
44     challenges = ExternalChallenge.objects.filter(hidden=False)
45     errors = []
46 
47     for challenge in challenges:
48         try:
49             url = challenge.homepage
50             if not url.startswith("http"):
51                 url = "http://" + url
52             r = get(url, timeout=60)
53             # raise an exception when we receive a http error (e.g., 404)
54             r.raise_for_status()
55         except exceptions.RequestException as err:
56             update_url = reverse(
57                 "challenges:external-update",
58                 kwargs={"short_name": challenge.short_name},
59             )
60             errors.append(
61                 f"Error when trying to access '{challenge}': {err}. You can "
62                 f"update it here: {update_url}"
63             )
64 
65     if errors:
66         mail_managers(
67             subject=f"Unreachable external challenges ({len(errors)})",
68             message="\n\n".join(errors),
69         )
70
```
--- 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()

```

diff --git a/app/grandchallenge/challenges/tasks.py b/app/grandchallenge/challenges/tasks.py
--- a/app/grandchallenge/challenges/tasks.py
+++ b/app/grandchallenge/challenges/tasks.py
@@ -1,5 +1,7 @@
 from celery import shared_task
+from django.contrib.auth import get_user_model
 from django.core.mail import mail_managers
+from django.db.models import Count, Max
 from requests import exceptions, get
 
 from grandchallenge.challenges.models import Challenge, ExternalChallenge
@@ -9,29 +11,47 @@
 
 @shared_task
 def update_challenge_results_cache():
-    for c in Challenge.objects.all():
-        kwargs = {
-            "cached_num_participants": c.participants_group.user_set.all().count()
-        }
-
-        challenge_results = Evaluation.objects.filter(
-            submission__phase__challenge=c, published=True
-        ).order_by("-created")
+    challenges = Challenge.objects.all()
+    evaluation_info = (
+        Evaluation.objects.filter(published=True)
+        .values("submission__phase__challenge_id")
+        .annotate(
+            cached_num_results=Count("submission__phase__challenge_id"),
+            cached_latest_result=Max("created"),
+        )
+    )
+    evaluation_info_by_challenge = {
+        str(v["submission__phase__challenge_id"]): v for v in evaluation_info
+    }
+    participant_counts = (
+        get_user_model()
+        .objects.values("groups__participants_of_challenge")
+        .annotate(cached_num_participants=Count("pk"))
+    )
+    participant_counts_by_challenge = {
+        str(v["groups__participants_of_challenge"]): v
+        for v in participant_counts
+    }
 
-        try:
-            kwargs.update(
-                {
-                    "cached_num_results": challenge_results.count(),
-                    "cached_latest_result": challenge_results.first().created,
-                }
-            )
-        except AttributeError:
-            # No results for this challenge
-            kwargs.update(
-                {"cached_num_results": 0, "cached_latest_result": None}
-            )
+    for c in challenges:
+        c.cached_num_results = evaluation_info_by_challenge.get(
+            str(c.pk), {}
+        ).get("cached_num_results", 0)
+        c.cached_latest_result = evaluation_info_by_challenge.get(
+            str(c.pk), {}
+        ).get("cached_latest_result", None)
+        c.cached_num_participants = participant_counts_by_challenge.get(
+            str(c.pk), {}
+        ).get("cached_num_participants", 0)
 
-        Challenge.objects.filter(pk=c.pk).update(**kwargs)
+    Challenge.objects.bulk_update(
+        challenges,
+        [
+            "cached_num_results",
+            "cached_num_participants",
+            "cached_latest_result",
+        ],
+    )
 
 
 @shared_task

{"golden_diff": "diff --git a/app/grandchallenge/challenges/tasks.py b/app/grandchallenge/challenges/tasks.py\n--- a/app/grandchallenge/challenges/tasks.py\n+++ b/app/grandchallenge/challenges/tasks.py\n@@ -1,5 +1,7 @@\n from celery import shared_task\n+from django.contrib.auth import get_user_model\n from django.core.mail import mail_managers\n+from django.db.models import Count, Max\n from requests import exceptions, get\n \n from grandchallenge.challenges.models import Challenge, ExternalChallenge\n@@ -9,29 +11,47 @@\n \n @shared_task\n def update_challenge_results_cache():\n-    for c in Challenge.objects.all():\n-        kwargs = {\n-            \"cached_num_participants\": c.participants_group.user_set.all().count()\n-        }\n-\n-        challenge_results = Evaluation.objects.filter(\n-            submission__phase__challenge=c, published=True\n-        ).order_by(\"-created\")\n+    challenges = Challenge.objects.all()\n+    evaluation_info = (\n+        Evaluation.objects.filter(published=True)\n+        .values(\"submission__phase__challenge_id\")\n+        .annotate(\n+            cached_num_results=Count(\"submission__phase__challenge_id\"),\n+            cached_latest_result=Max(\"created\"),\n+        )\n+    )\n+    evaluation_info_by_challenge = {\n+        str(v[\"submission__phase__challenge_id\"]): v for v in evaluation_info\n+    }\n+    participant_counts = (\n+        get_user_model()\n+        .objects.values(\"groups__participants_of_challenge\")\n+        .annotate(cached_num_participants=Count(\"pk\"))\n+    )\n+    participant_counts_by_challenge = {\n+        str(v[\"groups__participants_of_challenge\"]): v\n+        for v in participant_counts\n+    }\n \n-        try:\n-            kwargs.update(\n-                {\n-                    \"cached_num_results\": challenge_results.count(),\n-                    \"cached_latest_result\": challenge_results.first().created,\n-                }\n-            )\n-        except AttributeError:\n-            # No results for this challenge\n-            kwargs.update(\n-                {\"cached_num_results\": 0, \"cached_latest_result\": None}\n-            )\n+    for c in challenges:\n+        c.cached_num_results = evaluation_info_by_challenge.get(\n+            str(c.pk), {}\n+        ).get(\"cached_num_results\", 0)\n+        c.cached_latest_result = evaluation_info_by_challenge.get(\n+            str(c.pk), {}\n+        ).get(\"cached_latest_result\", None)\n+        c.cached_num_participants = participant_counts_by_challenge.get(\n+            str(c.pk), {}\n+        ).get(\"cached_num_participants\", 0)\n \n-        Challenge.objects.filter(pk=c.pk).update(**kwargs)\n+    Challenge.objects.bulk_update(\n+        challenges,\n+        [\n+            \"cached_num_results\",\n+            \"cached_num_participants\",\n+            \"cached_latest_result\",\n+        ],\n+    )\n \n \n @shared_task\n", "issue": "Reduce database calls for `update_challenge_results_cache()`\n\n", "before_files": [{"content": "from celery import shared_task\nfrom django.core.mail import mail_managers\nfrom requests import exceptions, get\n\nfrom grandchallenge.challenges.models import Challenge, ExternalChallenge\nfrom grandchallenge.evaluation.models import Evaluation\nfrom grandchallenge.subdomains.utils import reverse\n\n\n@shared_task\ndef update_challenge_results_cache():\n    for c in Challenge.objects.all():\n        kwargs = {\n            \"cached_num_participants\": c.participants_group.user_set.all().count()\n        }\n\n        challenge_results = Evaluation.objects.filter(\n            submission__phase__challenge=c, published=True\n        ).order_by(\"-created\")\n\n        try:\n            kwargs.update(\n                {\n                    \"cached_num_results\": challenge_results.count(),\n                    \"cached_latest_result\": challenge_results.first().created,\n                }\n            )\n        except AttributeError:\n            # No results for this challenge\n            kwargs.update(\n                {\"cached_num_results\": 0, \"cached_latest_result\": None}\n            )\n\n        Challenge.objects.filter(pk=c.pk).update(**kwargs)\n\n\n@shared_task\ndef check_external_challenge_urls():\n    \"\"\"\n    Checks that all external challenge urls are reachable.\n\n    Emails the managers if any of the challenges are not.\n    \"\"\"\n    challenges = ExternalChallenge.objects.filter(hidden=False)\n    errors = []\n\n    for challenge in challenges:\n        try:\n            url = challenge.homepage\n            if not url.startswith(\"http\"):\n                url = \"http://\" + url\n            r = get(url, timeout=60)\n            # raise an exception when we receive a http error (e.g., 404)\n            r.raise_for_status()\n        except exceptions.RequestException as err:\n            update_url = reverse(\n                \"challenges:external-update\",\n                kwargs={\"short_name\": challenge.short_name},\n            )\n            errors.append(\n                f\"Error when trying to access '{challenge}': {err}. You can \"\n                f\"update it here: {update_url}\"\n            )\n\n    if errors:\n        mail_managers(\n            subject=f\"Unreachable external challenges ({len(errors)})\",\n            message=\"\\n\\n\".join(errors),\n        )\n", "path": "app/grandchallenge/challenges/tasks.py"}], "after_files": [{"content": "from celery import shared_task\nfrom django.contrib.auth import get_user_model\nfrom django.core.mail import mail_managers\nfrom django.db.models import Count, Max\nfrom requests import exceptions, get\n\nfrom grandchallenge.challenges.models import Challenge, ExternalChallenge\nfrom grandchallenge.evaluation.models import Evaluation\nfrom grandchallenge.subdomains.utils import reverse\n\n\n@shared_task\ndef update_challenge_results_cache():\n    challenges = Challenge.objects.all()\n    evaluation_info = (\n        Evaluation.objects.filter(published=True)\n        .values(\"submission__phase__challenge_id\")\n        .annotate(\n            cached_num_results=Count(\"submission__phase__challenge_id\"),\n            cached_latest_result=Max(\"created\"),\n        )\n    )\n    evaluation_info_by_challenge = {\n        str(v[\"submission__phase__challenge_id\"]): v for v in evaluation_info\n    }\n    participant_counts = (\n        get_user_model()\n        .objects.values(\"groups__participants_of_challenge\")\n        .annotate(cached_num_participants=Count(\"pk\"))\n    )\n    participant_counts_by_challenge = {\n        str(v[\"groups__participants_of_challenge\"]): v\n        for v in participant_counts\n    }\n\n    for c in challenges:\n        c.cached_num_results = evaluation_info_by_challenge.get(\n            str(c.pk), {}\n        ).get(\"cached_num_results\", 0)\n        c.cached_latest_result = evaluation_info_by_challenge.get(\n            str(c.pk), {}\n        ).get(\"cached_latest_result\", None)\n        c.cached_num_participants = participant_counts_by_challenge.get(\n            str(c.pk), {}\n        ).get(\"cached_num_participants\", 0)\n\n    Challenge.objects.bulk_update(\n        challenges,\n        [\n            \"cached_num_results\",\n            \"cached_num_participants\",\n            \"cached_latest_result\",\n        ],\n    )\n\n\n@shared_task\ndef check_external_challenge_urls():\n    \"\"\"\n    Checks that all external challenge urls are reachable.\n\n    Emails the managers if any of the challenges are not.\n    \"\"\"\n    challenges = ExternalChallenge.objects.filter(hidden=False)\n    errors = []\n\n    for challenge in challenges:\n        try:\n            url = challenge.homepage\n            if not url.startswith(\"http\"):\n                url = \"http://\" + url\n            r = get(url, timeout=60)\n            # raise an exception when we receive a http error (e.g., 404)\n            r.raise_for_status()\n        except exceptions.RequestException as err:\n            update_url = reverse(\n                \"challenges:external-update\",\n                kwargs={\"short_name\": challenge.short_name},\n            )\n            errors.append(\n                f\"Error when trying to access '{challenge}': {err}. You can \"\n                f\"update it here: {update_url}\"\n            )\n\n    if errors:\n        mail_managers(\n            subject=f\"Unreachable external challenges ({len(errors)})\",\n            message=\"\\n\\n\".join(errors),\n        )\n", "path": "app/grandchallenge/challenges/tasks.py"}]}