Skip to content

Commit 6e79a53

Browse files
feat: Create DRF for course settings and course details views out of current Django views openedx#32397 (#558)
* revert: Removing the details_settings API --------- Co-authored-by: ruzniaievdm <ruzniaievdm@gmail.com>
1 parent d5e16f5 commit 6e79a53

File tree

13 files changed

+717
-345
lines changed

13 files changed

+717
-345
lines changed

cms/djangoapps/contentstore/rest_api/v0/tests/test_details_settings.py

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

cms/djangoapps/contentstore/rest_api/v0/urls.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@
33
from django.urls import re_path
44

55
from openedx.core.constants import COURSE_ID_PATTERN
6-
from .views import (
7-
AdvancedCourseSettingsView,
8-
CourseDetailsSettingsView,
9-
CourseTabSettingsView,
10-
CourseTabListView,
11-
CourseTabReorderView
12-
)
6+
from .views import AdvancedCourseSettingsView, CourseTabSettingsView, CourseTabListView, CourseTabReorderView
137

148
app_name = "v0"
159

@@ -34,9 +28,4 @@
3428
CourseTabReorderView.as_view(),
3529
name="course_tab_reorder",
3630
),
37-
re_path(
38-
fr"^details_settings/{COURSE_ID_PATTERN}$",
39-
CourseDetailsSettingsView.as_view(),
40-
name="course_details_settings",
41-
),
4231
]

cms/djangoapps/contentstore/rest_api/v0/views/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
Views for v0 contentstore API.
33
"""
44
from .advanced_settings import AdvancedCourseSettingsView
5-
from .details_settings import CourseDetailsSettingsView
65
from .tabs import CourseTabSettingsView, CourseTabListView, CourseTabReorderView

cms/djangoapps/contentstore/rest_api/v0/views/details_settings.py

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

cms/djangoapps/contentstore/rest_api/v1/serializers.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from rest_framework import serializers
66

7+
from openedx.core.lib.api.serializers import CourseKeyField
78
from xmodule.course_block import get_available_providers
89

910

@@ -64,3 +65,92 @@ class CourseGradingSerializer(serializers.Serializer):
6465
default_grade_designations = serializers.ListSerializer(
6566
child=serializers.CharField()
6667
)
68+
69+
70+
class InstructorInfoSerializer(serializers.Serializer):
71+
""" Serializer for instructor info """
72+
name = serializers.CharField(allow_blank=True)
73+
title = serializers.CharField(allow_blank=True)
74+
organization = serializers.CharField(allow_blank=True)
75+
image = serializers.CharField(allow_blank=True)
76+
bio = serializers.CharField(allow_blank=True)
77+
78+
79+
class InstructorsSerializer(serializers.Serializer):
80+
""" Serializer for instructors """
81+
instructors = InstructorInfoSerializer(many=True, allow_empty=True)
82+
83+
84+
class CourseDetailsSerializer(serializers.Serializer):
85+
""" Serializer for course details """
86+
about_sidebar_html = serializers.CharField(allow_null=True, allow_blank=True)
87+
banner_image_name = serializers.CharField(allow_blank=True)
88+
banner_image_asset_path = serializers.CharField()
89+
certificate_available_date = serializers.DateTimeField()
90+
certificates_display_behavior = serializers.CharField(allow_null=True)
91+
course_id = serializers.CharField()
92+
course_image_asset_path = serializers.CharField(allow_blank=True)
93+
course_image_name = serializers.CharField(allow_blank=True)
94+
description = serializers.CharField(allow_blank=True)
95+
duration = serializers.CharField(allow_blank=True)
96+
effort = serializers.CharField(allow_null=True, allow_blank=True)
97+
end_date = serializers.DateTimeField(allow_null=True)
98+
enrollment_end = serializers.DateTimeField(allow_null=True)
99+
enrollment_start = serializers.DateTimeField(allow_null=True)
100+
entrance_exam_enabled = serializers.CharField(allow_blank=True)
101+
entrance_exam_id = serializers.CharField(allow_blank=True)
102+
entrance_exam_minimum_score_pct = serializers.CharField(allow_blank=True)
103+
instructor_info = InstructorsSerializer()
104+
intro_video = serializers.CharField(allow_null=True)
105+
language = serializers.CharField(allow_null=True)
106+
learning_info = serializers.ListField(child=serializers.CharField(allow_blank=True))
107+
license = serializers.CharField(allow_null=True)
108+
org = serializers.CharField()
109+
overview = serializers.CharField(allow_blank=True)
110+
pre_requisite_courses = serializers.ListField(child=CourseKeyField())
111+
run = serializers.CharField()
112+
self_paced = serializers.BooleanField()
113+
short_description = serializers.CharField(allow_blank=True)
114+
start_date = serializers.DateTimeField()
115+
subtitle = serializers.CharField(allow_blank=True)
116+
syllabus = serializers.CharField(allow_null=True)
117+
title = serializers.CharField(allow_blank=True)
118+
video_thumbnail_image_asset_path = serializers.CharField()
119+
video_thumbnail_image_name = serializers.CharField(allow_blank=True)
120+
121+
122+
class PossiblePreRequisiteCourseSerializer(serializers.Serializer):
123+
""" Serializer for possible pre requisite course """
124+
course_key = CourseKeyField()
125+
display_name = serializers.CharField()
126+
lms_link = serializers.CharField()
127+
number = serializers.CharField()
128+
org = serializers.CharField()
129+
rerun_link = serializers.CharField()
130+
run = serializers.CharField()
131+
url = serializers.CharField()
132+
133+
134+
class CourseSettingsSerializer(serializers.Serializer):
135+
""" Serializer for course settings """
136+
about_page_editable = serializers.BooleanField()
137+
can_show_certificate_available_date_field = serializers.BooleanField()
138+
course_display_name = serializers.CharField()
139+
course_display_name_with_default = serializers.CharField()
140+
credit_eligibility_enabled = serializers.BooleanField()
141+
credit_requirements = serializers.DictField(required=False)
142+
enable_extended_course_details = serializers.BooleanField()
143+
enrollment_end_editable = serializers.BooleanField()
144+
is_credit_course = serializers.BooleanField()
145+
is_entrance_exams_enabled = serializers.BooleanField()
146+
is_prerequisite_courses_enabled = serializers.BooleanField()
147+
language_options = serializers.ListField(child=serializers.ListField(child=serializers.CharField()))
148+
lms_link_for_about_page = serializers.URLField()
149+
marketing_enabled = serializers.BooleanField()
150+
mfe_proctored_exam_settings_url = serializers.CharField(required=False, allow_null=True, allow_blank=True)
151+
possible_pre_requisite_courses = PossiblePreRequisiteCourseSerializer(required=False, many=True)
152+
short_description_editable = serializers.BooleanField()
153+
show_min_grade_warning = serializers.BooleanField()
154+
sidebar_html_enabled = serializers.BooleanField()
155+
upgrade_deadline = serializers.DateTimeField(allow_null=True)
156+
use_v2_cert_display_settings = serializers.BooleanField()
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""
2+
Unit tests for course details views.
3+
"""
4+
import json
5+
from unittest.mock import patch
6+
7+
import ddt
8+
from django.urls import reverse
9+
from rest_framework import status
10+
11+
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
12+
13+
from ..mixins import PermissionAccessMixin
14+
15+
16+
@ddt.ddt
17+
class CourseDetailsViewTest(CourseTestCase, PermissionAccessMixin):
18+
"""
19+
Tests for CourseDetailsView.
20+
"""
21+
22+
def setUp(self):
23+
super().setUp()
24+
self.url = reverse(
25+
'cms.djangoapps.contentstore:v1:course_details',
26+
kwargs={"course_id": self.course.id},
27+
)
28+
29+
def test_put_permissions_unauthenticated(self):
30+
"""
31+
Test that an error is returned in the absence of auth credentials.
32+
"""
33+
self.client.logout()
34+
response = self.client.put(self.url)
35+
error = self.get_and_check_developer_response(response)
36+
self.assertEqual(error, "Authentication credentials were not provided.")
37+
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
38+
39+
def test_put_permissions_unauthorized(self):
40+
"""
41+
Test that an error is returned if the user is unauthorised.
42+
"""
43+
client, _ = self.create_non_staff_authed_user_client()
44+
response = client.put(self.url)
45+
error = self.get_and_check_developer_response(response)
46+
self.assertEqual(error, "You do not have permission to perform this action.")
47+
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
48+
49+
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_PREREQUISITE_COURSES': True})
50+
def test_put_invalid_pre_requisite_course(self):
51+
pre_requisite_course_keys = [str(self.course.id), 'invalid_key']
52+
request_data = {"pre_requisite_courses": pre_requisite_course_keys}
53+
response = self.client.put(path=self.url, data=json.dumps(request_data), content_type="application/json")
54+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
55+
self.assertEqual(response.json()['error'], 'Invalid prerequisite course key')
56+
57+
def test_put_course_details(self):
58+
request_data = {
59+
"about_sidebar_html": "",
60+
"banner_image_name": "images_course_image.jpg",
61+
"banner_image_asset_path": "/asset-v1:edX+E2E-101+course+type@asset+block@images_course_image.jpg",
62+
"certificate_available_date": "2029-01-02T00:00:00Z",
63+
"certificates_display_behavior": "end",
64+
"course_id": "E2E-101",
65+
"course_image_asset_path": "/static/studio/images/pencils.jpg",
66+
"course_image_name": "bar_course_image_name",
67+
"description": "foo_description",
68+
"duration": "",
69+
"effort": None,
70+
"end_date": "2023-08-01T01:30:00Z",
71+
"enrollment_end": "2023-05-30T01:00:00Z",
72+
"enrollment_start": "2023-05-29T01:00:00Z",
73+
"entrance_exam_enabled": "",
74+
"entrance_exam_id": "",
75+
"entrance_exam_minimum_score_pct": "50",
76+
"intro_video": None,
77+
"language": "creative-commons: ver=4.0 BY NC ND",
78+
"learning_info": [
79+
"foo",
80+
"bar"
81+
],
82+
"license": "creative-commons: ver=4.0 BY NC ND",
83+
"org": "edX",
84+
"overview": "<section class=\"about\"></section>",
85+
"pre_requisite_courses": [],
86+
"run": "course",
87+
"self_paced": None,
88+
"short_description": "",
89+
"start_date": "2023-06-01T01:30:00Z",
90+
"subtitle": "",
91+
"syllabus": None,
92+
"title": "",
93+
"video_thumbnail_image_asset_path": "/asset-v1:edX+E2E-101+course+type@asset+block@images_course_image.jpg",
94+
"video_thumbnail_image_name": "images_course_image.jpg",
95+
"instructor_info": {
96+
"instructors": [
97+
{
98+
"name": "foo bar",
99+
"title": "title",
100+
"organization": "org",
101+
"image": "image",
102+
"bio": ""
103+
}
104+
]
105+
},
106+
}
107+
response = self.client.put(path=self.url, data=json.dumps(request_data), content_type="application/json")
108+
self.assertEqual(response.status_code, status.HTTP_200_OK)

0 commit comments

Comments
 (0)