codeforlife-portal 7.2.1__py2.py3-none-any.whl → 7.3.1__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforlife-portal
3
- Version: 7.2.1
3
+ Version: 7.3.1
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Framework :: Django
@@ -21,7 +21,7 @@ Requires-Dist: django-classy-tags==2.0.0
21
21
  Requires-Dist: libsass==0.23.0
22
22
  Requires-Dist: phonenumbers==8.12.12
23
23
  Requires-Dist: more-itertools==8.7.0
24
- Requires-Dist: cfl-common==7.2.1
24
+ Requires-Dist: cfl-common==7.3.1
25
25
  Requires-Dist: django-ratelimit==3.0.1
26
26
  Requires-Dist: django-preventconcurrentlogins==0.8.2
27
27
  Requires-Dist: django-csp==3.7
@@ -104,10 +104,10 @@ deploy/templates/deploy/csrf_failure.html,sha256=-pBRPn4Y7nUdYHGpTHCokT9Boi-isuw
104
104
  example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
105
  example_project/manage.py,sha256=EUgybZlZ7xk2Zf2KCwBbK_z7gf7Ifqs0_bl4Kijhdgo,242
106
106
  example_project/portal_test_settings.py,sha256=NfLY72mt1LR2c0_kxF-Yg5pCm2vQ52eceBMn573wW0Y,7247
107
- example_project/settings.py,sha256=vOGZyxsWfV_G28X3XnSGSE65BUSU7mIGKOd0Z4mSkaE,5600
107
+ example_project/settings.py,sha256=Smpr5FHAaQQhXPgh6-Zsy-jnH5_ZV9htPTWJWlGqptI,5575
108
108
  example_project/urls.py,sha256=3SsP5jvPAXV5xmduka4zbSZB5PbXggjsalu1ojY5KIo,434
109
109
  example_project/wsgi.py,sha256=U1W6WzZxZaIdYZ5tks7w9fqp5WS5qvn2iThsVcskrWw,829
110
- portal/__init__.py,sha256=5HkC96OIlIn2QaEoaF1aGy7IrAcyzQSiHxoFr7pNa8s,22
110
+ portal/__init__.py,sha256=EwNAYkUpUieq_F_-i4iAHHUWZC-cpSOWZYMwaEyUOT0,22
111
111
  portal/admin.py,sha256=on1-zNRnZvf2cwBN6GVRVYRhkaksrCgfzX8XPWtkvz8,6062
112
112
  portal/app_settings.py,sha256=DhWLQOwM0zVOXE3O5TNKbMM9K6agfLuCsHOdr1J7xEI,651
113
113
  portal/backends.py,sha256=2Dss6_WoQwPuDzJUF1yEaTQTNG4eUrD12ujJQ5cp5Tc,812
@@ -125,7 +125,7 @@ portal/forms/invite_teacher.py,sha256=jkDNcCfkts4_lXRzhcI3xBam21Zn2yX9wMpMVhDtW1
125
125
  portal/forms/organisation.py,sha256=QcQyd7AiqBmvt4y8uQSQylguUbKOKqo2pjqWIkpWjDg,7433
126
126
  portal/forms/play.py,sha256=z9P5LzyS3jjYcnfco84d2x8ptgLxRmh94Dnj05plmbY,11505
127
127
  portal/forms/registration.py,sha256=gWcY7rllhWO3c9as6QHUDWZx1Jme7DqtGHYaKcvxe-U,5990
128
- portal/forms/teach.py,sha256=4vBlBO2q8hexreZc1te_iBe9GilpxkM_zyXbfkMSd3Q,20462
128
+ portal/forms/teach.py,sha256=lFGM9gk1gC_N_qJhzV2VdAVArCiAYfhOmrxvPkYC-co,22446
129
129
  portal/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
130
130
  portal/helpers/captcha.py,sha256=Amwg3HZ9eIh1LGYVYWYruk1ccNj6P3nYP_evufOY8BY,254
131
131
  portal/helpers/decorators.py,sha256=AnHbcRg42cUYkpMnJMImEMueSKHZRBZ0o67B7_Wp_Zs,3695
@@ -449,7 +449,7 @@ portal/templates/portal/home_learning.html,sha256=AdKDe0dlbWUrL1BOCjJpdQcOsC6SR-
449
449
  portal/templates/portal/locked_out.html,sha256=JeiFHFyueFX03D54k82swjxvKY78cFG8Vre-bKKv_7Y,894
450
450
  portal/templates/portal/locked_out_school_student.html,sha256=bcpLtu4mkHR-zILLuArtHH2OfUSjHiqntcdbTMlx5Kg,684
451
451
  portal/templates/portal/mouseflow.html,sha256=ji9SWR86nqC59xdmivfdPNG5OzFk8gJEUeOUUh52alk,361
452
- portal/templates/portal/play.html,sha256=qiXUo44yXv4XDWjIU1xSC9pTXFKmx_YF4BKb4K6xOKY,5606
452
+ portal/templates/portal/play.html,sha256=ETfPEVoWeWC8Jwwrr_2WaxYM_TyV4EHkq1l3RuKtrRM,5604
453
453
  portal/templates/portal/privacy_notice.html,sha256=USklp2FWK4yEpa_-UGYrOutC4lORBMIxqcQihvCLTSA,56338
454
454
  portal/templates/portal/register.html,sha256=4RqUxRiQOvd-oHAM5x6U01zdlYRfy-h8NUGl8UIiBC0,12531
455
455
  portal/templates/portal/reset_password.html,sha256=YzsREz5D2OwhicMLahVOVDXiNDxoHlPqU5iu96i36W0,1373
@@ -497,7 +497,7 @@ portal/templates/portal/teach/onboarding_students.html,sha256=09q658n7U7Vuzy_M_0
497
497
  portal/templates/portal/teach/teacher_add_external_student.html,sha256=Lrrh4W3EwxxXKe45kcqzf2BcPxYo_mLahGIQMNJjX9E,2453
498
498
  portal/templates/portal/teach/teacher_added_external_student.html,sha256=f18RhP5PCE13yjBTrQTCwPLZnkcZb0bDUZ_Z2Oh3D3I,1189
499
499
  portal/templates/portal/teach/teacher_dismiss_students.html,sha256=_5FvvtNjdYpZaM9lDzXrhhTjAD3NK_92kDYzh7xqowI,4283
500
- portal/templates/portal/teach/teacher_edit_class.html,sha256=ulpUfsLc7WROMNjzqIMhRzWHhl5p2KSaHCggaLANKk8,8496
500
+ portal/templates/portal/teach/teacher_edit_class.html,sha256=TcACMixI-kd0OyUThS5EJ81O_E8j8SOTciccjfpIx5I,12254
501
501
  portal/templates/portal/teach/teacher_edit_student.html,sha256=JNF8JyoZh0J-oKH4dz7SGSxR0t7J1lbTGLvjhNbk2Rw,3237
502
502
  portal/templates/portal/teach/teacher_move_all_classes.html,sha256=u9PNJHrKaIXd67vuwhgMmVXumgSnkKH4QKx1bd3O2VY,1780
503
503
  portal/templates/portal/teach/teacher_move_students.html,sha256=toBtjtpiCwvxo2FTJX5oZDzJ9batXdi7ty5AiCHnq4s,1591
@@ -626,13 +626,13 @@ portal/views/student/edit_account_details.py,sha256=Ba-3D_zzKbX5N01NG5qqBS0ud10B
626
626
  portal/views/student/play.py,sha256=GMxk65bxWOe1Ds2kb6rvuOd1GoAtt5v_9AihLNXoUL0,8768
627
627
  portal/views/teacher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
628
628
  portal/views/teacher/dashboard.py,sha256=8WglspwuHF__2LtoX5_XvoW1ulICSupjKv--MtjrvJk,25714
629
- portal/views/teacher/teach.py,sha256=qNP3j4m1ecMJHdaob3HAL_dnSvaWXysajS7CuV3wlH0,34320
629
+ portal/views/teacher/teach.py,sha256=DFQSiC3RokLMArR_jTinFOqdymMa9phUOmwK0xBoDdk,37039
630
630
  portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
631
631
  portal/views/two_factor/core.py,sha256=O_wcBeFqdPYSGNGv-pT_vbs5-Dj1Z-Jfkd6f9-E5yZI,760
632
632
  portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
633
633
  portal/views/two_factor/profile.py,sha256=tkl_ludo8arMtd5LKNmohM66vpC_YQiP-0nspTSJiJ4,383
634
- codeforlife_portal-7.2.1.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
635
- codeforlife_portal-7.2.1.dist-info/METADATA,sha256=XS4XFqynBl5LSfI8NPsyl63vRE4fo80iVR-Fg2j7AQc,3317
636
- codeforlife_portal-7.2.1.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
637
- codeforlife_portal-7.2.1.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
638
- codeforlife_portal-7.2.1.dist-info/RECORD,,
634
+ codeforlife_portal-7.3.1.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
635
+ codeforlife_portal-7.3.1.dist-info/METADATA,sha256=86OkP1HAPvcVixUEN2CN5nWJLKPR_E8KMAqfM_Pd6T8,3317
636
+ codeforlife_portal-7.3.1.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
637
+ codeforlife_portal-7.3.1.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
638
+ codeforlife_portal-7.3.1.dist-info/RECORD,,
@@ -54,7 +54,6 @@ INSTALLED_APPS = [
54
54
  "django.contrib.staticfiles",
55
55
  "rest_framework",
56
56
  "import_export",
57
- "django_js_reverse",
58
57
  "django_otp",
59
58
  "django_otp.plugins.otp_static",
60
59
  "django_otp.plugins.otp_totp",
portal/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "7.2.1"
1
+ __version__ = "7.3.1"
portal/forms/teach.py CHANGED
@@ -20,25 +20,37 @@ from portal.helpers.ratelimit import clear_ratelimit_cache_for_user
20
20
  class InvitedTeacherForm(forms.Form):
21
21
  teacher_password = forms.CharField(
22
22
  help_text="Enter a password",
23
- widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
23
+ widget=forms.PasswordInput(
24
+ attrs={"autocomplete": "off", "placeholder": "Password"}
25
+ ),
24
26
  )
25
27
  teacher_confirm_password = forms.CharField(
26
28
  help_text="Repeat password",
27
- widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Repeat password"}),
29
+ widget=forms.PasswordInput(
30
+ attrs={"autocomplete": "off", "placeholder": "Repeat password"}
31
+ ),
28
32
  )
29
33
 
30
- consent_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=True)
31
- newsletter_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=False)
34
+ consent_ticked = forms.BooleanField(
35
+ widget=forms.CheckboxInput(), initial=False, required=True
36
+ )
37
+ newsletter_ticked = forms.BooleanField(
38
+ widget=forms.CheckboxInput(), initial=False, required=False
39
+ )
32
40
 
33
41
  def clean_teacher_password(self):
34
- return form_clean_password(self, "teacher_password", PasswordStrength.TEACHER)
42
+ return form_clean_password(
43
+ self, "teacher_password", PasswordStrength.TEACHER
44
+ )
35
45
 
36
46
  def clean(self):
37
47
  if any(self.errors):
38
48
  return
39
49
 
40
50
  password = self.cleaned_data.get("teacher_password", None)
41
- confirm_password = self.cleaned_data.get("teacher_confirm_password", None)
51
+ confirm_password = self.cleaned_data.get(
52
+ "teacher_confirm_password", None
53
+ )
42
54
 
43
55
  check_passwords(password, confirm_password)
44
56
 
@@ -49,16 +61,22 @@ class TeacherSignupForm(InvitedTeacherForm):
49
61
  teacher_first_name = forms.CharField(
50
62
  help_text="Enter your first name",
51
63
  max_length=100,
52
- widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "First name"}),
64
+ widget=forms.TextInput(
65
+ attrs={"autocomplete": "off", "placeholder": "First name"}
66
+ ),
53
67
  )
54
68
  teacher_last_name = forms.CharField(
55
69
  help_text="Enter your last name",
56
70
  max_length=100,
57
- widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Last name"}),
71
+ widget=forms.TextInput(
72
+ attrs={"autocomplete": "off", "placeholder": "Last name"}
73
+ ),
58
74
  )
59
75
  teacher_email = forms.EmailField(
60
76
  help_text="Enter your email address",
61
- widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
77
+ widget=forms.EmailInput(
78
+ attrs={"autocomplete": "off", "placeholder": "Email address"}
79
+ ),
62
80
  )
63
81
 
64
82
  captcha = ReCaptchaField(widget=ReCaptchaV2Invisible)
@@ -67,31 +85,42 @@ class TeacherSignupForm(InvitedTeacherForm):
67
85
  class TeacherEditAccountForm(forms.Form):
68
86
  first_name = forms.CharField(
69
87
  max_length=100,
70
- widget=forms.TextInput(attrs={"placeholder": "First name", "class": "fName"}),
88
+ widget=forms.TextInput(
89
+ attrs={"placeholder": "First name", "class": "fName"}
90
+ ),
71
91
  help_text="First name",
72
92
  )
73
93
  last_name = forms.CharField(
74
94
  max_length=100,
75
- widget=forms.TextInput(attrs={"placeholder": "Last name", "class": "lName"}),
95
+ widget=forms.TextInput(
96
+ attrs={"placeholder": "Last name", "class": "lName"}
97
+ ),
76
98
  help_text="Last name",
77
99
  )
78
100
  email = forms.EmailField(
79
101
  required=False,
80
- widget=forms.EmailInput(attrs={"placeholder": "New email address (optional)"}),
102
+ widget=forms.EmailInput(
103
+ attrs={"placeholder": "New email address (optional)"}
104
+ ),
81
105
  help_text="New email address (optional)",
82
106
  )
83
107
  password = forms.CharField(
84
108
  required=False,
85
- widget=forms.PasswordInput(attrs={"placeholder": "New password (optional)"}),
109
+ widget=forms.PasswordInput(
110
+ attrs={"placeholder": "New password (optional)"}
111
+ ),
86
112
  help_text="New password (optional)",
87
113
  )
88
114
  confirm_password = forms.CharField(
89
115
  required=False,
90
- widget=forms.PasswordInput(attrs={"placeholder": "Confirm new password (optional)"}),
116
+ widget=forms.PasswordInput(
117
+ attrs={"placeholder": "Confirm new password (optional)"}
118
+ ),
91
119
  help_text="Confirm new password (optional)",
92
120
  )
93
121
  current_password = forms.CharField(
94
- widget=forms.PasswordInput(attrs={"placeholder": "Current password"}), help_text="Enter your current password"
122
+ widget=forms.PasswordInput(attrs={"placeholder": "Current password"}),
123
+ help_text="Enter your current password",
95
124
  )
96
125
 
97
126
  def __init__(self, user, *args, **kwargs):
@@ -113,7 +142,9 @@ class TeacherEditAccountForm(forms.Form):
113
142
 
114
143
  return self.cleaned_data
115
144
 
116
- def check_password_errors(self, password, confirm_password, current_password):
145
+ def check_password_errors(
146
+ self, password, confirm_password, current_password
147
+ ):
117
148
  check_passwords(password, confirm_password)
118
149
 
119
150
  if not self.user.check_password(current_password):
@@ -122,11 +153,15 @@ class TeacherEditAccountForm(forms.Form):
122
153
 
123
154
  class TeacherLoginForm(AuthenticationForm):
124
155
  username = forms.EmailField(
125
- widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
156
+ widget=forms.EmailInput(
157
+ attrs={"autocomplete": "off", "placeholder": "Email address"}
158
+ ),
126
159
  help_text="Enter your email address",
127
160
  )
128
161
  password = forms.CharField(
129
- widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
162
+ widget=forms.PasswordInput(
163
+ attrs={"autocomplete": "off", "placeholder": "Password"}
164
+ ),
130
165
  help_text="Enter your password",
131
166
  )
132
167
 
@@ -160,7 +195,9 @@ class TeacherLoginForm(AuthenticationForm):
160
195
  users = User.objects.filter(email=email)
161
196
 
162
197
  for result in users:
163
- if hasattr(result, "userprofile") and hasattr(result.userprofile, "teacher"):
198
+ if hasattr(result, "userprofile") and hasattr(
199
+ result.userprofile, "teacher"
200
+ ):
164
201
  user = result
165
202
  break
166
203
 
@@ -186,7 +223,8 @@ class TeacherLoginForm(AuthenticationForm):
186
223
 
187
224
  class ClassCreationForm(forms.Form):
188
225
  class_name = forms.CharField(
189
- widget=forms.TextInput(attrs={"placeholder": "Class name"}), help_text="Enter a class name"
226
+ widget=forms.TextInput(attrs={"placeholder": "Class name"}),
227
+ help_text="Enter a class name",
190
228
  )
191
229
  teacher = forms.ChoiceField(help_text="Select teacher", required=False)
192
230
  classmate_progress = forms.BooleanField(
@@ -201,7 +239,12 @@ class ClassCreationForm(forms.Form):
201
239
 
202
240
  if teacher is not None:
203
241
  # Place current teacher at the top
204
- teacher_choices = [(teacher.id, f"{teacher.new_user.first_name} {teacher.new_user.last_name}")]
242
+ teacher_choices = [
243
+ (
244
+ teacher.id,
245
+ f"{teacher.new_user.first_name} {teacher.new_user.last_name}",
246
+ )
247
+ ]
205
248
 
206
249
  # Get coworkers and add them to the choices if the teacher is an admin
207
250
  if teacher.is_admin:
@@ -212,7 +255,10 @@ class ClassCreationForm(forms.Form):
212
255
  )
213
256
  for coworker in coworkers:
214
257
  teacher_choices.append(
215
- (coworker.id, f"{coworker.new_user.first_name} {coworker.new_user.last_name}")
258
+ (
259
+ coworker.id,
260
+ f"{coworker.new_user.first_name} {coworker.new_user.last_name}",
261
+ )
216
262
  )
217
263
 
218
264
  self.fields["teacher"].choices = teacher_choices
@@ -231,19 +277,35 @@ class ClassEditForm(forms.Form):
231
277
  ]
232
278
  join_choices.extend(
233
279
  [
234
- (str(hours), "Allow external requests to this class for the next " + str(hours) + " hours")
280
+ (
281
+ str(hours),
282
+ "Allow external requests to this class for the next "
283
+ + str(hours)
284
+ + " hours",
285
+ )
235
286
  for hours in range(4, 28, 4)
236
287
  ]
237
288
  )
238
289
  join_choices.extend(
239
290
  [
240
- (str(days * 24), "Allow external requests to this class for the next " + str(days) + " days")
291
+ (
292
+ str(days * 24),
293
+ "Allow external requests to this class for the next "
294
+ + str(days)
295
+ + " days",
296
+ )
241
297
  for days in range(2, 5)
242
298
  ]
243
299
  )
244
- join_choices.append(("1000", "Always allow external requests to this class (not recommended)"))
300
+ join_choices.append(
301
+ (
302
+ "1000",
303
+ "Always allow external requests to this class (not recommended)",
304
+ )
305
+ )
245
306
  name = forms.CharField(
246
- widget=forms.TextInput(attrs={"placeholder": "Enter class name"}), help_text="Enter class name"
307
+ widget=forms.TextInput(attrs={"placeholder": "Enter class name"}),
308
+ help_text="Enter class name",
247
309
  )
248
310
  classmate_progress = forms.BooleanField(
249
311
  label="Allow students to see their classmates' progress?",
@@ -264,7 +326,7 @@ class ClassLevelControlForm(forms.Form):
264
326
  def __init__(self, *args, **kwargs):
265
327
  super(ClassLevelControlForm, self).__init__(*args, **kwargs)
266
328
 
267
- episodes = Episode.objects.filter(pk__in=range(1, 10))
329
+ episodes = Episode.objects.filter(pk__in=range(1, 22))
268
330
 
269
331
  for episode in episodes:
270
332
  levels = []
@@ -275,27 +337,40 @@ class ClassLevelControlForm(forms.Form):
275
337
  levels_choices = [(level.id, level.name) for level in levels]
276
338
 
277
339
  self.fields[episode.name] = forms.MultipleChoiceField(
278
- choices=itertools.chain(levels_choices), widget=forms.CheckboxSelectMultiple(), required=False
340
+ choices=itertools.chain(levels_choices),
341
+ widget=forms.CheckboxSelectMultiple(),
342
+ required=False,
279
343
  )
280
344
 
281
345
 
282
346
  class ClassMoveForm(forms.Form):
283
347
  new_teacher = forms.ChoiceField(
284
- label="New teacher to take over class", help_text="Select teacher", widget=forms.Select()
348
+ label="New teacher to take over class",
349
+ help_text="Select teacher",
350
+ widget=forms.Select(),
285
351
  )
286
352
 
287
353
  def __init__(self, teachers, *args, **kwargs):
288
354
  self.teachers = teachers
289
355
  teacher_choices = []
290
356
  for teacher in teachers:
291
- teacher_choices.append((teacher.id, teacher.new_user.first_name + " " + teacher.new_user.last_name))
357
+ teacher_choices.append(
358
+ (
359
+ teacher.id,
360
+ teacher.new_user.first_name
361
+ + " "
362
+ + teacher.new_user.last_name,
363
+ )
364
+ )
292
365
  super(ClassMoveForm, self).__init__(*args, **kwargs)
293
366
  self.fields["new_teacher"].choices = teacher_choices
294
367
 
295
368
 
296
369
  class TeacherEditStudentForm(forms.Form):
297
370
  name = forms.CharField(
298
- label="Name", widget=forms.TextInput(attrs={"placeholder": "Name"}), help_text="Choose a name"
371
+ label="Name",
372
+ widget=forms.TextInput(attrs={"placeholder": "Name"}),
373
+ help_text="Choose a name",
299
374
  )
300
375
 
301
376
  def __init__(self, student, *args, **kwargs):
@@ -307,14 +382,24 @@ class TeacherEditStudentForm(forms.Form):
307
382
  name = stripStudentName(self.cleaned_data.get("name", ""))
308
383
 
309
384
  if name == "":
310
- raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
385
+ raise forms.ValidationError(
386
+ "'"
387
+ + self.cleaned_data.get("name", "")
388
+ + "' is not a valid name"
389
+ )
311
390
 
312
391
  if re.match(re.compile("^[\w -]+$"), name) is None:
313
- raise forms.ValidationError("Names may only contain letters, numbers, dashes, underscores, and spaces.")
392
+ raise forms.ValidationError(
393
+ "Names may only contain letters, numbers, dashes, underscores, and spaces."
394
+ )
314
395
 
315
- students = Student.objects.filter(class_field=self.klass, new_user__first_name__iexact=name)
396
+ students = Student.objects.filter(
397
+ class_field=self.klass, new_user__first_name__iexact=name
398
+ )
316
399
  if students.exists() and students[0] != self.student:
317
- raise forms.ValidationError("There is already a student called '" + name + "' in this class")
400
+ raise forms.ValidationError(
401
+ "There is already a student called '" + name + "' in this class"
402
+ )
318
403
 
319
404
  return name
320
405
 
@@ -323,12 +408,16 @@ class TeacherSetStudentPass(forms.Form):
323
408
  password = forms.CharField(
324
409
  label="New password",
325
410
  help_text="Enter new password",
326
- widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Enter new password"}),
411
+ widget=forms.PasswordInput(
412
+ attrs={"autocomplete": "off", "placeholder": "Enter new password"}
413
+ ),
327
414
  )
328
415
  confirm_password = forms.CharField(
329
416
  label="Confirm new password",
330
417
  help_text="Confirm new password",
331
- widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Confirm new password"}),
418
+ widget=forms.PasswordInput(
419
+ attrs={"autocomplete": "off", "placeholder": "Confirm new password"}
420
+ ),
332
421
  )
333
422
 
334
423
  def clean_password(self):
@@ -372,19 +461,32 @@ def validateStudentNames(klass, names):
372
461
 
373
462
  def find_clashes(names, students, clashes_found, validationErrors):
374
463
  for name in names:
375
- if students.filter(new_user__first_name__iexact=name).exists() and name not in clashes_found:
464
+ if (
465
+ students.filter(new_user__first_name__iexact=name).exists()
466
+ and name not in clashes_found
467
+ ):
376
468
  validationErrors.append(
377
- forms.ValidationError("There is already a student called '" + name + "' in this class")
469
+ forms.ValidationError(
470
+ "There is already a student called '"
471
+ + name
472
+ + "' in this class"
473
+ )
378
474
  )
379
475
  clashes_found.append(name)
380
476
 
381
477
 
382
478
  def find_duplicates(names, lower_names, validationErrors):
383
479
  duplicates_found = []
384
- for duplicate in [name for name in names if lower_names.count(name.lower()) > 1]:
480
+ for duplicate in [
481
+ name for name in names if lower_names.count(name.lower()) > 1
482
+ ]:
385
483
  if duplicate not in duplicates_found:
386
484
  validationErrors.append(
387
- forms.ValidationError("You cannot add more than one student called '" + duplicate + "'")
485
+ forms.ValidationError(
486
+ "You cannot add more than one student called '"
487
+ + duplicate
488
+ + "'"
489
+ )
388
490
  )
389
491
  duplicates_found.append(duplicate)
390
492
 
@@ -403,7 +505,9 @@ def find_illegal_characters(names, validationErrors):
403
505
 
404
506
  def check_passwords(password, confirm_password):
405
507
  if password is not None and password != confirm_password:
406
- raise forms.ValidationError("The password and the confirmation password do not match")
508
+ raise forms.ValidationError(
509
+ "The password and the confirmation password do not match"
510
+ )
407
511
 
408
512
 
409
513
  class TeacherMoveStudentsDestinationForm(forms.Form):
@@ -428,28 +532,47 @@ class TeacherMoveStudentsDestinationForm(forms.Form):
428
532
  + klass.teacher.new_user.last_name,
429
533
  )
430
534
  )
431
- super(TeacherMoveStudentsDestinationForm, self).__init__(*args, **kwargs)
535
+ super(TeacherMoveStudentsDestinationForm, self).__init__(
536
+ *args, **kwargs
537
+ )
432
538
  self.fields["new_class"].choices = class_choices
433
539
 
434
540
 
435
541
  class TeacherMoveStudentDisambiguationForm(forms.Form):
436
542
  orig_name = forms.CharField(
437
543
  label="Original Name",
438
- widget=forms.TextInput(attrs={"readonly": "readonly", "placeholder": "Original Name", "type": "hidden"}),
544
+ widget=forms.TextInput(
545
+ attrs={
546
+ "readonly": "readonly",
547
+ "placeholder": "Original Name",
548
+ "type": "hidden",
549
+ }
550
+ ),
551
+ )
552
+ name = forms.CharField(
553
+ label="Name",
554
+ widget=forms.TextInput(
555
+ attrs={"placeholder": "Name", "style": "margin : 0px"}
556
+ ),
439
557
  )
440
- name = forms.CharField(label="Name", widget=forms.TextInput(attrs={"placeholder": "Name", "style": "margin : 0px"}))
441
558
 
442
559
  def clean_name(self):
443
560
  name = stripStudentName(self.cleaned_data.get("name", ""))
444
561
  if name == "":
445
- raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
562
+ raise forms.ValidationError(
563
+ "'"
564
+ + self.cleaned_data.get("name", "")
565
+ + "' is not a valid name"
566
+ )
446
567
  return name
447
568
 
448
569
 
449
570
  class BaseTeacherMoveStudentsDisambiguationFormSet(forms.BaseFormSet):
450
571
  def __init__(self, destination, *args, **kwargs):
451
572
  self.destination = destination
452
- super(BaseTeacherMoveStudentsDisambiguationFormSet, self).__init__(*args, **kwargs)
573
+ super(BaseTeacherMoveStudentsDisambiguationFormSet, self).__init__(
574
+ *args, **kwargs
575
+ )
453
576
 
454
577
  def clean(self):
455
578
  if any(self.errors):
@@ -468,28 +591,44 @@ class BaseTeacherMoveStudentsDisambiguationFormSet(forms.BaseFormSet):
468
591
  class TeacherDismissStudentsForm(forms.Form):
469
592
  orig_name = forms.CharField(
470
593
  help_text="Original student name",
471
- widget=forms.TextInput(attrs={"readonly": "readonly", "placeholder": "Original Name", "class": "m-0"}),
594
+ widget=forms.TextInput(
595
+ attrs={
596
+ "readonly": "readonly",
597
+ "placeholder": "Original Name",
598
+ "class": "m-0",
599
+ }
600
+ ),
472
601
  )
473
602
  name = forms.CharField(
474
603
  help_text="New student name",
475
- widget=forms.TextInput(attrs={"placeholder": "Enter new student name", "class": "m-0"}),
604
+ widget=forms.TextInput(
605
+ attrs={"placeholder": "Enter new student name", "class": "m-0"}
606
+ ),
476
607
  )
477
608
  email = forms.EmailField(
478
609
  label="Email",
479
610
  help_text="New email address",
480
- widget=forms.EmailInput(attrs={"placeholder": "Enter email address", "class": "m-0"}),
611
+ widget=forms.EmailInput(
612
+ attrs={"placeholder": "Enter email address", "class": "m-0"}
613
+ ),
481
614
  )
482
615
  confirm_email = forms.EmailField(
483
616
  label="Confirm Email",
484
617
  help_text="Confirm email address",
485
- widget=forms.EmailInput(attrs={"placeholder": "Confirm email address", "class": "m-0"}),
618
+ widget=forms.EmailInput(
619
+ attrs={"placeholder": "Confirm email address", "class": "m-0"}
620
+ ),
486
621
  )
487
622
 
488
623
  def clean_name(self):
489
624
  name = stripStudentName(self.cleaned_data.get("name", ""))
490
625
 
491
626
  if name == "":
492
- raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
627
+ raise forms.ValidationError(
628
+ "'"
629
+ + self.cleaned_data.get("name", "")
630
+ + "' is not a valid name"
631
+ )
493
632
 
494
633
  return name
495
634
 
@@ -547,7 +686,10 @@ class StudentCreationForm(forms.Form):
547
686
 
548
687
 
549
688
  class TeacherAddExternalStudentForm(forms.Form):
550
- name = forms.CharField(label="Student name", widget=forms.TextInput(attrs={"placeholder": "Name"}))
689
+ name = forms.CharField(
690
+ label="Student name",
691
+ widget=forms.TextInput(attrs={"placeholder": "Name"}),
692
+ )
551
693
 
552
694
  def __init__(self, klass, *args, **kwargs):
553
695
  self.klass = klass
@@ -557,9 +699,17 @@ class TeacherAddExternalStudentForm(forms.Form):
557
699
  name = stripStudentName(self.cleaned_data.get("name", ""))
558
700
 
559
701
  if name == "":
560
- raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
702
+ raise forms.ValidationError(
703
+ "'"
704
+ + self.cleaned_data.get("name", "")
705
+ + "' is not a valid name"
706
+ )
561
707
 
562
- if Student.objects.filter(class_field=self.klass, new_user__first_name__iexact=name).exists():
563
- raise forms.ValidationError("There is already a student called '" + name + "' in this class")
708
+ if Student.objects.filter(
709
+ class_field=self.klass, new_user__first_name__iexact=name
710
+ ).exists():
711
+ raise forms.ValidationError(
712
+ "There is already a student called '" + name + "' in this class"
713
+ )
564
714
 
565
715
  return name
@@ -29,7 +29,7 @@
29
29
  </div>
30
30
  <div class="row my-4">
31
31
  <div class="col-sm-12">
32
- <a href="https://docs.codeforlife.education/rapid-router/blockly-guide"
32
+ <a href="https://code-for-life.gitbook.io/rapid-router/blockly-guide"
33
33
  class="button button--primary button--icon col-sm-8"
34
34
  target="_blank">
35
35
  Learn more about Blockly<span class="iconify" data-icon="mdi:open-in-new"></span></a>