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:
Michael Terry
2020-11-24 12:03:04 -05:00
parent 84930bd5e6
commit ae561256cb
8 changed files with 96 additions and 31 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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
"""

View File

@@ -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',
})

View File

@@ -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)

View File

@@ -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))

View 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),
}

View File

@@ -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):