From a99826b1ef44720ed92d1270e7f95acfa377e406 Mon Sep 17 00:00:00 2001 From: Shahbaz Shabbir <32649010+shahbaz-arbisoft@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:32:57 +0500 Subject: [PATCH] revert: remove algolia API endpoint and unit tests from lms (#31868) --- .../tests/test_views.py | 123 ------------------ .../learner_recommendations/urls.py | 3 - .../learner_recommendations/utils.py | 43 ------ .../learner_recommendations/views.py | 34 ----- 4 files changed, 203 deletions(-) diff --git a/lms/djangoapps/learner_recommendations/tests/test_views.py b/lms/djangoapps/learner_recommendations/tests/test_views.py index 9e5c639abb..1370c588ac 100644 --- a/lms/djangoapps/learner_recommendations/tests/test_views.py +++ b/lms/djangoapps/learner_recommendations/tests/test_views.py @@ -14,129 +14,6 @@ from lms.djangoapps.learner_recommendations.toggles import ( ) -class TestAlgoliaCoursesSearchView(APITestCase): - """Unit tests for the Algolia courses recommendation.""" - - password = "test" - view_url = reverse_lazy( - "learner_recommendations:algolia_courses", - kwargs={'course_id': 'course-v1:test+TestX+Test_Course'} - ) - - def setUp(self): - super().setUp() - self.user = UserFactory() - self.expected_courses_recommendation = { - "hits": [ - { - "availability": ["Available now"], - "level": ["Introductory"], - "marketing_url": "https://marketing-site.com/course/monsters-anatomy-101", - "card_image_url": "https://card-site.com/course/monsters-anatomy-101", - "active_run_key": "course-v1:test+TestX+Test_Course_1", - "skills": [{"skill": "skill_1"}, {"skill": "skill_2"}], - }, - { - "availability": ["Available now"], - "level": ["Intermediate"], - "marketing_url": "https://marketing-site.com/course/monsters-anatomy-101", - "card_image_url": "https://card-site.com/course/monsters-anatomy-101", - "active_run_key": "course-v1:test+TestX+Test_Course_2", - "skills": [{"skill": "skill_1"}, {"skill": "skill_2"}], - } - ], - "nbHits": 2 - } - - def test_unauthenticated_request(self): - """ - Test unauthenticated request to Algolia courses recommendation API view. - """ - - response = self.client.get(self.view_url) - self.assertEqual(response.status_code, 401) - - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_data" - ) - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_run_details" - ) - def test_no_course_data( - self, - mocked_get_course_run_details, - mocked_get_course_data - ): - """ - Verify API returns empty response if no course data found. - """ - mocked_get_course_run_details.return_value = {"course": "edX+DemoX"} - mocked_get_course_data.return_value = None - - self.client.login(username=self.user.username, password=self.password) - response = self.client.get(self.view_url) - self.assertEqual(response.status_code, 200) - - response_content = json.loads(response.content) - self.assertEqual(response_content.get("courses"), []) - self.assertEqual(response_content.get("count"), 0) - - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_data" - ) - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_run_details" - ) - def test_no_course_skill_names( - self, - mocked_get_course_run_details, - mocked_get_course_data - ): - """ - Verify API returns empty response if no course skill_names found. - """ - mocked_get_course_run_details.return_value = {"course": "edX+DemoX"} - mocked_get_course_data.return_value = {"level_type": "Advanced", "skill_names": []} - - self.client.login(username=self.user.username, password=self.password) - response = self.client.get(self.view_url) - self.assertEqual(response.status_code, 200) - - response_content = json.loads(response.content) - self.assertEqual(response_content.get("courses"), []) - self.assertEqual(response_content.get("count"), 0) - - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_algolia_courses_recommendation" - ) - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_run_details" - ) - @mock.patch( - "lms.djangoapps.learner_recommendations.views.get_course_data" - ) - def test_recommendations( - self, - mocked_get_course_data, - mocked_get_course_run_details, - mocked_get_algolia_courses_recommendation - ): - """ - Verify API response structure. - """ - mocked_get_course_run_details.return_value = {"course": "edX+DemoX"} - mocked_get_course_data.return_value = {"level_type": "Advanced", "skill_names": ["skill_1", "skill_2"]} - mocked_get_algolia_courses_recommendation.return_value = self.expected_courses_recommendation - - self.client.login(username=self.user.username, password=self.password) - response = self.client.get(self.view_url) - self.assertEqual(response.status_code, 200) - - response_content = json.loads(response.content) - self.assertEqual(response_content.get("courses"), self.expected_courses_recommendation["hits"]) - self.assertEqual(response_content.get("count"), self.expected_courses_recommendation["nbHits"]) - - @override_waffle_flag(ENABLE_COURSE_ABOUT_PAGE_RECOMMENDATIONS, active=True) class TestAmplitudeRecommendationsView(APITestCase): """Unit tests for the Amplitude recommendations API""" diff --git a/lms/djangoapps/learner_recommendations/urls.py b/lms/djangoapps/learner_recommendations/urls.py index 63d4371d7c..2e082c18f4 100644 --- a/lms/djangoapps/learner_recommendations/urls.py +++ b/lms/djangoapps/learner_recommendations/urls.py @@ -10,9 +10,6 @@ from lms.djangoapps.learner_recommendations import views app_name = "learner_recommendations" urlpatterns = [ - re_path(fr'^algolia/courses/{settings.COURSE_ID_PATTERN}/$', - views.AlgoliaCoursesSearchView.as_view(), - name='algolia_courses'), re_path(fr'^amplitude/{settings.COURSE_ID_PATTERN}/$', views.AmplitudeRecommendationsView.as_view(), name='amplitude_recommendations'), diff --git a/lms/djangoapps/learner_recommendations/utils.py b/lms/djangoapps/learner_recommendations/utils.py index ef493bf46f..8efe062dff 100644 --- a/lms/djangoapps/learner_recommendations/utils.py +++ b/lms/djangoapps/learner_recommendations/utils.py @@ -4,7 +4,6 @@ Additional utilities for Learner Recommendations. import logging import requests -from algoliasearch.exceptions import RequestException, AlgoliaUnreachableHostException from algoliasearch.search_client import SearchClient from django.conf import settings @@ -114,48 +113,6 @@ def _get_program_duration(weeks): return f'{total_years} years {total_remainder_months} months' -def get_algolia_courses_recommendation(course_data): - """ - Get courses recommendation from Algolia search. - - Args: - course_data (dict): Course data to create the search query. - - Returns: - Response object with courses recommendation from Algolia search. - """ - algolia_client = AlgoliaClient.get_algolia_client() - - search_query = " ".join(course_data["skill_names"]) - searchable_course_levels = [ - f"level:{course_level}" - for course_level in COURSE_LEVELS - if course_level != course_data["level_type"] - ] - if algolia_client and search_query: - algolia_index = algolia_client.init_index(settings.ALGOLIA_COURSES_RECOMMENDATION_INDEX_NAME) - try: - # Algolia search filter criteria: - # - Product type: Course - # - Courses are available (enrollable) - # - Courses should not have the same course level as the current course - # - Exclude current course from the results - results = algolia_index.search( - search_query, - { - "filters": f"NOT active_run_key:'{course_data['key']}'", - "facetFilters": ["availability:Available now", "product:Course", searchable_course_levels], - "optionalWords": f"{search_query}", - } - ) - - return results - except (AlgoliaUnreachableHostException, RequestException) as ex: - log.warning(f"Unexpected exception while attempting to fetch courses data from Algolia: {str(ex)}") - - return {} - - def get_amplitude_course_recommendations(user_id, recommendation_id): """ Get personalized recommendations from Amplitude. diff --git a/lms/djangoapps/learner_recommendations/views.py b/lms/djangoapps/learner_recommendations/views.py index 28956a8be3..cb6ef802ce 100644 --- a/lms/djangoapps/learner_recommendations/views.py +++ b/lms/djangoapps/learner_recommendations/views.py @@ -16,15 +16,10 @@ from rest_framework.response import Response from rest_framework.views import APIView from common.djangoapps.track import segment -from openedx.core.djangoapps.catalog.utils import ( - get_course_data, - get_course_run_details, -) from openedx.core.djangoapps.geoinfo.api import country_code_from_ip from openedx.features.enterprise_support.utils import is_enterprise_learner from lms.djangoapps.learner_recommendations.toggles import enable_course_about_page_recommendations from lms.djangoapps.learner_recommendations.utils import ( - get_algolia_courses_recommendation, get_amplitude_course_recommendations, filter_recommended_courses, ) @@ -34,35 +29,6 @@ from lms.djangoapps.learner_recommendations.serializers import RecommendationsSe log = logging.getLogger(__name__) -class AlgoliaCoursesSearchView(APIView): - """ - **Example Request** - - GET api/learner_recommendations/algolia/courses/{course_id}/ - """ - - authentication_classes = (JwtAuthentication, SessionAuthenticationAllowInactiveUser,) - permission_classes = (IsAuthenticated,) - - def get(self, request, course_id): - """ Retrieves course recommendations from Algolia based on course skills. """ - - course_run_data = get_course_run_details(course_id, ["course"]) - course_key_str = course_run_data.get("course", None) - - # Fetching course level type and skills from discovery service. - course_data = get_course_data(course_key_str, ["level_type", "skill_names"]) - - # If discovery service fails to fetch data, we will not run recommendations engine. - if not course_data: - return Response({"courses": [], "count": 0}, status=200) - - course_data["key"] = course_id - response = get_algolia_courses_recommendation(course_data) - - return Response({"courses": response.get("hits", []), "count": response.get("nbHits", 0)}, status=200) - - class AmplitudeRecommendationsView(APIView): """ **Example Request**