We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove usage of jsdelivr and include scss on admin page
Follow up from #2870 

--- 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/core/widgets.py`
Content:
```
1 from django import forms
2 from django.template.loader import render_to_string
3 from markdownx.widgets import AdminMarkdownxWidget, MarkdownxWidget
4 
5 
6 class JSONEditorWidget(forms.Textarea):
7     template_name = "jsoneditor/jsoneditor_widget.html"
8 
9     def __init__(self, schema=None, attrs=None):
10         super().__init__(attrs)
11         self.schema = schema
12 
13     def get_context(self, name, value, attrs):
14         context = super().get_context(name, value, attrs)
15         context.update({"schema": self.schema})
16         return context
17 
18     class Media:
19         css = {"all": ("vendored/jsoneditor/jsoneditor.min.css",)}
20         js = ("vendored/jsoneditor/jsoneditor.min.js",)
21 
22 
23 class ColorEditorWidget(forms.TextInput):
24     """
25     Widget that uses the vendored jscolor for editing a color.
26 
27     Parameters
28     ----------
29     format
30         Specify the color format by adding a format keyword:
31         >>> ColorEditorWidget(format="hex")
32 
33         Options include "auto", "any", "hex", "hexa", "rgb", "rgba".
34         See the jscolor (https://jscolor.com/docs/) for details.
35     """
36 
37     template_name = "coloreditor/coloreditor_widget.html"
38 
39     class Media:
40         js = (
41             "vendored/jscolor/jscolor.min.js",
42             "js/coloreditor_widget.js",
43         )
44 
45     def __init__(self, attrs=None, format="auto", placeholder=None):
46         super().__init__(attrs)
47         self.format = format
48         self.placeholder = placeholder
49 
50     def get_context(self, name, value, attrs=None):
51         context = super().get_context(name, value, attrs)
52         context["widget"].update(
53             {
54                 "placeholder": self.placeholder,
55                 "format": self.format,
56             }
57         )
58         return context
59 
60     def render(self, name, value, attrs=None, renderer=None):
61         return render_to_string(
62             self.template_name, self.get_context(name, value, attrs)
63         )
64 
65 
66 class MarkdownEditorWidget(MarkdownxWidget):
67     @property
68     def media(self):
69         return forms.Media(
70             js=(
71                 "js/markdownx.js",
72                 "vendored/@github/markdown-toolbar-element/dist/index.umd.js",
73             )
74         )
75 
76 
77 class MarkdownEditorAdminWidget(AdminMarkdownxWidget):
78     @property
79     def media(self):
80         return forms.Media(
81             css={
82                 "all": [
83                     *AdminMarkdownxWidget.Media.css["all"],
84                     "vendor/css/base.min.css",
85                     "vendored/font-awesome/css/all.min.css",
86                     "css/markdown.css",
87                 ]
88             },
89             js=[
90                 "js/markdownx.js",
91                 "vendored/@github/markdown-toolbar-element/dist/index.umd.js",
92                 "vendored/jquery/jquery.min.js",
93                 "vendored/bootstrap/js/bootstrap.bundle.min.js",
94             ],
95         )
96
```

Path: `app/grandchallenge/charts/specs.py`
Content:
```
1 def bar(*, values, lookup, title):
2     chart = {
3         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
4         "width": "container",
5         "padding": 0,
6         "title": title,
7         "data": {"values": values},
8         "mark": "bar",
9         "encoding": {
10             "x": {
11                 "field": "Month",
12                 "type": "temporal",
13                 "timeUnit": "yearmonth",
14             },
15             "y": {
16                 "field": lookup,
17                 "type": "quantitative",
18             },
19             "tooltip": [
20                 {
21                     "field": "Month",
22                     "type": "temporal",
23                     "timeUnit": "yearmonth",
24                 },
25                 {"field": lookup, "type": "quantitative"},
26             ],
27         },
28     }
29 
30     totals = sum(datum[lookup] for datum in values)
31 
32     return {"chart": chart, "totals": totals}
33 
34 
35 def stacked_bar(*, values, lookup, title, facet, domain):
36     domain = dict(domain)
37 
38     totals = {str(d): 0 for d in domain.values()}
39     for datum in values:
40         datum[facet] = domain[datum[facet]]
41         totals[str(datum[facet])] += datum[lookup]
42 
43     chart = {
44         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
45         "width": "container",
46         "padding": 0,
47         "title": title,
48         "data": {"values": values},
49         "mark": "bar",
50         "encoding": {
51             "x": {
52                 "field": "Month",
53                 "type": "temporal",
54                 "timeUnit": "yearmonth",
55             },
56             "y": {
57                 "field": lookup,
58                 "type": "quantitative",
59                 "stack": True,
60             },
61             "tooltip": [
62                 {
63                     "field": "Month",
64                     "type": "temporal",
65                     "timeUnit": "yearmonth",
66                 },
67                 {"field": facet, "type": "nominal"},
68                 {"field": lookup, "type": "quantitative"},
69             ],
70             "color": {
71                 "field": facet,
72                 "scale": {
73                     "domain": list(domain.values()),
74                 },
75                 "type": "nominal",
76             },
77         },
78     }
79 
80     return {"chart": chart, "totals": totals}
81 
82 
83 def horizontal_bar(*, values, lookup, title):
84     url_lookup = "absolute_url"
85     challenge_name_lookup = "short_name"
86     return {
87         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
88         "width": "container",
89         "padding": 0,
90         "data": {"values": values},
91         "mark": "bar",
92         "encoding": {
93             "color": {
94                 "field": lookup,
95                 "type": "nominal",
96                 "legend": None,
97                 "scale": {"scheme": {"name": "viridis", "extent": [0, 1]}},
98             },
99             "tooltip": [
100                 {
101                     "field": challenge_name_lookup,
102                     "type": "nominal",
103                     "title": "Challenge",
104                 },
105                 {
106                     "field": lookup,
107                     "type": "quantitative",
108                     "title": title,
109                     "format": ".0f",
110                 },
111             ],
112             "y": {
113                 "field": challenge_name_lookup,
114                 "type": "nominal",
115                 "axis": {"labelAngle": 0},
116                 "title": None,
117                 "sort": "-x",
118             },
119             "x": {
120                 "field": lookup,
121                 "type": "quantitative",
122                 "title": title,
123                 "axis": {"tickMinStep": "1", "format": ".0f"},
124             },
125             "href": {"field": url_lookup, "type": "nominal"},
126         },
127     }
128 
129 
130 def world_map(*, values):
131     return {
132         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
133         "width": "container",
134         "height": "container",
135         "padding": 0,
136         "view": {"stroke": "transparent", "fill": "#c9eeff"},
137         "data": {
138             "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
139             "format": {"type": "topojson", "feature": "countries"},
140         },
141         "transform": [
142             {
143                 "lookup": "id",
144                 "from": {
145                     "data": {"values": values},
146                     "key": "id",
147                     "fields": ["participants"],
148                 },
149                 "default": 0.01,
150             }
151         ],
152         "projection": {"type": "equalEarth"},
153         "mark": {
154             "type": "geoshape",
155             "stroke": "#757575",
156             "strokeWidth": 0.5,
157         },
158         "encoding": {
159             "color": {
160                 "field": "participants",
161                 "type": "quantitative",
162                 "scale": {
163                     "scheme": "viridis",
164                     "domainMin": 1,
165                     "type": "log",
166                 },
167                 "legend": None,
168                 "condition": {
169                     "test": "datum['participants'] === 0.01",
170                     "value": "#eee",
171                 },
172             },
173             "tooltip": [
174                 {
175                     "field": "properties.name",
176                     "type": "nominal",
177                     "title": "Location",
178                 },
179                 {
180                     "field": "participants",
181                     "type": "quantitative",
182                     "title": "Participants",
183                     "format": ".0f",
184                 },
185             ],
186         },
187     }
188 
189 
190 def components_line(*, values, title, cpu_limit, tooltip):
191     return {
192         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
193         "width": "container",
194         "padding": 0,
195         "title": title,
196         "data": {"values": values},
197         "layer": [
198             {
199                 "transform": [
200                     {"calculate": "100*datum.Percent", "as": "Percent100"},
201                 ],
202                 "encoding": {
203                     "x": {
204                         "timeUnit": "hoursminutesseconds",
205                         "field": "Timestamp",
206                         "title": "Local Time / HH:MM:SS",
207                     },
208                     "y": {
209                         "field": "Percent100",
210                         "type": "quantitative",
211                         "title": "Utilization / %",
212                     },
213                     "color": {"field": "Metric", "type": "nominal"},
214                 },
215                 "layer": [
216                     {"mark": "line"},
217                     {
218                         "transform": [
219                             {"filter": {"param": "hover", "empty": False}}
220                         ],
221                         "mark": "point",
222                     },
223                 ],
224             },
225             {
226                 "transform": [
227                     {
228                         "pivot": "Metric",
229                         "value": "Percent",
230                         "groupby": ["Timestamp"],
231                     }
232                 ],
233                 "mark": "rule",
234                 "encoding": {
235                     "opacity": {
236                         "condition": {
237                             "value": 0.3,
238                             "param": "hover",
239                             "empty": False,
240                         },
241                         "value": 0,
242                     },
243                     "tooltip": tooltip,
244                     "x": {
245                         "timeUnit": "hoursminutesseconds",
246                         "field": "Timestamp",
247                         "title": "Local Time / HH:MM:SS",
248                     },
249                 },
250                 "params": [
251                     {
252                         "name": "hover",
253                         "select": {
254                             "type": "point",
255                             "fields": ["Timestamp"],
256                             "nearest": True,
257                             "on": "mouseover",
258                             "clear": "mouseout",
259                         },
260                     }
261                 ],
262             },
263             {
264                 "data": {"values": [{}]},
265                 "mark": {"type": "rule", "strokeDash": [8, 8]},
266                 "encoding": {"y": {"datum": cpu_limit}},
267             },
268             {
269                 "data": {"values": [{}]},
270                 "mark": {"type": "text", "baseline": "line-bottom"},
271                 "encoding": {
272                     "text": {"datum": "CPU Utilization Limit"},
273                     "y": {"datum": cpu_limit},
274                 },
275             },
276             {
277                 "data": {"values": [{}]},
278                 "mark": {"type": "rule", "strokeDash": [8, 8]},
279                 "encoding": {"y": {"datum": 100}},
280             },
281             {
282                 "data": {"values": [{}]},
283                 "mark": {"type": "text", "baseline": "line-bottom"},
284                 "encoding": {
285                     "text": {
286                         "datum": "Memory / GPU / GPU Memory Utilization Limit"
287                     },
288                     "y": {"datum": 100},
289                 },
290             },
291         ],
292     }
293
```
--- 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/charts/specs.py b/app/grandchallenge/charts/specs.py
--- a/app/grandchallenge/charts/specs.py
+++ b/app/grandchallenge/charts/specs.py
@@ -1,3 +1,6 @@
+from django.templatetags.static import static
+
+
 def bar(*, values, lookup, title):
     chart = {
         "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
@@ -135,7 +138,7 @@
         "padding": 0,
         "view": {"stroke": "transparent", "fill": "#c9eeff"},
         "data": {
-            "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
+            "url": static("vendored/world-atlas/countries-110m.json"),
             "format": {"type": "topojson", "feature": "countries"},
         },
         "transform": [
diff --git a/app/grandchallenge/core/widgets.py b/app/grandchallenge/core/widgets.py
--- a/app/grandchallenge/core/widgets.py
+++ b/app/grandchallenge/core/widgets.py
@@ -75,15 +75,14 @@
 
 
 class MarkdownEditorAdminWidget(AdminMarkdownxWidget):
+    template_name = "markdownx/admin_widget.html"
+
     @property
     def media(self):
         return forms.Media(
             css={
                 "all": [
                     *AdminMarkdownxWidget.Media.css["all"],
-                    "vendor/css/base.min.css",
-                    "vendored/font-awesome/css/all.min.css",
-                    "css/markdown.css",
                 ]
             },
             js=[

{"golden_diff": "diff --git a/app/grandchallenge/charts/specs.py b/app/grandchallenge/charts/specs.py\n--- a/app/grandchallenge/charts/specs.py\n+++ b/app/grandchallenge/charts/specs.py\n@@ -1,3 +1,6 @@\n+from django.templatetags.static import static\n+\n+\n def bar(*, values, lookup, title):\n     chart = {\n         \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n@@ -135,7 +138,7 @@\n         \"padding\": 0,\n         \"view\": {\"stroke\": \"transparent\", \"fill\": \"#c9eeff\"},\n         \"data\": {\n-            \"url\": \"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json\",\n+            \"url\": static(\"vendored/world-atlas/countries-110m.json\"),\n             \"format\": {\"type\": \"topojson\", \"feature\": \"countries\"},\n         },\n         \"transform\": [\ndiff --git a/app/grandchallenge/core/widgets.py b/app/grandchallenge/core/widgets.py\n--- a/app/grandchallenge/core/widgets.py\n+++ b/app/grandchallenge/core/widgets.py\n@@ -75,15 +75,14 @@\n \n \n class MarkdownEditorAdminWidget(AdminMarkdownxWidget):\n+    template_name = \"markdownx/admin_widget.html\"\n+\n     @property\n     def media(self):\n         return forms.Media(\n             css={\n                 \"all\": [\n                     *AdminMarkdownxWidget.Media.css[\"all\"],\n-                    \"vendor/css/base.min.css\",\n-                    \"vendored/font-awesome/css/all.min.css\",\n-                    \"css/markdown.css\",\n                 ]\n             },\n             js=[\n", "issue": "Remove usage of jsdelivr and include scss on admin page\nFollow up from #2870 \n", "before_files": [{"content": "from django import forms\nfrom django.template.loader import render_to_string\nfrom markdownx.widgets import AdminMarkdownxWidget, MarkdownxWidget\n\n\nclass JSONEditorWidget(forms.Textarea):\n    template_name = \"jsoneditor/jsoneditor_widget.html\"\n\n    def __init__(self, schema=None, attrs=None):\n        super().__init__(attrs)\n        self.schema = schema\n\n    def get_context(self, name, value, attrs):\n        context = super().get_context(name, value, attrs)\n        context.update({\"schema\": self.schema})\n        return context\n\n    class Media:\n        css = {\"all\": (\"vendored/jsoneditor/jsoneditor.min.css\",)}\n        js = (\"vendored/jsoneditor/jsoneditor.min.js\",)\n\n\nclass ColorEditorWidget(forms.TextInput):\n    \"\"\"\n    Widget that uses the vendored jscolor for editing a color.\n\n    Parameters\n    ----------\n    format\n        Specify the color format by adding a format keyword:\n        >>> ColorEditorWidget(format=\"hex\")\n\n        Options include \"auto\", \"any\", \"hex\", \"hexa\", \"rgb\", \"rgba\".\n        See the jscolor (https://jscolor.com/docs/) for details.\n    \"\"\"\n\n    template_name = \"coloreditor/coloreditor_widget.html\"\n\n    class Media:\n        js = (\n            \"vendored/jscolor/jscolor.min.js\",\n            \"js/coloreditor_widget.js\",\n        )\n\n    def __init__(self, attrs=None, format=\"auto\", placeholder=None):\n        super().__init__(attrs)\n        self.format = format\n        self.placeholder = placeholder\n\n    def get_context(self, name, value, attrs=None):\n        context = super().get_context(name, value, attrs)\n        context[\"widget\"].update(\n            {\n                \"placeholder\": self.placeholder,\n                \"format\": self.format,\n            }\n        )\n        return context\n\n    def render(self, name, value, attrs=None, renderer=None):\n        return render_to_string(\n            self.template_name, self.get_context(name, value, attrs)\n        )\n\n\nclass MarkdownEditorWidget(MarkdownxWidget):\n    @property\n    def media(self):\n        return forms.Media(\n            js=(\n                \"js/markdownx.js\",\n                \"vendored/@github/markdown-toolbar-element/dist/index.umd.js\",\n            )\n        )\n\n\nclass MarkdownEditorAdminWidget(AdminMarkdownxWidget):\n    @property\n    def media(self):\n        return forms.Media(\n            css={\n                \"all\": [\n                    *AdminMarkdownxWidget.Media.css[\"all\"],\n                    \"vendor/css/base.min.css\",\n                    \"vendored/font-awesome/css/all.min.css\",\n                    \"css/markdown.css\",\n                ]\n            },\n            js=[\n                \"js/markdownx.js\",\n                \"vendored/@github/markdown-toolbar-element/dist/index.umd.js\",\n                \"vendored/jquery/jquery.min.js\",\n                \"vendored/bootstrap/js/bootstrap.bundle.min.js\",\n            ],\n        )\n", "path": "app/grandchallenge/core/widgets.py"}, {"content": "def bar(*, values, lookup, title):\n    chart = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": \"Month\",\n                \"type\": \"temporal\",\n                \"timeUnit\": \"yearmonth\",\n            },\n            \"y\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"Month\",\n                    \"type\": \"temporal\",\n                    \"timeUnit\": \"yearmonth\",\n                },\n                {\"field\": lookup, \"type\": \"quantitative\"},\n            ],\n        },\n    }\n\n    totals = sum(datum[lookup] for datum in values)\n\n    return {\"chart\": chart, \"totals\": totals}\n\n\ndef stacked_bar(*, values, lookup, title, facet, domain):\n    domain = dict(domain)\n\n    totals = {str(d): 0 for d in domain.values()}\n    for datum in values:\n        datum[facet] = domain[datum[facet]]\n        totals[str(datum[facet])] += datum[lookup]\n\n    chart = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": \"Month\",\n                \"type\": \"temporal\",\n                \"timeUnit\": \"yearmonth\",\n            },\n            \"y\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n                \"stack\": True,\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"Month\",\n                    \"type\": \"temporal\",\n                    \"timeUnit\": \"yearmonth\",\n                },\n                {\"field\": facet, \"type\": \"nominal\"},\n                {\"field\": lookup, \"type\": \"quantitative\"},\n            ],\n            \"color\": {\n                \"field\": facet,\n                \"scale\": {\n                    \"domain\": list(domain.values()),\n                },\n                \"type\": \"nominal\",\n            },\n        },\n    }\n\n    return {\"chart\": chart, \"totals\": totals}\n\n\ndef horizontal_bar(*, values, lookup, title):\n    url_lookup = \"absolute_url\"\n    challenge_name_lookup = \"short_name\"\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"color\": {\n                \"field\": lookup,\n                \"type\": \"nominal\",\n                \"legend\": None,\n                \"scale\": {\"scheme\": {\"name\": \"viridis\", \"extent\": [0, 1]}},\n            },\n            \"tooltip\": [\n                {\n                    \"field\": challenge_name_lookup,\n                    \"type\": \"nominal\",\n                    \"title\": \"Challenge\",\n                },\n                {\n                    \"field\": lookup,\n                    \"type\": \"quantitative\",\n                    \"title\": title,\n                    \"format\": \".0f\",\n                },\n            ],\n            \"y\": {\n                \"field\": challenge_name_lookup,\n                \"type\": \"nominal\",\n                \"axis\": {\"labelAngle\": 0},\n                \"title\": None,\n                \"sort\": \"-x\",\n            },\n            \"x\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n                \"title\": title,\n                \"axis\": {\"tickMinStep\": \"1\", \"format\": \".0f\"},\n            },\n            \"href\": {\"field\": url_lookup, \"type\": \"nominal\"},\n        },\n    }\n\n\ndef world_map(*, values):\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"height\": \"container\",\n        \"padding\": 0,\n        \"view\": {\"stroke\": \"transparent\", \"fill\": \"#c9eeff\"},\n        \"data\": {\n            \"url\": \"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json\",\n            \"format\": {\"type\": \"topojson\", \"feature\": \"countries\"},\n        },\n        \"transform\": [\n            {\n                \"lookup\": \"id\",\n                \"from\": {\n                    \"data\": {\"values\": values},\n                    \"key\": \"id\",\n                    \"fields\": [\"participants\"],\n                },\n                \"default\": 0.01,\n            }\n        ],\n        \"projection\": {\"type\": \"equalEarth\"},\n        \"mark\": {\n            \"type\": \"geoshape\",\n            \"stroke\": \"#757575\",\n            \"strokeWidth\": 0.5,\n        },\n        \"encoding\": {\n            \"color\": {\n                \"field\": \"participants\",\n                \"type\": \"quantitative\",\n                \"scale\": {\n                    \"scheme\": \"viridis\",\n                    \"domainMin\": 1,\n                    \"type\": \"log\",\n                },\n                \"legend\": None,\n                \"condition\": {\n                    \"test\": \"datum['participants'] === 0.01\",\n                    \"value\": \"#eee\",\n                },\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"properties.name\",\n                    \"type\": \"nominal\",\n                    \"title\": \"Location\",\n                },\n                {\n                    \"field\": \"participants\",\n                    \"type\": \"quantitative\",\n                    \"title\": \"Participants\",\n                    \"format\": \".0f\",\n                },\n            ],\n        },\n    }\n\n\ndef components_line(*, values, title, cpu_limit, tooltip):\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"layer\": [\n            {\n                \"transform\": [\n                    {\"calculate\": \"100*datum.Percent\", \"as\": \"Percent100\"},\n                ],\n                \"encoding\": {\n                    \"x\": {\n                        \"timeUnit\": \"hoursminutesseconds\",\n                        \"field\": \"Timestamp\",\n                        \"title\": \"Local Time / HH:MM:SS\",\n                    },\n                    \"y\": {\n                        \"field\": \"Percent100\",\n                        \"type\": \"quantitative\",\n                        \"title\": \"Utilization / %\",\n                    },\n                    \"color\": {\"field\": \"Metric\", \"type\": \"nominal\"},\n                },\n                \"layer\": [\n                    {\"mark\": \"line\"},\n                    {\n                        \"transform\": [\n                            {\"filter\": {\"param\": \"hover\", \"empty\": False}}\n                        ],\n                        \"mark\": \"point\",\n                    },\n                ],\n            },\n            {\n                \"transform\": [\n                    {\n                        \"pivot\": \"Metric\",\n                        \"value\": \"Percent\",\n                        \"groupby\": [\"Timestamp\"],\n                    }\n                ],\n                \"mark\": \"rule\",\n                \"encoding\": {\n                    \"opacity\": {\n                        \"condition\": {\n                            \"value\": 0.3,\n                            \"param\": \"hover\",\n                            \"empty\": False,\n                        },\n                        \"value\": 0,\n                    },\n                    \"tooltip\": tooltip,\n                    \"x\": {\n                        \"timeUnit\": \"hoursminutesseconds\",\n                        \"field\": \"Timestamp\",\n                        \"title\": \"Local Time / HH:MM:SS\",\n                    },\n                },\n                \"params\": [\n                    {\n                        \"name\": \"hover\",\n                        \"select\": {\n                            \"type\": \"point\",\n                            \"fields\": [\"Timestamp\"],\n                            \"nearest\": True,\n                            \"on\": \"mouseover\",\n                            \"clear\": \"mouseout\",\n                        },\n                    }\n                ],\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"rule\", \"strokeDash\": [8, 8]},\n                \"encoding\": {\"y\": {\"datum\": cpu_limit}},\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"text\", \"baseline\": \"line-bottom\"},\n                \"encoding\": {\n                    \"text\": {\"datum\": \"CPU Utilization Limit\"},\n                    \"y\": {\"datum\": cpu_limit},\n                },\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"rule\", \"strokeDash\": [8, 8]},\n                \"encoding\": {\"y\": {\"datum\": 100}},\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"text\", \"baseline\": \"line-bottom\"},\n                \"encoding\": {\n                    \"text\": {\n                        \"datum\": \"Memory / GPU / GPU Memory Utilization Limit\"\n                    },\n                    \"y\": {\"datum\": 100},\n                },\n            },\n        ],\n    }\n", "path": "app/grandchallenge/charts/specs.py"}], "after_files": [{"content": "from django import forms\nfrom django.template.loader import render_to_string\nfrom markdownx.widgets import AdminMarkdownxWidget, MarkdownxWidget\n\n\nclass JSONEditorWidget(forms.Textarea):\n    template_name = \"jsoneditor/jsoneditor_widget.html\"\n\n    def __init__(self, schema=None, attrs=None):\n        super().__init__(attrs)\n        self.schema = schema\n\n    def get_context(self, name, value, attrs):\n        context = super().get_context(name, value, attrs)\n        context.update({\"schema\": self.schema})\n        return context\n\n    class Media:\n        css = {\"all\": (\"vendored/jsoneditor/jsoneditor.min.css\",)}\n        js = (\"vendored/jsoneditor/jsoneditor.min.js\",)\n\n\nclass ColorEditorWidget(forms.TextInput):\n    \"\"\"\n    Widget that uses the vendored jscolor for editing a color.\n\n    Parameters\n    ----------\n    format\n        Specify the color format by adding a format keyword:\n        >>> ColorEditorWidget(format=\"hex\")\n\n        Options include \"auto\", \"any\", \"hex\", \"hexa\", \"rgb\", \"rgba\".\n        See the jscolor (https://jscolor.com/docs/) for details.\n    \"\"\"\n\n    template_name = \"coloreditor/coloreditor_widget.html\"\n\n    class Media:\n        js = (\n            \"vendored/jscolor/jscolor.min.js\",\n            \"js/coloreditor_widget.js\",\n        )\n\n    def __init__(self, attrs=None, format=\"auto\", placeholder=None):\n        super().__init__(attrs)\n        self.format = format\n        self.placeholder = placeholder\n\n    def get_context(self, name, value, attrs=None):\n        context = super().get_context(name, value, attrs)\n        context[\"widget\"].update(\n            {\n                \"placeholder\": self.placeholder,\n                \"format\": self.format,\n            }\n        )\n        return context\n\n    def render(self, name, value, attrs=None, renderer=None):\n        return render_to_string(\n            self.template_name, self.get_context(name, value, attrs)\n        )\n\n\nclass MarkdownEditorWidget(MarkdownxWidget):\n    @property\n    def media(self):\n        return forms.Media(\n            js=(\n                \"js/markdownx.js\",\n                \"vendored/@github/markdown-toolbar-element/dist/index.umd.js\",\n            )\n        )\n\n\nclass MarkdownEditorAdminWidget(AdminMarkdownxWidget):\n    template_name = \"markdownx/admin_widget.html\"\n\n    @property\n    def media(self):\n        return forms.Media(\n            css={\n                \"all\": [\n                    *AdminMarkdownxWidget.Media.css[\"all\"],\n                ]\n            },\n            js=[\n                \"js/markdownx.js\",\n                \"vendored/@github/markdown-toolbar-element/dist/index.umd.js\",\n                \"vendored/jquery/jquery.min.js\",\n                \"vendored/bootstrap/js/bootstrap.bundle.min.js\",\n            ],\n        )\n", "path": "app/grandchallenge/core/widgets.py"}, {"content": "from django.templatetags.static import static\n\n\ndef bar(*, values, lookup, title):\n    chart = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": \"Month\",\n                \"type\": \"temporal\",\n                \"timeUnit\": \"yearmonth\",\n            },\n            \"y\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"Month\",\n                    \"type\": \"temporal\",\n                    \"timeUnit\": \"yearmonth\",\n                },\n                {\"field\": lookup, \"type\": \"quantitative\"},\n            ],\n        },\n    }\n\n    totals = sum(datum[lookup] for datum in values)\n\n    return {\"chart\": chart, \"totals\": totals}\n\n\ndef stacked_bar(*, values, lookup, title, facet, domain):\n    domain = dict(domain)\n\n    totals = {str(d): 0 for d in domain.values()}\n    for datum in values:\n        datum[facet] = domain[datum[facet]]\n        totals[str(datum[facet])] += datum[lookup]\n\n    chart = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": \"Month\",\n                \"type\": \"temporal\",\n                \"timeUnit\": \"yearmonth\",\n            },\n            \"y\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n                \"stack\": True,\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"Month\",\n                    \"type\": \"temporal\",\n                    \"timeUnit\": \"yearmonth\",\n                },\n                {\"field\": facet, \"type\": \"nominal\"},\n                {\"field\": lookup, \"type\": \"quantitative\"},\n            ],\n            \"color\": {\n                \"field\": facet,\n                \"scale\": {\n                    \"domain\": list(domain.values()),\n                },\n                \"type\": \"nominal\",\n            },\n        },\n    }\n\n    return {\"chart\": chart, \"totals\": totals}\n\n\ndef horizontal_bar(*, values, lookup, title):\n    url_lookup = \"absolute_url\"\n    challenge_name_lookup = \"short_name\"\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"data\": {\"values\": values},\n        \"mark\": \"bar\",\n        \"encoding\": {\n            \"color\": {\n                \"field\": lookup,\n                \"type\": \"nominal\",\n                \"legend\": None,\n                \"scale\": {\"scheme\": {\"name\": \"viridis\", \"extent\": [0, 1]}},\n            },\n            \"tooltip\": [\n                {\n                    \"field\": challenge_name_lookup,\n                    \"type\": \"nominal\",\n                    \"title\": \"Challenge\",\n                },\n                {\n                    \"field\": lookup,\n                    \"type\": \"quantitative\",\n                    \"title\": title,\n                    \"format\": \".0f\",\n                },\n            ],\n            \"y\": {\n                \"field\": challenge_name_lookup,\n                \"type\": \"nominal\",\n                \"axis\": {\"labelAngle\": 0},\n                \"title\": None,\n                \"sort\": \"-x\",\n            },\n            \"x\": {\n                \"field\": lookup,\n                \"type\": \"quantitative\",\n                \"title\": title,\n                \"axis\": {\"tickMinStep\": \"1\", \"format\": \".0f\"},\n            },\n            \"href\": {\"field\": url_lookup, \"type\": \"nominal\"},\n        },\n    }\n\n\ndef world_map(*, values):\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"height\": \"container\",\n        \"padding\": 0,\n        \"view\": {\"stroke\": \"transparent\", \"fill\": \"#c9eeff\"},\n        \"data\": {\n            \"url\": static(\"vendored/world-atlas/countries-110m.json\"),\n            \"format\": {\"type\": \"topojson\", \"feature\": \"countries\"},\n        },\n        \"transform\": [\n            {\n                \"lookup\": \"id\",\n                \"from\": {\n                    \"data\": {\"values\": values},\n                    \"key\": \"id\",\n                    \"fields\": [\"participants\"],\n                },\n                \"default\": 0.01,\n            }\n        ],\n        \"projection\": {\"type\": \"equalEarth\"},\n        \"mark\": {\n            \"type\": \"geoshape\",\n            \"stroke\": \"#757575\",\n            \"strokeWidth\": 0.5,\n        },\n        \"encoding\": {\n            \"color\": {\n                \"field\": \"participants\",\n                \"type\": \"quantitative\",\n                \"scale\": {\n                    \"scheme\": \"viridis\",\n                    \"domainMin\": 1,\n                    \"type\": \"log\",\n                },\n                \"legend\": None,\n                \"condition\": {\n                    \"test\": \"datum['participants'] === 0.01\",\n                    \"value\": \"#eee\",\n                },\n            },\n            \"tooltip\": [\n                {\n                    \"field\": \"properties.name\",\n                    \"type\": \"nominal\",\n                    \"title\": \"Location\",\n                },\n                {\n                    \"field\": \"participants\",\n                    \"type\": \"quantitative\",\n                    \"title\": \"Participants\",\n                    \"format\": \".0f\",\n                },\n            ],\n        },\n    }\n\n\ndef components_line(*, values, title, cpu_limit, tooltip):\n    return {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n        \"width\": \"container\",\n        \"padding\": 0,\n        \"title\": title,\n        \"data\": {\"values\": values},\n        \"layer\": [\n            {\n                \"transform\": [\n                    {\"calculate\": \"100*datum.Percent\", \"as\": \"Percent100\"},\n                ],\n                \"encoding\": {\n                    \"x\": {\n                        \"timeUnit\": \"hoursminutesseconds\",\n                        \"field\": \"Timestamp\",\n                        \"title\": \"Local Time / HH:MM:SS\",\n                    },\n                    \"y\": {\n                        \"field\": \"Percent100\",\n                        \"type\": \"quantitative\",\n                        \"title\": \"Utilization / %\",\n                    },\n                    \"color\": {\"field\": \"Metric\", \"type\": \"nominal\"},\n                },\n                \"layer\": [\n                    {\"mark\": \"line\"},\n                    {\n                        \"transform\": [\n                            {\"filter\": {\"param\": \"hover\", \"empty\": False}}\n                        ],\n                        \"mark\": \"point\",\n                    },\n                ],\n            },\n            {\n                \"transform\": [\n                    {\n                        \"pivot\": \"Metric\",\n                        \"value\": \"Percent\",\n                        \"groupby\": [\"Timestamp\"],\n                    }\n                ],\n                \"mark\": \"rule\",\n                \"encoding\": {\n                    \"opacity\": {\n                        \"condition\": {\n                            \"value\": 0.3,\n                            \"param\": \"hover\",\n                            \"empty\": False,\n                        },\n                        \"value\": 0,\n                    },\n                    \"tooltip\": tooltip,\n                    \"x\": {\n                        \"timeUnit\": \"hoursminutesseconds\",\n                        \"field\": \"Timestamp\",\n                        \"title\": \"Local Time / HH:MM:SS\",\n                    },\n                },\n                \"params\": [\n                    {\n                        \"name\": \"hover\",\n                        \"select\": {\n                            \"type\": \"point\",\n                            \"fields\": [\"Timestamp\"],\n                            \"nearest\": True,\n                            \"on\": \"mouseover\",\n                            \"clear\": \"mouseout\",\n                        },\n                    }\n                ],\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"rule\", \"strokeDash\": [8, 8]},\n                \"encoding\": {\"y\": {\"datum\": cpu_limit}},\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"text\", \"baseline\": \"line-bottom\"},\n                \"encoding\": {\n                    \"text\": {\"datum\": \"CPU Utilization Limit\"},\n                    \"y\": {\"datum\": cpu_limit},\n                },\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"rule\", \"strokeDash\": [8, 8]},\n                \"encoding\": {\"y\": {\"datum\": 100}},\n            },\n            {\n                \"data\": {\"values\": [{}]},\n                \"mark\": {\"type\": \"text\", \"baseline\": \"line-bottom\"},\n                \"encoding\": {\n                    \"text\": {\n                        \"datum\": \"Memory / GPU / GPU Memory Utilization Limit\"\n                    },\n                    \"y\": {\"datum\": 100},\n                },\n            },\n        ],\n    }\n", "path": "app/grandchallenge/charts/specs.py"}]}