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.

@@ -7,8 +7,20 @@ from functools import partial, wraps
7
7
  from uuid import uuid4
8
8
 
9
9
  from common.helpers.emails import send_verification_email
10
- from common.helpers.generators import generate_access_code, generate_login_id, generate_password, get_hashed_login_id
11
- from common.models import Class, DailyActivity, JoinReleaseStudent, Student, Teacher, TotalActivity
10
+ from common.helpers.generators import (
11
+ generate_access_code,
12
+ generate_login_id,
13
+ generate_password,
14
+ get_hashed_login_id,
15
+ )
16
+ from common.models import (
17
+ Class,
18
+ DailyActivity,
19
+ JoinReleaseStudent,
20
+ Student,
21
+ Teacher,
22
+ TotalActivity,
23
+ )
12
24
  from common.permissions import check_teacher_authorised, logged_in_as_teacher
13
25
  from django.contrib import messages as messages
14
26
  from django.contrib.auth.decorators import login_required, user_passes_test
@@ -21,7 +33,7 @@ from django.shortcuts import get_object_or_404, render
21
33
  from django.urls import reverse, reverse_lazy
22
34
  from django.utils import timezone
23
35
  from django.views.decorators.http import require_POST
24
- from game.views.level_selection import get_blockly_episodes
36
+ from game.views.level_selection import get_blockly_episodes, get_python_episodes
25
37
  from portal.views.registration import handle_reset_password_tracking
26
38
  from reportlab.lib.colors import black, red
27
39
  from reportlab.lib.pagesizes import A4
@@ -47,7 +59,9 @@ from portal.forms.teach import (
47
59
  STUDENT_PASSWORD_LENGTH = 6
48
60
  REMINDER_CARDS_PDF_ROWS = 8
49
61
  REMINDER_CARDS_PDF_COLUMNS = 1
50
- REMINDER_CARDS_PDF_WARNING_TEXT = "Please ensure students keep login details in a secure place"
62
+ REMINDER_CARDS_PDF_WARNING_TEXT = (
63
+ "Please ensure students keep login details in a secure place"
64
+ )
51
65
 
52
66
 
53
67
  @login_required(login_url=reverse_lazy("teacher_login"))
@@ -57,7 +71,9 @@ def teacher_onboarding_create_class(request):
57
71
  Onboarding view for creating a class (and organisation if there isn't one, yet)
58
72
  """
59
73
  teacher = request.user.new_teacher
60
- requests = Student.objects.filter(pending_class_request__teacher=teacher, new_user__is_active=True)
74
+ requests = Student.objects.filter(
75
+ pending_class_request__teacher=teacher, new_user__is_active=True
76
+ )
61
77
 
62
78
  if not teacher.school:
63
79
  return HttpResponseRedirect(reverse_lazy("onboarding-organisation"))
@@ -67,10 +83,16 @@ def teacher_onboarding_create_class(request):
67
83
  if form.is_valid():
68
84
  created_class = create_class(form, teacher)
69
85
  messages.success(
70
- request, "The class '{className}' has been created successfully.".format(className=created_class.name)
86
+ request,
87
+ "The class '{className}' has been created successfully.".format(
88
+ className=created_class.name
89
+ ),
71
90
  )
72
91
  return HttpResponseRedirect(
73
- reverse_lazy("onboarding-class", kwargs={"access_code": created_class.access_code})
92
+ reverse_lazy(
93
+ "onboarding-class",
94
+ kwargs={"access_code": created_class.access_code},
95
+ )
74
96
  )
75
97
  else:
76
98
  form = ClassCreationForm(teacher=teacher)
@@ -78,7 +100,9 @@ def teacher_onboarding_create_class(request):
78
100
  classes = Class.objects.filter(teacher=teacher)
79
101
 
80
102
  return render(
81
- request, "portal/teach/onboarding_classes.html", {"form": form, "classes": classes, "requests": requests}
103
+ request,
104
+ "portal/teach/onboarding_classes.html",
105
+ {"form": form, "classes": classes, "requests": requests},
82
106
  )
83
107
 
84
108
 
@@ -96,7 +120,10 @@ def create_class(form, class_teacher, class_creator=None):
96
120
 
97
121
  def generate_student_url(request, student, login_id):
98
122
  return request.build_absolute_uri(
99
- reverse("student_direct_login", kwargs={"user_id": student.new_user.id, "login_id": login_id})
123
+ reverse(
124
+ "student_direct_login",
125
+ kwargs={"user_id": student.new_user.id, "login_id": login_id},
126
+ )
100
127
  )
101
128
 
102
129
 
@@ -106,7 +133,9 @@ def process_edit_class(request, access_code, onboarding_done, next_url):
106
133
  """
107
134
  klass = get_object_or_404(Class, access_code=access_code)
108
135
  teacher = request.user.new_teacher
109
- students = Student.objects.filter(class_field=klass, new_user__is_active=True).order_by("new_user__first_name")
136
+ students = Student.objects.filter(
137
+ class_field=klass, new_user__is_active=True
138
+ ).order_by("new_user__first_name")
110
139
 
111
140
  check_teacher_authorised(request, klass.teacher)
112
141
 
@@ -121,14 +150,24 @@ def process_edit_class(request, access_code, onboarding_done, next_url):
121
150
  login_id, hashed_login_id = generate_login_id()
122
151
 
123
152
  new_student = Student.objects.schoolFactory(
124
- klass=klass, name=name, password=password, login_id=hashed_login_id
153
+ klass=klass,
154
+ name=name,
155
+ password=password,
156
+ login_id=hashed_login_id,
125
157
  )
126
158
 
127
- TotalActivity.objects.update(student_registrations=F("student_registrations") + 1)
159
+ TotalActivity.objects.update(
160
+ student_registrations=F("student_registrations") + 1
161
+ )
128
162
 
129
163
  login_url = generate_student_url(request, new_student, login_id)
130
164
  students_info.append(
131
- {"id": new_student.new_user.id, "name": name, "password": password, "login_url": login_url}
165
+ {
166
+ "id": new_student.new_user.id,
167
+ "name": name,
168
+ "password": password,
169
+ "login_url": login_url,
170
+ }
132
171
  )
133
172
 
134
173
  return render(
@@ -140,7 +179,10 @@ def process_edit_class(request, access_code, onboarding_done, next_url):
140
179
  "onboarding_done": onboarding_done,
141
180
  "query_data": json.dumps(students_info),
142
181
  "class_url": request.build_absolute_uri(
143
- reverse("student_login", kwargs={"access_code": klass.access_code})
182
+ reverse(
183
+ "student_login",
184
+ kwargs={"access_code": klass.access_code},
185
+ )
144
186
  ),
145
187
  },
146
188
  )
@@ -169,7 +211,10 @@ def teacher_onboarding_edit_class(request, access_code):
169
211
  Adding students to a class during the onboarding process
170
212
  """
171
213
  return process_edit_class(
172
- request, access_code, onboarding_done=False, next_url="portal/teach/onboarding_students.html"
214
+ request,
215
+ access_code,
216
+ onboarding_done=False,
217
+ next_url="portal/teach/onboarding_students.html",
173
218
  )
174
219
 
175
220
 
@@ -179,7 +224,12 @@ def teacher_view_class(request, access_code):
179
224
  """
180
225
  Adding students to a class after the onboarding process has been completed
181
226
  """
182
- return process_edit_class(request, access_code, onboarding_done=True, next_url="portal/teach/class.html")
227
+ return process_edit_class(
228
+ request,
229
+ access_code,
230
+ onboarding_done=True,
231
+ next_url="portal/teach/class.html",
232
+ )
183
233
 
184
234
 
185
235
  @require_POST
@@ -191,11 +241,16 @@ def teacher_delete_class(request, access_code):
191
241
  # check user authorised to see class
192
242
  check_teacher_authorised(request, klass.teacher)
193
243
 
194
- if Student.objects.filter(class_field=klass, new_user__is_active=True).exists():
244
+ if Student.objects.filter(
245
+ class_field=klass, new_user__is_active=True
246
+ ).exists():
195
247
  messages.info(
196
- request, "This class still has students, please remove or delete them all before deleting the class."
248
+ request,
249
+ "This class still has students, please remove or delete them all before deleting the class.",
250
+ )
251
+ return HttpResponseRedirect(
252
+ reverse_lazy("view_class", kwargs={"access_code": access_code})
197
253
  )
198
- return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": access_code}))
199
254
 
200
255
  klass.anonymise()
201
256
 
@@ -212,7 +267,9 @@ def teacher_delete_students(request, access_code):
212
267
 
213
268
  # get student objects for students to be deleted, confirming they are in the class
214
269
  student_ids = json.loads(request.POST.get("transfer_students", "[]"))
215
- students = [get_object_or_404(Student, id=i, class_field=klass) for i in student_ids]
270
+ students = [
271
+ get_object_or_404(Student, id=i, class_field=klass) for i in student_ids
272
+ ]
216
273
 
217
274
  def __anonymise(user):
218
275
  # Delete all personal data from inactive user and mark as inactive.
@@ -234,7 +291,9 @@ def teacher_delete_students(request, access_code):
234
291
  else: # otherwise, just delete
235
292
  student.new_user.delete()
236
293
 
237
- return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": access_code}))
294
+ return HttpResponseRedirect(
295
+ reverse_lazy("view_class", kwargs={"access_code": access_code})
296
+ )
238
297
 
239
298
 
240
299
  @login_required(login_url=reverse_lazy("teacher_login"))
@@ -248,7 +307,9 @@ def teacher_edit_class(request, access_code):
248
307
  """
249
308
  klass = get_object_or_404(Class, access_code=access_code)
250
309
  old_teacher = klass.teacher
251
- other_teachers = Teacher.objects.filter(school=old_teacher.school).exclude(user=old_teacher.user)
310
+ other_teachers = Teacher.objects.filter(school=old_teacher.school).exclude(
311
+ user=old_teacher.user
312
+ )
252
313
 
253
314
  # check user authorised to see class
254
315
  check_teacher_authorised(request, klass.teacher)
@@ -256,11 +317,17 @@ def teacher_edit_class(request, access_code):
256
317
  external_requests_message = klass.get_requests_message()
257
318
 
258
319
  blockly_episodes = get_blockly_episodes(request)
320
+ python_episodes = get_python_episodes(request)
259
321
 
260
322
  locked_levels = klass.locked_levels.all()
261
323
  locked_levels_ids = [locked_level.id for locked_level in locked_levels]
262
324
 
263
- form = ClassEditForm(initial={"name": klass.name, "classmate_progress": klass.classmates_data_viewable})
325
+ form = ClassEditForm(
326
+ initial={
327
+ "name": klass.name,
328
+ "classmate_progress": klass.classmates_data_viewable,
329
+ }
330
+ )
264
331
  level_control_form = ClassLevelControlForm()
265
332
  class_move_form = ClassMoveForm(other_teachers)
266
333
 
@@ -272,7 +339,9 @@ def teacher_edit_class(request, access_code):
272
339
  elif "level_control_submit" in request.POST:
273
340
  level_control_form = ClassLevelControlForm(request.POST)
274
341
  if level_control_form.is_valid():
275
- return process_level_control_form(request, klass, blockly_episodes)
342
+ return process_level_control_form(
343
+ request, klass, blockly_episodes, python_episodes
344
+ )
276
345
  elif "class_move_submit" in request.POST:
277
346
  class_move_form = ClassMoveForm(other_teachers, request.POST)
278
347
  if class_move_form.is_valid():
@@ -286,6 +355,7 @@ def teacher_edit_class(request, access_code):
286
355
  "class_move_form": class_move_form,
287
356
  "level_control_form": level_control_form,
288
357
  "blockly_episodes": blockly_episodes,
358
+ "python_episodes": python_episodes,
289
359
  "locked_levels": locked_levels_ids,
290
360
  "class": klass,
291
361
  "external_requests_message": external_requests_message,
@@ -305,12 +375,17 @@ def process_edit_class_form(request, klass, form):
305
375
  # Setting to off
306
376
  klass.always_accept_requests = False
307
377
  klass.accept_requests_until = None
308
- messages.info(request, "Class set successfully to never receive requests from external students.")
378
+ messages.info(
379
+ request,
380
+ "Class set successfully to never receive requests from external students.",
381
+ )
309
382
 
310
383
  elif hours < 1000:
311
384
  # Setting to number of hours
312
385
  klass.always_accept_requests = False
313
- klass.accept_requests_until = timezone.now() + timedelta(hours=hours)
386
+ klass.accept_requests_until = timezone.now() + timedelta(
387
+ hours=hours
388
+ )
314
389
  messages.info(
315
390
  request,
316
391
  "Class set successfully to receive requests from external students until "
@@ -324,35 +399,53 @@ def process_edit_class_form(request, klass, form):
324
399
  klass.always_accept_requests = True
325
400
  klass.accept_requests_until = None
326
401
  messages.info(
327
- request, "Class set successfully to always receive requests from external students (not recommended)"
402
+ request,
403
+ "Class set successfully to always receive requests from external students (not recommended)",
328
404
  )
329
405
 
330
406
  klass.name = name
331
407
  klass.classmates_data_viewable = classmate_progress
332
408
  klass.save()
333
409
 
334
- messages.success(request, "The class's settings have been changed successfully.")
410
+ messages.success(
411
+ request, "The class's settings have been changed successfully."
412
+ )
335
413
 
336
- return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": klass.access_code}))
414
+ return HttpResponseRedirect(
415
+ reverse_lazy("view_class", kwargs={"access_code": klass.access_code})
416
+ )
337
417
 
338
418
 
339
- def process_level_control_form(request, klass, blockly_episodes):
419
+ def process_level_control_form(
420
+ request, klass, blockly_episodes, python_episodes
421
+ ):
340
422
  """
341
423
  Find the levels that the user wants to lock and lock them for the specific class.
342
424
  :param request: The request sent by the user submitting the form.
343
425
  :param klass: The class for which the levels are being locked / unlocked.
344
- :param blockly_episodes: The set of Blockly Episodes in the game.
426
+ :param blockly_episodes: The set of Blockly Episodes (Rapid Router).
427
+ :param blockly_episodes: The set of Python Episodes (Python Den).
345
428
  :return: A redirect to the teacher dashboard with a success message.
346
429
  """
347
430
  levels_to_lock_ids = []
348
431
 
349
- mark_levels_to_lock_in_episodes(request, blockly_episodes, levels_to_lock_ids)
432
+ mark_levels_to_lock_in_episodes(
433
+ request, blockly_episodes, levels_to_lock_ids
434
+ )
435
+ mark_levels_to_lock_in_episodes(
436
+ request, python_episodes, levels_to_lock_ids
437
+ )
350
438
 
351
439
  klass.locked_levels.clear()
352
- [klass.locked_levels.add(levels_to_lock_id) for levels_to_lock_id in levels_to_lock_ids]
440
+ [
441
+ klass.locked_levels.add(levels_to_lock_id)
442
+ for levels_to_lock_id in levels_to_lock_ids
443
+ ]
353
444
 
354
445
  messages.success(request, "Your level preferences have been saved.")
355
- activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
446
+ activity_today = DailyActivity.objects.get_or_create(
447
+ date=datetime.now().date()
448
+ )[0]
356
449
  activity_today.level_control_submits += 1
357
450
  activity_today.save()
358
451
 
@@ -375,10 +468,14 @@ def mark_levels_to_lock_in_episodes(request, episodes, levels_to_lock_ids):
375
468
  [
376
469
  levels_to_lock_ids.append(episode_level["id"])
377
470
  for episode_level in episode_levels
378
- if str(episode_level["id"]) not in request.POST.getlist(episode_name)
471
+ if str(episode_level["id"])
472
+ not in request.POST.getlist(episode_name)
379
473
  ]
380
474
  else:
381
- [levels_to_lock_ids.append(episode_level["id"]) for episode_level in episode_levels]
475
+ [
476
+ levels_to_lock_ids.append(episode_level["id"])
477
+ for episode_level in episode_levels
478
+ ]
382
479
 
383
480
 
384
481
  def process_move_class_form(request, klass, form):
@@ -388,7 +485,10 @@ def process_move_class_form(request, klass, form):
388
485
  klass.teacher = new_teacher
389
486
  klass.save()
390
487
 
391
- messages.success(request, "The class has been successfully assigned to a different teacher.")
488
+ messages.success(
489
+ request,
490
+ "The class has been successfully assigned to a different teacher.",
491
+ )
392
492
  return HttpResponseRedirect(reverse_lazy("dashboard"))
393
493
 
394
494
 
@@ -401,7 +501,9 @@ def teacher_edit_student(request, pk):
401
501
  student = get_object_or_404(Student, id=pk)
402
502
  check_teacher_authorised(request, student.class_field.teacher)
403
503
 
404
- name_form = TeacherEditStudentForm(student, initial={"name": student.new_user.first_name})
504
+ name_form = TeacherEditStudentForm(
505
+ student, initial={"name": student.new_user.first_name}
506
+ )
405
507
 
406
508
  password_form = TeacherSetStudentPass()
407
509
  set_password_mode = False
@@ -415,16 +517,24 @@ def teacher_edit_student(request, pk):
415
517
  student.new_user.save()
416
518
  student.save()
417
519
 
418
- messages.success(request, "The student's details have been changed successfully.")
520
+ messages.success(
521
+ request,
522
+ "The student's details have been changed successfully.",
523
+ )
419
524
 
420
525
  return HttpResponseRedirect(
421
- reverse_lazy("view_class", kwargs={"access_code": student.class_field.access_code})
526
+ reverse_lazy(
527
+ "view_class",
528
+ kwargs={"access_code": student.class_field.access_code},
529
+ )
422
530
  )
423
531
 
424
532
  else:
425
533
  password_form = TeacherSetStudentPass(request.POST)
426
534
  if password_form.is_valid():
427
- return process_reset_password_form(request, student, password_form)
535
+ return process_reset_password_form(
536
+ request, student, password_form
537
+ )
428
538
  set_password_mode = True
429
539
 
430
540
  return render(
@@ -448,7 +558,10 @@ def process_reset_password_form(request, student, password_form):
448
558
  uuidstr = uuid4().hex
449
559
  login_id = get_hashed_login_id(uuidstr)
450
560
  login_url = request.build_absolute_uri(
451
- reverse("student_direct_login", kwargs={"user_id": student.new_user.id, "login_id": uuidstr})
561
+ reverse(
562
+ "student_direct_login",
563
+ kwargs={"user_id": student.new_user.id, "login_id": uuidstr},
564
+ )
452
565
  )
453
566
 
454
567
  students_info = [
@@ -464,7 +577,9 @@ def process_reset_password_form(request, student, password_form):
464
577
  student.new_user.set_password(new_password)
465
578
  student.new_user.save()
466
579
  student.login_id = login_id
467
- clear_ratelimit_cache_for_user(f"{student.new_user.first_name},{student.class_field.access_code}")
580
+ clear_ratelimit_cache_for_user(
581
+ f"{student.new_user.first_name},{student.class_field.access_code}"
582
+ )
468
583
  student.blocked_time = datetime.now(tz=pytz.utc) - timedelta(days=1)
469
584
  student.save()
470
585
 
@@ -477,7 +592,10 @@ def process_reset_password_form(request, student, password_form):
477
592
  "onboarding_done": True,
478
593
  "query_data": json.dumps(students_info),
479
594
  "class_url": request.build_absolute_uri(
480
- reverse("student_login", kwargs={"access_code": student.class_field.access_code})
595
+ reverse(
596
+ "student_login",
597
+ kwargs={"access_code": student.class_field.access_code},
598
+ )
481
599
  ),
482
600
  },
483
601
  )
@@ -495,7 +613,9 @@ def teacher_dismiss_students(request, access_code):
495
613
 
496
614
  # get student objects for students to be dismissed, confirming they are in the class
497
615
  student_ids = json.loads(request.POST.get("transfer_students", "[]"))
498
- students = [get_object_or_404(Student, id=i, class_field=klass) for i in student_ids]
616
+ students = [
617
+ get_object_or_404(Student, id=i, class_field=klass) for i in student_ids
618
+ ]
499
619
 
500
620
  TeacherDismissStudentsFormSet = formset_factory(
501
621
  wraps(TeacherDismissStudentsForm)(partial(TeacherDismissStudentsForm)),
@@ -506,11 +626,17 @@ def teacher_dismiss_students(request, access_code):
506
626
  if is_right_dismiss_form(request):
507
627
  formset = TeacherDismissStudentsFormSet(request.POST)
508
628
  if formset.is_valid():
509
- return process_dismiss_student_form(request, formset, klass, access_code)
629
+ return process_dismiss_student_form(
630
+ request, formset, klass, access_code
631
+ )
510
632
 
511
633
  else:
512
634
  initial_data = [
513
- {"orig_name": student.new_user.first_name, "name": student.new_user.first_name, "email": ""}
635
+ {
636
+ "orig_name": student.new_user.first_name,
637
+ "name": student.new_user.first_name,
638
+ "email": "",
639
+ }
514
640
  for student in students
515
641
  ]
516
642
 
@@ -537,7 +663,11 @@ def process_dismiss_student_form(request, formset, klass, access_code):
537
663
  failed_users.append(data["orig_name"])
538
664
  continue
539
665
 
540
- student = get_object_or_404(Student, class_field=klass, new_user__first_name__iexact=data["orig_name"])
666
+ student = get_object_or_404(
667
+ Student,
668
+ class_field=klass,
669
+ new_user__first_name__iexact=data["orig_name"],
670
+ )
541
671
 
542
672
  student.class_field = None
543
673
  student.new_user.first_name = data["name"]
@@ -549,13 +679,20 @@ def process_dismiss_student_form(request, formset, klass, access_code):
549
679
  student.user.save()
550
680
 
551
681
  # log the data
552
- joinrelease = JoinReleaseStudent.objects.create(student=student, action_type=JoinReleaseStudent.RELEASE)
682
+ joinrelease = JoinReleaseStudent.objects.create(
683
+ student=student, action_type=JoinReleaseStudent.RELEASE
684
+ )
553
685
  joinrelease.save()
554
686
 
555
- send_verification_email(request, student.new_user, data, school=klass.teacher.school)
687
+ send_verification_email(
688
+ request, student.new_user, data, school=klass.teacher.school
689
+ )
556
690
 
557
691
  if not failed_users:
558
- messages.success(request, "The students have been released successfully from the class.")
692
+ messages.success(
693
+ request,
694
+ "The students have been released successfully from the class.",
695
+ )
559
696
  else:
560
697
  messages.warning(
561
698
  request,
@@ -563,7 +700,9 @@ def process_dismiss_student_form(request, formset, klass, access_code):
563
700
  "Please make sure the email has not been registered to another account.",
564
701
  )
565
702
 
566
- return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": access_code}))
703
+ return HttpResponseRedirect(
704
+ reverse_lazy("view_class", kwargs={"access_code": access_code})
705
+ )
567
706
 
568
707
 
569
708
  @login_required(login_url=reverse_lazy("teacher_login"))
@@ -578,7 +717,9 @@ def teacher_class_password_reset(request, access_code):
578
717
  check_teacher_authorised(request, klass.teacher)
579
718
 
580
719
  student_ids = json.loads(request.POST.get("transfer_students", "[]"))
581
- students = [get_object_or_404(Student, id=i, class_field=klass) for i in student_ids]
720
+ students = [
721
+ get_object_or_404(Student, id=i, class_field=klass) for i in student_ids
722
+ ]
582
723
 
583
724
  students_info = []
584
725
  handle_reset_password_tracking(request, "SCHOOL_STUDENT", access_code)
@@ -600,7 +741,9 @@ def teacher_class_password_reset(request, access_code):
600
741
  student.new_user.set_password(password)
601
742
  student.new_user.save()
602
743
  student.login_id = hashed_login_id
603
- clear_ratelimit_cache_for_user(f"{student.new_user.first_name},{access_code}")
744
+ clear_ratelimit_cache_for_user(
745
+ f"{student.new_user.first_name},{access_code}"
746
+ )
604
747
  student.blocked_time = datetime.now(tz=pytz.utc) - timedelta(days=1)
605
748
  student.save()
606
749
 
@@ -614,7 +757,9 @@ def teacher_class_password_reset(request, access_code):
614
757
  "students_info": students_info,
615
758
  "query_data": json.dumps(students_info),
616
759
  "class_url": request.build_absolute_uri(
617
- reverse("student_login", kwargs={"access_code": klass.access_code})
760
+ reverse(
761
+ "student_login", kwargs={"access_code": klass.access_code}
762
+ )
618
763
  ),
619
764
  },
620
765
  )
@@ -644,7 +789,11 @@ def teacher_move_students(request, access_code):
644
789
  return render(
645
790
  request,
646
791
  "portal/teach/teacher_move_students.html",
647
- {"transfer_students": transfer_students, "old_class": klass, "form": form},
792
+ {
793
+ "transfer_students": transfer_students,
794
+ "old_class": klass,
795
+ "form": form,
796
+ },
648
797
  )
649
798
 
650
799
 
@@ -660,34 +809,50 @@ def teacher_move_students_to_class(request, access_code):
660
809
 
661
810
  check_if_move_authorised(request, old_class, new_class)
662
811
 
663
- transfer_students_ids = json.loads(request.POST.get("transfer_students", "[]"))
812
+ transfer_students_ids = json.loads(
813
+ request.POST.get("transfer_students", "[]")
814
+ )
664
815
 
665
816
  # get student objects for students to be transferred, confirming they are in the old class still
666
- transfer_students = [get_object_or_404(Student, id=i, class_field=old_class) for i in transfer_students_ids]
817
+ transfer_students = [
818
+ get_object_or_404(Student, id=i, class_field=old_class)
819
+ for i in transfer_students_ids
820
+ ]
667
821
 
668
822
  # get new class' students
669
- new_class_students = Student.objects.filter(class_field=new_class, new_user__is_active=True).order_by(
670
- "new_user__first_name"
671
- )
823
+ new_class_students = Student.objects.filter(
824
+ class_field=new_class, new_user__is_active=True
825
+ ).order_by("new_user__first_name")
672
826
 
673
827
  TeacherMoveStudentDisambiguationFormSet = formset_factory(
674
- wraps(TeacherMoveStudentDisambiguationForm)(partial(TeacherMoveStudentDisambiguationForm)),
828
+ wraps(TeacherMoveStudentDisambiguationForm)(
829
+ partial(TeacherMoveStudentDisambiguationForm)
830
+ ),
675
831
  extra=0,
676
832
  formset=BaseTeacherMoveStudentsDisambiguationFormSet,
677
833
  )
678
834
 
679
835
  if is_right_move_form(request):
680
- formset = TeacherMoveStudentDisambiguationFormSet(new_class, request.POST)
836
+ formset = TeacherMoveStudentDisambiguationFormSet(
837
+ new_class, request.POST
838
+ )
681
839
  if formset.is_valid():
682
- return process_move_students_form(request, formset, old_class, new_class)
840
+ return process_move_students_form(
841
+ request, formset, old_class, new_class
842
+ )
683
843
  else:
684
844
  # format the students for the form
685
845
  initial_data = [
686
- {"orig_name": student.new_user.first_name, "name": student.new_user.first_name}
846
+ {
847
+ "orig_name": student.new_user.first_name,
848
+ "name": student.new_user.first_name,
849
+ }
687
850
  for student in transfer_students
688
851
  ]
689
852
 
690
- formset = TeacherMoveStudentDisambiguationFormSet(new_class, initial=initial_data)
853
+ formset = TeacherMoveStudentDisambiguationFormSet(
854
+ new_class, initial=initial_data
855
+ )
691
856
 
692
857
  return render(
693
858
  request,
@@ -722,7 +887,9 @@ def process_move_students_form(request, formset, old_class, new_class):
722
887
 
723
888
  for name_update in formset.cleaned_data:
724
889
  student = get_object_or_404(
725
- Student, class_field=old_class, new_user__first_name__iexact=name_update["orig_name"]
890
+ Student,
891
+ class_field=old_class,
892
+ new_user__first_name__iexact=name_update["orig_name"],
726
893
  )
727
894
  student.class_field = new_class
728
895
  student.new_user.first_name = name_update["name"]
@@ -730,8 +897,14 @@ def process_move_students_form(request, formset, old_class, new_class):
730
897
  student.save()
731
898
  student.new_user.save()
732
899
 
733
- messages.success(request, "The students have been transferred successfully.")
734
- return HttpResponseRedirect(reverse_lazy("view_class", kwargs={"access_code": old_class.access_code}))
900
+ messages.success(
901
+ request, "The students have been transferred successfully."
902
+ )
903
+ return HttpResponseRedirect(
904
+ reverse_lazy(
905
+ "view_class", kwargs={"access_code": old_class.access_code}
906
+ )
907
+ )
735
908
 
736
909
 
737
910
  class DownloadType(Enum):
@@ -764,7 +937,9 @@ def teacher_print_reminder_cards(request, access_code):
764
937
 
765
938
  CARD_INNER_HEIGHT = CARD_HEIGHT - CARD_PADDING * 2
766
939
 
767
- logo_image = ImageReader(staticfiles_storage.path("portal/img/logo_cfl_reminder_cards.jpg"))
940
+ logo_image = ImageReader(
941
+ staticfiles_storage.path("portal/img/logo_cfl_reminder_cards.jpg")
942
+ )
768
943
 
769
944
  klass = get_object_or_404(Class, access_code=access_code)
770
945
  # Check auth
@@ -772,8 +947,12 @@ def teacher_print_reminder_cards(request, access_code):
772
947
 
773
948
  # Use data from the query string if given
774
949
  student_data = get_student_data(request)
775
- student_login_link = request.build_absolute_uri(reverse("student_login_access_code"))
776
- class_login_link = request.build_absolute_uri(reverse("student_login", kwargs={"access_code": access_code}))
950
+ student_login_link = request.build_absolute_uri(
951
+ reverse("student_login_access_code")
952
+ )
953
+ class_login_link = request.build_absolute_uri(
954
+ reverse("student_login", kwargs={"access_code": access_code})
955
+ )
777
956
 
778
957
  # Now draw everything
779
958
  x = 0
@@ -785,10 +964,17 @@ def teacher_print_reminder_cards(request, access_code):
785
964
  if current_student_count % (NUM_X * NUM_Y) == 0:
786
965
  p.setFillColor(red)
787
966
  p.setFont("Helvetica-Bold", 10)
788
- p.drawString(PAGE_MARGIN, PAGE_MARGIN / 2, REMINDER_CARDS_PDF_WARNING_TEXT)
967
+ p.drawString(
968
+ PAGE_MARGIN, PAGE_MARGIN / 2, REMINDER_CARDS_PDF_WARNING_TEXT
969
+ )
789
970
 
790
971
  left = PAGE_MARGIN + x * CARD_WIDTH + x * INTER_CARD_MARGIN * 2
791
- bottom = PAGE_HEIGHT - PAGE_MARGIN - (y + 1) * CARD_HEIGHT - y * INTER_CARD_MARGIN
972
+ bottom = (
973
+ PAGE_HEIGHT
974
+ - PAGE_MARGIN
975
+ - (y + 1) * CARD_HEIGHT
976
+ - y * INTER_CARD_MARGIN
977
+ )
792
978
 
793
979
  inner_bottom = bottom + CARD_PADDING
794
980
 
@@ -808,7 +994,12 @@ def teacher_print_reminder_cards(request, access_code):
808
994
  anchor="w",
809
995
  )
810
996
 
811
- text_left = left + INTER_CARD_MARGIN + (logo_image.getSize()[0] / logo_image.getSize()[1]) * card_logo_height
997
+ text_left = (
998
+ left
999
+ + INTER_CARD_MARGIN
1000
+ + (logo_image.getSize()[0] / logo_image.getSize()[1])
1001
+ * card_logo_height
1002
+ )
812
1003
 
813
1004
  # student details
814
1005
  p.setFillColor(black)
@@ -821,9 +1012,19 @@ def teacher_print_reminder_cards(request, access_code):
821
1012
  p.setFont("Helvetica-BoldOblique", 12)
822
1013
  p.drawString(text_left, inner_bottom + CARD_INNER_HEIGHT * 0.6, "OR")
823
1014
  p.setFont("Helvetica", 12)
824
- p.drawString(text_left + 22, inner_bottom + CARD_INNER_HEIGHT * 0.6, f"class link: {class_login_link}")
825
- p.drawString(text_left, inner_bottom + CARD_INNER_HEIGHT * 0.3, f"Name: {student['name']}")
826
- p.drawString(text_left, inner_bottom, f"Password: {student['password']}")
1015
+ p.drawString(
1016
+ text_left + 22,
1017
+ inner_bottom + CARD_INNER_HEIGHT * 0.6,
1018
+ f"class link: {class_login_link}",
1019
+ )
1020
+ p.drawString(
1021
+ text_left,
1022
+ inner_bottom + CARD_INNER_HEIGHT * 0.3,
1023
+ f"Name: {student['name']}",
1024
+ )
1025
+ p.drawString(
1026
+ text_left, inner_bottom, f"Password: {student['password']}"
1027
+ )
827
1028
 
828
1029
  x = (x + 1) % NUM_X
829
1030
  y = compute_show_page_character(p, x, y, NUM_Y)
@@ -842,13 +1043,17 @@ def teacher_print_reminder_cards(request, access_code):
842
1043
  @user_passes_test(logged_in_as_teacher, login_url=reverse_lazy("teacher_login"))
843
1044
  def teacher_download_csv(request, access_code):
844
1045
  response = HttpResponse(content_type="text/csv")
845
- response["Content-Disposition"] = 'attachment; filename="student_login_urls.csv"'
1046
+ response[
1047
+ "Content-Disposition"
1048
+ ] = 'attachment; filename="student_login_urls.csv"'
846
1049
 
847
1050
  klass = get_object_or_404(Class, access_code=access_code)
848
1051
  # Check auth
849
1052
  check_teacher_authorised(request, klass.teacher)
850
1053
 
851
- class_url = request.build_absolute_uri(reverse("student_login", kwargs={"access_code": access_code}))
1054
+ class_url = request.build_absolute_uri(
1055
+ reverse("student_login", kwargs={"access_code": access_code})
1056
+ )
852
1057
 
853
1058
  # Use data from the query string if given
854
1059
  student_data = get_student_data(request)
@@ -856,7 +1061,9 @@ def teacher_download_csv(request, access_code):
856
1061
  writer = csv.writer(response)
857
1062
  writer.writerow([access_code, class_url])
858
1063
  for student in student_data:
859
- writer.writerow([student["name"], student["password"], student["login_url"]])
1064
+ writer.writerow(
1065
+ [student["name"], student["password"], student["login_url"]]
1066
+ )
860
1067
 
861
1068
  count_student_details_click(DownloadType.CSV)
862
1069
 
@@ -884,7 +1091,9 @@ def compute_show_page_end(p, x, y):
884
1091
 
885
1092
 
886
1093
  def count_student_pack_downloads_click(student_pack_type):
887
- activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
1094
+ activity_today = DailyActivity.objects.get_or_create(
1095
+ date=datetime.now().date()
1096
+ )[0]
888
1097
  if DownloadType(student_pack_type) == DownloadType.PRIMARY_PACK:
889
1098
  activity_today.primary_coding_club_downloads += 1
890
1099
  elif DownloadType(student_pack_type) == DownloadType.PYTHON_PACK:
@@ -895,7 +1104,9 @@ def count_student_pack_downloads_click(student_pack_type):
895
1104
 
896
1105
 
897
1106
  def count_student_details_click(download_type):
898
- activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
1107
+ activity_today = DailyActivity.objects.get_or_create(
1108
+ date=datetime.now().date()
1109
+ )[0]
899
1110
 
900
1111
  if download_type == DownloadType.CSV:
901
1112
  activity_today.csv_click_count += 1