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

portal/forms/teach.py CHANGED
@@ -3,14 +3,14 @@ import re
3
3
  from builtins import map, range, str
4
4
 
5
5
  from common.helpers.emails import send_verification_email
6
- from common.models import Student, stripStudentName, UserSession, Teacher
6
+ from common.models import Student, Teacher, UserSession, stripStudentName
7
7
  from django import forms
8
8
  from django.contrib.auth import authenticate
9
9
  from django.contrib.auth.forms import AuthenticationForm
10
10
  from django.contrib.auth.models import User
11
11
  from django_recaptcha.fields import ReCaptchaField
12
12
  from django_recaptcha.widgets import ReCaptchaV2Invisible
13
- from game.models import Episode
13
+ from game.models import Episode, Worksheet
14
14
 
15
15
  from portal.forms.error_messages import INVALID_LOGIN_MESSAGE
16
16
  from portal.helpers.password import PasswordStrength, form_clean_password
@@ -18,46 +18,34 @@ from portal.helpers.ratelimit import clear_ratelimit_cache_for_user
18
18
 
19
19
 
20
20
  class InvitedTeacherForm(forms.Form):
21
- prefix = 'teacher_signup'
21
+ prefix = "teacher_signup"
22
22
 
23
23
  def __init__(self, *args, **kwargs):
24
24
  super().__init__(*args, **kwargs)
25
25
  for field_name, field in self.fields.items():
26
- field.widget.attrs['id'] = f'id_teacher_signup-{field_name}'
26
+ field.widget.attrs["id"] = f"id_teacher_signup-{field_name}"
27
27
 
28
28
  teacher_password = forms.CharField(
29
29
  help_text="Enter a password",
30
- widget=forms.PasswordInput(
31
- attrs={"autocomplete": "off", "placeholder": "Password"}
32
- ),
30
+ widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
33
31
  )
34
32
  teacher_confirm_password = forms.CharField(
35
33
  help_text="Repeat password",
36
- widget=forms.PasswordInput(
37
- attrs={"autocomplete": "off", "placeholder": "Repeat password"}
38
- ),
34
+ widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Repeat password"}),
39
35
  )
40
36
 
41
- consent_ticked = forms.BooleanField(
42
- widget=forms.CheckboxInput(), initial=False, required=True
43
- )
44
- newsletter_ticked = forms.BooleanField(
45
- widget=forms.CheckboxInput(), initial=False, required=False
46
- )
37
+ consent_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=True)
38
+ newsletter_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=False)
47
39
 
48
40
  def clean_teacher_password(self):
49
- return form_clean_password(
50
- self, "teacher_password", PasswordStrength.TEACHER
51
- )
41
+ return form_clean_password(self, "teacher_password", PasswordStrength.TEACHER)
52
42
 
53
43
  def clean(self):
54
44
  if any(self.errors):
55
45
  return
56
46
 
57
47
  password = self.cleaned_data.get("teacher_password", None)
58
- confirm_password = self.cleaned_data.get(
59
- "teacher_confirm_password", None
60
- )
48
+ confirm_password = self.cleaned_data.get("teacher_confirm_password", None)
61
49
 
62
50
  check_passwords(password, confirm_password)
63
51
 
@@ -68,22 +56,16 @@ class TeacherSignupForm(InvitedTeacherForm):
68
56
  teacher_first_name = forms.CharField(
69
57
  help_text="Enter your first name",
70
58
  max_length=100,
71
- widget=forms.TextInput(
72
- attrs={"autocomplete": "off", "placeholder": "First name"}
73
- ),
59
+ widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "First name"}),
74
60
  )
75
61
  teacher_last_name = forms.CharField(
76
62
  help_text="Enter your last name",
77
63
  max_length=100,
78
- widget=forms.TextInput(
79
- attrs={"autocomplete": "off", "placeholder": "Last name"}
80
- ),
64
+ widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Last name"}),
81
65
  )
82
66
  teacher_email = forms.EmailField(
83
67
  help_text="Enter your email address",
84
- widget=forms.EmailInput(
85
- attrs={"autocomplete": "off", "placeholder": "Email address"}
86
- ),
68
+ widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
87
69
  )
88
70
 
89
71
  captcha = ReCaptchaField(widget=ReCaptchaV2Invisible)
@@ -92,37 +74,27 @@ class TeacherSignupForm(InvitedTeacherForm):
92
74
  class TeacherEditAccountForm(forms.Form):
93
75
  first_name = forms.CharField(
94
76
  max_length=100,
95
- widget=forms.TextInput(
96
- attrs={"placeholder": "First name", "class": "fName"}
97
- ),
77
+ widget=forms.TextInput(attrs={"placeholder": "First name", "class": "fName"}),
98
78
  help_text="First name",
99
79
  )
100
80
  last_name = forms.CharField(
101
81
  max_length=100,
102
- widget=forms.TextInput(
103
- attrs={"placeholder": "Last name", "class": "lName"}
104
- ),
82
+ widget=forms.TextInput(attrs={"placeholder": "Last name", "class": "lName"}),
105
83
  help_text="Last name",
106
84
  )
107
85
  email = forms.EmailField(
108
86
  required=False,
109
- widget=forms.EmailInput(
110
- attrs={"placeholder": "New email address (optional)"}
111
- ),
87
+ widget=forms.EmailInput(attrs={"placeholder": "New email address (optional)"}),
112
88
  help_text="New email address (optional)",
113
89
  )
114
90
  password = forms.CharField(
115
91
  required=False,
116
- widget=forms.PasswordInput(
117
- attrs={"placeholder": "New password (optional)"}
118
- ),
92
+ widget=forms.PasswordInput(attrs={"placeholder": "New password (optional)"}),
119
93
  help_text="New password (optional)",
120
94
  )
121
95
  confirm_password = forms.CharField(
122
96
  required=False,
123
- widget=forms.PasswordInput(
124
- attrs={"placeholder": "Confirm new password (optional)"}
125
- ),
97
+ widget=forms.PasswordInput(attrs={"placeholder": "Confirm new password (optional)"}),
126
98
  help_text="Confirm new password (optional)",
127
99
  )
128
100
  current_password = forms.CharField(
@@ -149,9 +121,7 @@ class TeacherEditAccountForm(forms.Form):
149
121
 
150
122
  return self.cleaned_data
151
123
 
152
- def check_password_errors(
153
- self, password, confirm_password, current_password
154
- ):
124
+ def check_password_errors(self, password, confirm_password, current_password):
155
125
  check_passwords(password, confirm_password)
156
126
 
157
127
  if not self.user.check_password(current_password):
@@ -160,15 +130,11 @@ class TeacherEditAccountForm(forms.Form):
160
130
 
161
131
  class TeacherLoginForm(AuthenticationForm):
162
132
  username = forms.EmailField(
163
- widget=forms.EmailInput(
164
- attrs={"autocomplete": "off", "placeholder": "Email address"}
165
- ),
133
+ widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
166
134
  help_text="Enter your email address",
167
135
  )
168
136
  password = forms.CharField(
169
- widget=forms.PasswordInput(
170
- attrs={"autocomplete": "off", "placeholder": "Password"}
171
- ),
137
+ widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
172
138
  help_text="Enter your password",
173
139
  )
174
140
 
@@ -202,9 +168,7 @@ class TeacherLoginForm(AuthenticationForm):
202
168
  users = User.objects.filter(email=email)
203
169
 
204
170
  for result in users:
205
- if hasattr(result, "userprofile") and hasattr(
206
- result.userprofile, "teacher"
207
- ):
171
+ if hasattr(result, "userprofile") and hasattr(result.userprofile, "teacher"):
208
172
  user = result
209
173
  break
210
174
 
@@ -286,9 +250,7 @@ class ClassEditForm(forms.Form):
286
250
  [
287
251
  (
288
252
  str(hours),
289
- "Allow external requests to this class for the next "
290
- + str(hours)
291
- + " hours",
253
+ "Allow external requests to this class for the next " + str(hours) + " hours",
292
254
  )
293
255
  for hours in range(4, 28, 4)
294
256
  ]
@@ -297,9 +259,7 @@ class ClassEditForm(forms.Form):
297
259
  [
298
260
  (
299
261
  str(days * 24),
300
- "Allow external requests to this class for the next "
301
- + str(days)
302
- + " days",
262
+ "Allow external requests to this class for the next " + str(days) + " days",
303
263
  )
304
264
  for days in range(2, 5)
305
265
  ]
@@ -333,18 +293,22 @@ class ClassLevelControlForm(forms.Form):
333
293
  def __init__(self, *args, **kwargs):
334
294
  super(ClassLevelControlForm, self).__init__(*args, **kwargs)
335
295
 
336
- episodes = Episode.objects.filter(pk__in=range(1, 22))
296
+ episodes = Episode.objects.filter(pk__in=range(1, 25))
337
297
 
338
298
  for episode in episodes:
339
- levels = []
340
-
299
+ choices = []
341
300
  for level in episode.levels:
342
- levels.append(level)
301
+ try:
302
+ choices.append((f"worksheet:{level.after_worksheet.id}", episode.name))
303
+ except Worksheet.DoesNotExist:
304
+ pass
305
+ choices.append((f"level:{level.id}", level.name))
343
306
 
344
- levels_choices = [(level.id, level.name) for level in levels]
307
+ for worksheet in episode.worksheets.filter(before_level__isnull=True):
308
+ choices.append((f"worksheet:{worksheet.id}", episode.name))
345
309
 
346
310
  self.fields[episode.name] = forms.MultipleChoiceField(
347
- choices=itertools.chain(levels_choices),
311
+ choices=itertools.chain(choices),
348
312
  widget=forms.CheckboxSelectMultiple(),
349
313
  required=False,
350
314
  )
@@ -364,9 +328,7 @@ class ClassMoveForm(forms.Form):
364
328
  teacher_choices.append(
365
329
  (
366
330
  teacher.id,
367
- teacher.new_user.first_name
368
- + " "
369
- + teacher.new_user.last_name,
331
+ teacher.new_user.first_name + " " + teacher.new_user.last_name,
370
332
  )
371
333
  )
372
334
  super(ClassMoveForm, self).__init__(*args, **kwargs)
@@ -389,24 +351,14 @@ class TeacherEditStudentForm(forms.Form):
389
351
  name = stripStudentName(self.cleaned_data.get("name", ""))
390
352
 
391
353
  if name == "":
392
- raise forms.ValidationError(
393
- "'"
394
- + self.cleaned_data.get("name", "")
395
- + "' is not a valid name"
396
- )
354
+ raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
397
355
 
398
356
  if re.match(re.compile("^[\w -]+$"), name) is None:
399
- raise forms.ValidationError(
400
- "Names may only contain letters, numbers, dashes, underscores, and spaces."
401
- )
357
+ raise forms.ValidationError("Names may only contain letters, numbers, dashes, underscores, and spaces.")
402
358
 
403
- students = Student.objects.filter(
404
- class_field=self.klass, new_user__first_name__iexact=name
405
- )
359
+ students = Student.objects.filter(class_field=self.klass, new_user__first_name__iexact=name)
406
360
  if students.exists() and students[0] != self.student:
407
- raise forms.ValidationError(
408
- "There is already a student called '" + name + "' in this class"
409
- )
361
+ raise forms.ValidationError("There is already a student called '" + name + "' in this class")
410
362
 
411
363
  return name
412
364
 
@@ -415,16 +367,12 @@ class TeacherSetStudentPass(forms.Form):
415
367
  password = forms.CharField(
416
368
  label="New password",
417
369
  help_text="Enter new password",
418
- widget=forms.PasswordInput(
419
- attrs={"autocomplete": "off", "placeholder": "Enter new password"}
420
- ),
370
+ widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Enter new password"}),
421
371
  )
422
372
  confirm_password = forms.CharField(
423
373
  label="Confirm new password",
424
374
  help_text="Confirm new password",
425
- widget=forms.PasswordInput(
426
- attrs={"autocomplete": "off", "placeholder": "Confirm new password"}
427
- ),
375
+ widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Confirm new password"}),
428
376
  )
429
377
 
430
378
  def clean_password(self):
@@ -468,32 +416,19 @@ def validateStudentNames(klass, names):
468
416
 
469
417
  def find_clashes(names, students, clashes_found, validationErrors):
470
418
  for name in names:
471
- if (
472
- students.filter(new_user__first_name__iexact=name).exists()
473
- and name not in clashes_found
474
- ):
419
+ if students.filter(new_user__first_name__iexact=name).exists() and name not in clashes_found:
475
420
  validationErrors.append(
476
- forms.ValidationError(
477
- "There is already a student called '"
478
- + name
479
- + "' in this class"
480
- )
421
+ forms.ValidationError("There is already a student called '" + name + "' in this class")
481
422
  )
482
423
  clashes_found.append(name)
483
424
 
484
425
 
485
426
  def find_duplicates(names, lower_names, validationErrors):
486
427
  duplicates_found = []
487
- for duplicate in [
488
- name for name in names if lower_names.count(name.lower()) > 1
489
- ]:
428
+ for duplicate in [name for name in names if lower_names.count(name.lower()) > 1]:
490
429
  if duplicate not in duplicates_found:
491
430
  validationErrors.append(
492
- forms.ValidationError(
493
- "You cannot add more than one student called '"
494
- + duplicate
495
- + "'"
496
- )
431
+ forms.ValidationError("You cannot add more than one student called '" + duplicate + "'")
497
432
  )
498
433
  duplicates_found.append(duplicate)
499
434
 
@@ -512,9 +447,7 @@ def find_illegal_characters(names, validationErrors):
512
447
 
513
448
  def check_passwords(password, confirm_password):
514
449
  if password is not None and password != confirm_password:
515
- raise forms.ValidationError(
516
- "The password and the confirmation password do not match"
517
- )
450
+ raise forms.ValidationError("The password and the confirmation password do not match")
518
451
 
519
452
 
520
453
  class TeacherMoveStudentsDestinationForm(forms.Form):
@@ -539,9 +472,7 @@ class TeacherMoveStudentsDestinationForm(forms.Form):
539
472
  + klass.teacher.new_user.last_name,
540
473
  )
541
474
  )
542
- super(TeacherMoveStudentsDestinationForm, self).__init__(
543
- *args, **kwargs
544
- )
475
+ super(TeacherMoveStudentsDestinationForm, self).__init__(*args, **kwargs)
545
476
  self.fields["new_class"].choices = class_choices
546
477
 
547
478
 
@@ -558,28 +489,20 @@ class TeacherMoveStudentDisambiguationForm(forms.Form):
558
489
  )
559
490
  name = forms.CharField(
560
491
  label="Name",
561
- widget=forms.TextInput(
562
- attrs={"placeholder": "Name", "style": "margin : 0px"}
563
- ),
492
+ widget=forms.TextInput(attrs={"placeholder": "Name", "style": "margin : 0px"}),
564
493
  )
565
494
 
566
495
  def clean_name(self):
567
496
  name = stripStudentName(self.cleaned_data.get("name", ""))
568
497
  if name == "":
569
- raise forms.ValidationError(
570
- "'"
571
- + self.cleaned_data.get("name", "")
572
- + "' is not a valid name"
573
- )
498
+ raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
574
499
  return name
575
500
 
576
501
 
577
502
  class BaseTeacherMoveStudentsDisambiguationFormSet(forms.BaseFormSet):
578
503
  def __init__(self, destination, *args, **kwargs):
579
504
  self.destination = destination
580
- super(BaseTeacherMoveStudentsDisambiguationFormSet, self).__init__(
581
- *args, **kwargs
582
- )
505
+ super(BaseTeacherMoveStudentsDisambiguationFormSet, self).__init__(*args, **kwargs)
583
506
 
584
507
  def clean(self):
585
508
  if any(self.errors):
@@ -608,34 +531,24 @@ class TeacherDismissStudentsForm(forms.Form):
608
531
  )
609
532
  name = forms.CharField(
610
533
  help_text="New student name",
611
- widget=forms.TextInput(
612
- attrs={"placeholder": "Enter new student name", "class": "m-0"}
613
- ),
534
+ widget=forms.TextInput(attrs={"placeholder": "Enter new student name", "class": "m-0"}),
614
535
  )
615
536
  email = forms.EmailField(
616
537
  label="Email",
617
538
  help_text="New email address",
618
- widget=forms.EmailInput(
619
- attrs={"placeholder": "Enter email address", "class": "m-0"}
620
- ),
539
+ widget=forms.EmailInput(attrs={"placeholder": "Enter email address", "class": "m-0"}),
621
540
  )
622
541
  confirm_email = forms.EmailField(
623
542
  label="Confirm Email",
624
543
  help_text="Confirm email address",
625
- widget=forms.EmailInput(
626
- attrs={"placeholder": "Confirm email address", "class": "m-0"}
627
- ),
544
+ widget=forms.EmailInput(attrs={"placeholder": "Confirm email address", "class": "m-0"}),
628
545
  )
629
546
 
630
547
  def clean_name(self):
631
548
  name = stripStudentName(self.cleaned_data.get("name", ""))
632
549
 
633
550
  if name == "":
634
- raise forms.ValidationError(
635
- "'"
636
- + self.cleaned_data.get("name", "")
637
- + "' is not a valid name"
638
- )
551
+ raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
639
552
 
640
553
  return name
641
554
 
@@ -706,17 +619,9 @@ class TeacherAddExternalStudentForm(forms.Form):
706
619
  name = stripStudentName(self.cleaned_data.get("name", ""))
707
620
 
708
621
  if name == "":
709
- raise forms.ValidationError(
710
- "'"
711
- + self.cleaned_data.get("name", "")
712
- + "' is not a valid name"
713
- )
622
+ raise forms.ValidationError("'" + self.cleaned_data.get("name", "") + "' is not a valid name")
714
623
 
715
- if Student.objects.filter(
716
- class_field=self.klass, new_user__first_name__iexact=name
717
- ).exists():
718
- raise forms.ValidationError(
719
- "There is already a student called '" + name + "' in this class"
720
- )
624
+ if Student.objects.filter(class_field=self.klass, new_user__first_name__iexact=name).exists():
625
+ raise forms.ValidationError("There is already a student called '" + name + "' in this class")
721
626
 
722
627
  return name
@@ -147,20 +147,21 @@
147
147
  </label>
148
148
  </div>
149
149
  {% for episode in python_episodes %}
150
- {% if episode.levels %}
151
150
  <div class="panel">
152
151
  <div class="panel-header bg--{{ episode.difficulty }}" id="episode-{{episode.id}}">
153
152
  <div class="d-flex align-items-center justify-content-end" data-toggle="collapse"
154
153
  data-target="#collapse-{{episode.id}}" aria-expanded="false"
155
154
  aria-controls="collapse-{{episode.id}}" data-parent="#episodes">
156
155
  <p class="episode-title flex-grow-1">{{episode.name}}</p>
157
- {% if episode.first_level > 1009 %}
158
- <span>Levels {{episode.first_level|stringformat:"i"|slice:"2:4"}}-{{episode.last_level|stringformat:"i"|slice:"2:4"}}</span>
159
- {% else %}
160
- {% if episode.last_level > 1009 %}
161
- <span>Levels {{episode.first_level|stringformat:"i"|slice:"3:4"}}-{{episode.last_level|stringformat:"i"|slice:"2:4"}}</span>
156
+ {% if episode.levels %}
157
+ {% if episode.first_level > 1009 %}
158
+ <span>Levels {{episode.first_level|stringformat:"i"|slice:"2:4"}}-{{episode.last_level|stringformat:"i"|slice:"2:4"}}</span>
162
159
  {% else %}
163
- <span>Levels {{episode.first_level|stringformat:"i"|slice:"3:4"}}-{{episode.last_level|stringformat:"i"|slice:"3:4"}}</span>
160
+ {% if episode.last_level > 1009 %}
161
+ <span>Levels {{episode.first_level|stringformat:"i"|slice:"3:4"}}-{{episode.last_level|stringformat:"i"|slice:"2:4"}}</span>
162
+ {% else %}
163
+ <span>Levels {{episode.first_level|stringformat:"i"|slice:"3:4"}}-{{episode.last_level|stringformat:"i"|slice:"3:4"}}</span>
164
+ {% endif %}
164
165
  {% endif %}
165
166
  {% endif %}
166
167
  <div class="episode_range_text collapsed d-flex align-items-center justify-content-end"
@@ -177,12 +178,22 @@
177
178
  <div class="panel-body d-flex justify-content-between">
178
179
  <div class="d-flex flex-grow-1 flex-column justify-content-between">
179
180
  {% for level in episode.levels %}
181
+ {% for worksheet in episode.worksheets %}
182
+ {% if worksheet.before_level == level.id %}
183
+ <p>{{episode.name}}</p>
184
+ {% endif %}
185
+ {% endfor %}
180
186
  {% if level.name < 1010 %}
181
187
  <p>{{level.name|stringformat:"i"|slice:"3:4"}}: {{level.title.strip | safe}}</p>
182
188
  {% else %}
183
189
  <p>{{level.name|stringformat:"i"|slice:"2:4"}}: {{level.title.strip | safe}}</p>
184
190
  {% endif %}
185
191
  {% endfor %}
192
+ {% for worksheet in episode.worksheets %}
193
+ {% if not worksheet.before_level %}
194
+ <p>{{episode.name}}{% if episode.worksheets|length > 1 %} pt. {{ forloop.counter }}{% endif %}</p>
195
+ {% endif %}
196
+ {% endfor %}
186
197
  </div>
187
198
  <div class="form__checkbox flex-column justify-content-between p-0">
188
199
  {% for input in level_control_form|get_dict_item:episode.name %}
@@ -194,7 +205,6 @@
194
205
  </div>
195
206
  </div>
196
207
  </div>
197
- {% endif %}
198
208
  {% endfor %}
199
209
  </div>
200
210