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
c74b34c1
Commit
c74b34c1
authored
Jun 16, 2014
by
Jim Abramson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4041 from edx/jsa/forums-search-users
implement forums endpoint for searching users.
parents
1d5f5db9
c2ebfde4
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
178 additions
and
52 deletions
+178
-52
common/djangoapps/django_comment_common/models.py
+1
-1
lms/djangoapps/django_comment_client/base/tests.py
+95
-12
lms/djangoapps/django_comment_client/base/urls.py
+1
-0
lms/djangoapps/django_comment_client/base/views.py
+80
-39
lms/lib/comment_client/user.py
+1
-0
No files found.
common/djangoapps/django_comment_common/models.py
View file @
c74b34c1
...
...
@@ -44,7 +44,7 @@ def assign_default_role(course_id, user):
"""
Assign forum default role 'Student' to user
"""
role
,
__
=
Role
.
objects
.
get_or_create
(
course_id
=
course_id
,
name
=
"Student"
)
role
,
__
=
Role
.
objects
.
get_or_create
(
course_id
=
course_id
,
name
=
FORUM_ROLE_STUDENT
)
user
.
roles
.
add
(
role
)
...
...
lms/djangoapps/django_comment_client/base/tests.py
View file @
c74b34c1
import
logging
import
json
from
django.test.utils
import
override_settings
from
django.test.client
import
Client
,
RequestFactory
from
django.test.utils
import
override_settings
from
django.contrib.auth.models
import
User
from
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
django.core.urlresolvers
import
reverse
from
django.core.management
import
call_command
from
util.testing
import
UrlResetMixin
from
django_comment_common.models
import
Role
from
django_comment_common.utils
import
seed_permissions_roles
from
django_comment_client.base
import
views
from
django_comment_client.tests.unicode
import
UnicodeTestMixin
from
django.core.urlresolvers
import
reverse
from
mock
import
patch
,
ANY
from
nose.tools
import
assert_true
,
assert_equal
# pylint: disable=E0611
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
nose.tools
import
assert_true
,
assert_equal
# pylint: disable=E0611
from
mock
import
patch
,
ANY
from
django_comment_client.base
import
views
from
django_comment_client.tests.unicode
import
UnicodeTestMixin
from
django_comment_common.models
import
Role
,
FORUM_ROLE_STUDENT
from
django_comment_common.utils
import
seed_permissions_roles
from
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
from
util.testing
import
UrlResetMixin
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -732,3 +733,85 @@ class CreateSubCommentUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, Moc
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertTrue
(
mock_request
.
called
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"body"
],
text
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
class
UsersEndpointTestCase
(
ModuleStoreTestCase
,
MockRequestSetupMixin
):
def
set_post_counts
(
self
,
mock_request
,
threads_count
=
1
,
comments_count
=
1
):
"""
sets up a mock response from the comments service for getting post counts for our other_user
"""
self
.
_set_mock_request_data
(
mock_request
,
{
"threads_count"
:
threads_count
,
"comments_count"
:
comments_count
,
})
def
setUp
(
self
):
self
.
course
=
CourseFactory
.
create
()
seed_permissions_roles
(
self
.
course
.
id
)
self
.
student
=
UserFactory
.
create
()
self
.
enrollment
=
CourseEnrollmentFactory
(
user
=
self
.
student
,
course_id
=
self
.
course
.
id
)
self
.
other_user
=
UserFactory
.
create
(
username
=
"other"
)
CourseEnrollmentFactory
(
user
=
self
.
other_user
,
course_id
=
self
.
course
.
id
)
def
make_request
(
self
,
method
=
'get'
,
course_id
=
None
,
**
kwargs
):
course_id
=
course_id
or
self
.
course
.
id
request
=
getattr
(
RequestFactory
(),
method
)(
"dummy_url"
,
kwargs
)
request
.
user
=
self
.
student
request
.
view_name
=
"users"
return
views
.
users
(
request
,
course_id
=
course_id
.
to_deprecated_string
())
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
def
test_finds_exact_match
(
self
,
mock_request
):
self
.
set_post_counts
(
mock_request
)
response
=
self
.
make_request
(
username
=
"other"
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
json
.
loads
(
response
.
content
)[
"users"
],
[{
"id"
:
self
.
other_user
.
id
,
"username"
:
self
.
other_user
.
username
}]
)
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
def
test_finds_no_match
(
self
,
mock_request
):
self
.
set_post_counts
(
mock_request
)
response
=
self
.
make_request
(
username
=
"othor"
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
json
.
loads
(
response
.
content
)[
"users"
],
[])
def
test_requires_GET
(
self
):
response
=
self
.
make_request
(
method
=
'post'
,
username
=
"other"
)
self
.
assertEqual
(
response
.
status_code
,
405
)
def
test_requires_username_param
(
self
):
response
=
self
.
make_request
()
self
.
assertEqual
(
response
.
status_code
,
400
)
content
=
json
.
loads
(
response
.
content
)
self
.
assertIn
(
"errors"
,
content
)
self
.
assertNotIn
(
"users"
,
content
)
def
test_course_does_not_exist
(
self
):
course_id
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
"does/not/exist"
)
response
=
self
.
make_request
(
course_id
=
course_id
,
username
=
"other"
)
self
.
assertEqual
(
response
.
status_code
,
404
)
content
=
json
.
loads
(
response
.
content
)
self
.
assertIn
(
"errors"
,
content
)
self
.
assertNotIn
(
"users"
,
content
)
def
test_requires_requestor_enrolled_in_course
(
self
):
# unenroll self.student from the course.
self
.
enrollment
.
delete
()
response
=
self
.
make_request
(
username
=
"other"
)
self
.
assertEqual
(
response
.
status_code
,
404
)
content
=
json
.
loads
(
response
.
content
)
self
.
assertTrue
(
content
.
has_key
(
"errors"
))
self
.
assertFalse
(
content
.
has_key
(
"users"
))
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
def
test_requires_matched_user_has_forum_content
(
self
,
mock_request
):
self
.
set_post_counts
(
mock_request
,
0
,
0
)
response
=
self
.
make_request
(
username
=
"other"
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
json
.
loads
(
response
.
content
)[
"users"
],
[])
lms/djangoapps/django_comment_client/base/urls.py
View file @
c74b34c1
...
...
@@ -27,4 +27,5 @@ urlpatterns = patterns('django_comment_client.base.views', # nopep8
url
(
r'^(?P<commentable_id>[\w\-.]+)/threads/create$'
,
'create_thread'
,
name
=
'create_thread'
),
url
(
r'^(?P<commentable_id>[\w\-.]+)/follow$'
,
'follow_commentable'
,
name
=
'follow_commentable'
),
url
(
r'^(?P<commentable_id>[\w\-.]+)/unfollow$'
,
'unfollow_commentable'
,
name
=
'unfollow_commentable'
),
url
(
r'users$'
,
'users'
,
name
=
'users'
),
)
lms/djangoapps/django_comment_client/base/views.py
View file @
c74b34c1
import
time
import
random
import
os.path
import
functools
import
logging
import
os.path
import
random
import
time
import
urlparse
import
functools
import
lms.lib.comment_client
as
cc
import
django_comment_client.utils
as
utils
import
django_comment_client.settings
as
cc_settings
from
django.core
import
exceptions
from
django.contrib.auth.decorators
import
login_required
from
django.
views.decorators.http
import
require_POST
from
django.
views.decorators
import
csrf
from
django.
contrib.auth.models
import
User
from
django.
core
import
exceptions
from
django.core.files.storage
import
get_storage_class
from
django.http
import
Http404
from
django.utils.translation
import
ugettext
as
_
from
django.views.decorators
import
csrf
from
django.views.decorators.http
import
require_GET
,
require_POST
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
courseware.access
import
has_access
from
courseware.courses
import
get_course_with_access
,
get_course_by_id
from
course_groups.cohorts
import
get_cohort_id
,
is_commentable_cohorted
from
django_comment_client.utils
import
JsonResponse
,
JsonError
,
extract
,
add_courseware_context
import
django_comment_client.settings
as
cc_settings
from
django_comment_client.utils
import
(
add_courseware_context
,
get_annotated_content_info
,
get_ability
,
JsonError
,
JsonResponse
,
safe_content
)
from
django_comment_client.permissions
import
check_permissions_by_view
,
cached_has_permission
from
courseware.access
import
has_access
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.keys
import
CourseKey
import
lms.lib.comment_client
as
cc
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -51,9 +55,9 @@ def permitted(fn):
def
ajax_content_response
(
request
,
course_id
,
content
):
user_info
=
cc
.
User
.
from_django_user
(
request
.
user
)
.
to_dict
()
annotated_content_info
=
utils
.
get_annotated_content_info
(
course_id
,
content
,
request
.
user
,
user_info
)
annotated_content_info
=
get_annotated_content_info
(
course_id
,
content
,
request
.
user
,
user_info
)
return
JsonResponse
({
'content'
:
utils
.
safe_content
(
content
),
'content'
:
safe_content
(
content
),
'annotated_content_info'
:
annotated_content_info
,
})
...
...
@@ -133,7 +137,7 @@ def create_thread(request, course_id, commentable_id):
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
course_id
,
data
)
else
:
return
JsonResponse
(
utils
.
safe_content
(
data
))
return
JsonResponse
(
safe_content
(
data
))
@require_POST
...
...
@@ -154,7 +158,7 @@ def update_thread(request, course_id, thread_id):
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
),
thread
.
to_dict
())
else
:
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
def
_create_comment
(
request
,
course_key
,
thread_id
=
None
,
parent_id
=
None
):
...
...
@@ -195,7 +199,7 @@ def _create_comment(request, course_key, thread_id=None, parent_id=None):
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
course_key
,
comment
.
to_dict
())
else
:
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -222,7 +226,7 @@ def delete_thread(request, course_id, thread_id):
"""
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
delete
()
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -241,7 +245,7 @@ def update_comment(request, course_id, comment_id):
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
),
comment
.
to_dict
())
else
:
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -255,7 +259,7 @@ def endorse_comment(request, course_id, comment_id):
comment
=
cc
.
Comment
.
find
(
comment_id
)
comment
.
endorsed
=
request
.
POST
.
get
(
'endorsed'
,
'false'
)
.
lower
()
==
'true'
comment
.
save
()
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -271,8 +275,8 @@ def openclose_thread(request, course_id, thread_id):
thread
.
save
()
thread
=
thread
.
to_dict
()
return
JsonResponse
({
'content'
:
utils
.
safe_content
(
thread
),
'ability'
:
utils
.
get_ability
(
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
),
thread
,
request
.
user
),
'content'
:
safe_content
(
thread
),
'ability'
:
get_ability
(
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
),
thread
,
request
.
user
),
})
...
...
@@ -300,7 +304,7 @@ def delete_comment(request, course_id, comment_id):
"""
comment
=
cc
.
Comment
.
find
(
comment_id
)
comment
.
delete
()
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -313,7 +317,7 @@ def vote_for_comment(request, course_id, comment_id, value):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
comment
=
cc
.
Comment
.
find
(
comment_id
)
user
.
vote
(
comment
,
value
)
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -327,7 +331,7 @@ def undo_vote_for_comment(request, course_id, comment_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
comment
=
cc
.
Comment
.
find
(
comment_id
)
user
.
unvote
(
comment
)
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -341,7 +345,7 @@ def vote_for_thread(request, course_id, thread_id, value):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
user
.
vote
(
thread
,
value
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -355,7 +359,7 @@ def flag_abuse_for_thread(request, course_id, thread_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
flagAbuse
(
user
,
thread
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -372,7 +376,7 @@ def un_flag_abuse_for_thread(request, course_id, thread_id):
thread
=
cc
.
Thread
.
find
(
thread_id
)
remove_all
=
cached_has_permission
(
request
.
user
,
'openclose_thread'
,
course_id
)
or
has_access
(
request
.
user
,
'staff'
,
course
)
thread
.
unFlagAbuse
(
user
,
thread
,
remove_all
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -386,7 +390,7 @@ def flag_abuse_for_comment(request, course_id, comment_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
comment
=
cc
.
Comment
.
find
(
comment_id
)
comment
.
flagAbuse
(
user
,
comment
)
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -403,7 +407,7 @@ def un_flag_abuse_for_comment(request, course_id, comment_id):
remove_all
=
cached_has_permission
(
request
.
user
,
'openclose_thread'
,
course_key
)
or
has_access
(
request
.
user
,
'staff'
,
course
)
comment
=
cc
.
Comment
.
find
(
comment_id
)
comment
.
unFlagAbuse
(
user
,
comment
,
remove_all
)
return
JsonResponse
(
utils
.
safe_content
(
comment
.
to_dict
()))
return
JsonResponse
(
safe_content
(
comment
.
to_dict
()))
@require_POST
...
...
@@ -417,7 +421,7 @@ def undo_vote_for_thread(request, course_id, thread_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
user
.
unvote
(
thread
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -431,7 +435,7 @@ def pin_thread(request, course_id, thread_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
pin
(
user
,
thread_id
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -445,7 +449,7 @@ def un_pin_thread(request, course_id, thread_id):
user
=
cc
.
User
.
from_django_user
(
request
.
user
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
un_pin
(
user
,
thread_id
)
return
JsonResponse
(
utils
.
safe_content
(
thread
.
to_dict
()))
return
JsonResponse
(
safe_content
(
thread
.
to_dict
()))
@require_POST
...
...
@@ -598,3 +602,40 @@ def upload(request, course_id): # ajax upload file to a question or answer
'file_url'
:
file_url
,
}
})
@require_GET
@login_required
def
users
(
request
,
course_id
):
"""
Given a `username` query parameter, find matches for users in the forum for this course.
Only exact matches are supported here, so the length of the result set will either be 0 or 1.
"""
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
try
:
course
=
get_course_with_access
(
request
.
user
,
'load_forum'
,
course_key
)
except
Http404
:
# course didn't exist, or requesting user does not have access to it.
return
JsonError
(
status
=
404
)
try
:
username
=
request
.
GET
[
'username'
]
except
KeyError
:
# 400 is default status for JsonError
return
JsonError
([
"username parameter is required"
])
user_objs
=
[]
try
:
matched_user
=
User
.
objects
.
get
(
username
=
username
)
cc_user
=
cc
.
User
.
from_django_user
(
matched_user
)
cc_user
.
course_id
=
course_key
cc_user
.
retrieve
(
complete
=
False
)
if
(
cc_user
[
'threads_count'
]
+
cc_user
[
'comments_count'
])
>
0
:
user_objs
.
append
({
'id'
:
matched_user
.
id
,
'username'
:
matched_user
.
username
,
})
except
User
.
DoesNotExist
:
pass
return
JsonResponse
({
"users"
:
user_objs
})
lms/lib/comment_client/user.py
View file @
c74b34c1
...
...
@@ -117,6 +117,7 @@ class User(models.Model):
def
_retrieve
(
self
,
*
args
,
**
kwargs
):
url
=
self
.
url
(
action
=
'get'
,
params
=
self
.
attributes
)
retrieve_params
=
self
.
default_retrieve_params
retrieve_params
.
update
(
kwargs
)
if
self
.
attributes
.
get
(
'course_id'
):
retrieve_params
[
'course_id'
]
=
self
.
course_id
.
to_deprecated_string
()
try
:
...
...
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