Skip to content

Commit

Permalink
feat: integrated resumeCourseRunUrl into enrollments API
Browse files Browse the repository at this point in the history
  • Loading branch information
mahamakifdar19 committed Nov 30, 2023
1 parent 90bf5ca commit 2582587
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 6 deletions.
1 change: 1 addition & 0 deletions enterprise_learner_portal/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def to_representation(self, instance):
representation['is_revoked'] = instance.license.is_revoked if instance.license else False
representation['is_enrollment_active'] = instance.is_active
representation['mode'] = instance.mode
representation['resume_course_run_url'] = self.context['course_enrollments_resume_urls'].get(course_run_id)

if CourseDetails:
course_details = CourseDetails.objects.filter(id=course_run_id).first()
Expand Down
18 changes: 17 additions & 1 deletion enterprise_learner_portal/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
from openedx.core.djangoapps.content.course_overviews.api import get_course_overviews
except ImportError:
get_course_overviews = None
try:
from lms.djangoapps.learner_home.views import get_resume_urls_for_course_enrollments
except ImportError:
get_resume_urls_for_course_enrollments = None


class EnterpriseCourseEnrollmentView(APIView):
Expand Down Expand Up @@ -86,10 +90,22 @@ def get(self, request):

course_overviews = get_course_overviews([record.course_id for record in filtered_enterprise_enrollments])

if get_resume_urls_for_course_enrollments:
course_enrollments_resume_urls = get_resume_urls_for_course_enrollments(
user,
list(filtered_enterprise_enrollments)
)

data = EnterpriseCourseEnrollmentSerializer(
filtered_enterprise_enrollments,
many=True,
context={'request': request, 'course_overviews': course_overviews},
context={
'request': request,
'course_overviews': course_overviews,
'course_enrollments_resume_urls': (
course_enrollments_resume_urls if course_enrollments_resume_urls else None
)
},
).data

if request.query_params.get('is_active'):
Expand Down
10 changes: 9 additions & 1 deletion tests/test_enterprise_learner_portal/api/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ def test_serializer_representation(
'pacing': 'instructor',
'display_org_with_default': 'my university',
}]
course_enrollments_resume_urls = {
course_run_id: "http://example.com/resume_url",
}

mock_get_cert.return_value = {
'download_url': 'example.com',
Expand Down Expand Up @@ -93,7 +96,11 @@ def test_serializer_representation(
serializer = EnterpriseCourseEnrollmentSerializer(
[enterprise_enrollment],
many=True,
context={'request': request, 'course_overviews': course_overviews},
context={
'request': request,
'course_overviews': course_overviews,
'course_enrollments_resume_urls': course_enrollments_resume_urls
},
)

expected = OrderedDict([
Expand All @@ -112,6 +119,7 @@ def test_serializer_representation(
('is_revoked', False),
('is_enrollment_active', True),
('mode', 'verified'),
('resume_course_run_url', 'http://example.com/resume_url'),
])
actual = serializer.data[0]
self.assertDictEqual(actual, expected)
Expand Down
37 changes: 33 additions & 4 deletions tests/test_enterprise_learner_portal/api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,19 @@ def setUp(self):
self.client = Client()
self.client.login(username=self.user.username, password="QWERTY")

@mock.patch('enterprise_learner_portal.api.v1.views.get_resume_urls_for_course_enrollments')
@mock.patch('enterprise_learner_portal.api.v1.views.EnterpriseCourseEnrollmentSerializer')
@mock.patch('enterprise_learner_portal.api.v1.views.get_course_overviews')
def test_view_returns_information(self, mock_get_overviews, mock_serializer):
def test_view_returns_information(self, mock_get_overviews, mock_serializer, mock_course_resume_urls):
"""
View should return data created by EnterpriseCourseEnrollmentSerializer
(which we mock in this case)
"""
mock_get_overviews.return_value = {'overview_info': 'this would be a larger dict'}
mock_serializer.return_value = self.MockSerializer()
mock_course_resume_urls.return_value = {
'course-v1:edX+DemoX+Demo_Course': 'http://example.com/resume_url'
}

resp = self.client.get(
'{host}{path}?enterprise_id={enterprise_id}'.format(
Expand All @@ -92,6 +96,7 @@ def test_view_returns_information(self, mock_get_overviews, mock_serializer):
SERIALIZED_MOCK_INACTIVE_ENROLLMENT,
]

@mock.patch('enterprise_learner_portal.api.v1.views.get_resume_urls_for_course_enrollments')
@mock.patch('enterprise_learner_portal.api.v1.views.EnterpriseCourseEnrollmentSerializer')
@mock.patch('enterprise_learner_portal.api.v1.views.get_course_overviews')
@ddt.data('true', 'false')
Expand All @@ -100,13 +105,17 @@ def test_view_get_filters_active_enrollments(
active_filter_value,
mock_get_overviews,
mock_serializer,
mock_course_resume_urls,
):
"""
View should return data created by EnterpriseCourseEnrollmentSerializer
(which we mock in this case)
"""
mock_get_overviews.return_value = {'overview_info': 'this would be a larger dict'}
mock_serializer.return_value = self.MockSerializer()
mock_course_resume_urls.return_value = {
'course-v1:edX+DemoX+Demo_Course': 'http://example.com/resume_url'
}

resp = self.client.get(
'{host}{path}?enterprise_id={enterprise_id}&is_active={active_filter_value}'.format(
Expand All @@ -123,14 +132,23 @@ def test_view_get_filters_active_enrollments(
expected_result = [SERIALIZED_MOCK_INACTIVE_ENROLLMENT]
assert resp.json() == expected_result

@mock.patch('enterprise_learner_portal.api.v1.views.get_resume_urls_for_course_enrollments')
@mock.patch('enterprise_learner_portal.api.v1.views.EnterpriseCourseEnrollmentSerializer')
@mock.patch('enterprise_learner_portal.api.v1.views.get_course_overviews')
def test_view_returns_bad_request_without_enterprise(self, mock_get_overviews, mock_serializer):
def test_view_returns_bad_request_without_enterprise(
self,
mock_get_overviews,
mock_serializer,
mock_course_resume_urls
):
"""
View should return a 400 because of the missing enterprise_id parameter.
"""
mock_get_overviews.return_value = {'overview_info': 'this would be a larger dict'}
mock_serializer.return_value = self.MockSerializer()
mock_course_resume_urls.return_value = {
'course-v1:edX+DemoX+Demo_Course': 'http://example.com/resume_url'
}

resp = self.client.get(
'{host}{path}'.format(
Expand All @@ -141,14 +159,23 @@ def test_view_returns_bad_request_without_enterprise(self, mock_get_overviews, m
assert resp.status_code == 400
assert resp.json() == {'error': 'enterprise_id must be provided as a query parameter'}

@mock.patch('enterprise_learner_portal.api.v1.views.get_resume_urls_for_course_enrollments')
@mock.patch('enterprise_learner_portal.api.v1.views.EnterpriseCourseEnrollmentSerializer')
@mock.patch('enterprise_learner_portal.api.v1.views.get_course_overviews')
def test_view_returns_not_found_unlinked_enterprise(self, mock_get_overviews, mock_serializer):
def test_view_returns_not_found_unlinked_enterprise(
self,
mock_get_overviews,
mock_serializer,
mock_course_resume_urls
):
"""
View should return a 404 because the user is not linked to the enterprise.
"""
mock_get_overviews.return_value = {'overview_info': 'this would be a larger dict'}
mock_serializer.return_value = self.MockSerializer()
mock_course_resume_urls.return_value = {
'course-v1:edX+DemoX+Demo_Course': 'http://example.com/resume_url'
}

resp = self.client.get(
'{host}{path}?enterprise_id={enterprise_id}'.format(
Expand All @@ -160,14 +187,16 @@ def test_view_returns_not_found_unlinked_enterprise(self, mock_get_overviews, mo
assert resp.status_code == 404
assert resp.json() == {'detail': 'Not found.'}

@mock.patch('enterprise_learner_portal.api.v1.views.get_resume_urls_for_course_enrollments')
@mock.patch('enterprise_learner_portal.api.v1.serializers.get_certificate_for_user', mock.MagicMock())
@mock.patch('enterprise_learner_portal.api.v1.views.get_course_overviews')
def test_view_filters_out_invalid_enterprise_enrollments(self, mock_get_overviews):
def test_view_filters_out_invalid_enterprise_enrollments(self, mock_get_overviews, mock_course_resume_urls):
"""
View does not fail, and view filters out all enrollments whose course_enrollment
field is None
"""
mock_get_overviews.return_value = {}
mock_course_resume_urls.return_value = {}

resp = self.client.get(
'{host}{path}?enterprise_id={enterprise_id}&is_active=true'.format(
Expand Down

0 comments on commit 2582587

Please sign in to comment.