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
4f164247
Commit
4f164247
authored
Jul 30, 2014
by
Matt Drayer
Committed by
Jonathan Piacenti
Aug 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mattdrayer/api-users-courses-roles: Added support for setting user roles
parent
e5a025df
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
425 additions
and
31 deletions
+425
-31
lms/djangoapps/api_manager/courses/tests.py
+93
-0
lms/djangoapps/api_manager/courses/urls.py
+8
-6
lms/djangoapps/api_manager/courses/views.py
+99
-2
lms/djangoapps/api_manager/users/serializers.py
+6
-0
lms/djangoapps/api_manager/users/tests.py
+109
-0
lms/djangoapps/api_manager/users/urls.py
+2
-0
lms/djangoapps/api_manager/users/views.py
+108
-23
No files found.
lms/djangoapps/api_manager/courses/tests.py
View file @
4f164247
...
...
@@ -17,6 +17,8 @@ from django.test.utils import override_settings
from
capa.tests.response_xml_factory
import
StringResponseXMLFactory
from
courseware.tests.factories
import
StudentModuleFactory
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
django_comment_common.models
import
Role
,
FORUM_ROLE_MODERATOR
from
instructor.access
import
allow_access
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
...
@@ -189,6 +191,10 @@ class CoursesApiTests(TestCase):
self
.
client
=
SecureClient
()
cache
.
clear
()
Role
.
objects
.
get_or_create
(
name
=
FORUM_ROLE_MODERATOR
,
course_id
=
self
.
course
.
id
)
def
do_get
(
self
,
uri
):
"""Submit an HTTP GET request"""
headers
=
{
...
...
@@ -1778,3 +1784,90 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
1
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'city'
],
'Denver'
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'count'
],
5
)
def
test_courses_roles_list_get
(
self
):
allow_access
(
self
.
course
,
self
.
users
[
0
],
'staff'
)
allow_access
(
self
.
course
,
self
.
users
[
1
],
'instructor'
)
allow_access
(
self
.
course
,
self
.
users
[
2
],
'staff'
)
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
3
)
# filter roleset by user
user_id
=
{
'user_id'
:
'{}'
.
format
(
self
.
users
[
0
]
.
id
)}
test_uri
=
'{}?{}'
.
format
(
test_uri
,
urlencode
(
user_id
))
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
def
test_courses_roles_list_get_invalid_course
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
self
.
test_bogus_course_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_roles_list_post
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
0
)
data
=
{
'user_id'
:
self
.
users
[
0
]
.
id
,
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
def
test_courses_roles_list_post_invalid_course
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
self
.
test_bogus_course_id
)
data
=
{
'user_id'
:
self
.
users
[
0
]
.
id
,
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_roles_list_post_invalid_user
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
data
=
{
'user_id'
:
23423
,
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_roles_list_post_invalid_role
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
data
=
{
'user_id'
:
self
.
users
[
0
]
.
id
,
'role'
:
'invalid_role'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_courses_roles_users_detail_delete
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
data
=
{
'user_id'
:
self
.
users
[
0
]
.
id
,
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
delete_uri
=
'{}instructor/users/{}'
.
format
(
test_uri
,
self
.
users
[
0
]
.
id
)
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
204
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
len
(
response
.
data
),
0
)
def
test_courses_roles_users_detail_delete_invalid_course
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
self
.
test_bogus_course_id
)
delete_uri
=
'{}instructor/users/{}'
.
format
(
test_uri
,
self
.
users
[
0
]
.
id
)
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_roles_users_detail_delete_invalid_user
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
delete_uri
=
'{}instructor/users/291231'
.
format
(
test_uri
)
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_roles_users_detail_delete_invalid_role
(
self
):
test_uri
=
'/api/courses/{}/roles/'
.
format
(
unicode
(
self
.
course
.
id
))
delete_uri
=
'{}invalid_role/users/{}'
.
format
(
test_uri
,
self
.
users
[
0
]
.
id
)
print
delete_uri
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
lms/djangoapps/api_manager/courses/urls.py
View file @
4f164247
...
...
@@ -20,19 +20,21 @@ urlpatterns = patterns(
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/groups/(?P<group_id>[0-9]+)$'
,
courses_views
.
CoursesGroupsDetail
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/groups/*$'
,
courses_views
.
CoursesGroupsList
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/overview/*$'
,
courses_views
.
CoursesOverview
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/updates/*$'
,
courses_views
.
CoursesUpdates
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/static_tabs/(?P<tab_id>[a-zA-Z0-9_+\/:]+)$'
,
courses_views
.
CoursesStaticTabsDetail
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/static_tabs/*$'
,
courses_views
.
CoursesStaticTabsList
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/users/(?P<user_id>[0-9]+)$'
,
courses_views
.
CoursesUsersDetail
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/users/*$'
,
courses_views
.
CoursesUsersList
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/completions/*$'
,
courses_views
.
CourseModuleCompletionList
.
as_view
(),
name
=
'completion-list'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/projects/*$'
,
courses_views
.
CoursesProjectList
.
as_view
(),
name
=
'courseproject-list'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/*$'
,
courses_views
.
CourseMetrics
.
as_view
(),
name
=
'course-metrics'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/
proficiency/leaders/*$'
,
courses_views
.
CoursesLeadersList
.
as_view
(),
name
=
'course-metrics-proficiency-leader
s'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/
cities/$'
,
courses_views
.
CoursesCitiesMetrics
.
as_view
(),
name
=
'courses-cities-metric
s'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/completions/leaders/*$'
,
courses_views
.
CoursesCompletionsLeadersList
.
as_view
(),
name
=
'course-metrics-completions-leaders'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/
workgroups/*$'
,
courses_views
.
CoursesWorkgroupsList
.
as_view
()
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/
metrics/proficiency/leaders/*$'
,
courses_views
.
CoursesLeadersList
.
as_view
(),
name
=
'course-metrics-proficiency-leaders'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/social/$'
,
courses_views
.
CoursesSocialMetrics
.
as_view
(),
name
=
'courses-social-metrics'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/metrics/cities/$'
,
courses_views
.
CoursesCitiesMetrics
.
as_view
(),
name
=
'courses-cities-metrics'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/roles/(?P<role>[a-z_]+)/users/(?P<user_id>[0-9]+)*$'
,
courses_views
.
CoursesRolesUsersDetail
.
as_view
(),
name
=
'courses-roles-users-detail'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/roles/*$'
,
courses_views
.
CoursesRolesList
.
as_view
(),
name
=
'courses-roles-list'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/updates/*$'
,
courses_views
.
CoursesUpdates
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/users/(?P<user_id>[0-9]+)$'
,
courses_views
.
CoursesUsersDetail
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/users/*$'
,
courses_views
.
CoursesUsersList
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)/workgroups/*$'
,
courses_views
.
CoursesWorkgroupsList
.
as_view
()),
url
(
r'^(?P<course_id>[a-zA-Z0-9_+\/:]+)$'
,
courses_views
.
CoursesDetail
.
as_view
()),
url
(
r'/*$^'
,
courses_views
.
CoursesList
.
as_view
()),
)
...
...
lms/djangoapps/api_manager/courses/views.py
View file @
4f164247
...
...
@@ -6,6 +6,7 @@ import itertools
from
lxml
import
etree
from
StringIO
import
StringIO
from
django.conf
import
settings
from
django.contrib.auth.models
import
Group
,
User
from
django.core.exceptions
import
ObjectDoesNotExist
...
...
@@ -20,7 +21,11 @@ from rest_framework.response import Response
from
courseware.courses
import
get_course_about_section
,
get_course_info_section
from
courseware.models
import
StudentModule
from
courseware.views
import
get_static_tab_contents
from
django_comment_common.models
import
FORUM_ROLE_MODERATOR
from
instructor.access
import
allow_access
,
revoke_access
,
update_forum_role
from
student.models
import
CourseEnrollment
,
CourseEnrollmentAllowed
from
student.roles
import
CourseInstructorRole
,
CourseStaffRole
from
xmodule.modulestore.django
import
modulestore
from
api_manager.courseware_access
import
get_course
,
get_course_child
...
...
@@ -31,7 +36,6 @@ from api_manager.users.serializers import UserSerializer, UserCountByCitySeriali
from
api_manager.utils
import
generate_base_uri
from
projects.models
import
Project
,
Workgroup
from
projects.serializers
import
ProjectSerializer
,
BasicWorkgroupSerializer
from
.serializers
import
CourseModuleCompletionSerializer
from
.serializers
import
GradeSerializer
,
CourseLeadersSerializer
,
CourseCompletionsLeadersSerializer
...
...
@@ -975,7 +979,7 @@ class CoursesUsersList(SecureAPIView):
def
get
(
self
,
request
,
course_id
):
"""
GET /api/courses/{course_id}
GET /api/courses/{course_id}
/users
"""
orgs
=
request
.
QUERY_PARAMS
.
get
(
'organizations'
)
groups
=
request
.
QUERY_PARAMS
.
get
(
'groups'
,
None
)
...
...
@@ -1644,3 +1648,96 @@ class CoursesCitiesMetrics(SecureListAPIView):
queryset
=
queryset
.
values
(
'profile__city'
)
.
annotate
(
count
=
Count
(
'profile__city'
))
\
.
filter
(
count__gt
=
0
)
.
order_by
(
'-count'
)
return
queryset
class
CoursesRolesList
(
SecureAPIView
):
"""
### The CoursesRolesList view allows clients to interact with the Course's roleset
- URI: ```/api/courses/{course_id}/roles```
- GET: Returns a JSON representation of the specified Course roleset
### Use Cases/Notes:
* Use the CoursesRolesList view to manage a User's TA status
* Use GET to retrieve the set of roles configured for a particular course
"""
def
get
(
self
,
request
,
course_id
):
# pylint: disable=W0613
"""
GET /api/courses/{course_id}/roles/
"""
course_id
=
self
.
kwargs
[
'course_id'
]
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
self
.
request
.
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
raise
Http404
response_data
=
[]
instructors
=
CourseInstructorRole
(
course_key
)
.
users_with_role
()
for
instructor
in
instructors
:
response_data
.
append
({
'id'
:
instructor
.
id
,
'role'
:
'instructor'
})
staff
=
CourseStaffRole
(
course_key
)
.
users_with_role
()
for
admin
in
staff
:
response_data
.
append
({
'id'
:
admin
.
id
,
'role'
:
'staff'
})
user_id
=
self
.
request
.
QUERY_PARAMS
.
get
(
'user_id'
,
None
)
if
user_id
:
response_data
=
list
([
item
for
item
in
response_data
if
int
(
item
[
'id'
])
==
int
(
user_id
)])
return
Response
(
response_data
,
status
=
status
.
HTTP_200_OK
)
def
post
(
self
,
request
,
course_id
):
"""
POST /api/courses/{course_id}/roles/
"""
course_id
=
self
.
kwargs
[
'course_id'
]
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
self
.
request
.
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
raise
Http404
user_id
=
request
.
DATA
.
get
(
'user_id'
,
None
)
try
:
user
=
User
.
objects
.
get
(
id
=
user_id
)
except
ObjectDoesNotExist
:
raise
Http404
role
=
request
.
DATA
.
get
(
'role'
,
None
)
try
:
allow_access
(
course_descriptor
,
user
,
role
)
update_forum_role
(
course_key
,
user
,
FORUM_ROLE_MODERATOR
,
'allow'
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
Response
(
request
.
DATA
,
status
=
status
.
HTTP_201_CREATED
)
class
CoursesRolesUsersDetail
(
SecureAPIView
):
"""
### The CoursesUsersRolesDetail view allows clients to interact with a specific Course Role
- URI: ```/api/courses/{course_id}/roles/{role}/users/{user_id}```
- DELETE: Removes an existing Course Role specification
### Use Cases/Notes:
* Use the DELETE operation to revoke a particular role for the specified user
"""
def
delete
(
self
,
request
,
course_id
,
role
,
user_id
):
# pylint: disable=W0613
"""
DELETE /api/courses/{course_id}/roles/{role}/users/{user_id}
"""
course_id
=
self
.
kwargs
[
'course_id'
]
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
self
.
request
.
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
user_id
=
self
.
kwargs
[
'user_id'
]
try
:
user
=
User
.
objects
.
get
(
id
=
user_id
)
except
ObjectDoesNotExist
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
role
=
self
.
kwargs
[
'role'
]
try
:
revoke_access
(
course_descriptor
,
user
,
role
)
update_forum_role
(
course_key
,
user
,
FORUM_ROLE_MODERATOR
,
'revoke'
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
({},
status
=
status
.
HTTP_204_NO_CONTENT
)
lms/djangoapps/api_manager/users/serializers.py
View file @
4f164247
...
...
@@ -21,3 +21,9 @@ class UserCountByCitySerializer(serializers.Serializer):
""" Serializer for user count by city """
city
=
serializers
.
CharField
(
source
=
'profile__city'
)
count
=
serializers
.
IntegerField
()
class
UserRolesSerializer
(
serializers
.
Serializer
):
""" Serializer for user roles """
course_id
=
serializers
.
CharField
(
source
=
'course_id'
)
role
=
serializers
.
CharField
(
source
=
'role'
)
lms/djangoapps/api_manager/users/tests.py
View file @
4f164247
...
...
@@ -19,6 +19,8 @@ from django.test.utils import override_settings
from
capa.tests.response_xml_factory
import
StringResponseXMLFactory
from
courseware.tests.factories
import
StudentModuleFactory
from
django_comment_common.models
import
Role
,
FORUM_ROLE_MODERATOR
from
instructor.access
import
allow_access
from
projects.models
import
Project
from
student.tests.factories
import
UserFactory
from
student.models
import
anonymous_id_for_user
...
...
@@ -93,6 +95,10 @@ class UsersApiTests(ModuleStoreTestCase):
self
.
client
=
SecureClient
()
cache
.
clear
()
Role
.
objects
.
get_or_create
(
name
=
FORUM_ROLE_MODERATOR
,
course_id
=
self
.
course
.
id
)
def
do_post
(
self
,
uri
,
data
):
"""Submit an HTTP POST request"""
headers
=
{
...
...
@@ -1346,3 +1352,106 @@ class UsersApiTests(ModuleStoreTestCase):
test_uri
=
'/api/users/{}/courses/{}/metrics/social/'
.
format
(
12345
,
self
.
course
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_list_get
(
self
):
allow_access
(
self
.
course
,
self
.
user
,
'staff'
)
course2
=
CourseFactory
.
create
(
display_name
=
"TEST COURSE2"
,
start
=
datetime
(
2014
,
6
,
16
,
14
,
30
),
end
=
datetime
(
2015
,
1
,
16
,
14
,
30
)
)
allow_access
(
course2
,
self
.
user
,
'instructor'
)
course3
=
CourseFactory
.
create
(
display_name
=
"TEST COURSE3"
,
start
=
datetime
(
2014
,
6
,
16
,
14
,
30
),
end
=
datetime
(
2015
,
1
,
16
,
14
,
30
)
)
allow_access
(
course3
,
self
.
user
,
'staff'
)
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
3
)
# filter roleset by course
course_id
=
{
'course_id'
:
'{}'
.
format
(
unicode
(
course3
.
id
))}
test_uri
=
'{}?{}'
.
format
(
test_uri
,
urlencode
(
course_id
))
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
1
)
def
test_users_roles_list_get_invalid_user
(
self
):
test_uri
=
'/api/users/23423/roles/'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_list_get_invalid_course
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
course_id
=
{
'course_id'
:
'{}'
.
format
(
unicode
(
self
.
test_bogus_course_id
))}
test_uri
=
'{}?{}'
.
format
(
test_uri
,
urlencode
(
course_id
))
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_list_post
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
0
)
data
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
1
)
def
test_users_roles_list_post_invalid_user
(
self
):
test_uri
=
'/api/users/2131/roles/'
data
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_list_post_invalid_course
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
data
=
{
'course_id'
:
self
.
test_bogus_course_id
,
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_list_post_invalid_role
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
data
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'role'
:
'invalid_role'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_users_roles_courses_detail_delete
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
data
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'role'
:
'instructor'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
data
[
'count'
],
1
)
delete_uri
=
'{}instructor/courses/{}'
.
format
(
test_uri
,
unicode
(
self
.
course
.
id
))
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
204
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
data
[
'count'
],
0
)
def
test_users_roles_courses_detail_delete_invalid_course
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
delete_uri
=
'{}instructor/courses/{}'
.
format
(
test_uri
,
self
.
test_bogus_course_id
)
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_courses_detail_delete_invalid_user
(
self
):
test_uri
=
'/api/users/124134/roles/'
delete_uri
=
'{}instructor/courses/{}'
.
format
(
test_uri
,
unicode
(
self
.
course
.
id
))
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_users_roles_courses_detail_delete_invalid_role
(
self
):
test_uri
=
'/api/users/{}/roles/'
.
format
(
self
.
user
.
id
)
delete_uri
=
'{}invalid_role/courses/{}'
.
format
(
test_uri
,
unicode
(
self
.
course
.
id
))
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
lms/djangoapps/api_manager/users/urls.py
View file @
4f164247
...
...
@@ -22,6 +22,8 @@ urlpatterns = patterns(
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/preferences$'
,
users_views
.
UsersPreferences
.
as_view
(),
name
=
'users-preferences-list'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/preferences/(?P<preference_id>[a-zA-Z0-9_]+)$'
,
users_views
.
UsersPreferencesDetail
.
as_view
(),
name
=
'users-preferences-detail'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/organizations/$'
,
users_views
.
UsersOrganizationsList
.
as_view
(),
name
=
'users-organizations-list'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/roles/(?P<role>[a-z_]+)/courses/(?P<course_id>[a-zA-Z0-9_+\/:]+)$'
,
users_views
.
UsersRolesCoursesDetail
.
as_view
(),
name
=
'users-roles-courses-detail'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/roles/*$'
,
users_views
.
UsersRolesList
.
as_view
(),
name
=
'users-roles-list'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)/workgroups/$'
,
users_views
.
UsersWorkgroupsList
.
as_view
(),
name
=
'users-workgroups-list'
),
url
(
r'^(?P<user_id>[a-zA-Z0-9]+)$'
,
users_views
.
UsersDetail
.
as_view
(),
name
=
'apimgr-users-detail'
),
url
(
r'/*$^'
,
users_views
.
UsersList
.
as_view
(),
name
=
'apimgr-users-list'
),
...
...
lms/djangoapps/api_manager/users/views.py
View file @
4f164247
...
...
@@ -15,37 +15,32 @@ from rest_framework import status
from
rest_framework
import
filters
from
rest_framework.response
import
Response
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_total_score
from
api_manager.permissions
import
SecureAPIView
,
SecureListAPIView
,
IdsInFilterBackend
,
HasOrgsFilterBackend
from
api_manager.models
import
GroupProfile
,
APIUser
as
User
from
api_manager.organizations.serializers
import
OrganizationSerializer
from
api_manager.courses.serializers
import
CourseModuleCompletionSerializer
from
api_manager.utils
import
generate_base_uri
from
projects.serializers
import
BasicWorkgroupSerializer
from
.serializers
import
UserSerializer
,
UserCountByCitySerializer
from
courseware
import
module_render
from
courseware
import
grades
,
module_render
from
courseware.model_data
import
FieldDataCache
from
courseware.models
import
StudentModule
from
courseware.views
import
get_module_for_descriptor
,
save_child_position
,
get_current_child
from
courseware.views
import
save_child_position
,
get_current_child
from
django_comment_common.models
import
FORUM_ROLE_MODERATOR
from
instructor.access
import
allow_access
,
revoke_access
,
update_forum_role
from
lang_pref
import
LANGUAGE_KEY
from
lms.lib.comment_client.user
import
User
as
CommentUser
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
student.models
import
CourseEnrollment
,
PasswordHistory
,
UserProfile
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
xmodule.modulestore.django
import
modulestore
from
student.roles
import
CourseInstructorRole
,
CourseStaffRole
,
UserBasedRole
from
util.bad_request_rate_limiter
import
BadRequestRateLimiter
from
util.password_policy_validators
import
(
validate_password_length
,
validate_password_complexity
,
validate_password_dictionary
)
from
util.bad_request_rate_limiter
import
BadRequestRateLimiter
from
courseware
import
grades
from
lms.lib.comment_client.user
import
User
as
CommentUser
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
api_manager.courses.serializers
import
CourseModuleCompletionSerializer
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_total_score
from
api_manager.permissions
import
SecureAPIView
,
SecureListAPIView
,
IdsInFilterBackend
,
HasOrgsFilterBackend
from
api_manager.models
import
GroupProfile
,
APIUser
as
User
from
api_manager.organizations.serializers
import
OrganizationSerializer
from
api_manager.utils
import
generate_base_uri
from
projects.serializers
import
BasicWorkgroupSerializer
from
.serializers
import
UserSerializer
,
UserCountByCitySerializer
,
UserRolesSerializer
log
=
logging
.
getLogger
(
__name__
)
AUDIT_LOG
=
logging
.
getLogger
(
"audit"
)
...
...
@@ -713,8 +708,6 @@ class UsersCoursesDetail(SecureAPIView):
return
Response
(
response_data
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
response_data
[
'position'
]
=
content_position
return
Response
(
response_data
,
status
=
status
.
HTTP_200_OK
)
def
get
(
self
,
request
,
user_id
,
course_id
):
...
...
@@ -1083,3 +1076,95 @@ class UsersMetricsCitiesList(SecureListAPIView):
queryset
=
queryset
.
values
(
'profile__city'
)
.
annotate
(
count
=
Count
(
'profile__city'
))
\
.
filter
(
count__gt
=
0
)
.
order_by
(
'-count'
)
return
queryset
class
UsersRolesList
(
SecureListAPIView
):
"""
### The UsersRolesList view allows clients to interact with the User's roleset
- URI: ```/api/users/{user_id}/courses/{course_id}/roles```
- GET: Returns a JSON representation of the specified Course roleset
### Use Cases/Notes:
* Use the UsersRolesList view to manage a User's TA status
* Use GET to retrieve the set of roles a User plays for a particular course
"""
serializer_class
=
UserRolesSerializer
def
get_queryset
(
self
):
user_id
=
self
.
kwargs
[
'user_id'
]
try
:
user
=
User
.
objects
.
get
(
id
=
user_id
)
except
ObjectDoesNotExist
:
raise
Http404
instructor_courses
=
UserBasedRole
(
user
,
CourseInstructorRole
.
ROLE
)
.
courses_with_role
()
staff_courses
=
UserBasedRole
(
user
,
CourseStaffRole
.
ROLE
)
.
courses_with_role
()
queryset
=
instructor_courses
|
staff_courses
course_id
=
self
.
request
.
QUERY_PARAMS
.
get
(
'course_id'
,
None
)
if
course_id
:
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
raise
Http404
queryset
=
queryset
.
filter
(
course_id
=
course_key
)
return
queryset
def
post
(
self
,
request
,
user_id
):
"""
POST /api/users/{user_id}/roles/
"""
user_id
=
self
.
kwargs
[
'user_id'
]
try
:
user
=
User
.
objects
.
get
(
id
=
user_id
)
except
ObjectDoesNotExist
:
raise
Http404
course_id
=
request
.
DATA
.
get
(
'course_id'
,
None
)
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
self
.
request
.
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
raise
Http404
role
=
request
.
DATA
.
get
(
'role'
,
None
)
try
:
allow_access
(
course_descriptor
,
user
,
role
)
update_forum_role
(
course_key
,
user
,
FORUM_ROLE_MODERATOR
,
'allow'
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
Response
(
request
.
DATA
,
status
=
status
.
HTTP_201_CREATED
)
class
UsersRolesCoursesDetail
(
SecureAPIView
):
"""
### The UsersRolesCoursesDetail view allows clients to interact with a specific User/Course Role
- URI: ```/api/users/{user_id}/roles/{role}/courses/{course_id}/```
- DELETE: Removes an existing Course Role specification
### Use Cases/Notes:
* Use the DELETE operation to revoke a particular role for the specified user
"""
def
delete
(
self
,
request
,
user_id
,
role
,
course_id
):
# pylint: disable=W0613
"""
DELETE /api/users/{user_id}/roles/{role}/courses/{course_id}
"""
course_id
=
self
.
kwargs
[
'course_id'
]
print
course_id
course_descriptor
,
course_key
,
course_content
=
get_course
(
self
.
request
,
self
.
request
.
user
,
course_id
,
depth
=
None
)
# pylint: disable=W0612
if
not
course_descriptor
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
user_id
=
self
.
kwargs
[
'user_id'
]
print
user_id
try
:
user
=
User
.
objects
.
get
(
id
=
user_id
)
except
ObjectDoesNotExist
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
role
=
self
.
kwargs
[
'role'
]
try
:
revoke_access
(
course_descriptor
,
user
,
role
)
update_forum_role
(
course_key
,
user
,
FORUM_ROLE_MODERATOR
,
'revoke'
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
({},
status
=
status
.
HTTP_204_NO_CONTENT
)
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