cfl-common 8.9.8__py3-none-any.whl → 8.9.10__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-8.9.8.dist-info → cfl_common-8.9.10.dist-info}/METADATA +7 -7
- {cfl_common-8.9.8.dist-info → cfl_common-8.9.10.dist-info}/RECORD +6 -6
- common/app_settings.py +5 -2
- common/helpers/emails.py +18 -48
- {cfl_common-8.9.8.dist-info → cfl_common-8.9.10.dist-info}/WHEEL +0 -0
- {cfl_common-8.9.8.dist-info → cfl_common-8.9.10.dist-info}/top_level.txt +0 -0
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfl-common
|
|
3
|
-
Version: 8.9.
|
|
3
|
+
Version: 8.9.10
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Operating System :: OS Independent
|
|
6
|
-
Requires-Dist: asgiref==3.
|
|
7
|
-
Requires-Dist: certifi==2025.
|
|
6
|
+
Requires-Dist: asgiref==3.11.0; python_version >= "3.9"
|
|
7
|
+
Requires-Dist: certifi==2025.11.12; python_version >= "3.7"
|
|
8
8
|
Requires-Dist: cffi==2.0.0; platform_python_implementation != "PyPy"
|
|
9
9
|
Requires-Dist: charset-normalizer==3.4.4; python_version >= "3.7"
|
|
10
10
|
Requires-Dist: cryptography==44.0.1; python_version >= "3.7" and python_full_version not in "3.9.0, 3.9.1"
|
|
11
11
|
Requires-Dist: diff-match-patch==20241021; python_version >= "3.7"
|
|
12
|
-
Requires-Dist: django==5.1.
|
|
12
|
+
Requires-Dist: django==5.1.15; python_version >= "3.10"
|
|
13
13
|
Requires-Dist: django-countries==7.6.1
|
|
14
14
|
Requires-Dist: django-csp==3.8
|
|
15
15
|
Requires-Dist: django-formtools==2.5.1; python_version >= "3.8"
|
|
16
16
|
Requires-Dist: django-import-export==4.2.0; python_version >= "3.9"
|
|
17
17
|
Requires-Dist: django-otp==1.6.3; python_version >= "3.7"
|
|
18
|
-
Requires-Dist: django-phonenumber-field==8.
|
|
18
|
+
Requires-Dist: django-phonenumber-field==8.4.0; python_version >= "3.10"
|
|
19
19
|
Requires-Dist: django-pipeline==4.0.0; python_version >= "3.9"
|
|
20
20
|
Requires-Dist: django-two-factor-auth==1.17.0; python_version >= "3.8"
|
|
21
21
|
Requires-Dist: djangorestframework==3.16.0; python_version >= "3.9"
|
|
22
22
|
Requires-Dist: idna==3.11; python_version >= "3.8"
|
|
23
23
|
Requires-Dist: libsass==0.23.0; python_version >= "3.8"
|
|
24
24
|
Requires-Dist: more-itertools==8.7.0; python_version >= "3.5"
|
|
25
|
-
Requires-Dist: numpy==2.3.
|
|
25
|
+
Requires-Dist: numpy==2.3.5; python_version >= "3.11"
|
|
26
26
|
Requires-Dist: pandas==2.3.3; python_version >= "3.9"
|
|
27
27
|
Requires-Dist: pgeocode==0.4.0; python_version >= "3.8"
|
|
28
28
|
Requires-Dist: pycparser==2.23; implementation_name != "PyPy"
|
|
@@ -34,7 +34,7 @@ Requires-Dist: qrcode==7.4.2; python_version >= "3.7"
|
|
|
34
34
|
Requires-Dist: requests==2.32.5; python_version >= "3.9"
|
|
35
35
|
Requires-Dist: setuptools==80.9.0; python_version >= "3.9"
|
|
36
36
|
Requires-Dist: six==1.17.0; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
|
|
37
|
-
Requires-Dist: sqlparse==0.5.
|
|
37
|
+
Requires-Dist: sqlparse==0.5.4; python_version >= "3.8"
|
|
38
38
|
Requires-Dist: tablib==3.7.0; python_version >= "3.9"
|
|
39
39
|
Requires-Dist: typing-extensions==4.15.0; python_version >= "3.9"
|
|
40
40
|
Requires-Dist: tzdata==2025.2; python_version >= "2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
common/__init__.py,sha256=XlncBOpKp_gekbKH7Y_i6yu1qy5tJc3Y8sn8cDy-Vgk,48
|
|
2
|
-
common/app_settings.py,sha256=
|
|
2
|
+
common/app_settings.py,sha256=br31aXMTs48oyxkvGmCSoGb94Ir8imONuD2uNO3k7-k,2763
|
|
3
3
|
common/apps.py,sha256=49UXZ3bSkFKvIEOL4zM7y1sAhccQJyRtsoOg5XVd_8Y,129
|
|
4
4
|
common/context_processors.py,sha256=X0iuX5qu9kMWa7q8osE9CJ2LgM7pPOYQFGdjm8X3rk0,236
|
|
5
5
|
common/csp_config.py,sha256=saeg9LbRr5xw7NDJPlt6fqi8Zz0vI8Rpc4VCS6oJNe8,2976
|
|
@@ -12,7 +12,7 @@ common/fixtures/aimmo_characters2.json,sha256=R-23mbjLrvpH4G9khXKcu7PTDK86xf9eHf
|
|
|
12
12
|
common/fixtures/aimmo_characters3.json,sha256=7Pv_6DFastPzmM86sPx6D60Y8Biq84GLL5pWz5NGSUA,1392
|
|
13
13
|
common/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
common/helpers/data_migration_loader.py,sha256=_BhS5lPmhcuVUbryBmJytlWdHyT02KYyxPkHar32mOE,1748
|
|
15
|
-
common/helpers/emails.py,sha256=
|
|
15
|
+
common/helpers/emails.py,sha256=wBgwTsOQSDIEFGZ3SS4N5XZO9nlwuCVzsjvgH4NNUkc,12060
|
|
16
16
|
common/helpers/generators.py,sha256=kTL5e91I8wgmjJ-mu4jr9vIacjccUZ5pZSAz5cUNhdM,1505
|
|
17
17
|
common/helpers/organisation.py,sha256=e-JKumKoXrkMTzZPv0H4ViWL8vtCt7oXJjn_zZ1ec00,427
|
|
18
18
|
common/migrations/0001_initial.py,sha256=Y2kt2xmdCbrmDXCgqmhXeacicNg26Zj7L7SANSsgAAI,9664
|
|
@@ -93,7 +93,7 @@ common/tests/utils/organisation.py,sha256=vNgKFtU3VPcWRnZfh82yCS90PLAK1XTYJNIxGw
|
|
|
93
93
|
common/tests/utils/student.py,sha256=GYOyd2VH6QXjjLzjCh4hpT86U5si2URlJJWZwlCVLrU,3846
|
|
94
94
|
common/tests/utils/teacher.py,sha256=KQ_NAl4yQqiX_zwcULQjkovc29JPhnkLR5Nk3Ljzbpg,2661
|
|
95
95
|
common/tests/utils/user.py,sha256=NvLzZLVP4jy5Hn1iztOYF_BTQ9WsbSmuWMEzGzhAsRU,919
|
|
96
|
-
cfl_common-8.9.
|
|
97
|
-
cfl_common-8.9.
|
|
98
|
-
cfl_common-8.9.
|
|
99
|
-
cfl_common-8.9.
|
|
96
|
+
cfl_common-8.9.10.dist-info/METADATA,sha256=Q7HrgpK92rSQMX7wClCd_9qBtraYJiWsmSWvTJ-bYZ0,2498
|
|
97
|
+
cfl_common-8.9.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
98
|
+
cfl_common-8.9.10.dist-info/top_level.txt,sha256=LOtYx8KZTmnxM_zLK4rwrcI3PRc40Ihwp5rgaQ-ceaI,7
|
|
99
|
+
cfl_common-8.9.10.dist-info/RECORD,,
|
common/app_settings.py
CHANGED
|
@@ -54,12 +54,15 @@ MODULE_NAME = getattr(settings, "MODULE_NAME", "local")
|
|
|
54
54
|
COOKIE_MANAGEMENT_ENABLED = getattr(settings, "COOKIE_MANAGEMENT_ENABLED", True)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def domain():
|
|
57
|
+
def domain(request=None):
|
|
58
58
|
"""Returns the full domain depending on whether it's local, dev, staging or prod."""
|
|
59
|
+
if hasattr(settings, "SERVICE_BASE_URL"):
|
|
60
|
+
return getattr(settings, "SERVICE_BASE_URL")
|
|
61
|
+
|
|
59
62
|
domain = "https://www.codeforlife.education"
|
|
60
63
|
|
|
61
64
|
if MODULE_NAME == "local":
|
|
62
|
-
domain = "localhost:8000"
|
|
65
|
+
domain = f"http://{request.get_host()}" if request is not None else "localhost:8000"
|
|
63
66
|
elif MODULE_NAME == "staging" or MODULE_NAME == "dev":
|
|
64
67
|
domain = f"https://{MODULE_NAME}-dot-decent-digit-629.appspot.com"
|
|
65
68
|
|
common/helpers/emails.py
CHANGED
|
@@ -20,15 +20,9 @@ from django.utils import timezone
|
|
|
20
20
|
from requests import delete, get, post, put
|
|
21
21
|
from requests.exceptions import RequestException
|
|
22
22
|
|
|
23
|
-
NOTIFICATION_EMAIL =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
VERIFICATION_EMAIL = (
|
|
27
|
-
"Code For Life Verification <" + app_settings.EMAIL_ADDRESS + ">"
|
|
28
|
-
)
|
|
29
|
-
PASSWORD_RESET_EMAIL = (
|
|
30
|
-
"Code For Life Password Reset <" + app_settings.EMAIL_ADDRESS + ">"
|
|
31
|
-
)
|
|
23
|
+
NOTIFICATION_EMAIL = "Code For Life Notification <" + app_settings.EMAIL_ADDRESS + ">"
|
|
24
|
+
VERIFICATION_EMAIL = "Code For Life Verification <" + app_settings.EMAIL_ADDRESS + ">"
|
|
25
|
+
PASSWORD_RESET_EMAIL = "Code For Life Password Reset <" + app_settings.EMAIL_ADDRESS + ">"
|
|
32
26
|
INVITE_FROM = "Code For Life Invitation <" + app_settings.EMAIL_ADDRESS + ">"
|
|
33
27
|
|
|
34
28
|
|
|
@@ -52,9 +46,7 @@ def generate_token_for_email(email: str, new_email: str = ""):
|
|
|
52
46
|
"email": email,
|
|
53
47
|
"new_email": new_email,
|
|
54
48
|
"email_verification_token": uuid4().hex[:30],
|
|
55
|
-
"expires": (
|
|
56
|
-
timezone.now() + datetime.timedelta(hours=1)
|
|
57
|
-
).timestamp(),
|
|
49
|
+
"expires": (timezone.now() + datetime.timedelta(hours=1)).timestamp(),
|
|
58
50
|
},
|
|
59
51
|
settings.SECRET_KEY,
|
|
60
52
|
algorithm="HS256",
|
|
@@ -87,9 +79,7 @@ def send_email(
|
|
|
87
79
|
)
|
|
88
80
|
|
|
89
81
|
|
|
90
|
-
def send_verification_email(
|
|
91
|
-
request, user, data, new_email=None, age=None, school=None
|
|
92
|
-
):
|
|
82
|
+
def send_verification_email(request, user, data, new_email=None, age=None, school=None):
|
|
93
83
|
"""
|
|
94
84
|
Sends emails relating to email address verification.
|
|
95
85
|
|
|
@@ -119,7 +109,7 @@ def send_verification_email(
|
|
|
119
109
|
if age is None:
|
|
120
110
|
# if the user is a released student
|
|
121
111
|
if hasattr(user, "new_student") and school is not None:
|
|
122
|
-
url = f"{
|
|
112
|
+
url = f"{app_settings.domain(request)}{reverse('verify_email', kwargs={'token': verification})}"
|
|
123
113
|
|
|
124
114
|
send_dotdigital_email(
|
|
125
115
|
campaign_ids["verify_released_student"],
|
|
@@ -130,7 +120,7 @@ def send_verification_email(
|
|
|
130
120
|
},
|
|
131
121
|
)
|
|
132
122
|
else:
|
|
133
|
-
url = f"{
|
|
123
|
+
url = f"{app_settings.domain(request)}{reverse('verify_email', kwargs={'token': verification})}"
|
|
134
124
|
|
|
135
125
|
send_dotdigital_email(
|
|
136
126
|
campaign_ids["verify_new_user"],
|
|
@@ -149,7 +139,7 @@ def send_verification_email(
|
|
|
149
139
|
# if the user is an independent student
|
|
150
140
|
else:
|
|
151
141
|
if age < 13:
|
|
152
|
-
url = f"{
|
|
142
|
+
url = f"{app_settings.domain(request)}{reverse('verify_email', kwargs={'token': verification})}"
|
|
153
143
|
send_dotdigital_email(
|
|
154
144
|
campaign_ids["verify_new_user_via_parent"],
|
|
155
145
|
[user.email],
|
|
@@ -159,7 +149,7 @@ def send_verification_email(
|
|
|
159
149
|
},
|
|
160
150
|
)
|
|
161
151
|
else:
|
|
162
|
-
url = f"{
|
|
152
|
+
url = f"{app_settings.domain(request)}{reverse('verify_email', kwargs={'token': verification})}"
|
|
163
153
|
send_dotdigital_email(
|
|
164
154
|
campaign_ids["verify_new_user"],
|
|
165
155
|
[user.email],
|
|
@@ -177,7 +167,7 @@ def send_verification_email(
|
|
|
177
167
|
# verifying change of email address.
|
|
178
168
|
else:
|
|
179
169
|
verification = generate_token(user, new_email)
|
|
180
|
-
url = f"{
|
|
170
|
+
url = f"{app_settings.domain(request)}{reverse('verify_email', kwargs={'token': verification})}"
|
|
181
171
|
send_dotdigital_email(
|
|
182
172
|
campaign_ids["email_change_verification"],
|
|
183
173
|
[new_email],
|
|
@@ -194,9 +184,7 @@ def add_to_dotmailer(
|
|
|
194
184
|
):
|
|
195
185
|
try:
|
|
196
186
|
create_contact(first_name, last_name, email)
|
|
197
|
-
add_contact_to_address_book(
|
|
198
|
-
first_name, last_name, email, address_book_id, user_type
|
|
199
|
-
)
|
|
187
|
+
add_contact_to_address_book(first_name, last_name, email, address_book_id, user_type)
|
|
200
188
|
except RequestException:
|
|
201
189
|
return HttpResponse(status=404)
|
|
202
190
|
|
|
@@ -261,18 +249,12 @@ def add_contact_to_address_book(
|
|
|
261
249
|
)
|
|
262
250
|
|
|
263
251
|
if user_type is not None:
|
|
264
|
-
specific_address_book_url =
|
|
265
|
-
app_settings.DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL
|
|
266
|
-
)
|
|
252
|
+
specific_address_book_url = app_settings.DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL
|
|
267
253
|
|
|
268
254
|
if user_type == DotmailerUserType.TEACHER:
|
|
269
|
-
specific_address_book_url =
|
|
270
|
-
app_settings.DOTMAILER_TEACHER_ADDRESS_BOOK_URL
|
|
271
|
-
)
|
|
255
|
+
specific_address_book_url = app_settings.DOTMAILER_TEACHER_ADDRESS_BOOK_URL
|
|
272
256
|
elif user_type == DotmailerUserType.STUDENT:
|
|
273
|
-
specific_address_book_url =
|
|
274
|
-
app_settings.DOTMAILER_STUDENT_ADDRESS_BOOK_URL
|
|
275
|
-
)
|
|
257
|
+
specific_address_book_url = app_settings.DOTMAILER_STUDENT_ADDRESS_BOOK_URL
|
|
276
258
|
|
|
277
259
|
post(
|
|
278
260
|
specific_address_book_url,
|
|
@@ -286,9 +268,7 @@ def delete_contact(email: str):
|
|
|
286
268
|
user = get_dotmailer_user_by_email(email)
|
|
287
269
|
user_id = user.get("id")
|
|
288
270
|
if user_id:
|
|
289
|
-
url = app_settings.DOTMAILER_DELETE_USER_BY_ID_URL.replace(
|
|
290
|
-
"ID", str(user_id)
|
|
291
|
-
)
|
|
271
|
+
url = app_settings.DOTMAILER_DELETE_USER_BY_ID_URL.replace("ID", str(user_id))
|
|
292
272
|
delete(
|
|
293
273
|
url,
|
|
294
274
|
auth=(
|
|
@@ -303,9 +283,7 @@ def delete_contact(email: str):
|
|
|
303
283
|
def get_dotmailer_user_by_email(email):
|
|
304
284
|
url = app_settings.DOTMAILER_GET_USER_BY_EMAIL_URL.replace("EMAIL", email)
|
|
305
285
|
|
|
306
|
-
response = get(
|
|
307
|
-
url, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD)
|
|
308
|
-
)
|
|
286
|
+
response = get(url, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
|
|
309
287
|
|
|
310
288
|
return json.loads(response.content)
|
|
311
289
|
|
|
@@ -313,9 +291,7 @@ def get_dotmailer_user_by_email(email):
|
|
|
313
291
|
def add_consent_record_to_dotmailer_user(user):
|
|
314
292
|
consent_date_time = datetime.datetime.now().__str__()
|
|
315
293
|
|
|
316
|
-
url = app_settings.DOTMAILER_PUT_CONSENT_DATA_URL.replace(
|
|
317
|
-
"USER_ID", str(user["id"])
|
|
318
|
-
)
|
|
294
|
+
url = app_settings.DOTMAILER_PUT_CONSENT_DATA_URL.replace("USER_ID", str(user["id"]))
|
|
319
295
|
body = {
|
|
320
296
|
"contact": {
|
|
321
297
|
"email": user["email"],
|
|
@@ -323,13 +299,7 @@ def add_consent_record_to_dotmailer_user(user):
|
|
|
323
299
|
"emailType": user["emailType"],
|
|
324
300
|
"dataFields": user["dataFields"],
|
|
325
301
|
},
|
|
326
|
-
"consentFields": [
|
|
327
|
-
{
|
|
328
|
-
"fields": [
|
|
329
|
-
{"key": "DATETIMECONSENTED", "value": consent_date_time}
|
|
330
|
-
]
|
|
331
|
-
}
|
|
332
|
-
],
|
|
302
|
+
"consentFields": [{"fields": [{"key": "DATETIMECONSENTED", "value": consent_date_time}]}],
|
|
333
303
|
}
|
|
334
304
|
|
|
335
305
|
put(
|
|
File without changes
|
|
File without changes
|