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.

Files changed (28) hide show
  1. {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/METADATA +24 -24
  2. {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/RECORD +27 -24
  3. {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/WHEEL +1 -1
  4. example_project/urls.py +2 -0
  5. portal/__init__.py +1 -1
  6. portal/forms/teach.py +1 -1
  7. portal/static/portal/img/RR_logo_grass_background.png +0 -0
  8. portal/static/portal/img/logo_python_den.svg +21 -0
  9. portal/static/portal/img/python_den.png +0 -0
  10. portal/static/portal/img/python_den_banner.svg +26 -0
  11. portal/static/portal/img/thumbnail_educate_rapid_router.png +0 -0
  12. portal/static/portal/img/thumbnail_python_den.png +0 -0
  13. portal/static/portal/sass/modules/_colours.scss +1 -0
  14. portal/static/portal/sass/partials/_text.scss +5 -0
  15. portal/templates/portal/contribute.html +6 -0
  16. portal/templates/portal/partials/header.html +10 -0
  17. portal/templates/portal/play/student_dashboard.html +39 -8
  18. portal/templates/portal/play.html +34 -0
  19. portal/templates/portal/teach/teacher_edit_class.html +1 -51
  20. portal/templates/portal/teach.html +35 -0
  21. portal/tests/snapshots/snap_test_partials.py +48 -0
  22. portal/tests/test_views.py +46 -15
  23. portal/urls.py +1 -1
  24. portal/views/student/play.py +26 -18
  25. portal/views/teacher/teach.py +3 -7
  26. portal/templates/portal/play/independent_student_dashboard.html +0 -51
  27. {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/LICENSE.md +0 -0
  28. {codeforlife_portal-7.1.1.dist-info → codeforlife_portal-7.2.0.dist-info}/top_level.txt +0 -0
@@ -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 class games</h4></section>
20
- <div class="grid-games col-center col-sm-4">
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/RR_logo_green.svg' %}">
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>Blockly levels</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&amp;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
+ '''
@@ -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
- "num_completed": 0,
564
- "num_top_scores": 0,
565
- "total_score": 0,
566
- "total_available_score": 2040,
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
- "num_completed": 2,
572
- "num_top_scores": 1,
573
- "total_score": 39,
574
- "total_available_score": 2040,
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
- "num_completed": 2,
581
- "num_top_scores": 1,
582
- "total_score": 39,
583
- "total_available_score": 2040,
584
- "total_custom_score": 10,
585
- "total_custom_available_score": 20,
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<levelName>[A-Z0-9]+)/$",
219
+ r"^(?P<level_name>[A-Z0-9]+)/$",
220
220
  play_default_level,
221
221
  name="play_default_level",
222
222
  ),
@@ -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
- levels = Level.objects.sorted_levels()
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 = _compute_rapid_router_scores(student, levels)
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 = _compute_rapid_router_scores(
53
- student, custom_levels
54
- )
58
+ custom_levels_data = _compute_scores(student, custom_levels)
55
59
 
56
- context_data["total_custom_score"] = custom_levels_data[
57
- "total_score"
58
- ]
59
- context_data["total_custom_available_score"] = custom_levels_data[
60
- "total_available_score"
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/independent_student_dashboard.html"
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
- levels = Level.objects.sorted_levels()
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 _compute_rapid_router_scores(
80
- student,
81
- levels,
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 _compute_rapid_router_scores(
93
+ def _compute_scores(
86
94
  student: Student, levels: List[Level] or QuerySet
87
95
  ) -> Dict[str, int]:
88
96
  """
@@ -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, get_python_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, python_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, python_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]