Skip to content

Commit cad5eb7

Browse files
committed
feat: #22 Course page template view (without frontend component)
1 parent 086ef21 commit cad5eb7

File tree

10 files changed

+116
-183
lines changed

10 files changed

+116
-183
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 %}

tests/api/conftest.py

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

tests/conftest.py

Whitespace-only changes.

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)