codeforlife-portal 7.1.1__py2.py3-none-any.whl → 7.2.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of codeforlife-portal might be problematic. Click here for more details.
- {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/METADATA +24 -24
- {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/RECORD +27 -24
- {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/WHEEL +1 -1
- example_project/urls.py +2 -0
- portal/__init__.py +1 -1
- portal/forms/teach.py +1 -1
- portal/static/portal/img/RR_logo_grass_background.png +0 -0
- portal/static/portal/img/logo_python_den.svg +21 -0
- portal/static/portal/img/python_den.png +0 -0
- portal/static/portal/img/python_den_banner.svg +26 -0
- portal/static/portal/img/thumbnail_educate_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_python_den.png +0 -0
- portal/static/portal/sass/modules/_colours.scss +1 -0
- portal/static/portal/sass/partials/_text.scss +5 -0
- portal/templates/portal/contribute.html +6 -0
- portal/templates/portal/partials/header.html +10 -0
- portal/templates/portal/play/student_dashboard.html +39 -8
- portal/templates/portal/play.html +34 -0
- portal/templates/portal/teach/teacher_edit_class.html +1 -51
- portal/templates/portal/teach.html +35 -0
- portal/tests/snapshots/snap_test_partials.py +48 -0
- portal/tests/test_views.py +46 -15
- portal/urls.py +1 -1
- portal/views/student/play.py +26 -18
- portal/views/teacher/teach.py +3 -7
- portal/templates/portal/play/independent_student_dashboard.html +0 -51
- {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/LICENSE.md +0 -0
- {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -107,6 +107,7 @@ $color-background-danger: #ff0000;
|
|
|
107
107
|
$color-background-tint-primary: #f1ecec;
|
|
108
108
|
$color-background-sub-banner: $color-secondary-300;
|
|
109
109
|
$color-background-box-shadow: rgba(0, 0, 0, 0.2);
|
|
110
|
+
$color-background-python-den: #213ce7;
|
|
110
111
|
// Popups & Messages
|
|
111
112
|
$color-ui-dialog-border: #808080;
|
|
112
113
|
$color-ui-widget-overlay: #666666;
|
|
@@ -180,6 +180,11 @@ blockquote {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
.background--python-den {
|
|
184
|
+
background-color: $color-background-python-den;
|
|
185
|
+
color: $color-text-secondary;
|
|
186
|
+
}
|
|
187
|
+
|
|
183
188
|
@media (min-width: $tablet-small-min-width) {
|
|
184
189
|
.list--two-columns {
|
|
185
190
|
columns: 2;
|
|
@@ -54,6 +54,12 @@
|
|
|
54
54
|
Built on Blockly, it's a visual programming language similar
|
|
55
55
|
to Scratch. Rapid Router is our flagship game with the
|
|
56
56
|
biggest user base.</p>
|
|
57
|
+
|
|
58
|
+
<p><b>Python Den</b></p>
|
|
59
|
+
<p>Our new Python Den uses Blockly, Blockly/Python split
|
|
60
|
+
screen, and an IDE. It takes students from no previous
|
|
61
|
+
Python experience to a foundation in Python, helping
|
|
62
|
+
prepare them for GCSE and beyond.</p>
|
|
57
63
|
</div>
|
|
58
64
|
<div class="col-sm-6">
|
|
59
65
|
<img title="Rapid Router" alt="Rapid Router"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
Games <span class="material-icons-outlined md-32">arrow_drop_down</span></a>
|
|
38
38
|
<div class="dropdown-content">
|
|
39
39
|
<a href="{% url 'levels' %}"><small>Rapid Router</small></a>
|
|
40
|
+
<a href="{% url 'python_levels' %}"><small>Python Den</small></a>
|
|
40
41
|
</div>
|
|
41
42
|
</div>
|
|
42
43
|
<div class="dropdown">
|
|
@@ -46,6 +47,8 @@
|
|
|
46
47
|
<div class="dropdown-content">
|
|
47
48
|
<a id="rapid_router_resources_button" href="https://code-for-life.gitbook.io/teaching-resources/" target="_blank">
|
|
48
49
|
<small>Rapid Router</small> <span class="iconify" data-icon="mdi:open-in-new"></span></a>
|
|
50
|
+
<a href="https://code-for-life.gitbook.io/python-lessons-with-raspberry-pi-ide/DGiFT28ihVJK7ghZQHqu" target="_blank">
|
|
51
|
+
<small>Python Den</small> <span class="iconify" data-icon="mdi:open-in-new"></span></a>
|
|
49
52
|
<a href="{% url 'codingClub' %}"><small>Coding Clubs</small></a>
|
|
50
53
|
</div>
|
|
51
54
|
</div>
|
|
@@ -75,6 +78,7 @@
|
|
|
75
78
|
</a>
|
|
76
79
|
<div class="dropdown-content">
|
|
77
80
|
<a href="{% url 'levels' %}"><small>Rapid Router</small></a>
|
|
81
|
+
<a href="{% url 'python_levels' %}"><small>Python Den</small></a>
|
|
78
82
|
</div>
|
|
79
83
|
</div>
|
|
80
84
|
{% if user|is_logged_in_as_school_user %}
|
|
@@ -186,11 +190,15 @@
|
|
|
186
190
|
href="{% url 'dashboard' %}">School / Club<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
187
191
|
<a class="button button--menu__item button--menu__item__sub"
|
|
188
192
|
href="{% url 'levels' %}">Rapid Router<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
193
|
+
<a class="button button--menu__item button--menu__item__sub"
|
|
194
|
+
href="{% url 'python_levels' %}">Python Den<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
189
195
|
<button class="button--menu__item button--menu__item__sub
|
|
190
196
|
button--menu__item--sub-header" data-toggle="collapse" data-target="#resources-tabs">Teaching Resources</button>
|
|
191
197
|
<div id="resources-tabs" class="collapse">
|
|
192
198
|
<a class="button button--menu__item button--menu__item__sub__sub"
|
|
193
199
|
href="https://code-for-life.gitbook.io/teaching-resources/" target="_blank">Rapid Router<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
200
|
+
<a class="button button--menu__item button--menu__item__sub__sub"
|
|
201
|
+
href="https://code-for-life.gitbook.io/python-lessons-with-raspberry-pi-ide/DGiFT28ihVJK7ghZQHqu" target="_blank">Python Den<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
194
202
|
</div>
|
|
195
203
|
</div>
|
|
196
204
|
{% else %}
|
|
@@ -209,6 +217,8 @@
|
|
|
209
217
|
<div id="games-tabs" class="collapse">
|
|
210
218
|
<a class="button button--menu__item button--menu__item--{% if user|is_independent_student %}independent{% else %}student{% endif %} button--menu__item__sub"
|
|
211
219
|
href="{% url 'levels' %}">Rapid Router<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
220
|
+
<a class="button button--menu__item button--menu__item--{% if user|is_independent_student %}independent{% else %}student{% endif %} button--menu__item__sub"
|
|
221
|
+
href="{% url 'python_levels' %}">Python Den<span class="material-icons-outlined md-32">chevron_right</span></a>
|
|
212
222
|
</div>
|
|
213
223
|
{% if user|is_logged_in_as_school_user %}
|
|
214
224
|
<a class="button button--menu__item button--menu__item--student"
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
{% load app_tags %}
|
|
4
4
|
|
|
5
5
|
{% block subNav %}
|
|
6
|
+
{% if user|is_independent_student %}
|
|
7
|
+
<section class="banner banner--independent-student row mx-0">
|
|
8
|
+
{% else %}
|
|
6
9
|
<section class="banner banner--student row mx-0">
|
|
10
|
+
{% endif %}
|
|
7
11
|
<div>
|
|
8
12
|
<h1 class="banner__text--primary">
|
|
9
13
|
Welcome, {{ user|make_into_username }}
|
|
@@ -16,13 +20,13 @@
|
|
|
16
20
|
{% block content %}
|
|
17
21
|
<div id="play_dashboard_page">
|
|
18
22
|
<div class="background container">
|
|
19
|
-
<section><h4>Your
|
|
20
|
-
<div class="grid-games col-center col-sm-
|
|
23
|
+
<section><h4>Your games</h4></section>
|
|
24
|
+
<div class="grid-games col-center col-sm-9">
|
|
21
25
|
<div class="card">
|
|
22
26
|
<div class="card__images">
|
|
23
27
|
<img title="Rapid Router" alt="Rapid Router"
|
|
24
28
|
class="card__image"
|
|
25
|
-
src="{% static 'portal/img/
|
|
29
|
+
src="{% static 'portal/img/RR_logo_grass_background.png' %}">
|
|
26
30
|
</div>
|
|
27
31
|
<div class="card__text">
|
|
28
32
|
<h5 class="card__title">Rapid Router</h5>
|
|
@@ -35,17 +39,34 @@
|
|
|
35
39
|
</div>
|
|
36
40
|
</div>
|
|
37
41
|
</div>
|
|
42
|
+
<div class="card">
|
|
43
|
+
<div class="card__images">
|
|
44
|
+
<img title="Python Den" alt="Python Den"
|
|
45
|
+
class="card__image"
|
|
46
|
+
src="{% static 'portal/img/python_den.png' %}">
|
|
47
|
+
</div>
|
|
48
|
+
<div class="card__text">
|
|
49
|
+
<h5 class="card__title">Python Den</h5>
|
|
50
|
+
<p>The Python Den takes you on a programming journey from
|
|
51
|
+
beginner to syntax superhero. Build a good solid
|
|
52
|
+
foundation of Python to start your coding career!</p>
|
|
53
|
+
<div class="button-group">
|
|
54
|
+
<a href="{% url 'python_levels'%}"
|
|
55
|
+
class="button button--primary">Play</a>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
38
59
|
</div>
|
|
39
60
|
</div>
|
|
40
61
|
<div class="background background--primary">
|
|
41
62
|
<div class="container">
|
|
42
63
|
<div class="text-center">
|
|
43
64
|
<section><img title="Rapid Router logo" alt="Rapid Router logo" src="{% static 'portal/img/RR_logo_simple.png' %}"></section>
|
|
44
|
-
<h4>You have completed {{ num_completed }} Rapid Router level{{ num_completed|pluralize }}!</h4>
|
|
45
|
-
<h4>You have {{ num_top_scores }} top score{{ num_top_scores|pluralize }}!</h4>
|
|
46
|
-
<h4>You have a score of {{ total_score }}. There are {{ total_available_score }} available points.</h4>
|
|
47
|
-
{% if total_custom_available_score %}
|
|
48
|
-
<h4>You have a score of {{ total_custom_score }} out of {{ total_custom_available_score }} available points on custom levels.</h4>
|
|
65
|
+
<h4>You have completed {{ rapid_router.num_completed }} Rapid Router level{{ rapid_router.num_completed|pluralize }}!</h4>
|
|
66
|
+
<h4>You have {{ rapid_router.num_top_scores }} top score{{ rapid_router.num_top_scores|pluralize }}!</h4>
|
|
67
|
+
<h4>You have a score of {{ rapid_router.total_score }}. There are {{ rapid_router.total_available_score }} available points.</h4>
|
|
68
|
+
{% if rapid_router.total_custom_available_score %}
|
|
69
|
+
<h4>You have a score of {{ rapid_router.total_custom_score }} out of {{ rapid_router.total_custom_available_score }} available points on custom levels.</h4>
|
|
49
70
|
{% endif %}
|
|
50
71
|
<section>
|
|
51
72
|
<a href="{% url 'scoreboard' %}"
|
|
@@ -56,5 +77,15 @@
|
|
|
56
77
|
</div>
|
|
57
78
|
</div>
|
|
58
79
|
</div>
|
|
80
|
+
<div class="background background--python-den">
|
|
81
|
+
<div class="container">
|
|
82
|
+
<div class="text-center">
|
|
83
|
+
<section><img title="Python Den logo" alt="Python Den logo" src="{% static 'portal/img/logo_python_den.svg' %}"></section>
|
|
84
|
+
<h4>You have completed {{ python_den.num_completed }} Python Den level{{ python_den.num_completed|pluralize }}!</h4>
|
|
85
|
+
<h4>You have {{ python_den.num_top_scores }} top score{{ python_den.num_top_scores|pluralize }}!</h4>
|
|
86
|
+
<h4>You have a score of {{ python_den.total_score }}. There are {{ python_den.total_available_score }} available points.</h4>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
59
90
|
</div>
|
|
60
91
|
{% endblock %}
|
|
@@ -94,6 +94,40 @@
|
|
|
94
94
|
</div>
|
|
95
95
|
</section>
|
|
96
96
|
|
|
97
|
+
<div class="background container">
|
|
98
|
+
<section>
|
|
99
|
+
<img title="Python Den logo"
|
|
100
|
+
alt="Banner image of Python Den logo and game"
|
|
101
|
+
src="{% static 'portal/img/python_den_banner.svg' %}">
|
|
102
|
+
</section>
|
|
103
|
+
<div class="background row d-flex">
|
|
104
|
+
<div class="col-sm-6">
|
|
105
|
+
<img src="{% static 'portal/img/python_den.png' %}"
|
|
106
|
+
alt="Python Den logo and characters">
|
|
107
|
+
</div>
|
|
108
|
+
<div class="col-sm-6 d-flex flex-column">
|
|
109
|
+
<div class="flex-grow-1">
|
|
110
|
+
<h5 class="mt-0">Python programming</h5>
|
|
111
|
+
<p>The Python Den is an exploration of Python programming
|
|
112
|
+
through a comprehensive course with free lesson plans,
|
|
113
|
+
videos and worksheets to support you in your learning.</p>
|
|
114
|
+
<p>From foundational syntax to advanced concepts like loops
|
|
115
|
+
and data manipulation, each session is a new challenge.</p>
|
|
116
|
+
<p>We aim to provide a tried-and-tested, structured set of
|
|
117
|
+
lessons that you can use at home or in class, no matter
|
|
118
|
+
your own level of experience.</p>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="row my-4">
|
|
121
|
+
<div class="col-sm-12">
|
|
122
|
+
<a href="{% url 'python_levels' %}"
|
|
123
|
+
class="button button--primary button--icon col-sm-8">
|
|
124
|
+
Play Python Den <span class="iconify" data-icon="mdi:chevron-right"></span></a>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
97
131
|
<script type="text/javascript">
|
|
98
132
|
$(document).ready(function () {
|
|
99
133
|
invokeColorbox();
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
|
|
100
100
|
<div id="episodes">
|
|
101
101
|
<div class="panel-intro d-flex align-items-center justify-content-between">
|
|
102
|
-
<h6>
|
|
102
|
+
<h6>Rapid Router levels</h6>
|
|
103
103
|
<label class="pr-5 mb-0 mr-5">
|
|
104
104
|
<input type="checkbox" id="select-all-blockly-levels">
|
|
105
105
|
</label>
|
|
@@ -140,56 +140,6 @@
|
|
|
140
140
|
</div>
|
|
141
141
|
</div>
|
|
142
142
|
{% endfor %}
|
|
143
|
-
|
|
144
|
-
<div class="panel-intro d-flex align-items-center justify-content-between">
|
|
145
|
-
<h6>Python levels</h6>
|
|
146
|
-
<label class="pr-5 mb-0 mr-5">
|
|
147
|
-
<input type="checkbox" id="select-all-python-levels">
|
|
148
|
-
</label>
|
|
149
|
-
</div>
|
|
150
|
-
{% for episode in python_episodes %}
|
|
151
|
-
<div
|
|
152
|
-
class="panel"
|
|
153
|
-
{% if "coming soon" in episode.name %}
|
|
154
|
-
style="pointer-events:none;"
|
|
155
|
-
{% endif %}
|
|
156
|
-
>
|
|
157
|
-
<div class="panel-header bg--{{ episode.difficulty }}" id="episode-{{episode.id}}">
|
|
158
|
-
<div class="d-flex align-items-center justify-content-end" data-toggle="collapse"
|
|
159
|
-
data-target="#collapse-{{episode.id}}" aria-expanded="false"
|
|
160
|
-
aria-controls="collapse-{{episode.id}}" data-parent="#episodes">
|
|
161
|
-
<p class="episode-title flex-grow-1">{{episode.name}}</p>
|
|
162
|
-
<span>Levels {{episode.first_level}}-{{episode.last_level}}</span>
|
|
163
|
-
<div class="episode_range_text collapsed d-flex align-items-center justify-content-end"
|
|
164
|
-
data-toggle="collapse" data-target="#collapse-{{episode.id}}"
|
|
165
|
-
aria-expanded="false" aria-controls="collapse-{{episode.id}}" data-parent="#episodes">
|
|
166
|
-
{% if "coming soon" not in episode.name %}
|
|
167
|
-
<label id="episode-label-{{ episode.id }}" class="mb-0" for="select-all-episode-levels">
|
|
168
|
-
<input type="checkbox" value="{{ episode.name }}" id="select-all-python-levels-{{ episode.id }}">
|
|
169
|
-
</label>
|
|
170
|
-
{% endif %}
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
|
-
<div id="collapse-{{episode.id}}" aria-labelledby="episode-{{episode.id}}" class="collapse">
|
|
176
|
-
<div class="panel-body d-flex justify-content-between">
|
|
177
|
-
<div class="d-flex flex-grow-1 flex-column justify-content-between">
|
|
178
|
-
{% for level in episode.levels %}
|
|
179
|
-
<p>{{level.name}}: {{level.title.strip | safe}}</p>
|
|
180
|
-
{% endfor %}
|
|
181
|
-
</div>
|
|
182
|
-
<div class="form__checkbox flex-column justify-content-between p-0">
|
|
183
|
-
{% for input in level_control_form|get_dict_item:episode.name %}
|
|
184
|
-
<div class="form__checkbox-input p-0">
|
|
185
|
-
{{ input }}
|
|
186
|
-
</div>
|
|
187
|
-
{% endfor %}
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
</div>
|
|
192
|
-
{% endfor %}
|
|
193
143
|
</div>
|
|
194
144
|
|
|
195
145
|
<div class="button-group">
|
|
@@ -149,6 +149,41 @@
|
|
|
149
149
|
</div>
|
|
150
150
|
</div>
|
|
151
151
|
|
|
152
|
+
<div class="background background--primary">
|
|
153
|
+
<div class="container">
|
|
154
|
+
<div class="row d-flex">
|
|
155
|
+
<div class="col-sm-6 d-flex flex-column">
|
|
156
|
+
<div class="flex-grow-1">
|
|
157
|
+
<h4 class="mt-0">Python programming</h4>
|
|
158
|
+
<p>The Python Den is an exploration of Python
|
|
159
|
+
programming through a comprehensive course with free
|
|
160
|
+
lesson plans, videos and worksheets to support you in
|
|
161
|
+
teaching Python programming to your students. From
|
|
162
|
+
foundational syntax to advanced concepts like loops
|
|
163
|
+
and data manipulation, each session is tailored to
|
|
164
|
+
engage young learners.</p>
|
|
165
|
+
<p>We aim to provide a tried-and-tested, structured set
|
|
166
|
+
of lessons that you can use in your classroom, no
|
|
167
|
+
matter your own level of experience.
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
<div>
|
|
171
|
+
<a href="{% url 'python_levels' %}"
|
|
172
|
+
class="button button--primary button-right-arrow">
|
|
173
|
+
Try out Python Den
|
|
174
|
+
</a>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="col-sm-6">
|
|
178
|
+
<a href="https://www.youtube-nocookie.com/embed/ko4nrr4kDzA?rel=0&autoplay=1" class="youtube">
|
|
179
|
+
<img src="{% static 'portal/img/thumbnail_python_den.png' %}"
|
|
180
|
+
alt="Python Den logo">
|
|
181
|
+
</a>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
152
187
|
<script type="text/javascript">
|
|
153
188
|
$(document).ready(function(){
|
|
154
189
|
invokeColorbox();
|
|
@@ -36,3 +36,51 @@ snapshots['test_banner 1'] = '''<div class="banner banner--teacher">
|
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
38
|
'''
|
|
39
|
+
|
|
40
|
+
snapshots['test_benefits 1'] = '''
|
|
41
|
+
|
|
42
|
+
<div class="grid-benefits col-sm-8 col-center">
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
<h5 class="grid-benefits__title grid-benefits__title1">Test title</h5>
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
<h5 class="grid-benefits__title grid-benefits__title2">Test title</h5>
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
<h5 class="grid-benefits__title grid-benefits__title3">Test title</h5>
|
|
54
|
+
|
|
55
|
+
<p class="grid-benefits__text1">Test text</p>
|
|
56
|
+
<p class="grid-benefits__text2">Test text</p>
|
|
57
|
+
<p class="grid-benefits__text3">Test text</p>
|
|
58
|
+
|
|
59
|
+
<div class="grid-benefits__button grid-benefits__button1">
|
|
60
|
+
|
|
61
|
+
<a href="/" class="button button--secondary button--secondary--dark">Test button</a>
|
|
62
|
+
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
<div class="grid-benefits__button grid-benefits__button2">
|
|
67
|
+
|
|
68
|
+
<a href="/" class="button button--secondary button--secondary--dark">Test button</a>
|
|
69
|
+
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
<div class="grid-benefits__button grid-benefits__button3">
|
|
74
|
+
|
|
75
|
+
<a href="/" class="button button--secondary button--secondary--dark">Test button</a>
|
|
76
|
+
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
</div>
|
|
80
|
+
'''
|
|
81
|
+
|
|
82
|
+
snapshots['test_headline 1'] = '''<section>
|
|
83
|
+
<h4>Test title</h4>
|
|
84
|
+
</section>
|
|
85
|
+
<p class="container">Test description</p>
|
|
86
|
+
'''
|
portal/tests/test_views.py
CHANGED
|
@@ -560,29 +560,53 @@ class TestViews(TestCase):
|
|
|
560
560
|
|
|
561
561
|
# Expected context data when a student hasn't played anything yet
|
|
562
562
|
EXPECTED_DATA_FIRST_LOGIN = {
|
|
563
|
-
"
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
"rapid_router": {
|
|
564
|
+
"num_completed": 0,
|
|
565
|
+
"num_top_scores": 0,
|
|
566
|
+
"total_score": 0,
|
|
567
|
+
"total_available_score": 1450,
|
|
568
|
+
},
|
|
569
|
+
"python_den": {
|
|
570
|
+
"num_completed": 0,
|
|
571
|
+
"num_top_scores": 0,
|
|
572
|
+
"total_score": 0,
|
|
573
|
+
"total_available_score": 830,
|
|
574
|
+
},
|
|
567
575
|
}
|
|
568
576
|
|
|
569
577
|
# Expected context data when a student has attempted some RR levels
|
|
570
578
|
EXPECTED_DATA_WITH_ATTEMPTS = {
|
|
571
|
-
"
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
579
|
+
"rapid_router": {
|
|
580
|
+
"num_completed": 2,
|
|
581
|
+
"num_top_scores": 1,
|
|
582
|
+
"total_score": 39,
|
|
583
|
+
"total_available_score": 1450,
|
|
584
|
+
},
|
|
585
|
+
"python_den": {
|
|
586
|
+
"num_completed": 2,
|
|
587
|
+
"num_top_scores": 2,
|
|
588
|
+
"total_score": 30,
|
|
589
|
+
"total_available_score": 830,
|
|
590
|
+
},
|
|
575
591
|
}
|
|
576
592
|
|
|
577
593
|
# Expected context data when a student has also attempted some custom RR
|
|
578
594
|
# levels
|
|
579
595
|
EXPECTED_DATA_WITH_CUSTOM_ATTEMPTS = {
|
|
580
|
-
"
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
596
|
+
"rapid_router": {
|
|
597
|
+
"num_completed": 2,
|
|
598
|
+
"num_top_scores": 1,
|
|
599
|
+
"total_score": 39,
|
|
600
|
+
"total_available_score": 1450,
|
|
601
|
+
"total_custom_score": 10,
|
|
602
|
+
"total_custom_available_score": 20,
|
|
603
|
+
},
|
|
604
|
+
"python_den": {
|
|
605
|
+
"num_completed": 2,
|
|
606
|
+
"num_top_scores": 2,
|
|
607
|
+
"total_score": 30,
|
|
608
|
+
"total_available_score": 830,
|
|
609
|
+
},
|
|
586
610
|
}
|
|
587
611
|
|
|
588
612
|
c = Client()
|
|
@@ -599,13 +623,20 @@ class TestViews(TestCase):
|
|
|
599
623
|
assert response.status_code == 200
|
|
600
624
|
assert response.context_data == EXPECTED_DATA_FIRST_LOGIN
|
|
601
625
|
|
|
602
|
-
# Attempt the first two levels, one perfect attempt, one not
|
|
626
|
+
# Attempt the first two RR levels, one perfect attempt, one not
|
|
603
627
|
level1 = Level.objects.get(name="1")
|
|
604
628
|
level2 = Level.objects.get(name="2")
|
|
605
629
|
|
|
606
630
|
create_attempt(student, level1, 20)
|
|
607
631
|
create_attempt(student, level2, 19)
|
|
608
632
|
|
|
633
|
+
# Attempt the first and fourth Python Den levels, both perfect
|
|
634
|
+
level1001 = Level.objects.get(name="1001")
|
|
635
|
+
level1004 = Level.objects.get(name="1004")
|
|
636
|
+
|
|
637
|
+
create_attempt(student, level1001, 20)
|
|
638
|
+
create_attempt(student, level1004, 10)
|
|
639
|
+
|
|
609
640
|
response = c.get(student_dashboard_url)
|
|
610
641
|
|
|
611
642
|
assert response.status_code == 200
|
portal/urls.py
CHANGED
|
@@ -216,7 +216,7 @@ urlpatterns = [
|
|
|
216
216
|
url(r"^i18n/", include("django.conf.urls.i18n")),
|
|
217
217
|
url(r"^jsi18n/$", JavaScriptCatalog.as_view(), js_info_dict),
|
|
218
218
|
url(
|
|
219
|
-
r"^(?P<
|
|
219
|
+
r"^(?P<level_name>[A-Z0-9]+)/$",
|
|
220
220
|
play_default_level,
|
|
221
221
|
name="play_default_level",
|
|
222
222
|
),
|
portal/views/student/play.py
CHANGED
|
@@ -37,10 +37,16 @@ class SchoolStudentDashboard(
|
|
|
37
37
|
the student's scores for any levels shared with them by their teacher.
|
|
38
38
|
"""
|
|
39
39
|
# Get score data for all original levels
|
|
40
|
-
|
|
40
|
+
rapid_router_levels = Level.objects.filter(episode__pk__in=range(1, 10))
|
|
41
|
+
python_den_levels = Level.objects.filter(
|
|
42
|
+
episode__pk__in=[12, 13, 14, 15, 22]
|
|
43
|
+
)
|
|
41
44
|
student = self.request.user.new_student
|
|
42
45
|
|
|
43
|
-
context_data =
|
|
46
|
+
context_data = {
|
|
47
|
+
"rapid_router": _compute_scores(student, rapid_router_levels),
|
|
48
|
+
"python_den": _compute_scores(student, python_den_levels),
|
|
49
|
+
}
|
|
44
50
|
|
|
45
51
|
# Find any custom levels created by the teacher and shared with the
|
|
46
52
|
# student
|
|
@@ -49,16 +55,15 @@ class SchoolStudentDashboard(
|
|
|
49
55
|
custom_levels = student.new_user.shared.filter(owner=teacher)
|
|
50
56
|
|
|
51
57
|
if custom_levels:
|
|
52
|
-
custom_levels_data =
|
|
53
|
-
student, custom_levels
|
|
54
|
-
)
|
|
58
|
+
custom_levels_data = _compute_scores(student, custom_levels)
|
|
55
59
|
|
|
56
|
-
context_data["total_custom_score"] =
|
|
57
|
-
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
context_data["rapid_router"]["total_custom_score"] = (
|
|
61
|
+
custom_levels_data
|
|
62
|
+
)["total_score"]
|
|
63
|
+
|
|
64
|
+
context_data["rapid_router"][
|
|
65
|
+
"total_custom_available_score"
|
|
66
|
+
] = custom_levels_data["total_available_score"]
|
|
62
67
|
|
|
63
68
|
return context_data
|
|
64
69
|
|
|
@@ -66,23 +71,26 @@ class SchoolStudentDashboard(
|
|
|
66
71
|
class IndependentStudentDashboard(
|
|
67
72
|
LoginRequiredNoErrorMixin, UserPassesTestMixin, TemplateView, FormView
|
|
68
73
|
):
|
|
69
|
-
template_name = "portal/play/
|
|
74
|
+
template_name = "portal/play/student_dashboard.html"
|
|
70
75
|
login_url = reverse_lazy("independent_student_login")
|
|
71
76
|
|
|
72
77
|
def test_func(self) -> Optional[bool]:
|
|
73
78
|
return logged_in_as_independent_student(self.request.user)
|
|
74
79
|
|
|
75
80
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
|
76
|
-
|
|
81
|
+
rapid_router_levels = Level.objects.filter(episode__pk__in=range(1, 10))
|
|
82
|
+
python_den_levels = Level.objects.filter(
|
|
83
|
+
episode__pk__in=[12, 13, 14, 15, 22]
|
|
84
|
+
)
|
|
77
85
|
student = self.request.user.new_student
|
|
78
86
|
|
|
79
|
-
return
|
|
80
|
-
student,
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
return {
|
|
88
|
+
"rapid_router": _compute_scores(student, rapid_router_levels),
|
|
89
|
+
"python_den": _compute_scores(student, python_den_levels),
|
|
90
|
+
}
|
|
83
91
|
|
|
84
92
|
|
|
85
|
-
def
|
|
93
|
+
def _compute_scores(
|
|
86
94
|
student: Student, levels: List[Level] or QuerySet
|
|
87
95
|
) -> Dict[str, int]:
|
|
88
96
|
"""
|
portal/views/teacher/teach.py
CHANGED
|
@@ -21,7 +21,7 @@ from django.shortcuts import get_object_or_404, render
|
|
|
21
21
|
from django.urls import reverse, reverse_lazy
|
|
22
22
|
from django.utils import timezone
|
|
23
23
|
from django.views.decorators.http import require_POST
|
|
24
|
-
from game.views.level_selection import get_blockly_episodes
|
|
24
|
+
from game.views.level_selection import get_blockly_episodes
|
|
25
25
|
from portal.views.registration import handle_reset_password_tracking
|
|
26
26
|
from reportlab.lib.colors import black, red
|
|
27
27
|
from reportlab.lib.pagesizes import A4
|
|
@@ -256,7 +256,6 @@ def teacher_edit_class(request, access_code):
|
|
|
256
256
|
external_requests_message = klass.get_requests_message()
|
|
257
257
|
|
|
258
258
|
blockly_episodes = get_blockly_episodes(request)
|
|
259
|
-
python_episodes = get_python_episodes(request)
|
|
260
259
|
|
|
261
260
|
locked_levels = klass.locked_levels.all()
|
|
262
261
|
locked_levels_ids = [locked_level.id for locked_level in locked_levels]
|
|
@@ -273,7 +272,7 @@ def teacher_edit_class(request, access_code):
|
|
|
273
272
|
elif "level_control_submit" in request.POST:
|
|
274
273
|
level_control_form = ClassLevelControlForm(request.POST)
|
|
275
274
|
if level_control_form.is_valid():
|
|
276
|
-
return process_level_control_form(request, klass, blockly_episodes
|
|
275
|
+
return process_level_control_form(request, klass, blockly_episodes)
|
|
277
276
|
elif "class_move_submit" in request.POST:
|
|
278
277
|
class_move_form = ClassMoveForm(other_teachers, request.POST)
|
|
279
278
|
if class_move_form.is_valid():
|
|
@@ -287,7 +286,6 @@ def teacher_edit_class(request, access_code):
|
|
|
287
286
|
"class_move_form": class_move_form,
|
|
288
287
|
"level_control_form": level_control_form,
|
|
289
288
|
"blockly_episodes": blockly_episodes,
|
|
290
|
-
"python_episodes": python_episodes,
|
|
291
289
|
"locked_levels": locked_levels_ids,
|
|
292
290
|
"class": klass,
|
|
293
291
|
"external_requests_message": external_requests_message,
|
|
@@ -338,19 +336,17 @@ def process_edit_class_form(request, klass, form):
|
|
|
338
336
|
return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": klass.access_code}))
|
|
339
337
|
|
|
340
338
|
|
|
341
|
-
def process_level_control_form(request, klass, blockly_episodes
|
|
339
|
+
def process_level_control_form(request, klass, blockly_episodes):
|
|
342
340
|
"""
|
|
343
341
|
Find the levels that the user wants to lock and lock them for the specific class.
|
|
344
342
|
:param request: The request sent by the user submitting the form.
|
|
345
343
|
:param klass: The class for which the levels are being locked / unlocked.
|
|
346
344
|
:param blockly_episodes: The set of Blockly Episodes in the game.
|
|
347
|
-
:param python_episodes: The set of Python Episodes in the game.
|
|
348
345
|
:return: A redirect to the teacher dashboard with a success message.
|
|
349
346
|
"""
|
|
350
347
|
levels_to_lock_ids = []
|
|
351
348
|
|
|
352
349
|
mark_levels_to_lock_in_episodes(request, blockly_episodes, levels_to_lock_ids)
|
|
353
|
-
mark_levels_to_lock_in_episodes(request, python_episodes, levels_to_lock_ids)
|
|
354
350
|
|
|
355
351
|
klass.locked_levels.clear()
|
|
356
352
|
[klass.locked_levels.add(levels_to_lock_id) for levels_to_lock_id in levels_to_lock_ids]
|