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/tests/test_api.py
CHANGED
|
@@ -3,12 +3,17 @@ from __future__ import absolute_import
|
|
|
3
3
|
from unittest.mock import patch
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
|
-
from common.
|
|
7
|
-
from common.
|
|
6
|
+
from common.models import Class, School, Student, Teacher
|
|
7
|
+
from common.tests.utils.classes import create_class_directly
|
|
8
|
+
from common.tests.utils.organisation import (
|
|
9
|
+
create_organisation_directly,
|
|
10
|
+
join_teacher_to_organisation,
|
|
11
|
+
)
|
|
12
|
+
from common.tests.utils.student import create_school_student_directly
|
|
13
|
+
from common.tests.utils.teacher import signup_teacher_directly
|
|
8
14
|
from common.tests.utils.user import create_user_directly, get_superuser
|
|
9
15
|
from django.contrib.auth.models import User
|
|
10
16
|
from django.urls import reverse
|
|
11
|
-
from django.utils import timezone
|
|
12
17
|
from hamcrest import *
|
|
13
18
|
from hamcrest.core.base_matcher import BaseMatcher
|
|
14
19
|
from rest_framework import status
|
|
@@ -18,7 +23,8 @@ from rest_framework.test import APIClient, APITestCase
|
|
|
18
23
|
class APITests(APITestCase):
|
|
19
24
|
def test_valid_date_registered(self):
|
|
20
25
|
url = reverse(
|
|
21
|
-
"registered-users",
|
|
26
|
+
"registered-users",
|
|
27
|
+
kwargs={"year": "2016", "month": "04", "day": "01"},
|
|
22
28
|
)
|
|
23
29
|
superuser = get_superuser()
|
|
24
30
|
self.client.force_authenticate(user=superuser)
|
|
@@ -28,7 +34,8 @@ class APITests(APITestCase):
|
|
|
28
34
|
|
|
29
35
|
def test_invalid_date_registered(self):
|
|
30
36
|
url = reverse(
|
|
31
|
-
"registered-users",
|
|
37
|
+
"registered-users",
|
|
38
|
+
kwargs={"year": "2016", "month": "05", "day": "35"},
|
|
32
39
|
)
|
|
33
40
|
superuser = get_superuser()
|
|
34
41
|
self.client.force_authenticate(user=superuser)
|
|
@@ -37,7 +44,8 @@ class APITests(APITestCase):
|
|
|
37
44
|
|
|
38
45
|
def test_valid_date_lastconnectedsince(self):
|
|
39
46
|
url = reverse(
|
|
40
|
-
"last-connected-since",
|
|
47
|
+
"last-connected-since",
|
|
48
|
+
kwargs={"year": "2016", "month": "04", "day": "01"},
|
|
41
49
|
)
|
|
42
50
|
superuser = get_superuser()
|
|
43
51
|
self.client.force_authenticate(user=superuser)
|
|
@@ -47,7 +55,8 @@ class APITests(APITestCase):
|
|
|
47
55
|
|
|
48
56
|
def test_invalid_date_lastconnectedsince(self):
|
|
49
57
|
url = reverse(
|
|
50
|
-
"last-connected-since",
|
|
58
|
+
"last-connected-since",
|
|
59
|
+
kwargs={"year": "2016", "month": "05", "day": "35"},
|
|
51
60
|
)
|
|
52
61
|
superuser = get_superuser()
|
|
53
62
|
self.client.force_authenticate(user=superuser)
|
|
@@ -70,10 +79,12 @@ class APITests(APITestCase):
|
|
|
70
79
|
client.force_authenticate(user=superuser)
|
|
71
80
|
url = reverse("inactive_users")
|
|
72
81
|
response = client.get(url)
|
|
73
|
-
|
|
82
|
+
assert len(response.data) == 1
|
|
74
83
|
|
|
75
84
|
@patch("portal.views.api.IS_CLOUD_SCHEDULER_FUNCTION", return_value=True)
|
|
76
|
-
def test_get_inactive_users_if_appengine(
|
|
85
|
+
def test_get_inactive_users_if_appengine(
|
|
86
|
+
self, mock_is_cloud_scheduler_function
|
|
87
|
+
):
|
|
77
88
|
client = APIClient()
|
|
78
89
|
create_user_directly(active=False)
|
|
79
90
|
create_user_directly(active=True)
|
|
@@ -89,10 +100,12 @@ class APITests(APITestCase):
|
|
|
89
100
|
create_user_directly(active=True)
|
|
90
101
|
url = reverse("inactive_users")
|
|
91
102
|
response = client.get(url)
|
|
92
|
-
|
|
103
|
+
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
93
104
|
|
|
94
105
|
@patch("portal.views.api.IS_CLOUD_SCHEDULER_FUNCTION", return_value=True)
|
|
95
|
-
def test_delete_inactive_users_if_appengine(
|
|
106
|
+
def test_delete_inactive_users_if_appengine(
|
|
107
|
+
self, mock_is_cloud_scheduler_function
|
|
108
|
+
):
|
|
96
109
|
client = APIClient()
|
|
97
110
|
create_user_directly(active=False)
|
|
98
111
|
create_user_directly(active=False)
|
|
@@ -100,14 +113,25 @@ class APITests(APITestCase):
|
|
|
100
113
|
response = client.get(url)
|
|
101
114
|
users = response.data
|
|
102
115
|
assert len(users) == 2
|
|
116
|
+
|
|
117
|
+
# NOTE: Migration 0049 causes user 34 (created via migration 0001) to
|
|
118
|
+
# be marked as inactive. Slightly tweaked this test so it still
|
|
119
|
+
# passes but takes into account this new anonymisation.
|
|
120
|
+
old_deleted_users = list(User.objects.filter(is_active=False))
|
|
121
|
+
assert len(old_deleted_users) == 1
|
|
122
|
+
|
|
103
123
|
response = client.delete(url)
|
|
104
124
|
assert mock_is_cloud_scheduler_function.called
|
|
105
125
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
126
|
+
|
|
106
127
|
for user in users:
|
|
107
128
|
with pytest.raises(User.DoesNotExist):
|
|
108
129
|
User.objects.get(username=user["username"])
|
|
130
|
+
|
|
109
131
|
deleted_users = list(User.objects.filter(is_active=False))
|
|
110
|
-
|
|
132
|
+
new_deleted_users_count = len(deleted_users) - len(old_deleted_users)
|
|
133
|
+
assert new_deleted_users_count == 2
|
|
134
|
+
|
|
111
135
|
for user in deleted_users:
|
|
112
136
|
assert user.first_name == "Deleted"
|
|
113
137
|
assert user.last_name == "User"
|
|
@@ -117,91 +141,186 @@ class APITests(APITestCase):
|
|
|
117
141
|
response = client.get(url)
|
|
118
142
|
assert len(response.data) == 0
|
|
119
143
|
|
|
120
|
-
def _create_indy_directly(self, email):
|
|
121
|
-
"""Create an indy in the database."""
|
|
122
|
-
student = Student.objects.independentStudentFactory(
|
|
123
|
-
username="indiana",
|
|
124
|
-
name="some student",
|
|
125
|
-
email=email,
|
|
126
|
-
password="Password1",
|
|
127
|
-
)
|
|
128
|
-
return student
|
|
129
|
-
|
|
130
|
-
def _create_teacher_directly(self, email):
|
|
131
|
-
first_name = "some"
|
|
132
|
-
last_name = "teacher"
|
|
133
|
-
password = "Password1!"
|
|
134
|
-
teacher = Teacher.objects.factory(first_name, last_name, email, password)
|
|
135
|
-
generate_token(teacher.new_user, preverified=True)
|
|
136
|
-
teacher.user.save()
|
|
137
|
-
return teacher
|
|
138
|
-
|
|
139
144
|
@patch("portal.views.api.IS_CLOUD_SCHEDULER_FUNCTION", return_value=True)
|
|
140
|
-
def
|
|
145
|
+
def test_orphan_schools_and_classes_are_anonymised(
|
|
146
|
+
self, mock_is_cloud_scheduler_function
|
|
147
|
+
):
|
|
141
148
|
client = APIClient()
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
teacher
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
# Create a school with an active teacher
|
|
150
|
+
school1_teacher1_email, _ = signup_teacher_directly()
|
|
151
|
+
school1 = create_organisation_directly(school1_teacher1_email)
|
|
152
|
+
klass11, _, access_code11 = create_class_directly(
|
|
153
|
+
school1_teacher1_email
|
|
154
|
+
)
|
|
155
|
+
_, _, student11 = create_school_student_directly(access_code11)
|
|
156
|
+
|
|
157
|
+
# Create a school with one active non-admin teacher and one inactive admin teacher
|
|
158
|
+
school2_teacher1_email, _ = signup_teacher_directly()
|
|
159
|
+
school2_teacher2_email, _ = signup_teacher_directly()
|
|
160
|
+
school2 = create_organisation_directly(school2_teacher1_email)
|
|
161
|
+
join_teacher_to_organisation(
|
|
162
|
+
school2_teacher2_email, school2.name, is_admin=True
|
|
163
|
+
)
|
|
164
|
+
klass21, _, access_code21 = create_class_directly(
|
|
165
|
+
school2_teacher1_email
|
|
166
|
+
)
|
|
167
|
+
_, _, student21 = create_school_student_directly(access_code21)
|
|
168
|
+
klass22, _, access_code22 = create_class_directly(
|
|
169
|
+
school2_teacher2_email
|
|
170
|
+
)
|
|
171
|
+
_, _, student22 = create_school_student_directly(access_code22)
|
|
172
|
+
school2_teacher1 = Teacher.objects.get(
|
|
173
|
+
new_user__email=school2_teacher1_email
|
|
174
|
+
)
|
|
175
|
+
school2_teacher1.is_admin = False
|
|
176
|
+
school2_teacher1.save()
|
|
177
|
+
school2_teacher2 = Teacher.objects.get(
|
|
178
|
+
new_user__email=school2_teacher2_email
|
|
179
|
+
)
|
|
180
|
+
school2_teacher2.new_user.is_active = False
|
|
181
|
+
school2_teacher2.new_user.save()
|
|
182
|
+
|
|
183
|
+
# Create a school with 2 inactive teachers
|
|
184
|
+
school3_teacher1_email, _ = signup_teacher_directly()
|
|
185
|
+
school3_teacher2_email, _ = signup_teacher_directly()
|
|
186
|
+
school3 = create_organisation_directly(school3_teacher1_email)
|
|
187
|
+
join_teacher_to_organisation(school3_teacher2_email, school3.name)
|
|
188
|
+
klass31, _, access_code31 = create_class_directly(
|
|
189
|
+
school3_teacher1_email
|
|
190
|
+
)
|
|
191
|
+
_, _, student31 = create_school_student_directly(access_code31)
|
|
192
|
+
klass32, _, access_code32 = create_class_directly(
|
|
193
|
+
school3_teacher2_email
|
|
194
|
+
)
|
|
195
|
+
_, _, student32 = create_school_student_directly(access_code32)
|
|
196
|
+
school3_teacher1 = Teacher.objects.get(
|
|
197
|
+
new_user__email=school3_teacher1_email
|
|
198
|
+
)
|
|
199
|
+
school3_teacher1.new_user.is_active = False
|
|
200
|
+
school3_teacher1.new_user.save()
|
|
201
|
+
school3_teacher2 = Teacher.objects.get(
|
|
202
|
+
new_user__email=school3_teacher2_email
|
|
203
|
+
)
|
|
204
|
+
school3_teacher2.new_user.is_active = False
|
|
205
|
+
school3_teacher2.new_user.save()
|
|
206
|
+
|
|
207
|
+
# Create a school with no active teachers
|
|
208
|
+
school4_teacher1_email, _ = signup_teacher_directly()
|
|
209
|
+
school4 = create_organisation_directly(school4_teacher1_email)
|
|
210
|
+
school4_teacher1 = Teacher.objects.get(
|
|
211
|
+
new_user__email=school4_teacher1_email
|
|
212
|
+
)
|
|
213
|
+
school4_teacher1.new_user.is_active = False
|
|
214
|
+
school4_teacher1.new_user.save()
|
|
215
|
+
|
|
216
|
+
# Create a school with no teachers
|
|
217
|
+
school5_teacher1_email, _ = signup_teacher_directly()
|
|
218
|
+
school5 = create_organisation_directly(school5_teacher1_email)
|
|
219
|
+
school5_teacher1 = Teacher.objects.get(
|
|
220
|
+
new_user__email=school5_teacher1_email
|
|
221
|
+
)
|
|
222
|
+
school5_teacher1.delete()
|
|
153
223
|
|
|
154
|
-
|
|
155
|
-
|
|
224
|
+
# Call the API
|
|
225
|
+
url = reverse("anonymise_orphan_schools", kwargs={"start_id": 1})
|
|
226
|
+
response = client.get(url)
|
|
156
227
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
157
228
|
|
|
158
|
-
|
|
159
|
-
assert
|
|
160
|
-
|
|
161
|
-
assert
|
|
162
|
-
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
229
|
+
# Check the first school/class/student still exist
|
|
230
|
+
assert School.objects.filter(name=school1.name).exists()
|
|
231
|
+
assert Class.objects.filter(pk=klass11.pk).exists()
|
|
232
|
+
assert Student.objects.filter(pk=student11.pk).exists()
|
|
233
|
+
|
|
234
|
+
# Check the second school exists and its first class/student, but the second ones are anonymised
|
|
235
|
+
assert School.objects.filter(name=school2.name).exists()
|
|
236
|
+
assert Class.objects.filter(pk=klass21.pk).exists()
|
|
237
|
+
assert not Class.objects.filter(pk=klass22.pk).exists()
|
|
238
|
+
assert Student.objects.filter(pk=student21.pk).exists()
|
|
239
|
+
assert not Student.objects.get(pk=student22.pk).new_user.is_active
|
|
240
|
+
# Also check the first teacher is now an admin
|
|
241
|
+
assert Teacher.objects.get(
|
|
242
|
+
new_user__email=school2_teacher1_email
|
|
243
|
+
).is_admin
|
|
244
|
+
|
|
245
|
+
# Check the third school is anonymised together with its classes and students
|
|
246
|
+
assert not School.objects.filter(name=school3.name).exists()
|
|
247
|
+
assert not Class.objects.filter(pk=klass31.pk).exists()
|
|
248
|
+
assert not Class.objects.filter(pk=klass32.pk).exists()
|
|
249
|
+
assert not Student.objects.get(pk=student31.pk).new_user.is_active
|
|
250
|
+
assert not Student.objects.get(pk=student32.pk).new_user.is_active
|
|
251
|
+
|
|
252
|
+
# Check that the fourth school is anonymised
|
|
253
|
+
assert not School.objects.filter(name=school4.name).exists()
|
|
254
|
+
|
|
255
|
+
# Check that the fifth school is anonymised
|
|
256
|
+
assert not School.objects.filter(name=school5.name).exists()
|
|
257
|
+
|
|
258
|
+
def test_remove_fake_accounts(self):
|
|
259
|
+
client = APIClient()
|
|
260
|
+
initial_users_length = len(User.objects.all())
|
|
261
|
+
admin_username = "codeforlife-portal@ocado.com"
|
|
262
|
+
admin_password = "abc123"
|
|
263
|
+
|
|
264
|
+
# First two accounts should be deleted
|
|
265
|
+
# Third account should be omitted because first and last name is different
|
|
266
|
+
# Fourth account should be omitted because it is verified
|
|
267
|
+
random_accounts = [
|
|
268
|
+
{
|
|
269
|
+
"username": "hiya",
|
|
270
|
+
"first_name": "name",
|
|
271
|
+
"last_name": "name",
|
|
272
|
+
"email": "eml@email.email",
|
|
273
|
+
"password": '!QAZXSW"3edc',
|
|
274
|
+
"verified": False,
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"username": "goodbye",
|
|
278
|
+
"first_name": "hello",
|
|
279
|
+
"last_name": "hello",
|
|
280
|
+
"email": "el@email.email",
|
|
281
|
+
"password": '!QAZXSW"3edc',
|
|
282
|
+
"verified": False,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"username": "different",
|
|
286
|
+
"first_name": "nope",
|
|
287
|
+
"last_name": "maybe",
|
|
288
|
+
"email": "eail@email.email",
|
|
289
|
+
"password": '!QAZXSW"3edc',
|
|
290
|
+
"verified": False,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"username": "same",
|
|
294
|
+
"first_name": "lastname",
|
|
295
|
+
"last_name": "lastname",
|
|
296
|
+
"email": "eail@email.email",
|
|
297
|
+
"password": '!QAZXSW"3edc',
|
|
298
|
+
"verified": True,
|
|
299
|
+
},
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
for random_account in random_accounts:
|
|
303
|
+
signup_teacher_directly(
|
|
304
|
+
preverified=random_account["verified"],
|
|
305
|
+
username=random_account["username"],
|
|
306
|
+
email=random_account["email"],
|
|
307
|
+
first_name=random_account["first_name"],
|
|
308
|
+
last_name=random_account["last_name"],
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
assert (
|
|
312
|
+
len(User.objects.all())
|
|
313
|
+
== len(random_accounts) + initial_users_length
|
|
314
|
+
)
|
|
198
315
|
|
|
199
|
-
|
|
316
|
+
client.login(username=admin_username, password=admin_password)
|
|
317
|
+
response = client.get(reverse("remove_fake_accounts"))
|
|
318
|
+
assert response.status_code == 204
|
|
200
319
|
|
|
201
|
-
|
|
202
|
-
assert
|
|
203
|
-
|
|
204
|
-
|
|
320
|
+
# check if after deletion all the users are still there
|
|
321
|
+
assert (
|
|
322
|
+
len(User.objects.all()) == initial_users_length + 2
|
|
323
|
+
) # mentioned in the fake_accounts description
|
|
205
324
|
|
|
206
325
|
|
|
207
326
|
def has_status_code(status_code):
|
|
@@ -216,7 +335,9 @@ class HasStatusCode(BaseMatcher):
|
|
|
216
335
|
return response.status_code == self.status_code
|
|
217
336
|
|
|
218
337
|
def describe_to(self, description):
|
|
219
|
-
description.append_text("has status code ").append_text(
|
|
338
|
+
description.append_text("has status code ").append_text(
|
|
339
|
+
self.status_code
|
|
340
|
+
)
|
|
220
341
|
|
|
221
342
|
def describe_mismatch(self, response, mismatch_description):
|
|
222
343
|
mismatch_description.append_text("had status code ").append_text(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from captcha.fields import ReCaptchaField
|
|
2
|
-
from captcha.widgets import ReCaptchaV2Invisible
|
|
3
1
|
from django import forms
|
|
4
2
|
from django.test import TestCase
|
|
3
|
+
from django_recaptcha.fields import ReCaptchaField
|
|
4
|
+
from django_recaptcha.widgets import ReCaptchaV2Invisible
|
|
5
5
|
|
|
6
6
|
from portal.helpers.captcha import is_captcha_in_form, remove_captcha_from_forms
|
|
7
7
|
|