Skip to content

Commit

Permalink
feat: create course home api DRF (#33173) (#33204)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruzniaievdm authored Sep 8, 2023
1 parent ad8ed53 commit 3978375
Show file tree
Hide file tree
Showing 13 changed files with 552 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""
Serializers for v1 contentstore API.
"""
from .home import CourseHomeSerializer
from .course_details import CourseDetailsSerializer
from .course_team import CourseTeamSerializer
from .course_rerun import CourseRerunSerializer
from .grading import CourseGradingModelSerializer, CourseGradingSerializer
from .proctoring import (
LimitedProctoredExamSettingsSerializer,
Expand Down
19 changes: 19 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Common API Serializers
"""

from rest_framework import serializers

from openedx.core.lib.api.serializers import CourseKeyField


class CourseCommonSerializer(serializers.Serializer):
"""Serializer for course renders"""
course_key = CourseKeyField()
display_name = serializers.CharField()
lms_link = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
rerun_link = serializers.CharField()
run = serializers.CharField()
url = serializers.CharField()
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
API Serializers for course rerun
"""

from rest_framework import serializers


class CourseRerunSerializer(serializers.Serializer):
""" Serializer for course rerun """
allow_unicode_course_id = serializers.BooleanField()
course_creator_status = serializers.CharField()
display_name = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
run = serializers.CharField()
62 changes: 62 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
API Serializers for course home
"""

from rest_framework import serializers

from openedx.core.lib.api.serializers import CourseKeyField

from .common import CourseCommonSerializer


class UnsucceededCourseSerializer(serializers.Serializer):
"""Serializer for unsucceeded course"""
display_name = serializers.CharField()
course_key = CourseKeyField()
org = serializers.CharField()
number = serializers.CharField()
run = serializers.CharField()
is_failed = serializers.BooleanField()
is_in_progress = serializers.BooleanField()
dismiss_link = serializers.CharField()


class LibraryViewSerializer(serializers.Serializer):
"""Serializer for library view"""
display_name = serializers.CharField()
library_key = serializers.CharField()
url = serializers.CharField()
org = serializers.CharField()
number = serializers.CharField()
can_edit = serializers.BooleanField()


class CourseHomeSerializer(serializers.Serializer):
"""Serializer for course home"""
allow_course_reruns = serializers.BooleanField()
allow_to_create_new_org = serializers.BooleanField()
allow_unicode_course_id = serializers.BooleanField()
allowed_organizations = serializers.ListSerializer(
child=serializers.CharField(),
allow_empty=True
)
archived_courses = CourseCommonSerializer(required=False, many=True)
can_create_organizations = serializers.BooleanField()
course_creator_status = serializers.CharField()
courses = CourseCommonSerializer(required=False, many=True)
in_process_course_actions = UnsucceededCourseSerializer(many=True, required=False, allow_null=True)
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)
libraries_enabled = serializers.BooleanField()
library_authoring_mfe_url = serializers.CharField()
optimization_enabled = serializers.BooleanField()
redirect_to_library_authoring_mfe = serializers.BooleanField()
request_course_creator_url = serializers.CharField()
rerun_creator_status = serializers.BooleanField()
show_new_library_button = serializers.BooleanField()
split_studio_home = serializers.BooleanField()
studio_name = serializers.CharField()
studio_short_name = serializers.CharField()
studio_request_email = serializers.CharField()
tech_support_email = serializers.CharField()
platform_name = serializers.CharField()
user_is_active = serializers.BooleanField()
18 changes: 3 additions & 15 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,11 @@

from rest_framework import serializers

from openedx.core.lib.api.serializers import CourseKeyField


class PossiblePreRequisiteCourseSerializer(serializers.Serializer):
""" Serializer for possible pre requisite course """
course_key = CourseKeyField()
display_name = serializers.CharField()
lms_link = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
rerun_link = serializers.CharField()
run = serializers.CharField()
url = serializers.CharField()
from .common import CourseCommonSerializer


class CourseSettingsSerializer(serializers.Serializer):
""" Serializer for course settings """
"""Serializer for course settings"""
about_page_editable = serializers.BooleanField()
can_show_certificate_available_date_field = serializers.BooleanField()
course_display_name = serializers.CharField()
Expand All @@ -38,7 +26,7 @@ class CourseSettingsSerializer(serializers.Serializer):
marketing_enabled = serializers.BooleanField()
mfe_proctored_exam_settings_url = serializers.CharField(required=False, allow_null=True, allow_blank=True)
platform_name = serializers.CharField()
possible_pre_requisite_courses = PossiblePreRequisiteCourseSerializer(required=False, many=True)
possible_pre_requisite_courses = CourseCommonSerializer(required=False, many=True)
short_description_editable = serializers.BooleanField()
show_min_grade_warning = serializers.BooleanField()
sidebar_html_enabled = serializers.BooleanField()
Expand Down
15 changes: 13 additions & 2 deletions cms/djangoapps/contentstore/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
""" Contenstore API v1 URLs. """

from django.urls import path
from django.urls import re_path
from django.conf import settings
from django.urls import re_path, path

from openedx.core.constants import COURSE_ID_PATTERN

from .views import (
CourseDetailsView,
CourseTeamView,
CourseGradingView,
CourseRerunView,
CourseSettingsView,
HomePageView,
ProctoredExamSettingsView,
ProctoringErrorsView,
xblock,
Expand All @@ -25,6 +26,11 @@
VIDEO_ID_PATTERN = r'(?:(?P<edx_video_id>[-\w]+))'

urlpatterns = [
path(
'home',
HomePageView.as_view(),
name="home"
),
re_path(
fr'^proctored_exam_settings/{COURSE_ID_PATTERN}$',
ProctoredExamSettingsView.as_view(),
Expand Down Expand Up @@ -92,4 +98,9 @@
HelpUrlsView.as_view(),
name="help_urls"
),
re_path(
fr'^course_rerun/{COURSE_ID_PATTERN}$',
CourseRerunView.as_view(),
name="course_rerun"
),
]
2 changes: 2 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
"""
from .course_details import CourseDetailsView
from .course_team import CourseTeamView
from .course_rerun import CourseRerunView
from .grading import CourseGradingView
from .proctoring import ProctoredExamSettingsView, ProctoringErrorsView
from .home import HomePageView
from .settings import CourseSettingsView
from .xblock import XblockView
from .assets import AssetsView
Expand Down
76 changes: 76 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/views/course_rerun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
""" API Views for course rerun """

import edx_api_doc_tools as apidocs
from opaque_keys.edx.keys import CourseKey
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

from cms.djangoapps.contentstore.utils import get_course_rerun_context
from cms.djangoapps.contentstore.rest_api.v1.serializers import CourseRerunSerializer
from common.djangoapps.student.roles import GlobalStaff
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, verify_course_exists, view_auth_classes
from xmodule.modulestore.django import modulestore


@view_auth_classes(is_authenticated=True)
class CourseRerunView(DeveloperErrorViewMixin, APIView):
"""
View for course rerun.
"""

@apidocs.schema(
parameters=[
apidocs.string_parameter("course_id", apidocs.ParameterLocation.PATH, description="Course ID"),
],
responses={
200: CourseRerunSerializer,
401: "The requester is not authenticated.",
403: "The requester cannot access the specified course.",
404: "The requested course does not exist.",
},
)
@verify_course_exists()
def get(self, request: Request, course_id: str):
"""
Get an object containing course rerun.
**Example Request**
GET /api/contentstore/v1/course_rerun/{course_id}
**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 rerun.
**Example Response**
```json
{
"allow_unicode_course_id": False,
"course_creator_status": "granted",
"number": "101",
"display_name": "new edx course",
"org": "edx",
"run": "2023",
}
```
"""

if not GlobalStaff().has_user(request.user):
self.permission_denied(request)

course_key = CourseKey.from_string(course_id)
with modulestore().bulk_operations(course_key):
course_block = modulestore().get_course(course_key)
course_rerun_context = get_course_rerun_context(course_key, course_block, request.user)
course_rerun_context.update({
'org': course_key.org,
'number': course_key.course,
'run': course_key.run,
})
serializer = CourseRerunSerializer(course_rerun_context)
return Response(serializer.data)
120 changes: 120 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/views/home.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
""" API Views for course home """

import edx_api_doc_tools as apidocs
from django.conf import settings
from rest_framework.request import Request
from rest_framework.response import Response
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


@view_auth_classes(is_authenticated=True)
class HomePageView(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: CourseHomeSerializer,
401: "The requester is not authenticated.",
},
)
def get(self, request: Request):
"""
Get an object containing all courses and libraries on home page.
**Example Request**
GET /api/contentstore/v1/home
**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
{
"allow_course_reruns": true,
"allow_to_create_new_org": true,
"allow_unicode_course_id": false,
"allowed_organizations": [],
"archived_courses": [
{
"course_key": "course-v1:edX+P315+2T2023",
"display_name": "Quantum Entanglement",
"lms_link": "//localhost:18000/courses/course-v1:edX+P315+2T2023",
"number": "P315",
"org": "edX",
"rerun_link": "/course_rerun/course-v1:edX+P315+2T2023",
"run": "2T2023"
"url": "/course/course-v1:edX+P315+2T2023"
},
],
"can_create_organizations": true,
"course_creator_status": "granted",
"courses": [
{
"course_key": "course-v1:edX+E2E-101+course",
"display_name": "E2E Test Course",
"lms_link": "//localhost:18000/courses/course-v1:edX+E2E-101+course",
"number": "E2E-101",
"org": "edX",
"rerun_link": "/course_rerun/course-v1:edX+E2E-101+course",
"run": "course",
"url": "/course/course-v1:edX+E2E-101+course"
},
],
"in_process_course_actions": [],
"libraries": [
{
"display_name": "My First Library",
"library_key": "library-v1:new+CPSPR",
"url": "/library/library-v1:new+CPSPR",
"org": "new",
"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": "[email protected]",
"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)
return Response(serializer.data)
Loading

0 comments on commit 3978375

Please sign in to comment.