AA-383: add verified mode info to course_home outline API
Adds can_show_course_sock and verified_mode values to the outline API serialization. And adds a utility method to generate the verified_mode dictionary, shared with the courseware API.
This commit is contained in:
@@ -20,7 +20,6 @@ from lms.djangoapps.courseware.masquerade import setup_masquerade
|
||||
from lms.djangoapps.course_home_api.dates.v1.serializers import DatesTabSerializer
|
||||
from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.features.course_experience.utils import dates_banner_should_display
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Course Home Mixins.
|
||||
"""
|
||||
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework import serializers
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link
|
||||
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link
|
||||
from openedx.core.djangoapps.courseware_api.utils import serialize_upgrade_info
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG
|
||||
from openedx.features.course_experience.utils import dates_banner_should_display
|
||||
|
||||
|
||||
@@ -42,3 +42,24 @@ class DatesBannerSerializerMixin(serializers.Serializer):
|
||||
)
|
||||
info['verified_upgrade_link'] = verified_upgrade_deadline_link(request.user, course_id=course_key)
|
||||
return info
|
||||
|
||||
|
||||
class VerifiedModeSerializerMixin(serializers.Serializer):
|
||||
"""
|
||||
Serializer Mixin for displaying verified mode upgrade information.
|
||||
|
||||
Requires 'course_overview', 'enrollment', and 'request' from self.context.
|
||||
"""
|
||||
can_show_upgrade_sock = serializers.SerializerMethodField()
|
||||
verified_mode = serializers.SerializerMethodField()
|
||||
|
||||
def get_can_show_upgrade_sock(self, _):
|
||||
course_overview = self.context['course_overview']
|
||||
return DISPLAY_COURSE_SOCK_FLAG.is_enabled(course_overview.id)
|
||||
|
||||
def get_verified_mode(self, _):
|
||||
"""Return verified mode information, or None."""
|
||||
course_overview = self.context['course_overview']
|
||||
enrollment = self.context['enrollment']
|
||||
request = self.context['request']
|
||||
return serialize_upgrade_info(request.user, course_overview, enrollment)
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.utils.translation import ngettext
|
||||
from rest_framework import serializers
|
||||
|
||||
from lms.djangoapps.course_home_api.dates.v1.serializers import DateSummarySerializer
|
||||
from lms.djangoapps.course_home_api.mixins import DatesBannerSerializerMixin
|
||||
from lms.djangoapps.course_home_api.mixins import DatesBannerSerializerMixin, VerifiedModeSerializerMixin
|
||||
|
||||
|
||||
class CourseBlockSerializer(serializers.Serializer):
|
||||
@@ -74,8 +74,8 @@ class CourseToolSerializer(serializers.Serializer):
|
||||
url = serializers.SerializerMethodField()
|
||||
|
||||
def get_url(self, tool):
|
||||
course_key = self.context.get('course_key')
|
||||
url = tool.url(course_key)
|
||||
course_overview = self.context.get('course_overview')
|
||||
url = tool.url(course_overview.id)
|
||||
request = self.context.get('request')
|
||||
return request.build_absolute_uri(url)
|
||||
|
||||
@@ -105,7 +105,7 @@ class ResumeCourseSerializer(serializers.Serializer):
|
||||
url = serializers.URLField()
|
||||
|
||||
|
||||
class OutlineTabSerializer(DatesBannerSerializerMixin, serializers.Serializer):
|
||||
class OutlineTabSerializer(DatesBannerSerializerMixin, VerifiedModeSerializerMixin, serializers.Serializer):
|
||||
"""
|
||||
Serializer for the Outline Tab
|
||||
"""
|
||||
|
||||
@@ -6,18 +6,22 @@ import itertools
|
||||
from datetime import datetime
|
||||
|
||||
import ddt
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from mock import Mock, patch
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
|
||||
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_MICROFRONTEND, COURSE_HOME_MICROFRONTEND_OUTLINE_TAB
|
||||
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
|
||||
from openedx.core.djangoapps.course_date_signals.utils import MIN_DURATION
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, ENABLE_COURSE_GOALS
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
from openedx.features.course_experience import (
|
||||
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, DISPLAY_COURSE_SOCK_FLAG, ENABLE_COURSE_GOALS,
|
||||
)
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
@@ -307,3 +311,27 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
self.assertEqual(data['offer_html'] is not None, show_enrolled)
|
||||
self.assertEqual(data['course_expired_html'] is not None, show_enrolled)
|
||||
self.assertEqual(data['resume_course']['url'] is not None, show_enrolled)
|
||||
|
||||
@override_experiment_waffle_flag(COURSE_HOME_MICROFRONTEND, active=True)
|
||||
@override_waffle_flag(COURSE_HOME_MICROFRONTEND_OUTLINE_TAB, active=True)
|
||||
@ddt.data(True, False)
|
||||
def test_can_show_upgrade_sock(self, sock_enabled):
|
||||
with override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=sock_enabled):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.data['can_show_upgrade_sock'], sock_enabled)
|
||||
|
||||
@override_experiment_waffle_flag(COURSE_HOME_MICROFRONTEND, active=True)
|
||||
@override_waffle_flag(COURSE_HOME_MICROFRONTEND_OUTLINE_TAB, active=True)
|
||||
def test_verified_mode(self):
|
||||
enrollment = CourseEnrollment.enroll(self.user, self.course.id)
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.data['verified_mode'], {
|
||||
'access_expiration_date': enrollment.created + MIN_DURATION,
|
||||
'currency': 'USD',
|
||||
'currency_symbol': '$',
|
||||
'price': 149,
|
||||
'sku': 'ABCD1234',
|
||||
'upgrade_url': '/dashboard',
|
||||
})
|
||||
|
||||
@@ -256,8 +256,9 @@ class OutlineTabView(RetrieveAPIView):
|
||||
'welcome_message_html': welcome_message_html or None,
|
||||
}
|
||||
context = self.get_serializer_context()
|
||||
context['course_key'] = course_key
|
||||
context['course_overview'] = course_overview
|
||||
context['enable_links'] = show_enrolled or allow_public
|
||||
context['enrollment'] = enrollment
|
||||
serializer = self.get_serializer_class()(data, context=context)
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -41,7 +41,9 @@ class BaseCourseHomeTests(ModuleStoreTestCase, MasqueradeMixin):
|
||||
CourseModeFactory(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
expiration_datetime=datetime(2028, 1, 1)
|
||||
expiration_datetime=datetime(2028, 1, 1),
|
||||
min_price=149,
|
||||
sku='ABCD1234',
|
||||
)
|
||||
VerificationDeadline.objects.create(course_key=self.course.id, deadline=datetime(2028, 1, 1))
|
||||
|
||||
|
||||
29
openedx/core/djangoapps/courseware_api/utils.py
Normal file
29
openedx/core/djangoapps/courseware_api/utils.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Courseware API Mixins.
|
||||
"""
|
||||
|
||||
from babel.numbers import get_currency_symbol
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from lms.djangoapps.courseware.utils import can_show_verified_upgrade, verified_upgrade_deadline_link
|
||||
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
|
||||
|
||||
|
||||
def serialize_upgrade_info(user, course_overview, enrollment):
|
||||
"""
|
||||
Return verified mode upgrade information, or None.
|
||||
|
||||
This is used in a few API views to provide consistent upgrade info to frontends.
|
||||
"""
|
||||
if not can_show_verified_upgrade(user, enrollment):
|
||||
return None
|
||||
|
||||
mode = CourseMode.verified_mode_for_course(course=course_overview)
|
||||
return {
|
||||
'access_expiration_date': get_user_course_expiration_date(user, course_overview),
|
||||
'currency': mode.currency.upper(),
|
||||
'currency_symbol': get_currency_symbol(mode.currency.upper()),
|
||||
'price': mode.min_price,
|
||||
'sku': mode.sku,
|
||||
'upgrade_url': verified_upgrade_deadline_link(user, course_overview),
|
||||
}
|
||||
@@ -4,7 +4,6 @@ Course API Views
|
||||
|
||||
import json
|
||||
|
||||
from babel.numbers import get_currency_symbol
|
||||
from completion.exceptions import UnavailableCompletionData
|
||||
from completion.utilities import get_key_to_last_completed_block
|
||||
from django.conf import settings
|
||||
@@ -33,8 +32,6 @@ from lms.djangoapps.courseware.masquerade import setup_masquerade
|
||||
from lms.djangoapps.courseware.module_render import get_module_by_usage_id
|
||||
from lms.djangoapps.courseware.tabs import get_course_tab_list
|
||||
from lms.djangoapps.courseware.toggles import REDIRECT_TO_COURSEWARE_MICROFRONTEND, course_exit_page_is_active
|
||||
from lms.djangoapps.courseware.utils import can_show_verified_upgrade
|
||||
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link
|
||||
from lms.djangoapps.courseware.views.views import get_cert_data
|
||||
from lms.djangoapps.grades.api import CourseGradeFactory
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
@@ -42,9 +39,7 @@ from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
|
||||
from openedx.core.djangoapps.programs.utils import ProgramProgressMeter
|
||||
from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.access import (
|
||||
get_user_course_expiration_date, generate_course_expired_message
|
||||
)
|
||||
from openedx.features.course_duration_limits.access import generate_course_expired_message
|
||||
from openedx.features.discounts.utils import generate_offer_html
|
||||
from common.djangoapps.student.models import (
|
||||
CourseEnrollment, CourseEnrollmentCelebration, LinkedInAddToProfileConfiguration
|
||||
@@ -53,6 +48,7 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.search import path_to_location
|
||||
|
||||
from .serializers import CourseInfoSerializer
|
||||
from .utils import serialize_upgrade_info
|
||||
|
||||
|
||||
class CoursewareMeta:
|
||||
@@ -186,18 +182,7 @@ class CoursewareMeta:
|
||||
"""
|
||||
Return verified mode information, or None.
|
||||
"""
|
||||
if not can_show_verified_upgrade(self.effective_user, self.enrollment_object):
|
||||
return None
|
||||
|
||||
mode = CourseMode.verified_mode_for_course(self.course_key)
|
||||
return {
|
||||
'access_expiration_date': get_user_course_expiration_date(self.effective_user, self.overview),
|
||||
'price': mode.min_price,
|
||||
'currency': mode.currency.upper(),
|
||||
'currency_symbol': get_currency_symbol(mode.currency.upper()),
|
||||
'sku': mode.sku,
|
||||
'upgrade_url': verified_upgrade_deadline_link(self.effective_user, self.overview),
|
||||
}
|
||||
return serialize_upgrade_info(self.effective_user, self.overview, self.enrollment_object)
|
||||
|
||||
@property
|
||||
def notes(self):
|
||||
|
||||
Reference in New Issue
Block a user