diff --git a/lms/djangoapps/learner_home/test_views.py b/lms/djangoapps/learner_home/test_views.py index 0ae90eb651..e849352072 100644 --- a/lms/djangoapps/learner_home/test_views.py +++ b/lms/djangoapps/learner_home/test_views.py @@ -823,6 +823,21 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase): password = "test" url = reverse_lazy("learner_home:courses") + GENERAL_RECOMMENDATIONS = [ + { + "course_key": "HogwartsX+6.00.1x", + "logo_image_url": random_url(), + "marketing_url": random_url(), + "title": "Defense Against the Dark Arts", + }, + { + "course_key": "MonstersX+SC101EN", + "logo_image_url": random_url(), + "marketing_url": random_url(), + "title": "Scaring 101", + }, + ] + def setUp(self): super().setUp() self.user = UserFactory() @@ -849,10 +864,10 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase): @override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=False) def test_waffle_flag_off(self): """ - Verify API returns 400 if waffle flag is off. + Verify API returns 404 if waffle flag is off. """ response = self.client.get(self.url) - self.assertEqual(response.status_code, 400) + self.assertEqual(response.status_code, 404) self.assertEqual(response.data, None) @override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True) @@ -864,13 +879,25 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase): self, mocked_get_course_data, mocked_get_personalized_course_recommendations ): """ - Verify API returns 400 if no course recommendations from amplitude. + Verify API returns 404 if no course recommendations from amplitude. """ mocked_get_personalized_course_recommendations.return_value = [False, []] mocked_get_course_data.return_value = self.course_data response = self.client.get(self.url) - self.assertEqual(response.status_code, 400) + self.assertEqual(response.status_code, 404) + self.assertEqual(response.data, None) + + @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) + ) + def test_amplitude_api_unexpected_error(self): + """ + Test that if the Amplitude API gives an unexpected error, 500 is returned. + """ + response = self.client.get(self.url) + self.assertEqual(response.status_code, 500) self.assertEqual(response.data, None) @override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True) @@ -898,6 +925,25 @@ class TestCourseRecommendationApiView(SharedModuleStoreTestCase): len(response.data.get("courses")), expected_recommendations_length ) + @override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True) + @mock.patch("django.conf.settings.GENERAL_RECOMMENDATIONS", GENERAL_RECOMMENDATIONS) + @mock.patch( + "lms.djangoapps.learner_home.views.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. + """ + mocked_get_personalized_course_recommendations.return_value = [ + True, + self.recommended_courses, + ] + + 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) + @override_waffle_flag(ENABLE_LEARNER_HOME_AMPLITUDE_RECOMMENDATIONS, active=True) @mock.patch( "lms.djangoapps.learner_home.views.get_personalized_course_recommendations" diff --git a/lms/djangoapps/learner_home/utils.py b/lms/djangoapps/learner_home/utils.py index 25aaf8c8de..e9af415c40 100644 --- a/lms/djangoapps/learner_home/utils.py +++ b/lms/djangoapps/learner_home/utils.py @@ -93,16 +93,13 @@ def get_personalized_course_recommendations(user_id): "get_recs": True, "rec_id": settings.REC_ID, } - try: - response = requests.get(settings.AMPLITUDE_URL, params=params, headers=headers) - if response.status_code == 200: - response = response.json() - recommendations = response.get("userData", {}).get("recommendations", []) - if recommendations: - is_control = recommendations[0].get("is_control") - recommended_course_keys = recommendations[0].get("items") - return is_control, recommended_course_keys - except Exception as ex: # pylint: disable=broad-except - log.warning(f"Cannot get recommendations from Amplitude: {ex}") + response = requests.get(settings.AMPLITUDE_URL, params=params, headers=headers) + if response.status_code == 200: + response = response.json() + recommendations = response.get("userData", {}).get("recommendations", []) + if recommendations: + is_control = recommendations[0].get("is_control") + recommended_course_keys = recommendations[0].get("items") + return is_control, recommended_course_keys return True, [] diff --git a/lms/djangoapps/learner_home/views.py b/lms/djangoapps/learner_home/views.py index 9f20ba0b97..b2e2f62f9c 100644 --- a/lms/djangoapps/learner_home/views.py +++ b/lms/djangoapps/learner_home/views.py @@ -543,6 +543,8 @@ class InitializeView(RetrieveAPIView): # pylint: disable=unused-argument class CourseRecommendationApiView(APIView): """ + API to get personalized recommendations from Amplitude. + **Example Request** GET /api/learner_home/recommendation/courses/ @@ -555,12 +557,18 @@ class CourseRecommendationApiView(APIView): permission_classes = (IsAuthenticated,) def get(self, request): - """Retrieves course recommendations details of a user in a specified course.""" + """ + Retrieves course recommendations details. + """ if not should_show_learner_home_amplitude_recommendations(): - return Response(status=400) + return Response(status=404) - user_id = request.user.id - is_control, course_keys = get_personalized_course_recommendations(user_id) + try: + user_id = request.user.id + is_control, course_keys = get_personalized_course_recommendations(user_id) + except Exception as ex: # pylint: disable=broad-except + logger.warning(f"Cannot get recommendations from Amplitude: {ex}") + return Response(status=500) # Emits an event to track student dashboard page visits. segment.track( @@ -571,8 +579,17 @@ class CourseRecommendationApiView(APIView): }, ) - if is_control or not course_keys: - return Response(status=400) + if is_control: + return Response( + { + "courses": settings.GENERAL_RECOMMENDATIONS, + "is_personalized_recommendation": False, + }, + status=200, + ) + + if not course_keys: + return Response(status=404) recommended_courses = [] user_enrolled_course_keys = set() diff --git a/lms/envs/common.py b/lms/envs/common.py index 05c6d0b7ff..075a993393 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -4753,8 +4753,11 @@ EDX_BRAZE_API_SERVER = None AMPLITUDE_URL = '' AMPLITUDE_API_KEY = '' REC_ID = '' +# Keeping this for back compatibility with learner dashboard api GENERAL_RECOMMENDATION = {} +GENERAL_RECOMMENDATIONS = [] + ############### Settings for Retirement ##################### # .. setting_name: RETIRED_USERNAME_PREFIX # .. setting_default: retired__user_