We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
The exercise results page does not show that a module is open for reading while the exercises are still closed.
The UI leaves the impression that the module is completely closed (between the reading opening time and the opening time). The "opens" badge in the module box also only shows the normal open time. The UI should show clearly that a module is open for reading. The collapsing box should be opened by default for read-open modules.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `exercise/templatetags/exercise.py`
Content:
```
1 import json
2 from django import template
3 from django.db.models import Max, Min
4 from django.template.loader import render_to_string
5 from django.utils import timezone
6 from django.utils.translation import ugettext_lazy as _
7 
8 from course.models import CourseModule
9 from lib.errors import TagUsageError
10 from ..cache.content import CachedContent
11 from ..cache.points import CachedPoints
12 from ..exercise_summary import UserExerciseSummary
13 from ..models import LearningObjectDisplay, LearningObject, Submission, BaseExercise
14 
15 
16 register = template.Library()
17 
18 
19 def _prepare_now(context):
20     if not 'now' in context:
21         context['now'] = timezone.now()
22     return context['now']
23 
24 
25 def _prepare_context(context, student=None):
26     if not 'instance' in context:
27         raise TagUsageError()
28     instance = context['instance']
29     _prepare_now(context)
30     if not 'content' in context:
31         context['content'] = CachedContent(instance)
32     def points(user, key):
33         if not key in context:
34             context[key] = CachedPoints(instance, user, context['content'])
35         return context[key]
36     if student:
37         return points(student, 'studentpoints')
38     return points(context['request'].user, 'points')
39 
40 
41 def _get_toc(context, student=None):
42     points = _prepare_context(context, student)
43     context = context.flatten()
44     context.update({
45         'modules': points.modules_flatted(),
46         'categories': points.categories(),
47         'total': points.total(),
48         'is_course_staff': context.get('is_course_staff', False),
49     })
50     return context
51 
52 
53 def _is_accessible(context, entry, t):
54     if t and t > _prepare_now(context):
55         return False
56     if entry.get('requirements'):
57         points = _prepare_context(context)
58         module = CourseModule.objects.get(id=entry['id'])
59         return module.are_requirements_passed(points)
60     return True
61 
62 
63 @register.inclusion_tag("exercise/_user_results.html", takes_context=True)
64 def user_results(context, student=None):
65     values = _get_toc(context, student)
66     values['total_json'] = json.dumps(values['total'])
67     if student:
68         values['is_course_staff'] = False
69     return values
70 
71 
72 @register.inclusion_tag("exercise/_user_toc.html", takes_context=True)
73 def user_toc(context, student=None):
74     return _get_toc(context, student)
75 
76 
77 @register.inclusion_tag("exercise/_user_last.html", takes_context=True)
78 def user_last(context):
79     user = context['request'].user
80     points = _prepare_context(context)
81     if user.is_authenticated:
82         last = LearningObjectDisplay.objects.filter(
83             profile=user.userprofile,
84             learning_object__status=LearningObject.STATUS.READY,
85             learning_object__course_module__course_instance=context['instance'],
86         ).select_related('learning_object').order_by('-timestamp').first()
87         if last:
88             entry,_,_,_ = points.find(last.learning_object)
89             return {
90                 'last': entry,
91                 'last_time': last.timestamp,
92             }
93     return {
94         'begin': points.begin(),
95         'instance': context['instance'],
96     }
97 
98 
99 @register.inclusion_tag("exercise/_category_points.html", takes_context=True)
100 def category_points(context, student=None):
101     return _get_toc(context, student)
102 
103 
104 @register.inclusion_tag("exercise/_submission_list.html", takes_context=True)
105 def latest_submissions(context):
106     submissions = context["profile"].submissions \
107         .filter(exercise__course_module__course_instance=context["instance"]) \
108         .order_by("-id")[:10]
109     return {
110         "submissions": submissions,
111         "title": _("Latest submissions"),
112         "empty": _("No submissions for this course."),
113     }
114 
115 
116 @register.filter
117 def max_submissions(exercise, user_profile):
118     return exercise.max_submissions_for_student(user_profile)
119 
120 
121 @register.filter
122 def percent(decimal):
123     return int(decimal * 100)
124 
125 
126 @register.filter
127 def submission_status(status):
128     return Submission.STATUS[status]
129 
130 
131 def _points_data(obj, classes=None):
132     if isinstance(obj, UserExerciseSummary):
133         exercise = obj.exercise
134         data = {
135             'points': obj.get_points(),
136             'max': exercise.max_points,
137             'difficulty': exercise.difficulty,
138             'required': exercise.points_to_pass,
139             'confirm_the_level': exercise.category.confirm_the_level,
140             'missing_points': obj.is_missing_points(),
141             'passed': obj.is_passed(),
142             'full_score': obj.is_full_points(),
143             'submitted': obj.is_submitted(),
144             'graded': obj.is_graded(),
145             'official': not obj.is_unofficial(),
146             'exercise_page': True,
147         }
148     elif isinstance(obj, Submission):
149         exercise = obj.exercise
150         data = {
151             'points': obj.grade,
152             'max': exercise.max_points,
153             'difficulty': exercise.difficulty,
154             'required': exercise.points_to_pass,
155             'confirm_the_level': exercise.category.confirm_the_level,
156             'missing_points': obj.grade < exercise.points_to_pass,
157             'passed': obj.grade >= exercise.points_to_pass,
158             'full_score': obj.grade >= exercise.max_points,
159             'submitted': True,
160             'graded': obj.is_graded,
161             'official': obj.status != Submission.STATUS.UNOFFICIAL,
162         }
163         if not obj.is_graded and (
164                     not exercise.category.confirm_the_level
165                     or obj.status != Submission.STATUS.WAITING
166                 ):
167             data['status'] = obj.status
168     else:
169         points = obj.get('points', 0)
170         max_points = obj.get('max_points', 0)
171         required = obj.get('points_to_pass', 0)
172         data = {
173             'points': points,
174             'max': max_points,
175             'difficulty': obj.get('difficulty', ''),
176             'required': required,
177             'confirm_the_level': obj.get('confirm_the_level', False),
178             'missing_points': points < required,
179             'passed': obj.get('passed', True),
180             'full_score': points >= max_points,
181             'submitted': obj.get('submission_count', 0) > 0,
182             'graded': obj.get('graded', True),
183             'status': obj.get('submission_status', False),
184             'unconfirmed': obj.get('unconfirmed', False),
185             'official': not obj.get('unofficial', False),
186             'confirmable_points': obj.get('confirmable_points', False),
187         }
188     percentage = 0
189     required_percentage = None
190     if data['max'] > 0:
191         percentage = int(round(100.0 * data['points'] / data['max']))
192         if data['required']:
193             required_percentage = int(round(100.0 * data['required'] / data['max']))
194     data.update({
195         'classes': classes,
196         'percentage': percentage,
197         'required_percentage': required_percentage,
198     })
199     return data
200 
201 
202 @register.inclusion_tag("exercise/_points_progress.html")
203 def points_progress(obj):
204     return _points_data(obj)
205 
206 
207 @register.inclusion_tag("exercise/_points_badge.html")
208 def points_badge(obj, classes=None):
209     return _points_data(obj, classes)
210 
211 
212 @register.simple_tag(takes_context=True)
213 def max_group_size(context):
214     points = _prepare_context(context)
215     return points.total()['max_group_size']
216 
217 
218 @register.simple_tag(takes_context=True)
219 def min_group_size(context):
220     points = _prepare_context(context)
221     return points.total()['min_group_size']
222 
223 
224 @register.simple_tag(takes_context=True)
225 def module_accessible(context, entry):
226     t = entry.get('reading_opening_time', entry.get('opening_time'))
227     return _is_accessible(context, entry, t)
228 
229 
230 @register.simple_tag(takes_context=True)
231 def exercise_accessible(context, entry):
232     t = entry.get('opening_time')
233     return _is_accessible(context, entry, t)
234 
235 
236 @register.simple_tag
237 def get_grading_errors(submission):
238     if not isinstance(submission.grading_data, dict):
239         return ""
240     grading_data = submission.grading_data.get('grading_data')
241     if not isinstance(grading_data, str):
242         return ""
243     if grading_data.startswith('<pre>'):
244         return grading_data[5:-6]
245     try:
246         return json.loads(grading_data).get('errors', "")
247     except (AttributeError, TypeError, ValueError):
248         return ""
249 
250 
251 @register.inclusion_tag("exercise/_text_stats.html", takes_context=True)
252 def exercise_text_stats(context, exercise):
253     if not 'instance' in context:
254         raise TagUsageError()
255     instance = context['instance']
256 
257     if not 'student_count' in context:
258         context['student_count'] = instance.students.count()
259     total = context['student_count']
260 
261     if isinstance(exercise, int):
262         num = instance.students.filter(submissions__exercise_id=exercise).distinct().count()
263     else:
264         num = exercise.number_of_submitters() if exercise else 0
265     return {
266         "number": num,
267         "percentage": int(100 * num / total) if total else 0,
268     }
269 
270 
271 @register.simple_tag
272 def get_format_info(format):
273     format_infos = {
274         'json' : {
275             'name': 'json',
276             'verbose_name': 'JSON',
277         },
278         'csv': {
279             'name': 'csv',
280             'verbose_name': 'CSV',
281         },
282         'excel.csv': {
283             'name': 'excel.csv',
284             'verbose_name': _('Excel compatible CSV'),
285         },
286     }
287     try:
288         return format_infos[format]
289     except KeyError as e:
290         raise RuntimeError('Invalid format: \'{}\''.format(format)) from e
291 
292 
293 @register.simple_tag
294 def get_format_info_list(formats):
295     return [get_format_info(format) for format in formats.split()]
296 
297
```
--- 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/exercise/templatetags/exercise.py b/exercise/templatetags/exercise.py
--- a/exercise/templatetags/exercise.py
+++ b/exercise/templatetags/exercise.py
@@ -223,8 +223,11 @@
 
 @register.simple_tag(takes_context=True)
 def module_accessible(context, entry):
-    t = entry.get('reading_opening_time', entry.get('opening_time'))
-    return _is_accessible(context, entry, t)
+    t = entry.get('reading_opening_time')
+    if t:
+        return _is_accessible(context, entry, t)
+    else:
+        return exercise_accessible(context, entry)
 
 
 @register.simple_tag(takes_context=True)
@@ -293,4 +296,3 @@
 @register.simple_tag
 def get_format_info_list(formats):
     return [get_format_info(format) for format in formats.split()]
-

{"golden_diff": "diff --git a/exercise/templatetags/exercise.py b/exercise/templatetags/exercise.py\n--- a/exercise/templatetags/exercise.py\n+++ b/exercise/templatetags/exercise.py\n@@ -223,8 +223,11 @@\n \n @register.simple_tag(takes_context=True)\n def module_accessible(context, entry):\n-    t = entry.get('reading_opening_time', entry.get('opening_time'))\n-    return _is_accessible(context, entry, t)\n+    t = entry.get('reading_opening_time')\n+    if t:\n+        return _is_accessible(context, entry, t)\n+    else:\n+        return exercise_accessible(context, entry)\n \n \n @register.simple_tag(takes_context=True)\n@@ -293,4 +296,3 @@\n @register.simple_tag\n def get_format_info_list(formats):\n     return [get_format_info(format) for format in formats.split()]\n-\n", "issue": "The exercise results page does not show that a module is open for reading while the exercises are still closed.\nThe UI leaves the impression that the module is completely closed (between the reading opening time and the opening time). The \"opens\" badge in the module box also only shows the normal open time. The UI should show clearly that a module is open for reading. The collapsing box should be opened by default for read-open modules.\n", "before_files": [{"content": "import json\nfrom django import template\nfrom django.db.models import Max, Min\nfrom django.template.loader import render_to_string\nfrom django.utils import timezone\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom course.models import CourseModule\nfrom lib.errors import TagUsageError\nfrom ..cache.content import CachedContent\nfrom ..cache.points import CachedPoints\nfrom ..exercise_summary import UserExerciseSummary\nfrom ..models import LearningObjectDisplay, LearningObject, Submission, BaseExercise\n\n\nregister = template.Library()\n\n\ndef _prepare_now(context):\n    if not 'now' in context:\n        context['now'] = timezone.now()\n    return context['now']\n\n\ndef _prepare_context(context, student=None):\n    if not 'instance' in context:\n        raise TagUsageError()\n    instance = context['instance']\n    _prepare_now(context)\n    if not 'content' in context:\n        context['content'] = CachedContent(instance)\n    def points(user, key):\n        if not key in context:\n            context[key] = CachedPoints(instance, user, context['content'])\n        return context[key]\n    if student:\n        return points(student, 'studentpoints')\n    return points(context['request'].user, 'points')\n\n\ndef _get_toc(context, student=None):\n    points = _prepare_context(context, student)\n    context = context.flatten()\n    context.update({\n        'modules': points.modules_flatted(),\n        'categories': points.categories(),\n        'total': points.total(),\n        'is_course_staff': context.get('is_course_staff', False),\n    })\n    return context\n\n\ndef _is_accessible(context, entry, t):\n    if t and t > _prepare_now(context):\n        return False\n    if entry.get('requirements'):\n        points = _prepare_context(context)\n        module = CourseModule.objects.get(id=entry['id'])\n        return module.are_requirements_passed(points)\n    return True\n\n\n@register.inclusion_tag(\"exercise/_user_results.html\", takes_context=True)\ndef user_results(context, student=None):\n    values = _get_toc(context, student)\n    values['total_json'] = json.dumps(values['total'])\n    if student:\n        values['is_course_staff'] = False\n    return values\n\n\n@register.inclusion_tag(\"exercise/_user_toc.html\", takes_context=True)\ndef user_toc(context, student=None):\n    return _get_toc(context, student)\n\n\n@register.inclusion_tag(\"exercise/_user_last.html\", takes_context=True)\ndef user_last(context):\n    user = context['request'].user\n    points = _prepare_context(context)\n    if user.is_authenticated:\n        last = LearningObjectDisplay.objects.filter(\n            profile=user.userprofile,\n            learning_object__status=LearningObject.STATUS.READY,\n            learning_object__course_module__course_instance=context['instance'],\n        ).select_related('learning_object').order_by('-timestamp').first()\n        if last:\n            entry,_,_,_ = points.find(last.learning_object)\n            return {\n                'last': entry,\n                'last_time': last.timestamp,\n            }\n    return {\n        'begin': points.begin(),\n        'instance': context['instance'],\n    }\n\n\n@register.inclusion_tag(\"exercise/_category_points.html\", takes_context=True)\ndef category_points(context, student=None):\n    return _get_toc(context, student)\n\n\n@register.inclusion_tag(\"exercise/_submission_list.html\", takes_context=True)\ndef latest_submissions(context):\n    submissions = context[\"profile\"].submissions \\\n        .filter(exercise__course_module__course_instance=context[\"instance\"]) \\\n        .order_by(\"-id\")[:10]\n    return {\n        \"submissions\": submissions,\n        \"title\": _(\"Latest submissions\"),\n        \"empty\": _(\"No submissions for this course.\"),\n    }\n\n\n@register.filter\ndef max_submissions(exercise, user_profile):\n    return exercise.max_submissions_for_student(user_profile)\n\n\n@register.filter\ndef percent(decimal):\n    return int(decimal * 100)\n\n\n@register.filter\ndef submission_status(status):\n    return Submission.STATUS[status]\n\n\ndef _points_data(obj, classes=None):\n    if isinstance(obj, UserExerciseSummary):\n        exercise = obj.exercise\n        data = {\n            'points': obj.get_points(),\n            'max': exercise.max_points,\n            'difficulty': exercise.difficulty,\n            'required': exercise.points_to_pass,\n            'confirm_the_level': exercise.category.confirm_the_level,\n            'missing_points': obj.is_missing_points(),\n            'passed': obj.is_passed(),\n            'full_score': obj.is_full_points(),\n            'submitted': obj.is_submitted(),\n            'graded': obj.is_graded(),\n            'official': not obj.is_unofficial(),\n            'exercise_page': True,\n        }\n    elif isinstance(obj, Submission):\n        exercise = obj.exercise\n        data = {\n            'points': obj.grade,\n            'max': exercise.max_points,\n            'difficulty': exercise.difficulty,\n            'required': exercise.points_to_pass,\n            'confirm_the_level': exercise.category.confirm_the_level,\n            'missing_points': obj.grade < exercise.points_to_pass,\n            'passed': obj.grade >= exercise.points_to_pass,\n            'full_score': obj.grade >= exercise.max_points,\n            'submitted': True,\n            'graded': obj.is_graded,\n            'official': obj.status != Submission.STATUS.UNOFFICIAL,\n        }\n        if not obj.is_graded and (\n                    not exercise.category.confirm_the_level\n                    or obj.status != Submission.STATUS.WAITING\n                ):\n            data['status'] = obj.status\n    else:\n        points = obj.get('points', 0)\n        max_points = obj.get('max_points', 0)\n        required = obj.get('points_to_pass', 0)\n        data = {\n            'points': points,\n            'max': max_points,\n            'difficulty': obj.get('difficulty', ''),\n            'required': required,\n            'confirm_the_level': obj.get('confirm_the_level', False),\n            'missing_points': points < required,\n            'passed': obj.get('passed', True),\n            'full_score': points >= max_points,\n            'submitted': obj.get('submission_count', 0) > 0,\n            'graded': obj.get('graded', True),\n            'status': obj.get('submission_status', False),\n            'unconfirmed': obj.get('unconfirmed', False),\n            'official': not obj.get('unofficial', False),\n            'confirmable_points': obj.get('confirmable_points', False),\n        }\n    percentage = 0\n    required_percentage = None\n    if data['max'] > 0:\n        percentage = int(round(100.0 * data['points'] / data['max']))\n        if data['required']:\n            required_percentage = int(round(100.0 * data['required'] / data['max']))\n    data.update({\n        'classes': classes,\n        'percentage': percentage,\n        'required_percentage': required_percentage,\n    })\n    return data\n\n\n@register.inclusion_tag(\"exercise/_points_progress.html\")\ndef points_progress(obj):\n    return _points_data(obj)\n\n\n@register.inclusion_tag(\"exercise/_points_badge.html\")\ndef points_badge(obj, classes=None):\n    return _points_data(obj, classes)\n\n\n@register.simple_tag(takes_context=True)\ndef max_group_size(context):\n    points = _prepare_context(context)\n    return points.total()['max_group_size']\n\n\n@register.simple_tag(takes_context=True)\ndef min_group_size(context):\n    points = _prepare_context(context)\n    return points.total()['min_group_size']\n\n\n@register.simple_tag(takes_context=True)\ndef module_accessible(context, entry):\n    t = entry.get('reading_opening_time', entry.get('opening_time'))\n    return _is_accessible(context, entry, t)\n\n\n@register.simple_tag(takes_context=True)\ndef exercise_accessible(context, entry):\n    t = entry.get('opening_time')\n    return _is_accessible(context, entry, t)\n\n\n@register.simple_tag\ndef get_grading_errors(submission):\n    if not isinstance(submission.grading_data, dict):\n        return \"\"\n    grading_data = submission.grading_data.get('grading_data')\n    if not isinstance(grading_data, str):\n        return \"\"\n    if grading_data.startswith('<pre>'):\n        return grading_data[5:-6]\n    try:\n        return json.loads(grading_data).get('errors', \"\")\n    except (AttributeError, TypeError, ValueError):\n        return \"\"\n\n\n@register.inclusion_tag(\"exercise/_text_stats.html\", takes_context=True)\ndef exercise_text_stats(context, exercise):\n    if not 'instance' in context:\n        raise TagUsageError()\n    instance = context['instance']\n\n    if not 'student_count' in context:\n        context['student_count'] = instance.students.count()\n    total = context['student_count']\n\n    if isinstance(exercise, int):\n        num = instance.students.filter(submissions__exercise_id=exercise).distinct().count()\n    else:\n        num = exercise.number_of_submitters() if exercise else 0\n    return {\n        \"number\": num,\n        \"percentage\": int(100 * num / total) if total else 0,\n    }\n\n\n@register.simple_tag\ndef get_format_info(format):\n    format_infos = {\n        'json' : {\n            'name': 'json',\n            'verbose_name': 'JSON',\n        },\n        'csv': {\n            'name': 'csv',\n            'verbose_name': 'CSV',\n        },\n        'excel.csv': {\n            'name': 'excel.csv',\n            'verbose_name': _('Excel compatible CSV'),\n        },\n    }\n    try:\n        return format_infos[format]\n    except KeyError as e:\n        raise RuntimeError('Invalid format: \\'{}\\''.format(format)) from e\n\n\n@register.simple_tag\ndef get_format_info_list(formats):\n    return [get_format_info(format) for format in formats.split()]\n\n", "path": "exercise/templatetags/exercise.py"}], "after_files": [{"content": "import json\nfrom django import template\nfrom django.db.models import Max, Min\nfrom django.template.loader import render_to_string\nfrom django.utils import timezone\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom course.models import CourseModule\nfrom lib.errors import TagUsageError\nfrom ..cache.content import CachedContent\nfrom ..cache.points import CachedPoints\nfrom ..exercise_summary import UserExerciseSummary\nfrom ..models import LearningObjectDisplay, LearningObject, Submission, BaseExercise\n\n\nregister = template.Library()\n\n\ndef _prepare_now(context):\n    if not 'now' in context:\n        context['now'] = timezone.now()\n    return context['now']\n\n\ndef _prepare_context(context, student=None):\n    if not 'instance' in context:\n        raise TagUsageError()\n    instance = context['instance']\n    _prepare_now(context)\n    if not 'content' in context:\n        context['content'] = CachedContent(instance)\n    def points(user, key):\n        if not key in context:\n            context[key] = CachedPoints(instance, user, context['content'])\n        return context[key]\n    if student:\n        return points(student, 'studentpoints')\n    return points(context['request'].user, 'points')\n\n\ndef _get_toc(context, student=None):\n    points = _prepare_context(context, student)\n    context = context.flatten()\n    context.update({\n        'modules': points.modules_flatted(),\n        'categories': points.categories(),\n        'total': points.total(),\n        'is_course_staff': context.get('is_course_staff', False),\n    })\n    return context\n\n\ndef _is_accessible(context, entry, t):\n    if t and t > _prepare_now(context):\n        return False\n    if entry.get('requirements'):\n        points = _prepare_context(context)\n        module = CourseModule.objects.get(id=entry['id'])\n        return module.are_requirements_passed(points)\n    return True\n\n\n@register.inclusion_tag(\"exercise/_user_results.html\", takes_context=True)\ndef user_results(context, student=None):\n    values = _get_toc(context, student)\n    values['total_json'] = json.dumps(values['total'])\n    if student:\n        values['is_course_staff'] = False\n    return values\n\n\n@register.inclusion_tag(\"exercise/_user_toc.html\", takes_context=True)\ndef user_toc(context, student=None):\n    return _get_toc(context, student)\n\n\n@register.inclusion_tag(\"exercise/_user_last.html\", takes_context=True)\ndef user_last(context):\n    user = context['request'].user\n    points = _prepare_context(context)\n    if user.is_authenticated:\n        last = LearningObjectDisplay.objects.filter(\n            profile=user.userprofile,\n            learning_object__status=LearningObject.STATUS.READY,\n            learning_object__course_module__course_instance=context['instance'],\n        ).select_related('learning_object').order_by('-timestamp').first()\n        if last:\n            entry,_,_,_ = points.find(last.learning_object)\n            return {\n                'last': entry,\n                'last_time': last.timestamp,\n            }\n    return {\n        'begin': points.begin(),\n        'instance': context['instance'],\n    }\n\n\n@register.inclusion_tag(\"exercise/_category_points.html\", takes_context=True)\ndef category_points(context, student=None):\n    return _get_toc(context, student)\n\n\n@register.inclusion_tag(\"exercise/_submission_list.html\", takes_context=True)\ndef latest_submissions(context):\n    submissions = context[\"profile\"].submissions \\\n        .filter(exercise__course_module__course_instance=context[\"instance\"]) \\\n        .order_by(\"-id\")[:10]\n    return {\n        \"submissions\": submissions,\n        \"title\": _(\"Latest submissions\"),\n        \"empty\": _(\"No submissions for this course.\"),\n    }\n\n\n@register.filter\ndef max_submissions(exercise, user_profile):\n    return exercise.max_submissions_for_student(user_profile)\n\n\n@register.filter\ndef percent(decimal):\n    return int(decimal * 100)\n\n\n@register.filter\ndef submission_status(status):\n    return Submission.STATUS[status]\n\n\ndef _points_data(obj, classes=None):\n    if isinstance(obj, UserExerciseSummary):\n        exercise = obj.exercise\n        data = {\n            'points': obj.get_points(),\n            'max': exercise.max_points,\n            'difficulty': exercise.difficulty,\n            'required': exercise.points_to_pass,\n            'confirm_the_level': exercise.category.confirm_the_level,\n            'missing_points': obj.is_missing_points(),\n            'passed': obj.is_passed(),\n            'full_score': obj.is_full_points(),\n            'submitted': obj.is_submitted(),\n            'graded': obj.is_graded(),\n            'official': not obj.is_unofficial(),\n            'exercise_page': True,\n        }\n    elif isinstance(obj, Submission):\n        exercise = obj.exercise\n        data = {\n            'points': obj.grade,\n            'max': exercise.max_points,\n            'difficulty': exercise.difficulty,\n            'required': exercise.points_to_pass,\n            'confirm_the_level': exercise.category.confirm_the_level,\n            'missing_points': obj.grade < exercise.points_to_pass,\n            'passed': obj.grade >= exercise.points_to_pass,\n            'full_score': obj.grade >= exercise.max_points,\n            'submitted': True,\n            'graded': obj.is_graded,\n            'official': obj.status != Submission.STATUS.UNOFFICIAL,\n        }\n        if not obj.is_graded and (\n                    not exercise.category.confirm_the_level\n                    or obj.status != Submission.STATUS.WAITING\n                ):\n            data['status'] = obj.status\n    else:\n        points = obj.get('points', 0)\n        max_points = obj.get('max_points', 0)\n        required = obj.get('points_to_pass', 0)\n        data = {\n            'points': points,\n            'max': max_points,\n            'difficulty': obj.get('difficulty', ''),\n            'required': required,\n            'confirm_the_level': obj.get('confirm_the_level', False),\n            'missing_points': points < required,\n            'passed': obj.get('passed', True),\n            'full_score': points >= max_points,\n            'submitted': obj.get('submission_count', 0) > 0,\n            'graded': obj.get('graded', True),\n            'status': obj.get('submission_status', False),\n            'unconfirmed': obj.get('unconfirmed', False),\n            'official': not obj.get('unofficial', False),\n            'confirmable_points': obj.get('confirmable_points', False),\n        }\n    percentage = 0\n    required_percentage = None\n    if data['max'] > 0:\n        percentage = int(round(100.0 * data['points'] / data['max']))\n        if data['required']:\n            required_percentage = int(round(100.0 * data['required'] / data['max']))\n    data.update({\n        'classes': classes,\n        'percentage': percentage,\n        'required_percentage': required_percentage,\n    })\n    return data\n\n\n@register.inclusion_tag(\"exercise/_points_progress.html\")\ndef points_progress(obj):\n    return _points_data(obj)\n\n\n@register.inclusion_tag(\"exercise/_points_badge.html\")\ndef points_badge(obj, classes=None):\n    return _points_data(obj, classes)\n\n\n@register.simple_tag(takes_context=True)\ndef max_group_size(context):\n    points = _prepare_context(context)\n    return points.total()['max_group_size']\n\n\n@register.simple_tag(takes_context=True)\ndef min_group_size(context):\n    points = _prepare_context(context)\n    return points.total()['min_group_size']\n\n\n@register.simple_tag(takes_context=True)\ndef module_accessible(context, entry):\n    t = entry.get('reading_opening_time')\n    if t:\n        return _is_accessible(context, entry, t)\n    else:\n        return exercise_accessible(context, entry)\n\n\n@register.simple_tag(takes_context=True)\ndef exercise_accessible(context, entry):\n    t = entry.get('opening_time')\n    return _is_accessible(context, entry, t)\n\n\n@register.simple_tag\ndef get_grading_errors(submission):\n    if not isinstance(submission.grading_data, dict):\n        return \"\"\n    grading_data = submission.grading_data.get('grading_data')\n    if not isinstance(grading_data, str):\n        return \"\"\n    if grading_data.startswith('<pre>'):\n        return grading_data[5:-6]\n    try:\n        return json.loads(grading_data).get('errors', \"\")\n    except (AttributeError, TypeError, ValueError):\n        return \"\"\n\n\n@register.inclusion_tag(\"exercise/_text_stats.html\", takes_context=True)\ndef exercise_text_stats(context, exercise):\n    if not 'instance' in context:\n        raise TagUsageError()\n    instance = context['instance']\n\n    if not 'student_count' in context:\n        context['student_count'] = instance.students.count()\n    total = context['student_count']\n\n    if isinstance(exercise, int):\n        num = instance.students.filter(submissions__exercise_id=exercise).distinct().count()\n    else:\n        num = exercise.number_of_submitters() if exercise else 0\n    return {\n        \"number\": num,\n        \"percentage\": int(100 * num / total) if total else 0,\n    }\n\n\n@register.simple_tag\ndef get_format_info(format):\n    format_infos = {\n        'json' : {\n            'name': 'json',\n            'verbose_name': 'JSON',\n        },\n        'csv': {\n            'name': 'csv',\n            'verbose_name': 'CSV',\n        },\n        'excel.csv': {\n            'name': 'excel.csv',\n            'verbose_name': _('Excel compatible CSV'),\n        },\n    }\n    try:\n        return format_infos[format]\n    except KeyError as e:\n        raise RuntimeError('Invalid format: \\'{}\\''.format(format)) from e\n\n\n@register.simple_tag\ndef get_format_info_list(formats):\n    return [get_format_info(format) for format in formats.split()]\n", "path": "exercise/templatetags/exercise.py"}]}