codeforlife-portal 6.46.1__py2.py3-none-any.whl → 7.0.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.
- cfl_common/common/csp_config.py +0 -2
- cfl_common/common/migrations/0005_add_worksheets.py +1 -5
- cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +1 -5
- cfl_common/common/migrations/0054_delete_aimmo_models.py +20 -0
- cfl_common/common/models.py +0 -25
- {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/METADATA +3 -4
- {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/RECORD +42 -66
- example_project/portal_test_settings.py +0 -1
- example_project/settings.py +0 -1
- example_project/urls.py +0 -2
- portal/__init__.py +1 -1
- portal/static/portal/sass/partials/_banners.scss +0 -177
- portal/static/portal/sass/partials/_buttons.scss +0 -12
- portal/static/portal/sass/partials/_grids.scss +0 -53
- portal/static/portal/sass/partials/_text.scss +1 -10
- portal/static/portal/sass/styles.scss +0 -1
- portal/strings/play.py +1 -2
- portal/strings/teacher_resources.py +0 -10
- portal/templates/portal/about.html +91 -60
- portal/templates/portal/contribute.html +45 -49
- portal/templates/portal/partials/header.html +0 -12
- portal/templates/portal/play/independent_student_dashboard.html +12 -25
- portal/templates/portal/play/student_dashboard.html +16 -34
- portal/templates/portal/play.html +36 -49
- portal/templates/portal/register.html +1 -1
- portal/templates/portal/teach.html +37 -55
- portal/templates/portal/ten_year_map.html +9 -9
- portal/templatetags/app_tags.py +13 -28
- portal/tests/conftest.py +4 -16
- portal/tests/pageObjects/portal/base_page.py +20 -20
- portal/tests/snapshots/snap_test_partials.py +0 -452
- portal/tests/test_class.py +213 -45
- portal/tests/test_independent_student.py +0 -9
- portal/tests/test_partials.py +6 -56
- portal/tests/test_teacher.py +221 -285
- portal/tests/test_views.py +156 -73
- portal/urls.py +23 -20
- portal/views/student/play.py +36 -25
- portal/views/teacher/teach.py +0 -5
- cfl_common/common/tests/test_migration_aimmo_characters.py +0 -29
- portal/forms/add_game.py +0 -29
- portal/static/portal/img/kurono_hero.jpg +0 -0
- portal/static/portal/img/kurono_landing_hero.png +0 -0
- portal/static/portal/img/kurono_logo.svg +0 -1
- portal/static/portal/img/kurono_logo_grey_background.svg +0 -1
- portal/static/portal/img/kurono_logo_mark.svg +0 -1
- portal/static/portal/img/kurono_resources_hero.jpg +0 -0
- portal/static/portal/img/kurono_story.png +0 -0
- portal/static/portal/img/thumbnail_educate_kurono.png +0 -0
- portal/static/portal/img/thumbnail_kurono_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_kurono.png +0 -0
- portal/static/portal/js/aimmoGame.js +0 -106
- portal/static/portal/sass/partials/_videos.scss +0 -10
- portal/static/portal/video/aimmo_play_now_background_video.mp4 +0 -0
- portal/strings/student_aimmo_dashboard.py +0 -6
- portal/templates/portal/partials/aimmo_games_table.html +0 -89
- portal/templates/portal/play/student_aimmo_dashboard.html +0 -46
- portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
- portal/templatetags/character_list_tags.py +0 -16
- portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
- portal/tests/test_aimmo_dashboards.py +0 -206
- portal/tests/utils/aimmo_games.py +0 -30
- portal/views/aimmo/__init__.py +0 -0
- portal/views/aimmo/dashboard.py +0 -105
- {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/LICENSE.md +0 -0
- {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/WHEEL +0 -0
- {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/top_level.txt +0 -0
portal/tests/test_views.py
CHANGED
|
@@ -6,7 +6,6 @@ from unittest.mock import ANY, Mock, patch
|
|
|
6
6
|
|
|
7
7
|
import PyPDF2
|
|
8
8
|
import pytest
|
|
9
|
-
from aimmo.models import Game
|
|
10
9
|
from common.models import (
|
|
11
10
|
Class,
|
|
12
11
|
DailyActivity,
|
|
@@ -55,7 +54,9 @@ class TestTeacherViews(TestCase):
|
|
|
55
54
|
cls.email, cls.password = signup_teacher_directly()
|
|
56
55
|
cls.school = create_organisation_directly(cls.email)
|
|
57
56
|
_, _, cls.class_access_code = create_class_directly(cls.email)
|
|
58
|
-
_, cls.password_student, cls.student = create_school_student_directly(
|
|
57
|
+
_, cls.password_student, cls.student = create_school_student_directly(
|
|
58
|
+
cls.class_access_code
|
|
59
|
+
)
|
|
59
60
|
|
|
60
61
|
def login(self):
|
|
61
62
|
c = Client()
|
|
@@ -64,7 +65,9 @@ class TestTeacherViews(TestCase):
|
|
|
64
65
|
|
|
65
66
|
def test_reminder_cards(self):
|
|
66
67
|
c = self.login()
|
|
67
|
-
url = reverse(
|
|
68
|
+
url = reverse(
|
|
69
|
+
"teacher_print_reminder_cards", args=[self.class_access_code]
|
|
70
|
+
)
|
|
68
71
|
|
|
69
72
|
# First test with 2 dummy students
|
|
70
73
|
NAME1 = "Test name"
|
|
@@ -98,7 +101,9 @@ class TestTeacherViews(TestCase):
|
|
|
98
101
|
# page number
|
|
99
102
|
students_per_page = REMINDER_CARDS_PDF_ROWS * REMINDER_CARDS_PDF_COLUMNS
|
|
100
103
|
for _ in range(len(studentlist), students_per_page + 1):
|
|
101
|
-
studentlist.append(
|
|
104
|
+
studentlist.append(
|
|
105
|
+
{"name": NAME1, "password": PASSWORD1, "login_url": URL}
|
|
106
|
+
)
|
|
102
107
|
|
|
103
108
|
assert len(studentlist) == students_per_page + 1
|
|
104
109
|
|
|
@@ -137,7 +142,9 @@ class TestTeacherViews(TestCase):
|
|
|
137
142
|
reader = csv.reader(io.StringIO(content))
|
|
138
143
|
|
|
139
144
|
access_code = self.class_access_code
|
|
140
|
-
class_url = reverse(
|
|
145
|
+
class_url = reverse(
|
|
146
|
+
"student_login", kwargs={"access_code": access_code}
|
|
147
|
+
)
|
|
141
148
|
row0 = next(reader)
|
|
142
149
|
assert row0[0].strip() == access_code
|
|
143
150
|
assert class_url in row0[1].strip()
|
|
@@ -176,7 +183,9 @@ class TestTeacherViews(TestCase):
|
|
|
176
183
|
|
|
177
184
|
def test_daily_activity_student_details(self):
|
|
178
185
|
c = self.login()
|
|
179
|
-
url = reverse(
|
|
186
|
+
url = reverse(
|
|
187
|
+
"teacher_print_reminder_cards", args=[self.class_access_code]
|
|
188
|
+
)
|
|
180
189
|
|
|
181
190
|
data = {
|
|
182
191
|
"data": json.dumps(
|
|
@@ -224,7 +233,9 @@ class TestTeacherViews(TestCase):
|
|
|
224
233
|
|
|
225
234
|
def test_release_verified_student(self):
|
|
226
235
|
c = Client()
|
|
227
|
-
student_login_url = reverse(
|
|
236
|
+
student_login_url = reverse(
|
|
237
|
+
"student_login", args=[self.class_access_code]
|
|
238
|
+
)
|
|
228
239
|
response = c.post(
|
|
229
240
|
student_login_url,
|
|
230
241
|
{
|
|
@@ -240,7 +251,9 @@ class TestTeacherViews(TestCase):
|
|
|
240
251
|
c.logout()
|
|
241
252
|
c.login(username=self.email, password=self.password)
|
|
242
253
|
|
|
243
|
-
release_url = reverse(
|
|
254
|
+
release_url = reverse(
|
|
255
|
+
"teacher_dismiss_students", args=[self.class_access_code]
|
|
256
|
+
)
|
|
244
257
|
response = c.post(
|
|
245
258
|
release_url,
|
|
246
259
|
{
|
|
@@ -277,7 +290,9 @@ class TestLoginViews(TestCase):
|
|
|
277
290
|
teacher_email, teacher_password = signup_teacher_directly()
|
|
278
291
|
create_organisation_directly(teacher_email)
|
|
279
292
|
_, _, class_access_code = create_class_directly(teacher_email)
|
|
280
|
-
student_name, student_password, _ = create_school_student_directly(
|
|
293
|
+
student_name, student_password, _ = create_school_student_directly(
|
|
294
|
+
class_access_code
|
|
295
|
+
)
|
|
281
296
|
|
|
282
297
|
return (
|
|
283
298
|
teacher_email,
|
|
@@ -310,9 +325,16 @@ class TestLoginViews(TestCase):
|
|
|
310
325
|
_, _, name, password, class_access_code = self._set_up_test_data()
|
|
311
326
|
|
|
312
327
|
if next_url:
|
|
313
|
-
url =
|
|
328
|
+
url = (
|
|
329
|
+
reverse(
|
|
330
|
+
"student_login", kwargs={"access_code": class_access_code}
|
|
331
|
+
)
|
|
332
|
+
+ "?next=/"
|
|
333
|
+
)
|
|
314
334
|
else:
|
|
315
|
-
url = reverse(
|
|
335
|
+
url = reverse(
|
|
336
|
+
"student_login", kwargs={"access_code": class_access_code}
|
|
337
|
+
)
|
|
316
338
|
|
|
317
339
|
c = Client()
|
|
318
340
|
response = c.post(url, {"username": name, "password": password})
|
|
@@ -351,7 +373,9 @@ class TestLoginViews(TestCase):
|
|
|
351
373
|
|
|
352
374
|
def _get_user_class(self, name, class_access_code):
|
|
353
375
|
klass = Class.objects.get(access_code=class_access_code)
|
|
354
|
-
students = Student.objects.filter(
|
|
376
|
+
students = Student.objects.filter(
|
|
377
|
+
new_user__first_name__iexact=name, class_field=klass
|
|
378
|
+
)
|
|
355
379
|
assert len(students) == 1
|
|
356
380
|
user = students[0].new_user
|
|
357
381
|
return user, klass
|
|
@@ -393,7 +417,9 @@ class TestLoginViews(TestCase):
|
|
|
393
417
|
_, _, name, password, class_access_code = self._set_up_test_data()
|
|
394
418
|
|
|
395
419
|
c = Client()
|
|
396
|
-
url = reverse(
|
|
420
|
+
url = reverse(
|
|
421
|
+
"student_login", kwargs={"access_code": class_access_code}
|
|
422
|
+
)
|
|
397
423
|
c.post(url, {"username": name, "password": password})
|
|
398
424
|
|
|
399
425
|
# check if there's a UserSession data within the last 10 secs
|
|
@@ -414,7 +440,9 @@ class TestLoginViews(TestCase):
|
|
|
414
440
|
randomname = "randomname"
|
|
415
441
|
|
|
416
442
|
c = Client()
|
|
417
|
-
url = reverse(
|
|
443
|
+
url = reverse(
|
|
444
|
+
"student_login", kwargs={"access_code": class_access_code}
|
|
445
|
+
)
|
|
418
446
|
c.post(url, {"username": randomname, "password": "xx"})
|
|
419
447
|
|
|
420
448
|
# check if there's a UserSession data within the last 10 secs
|
|
@@ -440,7 +468,9 @@ class TestLoginViews(TestCase):
|
|
|
440
468
|
|
|
441
469
|
def test_student_direct_login(self):
|
|
442
470
|
_, _, _, _, class_access_code = self._set_up_test_data()
|
|
443
|
-
student, login_id, _, _ = create_student_with_direct_login(
|
|
471
|
+
student, login_id, _, _ = create_student_with_direct_login(
|
|
472
|
+
class_access_code
|
|
473
|
+
)
|
|
444
474
|
|
|
445
475
|
c = Client()
|
|
446
476
|
assert c.login(user_id=student.new_user.id, login_id=login_id) == True
|
|
@@ -543,7 +573,8 @@ class TestViews(TestCase):
|
|
|
543
573
|
"total_available_score": 2040,
|
|
544
574
|
}
|
|
545
575
|
|
|
546
|
-
# Expected context data when a student has also attempted some custom RR
|
|
576
|
+
# Expected context data when a student has also attempted some custom RR
|
|
577
|
+
# levels
|
|
547
578
|
EXPECTED_DATA_WITH_CUSTOM_ATTEMPTS = {
|
|
548
579
|
"num_completed": 2,
|
|
549
580
|
"num_top_scores": 1,
|
|
@@ -553,22 +584,12 @@ class TestViews(TestCase):
|
|
|
553
584
|
"total_custom_available_score": 20,
|
|
554
585
|
}
|
|
555
586
|
|
|
556
|
-
# Expected context data when a student also has access to a Kurono game
|
|
557
|
-
EXPECTED_DATA_WITH_KURONO_GAME = {
|
|
558
|
-
"num_completed": 2,
|
|
559
|
-
"num_top_scores": 1,
|
|
560
|
-
"total_score": 39,
|
|
561
|
-
"total_available_score": 2040,
|
|
562
|
-
"total_custom_score": 10,
|
|
563
|
-
"total_custom_available_score": 20,
|
|
564
|
-
"worksheet_id": 3,
|
|
565
|
-
"worksheet_image": "images/worksheets/ancient.jpg",
|
|
566
|
-
}
|
|
567
|
-
|
|
568
587
|
c = Client()
|
|
569
588
|
|
|
570
589
|
# Login and check initial data
|
|
571
|
-
url = reverse(
|
|
590
|
+
url = reverse(
|
|
591
|
+
"student_login", kwargs={"access_code": class_access_code}
|
|
592
|
+
)
|
|
572
593
|
c.post(url, {"username": student_name, "password": student_password})
|
|
573
594
|
|
|
574
595
|
student_dashboard_url = reverse("student_details")
|
|
@@ -589,9 +610,9 @@ class TestViews(TestCase):
|
|
|
589
610
|
assert response.status_code == 200
|
|
590
611
|
assert response.context_data == EXPECTED_DATA_WITH_ATTEMPTS
|
|
591
612
|
|
|
592
|
-
# Teacher creates 3 custom levels, only shares the first 2 with the
|
|
593
|
-
# Check that the total available score only includes the
|
|
594
|
-
# student. Student attempts one level only.
|
|
613
|
+
# Teacher creates 3 custom levels, only shares the first 2 with the
|
|
614
|
+
# student. Check that the total available score only includes the
|
|
615
|
+
# levels shared with the student. Student attempts one level only.
|
|
595
616
|
custom_level1_id = create_save_level(student.class_field.teacher)
|
|
596
617
|
custom_level2_id = create_save_level(student.class_field.teacher)
|
|
597
618
|
create_save_level(student.class_field.teacher)
|
|
@@ -608,15 +629,6 @@ class TestViews(TestCase):
|
|
|
608
629
|
assert response.status_code == 200
|
|
609
630
|
assert response.context_data == EXPECTED_DATA_WITH_CUSTOM_ATTEMPTS
|
|
610
631
|
|
|
611
|
-
# Link Kurono game to student's class
|
|
612
|
-
game = Game(game_class=klass, worksheet_id=3)
|
|
613
|
-
game.save()
|
|
614
|
-
|
|
615
|
-
response = c.get(student_dashboard_url)
|
|
616
|
-
|
|
617
|
-
assert response.status_code == 200
|
|
618
|
-
assert response.context_data == EXPECTED_DATA_WITH_KURONO_GAME
|
|
619
|
-
|
|
620
632
|
@patch("portal.views.registration.send_dotdigital_email")
|
|
621
633
|
def test_delete_account(self, mock_send_dotdigital_email: Mock):
|
|
622
634
|
email, password = signup_teacher_directly()
|
|
@@ -647,7 +659,9 @@ class TestViews(TestCase):
|
|
|
647
659
|
|
|
648
660
|
# try again with the correct password
|
|
649
661
|
url = reverse("delete_account")
|
|
650
|
-
response = c.post(
|
|
662
|
+
response = c.post(
|
|
663
|
+
url, {"password": password, "unsubscribe_newsletter": "on"}
|
|
664
|
+
)
|
|
651
665
|
|
|
652
666
|
assert response.status_code == 302
|
|
653
667
|
mock_send_dotdigital_email.assert_called_once()
|
|
@@ -729,7 +743,9 @@ class TestViews(TestCase):
|
|
|
729
743
|
|
|
730
744
|
school_id = school.id
|
|
731
745
|
school_name = school.name
|
|
732
|
-
teachers = Teacher.objects.filter(school=school).order_by(
|
|
746
|
+
teachers = Teacher.objects.filter(school=school).order_by(
|
|
747
|
+
"new_user__last_name", "new_user__first_name"
|
|
748
|
+
)
|
|
733
749
|
assert len(teachers) == 3
|
|
734
750
|
|
|
735
751
|
# one of the remaining teachers should be admin (the second in our case, as it's alphabetical)
|
|
@@ -760,7 +776,9 @@ class TestViews(TestCase):
|
|
|
760
776
|
self.assertEqual(mock_send_dotdigital_email.call_count, 2)
|
|
761
777
|
|
|
762
778
|
# 2 teachers left
|
|
763
|
-
teachers = Teacher.objects.filter(school=school).order_by(
|
|
779
|
+
teachers = Teacher.objects.filter(school=school).order_by(
|
|
780
|
+
"new_user__last_name", "new_user__first_name"
|
|
781
|
+
)
|
|
764
782
|
assert len(teachers) == 2
|
|
765
783
|
|
|
766
784
|
# teacher2 should still be admin, teacher4 is not passed admin role because there is teacher2
|
|
@@ -772,7 +790,9 @@ class TestViews(TestCase):
|
|
|
772
790
|
# delete teacher4
|
|
773
791
|
anonymise(user4)
|
|
774
792
|
|
|
775
|
-
teachers = Teacher.objects.filter(school=school).order_by(
|
|
793
|
+
teachers = Teacher.objects.filter(school=school).order_by(
|
|
794
|
+
"new_user__last_name", "new_user__first_name"
|
|
795
|
+
)
|
|
776
796
|
assert len(teachers) == 1
|
|
777
797
|
u = User.objects.get(id=usrid2)
|
|
778
798
|
assert u.new_teacher.is_admin
|
|
@@ -830,13 +850,17 @@ class TestViews(TestCase):
|
|
|
830
850
|
c.logout()
|
|
831
851
|
|
|
832
852
|
@patch("common.helpers.emails.send_dotdigital_email")
|
|
833
|
-
def test_registrations_increment_data(
|
|
853
|
+
def test_registrations_increment_data(
|
|
854
|
+
self, mock_send_dotdigital_email: Mock
|
|
855
|
+
):
|
|
834
856
|
c = Client()
|
|
835
857
|
|
|
836
858
|
total_activity = TotalActivity.objects.get(id=1)
|
|
837
859
|
teacher_registration_count = total_activity.teacher_registrations
|
|
838
860
|
student_registration_count = total_activity.student_registrations
|
|
839
|
-
independent_registration_count =
|
|
861
|
+
independent_registration_count = (
|
|
862
|
+
total_activity.independent_registrations
|
|
863
|
+
)
|
|
840
864
|
|
|
841
865
|
response = c.post(
|
|
842
866
|
reverse("register"),
|
|
@@ -856,7 +880,10 @@ class TestViews(TestCase):
|
|
|
856
880
|
|
|
857
881
|
total_activity = TotalActivity.objects.get(id=1)
|
|
858
882
|
|
|
859
|
-
assert
|
|
883
|
+
assert (
|
|
884
|
+
total_activity.teacher_registrations
|
|
885
|
+
== teacher_registration_count + 1
|
|
886
|
+
)
|
|
860
887
|
|
|
861
888
|
response = c.post(
|
|
862
889
|
reverse("register"),
|
|
@@ -878,7 +905,10 @@ class TestViews(TestCase):
|
|
|
878
905
|
|
|
879
906
|
total_activity = TotalActivity.objects.get(id=1)
|
|
880
907
|
|
|
881
|
-
assert
|
|
908
|
+
assert (
|
|
909
|
+
total_activity.independent_registrations
|
|
910
|
+
== independent_registration_count + 1
|
|
911
|
+
)
|
|
882
912
|
|
|
883
913
|
teacher_email, teacher_password = signup_teacher_directly()
|
|
884
914
|
create_organisation_directly(teacher_email)
|
|
@@ -894,7 +924,10 @@ class TestViews(TestCase):
|
|
|
894
924
|
|
|
895
925
|
total_activity = TotalActivity.objects.get(id=1)
|
|
896
926
|
|
|
897
|
-
assert
|
|
927
|
+
assert (
|
|
928
|
+
total_activity.student_registrations
|
|
929
|
+
== student_registration_count + 3
|
|
930
|
+
)
|
|
898
931
|
|
|
899
932
|
|
|
900
933
|
# CRON view tests
|
|
@@ -913,8 +946,12 @@ class CronTestClient(APIClient):
|
|
|
913
946
|
secure=False,
|
|
914
947
|
**extra,
|
|
915
948
|
):
|
|
916
|
-
wsgi_response = super().generic(
|
|
917
|
-
|
|
949
|
+
wsgi_response = super().generic(
|
|
950
|
+
method, path, data, content_type, secure, **extra
|
|
951
|
+
)
|
|
952
|
+
assert (
|
|
953
|
+
200 <= wsgi_response.status_code < 300
|
|
954
|
+
), f"Response has error status code: {wsgi_response.status_code}"
|
|
918
955
|
|
|
919
956
|
return wsgi_response
|
|
920
957
|
|
|
@@ -933,7 +970,9 @@ class TestUser(CronTestCase):
|
|
|
933
970
|
indy_email, _, _ = create_independent_student_directly()
|
|
934
971
|
|
|
935
972
|
self.teacher_user = User.objects.get(email=teacher_email)
|
|
936
|
-
self.teacher_user_profile = UserProfile.objects.get(
|
|
973
|
+
self.teacher_user_profile = UserProfile.objects.get(
|
|
974
|
+
user=self.teacher_user
|
|
975
|
+
)
|
|
937
976
|
|
|
938
977
|
self.indy_user = User.objects.get(email=indy_email)
|
|
939
978
|
self.indy_user_profile = UserProfile.objects.get(user=self.indy_user)
|
|
@@ -949,11 +988,17 @@ class TestUser(CronTestCase):
|
|
|
949
988
|
assert_called: bool,
|
|
950
989
|
mock_send_dotdigital_email: Mock,
|
|
951
990
|
):
|
|
952
|
-
self.teacher_user.date_joined = timezone.now() - timedelta(
|
|
991
|
+
self.teacher_user.date_joined = timezone.now() - timedelta(
|
|
992
|
+
days=days, hours=12
|
|
993
|
+
)
|
|
953
994
|
self.teacher_user.save()
|
|
954
|
-
self.student_user.date_joined = timezone.now() - timedelta(
|
|
995
|
+
self.student_user.date_joined = timezone.now() - timedelta(
|
|
996
|
+
days=days, hours=12
|
|
997
|
+
)
|
|
955
998
|
self.student_user.save()
|
|
956
|
-
self.indy_user.date_joined = timezone.now() - timedelta(
|
|
999
|
+
self.indy_user.date_joined = timezone.now() - timedelta(
|
|
1000
|
+
days=days, hours=12
|
|
1001
|
+
)
|
|
957
1002
|
self.indy_user.save()
|
|
958
1003
|
|
|
959
1004
|
self.teacher_user_profile.is_verified = is_verified
|
|
@@ -964,9 +1009,13 @@ class TestUser(CronTestCase):
|
|
|
964
1009
|
self.client.get(reverse(view_name))
|
|
965
1010
|
|
|
966
1011
|
if assert_called:
|
|
967
|
-
mock_send_dotdigital_email.assert_any_call(
|
|
1012
|
+
mock_send_dotdigital_email.assert_any_call(
|
|
1013
|
+
ANY, [self.teacher_user.email], personalization_values=ANY
|
|
1014
|
+
)
|
|
968
1015
|
|
|
969
|
-
mock_send_dotdigital_email.assert_any_call(
|
|
1016
|
+
mock_send_dotdigital_email.assert_any_call(
|
|
1017
|
+
ANY, [self.indy_user.email], personalization_values=ANY
|
|
1018
|
+
)
|
|
970
1019
|
|
|
971
1020
|
# Check only two emails are sent - the student should never be included.
|
|
972
1021
|
assert mock_send_dotdigital_email.call_count == 2
|
|
@@ -976,22 +1025,40 @@ class TestUser(CronTestCase):
|
|
|
976
1025
|
mock_send_dotdigital_email.reset_mock()
|
|
977
1026
|
|
|
978
1027
|
def test_first_verify_email_reminder_view(self):
|
|
979
|
-
self.send_verify_email_reminder(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
self.send_verify_email_reminder(
|
|
1028
|
+
self.send_verify_email_reminder(
|
|
1029
|
+
6, False, "first-verify-email-reminder", False
|
|
1030
|
+
)
|
|
1031
|
+
self.send_verify_email_reminder(
|
|
1032
|
+
7, False, "first-verify-email-reminder", True
|
|
1033
|
+
)
|
|
1034
|
+
self.send_verify_email_reminder(
|
|
1035
|
+
7, True, "first-verify-email-reminder", False
|
|
1036
|
+
)
|
|
1037
|
+
self.send_verify_email_reminder(
|
|
1038
|
+
8, False, "first-verify-email-reminder", False
|
|
1039
|
+
)
|
|
983
1040
|
|
|
984
1041
|
def test_second_verify_email_reminder_view(self):
|
|
985
|
-
self.send_verify_email_reminder(
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
self.send_verify_email_reminder(
|
|
1042
|
+
self.send_verify_email_reminder(
|
|
1043
|
+
13, False, "second-verify-email-reminder", False
|
|
1044
|
+
)
|
|
1045
|
+
self.send_verify_email_reminder(
|
|
1046
|
+
14, False, "second-verify-email-reminder", True
|
|
1047
|
+
)
|
|
1048
|
+
self.send_verify_email_reminder(
|
|
1049
|
+
14, True, "second-verify-email-reminder", False
|
|
1050
|
+
)
|
|
1051
|
+
self.send_verify_email_reminder(
|
|
1052
|
+
15, False, "second-verify-email-reminder", False
|
|
1053
|
+
)
|
|
989
1054
|
|
|
990
1055
|
def test_anonymise_unverified_accounts_view(self):
|
|
991
1056
|
now = timezone.now()
|
|
992
1057
|
|
|
993
1058
|
for user in [self.teacher_user, self.indy_user, self.student_user]:
|
|
994
|
-
user.date_joined = now - timedelta(
|
|
1059
|
+
user.date_joined = now - timedelta(
|
|
1060
|
+
days=USER_DELETE_UNVERIFIED_ACCOUNT_DAYS + 1
|
|
1061
|
+
)
|
|
995
1062
|
user.save()
|
|
996
1063
|
|
|
997
1064
|
for user_profile in [self.teacher_user_profile, self.indy_user_profile]:
|
|
@@ -1056,7 +1123,9 @@ class TestUser(CronTestCase):
|
|
|
1056
1123
|
new_user=indy_user,
|
|
1057
1124
|
)
|
|
1058
1125
|
|
|
1059
|
-
activity_today = DailyActivity.objects.get_or_create(
|
|
1126
|
+
activity_today = DailyActivity.objects.get_or_create(
|
|
1127
|
+
date=datetime.now().date()
|
|
1128
|
+
)[0]
|
|
1060
1129
|
daily_teacher_count = activity_today.anonymised_unverified_teachers
|
|
1061
1130
|
daily_indy_count = activity_today.anonymised_unverified_independents
|
|
1062
1131
|
|
|
@@ -1079,16 +1148,30 @@ class TestUser(CronTestCase):
|
|
|
1079
1148
|
assert indy_user_active == assert_active
|
|
1080
1149
|
assert student_user_active
|
|
1081
1150
|
|
|
1082
|
-
activity_today = DailyActivity.objects.get_or_create(
|
|
1151
|
+
activity_today = DailyActivity.objects.get_or_create(
|
|
1152
|
+
date=datetime.now().date()
|
|
1153
|
+
)[0]
|
|
1083
1154
|
total_activity = TotalActivity.objects.get(id=1)
|
|
1084
1155
|
|
|
1085
1156
|
if not teacher_user_active:
|
|
1086
|
-
assert
|
|
1087
|
-
|
|
1157
|
+
assert (
|
|
1158
|
+
activity_today.anonymised_unverified_teachers
|
|
1159
|
+
== daily_teacher_count + 1
|
|
1160
|
+
)
|
|
1161
|
+
assert (
|
|
1162
|
+
total_activity.anonymised_unverified_teachers
|
|
1163
|
+
== total_teacher_count + 1
|
|
1164
|
+
)
|
|
1088
1165
|
|
|
1089
1166
|
if not indy_user_active:
|
|
1090
|
-
assert
|
|
1091
|
-
|
|
1167
|
+
assert (
|
|
1168
|
+
activity_today.anonymised_unverified_independents
|
|
1169
|
+
== daily_indy_count + 1
|
|
1170
|
+
)
|
|
1171
|
+
assert (
|
|
1172
|
+
total_activity.anonymised_unverified_independents
|
|
1173
|
+
== total_indy_count + 1
|
|
1174
|
+
)
|
|
1092
1175
|
|
|
1093
1176
|
teacher_user.delete()
|
|
1094
1177
|
indy_user.delete()
|
portal/urls.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from aimmo.urls import HOMEPAGE_REGEX
|
|
2
1
|
from common.permissions import teacher_verified
|
|
3
2
|
from django.conf.urls import include, url
|
|
4
3
|
from django.http import HttpResponse
|
|
@@ -25,8 +24,10 @@ from portal.helpers.ratelimit import (
|
|
|
25
24
|
from portal.helpers.regexes import ACCESS_CODE_REGEX, JWT_REGEX
|
|
26
25
|
from portal.views import cron
|
|
27
26
|
from portal.views.about import about, contribute, getinvolved
|
|
28
|
-
from portal.views.admin import
|
|
29
|
-
|
|
27
|
+
from portal.views.admin import (
|
|
28
|
+
AdminChangePasswordDoneView,
|
|
29
|
+
AdminChangePasswordView,
|
|
30
|
+
)
|
|
30
31
|
from portal.views.api import (
|
|
31
32
|
AnonymiseOrphanSchoolsView,
|
|
32
33
|
InactiveUsersView,
|
|
@@ -35,7 +36,10 @@ from portal.views.api import (
|
|
|
35
36
|
number_users_per_country,
|
|
36
37
|
registered_users,
|
|
37
38
|
)
|
|
38
|
-
from portal.views.dotmailer import
|
|
39
|
+
from portal.views.dotmailer import (
|
|
40
|
+
dotmailer_consent_form,
|
|
41
|
+
process_newsletter_form,
|
|
42
|
+
)
|
|
39
43
|
from portal.views.email import verify_email
|
|
40
44
|
from portal.views.home import (
|
|
41
45
|
coding_club,
|
|
@@ -109,7 +113,9 @@ from portal.views.two_factor.profile import CustomDisableView
|
|
|
109
113
|
js_info_dict = {"packages": ("conf.locale",)}
|
|
110
114
|
|
|
111
115
|
two_factor_patterns = [
|
|
112
|
-
url(
|
|
116
|
+
url(
|
|
117
|
+
r"^account/two_factor/setup/$", CustomSetupView.as_view(), name="setup"
|
|
118
|
+
),
|
|
113
119
|
url(r"^account/two_factor/qrcode/$", QRGeneratorView.as_view(), name="qr"),
|
|
114
120
|
url(
|
|
115
121
|
r"^account/two_factor/setup/complete/$",
|
|
@@ -164,20 +170,11 @@ urlpatterns = [
|
|
|
164
170
|
]
|
|
165
171
|
),
|
|
166
172
|
),
|
|
167
|
-
url(HOMEPAGE_REGEX, include("aimmo.urls")),
|
|
168
|
-
url(
|
|
169
|
-
r"^teach/kurono/dashboard/$",
|
|
170
|
-
TeacherAimmoDashboard.as_view(),
|
|
171
|
-
name="teacher_aimmo_dashboard",
|
|
172
|
-
),
|
|
173
|
-
url(
|
|
174
|
-
r"^play/kurono/dashboard/$",
|
|
175
|
-
StudentAimmoDashboard.as_view(),
|
|
176
|
-
name="student_aimmo_dashboard",
|
|
177
|
-
),
|
|
178
173
|
url(
|
|
179
174
|
r"^favicon\.ico$",
|
|
180
|
-
RedirectView.as_view(
|
|
175
|
+
RedirectView.as_view(
|
|
176
|
+
url="/static/portal/img/favicon.ico", permanent=True
|
|
177
|
+
),
|
|
181
178
|
),
|
|
182
179
|
url(
|
|
183
180
|
r"^administration/password_change/$",
|
|
@@ -189,7 +186,9 @@ urlpatterns = [
|
|
|
189
186
|
AdminChangePasswordDoneView.as_view(),
|
|
190
187
|
name="administration_password_change_done",
|
|
191
188
|
),
|
|
192
|
-
url(
|
|
189
|
+
url(
|
|
190
|
+
r"^users/inactive/", InactiveUsersView.as_view(), name="inactive_users"
|
|
191
|
+
),
|
|
193
192
|
url(
|
|
194
193
|
r"^locked_out/$",
|
|
195
194
|
TemplateView.as_view(template_name="portal/locked_out.html"),
|
|
@@ -267,7 +266,9 @@ urlpatterns = [
|
|
|
267
266
|
url(r"^consent_form/$", dotmailer_consent_form, name="consent_form"),
|
|
268
267
|
url(
|
|
269
268
|
r"^verify_email/$",
|
|
270
|
-
TemplateView.as_view(
|
|
269
|
+
TemplateView.as_view(
|
|
270
|
+
template_name="portal/email_verification_needed.html"
|
|
271
|
+
),
|
|
271
272
|
name="email_verification",
|
|
272
273
|
),
|
|
273
274
|
url(
|
|
@@ -380,7 +381,9 @@ urlpatterns = [
|
|
|
380
381
|
url(r"^contribute", contribute, name="contribute"),
|
|
381
382
|
url(r"^terms", terms, name="terms"),
|
|
382
383
|
url(r"^privacy-notice/$", privacy_notice, name="privacy_notice"),
|
|
383
|
-
url(
|
|
384
|
+
url(
|
|
385
|
+
r"^privacy-policy/$", privacy_notice, name="privacy_policy"
|
|
386
|
+
), # Keeping this to route from old URL
|
|
384
387
|
url(r"^teach/dashboard/$", dashboard_manage, name="dashboard"),
|
|
385
388
|
url(
|
|
386
389
|
r"^teach/dashboard/kick/(?P<pk>[0-9]+)/$",
|