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

MA-2554: DiscussionAPI - fixed unread_comment_count
This commit is contained in:
wajeeha-khalid
2016-11-15 15:07:50 +05:00
committed by GitHub
5 changed files with 146 additions and 405 deletions

View File

@@ -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):
) )
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):
) )
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):
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):
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):
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):
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):
) )
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):
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

View File

@@ -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
"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(
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(
"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(
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(
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(
"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(
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(
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(
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": (

View File

@@ -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"

View File

@@ -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
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
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):
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):
"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):
) )
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
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
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
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
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
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
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,
"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):

View File

@@ -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):
""" """