codeforlife-portal 5.33.5__py2.py3-none-any.whl → 8.9.9__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.
- cfl_common/common/__init__.py +1 -0
- cfl_common/common/app_settings.py +66 -0
- cfl_common/common/apps.py +6 -0
- cfl_common/common/context_processors.py +9 -0
- cfl_common/common/csp_config.py +85 -0
- cfl_common/common/helpers/__init__.py +0 -0
- cfl_common/common/helpers/data_migration_loader.py +42 -0
- cfl_common/common/helpers/emails.py +393 -0
- cfl_common/common/helpers/generators.py +52 -0
- cfl_common/common/helpers/organisation.py +10 -0
- cfl_common/common/mail.py +201 -0
- cfl_common/common/migrations/0001_initial.py +240 -0
- cfl_common/common/migrations/0002_emailverification.py +55 -0
- cfl_common/common/migrations/0003_aimmocharacter.py +31 -0
- cfl_common/common/migrations/0004_add_aimmocharacters.py +17 -0
- cfl_common/common/migrations/0005_add_worksheets.py +8 -0
- cfl_common/common/migrations/0006_update_aimmo_character_image_path.py +17 -0
- cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +8 -0
- cfl_common/common/migrations/0008_unlock_worksheet_3.py +11 -0
- cfl_common/common/migrations/0009_add_blocked_time_to_teacher_and_student.py +24 -0
- cfl_common/common/migrations/0010_remove_teacher_title.py +18 -0
- cfl_common/common/migrations/0011_student_login_id.py +18 -0
- cfl_common/common/migrations/0012_usersession.py +39 -0
- cfl_common/common/migrations/0013_class_school.py +42 -0
- cfl_common/common/migrations/0014_login_type.py +29 -0
- cfl_common/common/migrations/0015_dailyactivity.py +31 -0
- cfl_common/common/migrations/0016_joinreleasestudent.py +42 -0
- cfl_common/common/migrations/0017_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0018_update_aimmo_character_image_path.py +15 -0
- cfl_common/common/migrations/0019_aimmocharacter_alt.py +16 -0
- cfl_common/common/migrations/0020_class_is_active_and_null_access_code.py +23 -0
- cfl_common/common/migrations/0021_school_is_active.py +28 -0
- cfl_common/common/migrations/0022_school_cleanup.py +29 -0
- cfl_common/common/migrations/0023_userprofile_aimmo_badges.py +22 -0
- cfl_common/common/migrations/0024_teacher_invited_by.py +25 -0
- cfl_common/common/migrations/0025_schoolteacherinvitation.py +47 -0
- cfl_common/common/migrations/0026_teacher_remove_join_request.py +22 -0
- cfl_common/common/migrations/0027_class_created_by.py +25 -0
- cfl_common/common/migrations/0028_coding_club_downloads.py +23 -0
- cfl_common/common/migrations/0029_dynamicelement.py +22 -0
- cfl_common/common/migrations/0030_add_maintenance_banner.py +25 -0
- cfl_common/common/migrations/0031_improve_admin_panel.py +56 -0
- cfl_common/common/migrations/0032_dailyactivity_level_control_submits.py +18 -0
- cfl_common/common/migrations/0033_password_reset_tracking_fields.py +23 -0
- cfl_common/common/migrations/0034_dailyactivity_daily_school_student_lockout_reset.py +18 -0
- cfl_common/common/migrations/0035_rename_lockout_fields.py +27 -0
- cfl_common/common/migrations/0036_rename_awaiting_email_verification_userprofile_is_verified.py +17 -0
- cfl_common/common/migrations/0037_migrate_email_verification.py +21 -0
- cfl_common/common/migrations/0038_delete_emailverification.py +16 -0
- cfl_common/common/migrations/0039_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0040_school_county.py +18 -0
- cfl_common/common/migrations/0041_populate_gb_counties.py +27 -0
- cfl_common/common/migrations/0042_totalactivity.py +25 -0
- cfl_common/common/migrations/0043_add_total_activity.py +30 -0
- cfl_common/common/migrations/0044_update_activity_models.py +33 -0
- cfl_common/common/migrations/0045_otp.py +23 -0
- cfl_common/common/migrations/0046_alter_school_country.py +19 -0
- cfl_common/common/migrations/0047_delete_school_postcode.py +16 -0
- cfl_common/common/migrations/0048_unique_school_names.py +42 -0
- cfl_common/common/migrations/0049_anonymise_orphan_users.py +29 -0
- cfl_common/common/migrations/0050_anonymise_orphan_schools.py +30 -0
- cfl_common/common/migrations/0051_verify_returning_users.py +26 -0
- cfl_common/common/migrations/0052_add_cse_fields.py +68 -0
- cfl_common/common/migrations/0053_clean_class_data.py +24 -0
- cfl_common/common/migrations/0054_delete_aimmo_models.py +20 -0
- cfl_common/common/migrations/0055_alter_schoolteacherinvitation_token.py +18 -0
- cfl_common/common/migrations/0056_set_non_school_teachers_as_non_admins.py +25 -0
- cfl_common/common/migrations/0057_teacher_teacher__is_admin.py +19 -0
- cfl_common/common/migrations/0058_userprofile_google_refresh_token_and_more.py +24 -0
- cfl_common/common/migrations/__init__.py +0 -0
- cfl_common/common/models.py +557 -0
- cfl_common/common/permissions.py +84 -0
- cfl_common/common/tests/__init__.py +0 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_schools.py +30 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_users.py +30 -0
- cfl_common/common/tests/test_migration_blocked_time.py +15 -0
- cfl_common/common/tests/test_migration_remove_teacher_title.py +13 -0
- cfl_common/common/tests/test_migration_unique_school_names.py +33 -0
- cfl_common/common/tests/test_migration_verify_returning_users.py +59 -0
- cfl_common/common/tests/test_models.py +87 -0
- cfl_common/common/tests/utils/__init__.py +0 -0
- cfl_common/common/tests/utils/classes.py +38 -0
- cfl_common/common/tests/utils/email.py +67 -0
- cfl_common/common/tests/utils/organisation.py +41 -0
- cfl_common/common/tests/utils/student.py +123 -0
- cfl_common/common/tests/utils/teacher.py +73 -0
- cfl_common/common/tests/utils/user.py +27 -0
- cfl_common/common/utils.py +56 -0
- cfl_common/setup.py +61 -0
- codeforlife_portal-8.9.9.dist-info/METADATA +226 -0
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/RECORD +339 -241
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/WHEEL +1 -1
- codeforlife_portal-8.9.9.dist-info/licenses/LICENSE.md +3 -0
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/top_level.txt +1 -0
- deploy/middleware/maintenance.py +25 -0
- deploy/middleware/screentime_warning.py +29 -0
- deploy/middleware/security.py +5 -6
- deploy/middleware/session_timeout.py +4 -2
- deploy/middleware/tmp_basic_auth.py +41 -0
- example_project/portal_test_settings.py +239 -0
- example_project/settings.py +156 -17
- example_project/urls.py +5 -6
- portal/__init__.py +1 -1
- portal/admin.py +142 -29
- portal/app_settings.py +8 -7
- portal/forms/dotmailer.py +6 -4
- portal/forms/invite_teacher.py +19 -10
- portal/forms/organisation.py +137 -68
- portal/forms/play.py +53 -98
- portal/forms/registration.py +70 -164
- portal/forms/teach.py +147 -121
- portal/handlers.py +1 -2
- portal/helpers/decorators.py +30 -10
- portal/helpers/password.py +86 -47
- portal/helpers/ratelimit.py +32 -15
- portal/helpers/regexes.py +5 -0
- portal/helpers/request_handlers.py +10 -0
- portal/migrations/0044_auto_20150430_0959.py +6 -2
- portal/mixins/__init__.py +1 -0
- portal/mixins/cron_mixin.py +12 -0
- portal/permissions/__init__.py +1 -0
- portal/permissions/is_cron_request_from_google.py +14 -0
- portal/static/portal/img/10_years_anniversary.png +0 -0
- portal/static/portal/img/RR_logo_grass_background.png +0 -0
- portal/static/portal/img/coding_club_hero.jpg +0 -0
- portal/static/portal/img/coding_club_python_pack.png +0 -0
- portal/static/portal/img/facebook.png +0 -0
- portal/static/portal/img/gitbook.png +0 -0
- portal/static/portal/img/howe_dell_1.png +0 -0
- portal/static/portal/img/howe_dell_2.png +0 -0
- portal/static/portal/img/howe_dell_3.png +0 -0
- portal/static/portal/img/logo_cfl.png +0 -0
- portal/static/portal/img/logo_cfl_powered.svg +35 -0
- portal/static/portal/img/logo_cfl_reminder_cards.jpg +0 -0
- portal/static/portal/img/logo_ocado_group.png +0 -0
- portal/static/portal/img/logo_python_den.svg +21 -0
- portal/static/portal/img/long_europe_map.png +0 -0
- portal/static/portal/img/python_den.png +0 -0
- portal/static/portal/img/python_den_banner.svg +26 -0
- portal/static/portal/img/rapid_router_landing_hero.png +0 -0
- portal/static/portal/img/rr_advanced.png +0 -0
- portal/static/portal/img/ten_year_map_pin.svg +1 -0
- portal/static/portal/img/thumbnail_educate_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_python_den.png +0 -0
- portal/static/portal/img/twitter.png +0 -0
- portal/static/portal/js/carouselCards.js +25 -0
- portal/static/portal/js/common.js +96 -1
- portal/static/portal/js/independentLogin.js +16 -0
- portal/static/portal/js/independentRegistration.js +86 -0
- portal/static/portal/js/levelControl.js +77 -0
- portal/static/portal/js/lib/jquery.min.js +2 -0
- portal/static/portal/js/organisation_manage.js +142 -14
- portal/static/portal/js/passwordStrength.js +154 -64
- portal/static/portal/js/resetPassword.js +23 -0
- portal/static/portal/js/riveted.min.js +238 -239
- portal/static/portal/js/school.js +13 -0
- portal/static/portal/js/studentLogin.js +16 -0
- portal/static/portal/js/teacherEditStudent.js +23 -0
- portal/static/portal/js/teacherLogin.js +16 -0
- portal/static/portal/js/tenYearMap.js +14 -0
- portal/static/portal/sass/colorbox.scss +0 -1
- portal/static/portal/sass/modules/_colours.scss +1 -0
- portal/static/portal/sass/modules/_levels.scss +1 -1
- portal/static/portal/sass/modules/_mixins.scss +21 -0
- portal/static/portal/sass/partials/_banners.scss +4 -177
- portal/static/portal/sass/partials/_buttons.scss +12 -15
- portal/static/portal/sass/partials/_carousel.scss +129 -0
- portal/static/portal/sass/partials/_footer.scss +21 -22
- portal/static/portal/sass/partials/_forms.scss +60 -5
- portal/static/portal/sass/partials/_grids.scss +34 -61
- portal/static/portal/sass/partials/_header.scss +28 -20
- portal/static/portal/sass/partials/_images.scss +292 -39
- portal/static/portal/sass/partials/_popup.scss +18 -15
- portal/static/portal/sass/partials/_tables.scss +12 -20
- portal/static/portal/sass/partials/_text.scss +6 -10
- portal/static/portal/sass/styles.scss +0 -1
- portal/static/portal/video/code for life .pdf +0 -0
- portal/strings/about.py +5 -0
- portal/strings/coding_club.py +9 -0
- portal/strings/play.py +6 -5
- portal/strings/teach.py +1 -1
- portal/strings/teacher_resources.py +2 -8
- portal/strings/ten_year_map.py +13 -0
- portal/templates/403.html +2 -2
- portal/templates/404.html +1 -1
- portal/templates/500.html +2 -2
- portal/templates/{captcha → django_recaptcha}/includes/js_v2_invisible.html +3 -3
- portal/templates/{captcha → django_recaptcha}/widget_v2_invisible.html +2 -2
- portal/templates/email.html +4 -2
- portal/templates/maintenance.html +34 -0
- portal/templates/portal/about.html +94 -62
- portal/templates/portal/base.html +176 -152
- portal/templates/portal/coding_club.html +100 -0
- portal/templates/portal/contribute.html +56 -52
- portal/templates/portal/email_invitation_sent.html +1 -1
- portal/templates/portal/email_style_template.html +374 -0
- portal/templates/portal/email_verification_failed.html +1 -1
- portal/templates/portal/email_verification_needed.html +9 -9
- portal/templates/portal/form_shapes.html +20 -8
- portal/templates/portal/getinvolved.html +6 -6
- portal/templates/portal/home.html +35 -10
- portal/templates/portal/home_learning.html +19 -19
- portal/templates/portal/locked_out.html +0 -1
- portal/templates/portal/locked_out_school_student.html +16 -0
- portal/templates/portal/login/independent_student.html +31 -15
- portal/templates/portal/login/student.html +10 -7
- portal/templates/portal/login/student_class_code.html +7 -4
- portal/templates/portal/login/teacher.html +34 -17
- portal/templates/portal/partials/banner.html +18 -4
- portal/templates/portal/partials/benefits.html +1 -1
- portal/templates/portal/partials/card_list.html +34 -24
- portal/templates/portal/partials/character_list.html +5 -5
- portal/templates/portal/partials/cookie_list.html +161 -0
- portal/templates/portal/partials/delete_popup.html +18 -0
- portal/templates/portal/partials/footer.html +57 -26
- portal/templates/portal/partials/header.html +118 -117
- portal/templates/portal/partials/hero_card.html +4 -3
- portal/templates/portal/partials/info_popup.html +3 -3
- portal/templates/portal/partials/invite_admin_teacher.html +23 -0
- portal/templates/portal/partials/popup.html +7 -2
- portal/templates/portal/partials/register_newsletter_tickbox.html +2 -5
- portal/templates/portal/partials/screentime_popup.html +14 -0
- portal/templates/portal/partials/service_unavailable_popup.html +17 -0
- portal/templates/portal/partials/session_popup.html +19 -0
- portal/templates/portal/play/student_dashboard.html +42 -29
- portal/templates/portal/play/student_edit_account.html +64 -9
- portal/templates/portal/play.html +61 -41
- portal/templates/portal/privacy_notice.html +697 -0
- portal/templates/portal/register.html +122 -92
- portal/templates/portal/reset_password.html +20 -40
- portal/templates/portal/reset_password_confirm.html +9 -4
- portal/templates/portal/reset_password_email_sent.html +15 -13
- portal/templates/portal/teach/base_registering.html +1 -1
- portal/templates/portal/teach/class.html +4 -6
- portal/templates/portal/teach/dashboard.html +212 -117
- portal/templates/portal/teach/invited.html +90 -0
- portal/templates/portal/teach/onboarding_classes.html +5 -3
- portal/templates/portal/teach/onboarding_print.html +1 -1
- portal/templates/portal/teach/onboarding_school.html +26 -139
- portal/templates/portal/teach/onboarding_students.html +1 -1
- portal/templates/portal/teach/teacher_dismiss_students.html +73 -55
- portal/templates/portal/teach/teacher_edit_class.html +168 -11
- portal/templates/portal/teach/teacher_edit_student.html +12 -5
- portal/templates/portal/teach/teacher_move_all_classes.html +25 -38
- portal/templates/portal/teach/teacher_move_students_to_class.html +1 -1
- portal/templates/portal/teach.html +61 -42
- portal/templates/portal/ten_year_map.html +147 -0
- portal/templates/portal/terms.html +191 -42
- portal/templates/two_factor/core/login.html +71 -59
- portal/templates/two_factor/core/setup.html +58 -49
- portal/templates/two_factor/profile/disable.html +1 -1
- portal/templates/two_factor/profile/profile.html +35 -17
- portal/templatetags/app_tags.py +59 -84
- portal/templatetags/card_list_tags.py +0 -4
- portal/tests/base_test.py +14 -3
- portal/tests/conftest.py +0 -15
- portal/tests/migrations/test_migration_make_portaladmin_teacher.py +2 -6
- portal/tests/migrations/test_migration_preview_users.py +3 -9
- portal/tests/migrations/test_migration_remove_guardian.py +1 -3
- portal/tests/migrations/test_migration_use_common_models.py +2 -6
- portal/tests/migrations/test_migration_verify_portaladmin.py +1 -3
- portal/tests/pageObjects/portal/admin/admin_base_page.py +0 -21
- portal/tests/pageObjects/portal/base_page.py +16 -26
- portal/tests/pageObjects/portal/email_verification_needed_page.py +3 -2
- portal/tests/pageObjects/portal/game_page.py +12 -19
- portal/tests/pageObjects/portal/home_page.py +13 -15
- portal/tests/pageObjects/portal/independent_login_page.py +13 -17
- portal/tests/pageObjects/portal/password_reset_form_page.py +20 -4
- portal/tests/pageObjects/portal/password_reset_page.py +25 -0
- portal/tests/pageObjects/portal/play/account_page.py +18 -27
- portal/tests/pageObjects/portal/play/dashboard_page.py +4 -4
- portal/tests/pageObjects/portal/play/join_school_or_club_page.py +8 -10
- portal/tests/pageObjects/portal/play/play_base_page.py +5 -3
- portal/tests/pageObjects/portal/signup_page.py +28 -59
- portal/tests/pageObjects/portal/student_login_class_code.py +6 -9
- portal/tests/pageObjects/portal/student_login_page.py +6 -8
- portal/tests/pageObjects/portal/teach/add_independent_student_to_class_page.py +3 -3
- portal/tests/pageObjects/portal/teach/added_independent_student_to_class_page.py +3 -1
- portal/tests/pageObjects/portal/teach/class_page.py +36 -13
- portal/tests/pageObjects/portal/teach/dashboard_page.py +43 -84
- portal/tests/pageObjects/portal/teach/dismiss_students_page.py +7 -5
- portal/tests/pageObjects/portal/teach/edit_student_page.py +10 -8
- portal/tests/pageObjects/portal/teach/move_class_page.py +5 -10
- portal/tests/pageObjects/portal/teach/move_classes_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_disambiguate_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_page.py +6 -13
- portal/tests/pageObjects/portal/teach/onboarding_classes_page.py +5 -3
- portal/tests/pageObjects/portal/teach/onboarding_organisation_page.py +11 -49
- portal/tests/pageObjects/portal/teach/onboarding_student_list_page.py +7 -12
- portal/tests/pageObjects/portal/teach/onboarding_students_page.py +4 -27
- portal/tests/pageObjects/portal/teach/teach_base_page.py +6 -4
- portal/tests/pageObjects/portal/teacher_login_page.py +10 -16
- portal/tests/selenium_test_case.py +3 -43
- portal/tests/snapshots/snap_test_partials.py +11 -165
- portal/tests/test_2FA.py +15 -33
- portal/tests/test_admin.py +15 -97
- portal/tests/test_api.py +212 -91
- portal/tests/test_captcha_forms.py +2 -2
- portal/tests/test_class.py +374 -24
- portal/tests/test_emails.py +83 -20
- portal/tests/{test_newsletter_footer.py → test_global_forms.py} +5 -5
- portal/tests/test_helper_methods.py +30 -0
- portal/tests/test_independent_student.py +255 -144
- portal/tests/test_invite_teacher.py +318 -10
- portal/tests/test_middleware.py +96 -9
- portal/tests/test_organisation.py +78 -262
- portal/tests/test_partials.py +0 -88
- portal/tests/test_ratelimit.py +218 -36
- portal/tests/test_school_student.py +35 -40
- portal/tests/test_security.py +12 -31
- portal/tests/test_teacher.py +425 -325
- portal/tests/test_teacher_student.py +103 -91
- portal/tests/test_views.py +900 -76
- portal/tests/utils/classes.py +2 -2
- portal/tests/utils/messages.py +13 -28
- portal/urls.py +235 -166
- portal/views/admin.py +0 -332
- portal/views/api.py +82 -48
- portal/views/cron/__init__.py +1 -0
- portal/views/cron/user.py +322 -0
- portal/views/dotmailer.py +9 -1
- portal/views/email.py +33 -77
- portal/views/google_analytics.py +28 -0
- portal/views/home.py +126 -97
- portal/views/legal.py +13 -0
- portal/views/login/independent_student.py +5 -5
- portal/views/login/student.py +51 -14
- portal/views/login/teacher.py +2 -6
- portal/views/organisation.py +20 -189
- portal/views/registration.py +97 -17
- portal/views/student/edit_account_details.py +99 -72
- portal/views/student/play.py +81 -62
- portal/views/teacher/dashboard.py +421 -149
- portal/views/teacher/teach.py +226 -177
- portal/views/two_factor/core.py +22 -19
- portal/views/two_factor/profile.py +2 -2
- codeforlife_portal-5.33.5.dist-info/LICENSE.md +0 -577
- codeforlife_portal-5.33.5.dist-info/METADATA +0 -38
- deploy/permissions.py +0 -2
- example_project/manage.py +0 -10
- portal/autoconfig.py +0 -141
- portal/csp_config.py +0 -60
- portal/forms/add_game.py +0 -33
- portal/helpers/location.py +0 -121
- 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/ocado-swirl.svg +0 -22
- portal/static/portal/img/thumbnail_educate_kurono.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources_and_progress_tracking.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/img/x_close_video.png +0 -0
- portal/static/portal/js/aimmoGame.js +0 -106
- portal/static/portal/js/deleteWorkspaces.js +0 -14
- portal/static/portal/js/fuzzySchoolLookup.js +0 -46
- portal/static/portal/js/lib/jquery-3.5.1.min.js +0 -2
- portal/static/portal/js/lib/jquery-ui-1.12.1.min.js +0 -13
- 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/admin/aggregated_data.html +0 -35
- portal/templates/portal/admin/map.html +0 -70
- portal/templates/portal/mouseflow.html +0 -9
- portal/templates/portal/partials/aimmo_games_table.html +0 -83
- portal/templates/portal/partials/register_over_required_age_tickbox.html +0 -9
- portal/templates/portal/play/independent_student_dashboard.html +0 -64
- portal/templates/portal/play/student_aimmo_dashboard.html +0 -63
- portal/templates/portal/privacy_policy.html +0 -483
- portal/templates/portal/reset_password_email.html +0 -9
- portal/templates/portal/teach/invite.html +0 -25
- portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
- portal/templates/portal/teach/teacher_resources.html +0 -68
- portal/templatetags/character_list_tags.py +0 -16
- portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
- portal/tests/pageObjects/portal/student_password_reset_form_page.py +0 -23
- portal/tests/pageObjects/portal/teach/onboarding_revoke_request_page.py +0 -20
- portal/tests/pageObjects/portal/teacher_password_reset_form_page.py +0 -23
- portal/tests/test_aimmo_dashboards.py +0 -172
- portal/tests/test_location.py +0 -217
- portal/tests/utils/aimmo_games.py +0 -30
- portal/views/aimmo/dashboard.py +0 -119
- portal/views/privacy_policy.py +0 -9
- portal/views/teacher/teacher_resources.py +0 -42
- {portal/views/aimmo → cfl_common}/__init__.py +0 -0
portal/views/home.py
CHANGED
|
@@ -1,44 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
import math
|
|
2
|
+
|
|
2
3
|
from common.helpers.emails import (
|
|
3
4
|
send_verification_email,
|
|
4
|
-
send_email,
|
|
5
|
-
NOTIFICATION_EMAIL,
|
|
6
|
-
add_to_dotmailer,
|
|
7
5
|
)
|
|
8
|
-
from common.
|
|
6
|
+
from common.mail import campaign_ids, send_dotdigital_email
|
|
7
|
+
from common.models import DynamicElement, Student, Teacher, TotalActivity
|
|
9
8
|
from common.permissions import logged_in_as_student, logged_in_as_teacher
|
|
10
9
|
from common.utils import _using_two_factor
|
|
11
10
|
from django.contrib import messages as messages
|
|
12
11
|
from django.contrib.auth import logout
|
|
13
12
|
from django.contrib.auth.models import User
|
|
14
|
-
from django.
|
|
13
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
14
|
+
from django.db.models import F
|
|
15
|
+
from django.http import HttpResponse, HttpResponseRedirect
|
|
15
16
|
from django.shortcuts import render
|
|
16
17
|
from django.urls import reverse, reverse_lazy
|
|
18
|
+
from django.utils import timezone
|
|
19
|
+
from django.utils.html import format_html
|
|
17
20
|
from django.views.decorators.cache import cache_control
|
|
18
21
|
|
|
19
22
|
from deploy import captcha
|
|
20
23
|
from portal.forms.play import IndependentStudentSignupForm
|
|
21
24
|
from portal.forms.teach import TeacherSignupForm
|
|
22
25
|
from portal.helpers.captcha import remove_captcha_from_forms
|
|
26
|
+
from portal.helpers.ratelimit import (
|
|
27
|
+
RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_GROUP,
|
|
28
|
+
RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_RATE,
|
|
29
|
+
is_ratelimited,
|
|
30
|
+
)
|
|
31
|
+
from portal.strings.coding_club import CODING_CLUB_BANNER
|
|
23
32
|
from portal.strings.home_learning import HOME_LEARNING_BANNER
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return request.POST["login-teacher_email"]
|
|
29
|
-
|
|
30
|
-
return ""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def play_name_labeller(request):
|
|
34
|
-
if request.method == "POST":
|
|
35
|
-
if "school_login" in request.POST:
|
|
36
|
-
return request.POST["login-name"] + ":" + request.POST["login-access_code"]
|
|
37
|
-
|
|
38
|
-
if "independent_student_login" in request.POST:
|
|
39
|
-
return request.POST["independent_student-username"]
|
|
40
|
-
|
|
41
|
-
return ""
|
|
33
|
+
from portal.strings.ten_year_map import (
|
|
34
|
+
TEN_YEAR_MAP_BANNER,
|
|
35
|
+
TEN_YEAR_MAP_HEADLINE,
|
|
36
|
+
)
|
|
42
37
|
|
|
43
38
|
|
|
44
39
|
def register_view(request):
|
|
@@ -113,13 +108,26 @@ def process_signup_form(request, data):
|
|
|
113
108
|
email = data["teacher_email"]
|
|
114
109
|
|
|
115
110
|
if email and User.objects.filter(email=email).exists():
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
|
|
112
|
+
is_email_ratelimited = is_ratelimited(
|
|
113
|
+
request=request,
|
|
114
|
+
group=RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_GROUP,
|
|
115
|
+
key=lambda *_: email,
|
|
116
|
+
rate=RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_RATE,
|
|
117
|
+
increment=True,
|
|
122
118
|
)
|
|
119
|
+
|
|
120
|
+
if not is_email_ratelimited:
|
|
121
|
+
send_dotdigital_email(
|
|
122
|
+
campaign_ids["user_already_registered"],
|
|
123
|
+
[email],
|
|
124
|
+
personalization_values={
|
|
125
|
+
"EMAIL": email,
|
|
126
|
+
"LOGIN_URL": request.build_absolute_uri(
|
|
127
|
+
reverse("teacher_login")
|
|
128
|
+
),
|
|
129
|
+
},
|
|
130
|
+
)
|
|
123
131
|
else:
|
|
124
132
|
teacher = Teacher.objects.factory(
|
|
125
133
|
first_name=data["teacher_first_name"],
|
|
@@ -128,11 +136,11 @@ def process_signup_form(request, data):
|
|
|
128
136
|
password=data["teacher_password"],
|
|
129
137
|
)
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
user = teacher.user.user
|
|
133
|
-
add_to_dotmailer(user.first_name, user.last_name, user.email)
|
|
139
|
+
send_verification_email(request, teacher.user.user, data)
|
|
134
140
|
|
|
135
|
-
|
|
141
|
+
TotalActivity.objects.update(
|
|
142
|
+
teacher_registrations=F("teacher_registrations") + 1
|
|
143
|
+
)
|
|
136
144
|
|
|
137
145
|
return render(
|
|
138
146
|
request,
|
|
@@ -144,37 +152,28 @@ def process_signup_form(request, data):
|
|
|
144
152
|
|
|
145
153
|
def process_independent_student_signup_form(request, data):
|
|
146
154
|
email = data["email"]
|
|
147
|
-
username = data["username"]
|
|
148
|
-
|
|
149
|
-
independent_students = Student.objects.filter(class_field=None)
|
|
150
155
|
|
|
151
156
|
if email and User.objects.filter(email=email).exists():
|
|
152
|
-
|
|
153
|
-
request,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
email_message["subject"],
|
|
159
|
-
email_message["message"],
|
|
160
|
-
)
|
|
161
|
-
return render(
|
|
162
|
-
request,
|
|
163
|
-
"portal/email_verification_needed.html",
|
|
164
|
-
{"usertype": "INDEP_STUDENT"},
|
|
165
|
-
status=302,
|
|
157
|
+
is_email_ratelimited = is_ratelimited(
|
|
158
|
+
request=request,
|
|
159
|
+
group=RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_GROUP,
|
|
160
|
+
key=lambda *_: email,
|
|
161
|
+
rate=RATELIMIT_USER_ALREADY_REGISTERED_EMAIL_RATE,
|
|
162
|
+
increment=True,
|
|
166
163
|
)
|
|
167
164
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
165
|
+
if not is_email_ratelimited:
|
|
166
|
+
send_dotdigital_email(
|
|
167
|
+
campaign_ids["user_already_registered"],
|
|
168
|
+
[email],
|
|
169
|
+
personalization_values={
|
|
170
|
+
"EMAIL": email,
|
|
171
|
+
"LOGIN_URL": request.build_absolute_uri(
|
|
172
|
+
reverse("independent_student_login")
|
|
173
|
+
),
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
|
|
178
177
|
return render(
|
|
179
178
|
request,
|
|
180
179
|
"portal/email_verification_needed.html",
|
|
@@ -183,17 +182,18 @@ def process_independent_student_signup_form(request, data):
|
|
|
183
182
|
)
|
|
184
183
|
|
|
185
184
|
student = Student.objects.independentStudentFactory(
|
|
186
|
-
|
|
187
|
-
name=data["name"],
|
|
188
|
-
email=data["email"],
|
|
189
|
-
password=data["password"],
|
|
185
|
+
name=data["name"], email=data["email"], password=data["password"]
|
|
190
186
|
)
|
|
191
187
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
dob = data["date_of_birth"]
|
|
189
|
+
age_in_days = timezone.now().date() - dob
|
|
190
|
+
age = math.floor(age_in_days.days / 365.25)
|
|
195
191
|
|
|
196
|
-
send_verification_email(request, student.new_user)
|
|
192
|
+
send_verification_email(request, student.new_user, data, age=age)
|
|
193
|
+
|
|
194
|
+
TotalActivity.objects.update(
|
|
195
|
+
independent_registrations=F("independent_registrations") + 1
|
|
196
|
+
)
|
|
197
197
|
|
|
198
198
|
return render(
|
|
199
199
|
request,
|
|
@@ -204,44 +204,50 @@ def process_independent_student_signup_form(request, data):
|
|
|
204
204
|
|
|
205
205
|
|
|
206
206
|
def is_developer(request):
|
|
207
|
-
return
|
|
207
|
+
return (
|
|
208
|
+
hasattr(request.user, "userprofile")
|
|
209
|
+
and request.user.userprofile.developer
|
|
210
|
+
)
|
|
208
211
|
|
|
209
212
|
|
|
210
213
|
def redirect_teacher_to_correct_page(request, teacher):
|
|
211
214
|
if teacher.has_school():
|
|
212
|
-
|
|
213
|
-
if
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
),
|
|
228
|
-
extra_tags="safe",
|
|
229
|
-
)
|
|
230
|
-
return reverse_lazy("dashboard")
|
|
231
|
-
else:
|
|
232
|
-
return reverse_lazy(
|
|
233
|
-
"onboarding-class",
|
|
234
|
-
kwargs={"access_code": classes[0].access_code},
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
else:
|
|
238
|
-
return reverse_lazy("onboarding-classes")
|
|
215
|
+
link = reverse("two_factor:profile")
|
|
216
|
+
if not _using_two_factor(request.user):
|
|
217
|
+
messages.info(
|
|
218
|
+
request,
|
|
219
|
+
(
|
|
220
|
+
"You are not currently set up with two-factor authentication. "
|
|
221
|
+
+ "Use your phone or tablet to enhance your account’s security.</br>"
|
|
222
|
+
+ "Click <a href='"
|
|
223
|
+
+ link
|
|
224
|
+
+ "'>here</a> to find out more and "
|
|
225
|
+
+ "set it up or go to your account page at any time."
|
|
226
|
+
),
|
|
227
|
+
extra_tags="safe",
|
|
228
|
+
)
|
|
229
|
+
return reverse_lazy("dashboard")
|
|
239
230
|
else:
|
|
240
231
|
return reverse_lazy("onboarding-organisation")
|
|
241
232
|
|
|
242
233
|
|
|
243
234
|
@cache_control(private=True)
|
|
244
235
|
def home(request):
|
|
236
|
+
# Putting this in a try catch because it causes some weird issue in the
|
|
237
|
+
# tests where the first Selenium test passes, but any following test
|
|
238
|
+
# fails because it cannot find the Maintenance banner instance.
|
|
239
|
+
try:
|
|
240
|
+
maintenance_banner = DynamicElement.objects.get(
|
|
241
|
+
name="Maintenance banner"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if maintenance_banner.active:
|
|
245
|
+
messages.info(
|
|
246
|
+
request, format_html(maintenance_banner.text), extra_tags="safe"
|
|
247
|
+
)
|
|
248
|
+
except ObjectDoesNotExist:
|
|
249
|
+
pass
|
|
250
|
+
|
|
245
251
|
"""
|
|
246
252
|
This view is where we can add any messages to be shown upon loading the home page.
|
|
247
253
|
Following this format:
|
|
@@ -255,9 +261,32 @@ def home(request):
|
|
|
255
261
|
return render(request, "portal/home.html")
|
|
256
262
|
|
|
257
263
|
|
|
264
|
+
def coding_club(request):
|
|
265
|
+
return render(
|
|
266
|
+
request, "portal/coding_club.html", {"BANNER": CODING_CLUB_BANNER}
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
258
270
|
def home_learning(request):
|
|
259
271
|
return render(
|
|
260
272
|
request,
|
|
261
273
|
"portal/home_learning.html",
|
|
262
274
|
{"HOME_LEARNING_BANNER": HOME_LEARNING_BANNER},
|
|
263
275
|
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def ten_year_map_page(request):
|
|
279
|
+
messages.info(
|
|
280
|
+
request, "This page is currently under construction.", extra_tags="safe"
|
|
281
|
+
)
|
|
282
|
+
return render(
|
|
283
|
+
request,
|
|
284
|
+
"portal/ten_year_map.html",
|
|
285
|
+
{"BANNER": TEN_YEAR_MAP_BANNER, "HEADLINE": TEN_YEAR_MAP_HEADLINE},
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def reset_screentime_warning(request):
|
|
290
|
+
if request.user.is_authenticated:
|
|
291
|
+
request.session["last_screentime_warning"] = timezone.now().timestamp()
|
|
292
|
+
return HttpResponse(status=204) # No content
|
portal/views/legal.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.shortcuts import render
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def privacy_notice(request):
|
|
5
|
+
return render(
|
|
6
|
+
request,
|
|
7
|
+
"portal/privacy_notice.html",
|
|
8
|
+
{"last_updated": "19th March 2025", "last_updated_children": "19th March 2025"},
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def terms(request):
|
|
13
|
+
return render(request, "portal/terms.html", {"last_updated": "11th July 2022"})
|
|
@@ -36,9 +36,9 @@ class IndependentStudentLoginView(LoginView):
|
|
|
36
36
|
account, this redirects the user to the locked out page. However, if the lockout
|
|
37
37
|
time is more than 24 hours before this is executed, the account is unlocked.
|
|
38
38
|
"""
|
|
39
|
-
|
|
40
|
-
if Student.objects.filter(
|
|
41
|
-
student = Student.objects.get(
|
|
39
|
+
email = request.POST.get("username")
|
|
40
|
+
if Student.objects.filter(new_user__email=email).exists():
|
|
41
|
+
student = Student.objects.get(new_user__email=email)
|
|
42
42
|
|
|
43
43
|
if student.blocked_time is not None:
|
|
44
44
|
if has_user_lockout_expired(student):
|
|
@@ -60,8 +60,8 @@ class IndependentStudentLoginView(LoginView):
|
|
|
60
60
|
clear_ratelimit_cache_for_user(form.cleaned_data["username"])
|
|
61
61
|
|
|
62
62
|
# Log the login time
|
|
63
|
-
|
|
64
|
-
user = User.objects.get(
|
|
63
|
+
email = self.request.POST.get("username")
|
|
64
|
+
user = User.objects.get(email=email)
|
|
65
65
|
session = UserSession(user=user)
|
|
66
66
|
session.save()
|
|
67
67
|
|
portal/views/login/student.py
CHANGED
|
@@ -3,14 +3,14 @@ from django.contrib import messages
|
|
|
3
3
|
from django.contrib.auth import authenticate, login
|
|
4
4
|
from django.contrib.auth.views import LoginView, FormView
|
|
5
5
|
from django.http import HttpResponseRedirect
|
|
6
|
+
from django.shortcuts import render
|
|
6
7
|
from django.urls import reverse_lazy
|
|
7
8
|
from django.utils.html import escape
|
|
8
9
|
|
|
9
10
|
from portal.forms.play import StudentLoginForm, StudentClassCodeForm
|
|
10
|
-
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
LOGGER = logging.getLogger(__name__)
|
|
11
|
+
from portal.helpers.ratelimit import clear_ratelimit_cache_for_user
|
|
12
|
+
from portal.helpers.request_handlers import get_access_code_from_request
|
|
13
|
+
from portal.views.login import has_user_lockout_expired
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class StudentClassCodeView(FormView):
|
|
@@ -47,7 +47,8 @@ class StudentLoginView(LoginView):
|
|
|
47
47
|
class_name = self.kwargs["access_code"].upper()
|
|
48
48
|
messages.info(
|
|
49
49
|
request,
|
|
50
|
-
f"<strong>You are logged in to class: "
|
|
50
|
+
f"<strong>You are logged in to class: "
|
|
51
|
+
f"{escape(class_name)}</strong>",
|
|
51
52
|
extra_tags="safe message--student",
|
|
52
53
|
)
|
|
53
54
|
|
|
@@ -71,12 +72,7 @@ class StudentLoginView(LoginView):
|
|
|
71
72
|
students = Student.objects.filter(
|
|
72
73
|
new_user__first_name__iexact=name, class_field=klass
|
|
73
74
|
)
|
|
74
|
-
|
|
75
|
-
student = students[0]
|
|
76
|
-
except IndexError:
|
|
77
|
-
msg = f"Student {name} in class {class_code} is not found!"
|
|
78
|
-
LOGGER.error(msg)
|
|
79
|
-
raise Exception(msg)
|
|
75
|
+
student = students[0]
|
|
80
76
|
|
|
81
77
|
# Log the login time, class, and login type
|
|
82
78
|
session = UserSession(
|
|
@@ -84,16 +80,53 @@ class StudentLoginView(LoginView):
|
|
|
84
80
|
)
|
|
85
81
|
session.save()
|
|
86
82
|
|
|
83
|
+
student.user.is_verified = True
|
|
84
|
+
student.user.save()
|
|
85
|
+
|
|
87
86
|
def form_valid(self, form):
|
|
88
87
|
"""Security check complete. Log the user in."""
|
|
88
|
+
# Reset ratelimit cache upon successful login
|
|
89
|
+
clear_ratelimit_cache_for_user(form.cleaned_data["username"])
|
|
89
90
|
|
|
90
|
-
login_type = self.kwargs
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
login_type = self.kwargs.get(
|
|
92
|
+
"login_type", "classlink"
|
|
93
|
+
) # default to "classlink" if not specified
|
|
93
94
|
|
|
94
95
|
self._add_login_data(form, login_type)
|
|
95
96
|
return super(StudentLoginView, self).form_valid(form)
|
|
96
97
|
|
|
98
|
+
def post(self, request, *args, **kwargs):
|
|
99
|
+
"""
|
|
100
|
+
If the first name and access code found under the url inputted in the
|
|
101
|
+
form corresponds to that of a blocked account, this redirects the user
|
|
102
|
+
to the locked out page. However, if the lockout time is more than 24
|
|
103
|
+
hours before this is executed, the account is unlocked.
|
|
104
|
+
"""
|
|
105
|
+
username = request.POST.get("username")
|
|
106
|
+
|
|
107
|
+
# get access code from the current url
|
|
108
|
+
access_code = get_access_code_from_request(request)
|
|
109
|
+
if Student.objects.filter(
|
|
110
|
+
new_user__first_name=username, class_field__access_code=access_code
|
|
111
|
+
).exists():
|
|
112
|
+
student = Student.objects.get(
|
|
113
|
+
new_user__first_name=username,
|
|
114
|
+
class_field__access_code=access_code,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if student.blocked_time is not None:
|
|
118
|
+
if has_user_lockout_expired(student):
|
|
119
|
+
student.blocked_time = None
|
|
120
|
+
student.save()
|
|
121
|
+
else:
|
|
122
|
+
return render(
|
|
123
|
+
self.request,
|
|
124
|
+
"portal/locked_out_school_student.html",
|
|
125
|
+
{"is_teacher": False},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return super(StudentLoginView, self).post(request, *args, **kwargs)
|
|
129
|
+
|
|
97
130
|
|
|
98
131
|
def student_direct_login(request, user_id, login_id):
|
|
99
132
|
"""Direct login for student with unique url without username and password"""
|
|
@@ -108,5 +141,9 @@ def student_direct_login(request, user_id, login_id):
|
|
|
108
141
|
session.save()
|
|
109
142
|
|
|
110
143
|
login(request, user)
|
|
144
|
+
|
|
145
|
+
student.user.is_verified = True
|
|
146
|
+
student.user.save()
|
|
147
|
+
|
|
111
148
|
return HttpResponseRedirect(reverse_lazy("student_details"))
|
|
112
149
|
return HttpResponseRedirect(reverse_lazy("home"))
|
portal/views/login/teacher.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from common.models import Teacher
|
|
2
|
-
from
|
|
3
|
-
from django.shortcuts import redirect, render
|
|
4
|
-
from django.urls import reverse_lazy
|
|
2
|
+
from django.shortcuts import render
|
|
5
3
|
from two_factor.views import LoginView
|
|
6
4
|
from two_factor.forms import AuthenticationTokenForm, BackupTokenForm
|
|
7
5
|
|
|
@@ -23,9 +21,7 @@ class TeacherLoginView(LoginView):
|
|
|
23
21
|
|
|
24
22
|
def get_success_url(self):
|
|
25
23
|
url = self.get_redirect_url()
|
|
26
|
-
return url or redirect_teacher_to_correct_page(
|
|
27
|
-
self.request, self.request.user.userprofile.teacher
|
|
28
|
-
)
|
|
24
|
+
return url or redirect_teacher_to_correct_page(self.request, self.request.user.userprofile.teacher)
|
|
29
25
|
|
|
30
26
|
def post(self, request, *args, **kwargs):
|
|
31
27
|
"""
|