Skip to content

Commit c28632c

Browse files
authored
Merge pull request #80 from AvaCodeSolutions/feat/22/course-template-view
Feat/22/course template view
2 parents 086ef21 + 13f0187 commit c28632c

File tree

10 files changed

+203
-91
lines changed

10 files changed

+203
-91
lines changed

django_email_learning/platform/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from django.urls import path
22
from django.views.generic import RedirectView
3-
from django_email_learning.platform.views import Courses, Organizations
3+
from django_email_learning.platform.views import CourseView, Courses, Organizations
44

55
app_name = "email_learning"
66

77
urlpatterns = [
88
path("courses/", Courses.as_view(), name="courses_view"),
9+
path("courses/<int:course_id>/", CourseView.as_view(), name="course_detail_view"),
910
path("organizations/", Organizations.as_view(), name="organizations_view"),
1011
path(
1112
"",

django_email_learning/platform/views.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
from django.contrib.auth.decorators import login_required
44
from django.utils.decorators import method_decorator
55
from django.urls import reverse
6-
from django_email_learning.models import Organization
7-
from django_email_learning.decorators import is_platform_admin
6+
from django_email_learning.models import Organization, OrganizationUser, Course
7+
from django_email_learning.decorators import (
8+
is_platform_admin,
9+
is_an_organization_member,
10+
)
811
from typing import Dict, Any
912

1013

@@ -19,10 +22,19 @@ def get_context_data(self, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyp
1922

2023
def get_shared_context(self) -> Dict[str, Any]:
2124
"""Get shared context for all platform views"""
25+
active_organization_id = self.get_or_set_active_organization()
26+
if self.request.user.is_superuser:
27+
role = "admin"
28+
else:
29+
role = OrganizationUser.objects.get( # type: ignore[misc]
30+
user=self.request.user,
31+
organization_id=active_organization_id,
32+
).role
2233
return {
2334
"api_base_url": reverse("django_email_learning:api:root")[:-1],
2435
"platform_base_url": reverse("django_email_learning:platform:root")[:-1],
25-
"active_organization_id": self.get_or_set_active_organization(),
36+
"active_organization_id": active_organization_id,
37+
"user_role": role,
2638
"is_platform_admin": (
2739
self.request.user.is_superuser
2840
or (
@@ -62,6 +74,18 @@ def get_context_data(self, **kwargs) -> dict: # type: ignore[no-untyped-def]
6274
return context
6375

6476

77+
@method_decorator(login_required, name="dispatch")
78+
@method_decorator(is_an_organization_member(), name="dispatch")
79+
class CourseView(BasePlatformView):
80+
template_name = "platform/course.html"
81+
82+
def get_context_data(self, **kwargs) -> dict: # type: ignore[no-untyped-def]
83+
context = super().get_context_data(**kwargs)
84+
course = Course.objects.get(pk=self.kwargs["course_id"])
85+
context["page_title"] = course.title
86+
return context
87+
88+
6589
@method_decorator(login_required, name="dispatch")
6690
@method_decorator(is_platform_admin(), name="dispatch")
6791
class Organizations(BasePlatformView):

django_email_learning/templates/platform/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
localStorage.setItem('activeOrganizationId', '{{ active_organization_id }}');
1010
localStorage.setItem('apiBaseUrl', '{{ api_base_url }}');
1111
localStorage.setItem('platformBaseUrl', '{{ platform_base_url }}');
12+
localStorage.setItem('userRole', '{{ user_role }}');
1213
localStorage.setItem('isPlatformAdmin', {{ is_platform_admin|yesno:"true,false" }});
1314
</script>
1415
<meta charset="UTF-8" />
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% extends "platform/base.html" %}
2+
{% load django_vite %}
3+
{% block extra_head %}
4+
<!-- {% vite_asset 'courses/Courses.jsx' %} -->
5+
{% endblock %}

frontend/courses/Courses.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function Courses() {
2020
const [organizationId, setOrganizationId] = useState(null);
2121
const [queryParameters, setQueryParameters] = useState("");
2222
const apiBaseUrl = localStorage.getItem('apiBaseUrl');
23+
const platformBaseUrl = localStorage.getItem('platformBaseUrl');
2324

2425
const renderCourses = () => {
2526
if (!organizationId) {
@@ -130,7 +131,7 @@ function Courses() {
130131
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
131132
>
132133
<TableCell component="th" scope="row">
133-
<Link href={`/courses/${course.id}`}>{course.title}</Link>
134+
<Link href={`${platformBaseUrl}/courses/${course.id}`}>{course.title}</Link>
134135
</TableCell>
135136
<TableCell>{course.slug}</TableCell>
136137
<TableCell>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
from django.contrib.auth.models import User
22
from django_email_learning.models import OrganizationUser
33
from django.test import Client
4+
from django_email_learning.models import (
5+
ImapConnection,
6+
Quiz,
7+
Lesson,
8+
Course,
9+
BlockedEmail,
10+
Learner,
11+
Enrollment,
12+
CourseContent,
13+
)
414
import pytest
515

616

@@ -91,3 +101,78 @@ def _get_client(role_name):
91101
return role_map.get(role_name)
92102

93103
return _get_client(request.param)
104+
105+
106+
@pytest.fixture()
107+
def imap_connection(db) -> ImapConnection:
108+
connection = ImapConnection(
109+
server="IMAP.example.com",
110+
port=993,
111+
112+
password="my_secret_password",
113+
organization_id=1,
114+
)
115+
connection.save()
116+
return connection
117+
118+
119+
@pytest.fixture()
120+
def quiz(db) -> Quiz:
121+
quiz = Quiz(title="Sample Quiz", required_score=70)
122+
quiz.save()
123+
return quiz
124+
125+
126+
@pytest.fixture()
127+
def lesson(db) -> Lesson:
128+
lesson = Lesson(title="Sample Lesson", content="Lesson Content", is_published=True)
129+
lesson.save()
130+
return lesson
131+
132+
133+
@pytest.fixture()
134+
def course(db, imap_connection) -> Course:
135+
course = Course(
136+
title="Sample Course",
137+
slug="sample-course",
138+
imap_connection=imap_connection,
139+
organization_id=1,
140+
)
141+
course.save()
142+
return course
143+
144+
145+
@pytest.fixture()
146+
def blocked_email(db) -> BlockedEmail:
147+
blocked_email = BlockedEmail(email="[email protected]")
148+
blocked_email.save()
149+
return blocked_email
150+
151+
152+
@pytest.fixture()
153+
def learner(db) -> Learner:
154+
learner = Learner(email="[email protected]")
155+
learner.save()
156+
return learner
157+
158+
159+
@pytest.fixture()
160+
def enrollment(db, learner, course) -> Enrollment:
161+
enrollment = Enrollment.objects.create(learner=learner, course=course)
162+
return enrollment
163+
164+
165+
@pytest.fixture
166+
def course_lesson_content(db, course, lesson) -> CourseContent:
167+
content = CourseContent.objects.create(
168+
course=course, priority=1, type="lesson", lesson=lesson, waiting_period=10
169+
)
170+
return content
171+
172+
173+
@pytest.fixture
174+
def course_quiz_content(db, course, quiz) -> CourseContent:
175+
content = CourseContent.objects.create(
176+
course=course, priority=2, type="quiz", quiz=quiz, waiting_period=5
177+
)
178+
return content

tests/platform/__init__.py

Whitespace-only changes.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from django.urls import reverse
2+
import pytest
3+
4+
5+
def get_url(course_id: int = 1) -> str:
6+
return reverse(
7+
"django_email_learning:platform:course_detail_view",
8+
kwargs={"course_id": course_id},
9+
)
10+
11+
12+
def test_anonymous_user_redirects_to_login(anonymous_client):
13+
response = anonymous_client.get(get_url())
14+
assert response.status_code == 302
15+
assert "/login/" in response.url
16+
17+
18+
@pytest.mark.parametrize(
19+
"client,role",
20+
[
21+
("superadmin", "admin"),
22+
("platform_admin", "admin"),
23+
("editor", "editor"),
24+
("viewer", "viewer"),
25+
],
26+
indirect=["client"],
27+
)
28+
def test_authenticated_user_access_course_detail_view(client, role, course):
29+
response = client.get(get_url(course.id))
30+
assert response.status_code == 200
31+
assert response.context["user_role"] == role
32+
33+
34+
def test_context_values(superadmin_client, course):
35+
response = superadmin_client.get(get_url(course.id))
36+
assert response.status_code == 200
37+
assert "api_base_url" in response.context
38+
assert "platform_base_url" in response.context
39+
assert "active_organization_id" in response.context
40+
assert "user_role" in response.context
41+
assert response.context["page_title"] == course.title
42+
assert response.context["is_platform_admin"] is True
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from django.urls import reverse
2+
import pytest
3+
4+
5+
def get_url() -> str:
6+
return reverse("django_email_learning:platform:courses_view")
7+
8+
9+
def test_anonymous_user_redirects_to_login(anonymous_client):
10+
response = anonymous_client.get(get_url())
11+
assert response.status_code == 302
12+
assert "/login/" in response.url
13+
14+
15+
@pytest.mark.parametrize(
16+
"client,role",
17+
[
18+
("superadmin", "admin"),
19+
("platform_admin", "admin"),
20+
("editor", "editor"),
21+
("viewer", "viewer"),
22+
],
23+
indirect=["client"],
24+
)
25+
def test_authenticated_user_access_courses_view(client, role):
26+
response = client.get(get_url())
27+
assert response.status_code == 200
28+
assert response.context["user_role"] == role
29+
30+
31+
def test_context_values(superadmin_client):
32+
response = superadmin_client.get(get_url())
33+
assert response.status_code == 200
34+
assert "api_base_url" in response.context
35+
assert "platform_base_url" in response.context
36+
assert "active_organization_id" in response.context
37+
assert "user_role" in response.context
38+
assert response.context["page_title"] == "Courses"
39+
assert response.context["is_platform_admin"] is True

tests/test_models/conftest.py

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)