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
214fc702
Commit
214fc702
authored
Apr 20, 2016
by
Saqib
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #682 from edx-solutions/msaqib52/YONK-300
Update users list API
parents
e2389111
6574d611
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
208 additions
and
16 deletions
+208
-16
lms/djangoapps/api_manager/users/serializers.py
+46
-3
lms/djangoapps/api_manager/users/tests.py
+130
-4
lms/djangoapps/api_manager/users/views.py
+32
-9
No files found.
lms/djangoapps/api_manager/users/serializers.py
View file @
214fc702
...
...
@@ -2,7 +2,7 @@
from
rest_framework
import
serializers
from
api_manager.models
import
APIUser
from
api_manager.models
import
APIUser
,
GroupProfile
from
organizations.serializers
import
BasicOrganizationSerializer
from
student.models
import
UserProfile
...
...
@@ -27,6 +27,15 @@ class DynamicFieldsModelSerializer(serializers.ModelSerializer):
self
.
fields
.
pop
(
field_name
)
class
GroupProfileSerializer
(
serializers
.
ModelSerializer
):
""" Serializer for GroupProfile model interactions """
class
Meta
(
object
):
""" Serializer/field specification """
model
=
GroupProfile
fields
=
(
'id'
,
'name'
,
)
class
UserSerializer
(
DynamicFieldsModelSerializer
):
""" Serializer for User model interactions """
...
...
@@ -37,11 +46,45 @@ class UserSerializer(DynamicFieldsModelSerializer):
title
=
serializers
.
CharField
(
source
=
'profile.title'
)
country
=
serializers
.
CharField
(
source
=
'profile.country'
)
full_name
=
serializers
.
CharField
(
source
=
'profile.name'
)
courses_enrolled
=
serializers
.
SerializerMethodField
(
'get_courses_enrolled'
)
roles
=
serializers
.
SerializerMethodField
(
'get_permission_group_type_roles'
)
class
Meta
:
def
get_courses_enrolled
(
self
,
user
):
""" Serialize user enrolled courses """
if
hasattr
(
user
,
'courses_enrolled'
):
return
user
.
courses_enrolled
return
user
.
courseenrollment_set
.
count
def
get_permission_group_type_roles
(
self
,
user
):
""" Serialize GroupProfile for permission group type """
queryset
=
GroupProfile
.
objects
.
filter
(
group__user
=
user
,
group_type
=
'permission'
)
serializer
=
GroupProfileSerializer
(
queryset
,
many
=
True
)
return
serializer
.
data
class
Meta
(
object
):
""" Serializer/field specification """
model
=
APIUser
fields
=
(
"id"
,
"email"
,
"username"
,
"first_name"
,
"last_name"
,
"created"
,
"is_active"
,
"organizations"
,
"avatar_url"
,
"city"
,
"title"
,
"country"
,
"full_name"
,
"is_staff"
)
fields
=
(
"id"
,
"email"
,
"username"
,
"first_name"
,
"last_name"
,
"created"
,
"is_active"
,
"organizations"
,
"avatar_url"
,
"city"
,
"title"
,
"country"
,
"full_name"
,
"is_staff"
,
"last_login"
,
"courses_enrolled"
,
"roles"
)
read_only_fields
=
(
"id"
,
"email"
,
"username"
)
...
...
lms/djangoapps/api_manager/users/tests.py
View file @
214fc702
...
...
@@ -24,13 +24,15 @@ from django.test.utils import override_settings
from
django.utils
import
timezone
from
django.utils.translation
import
ugettext
as
_
from
api_manager.models
import
GroupProfile
from
capa.tests.response_xml_factory
import
StringResponseXMLFactory
from
courseware
import
module_render
from
courseware.model_data
import
FieldDataCache
from
django_comment_common.models
import
Role
,
FORUM_ROLE_MODERATOR
from
instructor.access
import
allow_access
from
organizations.models
import
Organization
from
projects.models
import
Project
,
Workgroup
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
,
GroupFactory
from
student.models
import
anonymous_id_for_user
from
openedx.core.djangoapps.user_api.models
import
UserPreference
...
...
@@ -75,7 +77,6 @@ class SecureClient(Client):
@override_settings
(
MODULESTORE
=
MODULESTORE_CONFIG
)
@override_settings
(
EDX_API_KEY
=
TEST_API_KEY
)
@override_settings
(
PASSWORD_MIN_LENGTH
=
4
)
@override_settings
(
API_PAGE_SIZE
=
10
)
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENFORCE_PASSWORD_POLICY'
:
True
})
class
UsersApiTests
(
ModuleStoreTestCase
):
""" Test suite for Users API views """
...
...
@@ -262,7 +263,14 @@ class UsersApiTests(ModuleStoreTestCase):
# fetch data without any filters applied
response
=
self
.
do_get
(
'{}?page=1'
.
format
(
test_uri
))
self
.
assertEqual
(
response
.
status_code
,
400
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# test default page size
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
20
)
self
.
assertEqual
(
response
.
data
[
'num_pages'
],
2
)
# fetch users data with page outside range
response
=
self
.
do_get
(
'{}?ids={}&page=5'
.
format
(
test_uri
,
'2,3,7,11,6,21,34'
))
self
.
assertEqual
(
response
.
status_code
,
404
)
...
...
@@ -274,6 +282,7 @@ class UsersApiTests(ModuleStoreTestCase):
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'organizations'
][
0
][
'name'
])
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'organizations'
][
0
][
'id'
])
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'organizations'
][
0
][
'url'
])
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'last_login'
])
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'created'
])
# fetch user data by multiple ids
response
=
self
.
do_get
(
'{}?page_size=5&ids={}'
.
format
(
test_uri
,
'2,3,7,11,6,21,34'
))
...
...
@@ -306,7 +315,57 @@ class UsersApiTests(ModuleStoreTestCase):
if
'id'
in
response
.
data
[
'results'
][
0
]:
self
.
fail
(
"Dynamic field filtering error in UserSerializer"
)
def
test_user_list_get_with_org_filter
(
self
):
def
test_user_list_get_courses_enrolled
(
self
):
test_uri
=
self
.
users_base_uri
# create a 2 new users
users
=
UserFactory
.
create_batch
(
2
)
# create course enrollments
CourseEnrollmentFactory
.
create
(
user
=
users
[
1
],
course_id
=
self
.
course
.
id
)
CourseEnrollmentFactory
.
create
(
user
=
users
[
1
],
course_id
=
self
.
course2
.
id
)
# fetch user 1
response
=
self
.
do_get
(
'{}?ids={}'
.
format
(
test_uri
,
users
[
0
]
.
id
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
1
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'courses_enrolled'
],
0
)
# fetch user 2
response
=
self
.
do_get
(
'{}?ids={}'
.
format
(
test_uri
,
users
[
1
]
.
id
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
1
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'courses_enrolled'
],
2
)
def
test_user_list_get_roles
(
self
):
test_uri
=
self
.
users_base_uri
# create a 3 new users
users
=
UserFactory
.
create_batch
(
3
)
groups
=
GroupFactory
.
create_batch
(
3
)
group_profile1
=
GroupProfile
.
objects
.
create
(
group
=
groups
[
0
],
name
=
'role1'
,
group_type
=
'permission'
)
group_profile2
=
GroupProfile
.
objects
.
create
(
group
=
groups
[
1
],
name
=
'role2'
,
group_type
=
'permission'
)
GroupProfile
.
objects
.
create
(
group
=
groups
[
2
],
name
=
'role3'
,
group_type
=
'test'
)
users
[
0
]
.
groups
.
add
(
*
groups
)
users
[
1
]
.
groups
.
add
(
groups
[
0
])
users
[
2
]
.
groups
.
add
(
groups
[
2
])
# fetch users
user_ids
=
','
.
join
([
str
(
user
.
id
)
for
user
in
users
])
response
=
self
.
do_get
(
'{}?ids={}'
.
format
(
test_uri
,
user_ids
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
3
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
][
0
][
'roles'
]),
2
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'roles'
][
0
][
'id'
],
group_profile1
.
id
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'roles'
][
0
][
'name'
],
'role1'
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'roles'
][
1
][
'id'
],
group_profile2
.
id
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'roles'
][
1
][
'name'
],
'role2'
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
][
1
][
'roles'
]),
1
)
self
.
assertEqual
(
response
.
data
[
'results'
][
1
][
'roles'
][
0
][
'id'
],
group_profile1
.
id
)
self
.
assertEqual
(
response
.
data
[
'results'
][
1
][
'roles'
][
0
][
'name'
],
'role1'
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
][
2
][
'roles'
]),
0
)
def
test_user_list_get_with_has_organization_filter
(
self
):
test_uri
=
self
.
users_base_uri
users
=
[]
# create a 7 new users
...
...
@@ -344,6 +403,73 @@ class UsersApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreaterEqual
(
len
(
response
.
data
[
'results'
]),
4
)
def
test_user_list_get_with_organizations_filter
(
self
):
test_uri
=
self
.
users_base_uri
# create a 8 new users
users
=
UserFactory
.
create_batch
(
8
)
# create organization and add 4 users to it
organizations
=
[]
for
i
in
xrange
(
2
):
organization
=
Organization
.
objects
.
create
(
name
=
'Test Organization{}'
.
format
(
i
),
display_name
=
'Test Org Display Name{}'
.
format
(
i
),
)
organizations
.
append
(
organization
)
organizations
[
0
]
.
users
.
add
(
*
users
)
organizations
[
1
]
.
users
.
add
(
*
users
[:
4
])
# fetch users for organization 1
response
=
self
.
do_get
(
'{}?organizations={}'
.
format
(
test_uri
,
organizations
[
1
]
.
id
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
4
)
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'is_active'
])
# fetch users in multiple organization
organization_ids
=
','
.
join
([
str
(
organization
.
id
)
for
organization
in
organizations
])
response
=
self
.
do_get
(
'{}?organizations={}'
.
format
(
test_uri
,
organization_ids
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
8
)
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'is_active'
])
def
test_user_list_get_with_course_enrollment_filter
(
self
):
test_uri
=
self
.
users_base_uri
# create a 8 new users
users
=
UserFactory
.
create_batch
(
8
)
# create course enrollments
for
user
in
users
[:
4
]:
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course
.
id
)
for
user
in
users
:
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course2
.
id
)
# fetch users enrolled in course 1
response
=
self
.
do_get
(
'{}?courses={}'
.
format
(
test_uri
,
self
.
course
.
id
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
4
)
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'is_active'
])
# fetch users enrolled in course 1 and 2
response
=
self
.
do_get
(
'{}?courses={},{}'
.
format
(
test_uri
,
self
.
course
.
id
,
self
.
course2
.
id
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
8
)
self
.
assertIsNotNone
(
response
.
data
[
'results'
][
0
][
'is_active'
])
def
test_user_list_get_with_name_filter
(
self
):
test_uri
=
self
.
users_base_uri
# create a 8 new users
users
=
UserFactory
.
create_batch
(
2
)
users
.
append
(
UserFactory
.
create_batch
(
2
,
first_name
=
"John"
,
last_name
=
"Doe"
))
# fetch users by name
response
=
self
.
do_get
(
'{}?name=John Doe'
.
format
(
test_uri
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
2
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'first_name'
],
'John'
)
self
.
assertEqual
(
response
.
data
[
'results'
][
0
][
'last_name'
],
'Doe'
)
def
test_user_list_post
(
self
):
test_uri
=
self
.
users_base_uri
local_username
=
self
.
test_username
+
str
(
randint
(
11
,
99
))
...
...
lms/djangoapps/api_manager/users/views.py
View file @
214fc702
...
...
@@ -209,14 +209,19 @@ class UsersList(SecureListAPIView):
"""
### The UsersList view allows clients to retrieve/append a list of User entities
- URI: ```/api/users/```
- GET: Provides paginated list of users, it supports email, username, has_organizations and id filters
- GET: Provides paginated list of users, it supports email, username, name, organizations, courses enrolled,
has_organizations and id filters
Possible use cases
GET /api/users?ids=23
GET /api/users?ids=11,12,13&page=2
GET /api/users?organizations=1,2,3
GET /api/users?courses={course_id},{course_id2}
GET /api/users?email={john@example.com}
GET /api/users?name={john doe}
GET /api/users?username={john}
* email: string, filters user set by email address
* username: string, filters user set by username
* name: string, filters user set by full name
GET /api/users?has_organizations={true}
* has_organizations: boolean, filters user set with organization association
GET /api/users?has_organizations={false}
...
...
@@ -269,18 +274,36 @@ class UsersList(SecureListAPIView):
filter_backends
=
(
filters
.
DjangoFilterBackend
,
IdsInFilterBackend
,
HasOrgsFilterBackend
)
filter_fields
=
(
'email'
,
'username'
,
)
def
get_queryset
(
self
):
"""
Optionally filter users by organizations and course enrollments
"""
queryset
=
self
.
queryset
org_ids
=
self
.
request
.
QUERY_PARAMS
.
get
(
'organizations'
,
None
)
if
org_ids
is
not
None
:
org_ids
=
map
(
int
,
org_ids
.
split
(
','
))
queryset
=
queryset
.
filter
(
organizations__id__in
=
org_ids
)
.
distinct
()
course_ids
=
self
.
request
.
QUERY_PARAMS
.
get
(
'courses'
,
None
)
if
course_ids
is
not
None
:
course_ids
=
map
(
CourseKey
.
from_string
,
course_ids
.
split
(
','
))
queryset
=
queryset
.
filter
(
courseenrollment__course_id__in
=
course_ids
)
.
distinct
()
name
=
self
.
request
.
QUERY_PARAMS
.
get
(
'name'
,
None
)
if
name
is
not
None
:
queryset
=
queryset
.
filter
(
profile__name
=
name
)
queryset
=
queryset
.
prefetch_related
(
'organizations'
)
\
.
select_related
(
'courseenrollment_set'
,
'profile'
)
\
.
annotate
(
courses_enrolled
=
Count
(
'courseenrollment'
))
return
queryset
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
"""
GET /api/users?ids=11,12,13.....&page=2
"""
email
=
request
.
QUERY_PARAMS
.
get
(
'email'
,
None
)
username
=
request
.
QUERY_PARAMS
.
get
(
'username'
,
None
)
ids
=
request
.
QUERY_PARAMS
.
get
(
'ids'
,
None
)
has_orgs
=
request
.
QUERY_PARAMS
.
get
(
'has_organizations'
,
None
)
if
email
or
username
or
ids
or
has_orgs
:
return
self
.
list
(
request
,
*
args
,
**
kwargs
)
else
:
return
Response
({
'message'
:
_
(
'Unfiltered request is not allowed.'
)},
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
self
.
list
(
request
,
*
args
,
**
kwargs
)
def
post
(
self
,
request
):
"""
...
...
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