Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion django_email_learning/platform/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from django.urls import path
from django.views.generic import RedirectView
from django_email_learning.platform.views import Courses, Organizations
from django_email_learning.platform.views import CourseView, Courses, Organizations

app_name = "email_learning"

urlpatterns = [
path("courses/", Courses.as_view(), name="courses_view"),
path("courses/<int:course_id>/", CourseView.as_view(), name="course_detail_view"),
path("organizations/", Organizations.as_view(), name="organizations_view"),
path(
"",
Expand Down
30 changes: 27 additions & 3 deletions django_email_learning/platform/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.urls import reverse
from django_email_learning.models import Organization
from django_email_learning.decorators import is_platform_admin
from django_email_learning.models import Organization, OrganizationUser, Course
from django_email_learning.decorators import (
is_platform_admin,
is_an_organization_member,
)
from typing import Dict, Any


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

def get_shared_context(self) -> Dict[str, Any]:
"""Get shared context for all platform views"""
active_organization_id = self.get_or_set_active_organization()
if self.request.user.is_superuser:
role = "admin"
else:
role = OrganizationUser.objects.get( # type: ignore[misc]
user=self.request.user,
organization_id=active_organization_id,
).role
return {
"api_base_url": reverse("django_email_learning:api:root")[:-1],
"platform_base_url": reverse("django_email_learning:platform:root")[:-1],
"active_organization_id": self.get_or_set_active_organization(),
"active_organization_id": active_organization_id,
"user_role": role,
"is_platform_admin": (
self.request.user.is_superuser
or (
Expand Down Expand Up @@ -62,6 +74,18 @@ def get_context_data(self, **kwargs) -> dict: # type: ignore[no-untyped-def]
return context


@method_decorator(login_required, name="dispatch")
@method_decorator(is_an_organization_member(), name="dispatch")
class CourseView(BasePlatformView):
template_name = "platform/course.html"

def get_context_data(self, **kwargs) -> dict: # type: ignore[no-untyped-def]
context = super().get_context_data(**kwargs)
course = Course.objects.get(pk=self.kwargs["course_id"])
context["page_title"] = course.title
return context


@method_decorator(login_required, name="dispatch")
@method_decorator(is_platform_admin(), name="dispatch")
class Organizations(BasePlatformView):
Expand Down
1 change: 1 addition & 0 deletions django_email_learning/templates/platform/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
localStorage.setItem('activeOrganizationId', '{{ active_organization_id }}');
localStorage.setItem('apiBaseUrl', '{{ api_base_url }}');
localStorage.setItem('platformBaseUrl', '{{ platform_base_url }}');
localStorage.setItem('userRole', '{{ user_role }}');
localStorage.setItem('isPlatformAdmin', {{ is_platform_admin|yesno:"true,false" }});
</script>
<meta charset="UTF-8" />
Expand Down
5 changes: 5 additions & 0 deletions django_email_learning/templates/platform/course.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "platform/base.html" %}
{% load django_vite %}
{% block extra_head %}
<!-- {% vite_asset 'courses/Courses.jsx' %} -->
{% endblock %}
3 changes: 2 additions & 1 deletion frontend/courses/Courses.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function Courses() {
const [organizationId, setOrganizationId] = useState(null);
const [queryParameters, setQueryParameters] = useState("");
const apiBaseUrl = localStorage.getItem('apiBaseUrl');
const platformBaseUrl = localStorage.getItem('platformBaseUrl');

const renderCourses = () => {
if (!organizationId) {
Expand Down Expand Up @@ -130,7 +131,7 @@ function Courses() {
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
<Link href={`/courses/${course.id}`}>{course.title}</Link>
<Link href={`${platformBaseUrl}/courses/${course.id}`}>{course.title}</Link>
</TableCell>
<TableCell>{course.slug}</TableCell>
<TableCell>
Expand Down
85 changes: 85 additions & 0 deletions tests/api/conftest.py → tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from django.contrib.auth.models import User
from django_email_learning.models import OrganizationUser
from django.test import Client
from django_email_learning.models import (
ImapConnection,
Quiz,
Lesson,
Course,
BlockedEmail,
Learner,
Enrollment,
CourseContent,
)
import pytest


Expand Down Expand Up @@ -91,3 +101,78 @@ def _get_client(role_name):
return role_map.get(role_name)

return _get_client(request.param)


@pytest.fixture()
def imap_connection(db) -> ImapConnection:
connection = ImapConnection(
server="IMAP.example.com",
port=993,
email="[email protected]",
password="my_secret_password",
organization_id=1,
)
connection.save()
return connection


@pytest.fixture()
def quiz(db) -> Quiz:
quiz = Quiz(title="Sample Quiz", required_score=70)
quiz.save()
return quiz


@pytest.fixture()
def lesson(db) -> Lesson:
lesson = Lesson(title="Sample Lesson", content="Lesson Content", is_published=True)
lesson.save()
return lesson


@pytest.fixture()
def course(db, imap_connection) -> Course:
course = Course(
title="Sample Course",
slug="sample-course",
imap_connection=imap_connection,
organization_id=1,
)
course.save()
return course


@pytest.fixture()
def blocked_email(db) -> BlockedEmail:
blocked_email = BlockedEmail(email="[email protected]")
blocked_email.save()
return blocked_email


@pytest.fixture()
def learner(db) -> Learner:
learner = Learner(email="[email protected]")
learner.save()
return learner


@pytest.fixture()
def enrollment(db, learner, course) -> Enrollment:
enrollment = Enrollment.objects.create(learner=learner, course=course)
return enrollment


@pytest.fixture
def course_lesson_content(db, course, lesson) -> CourseContent:
content = CourseContent.objects.create(
course=course, priority=1, type="lesson", lesson=lesson, waiting_period=10
)
return content


@pytest.fixture
def course_quiz_content(db, course, quiz) -> CourseContent:
content = CourseContent.objects.create(
course=course, priority=2, type="quiz", quiz=quiz, waiting_period=5
)
return content
Empty file added tests/platform/__init__.py
Empty file.
42 changes: 42 additions & 0 deletions tests/platform/test_views/test_course_details_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from django.urls import reverse
import pytest


def get_url(course_id: int = 1) -> str:
return reverse(
"django_email_learning:platform:course_detail_view",
kwargs={"course_id": course_id},
)


def test_anonymous_user_redirects_to_login(anonymous_client):
response = anonymous_client.get(get_url())
assert response.status_code == 302
assert "/login/" in response.url


@pytest.mark.parametrize(
"client,role",
[
("superadmin", "admin"),
("platform_admin", "admin"),
("editor", "editor"),
("viewer", "viewer"),
],
indirect=["client"],
)
def test_authenticated_user_access_course_detail_view(client, role, course):
response = client.get(get_url(course.id))
assert response.status_code == 200
assert response.context["user_role"] == role


def test_context_values(superadmin_client, course):
response = superadmin_client.get(get_url(course.id))
assert response.status_code == 200
assert "api_base_url" in response.context
assert "platform_base_url" in response.context
assert "active_organization_id" in response.context
assert "user_role" in response.context
assert response.context["page_title"] == course.title
assert response.context["is_platform_admin"] is True
39 changes: 39 additions & 0 deletions tests/platform/test_views/test_courses_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.urls import reverse
import pytest


def get_url() -> str:
return reverse("django_email_learning:platform:courses_view")


def test_anonymous_user_redirects_to_login(anonymous_client):
response = anonymous_client.get(get_url())
assert response.status_code == 302
assert "/login/" in response.url


@pytest.mark.parametrize(
"client,role",
[
("superadmin", "admin"),
("platform_admin", "admin"),
("editor", "editor"),
("viewer", "viewer"),
],
indirect=["client"],
)
def test_authenticated_user_access_courses_view(client, role):
response = client.get(get_url())
assert response.status_code == 200
assert response.context["user_role"] == role


def test_context_values(superadmin_client):
response = superadmin_client.get(get_url())
assert response.status_code == 200
assert "api_base_url" in response.context
assert "platform_base_url" in response.context
assert "active_organization_id" in response.context
assert "user_role" in response.context
assert response.context["page_title"] == "Courses"
assert response.context["is_platform_admin"] is True
86 changes: 0 additions & 86 deletions tests/test_models/conftest.py

This file was deleted.

Loading