codeforlife-portal 6.41.5__py2.py3-none-any.whl → 6.41.6__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of codeforlife-portal might be problematic. Click here for more details.

Files changed (49) hide show
  1. cfl_common/common/app_settings.py +3 -0
  2. cfl_common/common/email_messages.py +0 -58
  3. cfl_common/common/helpers/data_migration_loader.py +3 -4
  4. cfl_common/common/helpers/emails.py +27 -45
  5. cfl_common/common/helpers/generators.py +1 -1
  6. cfl_common/common/mail.py +116 -0
  7. cfl_common/common/migrations/0002_emailverification.py +1 -3
  8. cfl_common/common/migrations/0005_add_worksheets.py +1 -5
  9. cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +1 -5
  10. cfl_common/common/migrations/0008_unlock_worksheet_3.py +1 -5
  11. cfl_common/common/migrations/0017_copy_email_to_username.py +2 -8
  12. cfl_common/common/migrations/0021_school_is_active.py +7 -7
  13. cfl_common/common/migrations/0022_school_cleanup.py +9 -9
  14. cfl_common/common/migrations/0023_userprofile_aimmo_badges.py +4 -4
  15. cfl_common/common/migrations/0025_schoolteacherinvitation.py +29 -13
  16. cfl_common/common/migrations/0026_teacher_remove_join_request.py +5 -5
  17. cfl_common/common/migrations/0027_class_created_by.py +10 -4
  18. cfl_common/common/migrations/0028_coding_club_downloads.py +5 -5
  19. cfl_common/common/migrations/0029_dynamicelement.py +6 -6
  20. cfl_common/common/migrations/0030_add_maintenance_banner.py +1 -3
  21. cfl_common/common/migrations/0031_improve_admin_panel.py +32 -14
  22. cfl_common/common/migrations/0032_dailyactivity_level_control_submits.py +3 -3
  23. cfl_common/common/migrations/0033_password_reset_tracking_fields.py +5 -5
  24. cfl_common/common/migrations/0034_dailyactivity_daily_school_student_lockout_reset.py +3 -3
  25. cfl_common/common/migrations/0035_rename_lockout_fields.py +10 -10
  26. cfl_common/common/migrations/0037_migrate_email_verification.py +2 -2
  27. cfl_common/common/migrations/0038_delete_emailverification.py +2 -2
  28. cfl_common/common/migrations/0039_copy_email_to_username.py +1 -6
  29. cfl_common/common/migrations/0040_school_county.py +3 -3
  30. cfl_common/common/migrations/0042_totalactivity.py +7 -7
  31. cfl_common/common/migrations/0044_update_activity_models.py +9 -9
  32. cfl_common/common/migrations/0045_otp.py +5 -5
  33. cfl_common/common/migrations/0046_alter_school_country.py +3 -3
  34. cfl_common/common/tests/utils/email.py +14 -34
  35. cfl_common/common/tests/utils/student.py +8 -8
  36. cfl_common/common/tests/utils/teacher.py +8 -8
  37. {codeforlife_portal-6.41.5.dist-info → codeforlife_portal-6.41.6.dist-info}/METADATA +2 -2
  38. {codeforlife_portal-6.41.5.dist-info → codeforlife_portal-6.41.6.dist-info}/RECORD +49 -48
  39. example_project/portal_test_settings.py +5 -1
  40. portal/__init__.py +1 -1
  41. portal/tests/test_independent_student.py +30 -17
  42. portal/tests/test_ratelimit.py +15 -12
  43. portal/tests/test_teacher.py +35 -21
  44. portal/tests/test_teacher_student.py +13 -3
  45. portal/tests/test_views.py +55 -194
  46. portal/views/cron/user.py +12 -49
  47. {codeforlife_portal-6.41.5.dist-info → codeforlife_portal-6.41.6.dist-info}/LICENSE.md +0 -0
  48. {codeforlife_portal-6.41.5.dist-info → codeforlife_portal-6.41.6.dist-info}/WHEEL +0 -0
  49. {codeforlife_portal-6.41.5.dist-info → codeforlife_portal-6.41.6.dist-info}/top_level.txt +0 -0
@@ -22,6 +22,4 @@ class Migration(migrations.Migration):
22
22
 
23
23
  dependencies = [("common", "0029_dynamicelement")]
24
24
 
25
- operations = [
26
- migrations.RunPython(add_maintenance_banner, remove_maintenance_banner)
27
- ]
25
+ operations = [migrations.RunPython(add_maintenance_banner, remove_maintenance_banner)]
@@ -7,32 +7,50 @@ import django.db.models.deletion
7
7
  class Migration(migrations.Migration):
8
8
 
9
9
  dependencies = [
10
- ('common', '0030_add_maintenance_banner'),
10
+ ("common", "0030_add_maintenance_banner"),
11
11
  ]
12
12
 
13
13
  operations = [
14
14
  migrations.AlterModelOptions(
15
- name='dailyactivity',
16
- options={'verbose_name_plural': 'Daily activities'},
15
+ name="dailyactivity",
16
+ options={"verbose_name_plural": "Daily activities"},
17
17
  ),
18
18
  migrations.AlterField(
19
- model_name='student',
20
- name='blocked_time',
19
+ model_name="student",
20
+ name="blocked_time",
21
21
  field=models.DateTimeField(blank=True, null=True),
22
22
  ),
23
23
  migrations.AlterField(
24
- model_name='student',
25
- name='class_field',
26
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='students', to='common.class'),
24
+ model_name="student",
25
+ name="class_field",
26
+ field=models.ForeignKey(
27
+ blank=True,
28
+ null=True,
29
+ on_delete=django.db.models.deletion.CASCADE,
30
+ related_name="students",
31
+ to="common.class",
32
+ ),
27
33
  ),
28
34
  migrations.AlterField(
29
- model_name='student',
30
- name='pending_class_request',
31
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='class_request', to='common.class'),
35
+ model_name="student",
36
+ name="pending_class_request",
37
+ field=models.ForeignKey(
38
+ blank=True,
39
+ null=True,
40
+ on_delete=django.db.models.deletion.SET_NULL,
41
+ related_name="class_request",
42
+ to="common.class",
43
+ ),
32
44
  ),
33
45
  migrations.AlterField(
34
- model_name='teacher',
35
- name='school',
36
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='teacher_school', to='common.school'),
46
+ model_name="teacher",
47
+ name="school",
48
+ field=models.ForeignKey(
49
+ blank=True,
50
+ null=True,
51
+ on_delete=django.db.models.deletion.SET_NULL,
52
+ related_name="teacher_school",
53
+ to="common.school",
54
+ ),
37
55
  ),
38
56
  ]
@@ -6,13 +6,13 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0031_improve_admin_panel'),
9
+ ("common", "0031_improve_admin_panel"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='dailyactivity',
15
- name='level_control_submits',
14
+ model_name="dailyactivity",
15
+ name="level_control_submits",
16
16
  field=models.PositiveBigIntegerField(default=0),
17
17
  ),
18
18
  ]
@@ -6,18 +6,18 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0032_dailyactivity_level_control_submits'),
9
+ ("common", "0032_dailyactivity_level_control_submits"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='dailyactivity',
15
- name='daily_indy_lockout_reset',
14
+ model_name="dailyactivity",
15
+ name="daily_indy_lockout_reset",
16
16
  field=models.PositiveIntegerField(default=0),
17
17
  ),
18
18
  migrations.AddField(
19
- model_name='dailyactivity',
20
- name='daily_teacher_lockout_reset',
19
+ model_name="dailyactivity",
20
+ name="daily_teacher_lockout_reset",
21
21
  field=models.PositiveIntegerField(default=0),
22
22
  ),
23
23
  ]
@@ -6,13 +6,13 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0033_password_reset_tracking_fields'),
9
+ ("common", "0033_password_reset_tracking_fields"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='dailyactivity',
15
- name='daily_school_student_lockout_reset',
14
+ model_name="dailyactivity",
15
+ name="daily_school_student_lockout_reset",
16
16
  field=models.PositiveIntegerField(default=0),
17
17
  ),
18
18
  ]
@@ -5,23 +5,23 @@ from django.db import migrations
5
5
 
6
6
  class Migration(migrations.Migration):
7
7
  dependencies = [
8
- ('common', '0034_dailyactivity_daily_school_student_lockout_reset'),
8
+ ("common", "0034_dailyactivity_daily_school_student_lockout_reset"),
9
9
  ]
10
10
 
11
11
  operations = [
12
12
  migrations.RenameField(
13
- model_name='dailyactivity',
14
- old_name='daily_indy_lockout_reset',
15
- new_name='indy_lockout_resets',
13
+ model_name="dailyactivity",
14
+ old_name="daily_indy_lockout_reset",
15
+ new_name="indy_lockout_resets",
16
16
  ),
17
17
  migrations.RenameField(
18
- model_name='dailyactivity',
19
- old_name='daily_school_student_lockout_reset',
20
- new_name='school_student_lockout_resets',
18
+ model_name="dailyactivity",
19
+ old_name="daily_school_student_lockout_reset",
20
+ new_name="school_student_lockout_resets",
21
21
  ),
22
22
  migrations.RenameField(
23
- model_name='dailyactivity',
24
- old_name='daily_teacher_lockout_reset',
25
- new_name='teacher_lockout_resets',
23
+ model_name="dailyactivity",
24
+ old_name="daily_teacher_lockout_reset",
25
+ new_name="teacher_lockout_resets",
26
26
  ),
27
27
  ]
@@ -7,13 +7,13 @@ class Migration(migrations.Migration):
7
7
  ]
8
8
 
9
9
  def forwards(apps, schema_editor):
10
- """ Finds the users of verified Email Verification objects and sets their `is_verified` to True """
10
+ """Finds the users of verified Email Verification objects and sets their `is_verified` to True"""
11
11
  UserProfile = apps.get_model("common", "UserProfile")
12
12
  db_alias = schema_editor.connection.alias
13
13
  UserProfile.objects.using(db_alias).filter(user__email_verifications__verified=True).update(is_verified=True)
14
14
 
15
15
  def backwards(apps, schema_editor):
16
- """ Finds the users of verified Email Verification objects and sets their `is_verified` to False """
16
+ """Finds the users of verified Email Verification objects and sets their `is_verified` to False"""
17
17
  UserProfile = apps.get_model("common", "UserProfile")
18
18
  db_alias = schema_editor.connection.alias
19
19
  UserProfile.objects.using(db_alias).filter(user__email_verifications__verified=True).update(is_verified=False)
@@ -6,11 +6,11 @@ from django.db import migrations
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0037_migrate_email_verification'),
9
+ ("common", "0037_migrate_email_verification"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.DeleteModel(
14
- name='EmailVerification',
14
+ name="EmailVerification",
15
15
  ),
16
16
  ]
@@ -15,9 +15,4 @@ class Migration(migrations.Migration):
15
15
  ("common", "0038_delete_emailverification"),
16
16
  ]
17
17
 
18
- operations = [
19
- migrations.RunPython(
20
- code=copy_email_to_username, reverse_code=migrations.RunPython.noop
21
- )
22
- ]
23
-
18
+ operations = [migrations.RunPython(code=copy_email_to_username, reverse_code=migrations.RunPython.noop)]
@@ -6,13 +6,13 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0039_copy_email_to_username'),
9
+ ("common", "0039_copy_email_to_username"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='school',
15
- name='county',
14
+ model_name="school",
15
+ name="county",
16
16
  field=models.CharField(blank=True, max_length=50, null=True),
17
17
  ),
18
18
  ]
@@ -6,20 +6,20 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0041_populate_gb_counties'),
9
+ ("common", "0041_populate_gb_counties"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.CreateModel(
14
- name='TotalActivity',
14
+ name="TotalActivity",
15
15
  fields=[
16
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17
- ('teacher_registrations', models.PositiveIntegerField(default=0)),
18
- ('student_registrations', models.PositiveIntegerField(default=0)),
19
- ('independent_registrations', models.PositiveIntegerField(default=0)),
16
+ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
17
+ ("teacher_registrations", models.PositiveIntegerField(default=0)),
18
+ ("student_registrations", models.PositiveIntegerField(default=0)),
19
+ ("independent_registrations", models.PositiveIntegerField(default=0)),
20
20
  ],
21
21
  options={
22
- 'verbose_name_plural': 'Total activity',
22
+ "verbose_name_plural": "Total activity",
23
23
  },
24
24
  ),
25
25
  ]
@@ -6,28 +6,28 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0043_add_total_activity'),
9
+ ("common", "0043_add_total_activity"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='dailyactivity',
15
- name='anonymised_unverified_independents',
14
+ model_name="dailyactivity",
15
+ name="anonymised_unverified_independents",
16
16
  field=models.PositiveIntegerField(default=0),
17
17
  ),
18
18
  migrations.AddField(
19
- model_name='dailyactivity',
20
- name='anonymised_unverified_teachers',
19
+ model_name="dailyactivity",
20
+ name="anonymised_unverified_teachers",
21
21
  field=models.PositiveIntegerField(default=0),
22
22
  ),
23
23
  migrations.AddField(
24
- model_name='totalactivity',
25
- name='anonymised_unverified_independents',
24
+ model_name="totalactivity",
25
+ name="anonymised_unverified_independents",
26
26
  field=models.PositiveIntegerField(default=0),
27
27
  ),
28
28
  migrations.AddField(
29
- model_name='totalactivity',
30
- name='anonymised_unverified_teachers',
29
+ model_name="totalactivity",
30
+ name="anonymised_unverified_teachers",
31
31
  field=models.PositiveIntegerField(default=0),
32
32
  ),
33
33
  ]
@@ -6,18 +6,18 @@ from django.db import migrations, models
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('common', '0044_update_activity_models'),
9
+ ("common", "0044_update_activity_models"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.AddField(
14
- model_name='userprofile',
15
- name='last_otp_for_time',
14
+ model_name="userprofile",
15
+ name="last_otp_for_time",
16
16
  field=models.DateTimeField(blank=True, null=True),
17
17
  ),
18
18
  migrations.AddField(
19
- model_name='userprofile',
20
- name='otp_secret',
19
+ model_name="userprofile",
20
+ name="otp_secret",
21
21
  field=models.CharField(blank=True, max_length=40, null=True),
22
22
  ),
23
23
  ]
@@ -7,13 +7,13 @@ import django_countries.fields
7
7
  class Migration(migrations.Migration):
8
8
 
9
9
  dependencies = [
10
- ('common', '0045_otp'),
10
+ ("common", "0045_otp"),
11
11
  ]
12
12
 
13
13
  operations = [
14
14
  migrations.AlterField(
15
- model_name='school',
16
- name='country',
15
+ model_name="school",
16
+ name="country",
17
17
  field=django_countries.fields.CountryField(blank=True, max_length=2, null=True),
18
18
  ),
19
19
  ]
@@ -2,20 +2,14 @@ import re
2
2
  from builtins import str
3
3
 
4
4
 
5
- def follow_verify_email_link_to_onboarding(page, email):
6
- _follow_verify_email_link(page, email)
5
+ def follow_verify_email_link_to_onboarding(page, url):
6
+ page.browser.get(url)
7
7
 
8
8
  return go_to_teacher_login_page(page.browser)
9
9
 
10
10
 
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)
11
+ def follow_verify_email_link_to_login(page, url, user_type):
12
+ page.browser.get(url)
19
13
 
20
14
  if user_type == "teacher":
21
15
  return go_to_teacher_login_page(page.browser)
@@ -32,15 +26,6 @@ def follow_duplicate_account_link_to_login(page, email, user_type):
32
26
  return go_to_independent_student_login_page(page.browser)
33
27
 
34
28
 
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
29
  def _follow_duplicate_account_email_link(page, email):
45
30
  message = str(email.message())
46
31
  prefix = 'please login: <a href="'
@@ -55,32 +40,25 @@ def follow_reset_email_link(browser, email):
55
40
  link = re.search("http.+/", message).group(0)[:-1]
56
41
  browser.get(link)
57
42
 
58
- from portal.tests.pageObjects.portal.password_reset_form_page import PasswordResetPage
43
+ from portal.tests.pageObjects.portal.password_reset_form_page import (
44
+ PasswordResetPage,
45
+ )
59
46
 
60
47
  return PasswordResetPage(browser)
61
48
 
62
49
 
63
- def follow_change_email_link_to_dashboard(page, email):
64
- _follow_change_email_link(page, email)
50
+ def follow_change_email_link_to_dashboard(page, url):
51
+ page.browser.get(url)
65
52
 
66
53
  return go_to_teacher_login_page(page.browser)
67
54
 
68
55
 
69
- def follow_change_email_link_to_independent_dashboard(page, email):
70
- _follow_change_email_link(page, email)
56
+ def follow_change_email_link_to_independent_dashboard(page, url):
57
+ page.browser.get(url)
71
58
 
72
59
  return go_to_independent_student_login_page(page.browser)
73
60
 
74
61
 
75
- def _follow_change_email_link(page, email):
76
- message = str(email.message())
77
- prefix = "please go to "
78
- i = str.find(message, prefix) + len(prefix)
79
- suffix = " to verify"
80
- j = str.find(message, suffix, i)
81
- page.browser.get(message[i:j])
82
-
83
-
84
62
  def go_to_teacher_login_page(browser):
85
63
  from portal.tests.pageObjects.portal.teacher_login_page import TeacherLoginPage
86
64
 
@@ -94,6 +72,8 @@ def go_to_teacher_dashboard_page(browser):
94
72
 
95
73
 
96
74
  def go_to_independent_student_login_page(browser):
97
- from portal.tests.pageObjects.portal.independent_login_page import IndependentStudentLoginPage
75
+ from portal.tests.pageObjects.portal.independent_login_page import (
76
+ IndependentStudentLoginPage,
77
+ )
98
78
 
99
79
  return IndependentStudentLoginPage(browser)
@@ -1,5 +1,6 @@
1
1
  from builtins import range
2
2
  from typing import Tuple
3
+ from unittest.mock import patch
3
4
 
4
5
  from common.helpers.emails import generate_token
5
6
  from common.helpers.generators import generate_login_id
@@ -118,7 +119,8 @@ def signup_duplicate_independent_student_fail(page, duplicate_email=None):
118
119
  return page, name, username, email_address, password
119
120
 
120
121
 
121
- def create_independent_student(page):
122
+ @patch("common.helpers.emails.send_dotdigital_email")
123
+ def create_independent_student(page, mock_send_dotdigital_email):
122
124
  page = page.go_to_signup_page()
123
125
 
124
126
  name, username, email_address, password = generate_independent_student_details()
@@ -126,16 +128,14 @@ def create_independent_student(page):
126
128
 
127
129
  page = page.return_to_home_page()
128
130
 
129
- page = email.follow_verify_email_link_to_login(page, mail.outbox[0], "independent")
130
- mail.outbox = []
131
+ verification_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"]["VERIFICATION_LINK"]
131
132
 
132
- return page, name, username, email_address, password
133
+ page = email.follow_verify_email_link_to_login(page, verification_url, "independent")
133
134
 
135
+ return page, name, username, email_address, password
134
136
 
135
- def verify_email(page):
136
- assert len(mail.outbox) > 0
137
137
 
138
- page = email.follow_verify_email_link_to_login(page, mail.outbox[0], "independent")
139
- mail.outbox = []
138
+ def verify_email(page, verification_url):
139
+ page = email.follow_verify_email_link_to_login(page, verification_url, "independent")
140
140
 
141
141
  return page
@@ -1,5 +1,6 @@
1
1
  import random
2
2
  import sys
3
+ from unittest.mock import patch
3
4
 
4
5
  from common.helpers.emails import generate_token
5
6
  from common.models import Teacher
@@ -47,7 +48,8 @@ def signup_duplicate_teacher_fail(page, duplicate_email):
47
48
  return page, email_address, password
48
49
 
49
50
 
50
- def signup_teacher(page, newsletter=False):
51
+ @patch("common.helpers.emails.send_dotdigital_email")
52
+ def signup_teacher(page, mock_send_dotdigital_email, newsletter=False):
51
53
  page = page.go_to_signup_page()
52
54
 
53
55
  first_name, last_name, email_address, password = generate_details()
@@ -57,16 +59,14 @@ def signup_teacher(page, newsletter=False):
57
59
 
58
60
  page = page.return_to_home_page()
59
61
 
60
- page = email.follow_verify_email_link_to_onboarding(page, mail.outbox[0])
61
- mail.outbox = []
62
+ verification_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"]["VERIFICATION_LINK"]
62
63
 
63
- return page, email_address, password
64
+ page = email.follow_verify_email_link_to_onboarding(page, verification_url)
64
65
 
66
+ return page, email_address, password
65
67
 
66
- def verify_email(page):
67
- assert len(mail.outbox) > 0
68
68
 
69
- page = email.follow_verify_email_link_to_login(page, mail.outbox[0], "teacher")
70
- mail.outbox = []
69
+ def verify_email(page, verification_url):
70
+ page = email.follow_verify_email_link_to_login(page, verification_url, "teacher")
71
71
 
72
72
  return page
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforlife-portal
3
- Version: 6.41.5
3
+ Version: 6.41.6
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3.8
6
6
  Classifier: Framework :: Django
@@ -25,7 +25,7 @@ Requires-Dist: sqlparse ==0.4.4
25
25
  Requires-Dist: libsass ==0.22.0
26
26
  Requires-Dist: phonenumbers ==8.12.12
27
27
  Requires-Dist: more-itertools ==8.7.0
28
- Requires-Dist: cfl-common ==6.41.5
28
+ Requires-Dist: cfl-common ==6.41.6
29
29
  Requires-Dist: django-ratelimit ==3.0.1
30
30
  Requires-Dist: django-preventconcurrentlogins ==0.8.2
31
31
  Requires-Dist: django-csp ==3.7