We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix __str__ function in ProgrammingExerciseLanguageImplementation
The `__str__` function of `ProgrammingExerciseLanguageImplementation` returns an unknown property which creates an error.

--- 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/topics/management/commands/_ProgrammingExerciseLoader.py`
Content:
```
1 import os.path
2 from utils.BaseLoader import BaseLoader
3 from topics.models import (
4     LearningOutcome,
5     ProgrammingExerciseDifficulty,
6     ProgrammingExerciseLanguage,
7     ProgrammingExerciseLanguageImplementation,
8 )
9 
10 
11 class ProgrammingExerciseLoader(BaseLoader):
12     """Loader for a programming exercise"""
13 
14     def __init__(self, load_log, exercise_slug, exercise_structure, topic, BASE_PATH):
15         """Initiates the loader for a programming exercise
16 
17         Args:
18             exercise_slug (string): slug for exercise
19             exercise_structure (dict): attributes for exercise
20             topic: Topic model object
21         """
22         super().__init__(BASE_PATH, load_log)
23         self.exercise_slug = exercise_slug
24         self.exercise_structure = exercise_structure
25         self.topic = topic
26 
27     def load(self):
28         """load the content for a programming exercise"""
29         content = self.convert_md_file(os.path.join(self.BASE_PATH, self.exercise_structure['md-file']))
30 
31         programming_exercise = self.topic.topic_programming_exercises.create(
32             slug=self.exercise_slug,
33             name=content.title,
34             exercise_set_number=self.exercise_structure['exercise-set-number'],
35             exercise_number=self.exercise_structure['exercise-number'],
36             content=content.html_string,
37             difficulty=ProgrammingExerciseDifficulty.objects.get(
38                 level=self.exercise_structure['difficulty-level']
39             )
40         )
41         programming_exercise.save()
42 
43         language_solutions = self.exercise_structure['programming-languages']
44         for language in language_solutions:
45             # This gets the language for the solution, if not found it should throw an error!
46             language_object = ProgrammingExerciseLanguage.objects.get(
47                 slug=language
48             )
49 
50             expected_result_path = os.path.join(self.BASE_PATH, language_solutions[language]['expected-result'])
51             expected_result_content = self.convert_md_file(expected_result_path).html_string
52 
53             hint_path = os.path.join(self.BASE_PATH, language_solutions[language]['hints'])
54             hint_content = self.convert_md_file(hint_path).html_string
55 
56             solution_path = os.path.join(self.BASE_PATH, language_solutions[language]['solution'])
57             solution_content = self.convert_md_file(solution_path).html_string
58 
59             implementation = ProgrammingExerciseLanguageImplementation.objects.create(
60                 expected_result=expected_result_content,
61                 hints=hint_content,
62                 solution=solution_content,
63                 language=language_object,
64                 exercise=programming_exercise,
65                 topic=self.topic
66             )
67             implementation.save()
68 
69         for learning_outcome_slug in self.exercise_structure['learning-outcomes']:
70             learning_outcome = LearningOutcome.objects.get(
71                 slug=learning_outcome_slug
72             )
73             programming_exercise.learning_outcomes.add(learning_outcome)
74 
75         LOG_TEMPLATE = 'Added Programming Exercise: {}'
76         self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)
77
```

Path: `csunplugged/topics/models.py`
Content:
```
1 from collections import OrderedDict
2 
3 from django.db import models
4 from resources.models import Resource
5 
6 
7 class LearningOutcome(models.Model):
8     #  Auto-incrementing 'id' field is automatically set by Django
9     slug = models.SlugField(unique=True)
10     text = models.CharField(max_length=200, unique=True)
11 
12     def __str__(self):
13         return self.text
14 
15 
16 class CurriculumArea(models.Model):
17     #  Auto-incrementing 'id' field is automatically set by Django
18     slug = models.SlugField(unique=True)
19     name = models.CharField(max_length=100, unique=True)
20 
21     def __str__(self):
22         return self.name
23 
24 
25 class ClassroomResource(models.Model):
26     #  Auto-incrementing 'id' field is automatically set by Django
27     text = models.CharField(max_length=300, unique=True)
28 
29     def __str__(self):
30         return self.text
31 
32 
33 class Topic(models.Model):
34     #  Auto-incrementing 'id' field is automatically set by Django
35     slug = models.SlugField(unique=True)
36     name = models.CharField(max_length=100)
37     content = models.TextField()
38     other_resources = models.TextField()
39     icon = models.CharField(max_length=100, null=True)
40 
41     def __str__(self):
42         return self.name
43 
44 
45 class UnitPlan(models.Model):
46     #  Auto-incrementing 'id' field is automatically set by Django
47     topic = models.ForeignKey(
48         Topic,
49         on_delete=models.CASCADE,
50         related_name='topic_unit_plans'
51     )
52     slug = models.SlugField()
53     name = models.CharField(max_length=100)
54     content = models.TextField()
55 
56     def lessons_by_age_group(self):
57         """Returns groups of lessons grouped by the lesson minimum age
58         and maximum ages, and then order by number.
59 
60         Returns:
61             A ordered dictionary of grouped lessons.
62             The key is a tuple of the minimum age and maximum ages for
63             the lessons.
64             The value for a key is a sorted list of lessons.
65             The dictionary is ordered by minimum age, then maximum age.
66         """
67         grouped_lessons = OrderedDict()
68         lessons = self.unit_plan_lessons.order_by('min_age', 'max_age', 'number')
69         for lesson in lessons:
70             if (lesson.min_age, lesson.max_age) in grouped_lessons:
71                 grouped_lessons[(lesson.min_age, lesson.max_age)].append(lesson)
72             else:
73                 grouped_lessons[(lesson.min_age, lesson.max_age)] = [lesson]
74         return grouped_lessons
75 
76     def __str__(self):
77         return self.name
78 
79 
80 class ProgrammingExerciseDifficulty(models.Model):
81     #  Auto-incrementing 'id' field is automatically set by Django
82     level = models.PositiveSmallIntegerField(unique=True)
83     name = models.CharField(max_length=100, unique=True)
84 
85     def __str__(self):
86         return self.name
87 
88 
89 class ProgrammingExercise(models.Model):
90     #  Auto-incrementing 'id' field is automatically set by Django
91     topic = models.ForeignKey(
92         Topic,
93         on_delete=models.CASCADE,
94         related_name='topic_programming_exercises'
95     )
96     slug = models.SlugField()
97     name = models.CharField(max_length=200)
98     exercise_set_number = models.PositiveSmallIntegerField()
99     exercise_number = models.PositiveSmallIntegerField()
100     content = models.TextField()
101     learning_outcomes = models.ManyToManyField(
102         LearningOutcome,
103         related_name='programming_exercise_learning_outcomes'
104     )
105     difficulty = models.ForeignKey(
106         ProgrammingExerciseDifficulty,
107         on_delete=models.CASCADE,
108         related_name='difficulty_programming_exercises'
109     )
110 
111     def __str__(self):
112         return self.name
113 
114 
115 class ProgrammingExerciseLanguage(models.Model):
116     #  Auto-incrementing 'id' field is automatically set by Django
117     slug = models.SlugField()
118     name = models.CharField(max_length=200)
119     icon = models.CharField(max_length=100, null=True)
120 
121     def __str__(self):
122         return self.name
123 
124 
125 class ProgrammingExerciseLanguageImplementation(models.Model):
126     #  Auto-incrementing 'id' field is automatically set by Django
127     topic = models.ForeignKey(
128         Topic,
129         on_delete=models.CASCADE,
130         related_name='implementations'
131     )
132     language = models.ForeignKey(
133         ProgrammingExerciseLanguage,
134         on_delete=models.CASCADE,
135         related_name='implementations'
136     )
137     exercise = models.ForeignKey(
138         ProgrammingExercise,
139         on_delete=models.CASCADE,
140         related_name='implementations'
141     )
142     expected_result = models.TextField()
143     hints = models.TextField()
144     solution = models.TextField()
145 
146     def __str__(self):
147         return self.name
148 
149 
150 class Lesson(models.Model):
151     #  Auto-incrementing 'id' field is automatically set by Django
152     topic = models.ForeignKey(
153         Topic,
154         on_delete=models.CASCADE,
155         related_name='topic_lessons'
156     )
157     unit_plan = models.ForeignKey(
158         UnitPlan,
159         on_delete=models.CASCADE,
160         related_name='unit_plan_lessons'
161     )
162     slug = models.SlugField()
163     name = models.CharField(max_length=100)
164     number = models.IntegerField()
165     content = models.TextField()
166     min_age = models.PositiveSmallIntegerField()
167     max_age = models.PositiveSmallIntegerField()
168     programming_exercises = models.ManyToManyField(
169         ProgrammingExercise,
170         related_name='lessons'
171     )
172     learning_outcomes = models.ManyToManyField(
173         LearningOutcome,
174         related_name='lesson_learning_outcomes'
175     )
176     curriculum_areas = models.ManyToManyField(
177         CurriculumArea,
178         related_name='lesson_curriculum_areas'
179     )
180     classroom_resources = models.ManyToManyField(
181         ClassroomResource,
182         related_name='lesson_classroom_resources'
183     )
184     generated_resources = models.ManyToManyField(
185         Resource,
186         through='ConnectedGeneratedResource',
187         related_name='lesson_generated_resources'
188     )
189 
190     def has_programming_exercises(self):
191         """Returns a boolean to state whether the lesson has any
192         programming exercises.
193 
194         Returns:
195             True if the lesson has connected programming exercises.
196             Otherwise False.
197         """
198         return bool(self.programming_exercises.all())
199 
200     def __str__(self):
201         return self.name
202 
203 
204 class CurriculumIntegration(models.Model):
205     #  Auto-incrementing 'id' field is automatically set by Django
206     topic = models.ForeignKey(
207         Topic,
208         on_delete=models.CASCADE,
209         related_name='curriculum_integrations'
210     )
211     slug = models.SlugField()
212     number = models.PositiveSmallIntegerField()
213     name = models.CharField(max_length=200)
214     content = models.TextField()
215     curriculum_areas = models.ManyToManyField(
216         CurriculumArea,
217         related_name='curriculum_integrations'
218     )
219     prerequisite_lessons = models.ManyToManyField(
220         Lesson,
221         related_name='curriculum_integrations'
222     )
223 
224     def has_prerequisite_lessons(self):
225         """Returns True if the curriculum integration has at
226         least one prerequisite lesson, otherwise False.
227 
228         Returns:
229         True if the curriculum integration has at
230         least one prerequisite lesson, otherwise False.
231         """
232         return bool(self.prerequisite_lessons.all())
233 
234     def __str__(self):
235         return self.name
236 
237 
238 class ConnectedGeneratedResource(models.Model):
239     #  Auto-incrementing 'id' field is automatically set by Django
240     resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
241     lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)
242     description = models.CharField(max_length=300)
243
```

Path: `csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py`
Content:
```
1 import os.path
2 from utils.BaseLoader import BaseLoader
3 from topics.models import CurriculumArea, Lesson
4 
5 
6 class CurriculumIntegrationsLoader(BaseLoader):
7     """Loader for curriculum integrations"""
8 
9     def __init__(self, load_log, structure_file, topic, BASE_PATH):
10         """Initiates the loader for curriculum integrations
11 
12         Args:
13             structure_file: file path (string)
14             topic: Topic model object
15         """
16         super().__init__(BASE_PATH, load_log)
17         self.structure_file = os.path.join(self.BASE_PATH, structure_file)
18         self.BASE_PATH = os.path.join(self.BASE_PATH, os.path.split(structure_file)[0])
19         self.topic = topic
20 
21     def load(self):
22         """Load the content for curriculum integrations"""
23         if self.structure_file:
24             structure = self.load_yaml_file(self.structure_file)
25 
26             for integration_slug, integration_data in structure.items():
27                 md_file = integration_data['md-file']
28                 integration_content = self.convert_md_file(os.path.join(self.BASE_PATH, md_file))
29 
30                 integration = self.topic.curriculum_integrations.create(
31                     slug=integration_slug,
32                     number=integration_data['number'],
33                     name=integration_content.title,
34                     content=integration_content.html_string,
35                 )
36                 integration.save()
37 
38                 # Add curriculum areas
39                 curriculum_area_slugs = integration_data['curriculum-areas']
40                 for curriculum_area_slug in curriculum_area_slugs:
41                     curriculum_area = CurriculumArea.objects.get(
42                         slug=curriculum_area_slug
43                     )
44                     integration.curriculum_areas.add(curriculum_area)
45 
46                 # Add prerequisite lessons
47                 if 'prerequisite-lessons' in integration_data:
48                     prerequisite_lessons_slugs = integration_data['prerequisite-lessons']
49                     for prerequisite_lessons_slug in prerequisite_lessons_slugs:
50                         (unit_plan_slug, lesson_slug) = prerequisite_lessons_slug.split('/')
51                         print(unit_plan_slug, lesson_slug)
52                         lesson = Lesson.objects.get(
53                             slug=lesson_slug,
54                             unit_plan__slug=unit_plan_slug,
55                             topic__slug=self.topic.slug
56                         )
57                         integration.prerequisite_lessons.add(lesson)
58 
59                 self.log('Added Curriculum Integration: {}'.format(integration.name), 1)
60
```
--- 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/topics/management/commands/_CurriculumIntegrationsLoader.py b/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py
--- a/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py
+++ b/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py
@@ -48,7 +48,6 @@
                     prerequisite_lessons_slugs = integration_data['prerequisite-lessons']
                     for prerequisite_lessons_slug in prerequisite_lessons_slugs:
                         (unit_plan_slug, lesson_slug) = prerequisite_lessons_slug.split('/')
-                        print(unit_plan_slug, lesson_slug)
                         lesson = Lesson.objects.get(
                             slug=lesson_slug,
                             unit_plan__slug=unit_plan_slug,
diff --git a/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py b/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py
--- a/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py
+++ b/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py
@@ -40,6 +40,9 @@
         )
         programming_exercise.save()
 
+        LOG_TEMPLATE = 'Added Programming Exercise: {}'
+        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)
+
         language_solutions = self.exercise_structure['programming-languages']
         for language in language_solutions:
             # This gets the language for the solution, if not found it should throw an error!
@@ -66,11 +69,11 @@
             )
             implementation.save()
 
+            LOG_TEMPLATE = 'Added Language Implementation: {}'
+            self.log(LOG_TEMPLATE.format(implementation.language), 2)
+
         for learning_outcome_slug in self.exercise_structure['learning-outcomes']:
             learning_outcome = LearningOutcome.objects.get(
                 slug=learning_outcome_slug
             )
             programming_exercise.learning_outcomes.add(learning_outcome)
-
-        LOG_TEMPLATE = 'Added Programming Exercise: {}'
-        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)
diff --git a/csunplugged/topics/models.py b/csunplugged/topics/models.py
--- a/csunplugged/topics/models.py
+++ b/csunplugged/topics/models.py
@@ -144,7 +144,12 @@
     solution = models.TextField()
 
     def __str__(self):
-        return self.name
+        return '{} for exercise {}.{}, {}'.format(
+            self.language.name,
+            self.exercise.exercise_set_number,
+            self.exercise.exercise_number,
+            self.exercise.name
+        )
 
 
 class Lesson(models.Model):

{"golden_diff": "diff --git a/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py b/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py\n--- a/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py\n+++ b/csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py\n@@ -48,7 +48,6 @@\n                     prerequisite_lessons_slugs = integration_data['prerequisite-lessons']\n                     for prerequisite_lessons_slug in prerequisite_lessons_slugs:\n                         (unit_plan_slug, lesson_slug) = prerequisite_lessons_slug.split('/')\n-                        print(unit_plan_slug, lesson_slug)\n                         lesson = Lesson.objects.get(\n                             slug=lesson_slug,\n                             unit_plan__slug=unit_plan_slug,\ndiff --git a/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py b/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py\n--- a/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py\n+++ b/csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py\n@@ -40,6 +40,9 @@\n         )\n         programming_exercise.save()\n \n+        LOG_TEMPLATE = 'Added Programming Exercise: {}'\n+        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)\n+\n         language_solutions = self.exercise_structure['programming-languages']\n         for language in language_solutions:\n             # This gets the language for the solution, if not found it should throw an error!\n@@ -66,11 +69,11 @@\n             )\n             implementation.save()\n \n+            LOG_TEMPLATE = 'Added Language Implementation: {}'\n+            self.log(LOG_TEMPLATE.format(implementation.language), 2)\n+\n         for learning_outcome_slug in self.exercise_structure['learning-outcomes']:\n             learning_outcome = LearningOutcome.objects.get(\n                 slug=learning_outcome_slug\n             )\n             programming_exercise.learning_outcomes.add(learning_outcome)\n-\n-        LOG_TEMPLATE = 'Added Programming Exercise: {}'\n-        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)\ndiff --git a/csunplugged/topics/models.py b/csunplugged/topics/models.py\n--- a/csunplugged/topics/models.py\n+++ b/csunplugged/topics/models.py\n@@ -144,7 +144,12 @@\n     solution = models.TextField()\n \n     def __str__(self):\n-        return self.name\n+        return '{} for exercise {}.{}, {}'.format(\n+            self.language.name,\n+            self.exercise.exercise_set_number,\n+            self.exercise.exercise_number,\n+            self.exercise.name\n+        )\n \n \n class Lesson(models.Model):\n", "issue": "Fix __str__ function in ProgrammingExerciseLanguageImplementation\nThe `__str__` function of `ProgrammingExerciseLanguageImplementation` returns an unknown property which creates an error.\n", "before_files": [{"content": "import os.path\nfrom utils.BaseLoader import BaseLoader\nfrom topics.models import (\n    LearningOutcome,\n    ProgrammingExerciseDifficulty,\n    ProgrammingExerciseLanguage,\n    ProgrammingExerciseLanguageImplementation,\n)\n\n\nclass ProgrammingExerciseLoader(BaseLoader):\n    \"\"\"Loader for a programming exercise\"\"\"\n\n    def __init__(self, load_log, exercise_slug, exercise_structure, topic, BASE_PATH):\n        \"\"\"Initiates the loader for a programming exercise\n\n        Args:\n            exercise_slug (string): slug for exercise\n            exercise_structure (dict): attributes for exercise\n            topic: Topic model object\n        \"\"\"\n        super().__init__(BASE_PATH, load_log)\n        self.exercise_slug = exercise_slug\n        self.exercise_structure = exercise_structure\n        self.topic = topic\n\n    def load(self):\n        \"\"\"load the content for a programming exercise\"\"\"\n        content = self.convert_md_file(os.path.join(self.BASE_PATH, self.exercise_structure['md-file']))\n\n        programming_exercise = self.topic.topic_programming_exercises.create(\n            slug=self.exercise_slug,\n            name=content.title,\n            exercise_set_number=self.exercise_structure['exercise-set-number'],\n            exercise_number=self.exercise_structure['exercise-number'],\n            content=content.html_string,\n            difficulty=ProgrammingExerciseDifficulty.objects.get(\n                level=self.exercise_structure['difficulty-level']\n            )\n        )\n        programming_exercise.save()\n\n        language_solutions = self.exercise_structure['programming-languages']\n        for language in language_solutions:\n            # This gets the language for the solution, if not found it should throw an error!\n            language_object = ProgrammingExerciseLanguage.objects.get(\n                slug=language\n            )\n\n            expected_result_path = os.path.join(self.BASE_PATH, language_solutions[language]['expected-result'])\n            expected_result_content = self.convert_md_file(expected_result_path).html_string\n\n            hint_path = os.path.join(self.BASE_PATH, language_solutions[language]['hints'])\n            hint_content = self.convert_md_file(hint_path).html_string\n\n            solution_path = os.path.join(self.BASE_PATH, language_solutions[language]['solution'])\n            solution_content = self.convert_md_file(solution_path).html_string\n\n            implementation = ProgrammingExerciseLanguageImplementation.objects.create(\n                expected_result=expected_result_content,\n                hints=hint_content,\n                solution=solution_content,\n                language=language_object,\n                exercise=programming_exercise,\n                topic=self.topic\n            )\n            implementation.save()\n\n        for learning_outcome_slug in self.exercise_structure['learning-outcomes']:\n            learning_outcome = LearningOutcome.objects.get(\n                slug=learning_outcome_slug\n            )\n            programming_exercise.learning_outcomes.add(learning_outcome)\n\n        LOG_TEMPLATE = 'Added Programming Exercise: {}'\n        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)\n", "path": "csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py"}, {"content": "from collections import OrderedDict\n\nfrom django.db import models\nfrom resources.models import Resource\n\n\nclass LearningOutcome(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    text = models.CharField(max_length=200, unique=True)\n\n    def __str__(self):\n        return self.text\n\n\nclass CurriculumArea(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    name = models.CharField(max_length=100, unique=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ClassroomResource(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    text = models.CharField(max_length=300, unique=True)\n\n    def __str__(self):\n        return self.text\n\n\nclass Topic(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    name = models.CharField(max_length=100)\n    content = models.TextField()\n    other_resources = models.TextField()\n    icon = models.CharField(max_length=100, null=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass UnitPlan(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_unit_plans'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=100)\n    content = models.TextField()\n\n    def lessons_by_age_group(self):\n        \"\"\"Returns groups of lessons grouped by the lesson minimum age\n        and maximum ages, and then order by number.\n\n        Returns:\n            A ordered dictionary of grouped lessons.\n            The key is a tuple of the minimum age and maximum ages for\n            the lessons.\n            The value for a key is a sorted list of lessons.\n            The dictionary is ordered by minimum age, then maximum age.\n        \"\"\"\n        grouped_lessons = OrderedDict()\n        lessons = self.unit_plan_lessons.order_by('min_age', 'max_age', 'number')\n        for lesson in lessons:\n            if (lesson.min_age, lesson.max_age) in grouped_lessons:\n                grouped_lessons[(lesson.min_age, lesson.max_age)].append(lesson)\n            else:\n                grouped_lessons[(lesson.min_age, lesson.max_age)] = [lesson]\n        return grouped_lessons\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseDifficulty(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    level = models.PositiveSmallIntegerField(unique=True)\n    name = models.CharField(max_length=100, unique=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExercise(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_programming_exercises'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=200)\n    exercise_set_number = models.PositiveSmallIntegerField()\n    exercise_number = models.PositiveSmallIntegerField()\n    content = models.TextField()\n    learning_outcomes = models.ManyToManyField(\n        LearningOutcome,\n        related_name='programming_exercise_learning_outcomes'\n    )\n    difficulty = models.ForeignKey(\n        ProgrammingExerciseDifficulty,\n        on_delete=models.CASCADE,\n        related_name='difficulty_programming_exercises'\n    )\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseLanguage(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField()\n    name = models.CharField(max_length=200)\n    icon = models.CharField(max_length=100, null=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseLanguageImplementation(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    language = models.ForeignKey(\n        ProgrammingExerciseLanguage,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    exercise = models.ForeignKey(\n        ProgrammingExercise,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    expected_result = models.TextField()\n    hints = models.TextField()\n    solution = models.TextField()\n\n    def __str__(self):\n        return self.name\n\n\nclass Lesson(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_lessons'\n    )\n    unit_plan = models.ForeignKey(\n        UnitPlan,\n        on_delete=models.CASCADE,\n        related_name='unit_plan_lessons'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=100)\n    number = models.IntegerField()\n    content = models.TextField()\n    min_age = models.PositiveSmallIntegerField()\n    max_age = models.PositiveSmallIntegerField()\n    programming_exercises = models.ManyToManyField(\n        ProgrammingExercise,\n        related_name='lessons'\n    )\n    learning_outcomes = models.ManyToManyField(\n        LearningOutcome,\n        related_name='lesson_learning_outcomes'\n    )\n    curriculum_areas = models.ManyToManyField(\n        CurriculumArea,\n        related_name='lesson_curriculum_areas'\n    )\n    classroom_resources = models.ManyToManyField(\n        ClassroomResource,\n        related_name='lesson_classroom_resources'\n    )\n    generated_resources = models.ManyToManyField(\n        Resource,\n        through='ConnectedGeneratedResource',\n        related_name='lesson_generated_resources'\n    )\n\n    def has_programming_exercises(self):\n        \"\"\"Returns a boolean to state whether the lesson has any\n        programming exercises.\n\n        Returns:\n            True if the lesson has connected programming exercises.\n            Otherwise False.\n        \"\"\"\n        return bool(self.programming_exercises.all())\n\n    def __str__(self):\n        return self.name\n\n\nclass CurriculumIntegration(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='curriculum_integrations'\n    )\n    slug = models.SlugField()\n    number = models.PositiveSmallIntegerField()\n    name = models.CharField(max_length=200)\n    content = models.TextField()\n    curriculum_areas = models.ManyToManyField(\n        CurriculumArea,\n        related_name='curriculum_integrations'\n    )\n    prerequisite_lessons = models.ManyToManyField(\n        Lesson,\n        related_name='curriculum_integrations'\n    )\n\n    def has_prerequisite_lessons(self):\n        \"\"\"Returns True if the curriculum integration has at\n        least one prerequisite lesson, otherwise False.\n\n        Returns:\n        True if the curriculum integration has at\n        least one prerequisite lesson, otherwise False.\n        \"\"\"\n        return bool(self.prerequisite_lessons.all())\n\n    def __str__(self):\n        return self.name\n\n\nclass ConnectedGeneratedResource(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    resource = models.ForeignKey(Resource, on_delete=models.CASCADE)\n    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)\n    description = models.CharField(max_length=300)\n", "path": "csunplugged/topics/models.py"}, {"content": "import os.path\nfrom utils.BaseLoader import BaseLoader\nfrom topics.models import CurriculumArea, Lesson\n\n\nclass CurriculumIntegrationsLoader(BaseLoader):\n    \"\"\"Loader for curriculum integrations\"\"\"\n\n    def __init__(self, load_log, structure_file, topic, BASE_PATH):\n        \"\"\"Initiates the loader for curriculum integrations\n\n        Args:\n            structure_file: file path (string)\n            topic: Topic model object\n        \"\"\"\n        super().__init__(BASE_PATH, load_log)\n        self.structure_file = os.path.join(self.BASE_PATH, structure_file)\n        self.BASE_PATH = os.path.join(self.BASE_PATH, os.path.split(structure_file)[0])\n        self.topic = topic\n\n    def load(self):\n        \"\"\"Load the content for curriculum integrations\"\"\"\n        if self.structure_file:\n            structure = self.load_yaml_file(self.structure_file)\n\n            for integration_slug, integration_data in structure.items():\n                md_file = integration_data['md-file']\n                integration_content = self.convert_md_file(os.path.join(self.BASE_PATH, md_file))\n\n                integration = self.topic.curriculum_integrations.create(\n                    slug=integration_slug,\n                    number=integration_data['number'],\n                    name=integration_content.title,\n                    content=integration_content.html_string,\n                )\n                integration.save()\n\n                # Add curriculum areas\n                curriculum_area_slugs = integration_data['curriculum-areas']\n                for curriculum_area_slug in curriculum_area_slugs:\n                    curriculum_area = CurriculumArea.objects.get(\n                        slug=curriculum_area_slug\n                    )\n                    integration.curriculum_areas.add(curriculum_area)\n\n                # Add prerequisite lessons\n                if 'prerequisite-lessons' in integration_data:\n                    prerequisite_lessons_slugs = integration_data['prerequisite-lessons']\n                    for prerequisite_lessons_slug in prerequisite_lessons_slugs:\n                        (unit_plan_slug, lesson_slug) = prerequisite_lessons_slug.split('/')\n                        print(unit_plan_slug, lesson_slug)\n                        lesson = Lesson.objects.get(\n                            slug=lesson_slug,\n                            unit_plan__slug=unit_plan_slug,\n                            topic__slug=self.topic.slug\n                        )\n                        integration.prerequisite_lessons.add(lesson)\n\n                self.log('Added Curriculum Integration: {}'.format(integration.name), 1)\n", "path": "csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py"}], "after_files": [{"content": "import os.path\nfrom utils.BaseLoader import BaseLoader\nfrom topics.models import (\n    LearningOutcome,\n    ProgrammingExerciseDifficulty,\n    ProgrammingExerciseLanguage,\n    ProgrammingExerciseLanguageImplementation,\n)\n\n\nclass ProgrammingExerciseLoader(BaseLoader):\n    \"\"\"Loader for a programming exercise\"\"\"\n\n    def __init__(self, load_log, exercise_slug, exercise_structure, topic, BASE_PATH):\n        \"\"\"Initiates the loader for a programming exercise\n\n        Args:\n            exercise_slug (string): slug for exercise\n            exercise_structure (dict): attributes for exercise\n            topic: Topic model object\n        \"\"\"\n        super().__init__(BASE_PATH, load_log)\n        self.exercise_slug = exercise_slug\n        self.exercise_structure = exercise_structure\n        self.topic = topic\n\n    def load(self):\n        \"\"\"load the content for a programming exercise\"\"\"\n        content = self.convert_md_file(os.path.join(self.BASE_PATH, self.exercise_structure['md-file']))\n\n        programming_exercise = self.topic.topic_programming_exercises.create(\n            slug=self.exercise_slug,\n            name=content.title,\n            exercise_set_number=self.exercise_structure['exercise-set-number'],\n            exercise_number=self.exercise_structure['exercise-number'],\n            content=content.html_string,\n            difficulty=ProgrammingExerciseDifficulty.objects.get(\n                level=self.exercise_structure['difficulty-level']\n            )\n        )\n        programming_exercise.save()\n\n        LOG_TEMPLATE = 'Added Programming Exercise: {}'\n        self.log(LOG_TEMPLATE.format(programming_exercise.name), 1)\n\n        language_solutions = self.exercise_structure['programming-languages']\n        for language in language_solutions:\n            # This gets the language for the solution, if not found it should throw an error!\n            language_object = ProgrammingExerciseLanguage.objects.get(\n                slug=language\n            )\n\n            expected_result_path = os.path.join(self.BASE_PATH, language_solutions[language]['expected-result'])\n            expected_result_content = self.convert_md_file(expected_result_path).html_string\n\n            hint_path = os.path.join(self.BASE_PATH, language_solutions[language]['hints'])\n            hint_content = self.convert_md_file(hint_path).html_string\n\n            solution_path = os.path.join(self.BASE_PATH, language_solutions[language]['solution'])\n            solution_content = self.convert_md_file(solution_path).html_string\n\n            implementation = ProgrammingExerciseLanguageImplementation.objects.create(\n                expected_result=expected_result_content,\n                hints=hint_content,\n                solution=solution_content,\n                language=language_object,\n                exercise=programming_exercise,\n                topic=self.topic\n            )\n            implementation.save()\n\n            LOG_TEMPLATE = 'Added Language Implementation: {}'\n            self.log(LOG_TEMPLATE.format(implementation.language), 2)\n\n        for learning_outcome_slug in self.exercise_structure['learning-outcomes']:\n            learning_outcome = LearningOutcome.objects.get(\n                slug=learning_outcome_slug\n            )\n            programming_exercise.learning_outcomes.add(learning_outcome)\n", "path": "csunplugged/topics/management/commands/_ProgrammingExerciseLoader.py"}, {"content": "from collections import OrderedDict\n\nfrom django.db import models\nfrom resources.models import Resource\n\n\nclass LearningOutcome(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    text = models.CharField(max_length=200, unique=True)\n\n    def __str__(self):\n        return self.text\n\n\nclass CurriculumArea(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    name = models.CharField(max_length=100, unique=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ClassroomResource(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    text = models.CharField(max_length=300, unique=True)\n\n    def __str__(self):\n        return self.text\n\n\nclass Topic(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField(unique=True)\n    name = models.CharField(max_length=100)\n    content = models.TextField()\n    other_resources = models.TextField()\n    icon = models.CharField(max_length=100, null=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass UnitPlan(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_unit_plans'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=100)\n    content = models.TextField()\n\n    def lessons_by_age_group(self):\n        \"\"\"Returns groups of lessons grouped by the lesson minimum age\n        and maximum ages, and then order by number.\n\n        Returns:\n            A ordered dictionary of grouped lessons.\n            The key is a tuple of the minimum age and maximum ages for\n            the lessons.\n            The value for a key is a sorted list of lessons.\n            The dictionary is ordered by minimum age, then maximum age.\n        \"\"\"\n        grouped_lessons = OrderedDict()\n        lessons = self.unit_plan_lessons.order_by('min_age', 'max_age', 'number')\n        for lesson in lessons:\n            if (lesson.min_age, lesson.max_age) in grouped_lessons:\n                grouped_lessons[(lesson.min_age, lesson.max_age)].append(lesson)\n            else:\n                grouped_lessons[(lesson.min_age, lesson.max_age)] = [lesson]\n        return grouped_lessons\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseDifficulty(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    level = models.PositiveSmallIntegerField(unique=True)\n    name = models.CharField(max_length=100, unique=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExercise(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_programming_exercises'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=200)\n    exercise_set_number = models.PositiveSmallIntegerField()\n    exercise_number = models.PositiveSmallIntegerField()\n    content = models.TextField()\n    learning_outcomes = models.ManyToManyField(\n        LearningOutcome,\n        related_name='programming_exercise_learning_outcomes'\n    )\n    difficulty = models.ForeignKey(\n        ProgrammingExerciseDifficulty,\n        on_delete=models.CASCADE,\n        related_name='difficulty_programming_exercises'\n    )\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseLanguage(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    slug = models.SlugField()\n    name = models.CharField(max_length=200)\n    icon = models.CharField(max_length=100, null=True)\n\n    def __str__(self):\n        return self.name\n\n\nclass ProgrammingExerciseLanguageImplementation(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    language = models.ForeignKey(\n        ProgrammingExerciseLanguage,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    exercise = models.ForeignKey(\n        ProgrammingExercise,\n        on_delete=models.CASCADE,\n        related_name='implementations'\n    )\n    expected_result = models.TextField()\n    hints = models.TextField()\n    solution = models.TextField()\n\n    def __str__(self):\n        return '{} for exercise {}.{}, {}'.format(\n            self.language.name,\n            self.exercise.exercise_set_number,\n            self.exercise.exercise_number,\n            self.exercise.name\n        )\n\n\nclass Lesson(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='topic_lessons'\n    )\n    unit_plan = models.ForeignKey(\n        UnitPlan,\n        on_delete=models.CASCADE,\n        related_name='unit_plan_lessons'\n    )\n    slug = models.SlugField()\n    name = models.CharField(max_length=100)\n    number = models.IntegerField()\n    content = models.TextField()\n    min_age = models.PositiveSmallIntegerField()\n    max_age = models.PositiveSmallIntegerField()\n    programming_exercises = models.ManyToManyField(\n        ProgrammingExercise,\n        related_name='lessons'\n    )\n    learning_outcomes = models.ManyToManyField(\n        LearningOutcome,\n        related_name='lesson_learning_outcomes'\n    )\n    curriculum_areas = models.ManyToManyField(\n        CurriculumArea,\n        related_name='lesson_curriculum_areas'\n    )\n    classroom_resources = models.ManyToManyField(\n        ClassroomResource,\n        related_name='lesson_classroom_resources'\n    )\n    generated_resources = models.ManyToManyField(\n        Resource,\n        through='ConnectedGeneratedResource',\n        related_name='lesson_generated_resources'\n    )\n\n    def has_programming_exercises(self):\n        \"\"\"Returns a boolean to state whether the lesson has any\n        programming exercises.\n\n        Returns:\n            True if the lesson has connected programming exercises.\n            Otherwise False.\n        \"\"\"\n        return bool(self.programming_exercises.all())\n\n    def __str__(self):\n        return self.name\n\n\nclass CurriculumIntegration(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    topic = models.ForeignKey(\n        Topic,\n        on_delete=models.CASCADE,\n        related_name='curriculum_integrations'\n    )\n    slug = models.SlugField()\n    number = models.PositiveSmallIntegerField()\n    name = models.CharField(max_length=200)\n    content = models.TextField()\n    curriculum_areas = models.ManyToManyField(\n        CurriculumArea,\n        related_name='curriculum_integrations'\n    )\n    prerequisite_lessons = models.ManyToManyField(\n        Lesson,\n        related_name='curriculum_integrations'\n    )\n\n    def has_prerequisite_lessons(self):\n        \"\"\"Returns True if the curriculum integration has at\n        least one prerequisite lesson, otherwise False.\n\n        Returns:\n        True if the curriculum integration has at\n        least one prerequisite lesson, otherwise False.\n        \"\"\"\n        return bool(self.prerequisite_lessons.all())\n\n    def __str__(self):\n        return self.name\n\n\nclass ConnectedGeneratedResource(models.Model):\n    #  Auto-incrementing 'id' field is automatically set by Django\n    resource = models.ForeignKey(Resource, on_delete=models.CASCADE)\n    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)\n    description = models.CharField(max_length=300)\n", "path": "csunplugged/topics/models.py"}, {"content": "import os.path\nfrom utils.BaseLoader import BaseLoader\nfrom topics.models import CurriculumArea, Lesson\n\n\nclass CurriculumIntegrationsLoader(BaseLoader):\n    \"\"\"Loader for curriculum integrations\"\"\"\n\n    def __init__(self, load_log, structure_file, topic, BASE_PATH):\n        \"\"\"Initiates the loader for curriculum integrations\n\n        Args:\n            structure_file: file path (string)\n            topic: Topic model object\n        \"\"\"\n        super().__init__(BASE_PATH, load_log)\n        self.structure_file = os.path.join(self.BASE_PATH, structure_file)\n        self.BASE_PATH = os.path.join(self.BASE_PATH, os.path.split(structure_file)[0])\n        self.topic = topic\n\n    def load(self):\n        \"\"\"Load the content for curriculum integrations\"\"\"\n        if self.structure_file:\n            structure = self.load_yaml_file(self.structure_file)\n\n            for integration_slug, integration_data in structure.items():\n                md_file = integration_data['md-file']\n                integration_content = self.convert_md_file(os.path.join(self.BASE_PATH, md_file))\n\n                integration = self.topic.curriculum_integrations.create(\n                    slug=integration_slug,\n                    number=integration_data['number'],\n                    name=integration_content.title,\n                    content=integration_content.html_string,\n                )\n                integration.save()\n\n                # Add curriculum areas\n                curriculum_area_slugs = integration_data['curriculum-areas']\n                for curriculum_area_slug in curriculum_area_slugs:\n                    curriculum_area = CurriculumArea.objects.get(\n                        slug=curriculum_area_slug\n                    )\n                    integration.curriculum_areas.add(curriculum_area)\n\n                # Add prerequisite lessons\n                if 'prerequisite-lessons' in integration_data:\n                    prerequisite_lessons_slugs = integration_data['prerequisite-lessons']\n                    for prerequisite_lessons_slug in prerequisite_lessons_slugs:\n                        (unit_plan_slug, lesson_slug) = prerequisite_lessons_slug.split('/')\n                        lesson = Lesson.objects.get(\n                            slug=lesson_slug,\n                            unit_plan__slug=unit_plan_slug,\n                            topic__slug=self.topic.slug\n                        )\n                        integration.prerequisite_lessons.add(lesson)\n\n                self.log('Added Curriculum Integration: {}'.format(integration.name), 1)\n", "path": "csunplugged/topics/management/commands/_CurriculumIntegrationsLoader.py"}]}