We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
HTML can be inserted into a user's Plugging it in challenge templates
### The steps are:
- [Open any programming challenge on PII](https://www.csunplugged.org/en/plugging-it-in/binary-numbers/how-binary-digits-work/binary-numbers-no-calculations/)
- Submit this code in the answer box:
    - `</script><script type="text/javascript">console.log("hi")</script>`
        - It must be all on one line
        - Adapt to your desired level of maliciousness
- Refresh the page

When the user re-requests the page, the submitted 'code' is loaded [in the HTML template](https://github.com/uccser/cs-unplugged/blob/develop/csunplugged/templates/plugging_it_in/programming-challenge.html#L191). The first `</script>` closes the script that assigns the variables, and what follows is loaded as a new HTML script tag. The resulting HTML is sent to the user and executes 'as normal'.

I haven't been able to submit code that executes while also not breaking every PII challenge page. When the page does break, deleting your cookies for CSU allows you to get it working again

### Three potential fixes
- [Escape the submitted code.](https://docs.djangoproject.com/en/3.1/ref/utils/#module-django.utils.html)
    - It was already escaped, the problem is that [it gets un-escaped](https://docs.djangoproject.com/en/2.2/ref/templates/language/#automatic-html-escaping) in order to load as a variable.
        - So we would need to un-escape it in JS rather than HTML, and doing so will be tricky
(`&#39;}}&lt;` etc is loaded into the answer box)
- Don't load the user submitted code into the HTML template, and instead request it in JS
    - Will be slower, another request is needed
- Load the submitted code in the HTML, not as a variable, but straight into the <textarea> answer box.
    - This is how it's done in CodeWOF
    - Since it's in a <textarea> everything is rendered as plaintext

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `csunplugged/config/__init__.py`
Content:
```
1 """Module for Django system configuration."""
2 
3 __version__ = "6.0.0"
4
```

Path: `csunplugged/plugging_it_in/views.py`
Content:
```
1 """Views for the plugging_it_in application."""
2 
3 from django.http import HttpResponse
4 from django.http import Http404
5 
6 import json
7 import requests
8 
9 from django.shortcuts import get_object_or_404
10 from django.views import generic
11 from django.views import View
12 from django.urls import reverse
13 from django.conf import settings
14 from django.core.exceptions import ObjectDoesNotExist
15 from utils.translated_first import translated_first
16 from utils.group_lessons_by_age import group_lessons_by_age
17 
18 from topics.models import (
19     Topic,
20     ProgrammingChallenge,
21     Lesson
22 )
23 
24 
25 class IndexView(generic.ListView):
26     """View for the topics application homepage."""
27 
28     template_name = "plugging_it_in/index.html"
29     context_object_name = "programming_topics"
30 
31     def get_queryset(self):
32         """Get queryset of all topics.
33 
34         Returns:
35             Queryset of Topic objects ordered by name.
36         """
37         programming_topics = Topic.objects.order_by(
38             "name"
39         ).exclude(
40             programming_challenges__isnull=True
41         ).prefetch_related(
42             "programming_challenges",
43             "lessons",
44         )
45         return translated_first(programming_topics)
46 
47     def get_context_data(self, **kwargs):
48         """Provide the context data for the index view.
49 
50         Returns:
51             Dictionary of context data.
52         """
53         # Call the base implementation first to get a context
54         context = super(IndexView, self).get_context_data(**kwargs)
55         for topic in self.object_list:
56             topic.grouped_lessons = group_lessons_by_age(
57                 topic.lessons.all(),
58                 only_programming_exercises=True
59             )
60         return context
61 
62 
63 class AboutView(generic.TemplateView):
64     """View for the about page that renders from a template."""
65 
66     template_name = "plugging_it_in/about.html"
67 
68 
69 class ProgrammingChallengeListView(generic.DetailView):
70     """View showing all the programming exercises for a specific lesson."""
71 
72     model = Lesson
73     template_name = "plugging_it_in/lesson.html"
74 
75     def get_object(self, **kwargs):
76         """Retrieve object for the lesson view.
77 
78         Returns:
79             Lesson object, or raises 404 error if not found.
80         """
81         return get_object_or_404(
82             self.model.objects.select_related(),
83             topic__slug=self.kwargs.get("topic_slug", None),
84             slug=self.kwargs.get("lesson_slug", None),
85         )
86 
87     def get_context_data(self, **kwargs):
88         """Provide the context data for the programming challenge list view.
89 
90         Returns:
91             Dictionary of context data.
92         """
93         # Call the base implementation first to get a context
94         context = super(ProgrammingChallengeListView, self).get_context_data(**kwargs)
95 
96         context["topic"] = self.object.topic
97 
98         context["lesson"] = self.object
99 
100         # Add in a QuerySet of all the connected programming exercises for this topic
101         context["programming_challenges"] = self.object.retrieve_related_programming_challenges(
102         ).prefetch_related('implementations')
103         return context
104 
105 
106 class ProgrammingChallengeView(generic.DetailView):
107     """View for a specific programming challenge."""
108 
109     model = ProgrammingChallenge
110     template_name = "plugging_it_in/programming-challenge.html"
111     context_object_name = "programming_challenge"
112 
113     def get_object(self, **kwargs):
114         """Retrieve object for the programming challenge view.
115 
116         Returns:
117             ProgrammingChallenge object, or raises 404 error if not found.
118         """
119         return get_object_or_404(
120             self.model.objects.select_related(),
121             topic__slug=self.kwargs.get("topic_slug", None),
122             slug=self.kwargs.get("programming_challenge_slug", None)
123         )
124 
125     def get_context_data(self, **kwargs):
126         """Provide the context data for the programming challenge view.
127 
128         Returns:
129             Dictionary of context data.
130         """
131         # Call the base implementation first to get a context
132         context = super(ProgrammingChallengeView, self).get_context_data(**kwargs)
133 
134         context["topic"] = self.object.topic
135 
136         try:
137             lesson_slug = self.kwargs.get("lesson_slug", None)
138             lesson = Lesson.objects.get(slug=lesson_slug)
139             context["lesson"] = lesson
140             challlenges = lesson.retrieve_related_programming_challenges("Python")
141             context["programming_challenges"] = challlenges
142             context["programming_exercises_json"] = json.dumps(list(challlenges.values()))
143         except ObjectDoesNotExist:
144             raise Http404("Lesson does not exist")
145 
146         context["implementations"] = self.object.ordered_implementations()
147 
148         related_test_cases = self.object.related_test_cases()
149         context["test_cases_json"] = json.dumps(list(related_test_cases.values()))
150         context["test_cases"] = related_test_cases
151         context["jobe_proxy_url"] = reverse('plugging_it_in:jobe_proxy')
152         context["saved_attempts"] = self.request.session.get('saved_attempts', {})
153 
154         return context
155 
156 
157 class JobeProxyView(View):
158     """Proxy for Jobe Server."""
159 
160     def post(self, request, *args, **kwargs):
161         """Forward on request to Jobe from the frontend and adds API key if this is needed.
162 
163         Returns:
164             The response from the Jobe server.
165         """
166         # Extracting data from the request body
167         body_unicode = request.body.decode('utf-8')
168         body = json.dumps(json.loads(body_unicode))
169 
170         headers = {"Content-type": "application/json; charset=utf-8",
171                    "Accept": "application/json"}
172 
173         # Set API key for production
174         if hasattr(settings, 'JOBE_API_KEY'):
175             headers["X-API-KEY"] = settings.JOBE_API_KEY
176 
177         response = requests.post(settings.JOBE_SERVER_URL + "/jobe/index.php/restapi/runs/",
178                                  data=body, headers=headers)
179         return HttpResponse(response.text)
180 
181 
182 class SaveAttemptView(View):
183     """View to save the users challenge attempt."""
184 
185     def post(self, request):
186         """Save the users attempt to a Django session."""
187         body_unicode = request.body.decode('utf-8')
188         body = json.loads(body_unicode)
189 
190         request.session['saved_attempts'] = request.session.get('saved_attempts', {})
191 
192         # To stop a "passed" or "failed" status being overridden by "started"
193         if (not (body["status"] == "started"
194                  and request.session.get('saved_attempts', {}).get(body["challenge"], {}).get("status", "")
195                  in {'passed', 'failed'})
196                 and body["attempt"] != ""):
197             request.session['saved_attempts'][body["challenge"]] = {
198                 "status": body["status"],
199                 "code": body["attempt"]
200             }
201             return HttpResponse("Saved the attempt.")
202         else:
203             return HttpResponse("Response does not need to be saved.")
204
```
--- 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/csunplugged/config/__init__.py b/csunplugged/config/__init__.py
--- a/csunplugged/config/__init__.py
+++ b/csunplugged/config/__init__.py
@@ -1,3 +1,3 @@
 """Module for Django system configuration."""
 
-__version__ = "6.0.0"
+__version__ = "6.0.1"
diff --git a/csunplugged/plugging_it_in/views.py b/csunplugged/plugging_it_in/views.py
--- a/csunplugged/plugging_it_in/views.py
+++ b/csunplugged/plugging_it_in/views.py
@@ -150,6 +150,10 @@
         context["test_cases"] = related_test_cases
         context["jobe_proxy_url"] = reverse('plugging_it_in:jobe_proxy')
         context["saved_attempts"] = self.request.session.get('saved_attempts', {})
+        try:
+            context["previous_submission"] = context["saved_attempts"][self.object.slug]['code']
+        except KeyError:
+            context["previous_submission"] = ''
 
         return context

{"golden_diff": "diff --git a/csunplugged/config/__init__.py b/csunplugged/config/__init__.py\n--- a/csunplugged/config/__init__.py\n+++ b/csunplugged/config/__init__.py\n@@ -1,3 +1,3 @@\n \"\"\"Module for Django system configuration.\"\"\"\n \n-__version__ = \"6.0.0\"\n+__version__ = \"6.0.1\"\ndiff --git a/csunplugged/plugging_it_in/views.py b/csunplugged/plugging_it_in/views.py\n--- a/csunplugged/plugging_it_in/views.py\n+++ b/csunplugged/plugging_it_in/views.py\n@@ -150,6 +150,10 @@\n         context[\"test_cases\"] = related_test_cases\n         context[\"jobe_proxy_url\"] = reverse('plugging_it_in:jobe_proxy')\n         context[\"saved_attempts\"] = self.request.session.get('saved_attempts', {})\n+        try:\n+            context[\"previous_submission\"] = context[\"saved_attempts\"][self.object.slug]['code']\n+        except KeyError:\n+            context[\"previous_submission\"] = ''\n \n         return context\n", "issue": "HTML can be inserted into a user's Plugging it in challenge templates\n### The steps are:\r\n- [Open any programming challenge on PII](https://www.csunplugged.org/en/plugging-it-in/binary-numbers/how-binary-digits-work/binary-numbers-no-calculations/)\r\n- Submit this code in the answer box:\r\n    - `</script><script type=\"text/javascript\">console.log(\"hi\")</script>`\r\n        - It must be all on one line\r\n        - Adapt to your desired level of maliciousness\r\n- Refresh the page\r\n\r\nWhen the user re-requests the page, the submitted 'code' is loaded [in the HTML template](https://github.com/uccser/cs-unplugged/blob/develop/csunplugged/templates/plugging_it_in/programming-challenge.html#L191). The first `</script>` closes the script that assigns the variables, and what follows is loaded as a new HTML script tag. The resulting HTML is sent to the user and executes 'as normal'.\r\n\r\nI haven't been able to submit code that executes while also not breaking every PII challenge page. When the page does break, deleting your cookies for CSU allows you to get it working again\r\n\r\n### Three potential fixes\r\n- [Escape the submitted code.](https://docs.djangoproject.com/en/3.1/ref/utils/#module-django.utils.html)\r\n    - It was already escaped, the problem is that [it gets un-escaped](https://docs.djangoproject.com/en/2.2/ref/templates/language/#automatic-html-escaping) in order to load as a variable.\r\n        - So we would need to un-escape it in JS rather than HTML, and doing so will be tricky\r\n(`&#39;}}&lt;` etc is loaded into the answer box)\r\n- Don't load the user submitted code into the HTML template, and instead request it in JS\r\n    - Will be slower, another request is needed\r\n- Load the submitted code in the HTML, not as a variable, but straight into the <textarea> answer box.\r\n    - This is how it's done in CodeWOF\r\n    - Since it's in a <textarea> everything is rendered as plaintext\n", "before_files": [{"content": "\"\"\"Module for Django system configuration.\"\"\"\n\n__version__ = \"6.0.0\"\n", "path": "csunplugged/config/__init__.py"}, {"content": "\"\"\"Views for the plugging_it_in application.\"\"\"\n\nfrom django.http import HttpResponse\nfrom django.http import Http404\n\nimport json\nimport requests\n\nfrom django.shortcuts import get_object_or_404\nfrom django.views import generic\nfrom django.views import View\nfrom django.urls import reverse\nfrom django.conf import settings\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom utils.translated_first import translated_first\nfrom utils.group_lessons_by_age import group_lessons_by_age\n\nfrom topics.models import (\n    Topic,\n    ProgrammingChallenge,\n    Lesson\n)\n\n\nclass IndexView(generic.ListView):\n    \"\"\"View for the topics application homepage.\"\"\"\n\n    template_name = \"plugging_it_in/index.html\"\n    context_object_name = \"programming_topics\"\n\n    def get_queryset(self):\n        \"\"\"Get queryset of all topics.\n\n        Returns:\n            Queryset of Topic objects ordered by name.\n        \"\"\"\n        programming_topics = Topic.objects.order_by(\n            \"name\"\n        ).exclude(\n            programming_challenges__isnull=True\n        ).prefetch_related(\n            \"programming_challenges\",\n            \"lessons\",\n        )\n        return translated_first(programming_topics)\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the index view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(IndexView, self).get_context_data(**kwargs)\n        for topic in self.object_list:\n            topic.grouped_lessons = group_lessons_by_age(\n                topic.lessons.all(),\n                only_programming_exercises=True\n            )\n        return context\n\n\nclass AboutView(generic.TemplateView):\n    \"\"\"View for the about page that renders from a template.\"\"\"\n\n    template_name = \"plugging_it_in/about.html\"\n\n\nclass ProgrammingChallengeListView(generic.DetailView):\n    \"\"\"View showing all the programming exercises for a specific lesson.\"\"\"\n\n    model = Lesson\n    template_name = \"plugging_it_in/lesson.html\"\n\n    def get_object(self, **kwargs):\n        \"\"\"Retrieve object for the lesson view.\n\n        Returns:\n            Lesson object, or raises 404 error if not found.\n        \"\"\"\n        return get_object_or_404(\n            self.model.objects.select_related(),\n            topic__slug=self.kwargs.get(\"topic_slug\", None),\n            slug=self.kwargs.get(\"lesson_slug\", None),\n        )\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the programming challenge list view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(ProgrammingChallengeListView, self).get_context_data(**kwargs)\n\n        context[\"topic\"] = self.object.topic\n\n        context[\"lesson\"] = self.object\n\n        # Add in a QuerySet of all the connected programming exercises for this topic\n        context[\"programming_challenges\"] = self.object.retrieve_related_programming_challenges(\n        ).prefetch_related('implementations')\n        return context\n\n\nclass ProgrammingChallengeView(generic.DetailView):\n    \"\"\"View for a specific programming challenge.\"\"\"\n\n    model = ProgrammingChallenge\n    template_name = \"plugging_it_in/programming-challenge.html\"\n    context_object_name = \"programming_challenge\"\n\n    def get_object(self, **kwargs):\n        \"\"\"Retrieve object for the programming challenge view.\n\n        Returns:\n            ProgrammingChallenge object, or raises 404 error if not found.\n        \"\"\"\n        return get_object_or_404(\n            self.model.objects.select_related(),\n            topic__slug=self.kwargs.get(\"topic_slug\", None),\n            slug=self.kwargs.get(\"programming_challenge_slug\", None)\n        )\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the programming challenge view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(ProgrammingChallengeView, self).get_context_data(**kwargs)\n\n        context[\"topic\"] = self.object.topic\n\n        try:\n            lesson_slug = self.kwargs.get(\"lesson_slug\", None)\n            lesson = Lesson.objects.get(slug=lesson_slug)\n            context[\"lesson\"] = lesson\n            challlenges = lesson.retrieve_related_programming_challenges(\"Python\")\n            context[\"programming_challenges\"] = challlenges\n            context[\"programming_exercises_json\"] = json.dumps(list(challlenges.values()))\n        except ObjectDoesNotExist:\n            raise Http404(\"Lesson does not exist\")\n\n        context[\"implementations\"] = self.object.ordered_implementations()\n\n        related_test_cases = self.object.related_test_cases()\n        context[\"test_cases_json\"] = json.dumps(list(related_test_cases.values()))\n        context[\"test_cases\"] = related_test_cases\n        context[\"jobe_proxy_url\"] = reverse('plugging_it_in:jobe_proxy')\n        context[\"saved_attempts\"] = self.request.session.get('saved_attempts', {})\n\n        return context\n\n\nclass JobeProxyView(View):\n    \"\"\"Proxy for Jobe Server.\"\"\"\n\n    def post(self, request, *args, **kwargs):\n        \"\"\"Forward on request to Jobe from the frontend and adds API key if this is needed.\n\n        Returns:\n            The response from the Jobe server.\n        \"\"\"\n        # Extracting data from the request body\n        body_unicode = request.body.decode('utf-8')\n        body = json.dumps(json.loads(body_unicode))\n\n        headers = {\"Content-type\": \"application/json; charset=utf-8\",\n                   \"Accept\": \"application/json\"}\n\n        # Set API key for production\n        if hasattr(settings, 'JOBE_API_KEY'):\n            headers[\"X-API-KEY\"] = settings.JOBE_API_KEY\n\n        response = requests.post(settings.JOBE_SERVER_URL + \"/jobe/index.php/restapi/runs/\",\n                                 data=body, headers=headers)\n        return HttpResponse(response.text)\n\n\nclass SaveAttemptView(View):\n    \"\"\"View to save the users challenge attempt.\"\"\"\n\n    def post(self, request):\n        \"\"\"Save the users attempt to a Django session.\"\"\"\n        body_unicode = request.body.decode('utf-8')\n        body = json.loads(body_unicode)\n\n        request.session['saved_attempts'] = request.session.get('saved_attempts', {})\n\n        # To stop a \"passed\" or \"failed\" status being overridden by \"started\"\n        if (not (body[\"status\"] == \"started\"\n                 and request.session.get('saved_attempts', {}).get(body[\"challenge\"], {}).get(\"status\", \"\")\n                 in {'passed', 'failed'})\n                and body[\"attempt\"] != \"\"):\n            request.session['saved_attempts'][body[\"challenge\"]] = {\n                \"status\": body[\"status\"],\n                \"code\": body[\"attempt\"]\n            }\n            return HttpResponse(\"Saved the attempt.\")\n        else:\n            return HttpResponse(\"Response does not need to be saved.\")\n", "path": "csunplugged/plugging_it_in/views.py"}], "after_files": [{"content": "\"\"\"Module for Django system configuration.\"\"\"\n\n__version__ = \"6.0.1\"\n", "path": "csunplugged/config/__init__.py"}, {"content": "\"\"\"Views for the plugging_it_in application.\"\"\"\n\nfrom django.http import HttpResponse\nfrom django.http import Http404\n\nimport json\nimport requests\n\nfrom django.shortcuts import get_object_or_404\nfrom django.views import generic\nfrom django.views import View\nfrom django.urls import reverse\nfrom django.conf import settings\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom utils.translated_first import translated_first\nfrom utils.group_lessons_by_age import group_lessons_by_age\n\nfrom topics.models import (\n    Topic,\n    ProgrammingChallenge,\n    Lesson\n)\n\n\nclass IndexView(generic.ListView):\n    \"\"\"View for the topics application homepage.\"\"\"\n\n    template_name = \"plugging_it_in/index.html\"\n    context_object_name = \"programming_topics\"\n\n    def get_queryset(self):\n        \"\"\"Get queryset of all topics.\n\n        Returns:\n            Queryset of Topic objects ordered by name.\n        \"\"\"\n        programming_topics = Topic.objects.order_by(\n            \"name\"\n        ).exclude(\n            programming_challenges__isnull=True\n        ).prefetch_related(\n            \"programming_challenges\",\n            \"lessons\",\n        )\n        return translated_first(programming_topics)\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the index view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(IndexView, self).get_context_data(**kwargs)\n        for topic in self.object_list:\n            topic.grouped_lessons = group_lessons_by_age(\n                topic.lessons.all(),\n                only_programming_exercises=True\n            )\n        return context\n\n\nclass AboutView(generic.TemplateView):\n    \"\"\"View for the about page that renders from a template.\"\"\"\n\n    template_name = \"plugging_it_in/about.html\"\n\n\nclass ProgrammingChallengeListView(generic.DetailView):\n    \"\"\"View showing all the programming exercises for a specific lesson.\"\"\"\n\n    model = Lesson\n    template_name = \"plugging_it_in/lesson.html\"\n\n    def get_object(self, **kwargs):\n        \"\"\"Retrieve object for the lesson view.\n\n        Returns:\n            Lesson object, or raises 404 error if not found.\n        \"\"\"\n        return get_object_or_404(\n            self.model.objects.select_related(),\n            topic__slug=self.kwargs.get(\"topic_slug\", None),\n            slug=self.kwargs.get(\"lesson_slug\", None),\n        )\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the programming challenge list view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(ProgrammingChallengeListView, self).get_context_data(**kwargs)\n\n        context[\"topic\"] = self.object.topic\n\n        context[\"lesson\"] = self.object\n\n        # Add in a QuerySet of all the connected programming exercises for this topic\n        context[\"programming_challenges\"] = self.object.retrieve_related_programming_challenges(\n        ).prefetch_related('implementations')\n        return context\n\n\nclass ProgrammingChallengeView(generic.DetailView):\n    \"\"\"View for a specific programming challenge.\"\"\"\n\n    model = ProgrammingChallenge\n    template_name = \"plugging_it_in/programming-challenge.html\"\n    context_object_name = \"programming_challenge\"\n\n    def get_object(self, **kwargs):\n        \"\"\"Retrieve object for the programming challenge view.\n\n        Returns:\n            ProgrammingChallenge object, or raises 404 error if not found.\n        \"\"\"\n        return get_object_or_404(\n            self.model.objects.select_related(),\n            topic__slug=self.kwargs.get(\"topic_slug\", None),\n            slug=self.kwargs.get(\"programming_challenge_slug\", None)\n        )\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Provide the context data for the programming challenge view.\n\n        Returns:\n            Dictionary of context data.\n        \"\"\"\n        # Call the base implementation first to get a context\n        context = super(ProgrammingChallengeView, self).get_context_data(**kwargs)\n\n        context[\"topic\"] = self.object.topic\n\n        try:\n            lesson_slug = self.kwargs.get(\"lesson_slug\", None)\n            lesson = Lesson.objects.get(slug=lesson_slug)\n            context[\"lesson\"] = lesson\n            challlenges = lesson.retrieve_related_programming_challenges(\"Python\")\n            context[\"programming_challenges\"] = challlenges\n            context[\"programming_exercises_json\"] = json.dumps(list(challlenges.values()))\n        except ObjectDoesNotExist:\n            raise Http404(\"Lesson does not exist\")\n\n        context[\"implementations\"] = self.object.ordered_implementations()\n\n        related_test_cases = self.object.related_test_cases()\n        context[\"test_cases_json\"] = json.dumps(list(related_test_cases.values()))\n        context[\"test_cases\"] = related_test_cases\n        context[\"jobe_proxy_url\"] = reverse('plugging_it_in:jobe_proxy')\n        context[\"saved_attempts\"] = self.request.session.get('saved_attempts', {})\n        try:\n            context[\"previous_submission\"] = context[\"saved_attempts\"][self.object.slug]['code']\n        except KeyError:\n            context[\"previous_submission\"] = ''\n\n        return context\n\n\nclass JobeProxyView(View):\n    \"\"\"Proxy for Jobe Server.\"\"\"\n\n    def post(self, request, *args, **kwargs):\n        \"\"\"Forward on request to Jobe from the frontend and adds API key if this is needed.\n\n        Returns:\n            The response from the Jobe server.\n        \"\"\"\n        # Extracting data from the request body\n        body_unicode = request.body.decode('utf-8')\n        body = json.dumps(json.loads(body_unicode))\n\n        headers = {\"Content-type\": \"application/json; charset=utf-8\",\n                   \"Accept\": \"application/json\"}\n\n        # Set API key for production\n        if hasattr(settings, 'JOBE_API_KEY'):\n            headers[\"X-API-KEY\"] = settings.JOBE_API_KEY\n\n        response = requests.post(settings.JOBE_SERVER_URL + \"/jobe/index.php/restapi/runs/\",\n                                 data=body, headers=headers)\n        return HttpResponse(response.text)\n\n\nclass SaveAttemptView(View):\n    \"\"\"View to save the users challenge attempt.\"\"\"\n\n    def post(self, request):\n        \"\"\"Save the users attempt to a Django session.\"\"\"\n        body_unicode = request.body.decode('utf-8')\n        body = json.loads(body_unicode)\n\n        request.session['saved_attempts'] = request.session.get('saved_attempts', {})\n\n        # To stop a \"passed\" or \"failed\" status being overridden by \"started\"\n        if (not (body[\"status\"] == \"started\"\n                 and request.session.get('saved_attempts', {}).get(body[\"challenge\"], {}).get(\"status\", \"\")\n                 in {'passed', 'failed'})\n                and body[\"attempt\"] != \"\"):\n            request.session['saved_attempts'][body[\"challenge\"]] = {\n                \"status\": body[\"status\"],\n                \"code\": body[\"attempt\"]\n            }\n            return HttpResponse(\"Saved the attempt.\")\n        else:\n            return HttpResponse(\"Response does not need to be saved.\")\n", "path": "csunplugged/plugging_it_in/views.py"}]}