codeforlife-portal 8.4.4__py2.py3-none-any.whl → 8.4.5__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.
- cfl_common/setup.py +36 -13
- codeforlife_portal-8.4.5.dist-info/METADATA +191 -0
- {codeforlife_portal-8.4.4.dist-info → codeforlife_portal-8.4.5.dist-info}/RECORD +16 -17
- {codeforlife_portal-8.4.4.dist-info → codeforlife_portal-8.4.5.dist-info}/WHEEL +1 -1
- example_project/portal_test_settings.py +12 -12
- example_project/settings.py +2 -2
- portal/__init__.py +1 -1
- portal/handlers.py +1 -2
- portal/tests/base_test.py +0 -21
- portal/tests/test_class.py +3 -0
- portal/tests/test_invite_teacher.py +10 -8
- portal/tests/test_teacher.py +13 -26
- portal/tests/test_teacher_student.py +5 -1
- portal/tests/utils/messages.py +1 -0
- codeforlife_portal-8.4.4.dist-info/METADATA +0 -62
- example_project/manage.py +0 -10
- {codeforlife_portal-8.4.4.dist-info → codeforlife_portal-8.4.5.dist-info}/LICENSE.md +0 -0
- {codeforlife_portal-8.4.4.dist-info → codeforlife_portal-8.4.5.dist-info}/top_level.txt +0 -0
cfl_common/setup.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import re
|
|
3
|
+
import json
|
|
4
|
+
import typing as t
|
|
3
5
|
|
|
4
6
|
from setuptools import find_packages, setup
|
|
5
7
|
|
|
@@ -10,25 +12,46 @@ with open("../portal/__init__.py", "r") as fd:
|
|
|
10
12
|
re.MULTILINE,
|
|
11
13
|
).group(1)
|
|
12
14
|
|
|
15
|
+
|
|
16
|
+
def parse_requirements(packages: t.Dict[str, t.Dict[str, t.Any]]):
|
|
17
|
+
"""Parse a group of requirements from `Pipfile.lock`.
|
|
18
|
+
|
|
19
|
+
https://setuptools.pypa.io/en/latest/userguide/dependency_management.html
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
packages: The group name of the requirements.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The requirements as a list of strings, required by `setuptools.setup`.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
requirements: t.List[str] = []
|
|
29
|
+
for name, package in packages.items():
|
|
30
|
+
requirement = name
|
|
31
|
+
if "extras" in package:
|
|
32
|
+
requirement += f"[{','.join(package['extras'])}]"
|
|
33
|
+
if "version" in package:
|
|
34
|
+
requirement += package["version"]
|
|
35
|
+
if "markers" in package:
|
|
36
|
+
requirement += f"; {package['markers']}"
|
|
37
|
+
requirements.append(requirement)
|
|
38
|
+
|
|
39
|
+
return requirements
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Parse Pipfile.lock into strings.
|
|
43
|
+
with open("Pipfile.lock", "r", encoding="utf-8") as pipfile_lock:
|
|
44
|
+
lock = json.load(pipfile_lock)
|
|
45
|
+
install_requires = parse_requirements(lock["default"])
|
|
46
|
+
dev_requires = parse_requirements(lock["develop"])
|
|
47
|
+
|
|
13
48
|
setup(
|
|
14
49
|
name="cfl-common",
|
|
15
50
|
long_description="Common package for Code for Life",
|
|
16
51
|
packages=find_packages(),
|
|
17
52
|
version=version,
|
|
18
53
|
include_package_data=True,
|
|
19
|
-
install_requires=
|
|
20
|
-
"django==4.2.20",
|
|
21
|
-
"django-countries==7.6.1",
|
|
22
|
-
"django-csp==3.8",
|
|
23
|
-
"django-import-export==4.2.0",
|
|
24
|
-
"django-pipeline==3.1.0",
|
|
25
|
-
"django-two-factor-auth==1.17.0",
|
|
26
|
-
"djangorestframework==3.15.2",
|
|
27
|
-
"libsass==0.23.0",
|
|
28
|
-
"more-itertools==8.7.0",
|
|
29
|
-
"pgeocode==0.4.0",
|
|
30
|
-
"pyjwt==2.6.0",
|
|
31
|
-
],
|
|
54
|
+
install_requires=install_requires,
|
|
32
55
|
tests_require=[],
|
|
33
56
|
test_suite="tests",
|
|
34
57
|
classifiers=[
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: codeforlife-portal
|
|
3
|
+
Version: 8.4.5
|
|
4
|
+
Classifier: Programming Language :: Python
|
|
5
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
6
|
+
Classifier: Framework :: Django
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE.md
|
|
9
|
+
Requires-Dist: asgiref==3.8.1; python_version >= "3.8"
|
|
10
|
+
Requires-Dist: certifi==2025.1.31; python_version >= "3.6"
|
|
11
|
+
Requires-Dist: cfl-common==8.4.4
|
|
12
|
+
Requires-Dist: chardet==5.2.0; python_version >= "3.7"
|
|
13
|
+
Requires-Dist: charset-normalizer==3.4.1; python_version >= "3.7"
|
|
14
|
+
Requires-Dist: diff-match-patch==20241021; python_version >= "3.7"
|
|
15
|
+
Requires-Dist: django==4.2.20; python_version >= "3.8"
|
|
16
|
+
Requires-Dist: django-classy-tags==4.1.0; python_version >= "3.8"
|
|
17
|
+
Requires-Dist: django-countries==7.6.1
|
|
18
|
+
Requires-Dist: django-csp==3.8
|
|
19
|
+
Requires-Dist: django-formtools==2.5.1; python_version >= "3.8"
|
|
20
|
+
Requires-Dist: django-import-export==4.2.0; python_version >= "3.9"
|
|
21
|
+
Requires-Dist: django-otp==1.5.4; python_version >= "3.7"
|
|
22
|
+
Requires-Dist: django-phonenumber-field==8.0.0; python_version >= "3.8"
|
|
23
|
+
Requires-Dist: django-pipeline==3.1.0
|
|
24
|
+
Requires-Dist: django-preventconcurrentlogins==0.8.2
|
|
25
|
+
Requires-Dist: django-ratelimit==3.0.1; python_version >= "3.4"
|
|
26
|
+
Requires-Dist: django-recaptcha==4.0.0
|
|
27
|
+
Requires-Dist: django-sekizai==4.1.0; python_version >= "3.8"
|
|
28
|
+
Requires-Dist: django-treebeard==4.7.1; python_version >= "3.8"
|
|
29
|
+
Requires-Dist: django-two-factor-auth==1.17.0; python_version >= "3.8"
|
|
30
|
+
Requires-Dist: djangorestframework==3.15.2; python_version >= "3.8"
|
|
31
|
+
Requires-Dist: idna==3.10; python_version >= "3.6"
|
|
32
|
+
Requires-Dist: importlib-metadata==4.13.0; python_version >= "3.7"
|
|
33
|
+
Requires-Dist: libsass==0.23.0; python_version >= "3.8"
|
|
34
|
+
Requires-Dist: more-itertools==8.7.0; python_version >= "3.5"
|
|
35
|
+
Requires-Dist: numpy==2.2.4; python_version >= "3.10"
|
|
36
|
+
Requires-Dist: pandas==2.2.3; python_version >= "3.9"
|
|
37
|
+
Requires-Dist: pgeocode==0.4.0; python_version >= "3.8"
|
|
38
|
+
Requires-Dist: phonenumbers==8.12.12
|
|
39
|
+
Requires-Dist: pillow==11.1.0; python_version >= "3.9"
|
|
40
|
+
Requires-Dist: pyjwt==2.6.0; python_version >= "3.7"
|
|
41
|
+
Requires-Dist: pypng==0.20220715.0
|
|
42
|
+
Requires-Dist: python-dateutil==2.9.0.post0; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
|
|
43
|
+
Requires-Dist: pytz==2025.1
|
|
44
|
+
Requires-Dist: pyyaml==6.0.2; python_version >= "3.8"
|
|
45
|
+
Requires-Dist: qrcode==7.4.2; python_version >= "3.7"
|
|
46
|
+
Requires-Dist: reportlab==4.2.5; python_version >= "3.7" and python_version < "4"
|
|
47
|
+
Requires-Dist: requests==2.32.3; python_version >= "3.8"
|
|
48
|
+
Requires-Dist: setuptools==74.0.0; python_version >= "3.8"
|
|
49
|
+
Requires-Dist: six==1.17.0; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
|
|
50
|
+
Requires-Dist: sqlparse==0.5.3; python_version >= "3.8"
|
|
51
|
+
Requires-Dist: tablib==3.7.0; python_version >= "3.9"
|
|
52
|
+
Requires-Dist: typing-extensions==4.12.2; python_version >= "3.8"
|
|
53
|
+
Requires-Dist: tzdata==2025.1; python_version >= "2"
|
|
54
|
+
Requires-Dist: urllib3==2.3.0; python_version >= "3.9"
|
|
55
|
+
Requires-Dist: zipp==3.21.0; python_version >= "3.9"
|
|
56
|
+
Provides-Extra: dev
|
|
57
|
+
Requires-Dist: asgiref==3.8.1; python_version >= "3.8" and extra == "dev"
|
|
58
|
+
Requires-Dist: asttokens==3.0.0; python_version >= "3.8" and extra == "dev"
|
|
59
|
+
Requires-Dist: attrs==25.3.0; python_version >= "3.8" and extra == "dev"
|
|
60
|
+
Requires-Dist: black==25.1.0; python_version >= "3.9" and extra == "dev"
|
|
61
|
+
Requires-Dist: certifi==2025.1.31; python_version >= "3.6" and extra == "dev"
|
|
62
|
+
Requires-Dist: cfl-common==8.4.4; extra == "dev"
|
|
63
|
+
Requires-Dist: charset-normalizer==3.4.1; python_version >= "3.7" and extra == "dev"
|
|
64
|
+
Requires-Dist: click==8.1.8; python_version >= "3.7" and extra == "dev"
|
|
65
|
+
Requires-Dist: coverage[toml]==7.7.0; python_version >= "3.9" and extra == "dev"
|
|
66
|
+
Requires-Dist: decorator==5.2.1; python_version >= "3.8" and extra == "dev"
|
|
67
|
+
Requires-Dist: diff-match-patch==20241021; python_version >= "3.7" and extra == "dev"
|
|
68
|
+
Requires-Dist: django==4.2.20; python_version >= "3.8" and extra == "dev"
|
|
69
|
+
Requires-Dist: django-countries==7.6.1; extra == "dev"
|
|
70
|
+
Requires-Dist: django-csp==3.8; extra == "dev"
|
|
71
|
+
Requires-Dist: django-formtools==2.5.1; python_version >= "3.8" and extra == "dev"
|
|
72
|
+
Requires-Dist: django-import-export==4.2.0; python_version >= "3.9" and extra == "dev"
|
|
73
|
+
Requires-Dist: django-otp==1.5.4; python_version >= "3.7" and extra == "dev"
|
|
74
|
+
Requires-Dist: django-phonenumber-field==8.0.0; python_version >= "3.8" and extra == "dev"
|
|
75
|
+
Requires-Dist: django-pipeline==3.1.0; extra == "dev"
|
|
76
|
+
Requires-Dist: django-reverse-js==0.1.7; python_version >= "3.10" and extra == "dev"
|
|
77
|
+
Requires-Dist: django-selenium-clean==1.0.1; extra == "dev"
|
|
78
|
+
Requires-Dist: django-test-migrations==1.4.0; (python_version >= "3.9" and python_version < "4.0") and extra == "dev"
|
|
79
|
+
Requires-Dist: django-two-factor-auth==1.17.0; python_version >= "3.8" and extra == "dev"
|
|
80
|
+
Requires-Dist: djangorestframework==3.15.2; python_version >= "3.8" and extra == "dev"
|
|
81
|
+
Requires-Dist: execnet==2.1.1; python_version >= "3.8" and extra == "dev"
|
|
82
|
+
Requires-Dist: executing==2.2.0; python_version >= "3.8" and extra == "dev"
|
|
83
|
+
Requires-Dist: fastdiff==0.3.0; extra == "dev"
|
|
84
|
+
Requires-Dist: h11==0.14.0; python_version >= "3.7" and extra == "dev"
|
|
85
|
+
Requires-Dist: idna==3.10; python_version >= "3.6" and extra == "dev"
|
|
86
|
+
Requires-Dist: iniconfig==2.0.0; python_version >= "3.7" and extra == "dev"
|
|
87
|
+
Requires-Dist: ipython==9.0.2; python_version >= "3.11" and extra == "dev"
|
|
88
|
+
Requires-Dist: ipython-pygments-lexers==1.1.1; python_version >= "3.8" and extra == "dev"
|
|
89
|
+
Requires-Dist: isort==6.0.1; python_full_version >= "3.9.0" and extra == "dev"
|
|
90
|
+
Requires-Dist: jedi==0.19.2; python_version >= "3.6" and extra == "dev"
|
|
91
|
+
Requires-Dist: libsass==0.23.0; python_version >= "3.8" and extra == "dev"
|
|
92
|
+
Requires-Dist: matplotlib-inline==0.1.7; python_version >= "3.8" and extra == "dev"
|
|
93
|
+
Requires-Dist: more-itertools==8.7.0; python_version >= "3.5" and extra == "dev"
|
|
94
|
+
Requires-Dist: mypy-extensions==1.0.0; python_version >= "3.5" and extra == "dev"
|
|
95
|
+
Requires-Dist: numpy==2.2.4; python_version >= "3.10" and extra == "dev"
|
|
96
|
+
Requires-Dist: outcome==1.3.0.post0; python_version >= "3.7" and extra == "dev"
|
|
97
|
+
Requires-Dist: packaging==24.2; python_version >= "3.8" and extra == "dev"
|
|
98
|
+
Requires-Dist: pandas==2.2.3; python_version >= "3.9" and extra == "dev"
|
|
99
|
+
Requires-Dist: parso==0.8.4; python_version >= "3.6" and extra == "dev"
|
|
100
|
+
Requires-Dist: pathspec==0.12.1; python_version >= "3.8" and extra == "dev"
|
|
101
|
+
Requires-Dist: pexpect==4.9.0; (sys_platform != "win32" and sys_platform != "emscripten") and extra == "dev"
|
|
102
|
+
Requires-Dist: pgeocode==0.4.0; python_version >= "3.8" and extra == "dev"
|
|
103
|
+
Requires-Dist: platformdirs==4.3.6; python_version >= "3.8" and extra == "dev"
|
|
104
|
+
Requires-Dist: pluggy==1.5.0; python_version >= "3.8" and extra == "dev"
|
|
105
|
+
Requires-Dist: prompt-toolkit==3.0.50; python_full_version >= "3.8.0" and extra == "dev"
|
|
106
|
+
Requires-Dist: ptyprocess==0.7.0; extra == "dev"
|
|
107
|
+
Requires-Dist: pure-eval==0.2.3; extra == "dev"
|
|
108
|
+
Requires-Dist: pygments==2.19.1; python_version >= "3.8" and extra == "dev"
|
|
109
|
+
Requires-Dist: pyhamcrest==2.0.2; python_version >= "3.5" and extra == "dev"
|
|
110
|
+
Requires-Dist: pyjwt==2.6.0; python_version >= "3.7" and extra == "dev"
|
|
111
|
+
Requires-Dist: pypdf==5.1.0; python_version >= "3.8" and extra == "dev"
|
|
112
|
+
Requires-Dist: pypng==0.20220715.0; extra == "dev"
|
|
113
|
+
Requires-Dist: pysocks==1.7.1; extra == "dev"
|
|
114
|
+
Requires-Dist: pytest==8.3.5; python_version >= "3.8" and extra == "dev"
|
|
115
|
+
Requires-Dist: pytest-cov==6.0.0; python_version >= "3.9" and extra == "dev"
|
|
116
|
+
Requires-Dist: pytest-django==4.8.0; python_version >= "3.8" and extra == "dev"
|
|
117
|
+
Requires-Dist: pytest-mock==3.14.0; python_version >= "3.8" and extra == "dev"
|
|
118
|
+
Requires-Dist: pytest-order==1.3.0; python_version >= "3.7" and extra == "dev"
|
|
119
|
+
Requires-Dist: pytest-xdist==3.6.1; python_version >= "3.8" and extra == "dev"
|
|
120
|
+
Requires-Dist: python-dateutil==2.9.0.post0; (python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3") and extra == "dev"
|
|
121
|
+
Requires-Dist: pytz==2025.1; extra == "dev"
|
|
122
|
+
Requires-Dist: pyvirtualdisplay==3.0; extra == "dev"
|
|
123
|
+
Requires-Dist: qrcode==7.4.2; python_version >= "3.7" and extra == "dev"
|
|
124
|
+
Requires-Dist: rapid-router==7.4.0; extra == "dev"
|
|
125
|
+
Requires-Dist: requests==2.32.3; python_version >= "3.8" and extra == "dev"
|
|
126
|
+
Requires-Dist: responses==0.18.0; python_version >= "3.7" and extra == "dev"
|
|
127
|
+
Requires-Dist: selenium==4.29.0; python_version >= "3.9" and extra == "dev"
|
|
128
|
+
Requires-Dist: six==1.17.0; (python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3") and extra == "dev"
|
|
129
|
+
Requires-Dist: snapshottest==1.0.0a1; extra == "dev"
|
|
130
|
+
Requires-Dist: sniffio==1.3.1; python_version >= "3.7" and extra == "dev"
|
|
131
|
+
Requires-Dist: sortedcontainers==2.4.0; extra == "dev"
|
|
132
|
+
Requires-Dist: sqlparse==0.5.3; python_version >= "3.8" and extra == "dev"
|
|
133
|
+
Requires-Dist: stack-data==0.6.3; extra == "dev"
|
|
134
|
+
Requires-Dist: tablib==3.7.0; python_version >= "3.9" and extra == "dev"
|
|
135
|
+
Requires-Dist: termcolor==2.5.0; python_version >= "3.9" and extra == "dev"
|
|
136
|
+
Requires-Dist: traitlets==5.14.3; python_version >= "3.8" and extra == "dev"
|
|
137
|
+
Requires-Dist: trio==0.29.0; python_version >= "3.9" and extra == "dev"
|
|
138
|
+
Requires-Dist: trio-websocket==0.12.2; python_version >= "3.8" and extra == "dev"
|
|
139
|
+
Requires-Dist: typing-extensions==4.12.2; python_version >= "3.8" and extra == "dev"
|
|
140
|
+
Requires-Dist: tzdata==2025.1; python_version >= "2" and extra == "dev"
|
|
141
|
+
Requires-Dist: urllib3==2.3.0; python_version >= "3.9" and extra == "dev"
|
|
142
|
+
Requires-Dist: wasmer==1.1.0; extra == "dev"
|
|
143
|
+
Requires-Dist: wasmer-compiler-cranelift==1.1.0; extra == "dev"
|
|
144
|
+
Requires-Dist: wcwidth==0.2.13; extra == "dev"
|
|
145
|
+
Requires-Dist: websocket-client==1.8.0; python_version >= "3.8" and extra == "dev"
|
|
146
|
+
Requires-Dist: wsproto==1.2.0; python_full_version >= "3.7.0" and extra == "dev"
|
|
147
|
+
Dynamic: classifier
|
|
148
|
+
Dynamic: description
|
|
149
|
+
Dynamic: description-content-type
|
|
150
|
+
Dynamic: provides-extra
|
|
151
|
+
Dynamic: requires-dist
|
|
152
|
+
|
|
153
|
+
# Code for Life Portal
|
|
154
|
+
|
|
155
|
+
[](https://github.com/ocadotechnology/codeforlife-portal/actions/workflows/ci.yml)
|
|
156
|
+
[](https://codecov.io/gh/ocadotechnology/codeforlife-portal)
|
|
157
|
+
|
|
158
|
+
## LICENCE
|
|
159
|
+
In accordance with the [Terms of Use](https://www.codeforlife.education/terms#terms)
|
|
160
|
+
of the Code for Life website, all copyright, trademarks, and other
|
|
161
|
+
intellectual property rights in and relating to Code for Life (including all
|
|
162
|
+
content of the Code for Life website, the Rapid Router application, the
|
|
163
|
+
Kurono application, related software (including any drawn and/or animated
|
|
164
|
+
avatars, whether or not such avatars have any modifications) and any other
|
|
165
|
+
games, applications or any other content that we make available from time to
|
|
166
|
+
time) are owned by Ocado Innovation Limited.
|
|
167
|
+
|
|
168
|
+
The source code of the Code for Life portal, the Rapid Router application
|
|
169
|
+
and the Kurono/aimmo application are [licensed under the GNU Affero General
|
|
170
|
+
Public License](https://github.com/ocadotechnology/codeforlife-workspace/blob/main/LICENSE.md).
|
|
171
|
+
All other assets including images, logos, sounds etc., are not covered by
|
|
172
|
+
this licence and no-one may copy, modify, distribute, show in public or
|
|
173
|
+
create any derivative work from these assets.
|
|
174
|
+
|
|
175
|
+
## Code for Life
|
|
176
|
+
|
|
177
|
+
[Code for Life](https://www.codeforlife.education/) has been developed by Ocado Technology as a **free, open-source** project to inspire the next generation of computer scientists and to help teachers deliver the computing curriculum.
|
|
178
|
+
|
|
179
|
+
This repository hosts the source code of the [main website](https://www.codeforlife.education/), which includes the registration, log in, teacher and student dashboards, the teaching materials, etc.
|
|
180
|
+
|
|
181
|
+
We are open to contributors from anywhere around the world. Please read ahead if you'd like to get involved.
|
|
182
|
+
|
|
183
|
+
## To get started
|
|
184
|
+
|
|
185
|
+
- [Developer Guide](https://docs.codeforlife.education/developer-guide)
|
|
186
|
+
|
|
187
|
+
- [Good First Issues](https://github.com/ocadotechnology/codeforlife-portal/contribute)
|
|
188
|
+
|
|
189
|
+
- [How to set up your work environment](https://docs.codeforlife.education/git/common-setup)
|
|
190
|
+
|
|
191
|
+
- [Testing](https://docs.codeforlife.education/git/testing)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cfl_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
cfl_common/setup.py,sha256=
|
|
2
|
+
cfl_common/setup.py,sha256=zjoRk8QkIeb4ZfRW6BeozmtX_6Fme913s9ntoUJrpBM,1720
|
|
3
3
|
cfl_common/common/__init__.py,sha256=XlncBOpKp_gekbKH7Y_i6yu1qy5tJc3Y8sn8cDy-Vgk,48
|
|
4
4
|
cfl_common/common/app_settings.py,sha256=Bw1DXkZpNIdwUJ-cIOjZnngH5_NbMXC0koW7NgQ0pKY,2495
|
|
5
5
|
cfl_common/common/apps.py,sha256=49UXZ3bSkFKvIEOL4zM7y1sAhccQJyRtsoOg5XVd_8Y,129
|
|
@@ -106,18 +106,17 @@ deploy/static/apple-touch-icon.png,sha256=QjNQ8jDKI4BpfMMsrs2t3H0fXHa5KHV45fK_VS
|
|
|
106
106
|
deploy/static/robots.txt,sha256=5cS4RITuQhbpNzvpk4AyDCXdlIBfmfCoBYRvCHY2VT8,24
|
|
107
107
|
deploy/templates/deploy/csrf_failure.html,sha256=-pBRPn4Y7nUdYHGpTHCokT9Boi-isuwuivF8V2K1SgM,412
|
|
108
108
|
example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
109
|
-
example_project/
|
|
110
|
-
example_project/
|
|
111
|
-
example_project/settings.py,sha256=pHQDI_A_yXV3u1efj689tc-vPI6nBFma4gMMPBzj0aI,5652
|
|
109
|
+
example_project/portal_test_settings.py,sha256=fSjkcEWgW1bQW7wTPpiWzytPfKnlzDnI9ET6InTXI98,7342
|
|
110
|
+
example_project/settings.py,sha256=b680hIHT1mtrvBjOQSli1AkDo0k3anZYHfsKhFheKlM,5684
|
|
112
111
|
example_project/urls.py,sha256=FUTzHPlUS1O5kqMHjL5V4L552N2ln7uTDXcw9wjKUto,422
|
|
113
112
|
example_project/wsgi.py,sha256=U1W6WzZxZaIdYZ5tks7w9fqp5WS5qvn2iThsVcskrWw,829
|
|
114
|
-
portal/__init__.py,sha256=
|
|
113
|
+
portal/__init__.py,sha256=HEjuWo6NlDEiT2fPl7dfLzguQSK93scFhEekPFfHfaU,22
|
|
115
114
|
portal/admin.py,sha256=RKJizTF6dPJKmGPZw7nZUM0X8jkiTjgyKhLQxtvHJ0I,6148
|
|
116
115
|
portal/app_settings.py,sha256=DhWLQOwM0zVOXE3O5TNKbMM9K6agfLuCsHOdr1J7xEI,651
|
|
117
116
|
portal/backends.py,sha256=2Dss6_WoQwPuDzJUF1yEaTQTNG4eUrD12ujJQ5cp5Tc,812
|
|
118
117
|
portal/beta.py,sha256=0TCC-9_KZoM1nuzJ9FiuKR5n9JITdMYenHGQtRvn9UU,255
|
|
119
118
|
portal/context_processors.py,sha256=1TrUZqnMqGa5f7ERph9EpBqojSMJvOrcpnJzTdeCLDI,133
|
|
120
|
-
portal/handlers.py,sha256=
|
|
119
|
+
portal/handlers.py,sha256=TSaTIsEi-q8cwoYezw4NgVKyFy1UZkqREMipeSKA_KQ,421
|
|
121
120
|
portal/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
122
121
|
portal/urls.py,sha256=BWhRI-9rGEydbasI0UCtYYASqHbyUXokfepNWty7cQg,18218
|
|
123
122
|
portal/wsgi.py,sha256=3yRcNxBQG30NhzrVi93bX-DrbXtsIQBc70HiW5wbOyE,401
|
|
@@ -529,27 +528,27 @@ portal/templatetags/headline_tags.py,sha256=1KHOjcVZZTLfWnxpFAUoHhIndd3RbuD1hby0
|
|
|
529
528
|
portal/templatetags/hero_card_tags.py,sha256=nwTyPvjuaoaeG14ck8q1nbb-wSsTd6Grz3DOUCPEOMc,939
|
|
530
529
|
portal/templatetags/table_tags.py,sha256=eU8kkBky_Ouq_61-6-YVu5_1zH941KRJD_Wvs_TMXqE,448
|
|
531
530
|
portal/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
532
|
-
portal/tests/base_test.py,sha256=
|
|
531
|
+
portal/tests/base_test.py,sha256=SxoX3UMZ8rTDHqKJFUDaUpTvPS2w7qUdOXviv6wtS1o,1649
|
|
533
532
|
portal/tests/conftest.py,sha256=Rk4SOaFS22bFwNPRDOQHeNsm0We5BdEI52NTcfSlv1g,1297
|
|
534
533
|
portal/tests/selenium_test_case.py,sha256=eWUF_5SqkI178bkay5SUDa06r0QTIKUUT8jTAhrdbmk,469
|
|
535
534
|
portal/tests/test_2FA.py,sha256=0N4C9Ab3TvO9W__oQLCo-fLDH1Ho3CiGGsSg-2TiZUE,3597
|
|
536
535
|
portal/tests/test_admin.py,sha256=AM2dgv8j9m4L-SDO-sMA9tQvQH9GwRBrlwRG9OgqtfI,1451
|
|
537
536
|
portal/tests/test_api.py,sha256=Yo5s_nEGOoG35jA39yZ6nuDOUZvuCZ8o8o8XhZos61w,13819
|
|
538
537
|
portal/tests/test_captcha_forms.py,sha256=Yn_VYO_6jbq6AeKeLcv-YFL1YwXZpU0C3y7SK8fRUm4,1033
|
|
539
|
-
portal/tests/test_class.py,sha256=
|
|
538
|
+
portal/tests/test_class.py,sha256=KBDHL4JdjiifY1l3-TLxOVtaCHtZrpWXU5y7xGtrq7s,18124
|
|
540
539
|
portal/tests/test_emails.py,sha256=pLr06j3uMBxP1raoZQWzUTBVFvsEDFtUh85J8OnqCwE,9238
|
|
541
540
|
portal/tests/test_global_forms.py,sha256=GIm_oSN4VsfaO--E2SMRu8CwVraan0UBj-_LE_tu8w0,833
|
|
542
541
|
portal/tests/test_helper_methods.py,sha256=-SQCDZm2XUtyXGEp0CHIb_SSC9CPD-XOSnpnY8QclHk,890
|
|
543
542
|
portal/tests/test_independent_student.py,sha256=NrRjTEr6V4WXpCE74N8LYNVocvLSvddkjuo3dYpfAZc,27245
|
|
544
|
-
portal/tests/test_invite_teacher.py,sha256=
|
|
543
|
+
portal/tests/test_invite_teacher.py,sha256=QKHWVzyd5OLpTOQ0j8p9ZF6DSCWPHcWHZwCpNTFG_6o,12320
|
|
545
544
|
portal/tests/test_middleware.py,sha256=HV7AdgWyvgrtPyDMw3np5YDbUstFpKif7Qqw-e8gs5s,8258
|
|
546
545
|
portal/tests/test_organisation.py,sha256=kCMUNzLN6EzaMUBcFkqXwnqLGgOuQxQWIHHt63nhqBs,7574
|
|
547
546
|
portal/tests/test_partials.py,sha256=cSLNLjdsriROjPxkZlM3HKD3CSKDuKgpaDIdL3fPyBs,1794
|
|
548
547
|
portal/tests/test_ratelimit.py,sha256=XWq1A9XgRrlcMHibGoJ0kc4gLc5U_u5UhKHjthxCfYA,19376
|
|
549
548
|
portal/tests/test_school_student.py,sha256=bFZwY4twaFHQLp0cltMq8cLNDZGgCHTZBCZHK0JcV8s,8604
|
|
550
549
|
portal/tests/test_security.py,sha256=FGrlRfnzi-Xx2_bn4fTZlYORKm7w_GhGkD3havvplwc,3239
|
|
551
|
-
portal/tests/test_teacher.py,sha256=
|
|
552
|
-
portal/tests/test_teacher_student.py,sha256=
|
|
550
|
+
portal/tests/test_teacher.py,sha256=y4h5Bt7Xlglh9pylSl1jOOl5tl6rqHTewddBjB3RzRA,29171
|
|
551
|
+
portal/tests/test_teacher_student.py,sha256=4Di29Lsymbmh6WxZ0FYpqZ4fMN8NqNMTXXOsii1dfW4,21624
|
|
553
552
|
portal/tests/test_views.py,sha256=Hv0rVOLmYivMqzJK1AQeRJ85YHTCRrlr12hcf1B83Bk,47566
|
|
554
553
|
portal/tests/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
555
554
|
portal/tests/migrations/test_migration_make_portaladmin_teacher.py,sha256=ekMRb6cU97oT0k9gCKW7IUB7oPuGmv4uWJCqInQN7x8,2589
|
|
@@ -604,7 +603,7 @@ portal/tests/snapshots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
604
603
|
portal/tests/snapshots/snap_test_partials.py,sha256=ErYrQezzALwvP-vrOwOVyITG67WEUpcroSdYs414fek,2481
|
|
605
604
|
portal/tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
606
605
|
portal/tests/utils/classes.py,sha256=MEwGTKjPRYdWiVL1V6l28NpdvRUa06yRa6PPcHT6Trc,223
|
|
607
|
-
portal/tests/utils/messages.py,sha256=
|
|
606
|
+
portal/tests/utils/messages.py,sha256=tUGzJzhZwvzmO9H3CPxpWUfi2aXTpAWfkDQ2u2T3LbA,1770
|
|
608
607
|
portal/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
609
608
|
portal/views/about.py,sha256=-muXy17UhxCSKkjnMAkSLXiCvT_pBPlf2ykTYr794dI,443
|
|
610
609
|
portal/views/admin.py,sha256=4Xt3zEyQH7sUwQSrwuRtoCodWidjOzd7gJUwWU96pXY,957
|
|
@@ -634,8 +633,8 @@ portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
|
634
633
|
portal/views/two_factor/core.py,sha256=Lk32z2SN2Pg0rRkK-N-LXMvXC1kKKsH3l692kiSDQ4E,964
|
|
635
634
|
portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
|
|
636
635
|
portal/views/two_factor/profile.py,sha256=SHSg_xHccE5PtD-OfuOkYhREYz_er4bj5ro1RjJ88Yw,393
|
|
637
|
-
codeforlife_portal-8.4.
|
|
638
|
-
codeforlife_portal-8.4.
|
|
639
|
-
codeforlife_portal-8.4.
|
|
640
|
-
codeforlife_portal-8.4.
|
|
641
|
-
codeforlife_portal-8.4.
|
|
636
|
+
codeforlife_portal-8.4.5.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
|
|
637
|
+
codeforlife_portal-8.4.5.dist-info/METADATA,sha256=UnEWMYXHfi3KhoyZodjx1fhB4-PUvkNbpazuRSRXgdw,12065
|
|
638
|
+
codeforlife_portal-8.4.5.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
|
|
639
|
+
codeforlife_portal-8.4.5.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
|
|
640
|
+
codeforlife_portal-8.4.5.dist-info/RECORD,,
|
|
@@ -6,19 +6,19 @@ from selenium import webdriver
|
|
|
6
6
|
|
|
7
7
|
DEBUG = True
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
headless_firefox_options = webdriver.FirefoxOptions()
|
|
10
|
+
headless_firefox_options.add_argument("--headless")
|
|
11
|
+
headless_firefox_options.add_argument("--window-size=1920,1080")
|
|
12
|
+
headless_firefox_options.add_argument("--start-maximized")
|
|
13
|
+
headless_firefox_options.add_argument("--disable-gpu")
|
|
14
|
+
headless_firefox_options.add_argument("--no-sandbox")
|
|
15
|
+
headless_firefox_options.add_argument("--disable-extensions")
|
|
16
|
+
headless_firefox_options.add_argument("--disable-dev-shm-usage")
|
|
17
17
|
|
|
18
18
|
SELENIUM_WEBDRIVERS = {
|
|
19
|
-
"default": {"callable": webdriver.
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"default": {"callable": webdriver.Firefox, "args": (), "kwargs": {}},
|
|
20
|
+
"chrome": {"callable": webdriver.Chrome, "args": (), "kwargs": {}},
|
|
21
|
+
"firefox-headless": {"callable": webdriver.Firefox, "args": (), "kwargs": {"options": headless_firefox_options}},
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
SELENIUM_WIDTHS = [1624]
|
|
@@ -74,7 +74,7 @@ STATIC_URL = "/static/"
|
|
|
74
74
|
STATICFILES_DIRS = [os.path.join(BASE_DIR, "portal/static")]
|
|
75
75
|
MEDIA_ROOT = os.path.join(STATIC_ROOT, "email_media/")
|
|
76
76
|
|
|
77
|
-
WSGI_APPLICATION = "wsgi.application"
|
|
77
|
+
WSGI_APPLICATION = "example_project.wsgi.application"
|
|
78
78
|
|
|
79
79
|
LOGIN_REDIRECT_URL = "/teach/dashboard/"
|
|
80
80
|
|
example_project/settings.py
CHANGED
|
@@ -26,11 +26,11 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "portal/static")]
|
|
|
26
26
|
MEDIA_ROOT = os.path.join(STATIC_ROOT, "email_media/")
|
|
27
27
|
SECRET_KEY = "not-a-secret"
|
|
28
28
|
|
|
29
|
-
ROOT_URLCONF = "urls"
|
|
29
|
+
ROOT_URLCONF = "example_project.urls"
|
|
30
30
|
|
|
31
31
|
ALLOWED_HOSTS = ["*"]
|
|
32
32
|
|
|
33
|
-
WSGI_APPLICATION = "wsgi.application"
|
|
33
|
+
WSGI_APPLICATION = "example_project.wsgi.application"
|
|
34
34
|
|
|
35
35
|
LOGIN_REDIRECT_URL = "/teach/dashboard/"
|
|
36
36
|
|
portal/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "8.4.
|
|
1
|
+
__version__ = "8.4.5"
|
portal/handlers.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
from common.utils import two_factor_cache_key
|
|
1
2
|
from django.core.cache import cache
|
|
2
3
|
from django.db.models.signals import post_save, pre_delete
|
|
3
4
|
from django.dispatch import receiver
|
|
4
5
|
from django_otp.models import Device
|
|
5
6
|
|
|
6
|
-
from common.utils import two_factor_cache_key
|
|
7
|
-
|
|
8
7
|
|
|
9
8
|
@receiver([post_save, pre_delete])
|
|
10
9
|
def clear_two_factor_cache(sender, **kwargs):
|
portal/tests/base_test.py
CHANGED
|
@@ -5,30 +5,9 @@ from django.contrib.sites.models import Site
|
|
|
5
5
|
from django.urls import reverse
|
|
6
6
|
|
|
7
7
|
from deploy import captcha
|
|
8
|
-
|
|
9
|
-
# Uncomment to use FireFox
|
|
10
|
-
# master_browser = webdriver.Firefox()
|
|
11
8
|
from portal.tests.pageObjects.portal.home_page import HomePage
|
|
12
9
|
from .selenium_test_case import SeleniumTestCase
|
|
13
10
|
|
|
14
|
-
from selenium.webdriver.common.action_chains import ActionChains
|
|
15
|
-
from selenium.webdriver.common.by import By
|
|
16
|
-
from selenium.webdriver.support.ui import WebDriverWait
|
|
17
|
-
from selenium.webdriver.support import expected_conditions as EC
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def button_click_handler(page, self, button_element):
|
|
21
|
-
current_button = WebDriverWait(self.selenium, 20).until(EC.presence_of_element_located((By.ID, button_element)))
|
|
22
|
-
ActionChains(self.selenium).move_to_element(current_button).click(current_button).perform()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def click_buttons_by_id(page, self, button_ids):
|
|
26
|
-
if isinstance(button_ids, str):
|
|
27
|
-
button_click_handler(page, self, button_ids)
|
|
28
|
-
else:
|
|
29
|
-
for button_id in button_ids:
|
|
30
|
-
button_click_handler(page, self, button_id)
|
|
31
|
-
|
|
32
11
|
|
|
33
12
|
class BaseTest(SeleniumTestCase):
|
|
34
13
|
@classmethod
|
portal/tests/test_class.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import absolute_import
|
|
2
2
|
|
|
3
|
+
import time
|
|
3
4
|
from datetime import datetime, timedelta
|
|
4
5
|
|
|
5
6
|
from common.models import Class, DailyActivity, Teacher
|
|
@@ -550,6 +551,8 @@ class TestClassFrontend(BaseTest):
|
|
|
550
551
|
|
|
551
552
|
page = page.create_class(class_name, False)
|
|
552
553
|
|
|
554
|
+
time.sleep(1)
|
|
555
|
+
|
|
553
556
|
assert page.was_form_invalid(
|
|
554
557
|
"form-create-class", "Class name may only contain letters, numbers, dashes, underscores, and spaces."
|
|
555
558
|
)
|
|
@@ -16,7 +16,7 @@ from selenium.webdriver.common.by import By
|
|
|
16
16
|
from selenium.webdriver.support import expected_conditions as EC
|
|
17
17
|
from selenium.webdriver.support.wait import WebDriverWait
|
|
18
18
|
|
|
19
|
-
from portal.tests.base_test import BaseTest
|
|
19
|
+
from portal.tests.base_test import BaseTest
|
|
20
20
|
|
|
21
21
|
FADE_TIME = 0.9
|
|
22
22
|
WAIT_TIME = 15
|
|
@@ -185,7 +185,7 @@ class TestTeacherInviteActions(BaseTest):
|
|
|
185
185
|
field.send_keys(invite_data[key])
|
|
186
186
|
|
|
187
187
|
# check if invite text for a user has been generated
|
|
188
|
-
|
|
188
|
+
page.browser.find_element(By.ID, "invite_teacher_button").click()
|
|
189
189
|
banner = page.browser.find_element(By.ID, "messages")
|
|
190
190
|
assert (
|
|
191
191
|
f"You have invited {invite_data['teacher_first_name']} {invite_data['teacher_last_name']} to your school."
|
|
@@ -194,14 +194,17 @@ class TestTeacherInviteActions(BaseTest):
|
|
|
194
194
|
|
|
195
195
|
# check if popup message appears and if the invite is changed to admin
|
|
196
196
|
sleep(1) # this HAS to be there because of the animation :/
|
|
197
|
-
|
|
197
|
+
page.browser.find_element(By.ID, "make_admin_button_invite").click()
|
|
198
|
+
sleep(1)
|
|
199
|
+
page.browser.find_element(By.ID, "add_admin_button").click()
|
|
200
|
+
|
|
198
201
|
invite = SchoolTeacherInvitation.objects.filter(invited_teacher_first_name="Adam")[0]
|
|
199
202
|
assert invite.invited_teacher_is_admin
|
|
200
203
|
banner = page.browser.find_element(By.ID, "messages")
|
|
201
204
|
assert "Administrator invite status has been given successfully" in banner.text
|
|
202
205
|
|
|
203
206
|
# revoke admin
|
|
204
|
-
|
|
207
|
+
page.browser.find_element(By.ID, "make_non_admin_button_invite").click()
|
|
205
208
|
|
|
206
209
|
banner = page.browser.find_element(By.ID, "messages")
|
|
207
210
|
assert "Administrator invite status has been revoked successfully" in banner.text
|
|
@@ -220,8 +223,7 @@ class TestTeacherInviteActions(BaseTest):
|
|
|
220
223
|
for key in invite_data.keys():
|
|
221
224
|
field = page.browser.find_element(By.NAME, key)
|
|
222
225
|
field.send_keys(invite_data[key])
|
|
223
|
-
|
|
224
|
-
invite_button.click()
|
|
226
|
+
page.browser.find_element(By.NAME, "invite_teacher_button").click()
|
|
225
227
|
|
|
226
228
|
# check object was created
|
|
227
229
|
invite_queryset = SchoolTeacherInvitation.objects.filter(invited_teacher_first_name="Adam")
|
|
@@ -251,7 +253,7 @@ class TestTeacherInviteActions(BaseTest):
|
|
|
251
253
|
field = page.browser.find_element(By.NAME, key)
|
|
252
254
|
field.send_keys(invite_data[key])
|
|
253
255
|
|
|
254
|
-
|
|
256
|
+
page.browser.find_element(By.ID, "invite_teacher_button").click()
|
|
255
257
|
|
|
256
258
|
banner = page.browser.find_element(By.XPATH, '//*[@id="messages"]/div/div/div/div/div/p')
|
|
257
259
|
assert (
|
|
@@ -260,7 +262,7 @@ class TestTeacherInviteActions(BaseTest):
|
|
|
260
262
|
)
|
|
261
263
|
|
|
262
264
|
# resend an invite
|
|
263
|
-
|
|
265
|
+
page.browser.find_element(By.ID, "resend-invite").click()
|
|
264
266
|
|
|
265
267
|
# check if invite was updated by 30 days (used 29 for rounding errors)
|
|
266
268
|
new_invite_expiry = SchoolTeacherInvitation.objects.filter(invited_teacher_first_name="Adam")[0].expiry
|
portal/tests/test_teacher.py
CHANGED
|
@@ -33,9 +33,7 @@ from selenium.webdriver.support import expected_conditions as EC
|
|
|
33
33
|
from selenium.webdriver.support.wait import WebDriverWait
|
|
34
34
|
|
|
35
35
|
from portal.forms.error_messages import INVALID_LOGIN_MESSAGE
|
|
36
|
-
from portal.tests.base_test import click_buttons_by_id
|
|
37
36
|
from portal.tests.test_invite_teacher import WAIT_TIME
|
|
38
|
-
|
|
39
37
|
from .base_test import BaseTest
|
|
40
38
|
from .pageObjects.portal.home_page import HomePage
|
|
41
39
|
from .utils.messages import (
|
|
@@ -644,25 +642,13 @@ class TestTeacherFrontend(BaseTest):
|
|
|
644
642
|
field = page.browser.find_element(By.NAME, key)
|
|
645
643
|
field.send_keys(invite_data[key])
|
|
646
644
|
|
|
647
|
-
|
|
648
|
-
By.NAME, "invite_teacher_button"
|
|
649
|
-
)
|
|
650
|
-
invite_button.click()
|
|
651
|
-
|
|
645
|
+
page.browser.find_element(By.NAME, "invite_teacher_button").click()
|
|
652
646
|
# Once invite sent test the make admin button
|
|
653
|
-
""
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
)
|
|
657
|
-
|
|
658
|
-
"""
|
|
659
|
-
|
|
660
|
-
button_ids = [
|
|
661
|
-
"make_admin_button_invite",
|
|
662
|
-
"cancel_admin_popup_button",
|
|
663
|
-
"delete-invite",
|
|
664
|
-
]
|
|
665
|
-
click_buttons_by_id(page, self, button_ids)
|
|
647
|
+
page.browser.find_element(By.ID, "make_admin_button_invite").click()
|
|
648
|
+
time.sleep(1)
|
|
649
|
+
page.browser.find_element(By.ID, "cancel_admin_popup_button").click()
|
|
650
|
+
time.sleep(1)
|
|
651
|
+
page.browser.find_element(By.ID, "delete-invite").click()
|
|
666
652
|
|
|
667
653
|
# Delete the invite and check if the form invite with
|
|
668
654
|
# admin checked also makes a popup
|
|
@@ -673,17 +659,16 @@ class TestTeacherFrontend(BaseTest):
|
|
|
673
659
|
checkbox = page.browser.find_element(By.NAME, "make_admin_ticked")
|
|
674
660
|
checkbox.click()
|
|
675
661
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
662
|
+
page.browser.find_element(By.ID, "invite_teacher_button").click()
|
|
663
|
+
time.sleep(1)
|
|
664
|
+
page.browser.find_element(By.ID, "cancel_admin_popup_button").click()
|
|
679
665
|
|
|
680
666
|
# Non admin teacher joined - make admin should also make a popup
|
|
681
|
-
|
|
682
667
|
join_teacher_to_organisation(joining_email, school.name)
|
|
683
668
|
|
|
684
669
|
# refresh the page and scroll to the buttons
|
|
685
|
-
page.browser.
|
|
686
|
-
|
|
670
|
+
page.browser.find_element(By.CSS_SELECTOR, ".logo").click()
|
|
671
|
+
page.browser.find_element(By.ID, "make_admin_button").click()
|
|
687
672
|
|
|
688
673
|
assert page.element_exists((By.CLASS_NAME, "popup-box__msg"))
|
|
689
674
|
|
|
@@ -795,6 +780,8 @@ class TestTeacherFrontend(BaseTest):
|
|
|
795
780
|
.complete_setup()
|
|
796
781
|
)
|
|
797
782
|
|
|
783
|
+
time.sleep(1)
|
|
784
|
+
|
|
798
785
|
assert page.has_onboarding_complete_popup()
|
|
799
786
|
|
|
800
787
|
def get_to_forgotten_password_page(self):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import absolute_import
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import time
|
|
4
5
|
from unittest.mock import Mock, patch
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
@@ -24,7 +25,6 @@ from selenium.webdriver.common.alert import Alert
|
|
|
24
25
|
from selenium.webdriver.common.by import By
|
|
25
26
|
|
|
26
27
|
from portal.tests.pageObjects.portal.home_page import HomePage
|
|
27
|
-
|
|
28
28
|
from .base_test import BaseTest
|
|
29
29
|
|
|
30
30
|
|
|
@@ -146,6 +146,8 @@ class TestTeacherStudentFrontend(BaseTest):
|
|
|
146
146
|
.import_students_from_csv("test_students_names_no_name.csv")
|
|
147
147
|
)
|
|
148
148
|
|
|
149
|
+
time.sleep(1)
|
|
150
|
+
|
|
149
151
|
alert = Alert(page.browser)
|
|
150
152
|
assert alert.text == "'Name' column not found in CSV file."
|
|
151
153
|
alert.dismiss()
|
|
@@ -184,6 +186,8 @@ class TestTeacherStudentFrontend(BaseTest):
|
|
|
184
186
|
.import_students_from_csv("test_students_names_no_name.csv")
|
|
185
187
|
)
|
|
186
188
|
|
|
189
|
+
time.sleep(1)
|
|
190
|
+
|
|
187
191
|
alert = Alert(page.browser)
|
|
188
192
|
assert alert.text == "'Name' column not found in CSV file."
|
|
189
193
|
alert.dismiss()
|
portal/tests/utils/messages.py
CHANGED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: codeforlife-portal
|
|
3
|
-
Version: 8.4.4
|
|
4
|
-
Classifier: Programming Language :: Python
|
|
5
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
6
|
-
Classifier: Framework :: Django
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
License-File: LICENSE.md
|
|
9
|
-
Requires-Dist: django-recaptcha==4.0.0
|
|
10
|
-
Requires-Dist: pyyaml==6.0.2
|
|
11
|
-
Requires-Dist: importlib-metadata==4.13.0
|
|
12
|
-
Requires-Dist: reportlab==4.2.5
|
|
13
|
-
Requires-Dist: django-formtools==2.5.1
|
|
14
|
-
Requires-Dist: django-otp==1.5.4
|
|
15
|
-
Requires-Dist: requests==2.32.2
|
|
16
|
-
Requires-Dist: django-treebeard==4.7.1
|
|
17
|
-
Requires-Dist: django-sekizai==4.1.0
|
|
18
|
-
Requires-Dist: django-classy-tags==4.1.0
|
|
19
|
-
Requires-Dist: phonenumbers==8.12.12
|
|
20
|
-
Requires-Dist: django-ratelimit==3.0.1
|
|
21
|
-
Requires-Dist: django-preventconcurrentlogins==0.8.2
|
|
22
|
-
Requires-Dist: setuptools==74.0.0
|
|
23
|
-
|
|
24
|
-
# Code for Life Portal
|
|
25
|
-
|
|
26
|
-
[](https://github.com/ocadotechnology/codeforlife-portal/actions/workflows/ci.yml)
|
|
27
|
-
[](https://codecov.io/gh/ocadotechnology/codeforlife-portal)
|
|
28
|
-
|
|
29
|
-
## LICENCE
|
|
30
|
-
In accordance with the [Terms of Use](https://www.codeforlife.education/terms#terms)
|
|
31
|
-
of the Code for Life website, all copyright, trademarks, and other
|
|
32
|
-
intellectual property rights in and relating to Code for Life (including all
|
|
33
|
-
content of the Code for Life website, the Rapid Router application, the
|
|
34
|
-
Kurono application, related software (including any drawn and/or animated
|
|
35
|
-
avatars, whether or not such avatars have any modifications) and any other
|
|
36
|
-
games, applications or any other content that we make available from time to
|
|
37
|
-
time) are owned by Ocado Innovation Limited.
|
|
38
|
-
|
|
39
|
-
The source code of the Code for Life portal, the Rapid Router application
|
|
40
|
-
and the Kurono/aimmo application are [licensed under the GNU Affero General
|
|
41
|
-
Public License](https://github.com/ocadotechnology/codeforlife-workspace/blob/main/LICENSE.md).
|
|
42
|
-
All other assets including images, logos, sounds etc., are not covered by
|
|
43
|
-
this licence and no-one may copy, modify, distribute, show in public or
|
|
44
|
-
create any derivative work from these assets.
|
|
45
|
-
|
|
46
|
-
## Code for Life
|
|
47
|
-
|
|
48
|
-
[Code for Life](https://www.codeforlife.education/) has been developed by Ocado Technology as a **free, open-source** project to inspire the next generation of computer scientists and to help teachers deliver the computing curriculum.
|
|
49
|
-
|
|
50
|
-
This repository hosts the source code of the [main website](https://www.codeforlife.education/), which includes the registration, log in, teacher and student dashboards, the teaching materials, etc.
|
|
51
|
-
|
|
52
|
-
We are open to contributors from anywhere around the world. Please read ahead if you'd like to get involved.
|
|
53
|
-
|
|
54
|
-
## To get started
|
|
55
|
-
|
|
56
|
-
- [Developer Guide](https://docs.codeforlife.education/developer-guide)
|
|
57
|
-
|
|
58
|
-
- [Good First Issues](https://github.com/ocadotechnology/codeforlife-portal/contribute)
|
|
59
|
-
|
|
60
|
-
- [How to set up your work environment](https://docs.codeforlife.education/git/common-setup)
|
|
61
|
-
|
|
62
|
-
- [Testing](https://docs.codeforlife.education/git/testing)
|
example_project/manage.py
DELETED
|
File without changes
|
|
File without changes
|