codeforlife-portal 6.46.1__py2.py3-none-any.whl → 7.0.0__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 (67) hide show
  1. cfl_common/common/csp_config.py +0 -2
  2. cfl_common/common/migrations/0005_add_worksheets.py +1 -5
  3. cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +1 -5
  4. cfl_common/common/migrations/0054_delete_aimmo_models.py +20 -0
  5. cfl_common/common/models.py +0 -25
  6. {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/METADATA +3 -4
  7. {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/RECORD +42 -66
  8. example_project/portal_test_settings.py +0 -1
  9. example_project/settings.py +0 -1
  10. example_project/urls.py +0 -2
  11. portal/__init__.py +1 -1
  12. portal/static/portal/sass/partials/_banners.scss +0 -177
  13. portal/static/portal/sass/partials/_buttons.scss +0 -12
  14. portal/static/portal/sass/partials/_grids.scss +0 -53
  15. portal/static/portal/sass/partials/_text.scss +1 -10
  16. portal/static/portal/sass/styles.scss +0 -1
  17. portal/strings/play.py +1 -2
  18. portal/strings/teacher_resources.py +0 -10
  19. portal/templates/portal/about.html +91 -60
  20. portal/templates/portal/contribute.html +45 -49
  21. portal/templates/portal/partials/header.html +0 -12
  22. portal/templates/portal/play/independent_student_dashboard.html +12 -25
  23. portal/templates/portal/play/student_dashboard.html +16 -34
  24. portal/templates/portal/play.html +36 -49
  25. portal/templates/portal/register.html +1 -1
  26. portal/templates/portal/teach.html +37 -55
  27. portal/templates/portal/ten_year_map.html +9 -9
  28. portal/templatetags/app_tags.py +13 -28
  29. portal/tests/conftest.py +4 -16
  30. portal/tests/pageObjects/portal/base_page.py +20 -20
  31. portal/tests/snapshots/snap_test_partials.py +0 -452
  32. portal/tests/test_class.py +213 -45
  33. portal/tests/test_independent_student.py +0 -9
  34. portal/tests/test_partials.py +6 -56
  35. portal/tests/test_teacher.py +221 -285
  36. portal/tests/test_views.py +156 -73
  37. portal/urls.py +23 -20
  38. portal/views/student/play.py +36 -25
  39. portal/views/teacher/teach.py +0 -5
  40. cfl_common/common/tests/test_migration_aimmo_characters.py +0 -29
  41. portal/forms/add_game.py +0 -29
  42. portal/static/portal/img/kurono_hero.jpg +0 -0
  43. portal/static/portal/img/kurono_landing_hero.png +0 -0
  44. portal/static/portal/img/kurono_logo.svg +0 -1
  45. portal/static/portal/img/kurono_logo_grey_background.svg +0 -1
  46. portal/static/portal/img/kurono_logo_mark.svg +0 -1
  47. portal/static/portal/img/kurono_resources_hero.jpg +0 -0
  48. portal/static/portal/img/kurono_story.png +0 -0
  49. portal/static/portal/img/thumbnail_educate_kurono.png +0 -0
  50. portal/static/portal/img/thumbnail_kurono_resources.png +0 -0
  51. portal/static/portal/img/thumbnail_play_kurono.png +0 -0
  52. portal/static/portal/js/aimmoGame.js +0 -106
  53. portal/static/portal/sass/partials/_videos.scss +0 -10
  54. portal/static/portal/video/aimmo_play_now_background_video.mp4 +0 -0
  55. portal/strings/student_aimmo_dashboard.py +0 -6
  56. portal/templates/portal/partials/aimmo_games_table.html +0 -89
  57. portal/templates/portal/play/student_aimmo_dashboard.html +0 -46
  58. portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
  59. portal/templatetags/character_list_tags.py +0 -16
  60. portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
  61. portal/tests/test_aimmo_dashboards.py +0 -206
  62. portal/tests/utils/aimmo_games.py +0 -30
  63. portal/views/aimmo/__init__.py +0 -0
  64. portal/views/aimmo/dashboard.py +0 -105
  65. {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/LICENSE.md +0 -0
  66. {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/WHEEL +0 -0
  67. {codeforlife_portal-6.46.1.dist-info → codeforlife_portal-7.0.0.dist-info}/top_level.txt +0 -0
@@ -1,49 +0,0 @@
1
- from __future__ import absolute_import
2
-
3
- from selenium.webdriver.common.by import By
4
-
5
- from .base_page import BasePage
6
-
7
-
8
- class KuronoTeacherDashboardPage(BasePage):
9
- def __init__(self, browser):
10
- super(KuronoTeacherDashboardPage, self).__init__(browser)
11
-
12
- assert self.on_correct_page("kurono_teacher_dashboard_page")
13
-
14
- def create_game(self, class_id):
15
- self._click_add_game_dropdown()
16
-
17
- self.browser.find_element(By.ID, f"class_{class_id}").click()
18
-
19
- return self
20
-
21
- def change_game_worksheet(self, worksheet_id):
22
- self._click_change_worksheet_dropdown()
23
-
24
- self.browser.find_element(By.ID, f"worksheet_{worksheet_id}").click()
25
-
26
- self.confirm_dialog()
27
-
28
- return self
29
-
30
- def delete_games(self, game_ids):
31
- # Tick checkboxes
32
- for game_id in game_ids:
33
- self.browser.find_element(By.XPATH, f"//input[@name='game_ids' and @value='{game_id}']").click()
34
-
35
- # Click delete
36
- self.browser.find_element(By.ID, "deleteGamesButton").click()
37
-
38
- self.confirm_dialog()
39
-
40
- return self
41
-
42
- def _click_change_worksheet_confirm_button(self):
43
- self.browser.find_element(By.ID, "confirm_button").click()
44
-
45
- def _click_add_game_dropdown(self):
46
- self.browser.find_element(By.ID, "add_class_dropdown").click()
47
-
48
- def _click_change_worksheet_dropdown(self):
49
- self.browser.find_element(By.ID, "worksheets_dropdown").click()
@@ -1,206 +0,0 @@
1
- import json
2
-
3
- import pytest
4
- from aimmo.models import Game
5
- from aimmo.worksheets import WORKSHEETS
6
- from common.models import Class, Teacher
7
- from common.tests.utils.classes import create_class_directly
8
- from common.tests.utils.organisation import create_organisation_directly, join_teacher_to_organisation
9
- from common.tests.utils.student import create_school_student_directly
10
- from common.tests.utils.teacher import signup_teacher_directly
11
- from django.test.client import Client
12
- from django.urls.base import reverse
13
-
14
- from .base_test import BaseTest
15
- from .conftest import IndependentStudent, SchoolStudent
16
-
17
-
18
- # @pytest.mark.django_db
19
- # TODO: move tests to kurono microservice and fix them.
20
- @pytest.mark.skip(reason="Moved game creator to Django")
21
- def test_student_cannot_access_teacher_dashboard(student1: SchoolStudent, class1: Class):
22
- """
23
- Given you are logged in as a student,
24
- When you try to access the teacher dashboard,
25
- Then you cannot access it and are instead redirected.
26
- """
27
- c = Client()
28
- url = reverse("student_login", kwargs={"access_code": class1.access_code})
29
- data = {"username": student1.username, "password": student1.password}
30
-
31
- c.post(url, data)
32
-
33
- student_dashboard_url = reverse("student_aimmo_dashboard")
34
-
35
- response_s = c.get(student_dashboard_url)
36
-
37
- assert response_s.status_code == 200
38
-
39
- teacher_dashboard_url = reverse("teacher_aimmo_dashboard")
40
-
41
- response = c.get(teacher_dashboard_url)
42
-
43
- assert response.status_code == 302
44
-
45
-
46
- # @pytest.mark.django_db
47
- # TODO: move tests to kurono microservice and fix them.
48
- @pytest.mark.skip(reason="Moved game creator to Django")
49
- def test_indep_student_cannot_access_dashboard(
50
- independent_student1: IndependentStudent,
51
- ):
52
- """
53
- Given you are logged in as an independent student,
54
- When you try to access the student dashboard,
55
- Then you can access it but the context only has the banner.
56
- """
57
- c = Client()
58
- url = reverse("independent_student_login")
59
- data = {"username": independent_student1.username, "password": independent_student1.password}
60
-
61
- c.post(url, data)
62
-
63
- student_dashboard_url = reverse("student_aimmo_dashboard")
64
-
65
- response = c.get(student_dashboard_url)
66
-
67
- assert response.status_code == 200
68
- assert "BANNER" in response.context
69
- assert "HERO_CARD" not in response.context
70
- assert "CARD_LIST" not in response.context
71
-
72
-
73
- # @pytest.mark.django_db
74
- # TODO: move tests to kurono microservice and fix them.
75
- @pytest.mark.skip(reason="Moved game creator to Django")
76
- def test_student_aimmo_dashboard_loads(student1: SchoolStudent, class1: Class, aimmo_game1: Game):
77
- """
78
- Given an aimmo game is linked to a class,
79
- When a student of that class goes on the Student Kurono Dashboard page,
80
- Then the page loads and the context contains the hero card and card list
81
- associated to the aimmo game.
82
-
83
- Then, given that the class no longer has a game linked to it,
84
- When the student goes on the same page,
85
- Then the page still loads but the context no longer contains the hero card
86
- or the card list elements.
87
- """
88
- c = Client()
89
- student_login_url = reverse("student_login", kwargs={"access_code": class1.access_code})
90
- data = {"username": student1.username, "password": student1.password}
91
-
92
- c.post(student_login_url, data)
93
-
94
- student_dashboard_url = reverse("student_aimmo_dashboard")
95
- response = c.get(student_dashboard_url)
96
-
97
- assert response.status_code == 200
98
- assert "HERO_CARD" in response.context
99
- assert "CARD_LIST" in response.context
100
-
101
- aimmo_game1.delete()
102
-
103
- url = reverse("student_aimmo_dashboard")
104
- response = c.get(url)
105
-
106
- assert response.status_code == 200
107
- assert "HERO_CARD" not in response.context
108
- assert "CARD_LIST" not in response.context
109
-
110
-
111
- # TODO: move tests to kurono microservice and fix them.
112
- # Selenium tests
113
- # class TestAimmoDashboardFrontend(BaseTest):
114
- # def test_admin_permissions_actions(self):
115
- # # Create admin teacher, school and class
116
- # admin_email, admin_password = signup_teacher_directly()
117
- # school = create_organisation_directly(admin_email)
118
- # admin_class, _, admin_access_code = create_class_directly(admin_email, "class 1")
119
-
120
- # # create another teacher and add as not admin, create a class
121
- # non_admin_email, non_admin_password = signup_teacher_directly()
122
- # join_teacher_to_organisation(non_admin_email, school.name, school.postcode, is_admin=False)
123
- # non_admin_class, _, non_admin_access_code = create_class_directly(non_admin_email, "class 2")
124
-
125
- # non_admin_teacher: Teacher = Teacher.objects.get(new_user__email=non_admin_email)
126
- # admin_teacher: Teacher = Teacher.objects.get(new_user__email=admin_email)
127
-
128
- # c = Client()
129
- # # check if non_admin cannot create a game for the admin
130
- # c.login(username=non_admin_email, password=non_admin_password)
131
- # response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": admin_class.pk})
132
- # assert response.status_code == 200
133
- # assert Game.objects.filter(game_class__teacher__school=school).count() == 0
134
-
135
- # # create a game by non admin and by admin, then check if admin can delete both
136
- # response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": non_admin_class.pk})
137
- # assert response.status_code == 302
138
- # assert Game.objects.filter(game_class__teacher=non_admin_teacher).count() == 1
139
- # c.logout()
140
-
141
- # c.login(username=admin_email, password=admin_password)
142
- # response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": admin_class.pk})
143
- # assert response.status_code == 302
144
- # assert Game.objects.filter(game_class__teacher__school=school).count() == 2
145
-
146
- # admin_game = Game.objects.get(game_class=admin_class)
147
- # non_admin_game = Game.objects.get(game_class=non_admin_class)
148
-
149
- # # test admin deleting games
150
- # c.post(reverse("game-delete-games"), {"game_ids": admin_game.id})
151
- # c.post(reverse("game-delete-games"), {"game_ids": non_admin_game.id})
152
- # assert Game.objects.filter(game_class__teacher__school=school, is_archived=True).count() == 2
153
- # # now make check if the non admin can delete game
154
- # response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": admin_class.pk})
155
- # assert response.status_code == 302
156
- # assert Game.objects.filter(game_class__teacher=admin_teacher, is_archived=False).count() == 1
157
- # c.logout()
158
-
159
- # c.login(username=non_admin_email, password=non_admin_password)
160
- # response = c.post(reverse("game-delete-games"), {"game_ids": admin_game.id})
161
- # assert response.status_code == 204
162
- # assert Game.objects.filter(game_class__teacher=admin_teacher, is_archived=False).count() == 1
163
-
164
- # def test_worksheet_dropdown_changes_worksheet(self):
165
- # teacher_email, teacher_password = signup_teacher_directly()
166
- # create_organisation_directly(teacher_email)
167
- # klass, class_name, access_code = create_class_directly(teacher_email)
168
- # student_name, student_password, _ = create_school_student_directly(access_code)
169
-
170
- # worksheet1 = WORKSHEETS.get(1)
171
- # worksheet2 = WORKSHEETS.get(2)
172
-
173
- # self.selenium.get(self.live_server_url)
174
- # page = self.go_to_homepage().go_to_teacher_login_page().login(teacher_email, teacher_password)
175
- # page = page.go_to_kurono_teacher_dashboard_page().create_game(klass.id)
176
-
177
- # game = Game.objects.get(game_class=klass)
178
-
179
- # assert game.worksheet == worksheet1
180
-
181
- # page.change_game_worksheet(worksheet2.id)
182
-
183
- # game = Game.objects.get(game_class=klass)
184
-
185
- # assert game.worksheet == worksheet2
186
-
187
- # def test_delete_games(self):
188
- # teacher_email, teacher_password = signup_teacher_directly()
189
- # create_organisation_directly(teacher_email)
190
-
191
- # klass1, _, _ = create_class_directly(teacher_email)
192
- # game1 = Game(game_class=klass1)
193
- # game1.save()
194
-
195
- # klass2, _, _ = create_class_directly(teacher_email)
196
- # game2 = Game(game_class=klass2)
197
- # game2.save()
198
-
199
- # assert Game.objects.count() == 2
200
-
201
- # self.selenium.get(self.live_server_url)
202
- # page = self.go_to_homepage().go_to_teacher_login_page().login(teacher_email, teacher_password)
203
- # page.go_to_kurono_teacher_dashboard_page().delete_games([game1.id, game2.id])
204
-
205
- # assert Game.objects.filter(is_archived=False).count() == 0
206
- # assert Game.objects.filter(is_archived=True).count() == 2
@@ -1,30 +0,0 @@
1
- from aimmo.models import Game
2
- from common.models import Class
3
-
4
-
5
- def generate_name():
6
- name = "Game %d" % generate_name.next_id
7
-
8
- generate_name.next_id += 1
9
-
10
- return name
11
-
12
-
13
- generate_name.next_id = 1
14
-
15
-
16
- def create_aimmo_game_directly(klass: Class, worksheet_id: int) -> Game:
17
- """Generate an aimmo game with the details given.
18
-
19
- Args:
20
- klass (Class): The instance of the class.
21
- worksheet_id (int): The id of the worksheet.
22
-
23
- Returns:
24
- game: Game: The game model instance.
25
- """
26
- name = generate_name()
27
-
28
- game = Game.objects.create(name=name, game_class=klass, worksheet_id=worksheet_id)
29
-
30
- return game
File without changes
@@ -1,105 +0,0 @@
1
- from typing import Any, Dict, List, Optional
2
-
3
- from aimmo.models import Game
4
- from aimmo.worksheets import WORKSHEETS, Worksheet, get_worksheets_excluding_id
5
- from common.models import Class
6
- from common.permissions import logged_in_as_student, logged_in_as_teacher
7
- from common.utils import LoginRequiredNoErrorMixin
8
- from django.contrib import messages
9
- from django.contrib.auth.mixins import UserPassesTestMixin
10
- from django.db.models import QuerySet
11
- from django.urls import reverse_lazy
12
- from django.views.generic.base import TemplateView
13
- from django.views.generic.edit import CreateView
14
-
15
- from portal.forms.add_game import AddGameForm
16
- from portal.strings.student_aimmo_dashboard import AIMMO_DASHBOARD_BANNER
17
-
18
-
19
- class TeacherAimmoDashboard(LoginRequiredNoErrorMixin, UserPassesTestMixin, CreateView):
20
- login_url = reverse_lazy("teacher_login")
21
- form_class = AddGameForm
22
- template_name = "portal/teach/teacher_aimmo_dashboard.html"
23
-
24
- def test_func(self) -> Optional[bool]:
25
- return logged_in_as_teacher(self.request.user)
26
-
27
- def get_form(self, form_class=None):
28
- teacher = self.request.user.new_teacher
29
- non_admin_classes = teacher.class_teacher
30
- admin_classes = Class.objects.filter(teacher__school=teacher.school)
31
- classes = admin_classes if teacher.is_admin else non_admin_classes
32
- if form_class is None:
33
- form_class = self.get_form_class()
34
- return form_class(classes, **self.get_form_kwargs())
35
-
36
- def form_valid(self, form):
37
- form.instance = Game(
38
- game_class=form.cleaned_data["game_class"], created_by=self.request.user.userprofile.teacher
39
- )
40
- return super().form_valid(form)
41
-
42
- def form_invalid(self, form: AddGameForm):
43
- messages.warning(
44
- self.request,
45
- ", ".join(message for errors in form.errors.values() for message in errors),
46
- )
47
- return super().form_invalid(form)
48
-
49
- def get_success_url(self):
50
- return reverse_lazy("teacher_aimmo_dashboard")
51
-
52
-
53
- class StudentAimmoDashboard(LoginRequiredNoErrorMixin, UserPassesTestMixin, TemplateView):
54
- template_name = "portal/play/student_aimmo_dashboard.html"
55
-
56
- login_url = reverse_lazy("student_login_access_code")
57
-
58
- def test_func(self) -> Optional[bool]:
59
- return logged_in_as_student(self.request.user)
60
-
61
- def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
62
- student = self.request.user.new_student
63
- klass = student.class_field
64
-
65
- if klass is None:
66
- return {"BANNER": AIMMO_DASHBOARD_BANNER}
67
-
68
- aimmo_game = klass.active_game
69
- if aimmo_game:
70
- active_worksheet = WORKSHEETS.get(aimmo_game.worksheet_id)
71
- inactive_worksheets = get_worksheets_excluding_id(active_worksheet.id)
72
-
73
- return {
74
- "BANNER": AIMMO_DASHBOARD_BANNER,
75
- "HERO_CARD": self._get_hero_card(active_worksheet, aimmo_game),
76
- "CARD_LIST": {"cards": self._get_card_list(inactive_worksheets)},
77
- }
78
- else:
79
- return {"BANNER": AIMMO_DASHBOARD_BANNER}
80
-
81
- def _get_hero_card(self, active_worksheet: Worksheet, aimmo_game: Game) -> Dict[str, Any]:
82
- return {
83
- "image": active_worksheet.active_image_path,
84
- "title": active_worksheet.name,
85
- "description": active_worksheet.description,
86
- "button1": {
87
- "text": "Read challenge",
88
- "url": active_worksheet.student_challenge_url,
89
- },
90
- "button2": {
91
- "text": "Play game",
92
- "url": "kurono/play",
93
- "url_args": aimmo_game.id,
94
- },
95
- }
96
-
97
- def _get_card_list(self, inactive_worksheets: QuerySet) -> List[Dict[str, Any]]:
98
- return [
99
- {
100
- "image": inactive_worksheet.image_path,
101
- "title": inactive_worksheet.name,
102
- "description": inactive_worksheet.short_description,
103
- }
104
- for inactive_worksheet in inactive_worksheets
105
- ]