cfl-common 5.3.0__py3-none-any.whl → 8.9.15__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.
Files changed (92) hide show
  1. cfl_common-8.9.15.dist-info/METADATA +47 -0
  2. cfl_common-8.9.15.dist-info/RECORD +99 -0
  3. {cfl_common-5.3.0.dist-info → cfl_common-8.9.15.dist-info}/WHEEL +1 -1
  4. common/app_settings.py +35 -5
  5. common/csp_config.py +85 -0
  6. common/fixtures/aimmo_characters.json +30 -30
  7. common/fixtures/aimmo_characters2.json +1 -1
  8. common/fixtures/aimmo_characters3.json +35 -0
  9. common/helpers/data_migration_loader.py +3 -4
  10. common/helpers/emails.py +228 -108
  11. common/helpers/generators.py +1 -1
  12. common/helpers/organisation.py +10 -0
  13. common/mail.py +201 -0
  14. common/migrations/0002_emailverification.py +1 -3
  15. common/migrations/0005_add_worksheets.py +2 -13
  16. common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +2 -14
  17. common/migrations/0008_unlock_worksheet_3.py +1 -6
  18. common/migrations/0011_student_login_id.py +3 -3
  19. common/migrations/0012_usersession.py +39 -0
  20. common/migrations/0013_class_school.py +42 -0
  21. common/migrations/0014_login_type.py +29 -0
  22. common/migrations/0015_dailyactivity.py +31 -0
  23. common/migrations/0016_joinreleasestudent.py +42 -0
  24. common/migrations/0017_copy_email_to_username.py +18 -0
  25. common/migrations/0018_update_aimmo_character_image_path.py +15 -0
  26. common/migrations/0019_aimmocharacter_alt.py +16 -0
  27. common/migrations/0020_class_is_active_and_null_access_code.py +23 -0
  28. common/migrations/0021_school_is_active.py +28 -0
  29. common/migrations/0022_school_cleanup.py +29 -0
  30. common/migrations/0023_userprofile_aimmo_badges.py +22 -0
  31. common/migrations/0024_teacher_invited_by.py +25 -0
  32. common/migrations/0025_schoolteacherinvitation.py +47 -0
  33. common/migrations/0026_teacher_remove_join_request.py +22 -0
  34. common/migrations/0027_class_created_by.py +25 -0
  35. common/migrations/0028_coding_club_downloads.py +23 -0
  36. common/migrations/0029_dynamicelement.py +22 -0
  37. common/migrations/0030_add_maintenance_banner.py +25 -0
  38. common/migrations/0031_improve_admin_panel.py +56 -0
  39. common/migrations/0032_dailyactivity_level_control_submits.py +18 -0
  40. common/migrations/0033_password_reset_tracking_fields.py +23 -0
  41. common/migrations/0034_dailyactivity_daily_school_student_lockout_reset.py +18 -0
  42. common/migrations/0035_rename_lockout_fields.py +27 -0
  43. common/migrations/0036_rename_awaiting_email_verification_userprofile_is_verified.py +17 -0
  44. common/migrations/0037_migrate_email_verification.py +21 -0
  45. common/migrations/0038_delete_emailverification.py +16 -0
  46. common/migrations/0039_copy_email_to_username.py +18 -0
  47. common/migrations/0040_school_county.py +18 -0
  48. common/migrations/0041_populate_gb_counties.py +27 -0
  49. common/migrations/0042_totalactivity.py +25 -0
  50. common/migrations/0043_add_total_activity.py +30 -0
  51. common/migrations/0044_update_activity_models.py +33 -0
  52. common/migrations/0045_otp.py +23 -0
  53. common/migrations/0046_alter_school_country.py +19 -0
  54. common/migrations/0047_delete_school_postcode.py +16 -0
  55. common/migrations/0048_unique_school_names.py +42 -0
  56. common/migrations/0049_anonymise_orphan_users.py +29 -0
  57. common/migrations/0050_anonymise_orphan_schools.py +30 -0
  58. common/migrations/0051_verify_returning_users.py +26 -0
  59. common/migrations/0052_add_cse_fields.py +68 -0
  60. common/migrations/0053_clean_class_data.py +24 -0
  61. common/migrations/0054_delete_aimmo_models.py +20 -0
  62. common/migrations/0055_alter_schoolteacherinvitation_token.py +18 -0
  63. common/migrations/0056_set_non_school_teachers_as_non_admins.py +25 -0
  64. common/migrations/0057_teacher_teacher__is_admin.py +19 -0
  65. common/migrations/0058_userprofile_google_refresh_token_and_more.py +24 -0
  66. common/models.py +347 -63
  67. common/permissions.py +20 -8
  68. common/static/common/img/RR_logo.svg +336 -0
  69. common/static/common/img/brain.svg +1 -0
  70. common/templates/common/onetrust_cookies_consent_notice.html +6 -6
  71. common/tests/test_migration_anonymise_orphan_schools.py +30 -0
  72. common/tests/test_migration_anonymise_orphan_users.py +30 -0
  73. common/tests/test_migration_blocked_time.py +3 -11
  74. common/tests/test_migration_remove_teacher_title.py +1 -3
  75. common/tests/test_migration_unique_school_names.py +33 -0
  76. common/tests/test_migration_verify_returning_users.py +59 -0
  77. common/tests/test_models.py +49 -43
  78. common/tests/utils/classes.py +1 -3
  79. common/tests/utils/email.py +11 -49
  80. common/tests/utils/organisation.py +10 -14
  81. common/tests/utils/student.py +14 -67
  82. common/tests/utils/teacher.py +16 -38
  83. common/tests/utils/user.py +1 -3
  84. cfl_common-5.3.0.dist-info/METADATA +0 -20
  85. cfl_common-5.3.0.dist-info/RECORD +0 -48
  86. common/email_messages.py +0 -218
  87. common/fixtures/unlock_worksheet3.json +0 -20
  88. common/fixtures/worksheets.json +0 -98
  89. common/fixtures/worksheets2.json +0 -110
  90. common/tests/test_migration_aimmo_characters.py +0 -31
  91. common/tests/test_migration_worksheets.py +0 -49
  92. {cfl_common-5.3.0.dist-info → cfl_common-8.9.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 511.91 423.62"><defs><style>.d{fill:#f9c5db;}.e{fill:#fff;}.f{fill:#666;}.g{fill:#db5d90;}.h{fill:#afafaf;}.i{fill:#5b5a5b;}.j{opacity:.2;}</style></defs><g id="a"/><g id="b"><path class="d" d="M442.29,190.69c-4.39-18.43-13.61-35.22-22.63-51.72-2.21-4.04-4.38-8.11-6.49-12.2-.07-.13-.15-.37-.24-.62-.09-.56-.22-1.26-.27-1.5-.35-1.67-.71-3.47-.94-5.22,.17,.84,.17-2.04,.07-3.29-.21-2.8-.49-5.71-1.13-8.45-1.27-5.43-4.12-9.99-7.23-14.49-2.25-3.27-4.6-6.48-6.59-9.92-.51-.88-.98-1.79-1.46-2.69-.01-.03-.03-.06-.04-.09-.1-.52-.3-1.36-.42-1.68,0-.08,0-.15,0-.24-.25-7.47-1.39-15.18-3.36-22.4-4.34-15.9-13.85-30.79-29.48-37.38-10.06-4.25-21.31-5.05-31.65-1.16-5.07,1.91-9.75,4.4-14.44,7.09-.67,.38-1.33,.76-2,1.14-.09-.07-.17-.13-.24-.18-.89-.7-1.77-1.4-2.66-2.09-4.9-3.81-9.64-7.47-15.34-10-12.16-5.41-25.49-7.1-38.72-7.13-6-3.36-12.68-5.63-20.27-6.29-19.81-1.71-35.07,9.1-50.05,19.98-1.84,.87-3.65,1.79-5.43,2.76-11.73-2.88-23.23-6.51-35.37-7.64-16.82-1.56-32.38,3.66-45.25,14.38-5.58,4.65-10.45,10.7-14,17.38-11.73-2.99-24.82-.3-33.89,8.54-2.85,2.78-5.27,6.15-6.98,9.75-.74,1.55-1.52,3.11-2.19,4.69-6.99,3.63-13.43,6.54-18.48,13.08-5.54,7.17-8.17,15.32-9.36,24.21-.58,4.3-.78,8.52-.87,12.85-.03,1.77-.06,3.54-.14,5.32-.05,1.17-.2,2.35-.22,3.52,0,.05,0,.07,0,.11-.02,.05-.03,.09-.05,.16-.27,.86-1.49,4.85-.87,3.73-.57,1.18-1.24,2.31-1.87,3.45-2.45,4.43-4.95,9.05-6.21,13.98-2.6,10.17-.92,20.53,1.19,30.62,1.99,9.53,5.06,19.27,9.72,28.06-.18,.57-.32,1.16-.42,1.77-2.48,14.32-4.41,28.45-1.82,42.89,2.62,14.62,11.3,25.55,22.21,35.04,2.12,1.85,4.25,3.69,6.2,5.72,.6,.63,3.12,4.22,1.03,.93,.88,1.38,1.76,2.72,2.57,4.15,3.65,6.37,7.45,12.39,12.6,17.71,5.44,5.63,12.04,12.23,19.71,14.34,1.1,.3,2.19,.49,3.27,.58,1.27,1.04,2.59,2.03,3.93,3,.49,1.1,1.04,2.17,1.63,3.23,2.77,4.92,7.75,7.89,13.06,8.76,.6,.65,1.2,1.31,1.79,1.97,3.76,4.21,7.45,8.48,11.09,12.8,1.53,1.81,3.04,3.64,4.52,5.49,.29,.36,1.27,1.64,1.56,2,.5,.66,.99,1.33,1.48,2,3.11,4.29,6.03,8.73,8.6,13.37,5.42,9.78,8.61,19.81,17.84,26.94,10.02,7.75,22.28,10.02,34.59,11.32,12.11,1.27,25.35,.72,34.91-7.83,21.04-18.81,.59-49.34-14.08-65.08-8.43-9.04-18.2-16.49-28.57-23.08,9.77-2.69,18.56-7.88,24.67-16.9,2.18-3.21,3.76-6.87,4.87-10.57,.23-.78,.41-1.57,.57-2.36,1.78-1.12,3.91-2.13,5.26-3.14,4.02-3.01,7.77-6.12,11.24-9.74,4.61-4.8,6.41-12.13,4.66-18.46,2.6-.17,5.2-.64,7.81-1.01,9.57-1.35,19.14-2.8,28.66-4.47,18.7-3.27,37.29-7.36,55.41-13.08,12.41-3.92,24.3-9.45,36.49-14.02,4.91,.32,9.79-1.21,13.52-4.49,.5-.14,.99-.29,1.49-.42,17.65-4.73,36.7,1.28,53.41-7.59,4.09-2.17,7.51-5.13,10.14-8.66,7.9-3.99,12.07-12.5,9.92-21.51Zm-161.74-110.32c.03-.22,.05-.45,.08-.71,.06,.04,.01,.32-.08,.71Zm-116.35,174.2l-.05-.28c.14,.03,.27,.05,.41,.07-.12,.07-.24,.14-.36,.21Zm217.74-50.6c0,.43,0,.86,.03,1.28-.22,0-.44,.02-.66,.03,.21-.44,.42-.87,.63-1.31ZM313.52,25.5s-.03-.02,0,0h0ZM43.06,70.85c-.27,.26-.42,.24,0,0h0Zm160.22,227.41c-.17,.09-.71,.52-1.66,1.28,.47-.68,1.21-1.3,2.08-1.89-.13,.21-.27,.41-.42,.61Z"/><path class="f" d="M151.43,412.81c-21.81-28.05-42.03-56.46-67.07-81.79l.29-.41c22.02,15.35,39.34,36.47,53.86,58.84,4.84,7.49,9.37,15.17,13.38,23.14l-.45,.22h0Z"/><path class="f" d="M137.94,258.21c17.21,15.87,21.48,40.05,4.62,57.96-8.03,8.83-18.23,15.19-28.93,20.23-1.05,.49-2.13,.96-3.3,1.43-.68,.12-1.32,.3-1.87,.36-30.97,3.76-53.28-17.65-62.97-45.27l2.16,2.23c-.95-.43-1.69-.81-2.51-1.23-14.65-7.92-28.03-20.03-34-35.9-3.7-9.79-3.97-20.93-1.03-30.81l.49,.1c-1.11,5.8-1.49,11.76-.59,17.61,3.02,21.31,20.64,37.47,39.34,46.21l.16,.07,.06,.16c2.39,6.17,5.35,12.12,9.04,17.59,3.64,5.46,8.13,10.45,13.32,14.47,10.28,7.99,24.34,11.99,36.99,9.88,14.78-6.28,30.14-15.16,38.07-29.65,5.83-11.05,4.01-24.66-2.15-35.19-2.02-3.56-4.43-6.94-7.24-9.9l.35-.36h0Z"/><path class="f" d="M27.46,222.17c-8.91-6.16-15.79-14.98-20.61-24.63-15.83-30.94-2.67-67.45,23.43-88.04l.37,.33c-3.06,3.58-7.03,8.52-9.64,12.33-19.27,27.09-22.14,59.5-3.07,87.55,2.88,4.34,6.14,8.46,9.86,12.09l-.33,.37h0Z"/><path class="f" d="M22.29,119.02c-3.79-12.15-4.93-27.75,2.01-39.02,2.02-3.31,5.16-6.1,8.75-7.72,2.72-1.24,5.28-2.55,7.75-4.04,4.52-2.89,8.94-6.32,11.66-10.9,.44-1.01,1.09-1.78,1.7-2.61,7.74-9.88,20.64-13.83,30.48-4.47l-.27,.42c-7.43-4.82-16.18-4.26-22.64,1.9-2.02,1.85-3.81,4.01-5.19,6.35-.5,1.23-1.54,2.88-2.46,4.08-2.88,3.82-6.63,6.9-10.64,9.45-5.14,3.49-11.89,4.55-15.37,10.16-6.63,10.14-6.39,24.65-5.28,36.34l-.49,.07h0Z"/><path class="f" d="M101.06,97.94c-16.06-4.22-25.06-17.92-19.42-34.18,3.35-9.68,8.2-19.07,14.86-26.9,7.87-9.22,18.31-16.7,30.21-19.57,7.99-1.66,16.54-.12,23.9,2.21,5.9,1.98,11.57,4.42,16.77,7.71l-.21,.45c-5.53-2.37-11.31-4.21-17.17-5.54-5.85-1.35-11.9-2.06-17.86-1.54-19.17,2.75-34.69,19.81-42.92,36.44-1.4,2.91-3.37,7.3-4.37,10.37-2.09,6.36-1.5,13.63,2.44,19.16,3.38,4.92,8.47,8.6,13.93,10.92l-.17,.47h0Z"/><path class="f" d="M222.86,8.58c-20.62-.37-39.77,10.41-49.37,28.44-4.75,8.27-7.95,17.74-6.82,27.2,1.23,9.55,6.94,18.16,14.57,23.91,4.25,4.33,10.56,7.19,16.69,6.2,.72-.1,2.24-.35,2.97-.45,3.74,5.24,4.49,13.15-.44,17.83-.56,.59-1.23,1.06-1.82,1.58l-.34-.37c.51-.57,1.08-1.09,1.54-1.7,3.55-4.25,2.88-10.56-.25-14.86l.33,.11c-7.4,2.48-16.1-.49-21.36-5.5-13.67-9.24-20.29-25.92-15.53-41.88,4.54-15.12,14.77-28.97,29.08-36.04,9.5-4.65,20.36-6.44,30.84-4.94l-.09,.49h0Z"/><path class="f" d="M208.82,29.72c3.06-6.17,8.58-11.41,15.39-13.1,2.3-.67,4.72-.73,7.11-.68l-.77,.11c16.73-7,45.75-16.61,61.69-4.02,7.33,5.02,15.97,7.39,23.17,12.95,4.68,3.47,8.18,8.46,10.46,13.78,1.22,3.08,2.03,5.92,2.74,9.13,.71,3.17,1.3,7.92,1.74,11.16,.83,5.5,1.47,11.05,2.98,16.39,.04,.11,.06,.12,.08,.18l.03,.08v.04l.12,.23c.58,1.26,.96,2.72,1.27,3.99,1.87,8.86,3.6,21.66-2.41,29.43-1.54,1.93-4.03,3.42-6.55,3.93-.42,.12-.86,.19-1.3,.35-16.56,5.08-47.28,23.42-63.2,11.85-6.73-4.79-10.14-12.9-11.28-20.77-.78-6.24-.79-12.78,2.13-18.55l2.06,2.61c-2.85,.53-5.61,.37-8.39-.26-8.19-1.76-13.79-8.51-16.56-16.05l.46-.2c.8,1.65,1.73,3.49,2.78,5.01,3.07,4.79,7.98,8.53,13.64,9.53,2.69,.52,5.51,.46,8.14-.19-1.47,2.7-1.91,5.86-2.05,8.95-.25,8.56,1.87,17.79,7.81,24.18,3.98,4.42,9.75,6.23,15.6,5.58,11.62-1.15,22.5-6.08,33.19-10.54,5.41-2.24,10.71-4.85,16.43-6.3,1.33-.34,2.51-1.12,3.38-2.19,4.89-6.41,2.84-20.94,.33-28.21-2.14-5.93-2.56-12.25-3.53-18.42-.41-3.14-.94-7.72-1.61-10.81-1.55-7.71-4.88-15.51-11.3-20.37-4.54-3.65-10.02-5.9-15.15-8.62-3.91-1.96-7.52-4.61-11.12-7.04-11.31-6.02-26.41-3-38.37-.24-5.68,1.43-11.3,3.16-16.76,5.24-2.22-.14-4.47-.2-6.64,.34-6.51,1.35-11.99,6.1-15.28,11.76l-.44-.23h0Z"/><path class="f" d="M311.48,27.06c7.57-7.87,18.84-16.99,30.49-16.05,19.31,2.51,43.75,14.12,48.92,34.68,2.45,10.77,3.61,23.1-1.2,33.42l.56-2.4c.1,1.11,.37,2.25,.78,3.27,.7,1.67,1.64,2.83,3.15,3.59,2.07,.77,4.18,1.3,6.27,2.21,10.93,4.24,17.68,15.08,15.89,26.8-1.25,10.43-9.13,19.74-18.77,22.96l-.21-.45c2.92-1.43,5.63-3.28,7.95-5.55,9.36-8.81,11.67-25.51,1.81-34.8-2.35-2.21-5.2-3.83-8.23-4.98-3.02-1.32-6.58-1.49-9.14-3.79-2.58-2.27-3.91-5.6-4.27-8.97-.02-.13,0-.14,.06-.25,3.97-9.4,2.6-20.19,.56-29.93-3.84-15.21-18.86-24.21-32.65-29.45-4.77-1.63-9.71-3.37-14.79-3.43-5.09,.17-9.93,2.26-14.35,4.67-4.45,2.49-8.65,5.48-12.5,8.81l-.34-.37h0Z"/><path class="f" d="M359.05,101.57c13.08,1.53,24.07,11.47,26.11,24.71,.67,3.96,.78,8.16,1.35,12.13,1.07,7.82,2.37,15.62,4.23,23.29,1.25,6.09,3.42,11.85,4.34,17.9,2.43,14.92-1.95,31.82-15.54,40.1-1.26,.74-2.86,1.69-4.15,2.37-11.26,5.92-28.2,16.39-39.67,21.84-3.69,1.7-7.55,3.38-11.68,3.84l-.09-.49c3.87-.87,7.44-2.86,10.92-4.83,13.81-8.27,26.97-17.62,40.96-25.61,10.81-6.29,15.41-18.47,14.87-30.57-.02-8.12-3.05-15.71-4.56-23.57-2.06-9.87-3.46-19.9-3.93-29.97,.25-15.5-8.16-26.76-23.25-30.64l.09-.49h0Z"/><path class="f" d="M408.01,132.23c6.68-3.19,14.64-.21,18.26,6.23,1.17,1.85,2.09,4.26,2.89,6.27,1.48,3.8,2.98,8.11,4.41,11.95,2.12,6.22,4.76,13.86,6.71,20.1,1.85,5.6,3.4,11.49,3.76,17.45,.28,4.71,.02,10.32-2.47,14.72-2.31,3.88-4.61,7.65-7.83,11.03-8.16,9.08-21.38,12.19-33.04,10.72l-.02-.5c8.38-.47,16.91-2.31,23.87-7.15,6.1-4.2,10.11-10.66,13.43-17.15,2.27-6.27,1.15-13.21-.15-19.59-2.42-9.3-6.87-23.16-9.55-32.51-1.22-3.7-2.83-10.58-4.58-14.01-2.71-5.92-9.27-8.91-15.53-7.09l-.16-.47h0Z"/><path class="f" d="M221.65,273.51c2.26,9.89-2.86,20.36-10.84,26.3-8.02,5.94-18.74,7.75-28.08,4.56l-.02-.5c2.02-.15,4.73-.37,6.68-.71,13.4-1.78,25.65-10.16,30.09-23.24,.76-2.09,1.38-4.26,1.67-6.45l.5,.04h0Z"/><path class="f" d="M200.07,300.72c3.54,4.74,5.27,12.19,.36,16.88-1.33,1.29-2.95,2.41-4.44,3.5-8.96,6.27-19.78,11.25-31.01,10.18-8.27-.2-18.35-1.37-22.86-9.37-1.38-2.44-2.16-5.13-2.46-7.88l.47-.17c2.37,6.69,7.42,11.59,14.64,12.33,3.36,.49,6.8,.35,10.25,.37,3.47,.14,6.78-.08,10.12-.96,7.46-1.85,14.5-5.27,21.05-9.25,.7-.44,1.46-.88,2.1-1.42,4.09-3.36,2.96-9.61,1.32-14l.46-.2h0Z"/><path class="f" d="M176.12,326.86c24.94,11.54,50.15,41.47,49.33,70.03l-.49,.09c-4.51-21.44-17.93-39.76-32.96-55.19-5.11-5.13-10.46-10.07-16.17-14.53l.3-.4h0Z"/><path class="f" d="M137.61,234.31c-12.87-5.26-28.97-12.85-41.88-7.04-1.34,.62-3.31,1.96-4.59,2.75-11.69,7.35-22.74,15.2-34.81,22.2l-.34-.37c5.77-5.42,11.8-10.57,18.04-15.44,4.67-3.67,9.46-7.19,14.4-10.49,1.66-1.08,3.28-2.22,5.1-3.14,3.65-1.77,7.74-2.47,11.74-2.33,12.02,.6,23.14,6.48,32.65,13.46l-.31,.4h0Z"/><path class="f" d="M90.49,123.12c6.33,13.05,6.79,31.3-5.1,41.5-10.92,8.88-24.57,16.5-39.18,15.65-4.18-.4-8.44-1.75-11.45-4.52l.24-.44c6.92,3.68,15.23,2.4,22.36-.05,7.28-2.54,14.04-6.47,20.3-10.98,3.08-2.25,6.3-4.46,8.52-7.63,4.53-6.27,5.59-14.37,5.32-21.95-.14-3.85-.61-7.72-1.49-11.46l.48-.13h0Z"/><path class="f" d="M76.79,174.22c3.7,1.29,6.73,4.02,9.14,7.02,7.21,9.2,8.66,21.88,5.65,32.96l-.13-1.32c.12,.93,.23,1.85,.32,2.79,.25,2.86,.4,5.72,.17,8.61l-.48,.15c-1.04-3.12-2.49-7.38-3.46-10.47,1.28-13.81-1.06-29.39-11.54-39.35l.31-.39h0Z"/><path class="f" d="M161.1,151.64c18.17-8.77,42.86,4.98,40,26.29l-.49,.11c-1.08-4.19-2.72-8.24-5.15-11.81-6.15-9.08-17.16-13.47-27.83-14.07-2.16-.15-4.34-.19-6.48-.02l-.06-.5h0Z"/><path class="f" d="M195.26,197.53c9.79-9.84,24.22-14.13,37.94-13.48,1.11,.1,3.32,.32,4.42,.46,7.16,1.13,14.35,2.25,21.07,5.18,1.06,.51,2.08,1.12,2.81,2.1,1.44,1.78,1.05,4.71-.91,5.97l-4.67-6.12c4.01-2.82,8.54-4.73,13.23-6.02,4.5-1.22,9.21-1.75,13.86-1.45l.08,.49c-6.59,1.14-13.01,3.31-18.85,6.56-1.93,1.09-3.84,2.29-5.53,3.67l-.52-.68c-.19,.12-.03,.28-.13,.16-.82-.7-2-.99-3.03-1.34-3.93-1.17-8-1.9-12.05-2.61-5.43-.94-10.83-1.83-16.31-1.67-10.9,.26-21.85,3.41-31.11,9.19l-.29-.41h0Z"/><path class="f" d="M280.33,149.53c21.07,10.79,41.84,14.23,63.49,3.14,4.86-2.38,9.69-5.07,14.86-7l.29,.41-8.8,6.36c-8.7,6.56-19.03,11.36-30.03,11.98-14.62,.82-29.2-4.98-40.14-14.52l.34-.37h0Z"/><path class="f" d="M181.26,213.67c9.93,6.09,14.18,19.54,8.98,30.15-2.37,5.24-6.32,9.39-10.53,13.13-.7,.55-2.55,1.99-3.24,2.52-.76,.57-2.62,1.68-3.41,2.24-8.4,4.91-18.22,8-27.95,7.45l-.08-.49c7.4-1.54,14.6-4.36,21.17-8.09,2.82-1.6,5.82-3.63,8.32-5.7,6.91-5.72,13.87-13.25,13.91-22.75,.06-6.69-3.05-13.21-7.53-18.09l.35-.36h0Z"/><path class="f" d="M167.22,124.02c16.67-8.3,39.6-10.49,56.5-1.46,1.18,.63,2.62,1.58,3.71,2.33,6.81,5.05,13.4,10.22,19.4,16.43l-.27,.42-9.05-5.51c-3.22-1.95-10.39-6.51-13.52-8.33-1.47-.86-3.13-1.66-4.7-2.3-13.13-5.17-27.76-4.86-41.56-2.99-3.49,.48-6.97,1.08-10.39,1.89l-.13-.48h0Z"/><path class="g" d="M340.25,209.52c-25.34,51.85-74.22,96.17-129.16,52.06-5.91-4.68-11.34-9.88-16.4-15.37,16.15,14.34,36.02,27.42,58.32,26.95,29.45-1,49.96-26.56,62.8-50.65,3.26-6.21,6.29-12.71,8.64-19.29,1.48-4.46,6.3-6.88,10.76-5.4,4.81,1.56,7.2,7.13,5.03,11.69h0Z"/><path class="g" d="M450.9,222.82c-3.86,14.01-12.5,26.41-23.37,35.88-8.87,7.57-19.71,13.2-30.75,16.3-38.57,10.56-80.42-1.74-116.27-16.32,22.35,6.65,45.15,11.56,68.41,12.15,1.99,0,6.67-.07,8.57-.09,1.81-.07,6.59-.45,8.5-.58l5.59-.75c17.07-2.47,33.77-9.13,45.66-21.69,7.23-7.71,12.77-17.29,14.99-27.65,.57-5.45,5.76-9.39,11.19-8.31,5.12,.99,8.48,5.94,7.49,11.06h0Z"/><g><path class="e" d="M328.57,220.35c-.65,0-1.3-.03-1.94-.08-16.21-1.28-28.15-18.25-26.61-37.84,1.47-18.67,14.76-33.29,30.26-33.29,.65,0,1.3,.03,1.94,.08,16.21,1.28,28.15,18.25,26.61,37.84-1.47,18.67-14.76,33.29-30.26,33.29Z"/><path class="h" d="M330.28,147.64v3h0c.6,0,1.22,.02,1.82,.07,15.39,1.21,26.71,17.46,25.23,36.23-1.41,17.89-14.04,31.91-28.76,31.91-.61,0-1.22-.02-1.83-.07-15.39-1.21-26.71-17.46-25.23-36.23,1.41-17.89,14.04-31.91,28.76-31.91v-3m0,0c-16.21,0-30.21,15.05-31.75,34.67-1.61,20.45,10.92,38.11,27.98,39.46,.69,.05,1.38,.08,2.06,.08,16.21,0,30.21-15.05,31.75-34.67,1.61-20.45-10.92-38.11-27.98-39.46-.69-.05-1.38-.08-2.06-.08h0Z"/></g><ellipse class="i" cx="334.82" cy="199.39" rx="13.2" ry="9.39" transform="translate(39 453.06) rotate(-71.39)"/><g><path class="e" d="M446.3,231.11c-1.21,0-2.43-.1-3.63-.29-15.09-2.39-25.01-19.17-22.12-37.41,2.62-16.55,15.08-29.03,28.97-29.03,1.21,0,2.43,.1,3.63,.29,15.09,2.39,25.01,19.17,22.12,37.41-2.62,16.55-15.08,29.03-28.97,29.03Z"/><path class="h" d="M449.52,165.88h0c1.13,0,2.27,.09,3.39,.27,14.27,2.26,23.63,18.27,20.87,35.7-2.51,15.83-14.33,27.77-27.49,27.77-1.13,0-2.27-.09-3.39-.27-14.27-2.26-23.63-18.27-20.87-35.7,2.51-15.83,14.33-27.76,27.49-27.77m0-3c-14.48,0-27.67,12.76-30.45,30.3-3.02,19.09,7.44,36.61,23.37,39.13,1.29,.2,2.58,.3,3.86,.3,14.48,0,27.67-12.76,30.45-30.3,3.02-19.09-7.44-36.61-23.37-39.13-1.29-.2-2.58-.3-3.86-.3h0Z"/></g><ellipse class="i" cx="446.51" cy="209.6" rx="12.44" ry="8.84" transform="translate(78.46 537.99) rotate(-66.89)"/></g><g id="c"><g class="j"><polygon class="e" points="329.42 118.98 367.62 124.71 301.63 213.52 283.04 209.6 267.19 191.08 329.42 118.98"/></g><g class="j"><polygon class="e" points="265.79 162.67 265.79 110.28 280.5 109.04 306.96 113.1 265.79 162.67"/></g><g class="j"><polygon class="e" points="469.91 148.92 504.3 154.03 444.89 233.05 428.16 229.56 413.89 213.08 469.91 148.92"/></g><g><path d="M510.06,159.47c-1.72-6.02-6.83-10.58-13.04-11.65l-69.11-14.88c-.07-.01-.14-.03-.21-.04-.22-.04-.87-.14-1.8-.2,.43-.03,.68-.02,.68-.02l-63.81-15.39s.09,.05,.14,.08c-.13-.02-.26-.06-.39-.08l-81.21-13.19c-.51-.08-3-.45-6.09-.21-.4-.09-.8-.14-1.22-.12-3.73,.15-92.16,3.94-158.51,31.15-1.6,.65-2.74,2.09-3.03,3.79-.17,.98-3.97,24.18,5.96,42.8,2.22,4.17,5,7.83,8.25,10.88,.7,.66,1.58,1.1,2.53,1.28,1.15,.21,2.92,.46,4.89,.46,3.22,0,6.94-.67,9.21-3.26,1.08-1.23,2.68-3.9,1.36-7.97-.85-2.63-3.66-4.03-6.29-3.18-1.97,.64-3.27,2.41-3.43,4.37-.72,.04-1.62,.02-2.51-.06-1.99-2.08-3.73-4.5-5.18-7.22-6.52-12.22-5.86-27.59-5.26-33.71,49.43-19.59,112.16-26.35,139.12-28.46-.19,.94-.32,1.95-.32,3.07v66.73c0,.12,0,.24,.01,.36,.06,.88,1.75,21.62,20.3,29.43,11.43,4.81,43.5,6.74,68.38,7.52,.28,0,.55,.01,.82,.01,11.7,0,21.98-7.74,25.19-19.07l.23-.84c3.05-11.22,3.54-40.64,3.62-51.05,1.2-3.01,3.1-4.63,5.93-6.52,7.68-5.14,18.12-2.75,22.92,5.14,.42,.69,.82,1.4,1.21,2.11v53.78c0,.1,0,.21,.01,.31,.06,.79,1.55,19.46,18.05,26.51,10.37,4.43,37.17,6.13,57.82,6.78,.25,0,.49,.01,.74,.01,10.42,0,19.58-6.98,22.43-17.2l.2-.74c3.21-11.98,3.23-47.01,3.23-49.25,.02-.85,.04-5.71-1.84-12.26Zm-140.69-12.46s0,.09,0,.13c.02,11.06-.54,41.98-3.28,52.06l-.21,.75c-2.03,7.17-8.61,12.03-16.08,11.78-33.48-1.04-57.11-3.49-64.81-6.74-12.31-5.19-14.03-19.14-14.19-20.79V117.69c0-1.77,.62-2.3,.88-2.52,1.65-1.41,5.77-1.54,8.03-1.19l81.22,13.19c3.23,.53,5.9,2.83,6.78,5.87,1.83,6.29,1.67,13.9,1.67,13.97Zm133.58,24.48c0,.06,0,.12,0,.19,.01,9.98-.49,37.88-2.93,46.98l-.18,.67c-1.8,6.47-7.66,10.83-14.26,10.63-27.7-.87-47.6-3.08-54.59-6.07-10.96-4.68-12.48-17.3-12.62-18.78v-59.99c0-1.64,.57-2.12,.81-2.33,1.44-1.23,4.99-1.36,6.97-1.06l69.08,14.87c.07,.02,.14,.03,.21,.04,2.86,.47,5.22,2.55,6.01,5.3,1.6,5.58,1.49,9.53,1.49,9.55Z"/><path d="M431.58,118.93c2.88-2.73,6.23-4.98,9.71-6.86,10.9-6.05,23.5-5.13,33.14,2.88,1.88,2.01,4.26,4.78,6,7.27,1.74,2.53,3.65,5.26,4.1,8.34,.03,.36,.57,.4,.66,.06,1.36-5.31-1.24-10.84-3.68-15.43-1-1.64-1.88-3.43-3.32-4.86-3.2-3.28-7.29-6.04-11.99-7.63-13.51-4.78-29.72,2.7-35.13,15.74l-.04,.11c-.15,.32,.31,.63,.55,.37Z"/><path d="M297.47,90.2c2.88-2.73,6.23-4.98,9.71-6.86,10.9-6.05,23.5-5.13,33.14,2.88,1.88,2.01,4.26,4.78,6,7.27,1.74,2.53,3.65,5.26,4.1,8.34,.03,.36,.57,.4,.66,.06,1.36-5.31-1.24-10.84-3.68-15.43-1-1.64-1.88-3.43-3.32-4.86-3.2-3.28-7.29-6.04-11.99-7.63-13.51-4.78-29.72,2.7-35.13,15.74l-.04,.11c-.15,.32,.31,.63,.55,.37Z"/></g><path class="d" d="M246.7,141.54s-45.89-33.79-87.25-15.71c-41.36,18.08-12.08,29.13-12.08,29.13l39-19.62,60.33,6.21Z"/></g></svg>
@@ -1,15 +1,15 @@
1
1
  <!-- OneTrust Cookies Consent Notice start for codeforlife.education -->
2
2
  {% if module_name == "default" %}
3
- <script type="text/javascript" src="https://cdn-ukwest.onetrust.com/consent/26fc0b44-90c4-44e3-aaa7-335ac3d03d13/OtAutoBlock.js" ></script>
4
- <script src="https://cdn-ukwest.onetrust.com/scripttemplates/otSDKStub.js" type="text/javascript" charset="UTF-8" data-domain-script="26fc0b44-90c4-44e3-aaa7-335ac3d03d13" ></script>
3
+ <script type="text/javascript" src="https://cdn-ukwest.onetrust.com/consent/5da42396-cb12-4493-8d04-5179033cfbad/OtAutoBlock.js" ></script>
4
+ <script src="https://cdn-ukwest.onetrust.com/scripttemplates/otSDKStub.js" type="text/javascript" charset="UTF-8" data-domain-script="5da42396-cb12-4493-8d04-5179033cfbad" ></script>
5
5
  <script type="text/javascript">
6
- function OptanonWrapper() { }
6
+ function OptanonWrapper() { }
7
7
  </script>
8
8
  {% elif cookie_management_enabled %}
9
- <script type="text/javascript" src="https://cdn-ukwest.onetrust.com/consent/26fc0b44-90c4-44e3-aaa7-335ac3d03d13-test/OtAutoBlock.js" ></script>
10
- <script src="https://cdn-ukwest.onetrust.com/scripttemplates/otSDKStub.js" type="text/javascript" charset="UTF-8" data-domain-script="26fc0b44-90c4-44e3-aaa7-335ac3d03d13-test" ></script>
9
+ <script type="text/javascript" src="https://cdn-ukwest.onetrust.com/consent/5da42396-cb12-4493-8d04-5179033cfbad-test/OtAutoBlock.js" ></script>
10
+ <script src="https://cdn-ukwest.onetrust.com/scripttemplates/otSDKStub.js" type="text/javascript" charset="UTF-8" data-domain-script="5da42396-cb12-4493-8d04-5179033cfbad-test" ></script>
11
11
  <script type="text/javascript">
12
- function OptanonWrapper() { }
12
+ function OptanonWrapper() { }
13
13
  </script>
14
14
  {% endif %}
15
15
  <!-- OneTrust Cookies Consent Notice end for codeforlife.education -->
@@ -0,0 +1,30 @@
1
+ import pytest
2
+ from django_test_migrations.migrator import Migrator
3
+
4
+
5
+ @pytest.mark.django_db
6
+ def test_migration_anonymise_orphan_schools(migrator: Migrator):
7
+ state = migrator.apply_initial_migration(
8
+ ("common", "0049_anonymise_orphan_users")
9
+ )
10
+ User = state.apps.get_model("auth", "User")
11
+ UserProfile = state.apps.get_model("common", "UserProfile")
12
+ Teacher = state.apps.get_model("common", "Teacher")
13
+ School = state.apps.get_model("common", "School")
14
+
15
+ orphan_school = School.objects.create(name="OrphanSchool")
16
+ teacher_school = School.objects.create(name="TeacherSchool")
17
+
18
+ teacher_user = User.objects.create_user("TeacherUser", password="password")
19
+ teacher_userprofile = UserProfile.objects.create(user=teacher_user)
20
+ Teacher.objects.create(
21
+ user=teacher_userprofile, new_user=teacher_user, school=teacher_school
22
+ )
23
+
24
+ migrator.apply_tested_migration(("common", "0050_anonymise_orphan_schools"))
25
+
26
+ def assert_school_anonymised(pk: int, anonymised: bool):
27
+ assert School.objects.get(pk=pk).is_active != anonymised
28
+
29
+ assert_school_anonymised(orphan_school.pk, True)
30
+ assert_school_anonymised(teacher_school.pk, False)
@@ -0,0 +1,30 @@
1
+ import pytest
2
+ from django_test_migrations.migrator import Migrator
3
+
4
+
5
+ @pytest.mark.django_db
6
+ def test_migration_anonymise_orphan_users(migrator: Migrator):
7
+ state = migrator.apply_initial_migration(
8
+ ("common", "0048_unique_school_names")
9
+ )
10
+ User = state.apps.get_model("auth", "User")
11
+ UserProfile = state.apps.get_model("common", "UserProfile")
12
+ Teacher = state.apps.get_model("common", "Teacher")
13
+ Student = state.apps.get_model("common", "Student")
14
+
15
+ orphan_user = User.objects.create_user("OrphanUser", password="password")
16
+ teacher_user = User.objects.create_user("TeacherUser", password="password")
17
+ student_user = User.objects.create_user("StudentUser", password="password")
18
+ teacher_userprofile = UserProfile.objects.create(user=teacher_user)
19
+ student_userprofile = UserProfile.objects.create(user=student_user)
20
+ Teacher.objects.create(user=teacher_userprofile, new_user=teacher_user)
21
+ Student.objects.create(user=student_userprofile, new_user=student_user)
22
+
23
+ migrator.apply_tested_migration(("common", "0049_anonymise_orphan_users"))
24
+
25
+ def assert_user_anonymised(pk: int, anonymised: bool):
26
+ assert User.objects.get(pk=pk).is_active != anonymised
27
+
28
+ assert_user_anonymised(orphan_user.pk, True)
29
+ assert_user_anonymised(teacher_user.pk, False)
30
+ assert_user_anonymised(student_user.pk, False)
@@ -4,20 +4,12 @@ import pytest
4
4
  @pytest.mark.django_db
5
5
  def test_blocked_time_added(migrator):
6
6
  migrator.apply_initial_migration(("common", "0008_unlock_worksheet_3"))
7
- new_state = migrator.apply_tested_migration(
8
- ("common", "0009_add_blocked_time_to_teacher_and_student")
9
- )
7
+ new_state = migrator.apply_tested_migration(("common", "0009_add_blocked_time_to_teacher_and_student"))
10
8
 
11
9
  teacher_model = new_state.apps.get_model("common", "Teacher")
12
10
 
13
- assert (
14
- teacher_model._meta.get_field("blocked_time").get_internal_type()
15
- == "DateTimeField"
16
- )
11
+ assert teacher_model._meta.get_field("blocked_time").get_internal_type() == "DateTimeField"
17
12
 
18
13
  student_model = new_state.apps.get_model("common", "Student")
19
14
 
20
- assert (
21
- student_model._meta.get_field("blocked_time").get_internal_type()
22
- == "DateTimeField"
23
- )
15
+ assert student_model._meta.get_field("blocked_time").get_internal_type() == "DateTimeField"
@@ -4,9 +4,7 @@ from django.db.models.query import QuerySet
4
4
 
5
5
  @pytest.mark.django_db
6
6
  def test_teacher_title_removed(migrator):
7
- old_state = migrator.apply_initial_migration(
8
- ("common", "0009_add_blocked_time_to_teacher_and_student")
9
- )
7
+ old_state = migrator.apply_initial_migration(("common", "0009_add_blocked_time_to_teacher_and_student"))
10
8
  Teacher = old_state.apps.get_model("common", "Teacher")
11
9
  assert hasattr(Teacher, "title")
12
10
 
@@ -0,0 +1,33 @@
1
+ import pytest
2
+ from django_test_migrations.migrator import Migrator
3
+
4
+
5
+ @pytest.mark.django_db
6
+ def test_migration_unique_school_names(migrator: Migrator):
7
+ state = migrator.apply_initial_migration(
8
+ ("common", "0047_delete_school_postcode")
9
+ )
10
+ School = state.apps.get_model("common", "School")
11
+
12
+ school_name = "ExampleSchool"
13
+ School.objects.bulk_create(
14
+ [
15
+ School(name=school_name),
16
+ School(name=school_name),
17
+ School(name=f"{school_name} 1"),
18
+ ]
19
+ )
20
+ school_ids = list(
21
+ School.objects.order_by("-id")[:3].values_list("id", flat=True)
22
+ )
23
+ school_ids.reverse()
24
+
25
+ migrator.apply_tested_migration(("common", "0048_unique_school_names"))
26
+ School = state.apps.get_model("common", "School")
27
+
28
+ def assert_school_name(index: int, name: str):
29
+ assert School.objects.get(id=school_ids[index]).name == name
30
+
31
+ assert_school_name(0, school_name)
32
+ assert_school_name(1, f"{school_name} 2")
33
+ assert_school_name(2, f"{school_name} 1")
@@ -0,0 +1,59 @@
1
+ from datetime import datetime, timezone
2
+
3
+ import pytest
4
+ from django_test_migrations.migrator import Migrator
5
+
6
+ from portal.views.api import __anonymise_user
7
+
8
+
9
+ @pytest.mark.django_db
10
+ def test_migration_verify_returning_users(migrator: Migrator):
11
+ state = migrator.apply_initial_migration(
12
+ ("common", "0050_anonymise_orphan_schools")
13
+ )
14
+ User = state.apps.get_model("auth", "User")
15
+ UserProfile = state.apps.get_model("common", "UserProfile")
16
+
17
+ returning_user = User.objects.create_user(
18
+ "ReturningUser",
19
+ password="password",
20
+ last_login=datetime.now(tz=timezone.utc),
21
+ )
22
+ returning_userprofile = UserProfile.objects.create(user=returning_user)
23
+
24
+ non_returning_user = User.objects.create_user(
25
+ "NonReturningUser", password="password"
26
+ )
27
+ non_returning_userprofile = UserProfile.objects.create(
28
+ user=non_returning_user
29
+ )
30
+
31
+ anonymised_returning_user = User.objects.create_user(
32
+ "AnonReturningUser",
33
+ password="password",
34
+ last_login=datetime.now(tz=timezone.utc),
35
+ )
36
+ anonymised_returning_userprofile = UserProfile.objects.create(
37
+ user=anonymised_returning_user
38
+ )
39
+ __anonymise_user(anonymised_returning_user)
40
+
41
+ anonymised_non_returning_user = User.objects.create_user(
42
+ "AnonNonReturningUser", password="password"
43
+ )
44
+ anonymised_non_returning_userprofile = UserProfile.objects.create(
45
+ user=anonymised_non_returning_user
46
+ )
47
+ __anonymise_user(anonymised_non_returning_user)
48
+
49
+ migrator.apply_tested_migration(("common", "0051_verify_returning_users"))
50
+
51
+ def assert_userprofile_is_verified(pk: int, verified: bool):
52
+ assert UserProfile.objects.get(pk=pk).is_verified == verified
53
+
54
+ assert_userprofile_is_verified(returning_userprofile.pk, True)
55
+ assert_userprofile_is_verified(non_returning_userprofile.pk, False)
56
+ assert_userprofile_is_verified(anonymised_returning_userprofile.pk, True)
57
+ assert_userprofile_is_verified(
58
+ anonymised_non_returning_userprofile.pk, False
59
+ )
@@ -1,8 +1,10 @@
1
- from common.models import Student, Teacher, School
1
+ from common.models import DailyActivity, Student, Teacher
2
2
  from django.test import TestCase
3
+ from django.utils import timezone
3
4
 
5
+ from ..helpers.organisation import sanitise_uk_postcode
4
6
  from .utils.classes import create_class_directly
5
- from .utils.organisation import create_organisation_directly
7
+ from .utils.organisation import create_organisation_directly, join_teacher_to_organisation
6
8
  from .utils.student import create_independent_student_directly
7
9
  from .utils.teacher import signup_teacher_directly
8
10
 
@@ -15,7 +17,7 @@ class TestModels(TestCase):
15
17
  Then the student's pending class request field is set to null.
16
18
  """
17
19
  teacher_email, _ = signup_teacher_directly()
18
- school_name, _ = create_organisation_directly(teacher_email)
20
+ create_organisation_directly(teacher_email)
19
21
  class_name = "Test Class"
20
22
  klass, _, _ = create_class_directly(teacher_email, class_name)
21
23
 
@@ -27,55 +29,59 @@ class TestModels(TestCase):
27
29
  indep_student.pending_class_request = klass
28
30
  indep_student.save()
29
31
 
30
- klass.delete()
32
+ klass.anonymise()
31
33
 
32
34
  indep_student = Student.objects.get(new_user__username=username)
33
35
 
34
36
  assert indep_student.pending_class_request is None
35
37
 
36
- def test_teacher_school_on_delete(self):
37
- """
38
- Given a school and a teacher in that school,
39
- When the school is deleted,
40
- Then the teacher's school field is set to null.
41
- """
38
+ def test_creation_time(self):
42
39
  teacher_email, _ = signup_teacher_directly()
43
- school_name, _ = create_organisation_directly(teacher_email)
44
-
45
- teacher = Teacher.objects.get(new_user__email=teacher_email)
46
- school = School.objects.get(name=school_name)
47
40
 
48
- assert teacher.school == school
41
+ sometime = timezone.now() # mark time before the school creation
42
+ school = create_organisation_directly(teacher_email)
49
43
 
50
- school.delete()
51
- teacher = Teacher.objects.get(new_user__email=teacher_email)
44
+ # check the creation time
45
+ assert school.creation_time > sometime
52
46
 
53
- assert teacher.school is None
47
+ sometime = timezone.now() # mark time before the class creation
48
+ klass, name, access_code = create_class_directly(teacher_email)
49
+ # check the creation time
50
+ assert klass.creation_time > sometime
54
51
 
55
- def test_teacher_pending_join_request_on_delete(self):
52
+ def test_school_admins(self):
56
53
  """
57
- Given a school and a teacher without a school,
58
- When the teacher requests to join the school, and that school is deleted,
59
- Then the teacher's pending join request field is set to null.
54
+ Test that only the admins of a school are returned by the school.admins() function.
60
55
  """
61
- teacher1_email, _ = signup_teacher_directly()
62
- teacher2_email, _ = signup_teacher_directly()
63
- school_name, _ = create_organisation_directly(teacher1_email)
64
-
65
- teacher1 = Teacher.objects.get(new_user__email=teacher1_email)
66
- teacher2 = Teacher.objects.get(new_user__email=teacher2_email)
67
- school = School.objects.get(name=school_name)
68
-
69
- assert teacher1.school == school
70
- assert teacher2.school is None
71
- assert teacher2.pending_join_request is None
72
-
73
- teacher2.pending_join_request = school
74
- teacher2.save()
75
-
76
- school.delete()
77
-
78
- teacher2 = Teacher.objects.get(new_user__email=teacher2_email)
79
-
80
- assert teacher2.school is None
81
- assert teacher2.pending_join_request is None
56
+ email1, password1 = signup_teacher_directly()
57
+ email2, password2 = signup_teacher_directly()
58
+ email3, password3 = signup_teacher_directly()
59
+ school = create_organisation_directly(email1)
60
+ join_teacher_to_organisation(email2, school.name)
61
+ join_teacher_to_organisation(email3, school.name, is_admin=True)
62
+
63
+ teacher1 = Teacher.objects.get(new_user__username=email1)
64
+ teacher2 = Teacher.objects.get(new_user__username=email2)
65
+ teacher3 = Teacher.objects.get(new_user__username=email3)
66
+
67
+ assert len(school.admins()) == 2
68
+ assert teacher1 in school.admins()
69
+ assert teacher2 not in school.admins()
70
+ assert teacher3 in school.admins()
71
+
72
+ def test_sanitise_uk_postcode(self):
73
+ postcode_with_space = "AL10 9NE"
74
+ postcode_without_space = "AL109UL"
75
+ invalid_postcode = "123"
76
+
77
+ assert sanitise_uk_postcode(postcode_with_space) == "AL10 9NE" # Check it stays the same
78
+ assert sanitise_uk_postcode(postcode_without_space) == "AL10 9UL" # Check a space is added
79
+ assert sanitise_uk_postcode(invalid_postcode) == "123" # Check nothing happens
80
+
81
+ def test_daily_activity_serializer(self):
82
+ daily_activity = DailyActivity()
83
+
84
+ assert (
85
+ str(daily_activity)
86
+ == f"Activity on {daily_activity.date}: CSV clicks: 0, login cards clicks: 0, primary pack downloads: 0, python pack downloads: 0, level control submits: 0, teacher lockout resets: 0, indy lockout resets: 0, school student lockout resets: 0, unverified teachers anonymised: 0, unverified independents anonymised: 0"
87
+ )
@@ -16,9 +16,7 @@ def generate_details():
16
16
  generate_details.next_id = 1
17
17
 
18
18
 
19
- def create_class_directly(
20
- teacher_email: str, class_name: str = None
21
- ) -> Tuple[Class, str, str]:
19
+ def create_class_directly(teacher_email: str, class_name: str = None) -> Tuple[Class, str, str]:
22
20
  """Generate a class with the details given.
23
21
 
24
22
  Args:
@@ -1,21 +1,14 @@
1
- import re
2
1
  from builtins import str
3
2
 
4
3
 
5
- def follow_verify_email_link_to_onboarding(page, email):
6
- _follow_verify_email_link(page, email)
4
+ def follow_verify_email_link_to_onboarding(page, url):
5
+ page.browser.get(url)
7
6
 
8
7
  return go_to_teacher_login_page(page.browser)
9
8
 
10
9
 
11
- def follow_verify_email_link_to_teacher_dashboard(page, email):
12
- _follow_verify_email_link(page, email)
13
-
14
- return go_to_teacher_dashboard_page(page.browser)
15
-
16
-
17
- def follow_verify_email_link_to_login(page, email, user_type):
18
- _follow_verify_email_link(page, email)
10
+ def follow_verify_email_link_to_login(page, url, user_type):
11
+ page.browser.get(url)
19
12
 
20
13
  if user_type == "teacher":
21
14
  return go_to_teacher_login_page(page.browser)
@@ -23,8 +16,8 @@ def follow_verify_email_link_to_login(page, email, user_type):
23
16
  return go_to_independent_student_login_page(page.browser)
24
17
 
25
18
 
26
- def follow_duplicate_account_link_to_login(page, email, user_type):
27
- _follow_duplicate_account_email_link(page, email)
19
+ def follow_duplicate_account_link_to_login(page, url, user_type):
20
+ page.browser.get(url)
28
21
 
29
22
  if user_type == "teacher":
30
23
  return go_to_teacher_login_page(page.browser)
@@ -32,29 +25,7 @@ def follow_duplicate_account_link_to_login(page, email, user_type):
32
25
  return go_to_independent_student_login_page(page.browser)
33
26
 
34
27
 
35
- def _follow_verify_email_link(page, email):
36
- message = str(email.message())
37
- prefix = '<p>Please go to <a href="'
38
- i = str.find(message, prefix) + len(prefix)
39
- suffix = '" rel="nofollow">'
40
- j = str.find(message, suffix, i)
41
- page.browser.get(message[i:j])
42
-
43
-
44
- def _follow_duplicate_account_email_link(page, email):
45
- message = str(email.message())
46
- prefix = 'please login: <a href="'
47
- i = str.find(message, prefix) + len(prefix)
48
- suffix = '" rel="nofollow">'
49
- j = str.find(message, suffix, i)
50
- page.browser.get(message[i:j])
51
-
52
-
53
- def follow_reset_email_link(browser, email):
54
- message = str(email.body)
55
-
56
- link = re.search("http.+/", message).group(0)
57
-
28
+ def follow_reset_email_link(browser, link):
58
29
  browser.get(link)
59
30
 
60
31
  from portal.tests.pageObjects.portal.password_reset_form_page import (
@@ -64,27 +35,18 @@ def follow_reset_email_link(browser, email):
64
35
  return PasswordResetPage(browser)
65
36
 
66
37
 
67
- def follow_change_email_link_to_dashboard(page, email):
68
- _follow_change_email_link(page, email)
38
+ def follow_change_email_link_to_dashboard(page, url):
39
+ page.browser.get(url)
69
40
 
70
41
  return go_to_teacher_login_page(page.browser)
71
42
 
72
43
 
73
- def follow_change_email_link_to_independent_dashboard(page, email):
74
- _follow_change_email_link(page, email)
44
+ def follow_change_email_link_to_independent_dashboard(page, url):
45
+ page.browser.get(url)
75
46
 
76
47
  return go_to_independent_student_login_page(page.browser)
77
48
 
78
49
 
79
- def _follow_change_email_link(page, email):
80
- message = str(email.message())
81
- prefix = "please go to "
82
- i = str.find(message, prefix) + len(prefix)
83
- suffix = " to verify"
84
- j = str.find(message, suffix, i)
85
- page.browser.get(message[i:j])
86
-
87
-
88
50
  def go_to_teacher_login_page(browser):
89
51
  from portal.tests.pageObjects.portal.teacher_login_page import TeacherLoginPage
90
52
 
@@ -1,36 +1,33 @@
1
- from common.models import Teacher, School
1
+ from common.models import School, Teacher
2
2
 
3
3
 
4
4
  def generate_details(**kwargs):
5
5
  name = kwargs.get("name", "School %d" % generate_details.next_id)
6
- postcode = kwargs.get("postcode", "Al10 9NE")
7
6
 
8
7
  generate_details.next_id += 1
9
8
 
10
- return name, postcode
9
+ return name
11
10
 
12
11
 
13
12
  generate_details.next_id = 1
14
13
 
15
14
 
16
15
  def create_organisation_directly(teacher_email, **kwargs):
17
- name, postcode = generate_details(**kwargs)
16
+ name = generate_details(**kwargs)
18
17
 
19
- school = School.objects.create(
20
- name=name, postcode=postcode, country="GB", town="", latitude="", longitude=""
21
- )
18
+ school = School.objects.create(name=name, country="GB")
22
19
 
23
20
  teacher = Teacher.objects.get(new_user__email=teacher_email)
24
21
  teacher.school = school
25
22
  teacher.is_admin = True
26
23
  teacher.save()
27
24
 
28
- return name, postcode
25
+ return school
29
26
 
30
27
 
31
- def join_teacher_to_organisation(teacher_email, org_name, postcode, is_admin=False):
28
+ def join_teacher_to_organisation(teacher_email, org_name, is_admin=False):
32
29
  teacher = Teacher.objects.get(new_user__email=teacher_email)
33
- school = School.objects.get(name=org_name, postcode=postcode)
30
+ school = School.objects.get(name=org_name)
34
31
 
35
32
  teacher.school = school
36
33
  teacher.is_admin = is_admin
@@ -38,8 +35,7 @@ def join_teacher_to_organisation(teacher_email, org_name, postcode, is_admin=Fal
38
35
 
39
36
 
40
37
  def create_organisation(page, password):
38
+ name = generate_details()
39
+ page = page.create_organisation(name, password)
41
40
 
42
- name, postcode = generate_details()
43
- page = page.create_organisation(name, password, postcode)
44
-
45
- return page, name, postcode
41
+ return page, name