Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lms/djangoapps/discussion/rest_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
155 changes: 155 additions & 0 deletions lms/djangoapps/discussion/rest_api/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,7 @@ def test_delete_nonexistent_thread(self):
assert response.status_code == 404


@ddt.ddt
@httpretty.activate
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
class LearnerThreadViewAPITest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
Expand Down Expand Up @@ -1601,6 +1602,160 @@ def test_not_authenticated(self):
"""
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
Expand Down
28 changes: 28 additions & 0 deletions lms/djangoapps/discussion/rest_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand All @@ -594,6 +603,15 @@ def get(self, request, course_id=None):
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)
Expand All @@ -610,7 +628,17 @@ def get(self, request, course_id=None):
"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)


Expand Down