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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .course_rerun import CourseRerunSerializer
from .course_team import CourseTeamSerializer
from .grading import CourseGradingModelSerializer, CourseGradingSerializer
from .home import CourseHomeSerializer
from .home import CourseHomeSerializer, CourseTabSerializer, LibraryTabSerializer
from .proctoring import (
LimitedProctoredExamSettingsSerializer,
ProctoredExamConfigurationSerializer,
Expand Down
10 changes: 10 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ class LibraryViewSerializer(serializers.Serializer):
can_edit = serializers.BooleanField()


class CourseTabSerializer(serializers.Serializer):
archived_courses = CourseCommonSerializer(required=False, many=True)
courses = CourseCommonSerializer(required=False, many=True)
in_process_course_actions = UnsucceededCourseSerializer(many=True, required=False, allow_null=True)


class LibraryTabSerializer(serializers.Serializer):
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)


class CourseHomeSerializer(serializers.Serializer):
"""Serializer for course home"""
allow_course_reruns = serializers.BooleanField()
Expand Down
10 changes: 10 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
CourseSettingsView,
CourseVideosView,
HomePageView,
HomePageCoursesView,
HomePageLibrariesView,
ProctoredExamSettingsView,
ProctoringErrorsView,
HelpUrlsView,
Expand All @@ -29,6 +31,14 @@
HomePageView.as_view(),
name="home"
),
path(
'home/courses',
HomePageCoursesView.as_view(),
name="courses"),
path(
'home/libraries',
HomePageLibrariesView.as_view(),
name="libraries"),
re_path(
fr'^videos/{COURSE_ID_PATTERN}$',
CourseVideosView.as_view(),
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/rest_api/v1/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .course_rerun import CourseRerunView
from .grading import CourseGradingView
from .proctoring import ProctoredExamSettingsView, ProctoringErrorsView
from .home import HomePageView
from .home import HomePageView, HomePageCoursesView, HomePageLibrariesView
from .settings import CourseSettingsView
from .videos import (
CourseVideosView,
Expand Down
160 changes: 129 additions & 31 deletions cms/djangoapps/contentstore/rest_api/v1/views/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from rest_framework.views import APIView
from openedx.core.lib.api.view_utils import view_auth_classes

from ....utils import get_home_context
from ..serializers import CourseHomeSerializer
from ....utils import get_home_context, get_course_context, get_library_context
from ..serializers import CourseHomeSerializer, CourseTabSerializer, LibraryTabSerializer


@view_auth_classes(is_authenticated=True)
Expand Down Expand Up @@ -51,6 +51,80 @@ def get(self, request: Request):
"allow_to_create_new_org": true,
"allow_unicode_course_id": false,
"allowed_organizations": [],
"archived_courses": [],
"can_create_organizations": true,
"course_creator_status": "granted",
"courses": [],
"in_process_course_actions": [],
"libraries": [],
"libraries_enabled": true,
"library_authoring_mfe_url": "//localhost:3001/course/course-v1:edX+P315+2T2023",
"optimization_enabled": true,
"redirect_to_library_authoring_mfe": false,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": true,
"show_new_library_button": true,
"split_studio_home": false,
"studio_name": "Studio",
"studio_short_name": "Studio",
"studio_request_email": "",
"tech_support_email": "technical@example.com",
"platform_name": "Your Platform Name Here"
"user_is_active": true,
}
```
"""

home_context = get_home_context(request, True)
home_context.update({
'allow_to_create_new_org': settings.FEATURES.get('ENABLE_CREATOR_GROUP', True) and request.user.is_staff,
'studio_name': settings.STUDIO_NAME,
'studio_short_name': settings.STUDIO_SHORT_NAME,
'studio_request_email': settings.FEATURES.get('STUDIO_REQUEST_EMAIL', ''),
'tech_support_email': settings.TECH_SUPPORT_EMAIL,
'platform_name': settings.PLATFORM_NAME,
'user_is_active': request.user.is_active,
})
serializer = CourseHomeSerializer(home_context)
return Response(serializer.data)


@view_auth_classes(is_authenticated=True)
class HomePageCoursesView(APIView):
"""
View for getting all courses and libraries available to the logged in user.
"""
@apidocs.schema(
parameters=[
apidocs.string_parameter(
"org",
apidocs.ParameterLocation.QUERY,
description="Query param to filter by course org",
)],
responses={
200: CourseTabSerializer,
401: "The requester is not authenticated.",
},
)
def get(self, request: Request):
"""
Get an object containing all courses.

**Example Request**

GET /api/contentstore/v1/home/courses

**Response Values**

If the request is successful, an HTTP 200 "OK" response is returned.

The HTTP 200 response contains a single dict that contains keys that
are the course's home.

**Example Response**

```json
{
"archived_courses": [
{
"course_key": "course-v1:edX+P315+2T2023",
Expand All @@ -63,8 +137,6 @@ def get(self, request: Request):
"url": "/course/course-v1:edX+P315+2T2023"
},
],
"can_create_organizations": true,
"course_creator_status": "granted",
"courses": [
{
"course_key": "course-v1:edX+E2E-101+course",
Expand All @@ -78,6 +150,56 @@ def get(self, request: Request):
},
],
"in_process_course_actions": [],
}
```
"""

active_courses, archived_courses, in_process_course_actions = get_course_context(request)
courses_context = {
"courses": active_courses,
"archived_courses": archived_courses,
"in_process_course_actions": in_process_course_actions,
}
serializer = CourseTabSerializer(courses_context)
return Response(serializer.data)


@view_auth_classes(is_authenticated=True)
class HomePageLibrariesView(APIView):
"""
View for getting all courses and libraries available to the logged in user.
"""
@apidocs.schema(
parameters=[
apidocs.string_parameter(
"org",
apidocs.ParameterLocation.QUERY,
description="Query param to filter by course org",
)],
responses={
200: LibraryTabSerializer,
401: "The requester is not authenticated.",
},
)
def get(self, request: Request):
"""
Get an object containing all libraries on home page.

**Example Request**

GET /api/contentstore/v1/home/libraries

**Response Values**

If the request is successful, an HTTP 200 "OK" response is returned.

The HTTP 200 response contains a single dict that contains keys that
are the course's home.

**Example Response**

```json
{
"libraries": [
{
"display_name": "My First Library",
Expand All @@ -87,34 +209,10 @@ def get(self, request: Request):
"number": "CPSPR",
"can_edit": true
}
],
"libraries_enabled": true,
"library_authoring_mfe_url": "//localhost:3001/course/course-v1:edX+P315+2T2023",
"optimization_enabled": true,
"redirect_to_library_authoring_mfe": false,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": true,
"show_new_library_button": true,
"split_studio_home": false,
"studio_name": "Studio",
"studio_short_name": "Studio",
"studio_request_email": "",
"tech_support_email": "technical@example.com",
"platform_name": "Your Platform Name Here"
"user_is_active": true,
}
], }
```
"""

home_context = get_home_context(request)
home_context.update({
'allow_to_create_new_org': settings.FEATURES.get('ENABLE_CREATOR_GROUP', True) and request.user.is_staff,
'studio_name': settings.STUDIO_NAME,
'studio_short_name': settings.STUDIO_SHORT_NAME,
'studio_request_email': settings.FEATURES.get('STUDIO_REQUEST_EMAIL', ''),
'tech_support_email': settings.TECH_SUPPORT_EMAIL,
'platform_name': settings.PLATFORM_NAME,
'user_is_active': request.user.is_active,
})
serializer = CourseHomeSerializer(home_context)
library_context = get_library_context(request)
serializer = LibraryTabSerializer(library_context)
return Response(serializer.data)
96 changes: 76 additions & 20 deletions cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from rest_framework import status

from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
from cms.djangoapps.contentstore.views.course import ENABLE_GLOBAL_STAFF_OPTIMIZATION
from cms.djangoapps.contentstore.toggles import ENABLE_TAGGING_TAXONOMY_LIST_PAGE
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
Expand All @@ -20,17 +21,16 @@
@ddt.ddt
class HomePageViewTest(CourseTestCase):
"""
Tests for HomePageView.
Tests for HomePageCoursesView.
"""

def setUp(self):
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:home")

def test_home_page_response(self):
def test_home_page_courses_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
course_id = str(self.course.id)

expected_response = {
"allow_course_reruns": True,
Expand All @@ -40,16 +40,7 @@ def test_home_page_response(self):
"archived_courses": [],
"can_create_organizations": True,
"course_creator_status": "granted",
"courses": [{
"course_key": course_id,
"display_name": self.course.display_name,
"lms_link": f'//{settings.LMS_BASE}/courses/{course_id}/jump_to/{self.course.location}',
"number": self.course.number,
"org": self.course.org,
"rerun_link": f'/course_rerun/{course_id}',
"run": self.course.id.run,
"url": f'/course/{course_id}',
}],
"courses": [],
"in_process_course_actions": [],
"libraries": [],
"libraries_enabled": True,
Expand All @@ -73,6 +64,50 @@ def test_home_page_response(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)

@override_waffle_flag(ENABLE_TAGGING_TAXONOMY_LIST_PAGE, True)
def test_taxonomy_list_link(self):
response = self.client.get(self.url)
self.assertTrue(response.data['taxonomies_enabled'])
self.assertEqual(
response.data['taxonomy_list_mfe_url'],
f'{settings.COURSE_AUTHORING_MICROFRONTEND_URL}/taxonomies'
)


@ddt.ddt
class HomePageCoursesViewTest(CourseTestCase):
"""
Tests for HomePageView.
"""

def setUp(self):
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:courses")

def test_home_page_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
course_id = str(self.course.id)

expected_response = {
"archived_courses": [],
"courses": [{
"course_key": course_id,
"display_name": self.course.display_name,
"lms_link": f'//{settings.LMS_BASE}/courses/{course_id}/jump_to/{self.course.location}',
"number": self.course.number,
"org": self.course.org,
"rerun_link": f'/course_rerun/{course_id}',
"run": self.course.id.run,
"url": f'/course/{course_id}',
}],
"in_process_course_actions": [],
}

self.assertEqual(response.status_code, status.HTTP_200_OK)
print(response.data)
self.assertDictEqual(expected_response, response.data)

@override_waffle_switch(ENABLE_GLOBAL_STAFF_OPTIMIZATION, True)
def test_org_query_if_passed(self):
"""Test home page when org filter passed as a query param"""
Expand All @@ -94,11 +129,32 @@ def test_org_query_if_empty(self):
self.assertEqual(len(response.data['courses']), 0)
self.assertEqual(response.status_code, status.HTTP_200_OK)

@override_waffle_flag(ENABLE_TAGGING_TAXONOMY_LIST_PAGE, True)
def test_taxonomy_list_link(self):

@ddt.ddt
class HomePageLibrariesViewTest(LibraryTestCase):
"""
Tests for HomePageLibrariesView.
"""

def setUp(self):
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:libraries")

def test_home_page_libraries_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
self.assertTrue(response.data['taxonomies_enabled'])
self.assertEqual(
response.data['taxonomy_list_mfe_url'],
f'{settings.COURSE_AUTHORING_MICROFRONTEND_URL}/taxonomies'
)

expected_response = {
"libraries": [{
'display_name': 'Test Library',
'library_key': 'library-v1:org+lib',
'url': '/library/library-v1:org+lib',
'org': 'org',
'number': 'lib',
'can_edit': True
}],
}

self.assertEqual(response.status_code, status.HTTP_200_OK)
print(response.data)
self.assertDictEqual(expected_response, response.data)
Loading