From fa6e5338c238b6b895d7c0b8d4b390bcdc26d06e Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 4 May 2015 17:13:55 -0400 Subject: [PATCH] Add fields to discussion thread list endpoint This commit adds fields that are related to the requesting user's interaction with the thread (e.g. following). --- lms/djangoapps/discussion_api/api.py | 10 +++++-- .../discussion_api/tests/test_api.py | 28 ++++++++++++++++++- .../discussion_api/tests/test_views.py | 8 ++++++ lms/djangoapps/discussion_api/tests/utils.py | 13 +++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/discussion_api/api.py b/lms/djangoapps/discussion_api/api.py index fd35f9a91c..071fba0545 100644 --- a/lms/djangoapps/discussion_api/api.py +++ b/lms/djangoapps/discussion_api/api.py @@ -14,6 +14,7 @@ from django_comment_common.models import ( Role, ) from lms.lib.comment_client.thread import Thread +from lms.lib.comment_client.user import User from openedx.core.djangoapps.course_groups.cohorts import get_cohort_id @@ -76,7 +77,7 @@ def get_course_topics(course, user): } -def _cc_thread_to_api_thread(thread): +def _cc_thread_to_api_thread(thread, cc_user): """ Convert a thread data dict from the comment_client format (which is a direct representation of the format returned by the comments service) to the format @@ -98,6 +99,10 @@ def _cc_thread_to_api_thread(thread): ret.update({ "topic_id": thread["commentable_id"], "raw_body": thread["body"], + "following": thread["id"] in cc_user["subscribed_thread_ids"], + "abuse_flagged": cc_user["id"] in thread["abuse_flaggers"], + "voted": thread["id"] in cc_user["upvoted_ids"], + "vote_count": thread["votes"]["up_count"], "comment_count": thread["comments_count"], "unread_comment_count": thread["unread_comments_count"], }) @@ -125,6 +130,7 @@ def get_thread_list(request, course_key, page, page_size): name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA], users=request.user ).exists() + cc_user = User.from_django_user(request.user).retrieve() threads, result_page, num_pages, _ = Thread.search({ "course_id": unicode(course_key), "group_id": None if user_is_privileged else get_cohort_id(request.user, course_key), @@ -139,5 +145,5 @@ def get_thread_list(request, course_key, page, page_size): if result_page != page: raise Http404 - results = [_cc_thread_to_api_thread(thread) for thread in threads] + results = [_cc_thread_to_api_thread(thread, cc_user) for thread in threads] return get_paginated_data(request, results, page, num_pages) diff --git a/lms/djangoapps/discussion_api/tests/test_api.py b/lms/djangoapps/discussion_api/tests/test_api.py index 643f7eb51c..7b5a729d9a 100644 --- a/lms/djangoapps/discussion_api/tests/test_api.py +++ b/lms/djangoapps/discussion_api/tests/test_api.py @@ -323,13 +323,16 @@ class GetCourseTopicsTest(ModuleStoreTestCase): @ddt.ddt -@httpretty.activate class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): """Test for get_thread_list""" def setUp(self): super(GetThreadListTest, self).setUp() + httpretty.reset() + httpretty.enable() + self.addCleanup(httpretty.disable) self.maxDiff = None # pylint: disable=invalid-name self.user = UserFactory.create() + self.register_get_user_response(self.user) self.request = RequestFactory().get("/test_path") self.request.user = self.user self.course = CourseFactory.create() @@ -366,6 +369,11 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): }) def test_thread_content(self): + self.register_get_user_response( + self.user, + subscribed_thread_ids=["test_thread_id_0"], + upvoted_ids=["test_thread_id_1"] + ) source_threads = [ { "id": "test_thread_id_0", @@ -378,6 +386,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "body": "Test body", "pinned": False, "closed": False, + "abuse_flaggers": [], + "votes": {"up_count": 4}, "comments_count": 5, "unread_comments_count": 3, }, @@ -392,6 +402,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "body": "More content", "pinned": False, "closed": True, + "abuse_flaggers": [], + "votes": {"up_count": 9}, "comments_count": 18, "unread_comments_count": 0, }, @@ -406,6 +418,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "body": "Still more content", "pinned": True, "closed": False, + "abuse_flaggers": [str(self.user.id)], + "votes": {"up_count": 0}, "comments_count": 0, "unread_comments_count": 0, }, @@ -422,6 +436,10 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "raw_body": "Test body", "pinned": False, "closed": False, + "following": True, + "abuse_flagged": False, + "voted": False, + "vote_count": 4, "comment_count": 5, "unread_comment_count": 3, }, @@ -436,6 +454,10 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "raw_body": "More content", "pinned": False, "closed": True, + "following": False, + "abuse_flagged": False, + "voted": True, + "vote_count": 9, "comment_count": 18, "unread_comment_count": 0, }, @@ -450,6 +472,10 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): "raw_body": "Still more content", "pinned": True, "closed": False, + "following": False, + "abuse_flagged": True, + "voted": False, + "vote_count": 0, "comment_count": 0, "unread_comment_count": 0, }, diff --git a/lms/djangoapps/discussion_api/tests/test_views.py b/lms/djangoapps/discussion_api/tests/test_views.py index df6d63cfcc..69342ed039 100644 --- a/lms/djangoapps/discussion_api/tests/test_views.py +++ b/lms/djangoapps/discussion_api/tests/test_views.py @@ -141,6 +141,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ) def test_basic(self): + self.register_get_user_response(self.user, upvoted_ids=["test_thread"]) source_threads = [{ "id": "test_thread", "course_id": unicode(self.course.id), @@ -152,6 +153,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): "body": "Test body", "pinned": False, "closed": False, + "abuse_flaggers": [], + "votes": {"up_count": 4}, "comments_count": 5, "unread_comments_count": 3, }] @@ -166,6 +169,10 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): "raw_body": "Test body", "pinned": False, "closed": False, + "following": False, + "abuse_flagged": False, + "voted": True, + "vote_count": 4, "comment_count": 5, "unread_comment_count": 3, }] @@ -190,6 +197,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): }) def test_pagination(self): + self.register_get_user_response(self.user) self.register_get_threads_response([], page=1, num_pages=1) response = self.client.get( self.url, diff --git a/lms/djangoapps/discussion_api/tests/utils.py b/lms/djangoapps/discussion_api/tests/utils.py index 41f7f50e8c..b931d7dbc8 100644 --- a/lms/djangoapps/discussion_api/tests/utils.py +++ b/lms/djangoapps/discussion_api/tests/utils.py @@ -21,6 +21,19 @@ class CommentsServiceMockMixin(object): status=200 ) + def register_get_user_response(self, user, subscribed_thread_ids=None, upvoted_ids=None): + """Register a mock response for GET on the CS user instance endpoint""" + httpretty.register_uri( + httpretty.GET, + "http://localhost:4567/api/v1/users/{id}".format(id=user.id), + body=json.dumps({ + "id": str(user.id), + "subscribed_thread_ids": subscribed_thread_ids or [], + "upvoted_ids": upvoted_ids or [], + }), + status=200 + ) + def assert_last_query_params(self, expected_params): """ Assert that the last mock request had the expected query parameters