feat: add serializer for recommendations api (#31270)
This commit is contained in:
@@ -515,6 +515,26 @@ class UnfulfilledEntitlementSerializer(serializers.Serializer):
|
||||
).data
|
||||
|
||||
|
||||
class RecommendedCourseSerializer(serializers.Serializer):
|
||||
"""Serializer for a recommended course from the recommendation engine"""
|
||||
|
||||
courseKey = serializers.CharField(source="course_key")
|
||||
logoImageUrl = serializers.URLField(source="logo_image_url")
|
||||
marketingUrl = serializers.URLField(source="marketing_url")
|
||||
title = serializers.CharField()
|
||||
|
||||
|
||||
class CourseRecommendationSerializer(serializers.Serializer):
|
||||
"""Recommended courses by the Amplitude"""
|
||||
|
||||
courses = serializers.ListField(
|
||||
child=RecommendedCourseSerializer(), allow_empty=True
|
||||
)
|
||||
isPersonalizedRecommendation = serializers.BooleanField(
|
||||
source="is_personalized_recommendation"
|
||||
)
|
||||
|
||||
|
||||
class SuggestedCourseSerializer(serializers.Serializer):
|
||||
"""Serializer for a suggested course from recommendation engine"""
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from openedx.core.djangoapps.content.course_overviews.tests.factories import (
|
||||
from lms.djangoapps.learner_home.serializers import (
|
||||
CertificateSerializer,
|
||||
CourseProviderSerializer,
|
||||
CourseRecommendationSerializer,
|
||||
CourseRunSerializer,
|
||||
CourseSerializer,
|
||||
EmailConfirmationSerializer,
|
||||
@@ -960,6 +961,81 @@ class TestUnfulfilledEntitlementSerializer(LearnerDashboardBaseTest):
|
||||
assert expected_keys == actual_keys
|
||||
|
||||
|
||||
class TestCourseRecommendationSerializer(TestCase):
|
||||
"""High-level tests for CourseRecommendationSerializer"""
|
||||
|
||||
@classmethod
|
||||
def mock_recommended_courses(cls, courses_count=2):
|
||||
"""Sample course data"""
|
||||
|
||||
recommended_courses = []
|
||||
|
||||
for _ in range(courses_count):
|
||||
recommended_courses.append(
|
||||
{
|
||||
"course_key": str(uuid4()),
|
||||
"logo_image_url": random_url(),
|
||||
"marketing_url": random_url(),
|
||||
"title": str(uuid4()),
|
||||
},
|
||||
)
|
||||
|
||||
return recommended_courses
|
||||
|
||||
def test_no_recommended_courses(self):
|
||||
"""That that data serializes correctly for empty courses list"""
|
||||
|
||||
recommended_courses = self.mock_recommended_courses(courses_count=0)
|
||||
|
||||
output_data = CourseRecommendationSerializer(
|
||||
{
|
||||
"courses": recommended_courses,
|
||||
"is_personalized_recommendation": False,
|
||||
}
|
||||
).data
|
||||
|
||||
self.assertDictEqual(
|
||||
output_data,
|
||||
{
|
||||
"courses": [],
|
||||
"isPersonalizedRecommendation": False,
|
||||
},
|
||||
)
|
||||
|
||||
def test_happy_path(self):
|
||||
"""Test that data serializes correctly"""
|
||||
|
||||
recommended_courses = self.mock_recommended_courses()
|
||||
|
||||
output_data = CourseRecommendationSerializer(
|
||||
{
|
||||
"courses": recommended_courses,
|
||||
"is_personalized_recommendation": True,
|
||||
}
|
||||
).data
|
||||
|
||||
self.assertDictEqual(
|
||||
output_data,
|
||||
{
|
||||
"courses": [
|
||||
{
|
||||
"courseKey": recommended_courses[0]["course_key"],
|
||||
"logoImageUrl": recommended_courses[0]["logo_image_url"],
|
||||
"marketingUrl": recommended_courses[0]["marketing_url"],
|
||||
"title": recommended_courses[0]["title"],
|
||||
},
|
||||
{
|
||||
"courseKey": recommended_courses[1]["course_key"],
|
||||
"logoImageUrl": recommended_courses[1]["logo_image_url"],
|
||||
"marketingUrl": recommended_courses[1]["marketing_url"],
|
||||
"title": recommended_courses[1]["title"],
|
||||
},
|
||||
],
|
||||
"isPersonalizedRecommendation": True,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class TestSuggestedCourseSerializer(TestCase):
|
||||
"""High-level tests for SuggestedCourseSerializer"""
|
||||
|
||||
|
||||
@@ -890,7 +890,8 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase):
|
||||
|
||||
@override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True)
|
||||
@mock.patch(
|
||||
"lms.djangoapps.learner_home.views.get_personalized_course_recommendations", Mock(side_effect=Exception)
|
||||
"lms.djangoapps.learner_home.views.get_personalized_course_recommendations",
|
||||
Mock(side_effect=Exception),
|
||||
)
|
||||
def test_amplitude_api_unexpected_error(self):
|
||||
"""
|
||||
@@ -920,9 +921,11 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase):
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data.get("is_personalized_recommendation"), True)
|
||||
|
||||
response_content = json.loads(response.content)
|
||||
self.assertEqual(response_content.get("isPersonalizedRecommendation"), True)
|
||||
self.assertEqual(
|
||||
len(response.data.get("courses")), expected_recommendations_length
|
||||
len(response_content.get("courses")), expected_recommendations_length
|
||||
)
|
||||
|
||||
@override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True)
|
||||
@@ -930,7 +933,9 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase):
|
||||
@mock.patch(
|
||||
"lms.djangoapps.learner_home.views.get_personalized_course_recommendations"
|
||||
)
|
||||
def test_general_recommendations(self, mocked_get_personalized_course_recommendations):
|
||||
def test_general_recommendations(
|
||||
self, mocked_get_personalized_course_recommendations
|
||||
):
|
||||
"""
|
||||
Test that a user gets general recommendations for the control group.
|
||||
"""
|
||||
@@ -941,8 +946,26 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase):
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data.get("is_personalized_recommendation"), False)
|
||||
self.assertEqual(response.data.get("courses"), self.GENERAL_RECOMMENDATIONS)
|
||||
|
||||
response_content = json.loads(response.content)
|
||||
self.assertEqual(response_content.get("isPersonalizedRecommendation"), False)
|
||||
self.assertEqual(
|
||||
response_content.get("courses"),
|
||||
[
|
||||
{
|
||||
"courseKey": self.GENERAL_RECOMMENDATIONS[0]["course_key"],
|
||||
"logoImageUrl": self.GENERAL_RECOMMENDATIONS[0]["logo_image_url"],
|
||||
"marketingUrl": self.GENERAL_RECOMMENDATIONS[0]["marketing_url"],
|
||||
"title": self.GENERAL_RECOMMENDATIONS[0]["title"],
|
||||
},
|
||||
{
|
||||
"courseKey": self.GENERAL_RECOMMENDATIONS[1]["course_key"],
|
||||
"logoImageUrl": self.GENERAL_RECOMMENDATIONS[1]["logo_image_url"],
|
||||
"marketingUrl": self.GENERAL_RECOMMENDATIONS[1]["marketing_url"],
|
||||
"title": self.GENERAL_RECOMMENDATIONS[1]["title"],
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
@override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True)
|
||||
@mock.patch(
|
||||
@@ -975,5 +998,7 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase):
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data.get("is_personalized_recommendation"), True)
|
||||
self.assertEqual(len(response.data.get("courses")), expected_recommendations)
|
||||
|
||||
response_content = json.loads(response.content)
|
||||
self.assertEqual(response_content.get("isPersonalizedRecommendation"), True)
|
||||
self.assertEqual(len(response_content.get("courses")), expected_recommendations)
|
||||
|
||||
@@ -48,7 +48,10 @@ from lms.djangoapps.courseware.access import administrative_accesses_to_course_f
|
||||
from lms.djangoapps.courseware.access_utils import (
|
||||
check_course_open_for_learner,
|
||||
)
|
||||
from lms.djangoapps.learner_home.serializers import LearnerDashboardSerializer
|
||||
from lms.djangoapps.learner_home.serializers import (
|
||||
CourseRecommendationSerializer,
|
||||
LearnerDashboardSerializer,
|
||||
)
|
||||
from lms.djangoapps.learner_home.waffle import (
|
||||
should_show_learner_home_amplitude_recommendations,
|
||||
)
|
||||
@@ -581,10 +584,12 @@ class CourseRecommendationApiView(APIView):
|
||||
|
||||
if is_control:
|
||||
return Response(
|
||||
{
|
||||
"courses": settings.GENERAL_RECOMMENDATIONS,
|
||||
"is_personalized_recommendation": False,
|
||||
},
|
||||
CourseRecommendationSerializer(
|
||||
{
|
||||
"courses": settings.GENERAL_RECOMMENDATIONS,
|
||||
"is_personalized_recommendation": False,
|
||||
}
|
||||
).data,
|
||||
status=200,
|
||||
)
|
||||
|
||||
@@ -622,9 +627,11 @@ class CourseRecommendationApiView(APIView):
|
||||
{"count": len(recommended_courses)},
|
||||
)
|
||||
return Response(
|
||||
{
|
||||
"courses": recommended_courses,
|
||||
"is_personalized_recommendation": not is_control,
|
||||
},
|
||||
CourseRecommendationSerializer(
|
||||
{
|
||||
"courses": recommended_courses,
|
||||
"is_personalized_recommendation": not is_control,
|
||||
}
|
||||
).data,
|
||||
status=200,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user