Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
ef26e8e8
Commit
ef26e8e8
authored
Jun 05, 2015
by
Greg Price
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add comment endorsement to discussion API
parent
94c1bf49
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
128 additions
and
24 deletions
+128
-24
lms/djangoapps/discussion_api/api.py
+33
-9
lms/djangoapps/discussion_api/serializers.py
+6
-1
lms/djangoapps/discussion_api/tests/test_api.py
+55
-10
lms/djangoapps/discussion_api/tests/test_serializers.py
+34
-4
No files found.
lms/djangoapps/discussion_api/api.py
View file @
ef26e8e8
...
@@ -424,6 +424,20 @@ def _get_thread_editable_fields(cc_thread, context):
...
@@ -424,6 +424,20 @@ def _get_thread_editable_fields(cc_thread, context):
return
_THREAD_EDITABLE_BY_ANY
return
_THREAD_EDITABLE_BY_ANY
def
_check_editable_fields
(
editable_fields
,
update_data
):
"""
Raise ValidationError if the given update data contains a field that is not
in editable_fields.
"""
non_editable_errors
=
{
field
:
[
"This field is not editable."
]
for
field
in
update_data
.
keys
()
if
field
not
in
editable_fields
}
if
non_editable_errors
:
raise
ValidationError
(
non_editable_errors
)
def
update_thread
(
request
,
thread_id
,
update_data
):
def
update_thread
(
request
,
thread_id
,
update_data
):
"""
"""
Update a thread.
Update a thread.
...
@@ -444,13 +458,7 @@ def update_thread(request, thread_id, update_data):
...
@@ -444,13 +458,7 @@ def update_thread(request, thread_id, update_data):
"""
"""
cc_thread
,
context
=
_get_thread_and_context
(
request
,
thread_id
)
cc_thread
,
context
=
_get_thread_and_context
(
request
,
thread_id
)
editable_fields
=
_get_thread_editable_fields
(
cc_thread
,
context
)
editable_fields
=
_get_thread_editable_fields
(
cc_thread
,
context
)
non_editable_errors
=
{
_check_editable_fields
(
editable_fields
,
update_data
)
field
:
[
"This field is not editable."
]
for
field
in
update_data
.
keys
()
if
field
not
in
editable_fields
}
if
non_editable_errors
:
raise
ValidationError
(
non_editable_errors
)
serializer
=
ThreadSerializer
(
cc_thread
,
data
=
update_data
,
partial
=
True
,
context
=
context
)
serializer
=
ThreadSerializer
(
cc_thread
,
data
=
update_data
,
partial
=
True
,
context
=
context
)
actions_form
=
ThreadActionsForm
(
update_data
)
actions_form
=
ThreadActionsForm
(
update_data
)
if
not
(
serializer
.
is_valid
()
and
actions_form
.
is_valid
()):
if
not
(
serializer
.
is_valid
()
and
actions_form
.
is_valid
()):
...
@@ -463,6 +471,22 @@ def update_thread(request, thread_id, update_data):
...
@@ -463,6 +471,22 @@ def update_thread(request, thread_id, update_data):
return
api_thread
return
api_thread
_COMMENT_EDITABLE_BY_AUTHOR
=
{
"raw_body"
}
_COMMENT_EDITABLE_BY_THREAD_AUTHOR
=
{
"endorsed"
}
def
_get_comment_editable_fields
(
cc_comment
,
context
):
"""
Get the list of editable fields for the given comment in the given context
"""
ret
=
set
()
if
_is_user_author_or_privileged
(
cc_comment
,
context
):
ret
|=
_COMMENT_EDITABLE_BY_AUTHOR
if
_is_user_author_or_privileged
(
context
[
"thread"
],
context
):
ret
|=
_COMMENT_EDITABLE_BY_THREAD_AUTHOR
return
ret
def
update_comment
(
request
,
comment_id
,
update_data
):
def
update_comment
(
request
,
comment_id
,
update_data
):
"""
"""
Update a comment.
Update a comment.
...
@@ -493,8 +517,8 @@ def update_comment(request, comment_id, update_data):
...
@@ -493,8 +517,8 @@ def update_comment(request, comment_id, update_data):
is empty or thread_id is included)
is empty or thread_id is included)
"""
"""
cc_comment
,
context
=
_get_comment_and_context
(
request
,
comment_id
)
cc_comment
,
context
=
_get_comment_and_context
(
request
,
comment_id
)
if
not
_is_user_author_or_privileged
(
cc_comment
,
context
):
editable_fields
=
_get_comment_editable_fields
(
cc_comment
,
context
)
raise
PermissionDenied
(
)
_check_editable_fields
(
editable_fields
,
update_data
)
serializer
=
CommentSerializer
(
cc_comment
,
data
=
update_data
,
partial
=
True
,
context
=
context
)
serializer
=
CommentSerializer
(
cc_comment
,
data
=
update_data
,
partial
=
True
,
context
=
context
)
if
not
serializer
.
is_valid
():
if
not
serializer
.
is_valid
():
raise
ValidationError
(
serializer
.
errors
)
raise
ValidationError
(
serializer
.
errors
)
...
...
lms/djangoapps/discussion_api/serializers.py
View file @
ef26e8e8
...
@@ -231,7 +231,7 @@ class CommentSerializer(_ContentSerializer):
...
@@ -231,7 +231,7 @@ class CommentSerializer(_ContentSerializer):
"""
"""
thread_id
=
serializers
.
CharField
()
thread_id
=
serializers
.
CharField
()
parent_id
=
serializers
.
CharField
(
required
=
False
)
parent_id
=
serializers
.
CharField
(
required
=
False
)
endorsed
=
serializers
.
BooleanField
(
re
ad_only
=
Tru
e
)
endorsed
=
serializers
.
BooleanField
(
re
quired
=
Fals
e
)
endorsed_by
=
serializers
.
SerializerMethodField
(
"get_endorsed_by"
)
endorsed_by
=
serializers
.
SerializerMethodField
(
"get_endorsed_by"
)
endorsed_by_label
=
serializers
.
SerializerMethodField
(
"get_endorsed_by_label"
)
endorsed_by_label
=
serializers
.
SerializerMethodField
(
"get_endorsed_by_label"
)
endorsed_at
=
serializers
.
SerializerMethodField
(
"get_endorsed_at"
)
endorsed_at
=
serializers
.
SerializerMethodField
(
"get_endorsed_at"
)
...
@@ -300,6 +300,11 @@ class CommentSerializer(_ContentSerializer):
...
@@ -300,6 +300,11 @@ class CommentSerializer(_ContentSerializer):
if
instance
:
if
instance
:
for
key
,
val
in
attrs
.
items
():
for
key
,
val
in
attrs
.
items
():
instance
[
key
]
=
val
instance
[
key
]
=
val
# TODO: The comments service doesn't populate the endorsement
# field on comment creation, so we only provide
# endorsement_user_id on update
if
key
==
"endorsed"
:
instance
[
"endorsement_user_id"
]
=
self
.
context
[
"cc_requester"
][
"id"
]
return
instance
return
instance
return
Comment
(
return
Comment
(
course_id
=
self
.
context
[
"thread"
][
"course_id"
],
course_id
=
self
.
context
[
"thread"
][
"course_id"
],
...
...
lms/djangoapps/discussion_api/tests/test_api.py
View file @
ef26e8e8
...
@@ -1787,22 +1787,67 @@ class UpdateCommentTest(CommentsServiceMockMixin, UrlResetMixin, ModuleStoreTest
...
@@ -1787,22 +1787,67 @@ class UpdateCommentTest(CommentsServiceMockMixin, UrlResetMixin, ModuleStoreTest
except
Http404
:
except
Http404
:
self
.
assertTrue
(
expected_error
)
self
.
assertTrue
(
expected_error
)
@ddt.data
(
@ddt.data
(
*
itertools
.
product
(
FORUM_ROLE_ADMINISTRATOR
,
[
FORUM_ROLE_MODERATOR
,
FORUM_ROLE_ADMINISTRATOR
,
FORUM_ROLE_COMMUNITY_TA
,
FORUM_ROLE_MODERATOR
,
FORUM_ROLE_STUDENT
,
FORUM_ROLE_COMMUNITY_TA
,
)
FORUM_ROLE_STUDENT
,
def
test_role_access
(
self
,
role_name
):
],
[
True
,
False
],
[
True
,
False
],
))
@ddt.unpack
def
test_raw_body_access
(
self
,
role_name
,
is_thread_author
,
is_comment_author
):
role
=
Role
.
objects
.
create
(
name
=
role_name
,
course_id
=
self
.
course
.
id
)
role
=
Role
.
objects
.
create
(
name
=
role_name
,
course_id
=
self
.
course
.
id
)
role
.
users
=
[
self
.
user
]
role
.
users
=
[
self
.
user
]
self
.
register_comment
({
"user_id"
:
str
(
self
.
user
.
id
+
1
)})
self
.
register_comment
(
expected_error
=
role_name
==
FORUM_ROLE_STUDENT
{
"user_id"
:
str
(
self
.
user
.
id
if
is_comment_author
else
(
self
.
user
.
id
+
1
))},
thread_overrides
=
{
"user_id"
:
str
(
self
.
user
.
id
if
is_thread_author
else
(
self
.
user
.
id
+
1
))
}
)
expected_error
=
role_name
==
FORUM_ROLE_STUDENT
and
not
is_comment_author
try
:
try
:
update_comment
(
self
.
request
,
"test_comment"
,
{
"raw_body"
:
"edited"
})
update_comment
(
self
.
request
,
"test_comment"
,
{
"raw_body"
:
"edited"
})
self
.
assertFalse
(
expected_error
)
self
.
assertFalse
(
expected_error
)
except
PermissionDenied
:
except
ValidationError
as
err
:
self
.
assertTrue
(
expected_error
)
self
.
assertEqual
(
err
.
message_dict
,
{
"raw_body"
:
[
"This field is not editable."
]}
)
@ddt.data
(
*
itertools
.
product
(
[
FORUM_ROLE_ADMINISTRATOR
,
FORUM_ROLE_MODERATOR
,
FORUM_ROLE_COMMUNITY_TA
,
FORUM_ROLE_STUDENT
,
],
[
True
,
False
],
[
True
,
False
],
))
@ddt.unpack
def
test_endorsed_access
(
self
,
role_name
,
is_thread_author
,
is_comment_author
):
role
=
Role
.
objects
.
create
(
name
=
role_name
,
course_id
=
self
.
course
.
id
)
role
.
users
=
[
self
.
user
]
self
.
register_comment
(
{
"user_id"
:
str
(
self
.
user
.
id
if
is_comment_author
else
(
self
.
user
.
id
+
1
))},
thread_overrides
=
{
"user_id"
:
str
(
self
.
user
.
id
if
is_thread_author
else
(
self
.
user
.
id
+
1
))
}
)
expected_error
=
role_name
==
FORUM_ROLE_STUDENT
and
not
is_thread_author
try
:
update_comment
(
self
.
request
,
"test_comment"
,
{
"endorsed"
:
True
})
self
.
assertFalse
(
expected_error
)
except
ValidationError
as
err
:
self
.
assertTrue
(
expected_error
)
self
.
assertTrue
(
expected_error
)
self
.
assertEqual
(
err
.
message_dict
,
{
"endorsed"
:
[
"This field is not editable."
]}
)
@ddt.ddt
@ddt.ddt
...
...
lms/djangoapps/discussion_api/tests/test_serializers.py
View file @
ef26e8e8
...
@@ -656,6 +656,27 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -656,6 +656,27 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
{
field
:
[
"This field is required."
]}
{
field
:
[
"This field is required."
]}
)
)
def
test_create_endorsed
(
self
):
# TODO: The comments service doesn't populate the endorsement field on
# comment creation, so this is sadly realistic
self
.
register_post_comment_response
({},
thread_id
=
"test_thread"
)
data
=
self
.
minimal_data
.
copy
()
data
[
"endorsed"
]
=
True
saved
=
self
.
save_and_reserialize
(
data
)
self
.
assertEqual
(
httpretty
.
last_request
()
.
parsed_body
,
{
"course_id"
:
[
unicode
(
self
.
course
.
id
)],
"body"
:
[
"Test body"
],
"user_id"
:
[
str
(
self
.
user
.
id
)],
"endorsed"
:
[
"True"
],
}
)
self
.
assertTrue
(
saved
[
"endorsed"
])
self
.
assertIsNone
(
saved
[
"endorsed_by"
])
self
.
assertIsNone
(
saved
[
"endorsed_by_label"
])
self
.
assertIsNone
(
saved
[
"endorsed_at"
])
def
test_update_empty
(
self
):
def
test_update_empty
(
self
):
self
.
register_put_comment_response
(
self
.
existing_comment
.
attributes
)
self
.
register_put_comment_response
(
self
.
existing_comment
.
attributes
)
self
.
save_and_reserialize
({},
instance
=
self
.
existing_comment
)
self
.
save_and_reserialize
({},
instance
=
self
.
existing_comment
)
...
@@ -672,8 +693,13 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -672,8 +693,13 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
)
)
def
test_update_all
(
self
):
def
test_update_all
(
self
):
self
.
register_put_comment_response
(
self
.
existing_comment
.
attributes
)
cs_response_data
=
self
.
existing_comment
.
attributes
.
copy
()
data
=
{
"raw_body"
:
"Edited body"
}
cs_response_data
[
"endorsement"
]
=
{
"user_id"
:
str
(
self
.
user
.
id
),
"time"
:
"2015-06-05T00:00:00Z"
,
}
self
.
register_put_comment_response
(
cs_response_data
)
data
=
{
"raw_body"
:
"Edited body"
,
"endorsed"
:
True
}
saved
=
self
.
save_and_reserialize
(
data
,
instance
=
self
.
existing_comment
)
saved
=
self
.
save_and_reserialize
(
data
,
instance
=
self
.
existing_comment
)
self
.
assertEqual
(
self
.
assertEqual
(
httpretty
.
last_request
()
.
parsed_body
,
httpretty
.
last_request
()
.
parsed_body
,
...
@@ -683,10 +709,14 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -683,10 +709,14 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
"user_id"
:
[
str
(
self
.
user
.
id
)],
"user_id"
:
[
str
(
self
.
user
.
id
)],
"anonymous"
:
[
"False"
],
"anonymous"
:
[
"False"
],
"anonymous_to_peers"
:
[
"False"
],
"anonymous_to_peers"
:
[
"False"
],
"endorsed"
:
[
"False"
],
"endorsed"
:
[
"True"
],
"endorsement_user_id"
:
[
str
(
self
.
user
.
id
)],
}
}
)
)
self
.
assertEqual
(
saved
[
"raw_body"
],
data
[
"raw_body"
])
for
key
in
data
:
self
.
assertEqual
(
saved
[
key
],
data
[
key
])
self
.
assertEqual
(
saved
[
"endorsed_by"
],
self
.
user
.
username
)
self
.
assertEqual
(
saved
[
"endorsed_at"
],
"2015-06-05T00:00:00Z"
)
def
test_update_empty_raw_body
(
self
):
def
test_update_empty_raw_body
(
self
):
serializer
=
CommentSerializer
(
serializer
=
CommentSerializer
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment