Merge pull request #28754 from edx/dsheraz/PROD-2477
feat: add support-only endpoint to get FBE details
This commit is contained in:
@@ -40,6 +40,8 @@ from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from lms.djangoapps.verify_student.tests.factories import SSOVerificationFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -1144,3 +1146,46 @@ class SsoRecordsTests(SupportViewTestCase): # lint-amnesty, pylint: disable=mis
|
||||
assert response.status_code == 200
|
||||
assert len(data) == 1
|
||||
self.assertContains(response, '"uid": "test@example.com"')
|
||||
|
||||
|
||||
class FeatureBasedEnrollmentSupportApiViewTests(SupportViewTestCase):
|
||||
"""
|
||||
Test suite for FBE Support API view.
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
SupportStaffRole().add_users(self.user)
|
||||
|
||||
def test_fbe_enabled_response(self):
|
||||
"""
|
||||
Test the response for the api view when the gating and duration configs
|
||||
are enabled.
|
||||
"""
|
||||
for course_mode in [CourseMode.AUDIT, CourseMode.VERIFIED]:
|
||||
CourseModeFactory.create(mode_slug=course_mode, course_id=self.course.id)
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
response = self.client.get(
|
||||
reverse("support:feature_based_enrollment_details", kwargs={'course_id': str(self.course.id)})
|
||||
)
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
gating_config = data['gating_config']
|
||||
duration_config = data['duration_config']
|
||||
|
||||
assert str(self.course.id) == data['course_id']
|
||||
assert gating_config['enabled']
|
||||
assert gating_config['enabled_as_of'] == '2018-01-01 00:00:00+00:00'
|
||||
assert duration_config['enabled']
|
||||
assert duration_config['enabled_as_of'] == '2018-01-01 00:00:00+00:00'
|
||||
|
||||
def test_fbe_disabled_response(self):
|
||||
"""
|
||||
Test the FBE support api view response to be empty when no gating and duration
|
||||
config is present.
|
||||
"""
|
||||
response = self.client.get(
|
||||
reverse("support:feature_based_enrollment_details", kwargs={'course_id': str(self.course.id)})
|
||||
)
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
assert data == {}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
URLs for the student support app.
|
||||
"""
|
||||
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views.certificate import CertificatesSupportView
|
||||
from .views.contact_us import ContactUsView
|
||||
from .views.course_entitlements import EntitlementSupportView
|
||||
from .views.enrollments import EnrollmentSupportListView, EnrollmentSupportView
|
||||
from .views.feature_based_enrollments import FeatureBasedEnrollmentsSupportView
|
||||
from .views.feature_based_enrollments import FeatureBasedEnrollmentsSupportView, FeatureBasedEnrollmentSupportAPIView
|
||||
from .views.index import index
|
||||
from .views.manage_user import ManageUserDetailView, ManageUserSupportView
|
||||
from .views.program_enrollments import LinkProgramEnrollmentSupportView, ProgramEnrollmentsInspectorView
|
||||
@@ -40,6 +40,11 @@ urlpatterns = [
|
||||
FeatureBasedEnrollmentsSupportView.as_view(),
|
||||
name="feature_based_enrollments"
|
||||
),
|
||||
url(
|
||||
fr'^feature_based_enrollment_details/{settings.COURSE_ID_PATTERN}$',
|
||||
FeatureBasedEnrollmentSupportAPIView.as_view(),
|
||||
name="feature_based_enrollment_details"
|
||||
),
|
||||
url(r'link_program_enrollments/?$', LinkProgramEnrollmentSupportView.as_view(), name='link_program_enrollments'),
|
||||
url(
|
||||
r'program_enrollments_inspector/?$',
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
Support tool for viewing course duration information
|
||||
"""
|
||||
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import View
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.generics import GenericAPIView
|
||||
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_response
|
||||
from common.djangoapps.util.json_request import JsonResponse
|
||||
from lms.djangoapps.support.decorators import require_support_permission
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
from lms.djangoapps.support.views.utils import get_course_duration_info
|
||||
|
||||
|
||||
class FeatureBasedEnrollmentsSupportView(View):
|
||||
@@ -29,7 +28,7 @@ class FeatureBasedEnrollmentsSupportView(View):
|
||||
course_key = request.GET.get('course_key', '')
|
||||
|
||||
if course_key:
|
||||
results = self._get_course_duration_info(course_key)
|
||||
results = get_course_duration_info(course_key)
|
||||
else:
|
||||
results = {}
|
||||
|
||||
@@ -38,35 +37,40 @@ class FeatureBasedEnrollmentsSupportView(View):
|
||||
'results': results,
|
||||
})
|
||||
|
||||
def _get_course_duration_info(self, course_key):
|
||||
|
||||
class FeatureBasedEnrollmentSupportAPIView(GenericAPIView):
|
||||
"""
|
||||
Support-only API View for getting feature based enrollment configuration details
|
||||
for a course.
|
||||
"""
|
||||
authentication_classes = (
|
||||
JwtAuthentication, SessionAuthentication
|
||||
)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
@method_decorator(require_support_permission)
|
||||
def get(self, request, course_id):
|
||||
"""
|
||||
Fetch course duration information from database
|
||||
Returns the duration config information if FBE is enabled. If
|
||||
FBE is not enabled, empty dict is returned.
|
||||
|
||||
* Example Request:
|
||||
- GET /support/feature_based_enrollment_details/<course_id>
|
||||
|
||||
* Example Response:
|
||||
{
|
||||
"course_id": <course_id>,
|
||||
"course_name": "FBE course",
|
||||
"gating_config": {
|
||||
"enabled": true,
|
||||
"enabled_as_of": "2030-01-01 00:00:00+00:00",
|
||||
"reason": "Site"
|
||||
},
|
||||
"duration_config": {
|
||||
"enabled": true,
|
||||
"enabled_as_of": "2030-01-01 00:00:00+00:00",
|
||||
"reason": "Site"
|
||||
}
|
||||
}
|
||||
"""
|
||||
try:
|
||||
key = CourseKey.from_string(course_key)
|
||||
course = CourseOverview.objects.values('display_name').get(id=key)
|
||||
duration_config = CourseDurationLimitConfig.current(course_key=key)
|
||||
gating_config = ContentTypeGatingConfig.current(course_key=key)
|
||||
duration_enabled = CourseDurationLimitConfig.enabled_for_course(course_key=key)
|
||||
gating_enabled = ContentTypeGatingConfig.enabled_for_course(course_key=key)
|
||||
|
||||
gating_dict = {
|
||||
'enabled': gating_enabled,
|
||||
'enabled_as_of': str(gating_config.enabled_as_of) if gating_config.enabled_as_of else 'N/A',
|
||||
'reason': gating_config.provenances['enabled'].value
|
||||
}
|
||||
duration_dict = {
|
||||
'enabled': duration_enabled,
|
||||
'enabled_as_of': str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A',
|
||||
'reason': duration_config.provenances['enabled'].value
|
||||
}
|
||||
|
||||
return {
|
||||
'course_id': course_key,
|
||||
'course_name': course.get('display_name'),
|
||||
'gating_config': gating_dict,
|
||||
'duration_config': duration_dict,
|
||||
}
|
||||
|
||||
except (ObjectDoesNotExist, InvalidKeyError):
|
||||
return {}
|
||||
return JsonResponse(get_course_duration_info(course_id))
|
||||
|
||||
44
lms/djangoapps/support/views/utils.py
Normal file
44
lms/djangoapps/support/views/utils.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Various utility methods used by support app views.
|
||||
"""
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
|
||||
|
||||
def get_course_duration_info(course_key):
|
||||
"""
|
||||
Fetch course duration information from database.
|
||||
"""
|
||||
try:
|
||||
key = CourseKey.from_string(course_key)
|
||||
course = CourseOverview.objects.values('display_name').get(id=key)
|
||||
duration_config = CourseDurationLimitConfig.current(course_key=key)
|
||||
gating_config = ContentTypeGatingConfig.current(course_key=key)
|
||||
duration_enabled = CourseDurationLimitConfig.enabled_for_course(course_key=key)
|
||||
gating_enabled = ContentTypeGatingConfig.enabled_for_course(course_key=key)
|
||||
|
||||
gating_dict = {
|
||||
'enabled': gating_enabled,
|
||||
'enabled_as_of': str(gating_config.enabled_as_of) if gating_config.enabled_as_of else 'N/A',
|
||||
'reason': gating_config.provenances['enabled'].value
|
||||
}
|
||||
duration_dict = {
|
||||
'enabled': duration_enabled,
|
||||
'enabled_as_of': str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A',
|
||||
'reason': duration_config.provenances['enabled'].value
|
||||
}
|
||||
|
||||
return {
|
||||
'course_id': course_key,
|
||||
'course_name': course.get('display_name'),
|
||||
'gating_config': gating_dict,
|
||||
'duration_config': duration_dict,
|
||||
}
|
||||
|
||||
except (ObjectDoesNotExist, InvalidKeyError):
|
||||
return {}
|
||||
Reference in New Issue
Block a user