From 1a25a5d3fdd018e0ce44f2e6d720c203369b0a5c Mon Sep 17 00:00:00 2001 From: Muhammad Adeel Tajamul <77053848+muhammadadeeltajamul@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:53:50 +0500 Subject: [PATCH] feat: added post filters in learners tab api (#31191) Co-authored-by: adeel.tajamul --- lms/djangoapps/discussion/rest_api/api.py | 6 + .../discussion/rest_api/tests/test_views.py | 155 ++++++++++++++++++ lms/djangoapps/discussion/rest_api/views.py | 28 ++++ 3 files changed, 189 insertions(+) diff --git a/lms/djangoapps/discussion/rest_api/api.py b/lms/djangoapps/discussion/rest_api/api.py index 768a56340a..25183c6548 100644 --- a/lms/djangoapps/discussion/rest_api/api.py +++ b/lms/djangoapps/discussion/rest_api/api.py @@ -1003,9 +1003,15 @@ def get_learner_active_thread_list(request, course_key, query_params): group_id = query_params.get('group_id', None) user_id = query_params.get('user_id', None) + count_flagged = query_params.get('count_flagged', None) if user_id is None: return Response({'detail': 'Invalid user id'}, status=status.HTTP_400_BAD_REQUEST) + if count_flagged and not context["has_moderation_privilege"]: + raise PermissionDenied("count_flagged can only be set by users with moderation roles.") + if "flagged" in query_params.keys() and not context["has_moderation_privilege"]: + raise PermissionDenied("Flagged filter is only available for moderators") + if group_id is None: comment_client_user = comment_client.User(id=user_id, course_id=course_key) else: diff --git a/lms/djangoapps/discussion/rest_api/tests/test_views.py b/lms/djangoapps/discussion/rest_api/tests/test_views.py index b856efeb83..b69b60df9c 100644 --- a/lms/djangoapps/discussion/rest_api/tests/test_views.py +++ b/lms/djangoapps/discussion/rest_api/tests/test_views.py @@ -1452,6 +1452,7 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): assert response.status_code == 404 +@ddt.ddt @httpretty.activate @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True}) class LearnerThreadViewAPITest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): @@ -1601,6 +1602,160 @@ class LearnerThreadViewAPITest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): """ assert True + @ddt.data("None", "discussion", "question") + def test_thread_type_by(self, thread_type): + """ + Tests the thread_type parameter + + Arguments: + thread_type (str): Value of thread_type can be 'None', + 'discussion' and 'question' + """ + threads = [make_minimal_cs_thread({ + "id": "test_thread", + "course_id": str(self.course.id), + "commentable_id": "test_topic", + "user_id": str(self.user.id), + "username": self.user.username, + "created_at": "2015-04-28T00:00:00Z", + "updated_at": "2015-04-28T11:11:11Z", + "title": "Test Title", + "body": "Test body", + "votes": {"up_count": 4}, + "comments_count": 5, + "unread_comments_count": 3, + })] + expected_cs_comments_response = { + "collection": threads, + "page": 1, + "num_pages": 1, + } + self.register_get_user_response(self.user) + self.register_user_active_threads(self.user.id, expected_cs_comments_response) + response = self.client.get( + self.url, + { + "course_id": str(self.course.id), + "username": self.user.username, + "thread_type": thread_type, + } + ) + assert response.status_code == 200 + self.assert_last_query_params({ + "user_id": [str(self.user.id)], + "course_id": [str(self.course.id)], + "page": ["1"], + "per_page": ["10"], + "thread_type": [thread_type], + "sort_key": ['activity'], + "count_flagged": ["False"] + }) + + @ddt.data( + ("last_activity_at", "activity"), + ("comment_count", "comments"), + ("vote_count", "votes") + ) + @ddt.unpack + def test_order_by(self, http_query, cc_query): + """ + Tests the order_by parameter for active threads + + Arguments: + http_query (str): Query string sent in the http request + cc_query (str): Query string used for the comments client service + """ + threads = [make_minimal_cs_thread({ + "id": "test_thread", + "course_id": str(self.course.id), + "commentable_id": "test_topic", + "user_id": str(self.user.id), + "username": self.user.username, + "created_at": "2015-04-28T00:00:00Z", + "updated_at": "2015-04-28T11:11:11Z", + "title": "Test Title", + "body": "Test body", + "votes": {"up_count": 4}, + "comments_count": 5, + "unread_comments_count": 3, + })] + expected_cs_comments_response = { + "collection": threads, + "page": 1, + "num_pages": 1, + } + self.register_get_user_response(self.user) + self.register_user_active_threads(self.user.id, expected_cs_comments_response) + response = self.client.get( + self.url, + { + "course_id": str(self.course.id), + "username": self.user.username, + "order_by": http_query, + } + ) + assert response.status_code == 200 + self.assert_last_query_params({ + "user_id": [str(self.user.id)], + "course_id": [str(self.course.id)], + "page": ["1"], + "per_page": ["10"], + "sort_key": [cc_query], + "count_flagged": ["False"] + }) + + @ddt.data("flagged", "unanswered", "unread", "unresponded") + def test_status_by(self, post_status): + """ + Tests the post_status parameter + + Arguments: + post_status (str): Value of post_status can be 'flagged', + 'unanswered' and 'unread' + """ + threads = [make_minimal_cs_thread({ + "id": "test_thread", + "course_id": str(self.course.id), + "commentable_id": "test_topic", + "user_id": str(self.user.id), + "username": self.user.username, + "created_at": "2015-04-28T00:00:00Z", + "updated_at": "2015-04-28T11:11:11Z", + "title": "Test Title", + "body": "Test body", + "votes": {"up_count": 4}, + "comments_count": 5, + "unread_comments_count": 3, + })] + expected_cs_comments_response = { + "collection": threads, + "page": 1, + "num_pages": 1, + } + self.register_get_user_response(self.user) + self.register_user_active_threads(self.user.id, expected_cs_comments_response) + response = self.client.get( + self.url, + { + "course_id": str(self.course.id), + "username": self.user.username, + "status": post_status, + } + ) + if post_status == "flagged": + assert response.status_code == 403 + else: + assert response.status_code == 200 + self.assert_last_query_params({ + "user_id": [str(self.user.id)], + "course_id": [str(self.course.id)], + "page": ["1"], + "per_page": ["10"], + post_status: ['True'], + "sort_key": ['activity'], + "count_flagged": ["False"] + }) + @ddt.ddt @httpretty.activate diff --git a/lms/djangoapps/discussion/rest_api/views.py b/lms/djangoapps/discussion/rest_api/views.py index da55651463..c04fc7e484 100644 --- a/lms/djangoapps/discussion/rest_api/views.py +++ b/lms/djangoapps/discussion/rest_api/views.py @@ -574,6 +574,15 @@ class LearnerThreadView(APIView): * count_flagged: If True, return the count of flagged comments for each thread. (can only be used by moderators or above) + + * thread_type: The type of thread to filter None, "discussion" or "question". + + * order_by: Sort order for threads "last_activity_at", "comment_count" or + "vote_count". + + * status: Filter for threads "flagged", "unanswered", "unread". + + * group_id: Filter threads w.r.t cohorts (Cohort ID). """ authentication_classes = ( @@ -594,6 +603,15 @@ class LearnerThreadView(APIView): page_num = request.GET.get('page', 1) threads_per_page = request.GET.get('page_size', 10) count_flagged = request.GET.get('count_flagged', False) + thread_type = request.GET.get('thread_type') + order_by = request.GET.get('order_by') + order_by_mapping = { + "last_activity_at": "activity", + "comment_count": "comments", + "vote_count": "votes" + } + order_by = order_by_mapping.get(order_by, 'activity') + post_status = request.GET.get('status', None) discussion_id = None username = request.GET.get('username', None) user = get_object_or_404(User, username=username) @@ -610,7 +628,17 @@ class LearnerThreadView(APIView): "user_id": user.id, "group_id": group_id, "count_flagged": count_flagged, + "thread_type": thread_type, + "sort_key": order_by, } + if post_status: + if post_status not in ['flagged', 'unanswered', 'unread', 'unresponded']: + raise ValidationError({ + "status": [ + f"Invalid value. '{post_status}' must be 'flagged', 'unanswered', 'unread' or 'unresponded" + ] + }) + query_params[post_status] = True return get_learner_active_thread_list(request, course_key, query_params)