diff --git a/openedx/core/djangoapps/courseware_api/serializers.py b/openedx/core/djangoapps/courseware_api/serializers.py index 2cb560ef90..aadd2b1ae9 100644 --- a/openedx/core/djangoapps/courseware_api/serializers.py +++ b/openedx/core/djangoapps/courseware_api/serializers.py @@ -145,16 +145,13 @@ class CourseInfoSerializer(serializers.Serializer): # pylint: disable=abstract- allow_empty=True, default=list, ) - sidebar_html_enabled = serializers.BooleanField() - course_about_section_html = serializers.CharField( + about_sidebar_html = serializers.CharField( allow_blank=True, allow_null=True, default=None, ) display_number_with_default = serializers.CharField() - org = serializers.CharField(source='display_org_with_default') - studio_url = serializers.CharField() - is_cosmetic_price_enabled = serializers.BooleanField(default=False) + display_org_with_default = serializers.CharField() overview = serializers.CharField( allow_blank=True, allow_null=True, diff --git a/openedx/core/djangoapps/courseware_api/tests/test_views.py b/openedx/core/djangoapps/courseware_api/tests/test_views.py index 5baec6fdb4..240f750ef8 100644 --- a/openedx/core/djangoapps/courseware_api/tests/test_views.py +++ b/openedx/core/djangoapps/courseware_api/tests/test_views.py @@ -634,14 +634,18 @@ class CoursewareMetaTestViews(BaseCoursewareTests): self.request = RequestFactory().get(self.url) def create_courseware_meta(self, user=None): - """Helper method to create CoursewareMeta instance""" + """ + Helper method to create CoursewareMeta instance + """ user = user or self.user self.request.user = user return CoursewareMeta(self.course.id, self.request, username=user.username) @ddt.data(True, False) def test_is_course_full_property(self, is_course_full): - """Test is_course_full property""" + """ + Test is_course_full property + """ with mock.patch( 'openedx.core.djangoapps.courseware_api.views.CourseEnrollment.objects.is_course_full' ) as mock_is_course_full: @@ -651,14 +655,18 @@ class CoursewareMetaTestViews(BaseCoursewareTests): @ddt.data(True, False) def test_invitation_only_property(self, invitation_only): - """Test invitation_only property""" + """ + Test invitation_only property + """ with override_settings(COURSES_INVITE_ONLY=invitation_only): meta = self.create_courseware_meta() assert meta.invitation_only is invitation_only @ddt.data(True, False) def test_sidebar_html_enabled_property(self, waffle_enabled): - """Test sidebar_html_enabled property with different waffle settings""" + """ + Test sidebar_html_enabled property with different waffle settings + """ with override_waffle_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML, active=waffle_enabled): meta = self.create_courseware_meta() assert meta.sidebar_html_enabled == waffle_enabled @@ -667,15 +675,17 @@ class CoursewareMetaTestViews(BaseCoursewareTests): @mock.patch( 'openedx.core.djangoapps.courseware_api.views.get_course_about_section', new_callable=mock.PropertyMock ) - def test_course_about_section_html_property(self, waffle_enabled, mock_get_course_about_section): - """Test course_about_section_html property with different waffle settings""" + def test_about_sidebar_html_property(self, waffle_enabled, mock_get_course_about_section): + """ + Test about_sidebar_html property with different waffle settings + """ mock_get_course_about_section.return_value = '
About Course
' with override_waffle_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML, active=waffle_enabled): meta = self.create_courseware_meta() if waffle_enabled: - assert meta.course_about_section_html == '
About Course
' + assert meta.about_sidebar_html == '
About Course
' else: - assert meta.course_about_section_html is None + assert meta.about_sidebar_html is None @ddt.ddt @@ -690,63 +700,81 @@ class CoursewareMetaAPIResponseTestViews(BaseCoursewareTests): CourseEnrollment.enroll(self.user, self.course.id, 'audit') def test_api_returns_show_courseware_link_field(self): - """Test that API response contains show_courseware_link field""" + """ + Test that API response contains show_courseware_link field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'show_courseware_link' in response.data assert isinstance(response.data['show_courseware_link'], bool) def test_api_returns_is_course_full_field(self): - """Test that API response contains is_course_full field""" + """ + Test that API response contains is_course_full field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'is_course_full' in response.data assert isinstance(response.data['is_course_full'], bool) def test_api_returns_can_enroll_field(self): - """Test that API response contains can_enroll field""" + """ + Test that API response contains can_enroll field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'can_enroll' in response.data assert isinstance(response.data['can_enroll'], bool) def test_api_returns_invitation_only_field(self): - """Test that API response contains invitation_only field""" + """ + Test that API response contains invitation_only field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'invitation_only' in response.data assert isinstance(response.data['invitation_only'], bool) def test_api_returns_is_shib_course_field(self): - """Test that API response contains is_shib_course field""" + """ + Test that API response contains is_shib_course field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'is_shib_course' in response.data assert isinstance(response.data['is_shib_course'], bool) def test_api_returns_allow_anonymous_field(self): - """Test that API response contains allow_anonymous field""" + """ + Test that API response contains allow_anonymous field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'allow_anonymous' in response.data assert isinstance(response.data['allow_anonymous'], bool) def test_api_returns_ecommerce_checkout_field(self): - """Test that API response contains ecommerce_checkout field""" + """ + Test that API response contains ecommerce_checkout field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'ecommerce_checkout' in response.data assert isinstance(response.data['ecommerce_checkout'], bool) def test_api_returns_single_paid_mode_field(self): - """Test that API response contains single_paid_mode field""" + """ + Test that API response contains single_paid_mode field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'single_paid_mode' in response.data assert isinstance(response.data['single_paid_mode'], dict) def test_api_returns_ecommerce_checkout_link_field(self): - """Test that API response contains ecommerce_checkout_link field""" + """ + Test that API response contains ecommerce_checkout_link field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'ecommerce_checkout_link' in response.data @@ -754,21 +782,27 @@ class CoursewareMetaAPIResponseTestViews(BaseCoursewareTests): assert isinstance(checkout_link, str) or checkout_link is None def test_api_returns_course_image_urls_field(self): - """Test that API response contains course_image_urls field""" + """ + Test that API response contains course_image_urls field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'course_image_urls' in response.data assert isinstance(response.data['course_image_urls'], list) def test_api_returns_start_date_is_still_default_field(self): - """Test that API response contains start_date_is_still_default field""" + """ + Test that API response contains start_date_is_still_default field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'start_date_is_still_default' in response.data assert isinstance(response.data['start_date_is_still_default'], bool) def test_api_returns_advertised_start_field(self): - """Test that API response contains advertised_start field""" + """ + Test that API response contains advertised_start field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'advertised_start' in response.data @@ -776,14 +810,18 @@ class CoursewareMetaAPIResponseTestViews(BaseCoursewareTests): assert isinstance(advertised_start, str) or advertised_start is None def test_api_returns_course_price_field(self): - """Test that API response contains course_price field""" + """ + Test that API response contains course_price field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'course_price' in response.data assert isinstance(response.data['course_price'], str) def test_api_returns_pre_requisite_courses_field(self): - """Test that API response contains pre_requisite_courses field""" + """ + Test that API response contains pre_requisite_courses field + """ response = self.client.get(self.url) assert response.status_code == 200 assert 'pre_requisite_courses' in response.data @@ -791,7 +829,9 @@ class CoursewareMetaAPIResponseTestViews(BaseCoursewareTests): @ddt.data(True, False) def test_api_sidebar_html_enabled_with_waffle(self, waffle_enabled): - """Test API returns correct sidebar_html_enabled value based on waffle flag""" + """ + Test API returns correct sidebar_html_enabled value based on waffle flag + """ with override_waffle_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML, active=waffle_enabled): response = self.client.get(self.url) assert response.status_code == 200 @@ -802,17 +842,19 @@ class CoursewareMetaAPIResponseTestViews(BaseCoursewareTests): @mock.patch( 'openedx.core.djangoapps.courseware_api.views.get_course_about_section', new_callable=mock.PropertyMock ) - def test_api_course_about_section_html_with_waffle(self, waffle_enabled, mock_get_course_about_section): - """Test API returns correct course_about_section_html value based on waffle flag""" + def test_api_about_sidebar_html_with_waffle(self, waffle_enabled, mock_get_course_about_section): + """ + Test API returns correct about_sidebar_html value based on waffle flag + """ with override_waffle_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML, active=waffle_enabled): mock_get_course_about_section.return_value = '
About Course
' response = self.client.get(self.url) assert response.status_code == 200 - assert 'course_about_section_html' in response.data + assert 'about_sidebar_html' in response.data if waffle_enabled: - assert response.data['course_about_section_html'] == '
About Course
' + assert response.data['about_sidebar_html'] == '
About Course
' else: - assert response.data['course_about_section_html'] is None + assert response.data['about_sidebar_html'] is None @ddt.ddt @@ -830,7 +872,9 @@ class CoursewareMetaIntegrationTestViews(BaseCoursewareTests): ) @ddt.unpack def test_enrollment_mode_affects_can_access_proctored_exams(self, enrollment_mode, expected_access): - """Test that enrollment mode affects proctored exam access in API response""" + """ + Test that enrollment mode affects proctored exam access in API response + """ CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) response = self.client.get(self.url) @@ -839,7 +883,9 @@ class CoursewareMetaIntegrationTestViews(BaseCoursewareTests): @mock.patch('openedx.core.djangoapps.courseware_api.views.check_public_access') def test_public_course_affects_allow_anonymous(self, mock_check_public_access): - """Test that course visibility settings affect allow_anonymous field""" + """ + Test that course visibility settings affect allow_anonymous field + """ mock_check_public_access.return_value = ACCESS_GRANTED response = self.client.get(self.url) diff --git a/openedx/core/djangoapps/courseware_api/views.py b/openedx/core/djangoapps/courseware_api/views.py index 6131ac5b69..fcc5b09a6c 100644 --- a/openedx/core/djangoapps/courseware_api/views.py +++ b/openedx/core/djangoapps/courseware_api/views.py @@ -40,7 +40,6 @@ from lms.djangoapps.courseware.courses import ( get_course_about_section, get_course_with_access, get_permission_for_course_about, - get_studio_url, ) from lms.djangoapps.courseware.context_processor import user_timezone_locale_prefs @@ -505,14 +504,7 @@ class CoursewareMeta: return get_prerequisite_courses_display(self.course) @property - def sidebar_html_enabled(self): - """ - Returns a boolean indicating whether the sidebar HTML is enabled for the course. - """ - return ENABLE_COURSE_ABOUT_SIDEBAR_HTML.is_enabled() - - @property - def course_about_section_html(self): + def about_sidebar_html(self): """ Returns the HTML content for the course about section. """ @@ -520,22 +512,6 @@ class CoursewareMeta: return get_course_about_section(self.request, self.course, "about_sidebar_html") return None - @property - def studio_url(self): - """ - Returns the URL to the course in Studio. - """ - if self.request.user.is_authenticated and self.request.user.is_staff: - return get_studio_url(self.course, 'settings/details') - return None - - @property - def is_cosmetic_price_enabled(self): - """ - Returns a boolean indicating whether the cosmetic price feature is enabled. - """ - return settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE', False) - @property def overview(self): """ @@ -550,13 +526,6 @@ class CoursewareMeta: """ return get_course_about_section(self.request, self.course, "ocw_links") - @property - def prerequisites(self): - """ - Returns a list of prerequisite courses for the course. - """ - return get_course_about_section(self.request, self.course, "prerequisites") - @method_decorator(transaction.non_atomic_requests, name='dispatch') class CoursewareInformation(RetrieveAPIView): @@ -646,9 +615,37 @@ class CoursewareInformation(RetrieveAPIView): * certificate_data: data regarding the effective user's certificate for the given course * verify_identity_url: URL for a learner to verify their identity. Only returned for learners enrolled in a verified mode. Will update to reverify URL if necessary. + * verification_status: The verification status of the effective user in the course. Possible values: + * 'none': No verification has been created for the user + * 'expired': The verification has expired + * 'approved': The verification has been approved + * 'pending': The verification is pending + * 'must_reverify': The user must reverify their identity * linkedin_add_to_profile_url: URL to add the effective user's certificate to a LinkedIn Profile. * user_needs_integrity_signature: Whether the user needs to sign the integrity agreement for the course * learning_assistant_enabled: Whether the Xpert Learning Assistant is enabled for the requesting user + * show_courseware_link: Whether the courseware link should be shown in the course details page + * is_course_full: Whether the course is full + * can_enroll: Whether the user can enroll in the course + * invitation_only: Whether the course is invitation only + * is_shib_course: Whether the course is a Shibboleth course + * allow_anonymous: Whether the course allows anonymous access + * ecommerce_checkout: Whether the course has an ecommerce checkout + * single_paid_mode: An object representing the single paid mode for the course, if it exists + * sku: (str) The SKU for the single paid mode + * name: (str) The name of the single paid mode + * min_price: (str) The minimum price for the single paid mode, formatted with the currency symbol + * description: (str) The description of the single paid mode + * is_discounted: (bool) Whether the single paid mode is discounted + * ecommerce_checkout_link: The ecommerce checkout link for the course, if it exists + * course_image_urls: A list of course image URLs + * start_date_is_still_default: Whether the course start date is still the default value + * advertised_start: The advertised start date of the course + * course_price: The course price, formatted with the currency symbol + * pre_requisite_courses: A list of pre-requisite courses for the course + * about_sidebar_html: The HTML content for the course about section, if enabled + * display_number_with_default: The course number with the org name, if set + * display_org_with_default: The org name with the course number, if set * content_type_gating_enabled: Whether the content type gating is enabled for the course * show_calculator: Whether the calculator should be shown in the course details page * can_access_proctored_exams: Whether the user is eligible to access proctored exams @@ -656,11 +653,10 @@ class CoursewareInformation(RetrieveAPIView): * enabled: Boolean indicating whether edxnotes feature is enabled for the course * visible: Boolean indicating whether notes are visible in the course * marketing_url: The marketing URL for the course - * studio_url: The URL to the course in Studio, if the user is staff - * is_cosmetic_price_enabled: Boolean indicating whether the cosmetic price feature is enabled * overview: The overview HTML content for the course * ocw_links: A list of OpenCourseWare links for the course * prerequisites: A list of prerequisite courses for the course + * license: The license for the course **Parameters:**