Commit 07b6cdc2 by wajeeha-khalid Committed by GitHub

Merge pull request #13954 from edx/jia/MA-2554

MA-2554: DiscussionAPI - fixed unread_comment_count
parents b8bc4c87 b837427b
...@@ -76,7 +76,9 @@ def validate_not_blank(value): ...@@ -76,7 +76,9 @@ def validate_not_blank(value):
class _ContentSerializer(serializers.Serializer): class _ContentSerializer(serializers.Serializer):
"""A base class for thread and comment serializers.""" """
A base class for thread and comment serializers.
"""
id = serializers.CharField(read_only=True) # pylint: disable=invalid-name id = serializers.CharField(read_only=True) # pylint: disable=invalid-name
author = serializers.SerializerMethodField() author = serializers.SerializerMethodField()
author_label = serializers.SerializerMethodField() author_label = serializers.SerializerMethodField()
...@@ -121,7 +123,9 @@ class _ContentSerializer(serializers.Serializer): ...@@ -121,7 +123,9 @@ class _ContentSerializer(serializers.Serializer):
) )
def get_author(self, obj): def get_author(self, obj):
"""Returns the author's username, or None if the content is anonymous.""" """
Returns the author's username, or None if the content is anonymous.
"""
return None if self._is_anonymous(obj) else obj["username"] return None if self._is_anonymous(obj) else obj["username"]
def _get_user_label(self, user_id): def _get_user_label(self, user_id):
...@@ -136,7 +140,9 @@ class _ContentSerializer(serializers.Serializer): ...@@ -136,7 +140,9 @@ class _ContentSerializer(serializers.Serializer):
) )
def get_author_label(self, obj): def get_author_label(self, obj):
"""Returns the role label for the content author.""" """
Returns the role label for the content author.
"""
if self._is_anonymous(obj) or obj["user_id"] is None: if self._is_anonymous(obj) or obj["user_id"] is None:
return None return None
else: else:
...@@ -144,7 +150,9 @@ class _ContentSerializer(serializers.Serializer): ...@@ -144,7 +150,9 @@ class _ContentSerializer(serializers.Serializer):
return self._get_user_label(user_id) return self._get_user_label(user_id)
def get_rendered_body(self, obj): def get_rendered_body(self, obj):
"""Returns the rendered body content.""" """
Returns the rendered body content.
"""
return render_body(obj["body"]) return render_body(obj["body"])
def get_abuse_flagged(self, obj): def get_abuse_flagged(self, obj):
...@@ -162,11 +170,15 @@ class _ContentSerializer(serializers.Serializer): ...@@ -162,11 +170,15 @@ class _ContentSerializer(serializers.Serializer):
return obj["id"] in self.context["cc_requester"]["upvoted_ids"] return obj["id"] in self.context["cc_requester"]["upvoted_ids"]
def get_vote_count(self, obj): def get_vote_count(self, obj):
"""Returns the number of votes for the content.""" """
Returns the number of votes for the content.
"""
return obj.get("votes", {}).get("up_count", 0) return obj.get("votes", {}).get("up_count", 0)
def get_editable_fields(self, obj): def get_editable_fields(self, obj):
"""Return the list of the fields the requester can edit""" """
Return the list of the fields the requester can edit
"""
return sorted(get_editable_fields(obj, self.context)) return sorted(get_editable_fields(obj, self.context))
...@@ -196,7 +208,7 @@ class ThreadSerializer(_ContentSerializer): ...@@ -196,7 +208,7 @@ class ThreadSerializer(_ContentSerializer):
endorsed_comment_list_url = serializers.SerializerMethodField() endorsed_comment_list_url = serializers.SerializerMethodField()
non_endorsed_comment_list_url = serializers.SerializerMethodField() non_endorsed_comment_list_url = serializers.SerializerMethodField()
read = serializers.BooleanField(required=False) read = serializers.BooleanField(required=False)
has_endorsed = serializers.BooleanField(read_only=True, source="endorsed") has_endorsed = serializers.BooleanField(source="endorsed", read_only=True)
response_count = serializers.IntegerField(source="resp_total", read_only=True, required=False) response_count = serializers.IntegerField(source="resp_total", read_only=True, required=False)
non_updatable_fields = NON_UPDATABLE_THREAD_FIELDS non_updatable_fields = NON_UPDATABLE_THREAD_FIELDS
...@@ -216,7 +228,9 @@ class ThreadSerializer(_ContentSerializer): ...@@ -216,7 +228,9 @@ class ThreadSerializer(_ContentSerializer):
return bool(obj["pinned"]) return bool(obj["pinned"])
def get_group_name(self, obj): def get_group_name(self, obj):
"""Returns the name of the group identified by the thread's group_id.""" """
Returns the name of the group identified by the thread's group_id.
"""
return self.context["group_ids_to_names"].get(obj["group_id"]) return self.context["group_ids_to_names"].get(obj["group_id"])
def get_following(self, obj): def get_following(self, obj):
...@@ -245,22 +259,31 @@ class ThreadSerializer(_ContentSerializer): ...@@ -245,22 +259,31 @@ class ThreadSerializer(_ContentSerializer):
) )
def get_endorsed_comment_list_url(self, obj): def get_endorsed_comment_list_url(self, obj):
"""Returns the URL to retrieve the thread's endorsed comments.""" """
Returns the URL to retrieve the thread's endorsed comments.
"""
return self.get_comment_list_url(obj, endorsed=True) return self.get_comment_list_url(obj, endorsed=True)
def get_non_endorsed_comment_list_url(self, obj): def get_non_endorsed_comment_list_url(self, obj):
"""Returns the URL to retrieve the thread's non-endorsed comments.""" """
Returns the URL to retrieve the thread's non-endorsed comments.
"""
return self.get_comment_list_url(obj, endorsed=False) return self.get_comment_list_url(obj, endorsed=False)
def get_comment_count(self, obj): def get_comment_count(self, obj):
"""Increments comment count to include post and returns total count of """
contributions (i.e. post + responses + comments) for the thread""" Increments comment count to include post and returns total count of
contributions (i.e. post + responses + comments) for the thread
"""
return obj["comments_count"] + 1 return obj["comments_count"] + 1
def get_unread_comment_count(self, obj): def get_unread_comment_count(self, obj):
"""Increments comment count to include post if thread is unread and returns """
total count of unread contributions (i.e. post + responses + comments) for the thread""" Returns the number of unread comments. If the thread has never been read,
if not obj["read"]: this additionally includes 1 for the post itself, in addition to its responses and
comments.
"""
if not obj["read"] and obj["comments_count"] == obj["unread_comments_count"]:
return obj["unread_comments_count"] + 1 return obj["unread_comments_count"] + 1
return obj["unread_comments_count"] return obj["unread_comments_count"]
...@@ -333,7 +356,9 @@ class CommentSerializer(_ContentSerializer): ...@@ -333,7 +356,9 @@ class CommentSerializer(_ContentSerializer):
return None return None
def get_endorsed_at(self, obj): def get_endorsed_at(self, obj):
"""Returns the timestamp for the endorsement, if available.""" """
Returns the timestamp for the endorsement, if available.
"""
endorsement = obj.get("endorsement") endorsement = obj.get("endorsement")
return endorsement["time"] if endorsement else None return endorsement["time"] if endorsement else None
......
...@@ -645,107 +645,67 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix ...@@ -645,107 +645,67 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
def test_thread_content(self): def test_thread_content(self):
source_threads = [ source_threads = [
{ make_minimal_cs_thread({
"type": "thread",
"id": "test_thread_id_0", "id": "test_thread_id_0",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "topic_x", "commentable_id": "topic_x",
"group_id": None,
"user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "user_id": str(self.author.id),
"anonymous_to_peers": False,
"created_at": "2015-04-28T00:00:00Z",
"updated_at": "2015-04-28T11:11:11Z",
"thread_type": "discussion",
"title": "Test Title", "title": "Test Title",
"body": "Test body", "body": "Test body",
"pinned": False,
"closed": False,
"abuse_flaggers": [],
"votes": {"up_count": 4}, "votes": {"up_count": 4},
"comments_count": 5, "comments_count": 5,
"unread_comments_count": 3, "unread_comments_count": 3,
"endorsed": True, "endorsed": True,
"read": True, "read": True,
}, "created_at": "2015-04-28T00:00:00Z",
{ "updated_at": "2015-04-28T11:11:11Z",
"type": "thread", }),
make_minimal_cs_thread({
"id": "test_thread_id_1", "id": "test_thread_id_1",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "topic_y", "commentable_id": "topic_y",
"group_id": self.cohort.id, "group_id": self.cohort.id,
"user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "user_id": str(self.author.id),
"anonymous_to_peers": False,
"created_at": "2015-04-28T22:22:22Z",
"updated_at": "2015-04-28T00:33:33Z",
"thread_type": "question", "thread_type": "question",
"title": "Another Test Title", "title": "Another Test Title",
"body": "More content", "body": "More content",
"pinned": False,
"closed": False,
"abuse_flaggers": [],
"votes": {"up_count": 9}, "votes": {"up_count": 9},
"comments_count": 18, "comments_count": 18,
"unread_comments_count": 0, "created_at": "2015-04-28T22:22:22Z",
"endorsed": False, "updated_at": "2015-04-28T00:33:33Z",
"read": False, })
},
] ]
expected_threads = [ expected_threads = [
{ self.expected_thread_data({
"id": "test_thread_id_0", "id": "test_thread_id_0",
"course_id": unicode(self.course.id),
"topic_id": "topic_x",
"group_id": None,
"group_name": None,
"author": self.author.username, "author": self.author.username,
"author_label": None, "topic_id": "topic_x",
"created_at": "2015-04-28T00:00:00Z",
"updated_at": "2015-04-28T11:11:11Z",
"type": "discussion",
"title": "Test Title",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 4, "vote_count": 4,
"comment_count": 6, "comment_count": 6,
"unread_comment_count": 3, "unread_comment_count": 3,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_0", "comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_0",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": ["abuse_flagged", "following", "read", "voted"], "editable_fields": ["abuse_flagged", "following", "read", "voted"],
"has_endorsed": True, "has_endorsed": True,
"read": True, "read": True,
}, "created_at": "2015-04-28T00:00:00Z",
{ "updated_at": "2015-04-28T11:11:11Z",
}),
self.expected_thread_data({
"id": "test_thread_id_1", "id": "test_thread_id_1",
"course_id": unicode(self.course.id), "author": self.author.username,
"topic_id": "topic_y", "topic_id": "topic_y",
"group_id": self.cohort.id, "group_id": self.cohort.id,
"group_name": self.cohort.name, "group_name": self.cohort.name,
"author": self.author.username,
"author_label": None,
"created_at": "2015-04-28T22:22:22Z",
"updated_at": "2015-04-28T00:33:33Z",
"type": "question", "type": "question",
"title": "Another Test Title", "title": "Another Test Title",
"raw_body": "More content", "raw_body": "More content",
"rendered_body": "<p>More content</p>", "rendered_body": "<p>More content</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 9, "vote_count": 9,
"comment_count": 19, "comment_count": 19,
"unread_comment_count": 1, "created_at": "2015-04-28T22:22:22Z",
"updated_at": "2015-04-28T00:33:33Z",
"comment_list_url": None, "comment_list_url": None,
"endorsed_comment_list_url": ( "endorsed_comment_list_url": (
"http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_1&endorsed=True" "http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_1&endorsed=True"
...@@ -754,9 +714,7 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix ...@@ -754,9 +714,7 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
"http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_1&endorsed=False" "http://testserver/api/discussion/v1/comments/?thread_id=test_thread_id_1&endorsed=False"
), ),
"editable_fields": ["abuse_flagged", "following", "read", "voted"], "editable_fields": ["abuse_flagged", "following", "read", "voted"],
"has_endorsed": False, }),
"read": False,
},
] ]
expected_result = make_paginated_api_response( expected_result = make_paginated_api_response(
...@@ -1479,42 +1437,17 @@ class CreateThreadTest( ...@@ -1479,42 +1437,17 @@ class CreateThreadTest(
cs_thread = make_minimal_cs_thread({ cs_thread = make_minimal_cs_thread({
"id": "test_id", "id": "test_id",
"username": self.user.username, "username": self.user.username,
"created_at": "2015-05-19T00:00:00Z", "read": True,
"updated_at": "2015-05-19T00:00:00Z",
}) })
self.register_post_thread_response(cs_thread) self.register_post_thread_response(cs_thread)
with self.assert_signal_sent(api, 'thread_created', sender=None, user=self.user, exclude_args=('post',)): with self.assert_signal_sent(api, 'thread_created', sender=None, user=self.user, exclude_args=('post',)):
actual = create_thread(self.request, self.minimal_data) actual = create_thread(self.request, self.minimal_data)
expected = { expected = self.expected_thread_data({
"id": "test_id", "id": "test_id",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"author": self.user.username,
"author_label": None,
"created_at": "2015-05-19T00:00:00Z",
"updated_at": "2015-05-19T00:00:00Z",
"type": "discussion",
"title": "Test Title",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"comment_count": 1,
"unread_comment_count": 1,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_id", "comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_id",
"endorsed_comment_list_url": None, "read": True,
"non_endorsed_comment_list_url": None, })
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
'read': False,
'has_endorsed': False,
'response_count': 0,
}
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
self.assertEqual( self.assertEqual(
httpretty.last_request().parsed_body, httpretty.last_request().parsed_body,
...@@ -2002,8 +1935,6 @@ class UpdateThreadTest( ...@@ -2002,8 +1935,6 @@ class UpdateThreadTest(
"commentable_id": "original_topic", "commentable_id": "original_topic",
"username": self.user.username, "username": self.user.username,
"user_id": str(self.user.id), "user_id": str(self.user.id),
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"thread_type": "discussion", "thread_type": "discussion",
"title": "Original Title", "title": "Original Title",
"body": "Original body", "body": "Original body",
...@@ -2025,37 +1956,14 @@ class UpdateThreadTest( ...@@ -2025,37 +1956,14 @@ class UpdateThreadTest(
self.register_thread() self.register_thread()
with self.assert_signal_sent(api, 'thread_edited', sender=None, user=self.user, exclude_args=('post',)): with self.assert_signal_sent(api, 'thread_edited', sender=None, user=self.user, exclude_args=('post',)):
actual = update_thread(self.request, "test_thread", {"raw_body": "Edited body"}) actual = update_thread(self.request, "test_thread", {"raw_body": "Edited body"})
expected = {
"id": "test_thread", self.assertEqual(actual, self.expected_thread_data({
"course_id": unicode(self.course.id),
"topic_id": "original_topic",
"group_id": None,
"group_name": None,
"author": self.user.username,
"author_label": None,
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"type": "discussion",
"title": "Original Title",
"raw_body": "Edited body", "raw_body": "Edited body",
"rendered_body": "<p>Edited body</p>", "rendered_body": "<p>Edited body</p>",
"pinned": False, "topic_id": "original_topic",
"closed": False, "read": True,
"following": False, "title": "Original Title",
"abuse_flagged": False, }))
"voted": False,
"vote_count": 0,
"comment_count": 1,
"unread_comment_count": 0,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
'read': True,
'has_endorsed': False,
'response_count': 0
}
self.assertEqual(actual, expected)
self.assertEqual( self.assertEqual(
httpretty.last_request().parsed_body, httpretty.last_request().parsed_body,
{ {
...@@ -3096,12 +3004,12 @@ class RetrieveThreadTest( ...@@ -3096,12 +3004,12 @@ class RetrieveThreadTest(
httpretty.reset() httpretty.reset()
httpretty.enable() httpretty.enable()
self.addCleanup(httpretty.disable) self.addCleanup(httpretty.disable)
self.thread_author = UserFactory.create() self.user = UserFactory.create()
self.register_get_user_response(self.thread_author) self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path") self.request = RequestFactory().get("/test_path")
self.request.user = self.thread_author self.request.user = self.user
self.thread_id = "test_thread" self.thread_id = "test_thread"
CourseEnrollmentFactory.create(user=self.thread_author, course_id=self.course.id) CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
def register_thread(self, overrides=None): def register_thread(self, overrides=None):
""" """
...@@ -3113,12 +3021,10 @@ class RetrieveThreadTest( ...@@ -3113,12 +3021,10 @@ class RetrieveThreadTest(
"id": self.thread_id, "id": self.thread_id,
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "test_topic", "commentable_id": "test_topic",
"username": self.thread_author.username, "username": self.user.username,
"user_id": str(self.thread_author.id), "user_id": str(self.user.id),
"title": "Test Title", "title": "Test Title",
"body": "Test body", "body": "Test body",
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"resp_total": 0, "resp_total": 0,
}) })
...@@ -3126,38 +3032,11 @@ class RetrieveThreadTest( ...@@ -3126,38 +3032,11 @@ class RetrieveThreadTest(
self.register_get_thread_response(cs_data) self.register_get_thread_response(cs_data)
def test_basic(self): def test_basic(self):
expected_response_data = {
"author": self.thread_author.username,
"author_label": None,
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"title": "Test Title",
"pinned": False,
"closed": False,
"following": False,
"comment_count": 1,
"unread_comment_count": 1,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"read": False,
"has_endorsed": False,
"id": "test_thread",
"type": "discussion",
"response_count": 2,
}
self.register_thread({"resp_total": 2}) self.register_thread({"resp_total": 2})
self.assertEqual(get_thread(self.request, self.thread_id), expected_response_data) self.assertEqual(get_thread(self.request, self.thread_id), self.expected_thread_data({
"response_count": 2,
"unread_comment_count": 1,
}))
self.assertEqual(httpretty.last_request().method, "GET") self.assertEqual(httpretty.last_request().method, "GET")
def test_thread_id_not_found(self): def test_thread_id_not_found(self):
...@@ -3166,42 +3045,15 @@ class RetrieveThreadTest( ...@@ -3166,42 +3045,15 @@ class RetrieveThreadTest(
get_thread(self.request, "missing_thread") get_thread(self.request, "missing_thread")
def test_nonauthor_enrolled_in_course(self): def test_nonauthor_enrolled_in_course(self):
expected_response_data = {
"author": self.thread_author.username,
"author_label": None,
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"editable_fields": ["abuse_flagged", "following", "read", "voted"],
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"title": "Test Title",
"pinned": False,
"closed": False,
"following": False,
"comment_count": 1,
"unread_comment_count": 1,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"read": False,
"has_endorsed": False,
"id": "test_thread",
"type": "discussion",
"response_count": 0,
}
non_author_user = UserFactory.create() non_author_user = UserFactory.create()
self.register_get_user_response(non_author_user) self.register_get_user_response(non_author_user)
CourseEnrollmentFactory.create(user=non_author_user, course_id=self.course.id) CourseEnrollmentFactory.create(user=non_author_user, course_id=self.course.id)
self.register_thread() self.register_thread()
self.request.user = non_author_user self.request.user = non_author_user
self.assertEqual(get_thread(self.request, self.thread_id), expected_response_data) self.assertEqual(get_thread(self.request, self.thread_id), self.expected_thread_data({
"editable_fields": ["abuse_flagged", "following", "read", "voted"],
"unread_comment_count": 1,
}))
self.assertEqual(httpretty.last_request().method, "GET") self.assertEqual(httpretty.last_request().method, "GET")
def test_not_enrolled_in_course(self): def test_not_enrolled_in_course(self):
...@@ -3233,10 +3085,10 @@ class RetrieveThreadTest( ...@@ -3233,10 +3085,10 @@ class RetrieveThreadTest(
the student role is the author and the thread is in the author's cohort. the student role is the author and the thread is in the author's cohort.
""" """
cohort_course = CourseFactory.create(cohort_config={"cohorted": course_is_cohorted}) cohort_course = CourseFactory.create(cohort_config={"cohorted": course_is_cohorted})
CourseEnrollmentFactory.create(user=self.thread_author, course_id=cohort_course.id) CourseEnrollmentFactory.create(user=self.user, course_id=cohort_course.id)
cohort = CohortFactory.create(course_id=cohort_course.id, users=[self.thread_author]) cohort = CohortFactory.create(course_id=cohort_course.id, users=[self.user])
role = Role.objects.create(name=role_name, course_id=cohort_course.id) role = Role.objects.create(name=role_name, course_id=cohort_course.id)
role.users = [self.thread_author] role.users = [self.user]
self.register_thread({ self.register_thread({
"course_id": unicode(cohort_course.id), "course_id": unicode(cohort_course.id),
"group_id": ( "group_id": (
......
...@@ -161,60 +161,27 @@ class ThreadSerializerSerializationTest(SerializerTestMixin, SharedModuleStoreTe ...@@ -161,60 +161,27 @@ class ThreadSerializerSerializationTest(SerializerTestMixin, SharedModuleStoreTe
return ThreadSerializer(thread, context=get_context(self.course, self.request)).data return ThreadSerializer(thread, context=get_context(self.course, self.request)).data
def test_basic(self): def test_basic(self):
thread = { thread = make_minimal_cs_thread({
"type": "thread",
"id": "test_thread", "id": "test_thread",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "test_topic", "commentable_id": "test_topic",
"group_id": None,
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False,
"anonymous_to_peers": False,
"created_at": "2015-04-28T00:00:00Z",
"updated_at": "2015-04-28T11:11:11Z",
"thread_type": "discussion",
"title": "Test Title", "title": "Test Title",
"body": "Test body", "body": "Test body",
"pinned": True, "pinned": True,
"closed": False,
"abuse_flaggers": [],
"votes": {"up_count": 4}, "votes": {"up_count": 4},
"comments_count": 5, "comments_count": 5,
"unread_comments_count": 3, "unread_comments_count": 3,
"read": False, })
"endorsed": False, expected = self.expected_thread_data({
"response_count": None,
}
expected = {
"id": "test_thread",
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"author": self.author.username, "author": self.author.username,
"author_label": None,
"created_at": "2015-04-28T00:00:00Z",
"updated_at": "2015-04-28T11:11:11Z",
"type": "discussion",
"title": "Test Title",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"pinned": True,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 4, "vote_count": 4,
"comment_count": 6, "comment_count": 6,
"unread_comment_count": 4, "unread_comment_count": 3,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread", "pinned": True,
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": ["abuse_flagged", "following", "read", "voted"], "editable_fields": ["abuse_flagged", "following", "read", "voted"],
"read": False, })
"has_endorsed": False,
}
self.assertEqual(self.serialize(thread), expected) self.assertEqual(self.serialize(thread), expected)
thread["thread_type"] = "question" thread["thread_type"] = "question"
......
...@@ -77,12 +77,12 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur ...@@ -77,12 +77,12 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
cs_thread = make_minimal_cs_thread({ cs_thread = make_minimal_cs_thread({
"id": "test_thread", "id": "test_thread",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "original_topic", "commentable_id": "test_topic",
"username": self.user.username, "username": self.user.username,
"user_id": str(self.user.id), "user_id": str(self.user.id),
"thread_type": "discussion", "thread_type": "discussion",
"title": "Original Title", "title": "Test Title",
"body": "Original body", "body": "Test body",
}) })
cs_thread.update(overrides or {}) cs_thread.update(overrides or {})
self.register_get_thread_response(cs_thread) self.register_get_thread_response(cs_thread)
...@@ -318,43 +318,6 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro ...@@ -318,43 +318,6 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.author = UserFactory.create() self.author = UserFactory.create()
self.url = reverse("thread-list") self.url = reverse("thread-list")
def make_expected_thread(self, overrides=None):
"""
Create a sample expected thread for response
"""
thread = {
"id": "test_thread",
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"author": "dummy",
"author_label": None,
"created_at": "1970-01-01T00:00:00Z",
"updated_at": "1970-01-01T00:00:00Z",
"type": "discussion",
"title": "dummy",
"raw_body": "dummy",
"rendered_body": "<p>dummy</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"comment_count": 1,
"unread_comment_count": 1,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": ["abuse_flagged", "following", "read", "voted"],
"read": False,
"has_endorsed": False,
"response_count": 0,
}
thread.update(overrides or {})
return thread
def create_source_thread(self, overrides=None): def create_source_thread(self, overrides=None):
""" """
Create a sample source cs_thread Create a sample source cs_thread
...@@ -398,17 +361,15 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro ...@@ -398,17 +361,15 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
source_threads = [ source_threads = [
self.create_source_thread({"user_id": str(self.author.id), "username": self.author.username}) self.create_source_thread({"user_id": str(self.author.id), "username": self.author.username})
] ]
expected_threads = [self.make_expected_thread({ expected_threads = [self.expected_thread_data({
"created_at": "2015-04-28T00:00:00Z", "created_at": "2015-04-28T00:00:00Z",
"updated_at": "2015-04-28T11:11:11Z", "updated_at": "2015-04-28T11:11:11Z",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"title": "Test Title",
"vote_count": 4, "vote_count": 4,
"comment_count": 6, "comment_count": 6,
"unread_comment_count": 4, "unread_comment_count": 3,
"voted": True, "voted": True,
"author": self.author.username "author": self.author.username,
"editable_fields": ["abuse_flagged", "following", "read", "voted"],
})] })]
self.register_get_threads_response(source_threads, page=1, num_pages=2) self.register_get_threads_response(source_threads, page=1, num_pages=2)
response = self.client.get(self.url, {"course_id": unicode(self.course.id), "following": ""}) response = self.client.get(self.url, {"course_id": unicode(self.course.id), "following": ""})
...@@ -701,8 +662,6 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ...@@ -701,8 +662,6 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
cs_thread = make_minimal_cs_thread({ cs_thread = make_minimal_cs_thread({
"id": "test_thread", "id": "test_thread",
"username": self.user.username, "username": self.user.username,
"created_at": "2015-05-19T00:00:00Z",
"updated_at": "2015-05-19T00:00:00Z",
"read": True, "read": True,
}) })
self.register_post_thread_response(cs_thread) self.register_post_thread_response(cs_thread)
...@@ -713,36 +672,6 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ...@@ -713,36 +672,6 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"title": "Test Title", "title": "Test Title",
"raw_body": "Test body", "raw_body": "Test body",
} }
expected_response_data = {
"id": "test_thread",
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"author": self.user.username,
"author_label": None,
"created_at": "2015-05-19T00:00:00Z",
"updated_at": "2015-05-19T00:00:00Z",
"type": "discussion",
"title": "Test Title",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"comment_count": 1,
"unread_comment_count": 0,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
"read": True,
"has_endorsed": False,
"response_count": 0,
}
response = self.client.post( response = self.client.post(
self.url, self.url,
json.dumps(request_data), json.dumps(request_data),
...@@ -750,7 +679,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ...@@ -750,7 +679,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual(response_data, expected_response_data) self.assertEqual(response_data, self.expected_thread_data({"read": True}))
self.assertEqual( self.assertEqual(
httpretty.last_request().parsed_body, httpretty.last_request().parsed_body,
{ {
...@@ -795,43 +724,6 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -795,43 +724,6 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
super(ThreadViewSetPartialUpdateTest, self).setUp() super(ThreadViewSetPartialUpdateTest, self).setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"}) self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
def expected_response_data(self, overrides=None):
"""
create expected response data from comment update endpoint
"""
response_data = {
"id": "test_thread",
"course_id": unicode(self.course.id),
"topic_id": "original_topic",
"group_id": None,
"group_name": None,
"author": self.user.username,
"author_label": None,
"created_at": "1970-01-01T00:00:00Z",
"updated_at": "1970-01-01T00:00:00Z",
"type": "discussion",
"title": "Original Title",
"raw_body": "Original body",
"rendered_body": "<p>Original body</p>",
"pinned": False,
"closed": False,
"following": False,
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"comment_count": 0,
"unread_comment_count": 0,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"editable_fields": [],
"read": False,
"has_endorsed": False,
"response_count": 0,
}
response_data.update(overrides or {})
return response_data
def test_basic(self): def test_basic(self):
self.register_get_user_response(self.user) self.register_get_user_response(self.user)
self.register_thread({ self.register_thread({
...@@ -846,7 +738,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -846,7 +738,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual( self.assertEqual(
response_data, response_data,
self.expected_response_data({ self.expected_thread_data({
"raw_body": "Edited body", "raw_body": "Edited body",
"rendered_body": "<p>Edited body</p>", "rendered_body": "<p>Edited body</p>",
"editable_fields": [ "editable_fields": [
...@@ -863,9 +755,9 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -863,9 +755,9 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
httpretty.last_request().parsed_body, httpretty.last_request().parsed_body,
{ {
"course_id": [unicode(self.course.id)], "course_id": [unicode(self.course.id)],
"commentable_id": ["original_topic"], "commentable_id": ["test_topic"],
"thread_type": ["discussion"], "thread_type": ["discussion"],
"title": ["Original Title"], "title": ["Test Title"],
"body": ["Edited body"], "body": ["Edited body"],
"user_id": [str(self.user.id)], "user_id": [str(self.user.id)],
"anonymous": ["False"], "anonymous": ["False"],
...@@ -903,7 +795,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -903,7 +795,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual( self.assertEqual(
response_data, response_data,
self.expected_response_data({ self.expected_thread_data({
"read": True, "read": True,
"closed": True, "closed": True,
"abuse_flagged": value, "abuse_flagged": value,
...@@ -938,7 +830,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -938,7 +830,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual( self.assertEqual(
response_data, response_data,
self.expected_response_data({ self.expected_thread_data({
"comment_count": 1, "comment_count": 1,
"read": True, "read": True,
"editable_fields": [ "editable_fields": [
...@@ -966,7 +858,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest ...@@ -966,7 +858,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual( self.assertEqual(
response_data, response_data,
self.expected_response_data({ self.expected_thread_data({
"author": str(thread_owner_user.username), "author": str(thread_owner_user.username),
"comment_count": 1, "comment_count": 1,
"read": True, "read": True,
...@@ -1655,43 +1547,11 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, ...@@ -1655,43 +1547,11 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
"user_id": str(self.user.id), "user_id": str(self.user.id),
"title": "Test Title", "title": "Test Title",
"body": "Test body", "body": "Test body",
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z"
}) })
expected_response_data = {
"author": self.user.username,
"author_label": None,
"created_at": "2015-05-29T00:00:00Z",
"updated_at": "2015-05-29T00:00:00Z",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"title": "Test Title",
"pinned": False,
"closed": False,
"following": False,
"comment_count": 1,
"unread_comment_count": 1,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"read": False,
"has_endorsed": False,
"id": "test_thread",
"type": "discussion",
"response_count": 0,
}
self.register_get_thread_response(cs_thread) self.register_get_thread_response(cs_thread)
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(json.loads(response.content), expected_response_data) self.assertEqual(json.loads(response.content), self.expected_thread_data({"unread_comment_count": 1}))
self.assertEqual(httpretty.last_request().method, "GET") self.assertEqual(httpretty.last_request().method, "GET")
def test_retrieve_nonexistent_thread(self): def test_retrieve_nonexistent_thread(self):
......
...@@ -335,6 +335,43 @@ class CommentsServiceMockMixin(object): ...@@ -335,6 +335,43 @@ class CommentsServiceMockMixin(object):
content_type="application/merge-patch+json" content_type="application/merge-patch+json"
) )
def expected_thread_data(self, overrides=None):
"""
Returns expected thread data in API response
"""
response_data = {
"author": self.user.username,
"author_label": None,
"created_at": "1970-01-01T00:00:00Z",
"updated_at": "1970-01-01T00:00:00Z",
"raw_body": "Test body",
"rendered_body": "<p>Test body</p>",
"abuse_flagged": False,
"voted": False,
"vote_count": 0,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
"course_id": unicode(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
"title": "Test Title",
"pinned": False,
"closed": False,
"following": False,
"comment_count": 1,
"unread_comment_count": 0,
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_thread",
"endorsed_comment_list_url": None,
"non_endorsed_comment_list_url": None,
"read": False,
"has_endorsed": False,
"id": "test_thread",
"type": "discussion",
"response_count": 0,
}
response_data.update(overrides or {})
return response_data
def make_minimal_cs_thread(overrides=None): def make_minimal_cs_thread(overrides=None):
""" """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment