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
03517b26
Commit
03517b26
authored
May 07, 2014
by
Matt Drayer
Committed by
Jonathan Piacenti
Aug 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mattdrayer/api-modulesgroups: API - Added grouping capability for courseware content
parent
b4f9b84d
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1015 additions
and
308 deletions
+1015
-308
lms/djangoapps/api_manager/courses/tests.py
+260
-56
lms/djangoapps/api_manager/courses/urls.py
+5
-3
lms/djangoapps/api_manager/courses/views.py
+202
-79
lms/djangoapps/api_manager/groups/tests.py
+94
-91
lms/djangoapps/api_manager/groups/views.py
+212
-48
lms/djangoapps/api_manager/migrations/0005_coursecontentgrouprelationship.py
+99
-0
lms/djangoapps/api_manager/migrations/0006_contentgroupuniqueconstraint.py
+92
-0
lms/djangoapps/api_manager/models.py
+17
-0
lms/djangoapps/api_manager/users/tests.py
+18
-18
lms/djangoapps/api_manager/users/views.py
+16
-13
No files found.
lms/djangoapps/api_manager/courses/tests.py
View file @
03517b26
...
...
@@ -50,16 +50,30 @@ class CoursesApiTests(TestCase):
display_name
=
"Overview"
)
self
.
module
=
ItemFactory
.
create
(
self
.
course_project
=
ItemFactory
.
create
(
category
=
"chapter"
,
parent_location
=
self
.
course
.
location
,
data
=
self
.
test_data
,
display_name
=
"Group Project"
)
self
.
course_project2
=
ItemFactory
.
create
(
category
=
"chapter"
,
parent_location
=
self
.
course
.
location
,
data
=
self
.
test_data
,
display_name
=
"Group Project2"
)
self
.
course_content
=
ItemFactory
.
create
(
category
=
"videosequence"
,
parent_location
=
self
.
chapter
.
location
,
data
=
self
.
test_data
,
display_name
=
"Video_Sequence"
)
self
.
submodule
=
ItemFactory
.
create
(
self
.
content_child
=
ItemFactory
.
create
(
category
=
"video"
,
parent_location
=
self
.
module
.
location
,
parent_location
=
self
.
course_content
.
location
,
data
=
self
.
test_data
,
display_name
=
"Video_Resources"
)
...
...
@@ -98,10 +112,11 @@ class CoursesApiTests(TestCase):
self
.
test_course_number
=
self
.
course
.
number
self
.
test_course_org
=
self
.
course
.
org
self
.
test_chapter_id
=
self
.
chapter
.
id
self
.
test_module_id
=
self
.
module
.
id
self
.
test_submodule_id
=
self
.
submodule
.
id
self
.
base_modules_uri
=
'/api/courses/'
+
self
.
test_course_id
+
'/modules'
self
.
base_chapters_uri
=
self
.
base_modules_uri
+
'?type=chapter'
self
.
test_course_content_id
=
self
.
course_content
.
id
self
.
test_bogus_content_id
=
"j5y://foo/bar/baz"
self
.
test_content_child_id
=
self
.
content_child
.
id
self
.
base_course_content_uri
=
'/api/courses/'
+
self
.
test_course_id
+
'/content'
self
.
base_chapters_uri
=
self
.
base_course_content_uri
+
'?type=chapter'
self
.
client
=
SecureClient
()
cache
.
clear
()
...
...
@@ -169,7 +184,7 @@ class CoursesApiTests(TestCase):
confirm_uri
=
self
.
test_server_prefix
+
test_uri
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
def
test_courses_detail_get_with_
submodules
(
self
):
def
test_courses_detail_get_with_
child_content
(
self
):
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'?depth=100'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
...
...
@@ -180,7 +195,7 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
response
.
data
[
'org'
],
self
.
test_course_org
)
confirm_uri
=
self
.
test_server_prefix
+
test_uri
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertGreater
(
len
(
response
.
data
[
'
modules
'
]),
0
)
self
.
assertGreater
(
len
(
response
.
data
[
'
content
'
]),
0
)
def
test_courses_detail_get_notfound
(
self
):
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_bogus_course_id
...
...
@@ -195,17 +210,17 @@ class CoursesApiTests(TestCase):
self
.
assertGreater
(
len
(
response
.
data
),
0
)
self
.
assertEqual
(
response
.
data
[
'category'
],
'course'
)
self
.
assertEqual
(
response
.
data
[
'name'
],
self
.
course
.
display_name
)
self
.
assertEqual
(
len
(
response
.
data
[
'
modules'
]),
1
)
self
.
assertEqual
(
len
(
response
.
data
[
'
content'
]),
3
)
chapter
=
response
.
data
[
'
modules
'
][
0
]
chapter
=
response
.
data
[
'
content
'
][
0
]
self
.
assertEqual
(
chapter
[
'category'
],
'chapter'
)
self
.
assertEqual
(
chapter
[
'name'
],
'Overview'
)
self
.
assertEqual
(
len
(
chapter
[
'
modules
'
]),
1
)
self
.
assertEqual
(
len
(
chapter
[
'
children
'
]),
1
)
sequence
=
chapter
[
'
modules
'
][
0
]
sequence
=
chapter
[
'
children
'
][
0
]
self
.
assertEqual
(
sequence
[
'category'
],
'videosequence'
)
self
.
assertEqual
(
sequence
[
'name'
],
'Video_Sequence'
)
self
.
assertNotIn
(
'
modules
'
,
sequence
)
self
.
assertNotIn
(
'
children
'
,
sequence
)
def
test_courses_tree_get_root
(
self
):
# query the course tree to quickly get naviation information
...
...
@@ -215,7 +230,7 @@ class CoursesApiTests(TestCase):
self
.
assertGreater
(
len
(
response
.
data
),
0
)
self
.
assertEqual
(
response
.
data
[
'category'
],
'course'
)
self
.
assertEqual
(
response
.
data
[
'name'
],
self
.
course
.
display_name
)
self
.
assertNotIn
(
'
modules
'
,
response
.
data
)
self
.
assertNotIn
(
'
content
'
,
response
.
data
)
def
test_chapter_list_get
(
self
):
test_uri
=
self
.
base_chapters_uri
...
...
@@ -227,81 +242,86 @@ class CoursesApiTests(TestCase):
if
matched_chapter
is
False
and
chapter
[
'id'
]
==
self
.
test_chapter_id
:
self
.
assertIsNotNone
(
chapter
[
'uri'
])
self
.
assertGreater
(
len
(
chapter
[
'uri'
]),
0
)
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_
modules
_uri
+
'/'
+
chapter
[
'id'
]
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_
course_content
_uri
+
'/'
+
chapter
[
'id'
]
self
.
assertEqual
(
chapter
[
'uri'
],
confirm_uri
)
matched_chapter
=
True
self
.
assertTrue
(
matched_chapter
)
def
test_chapter_detail_get
(
self
):
test_uri
=
self
.
base_
modules
_uri
+
'/'
+
self
.
test_chapter_id
test_uri
=
self
.
base_
course_content
_uri
+
'/'
+
self
.
test_chapter_id
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
[
'id'
]),
0
)
self
.
assertEqual
(
response
.
data
[
'id'
],
self
.
test_chapter_id
)
confirm_uri
=
self
.
test_server_prefix
+
test_uri
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertGreater
(
len
(
response
.
data
[
'
modules
'
]),
0
)
self
.
assertGreater
(
len
(
response
.
data
[
'
children
'
]),
0
)
def
test_
modules
_list_get
(
self
):
test_uri
=
self
.
base_modules_uri
+
'/'
+
self
.
test_module_id
def
test_
course_content
_list_get
(
self
):
test_uri
=
'{}/{}/children'
.
format
(
self
.
base_course_content_uri
,
self
.
test_course_content_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
matched_submodule
=
False
for
submodule
in
response
.
data
[
'modules'
]:
if
matched_submodule
is
False
and
submodule
[
'id'
]
==
self
.
test_submodule_id
:
self
.
assertIsNotNone
(
submodule
[
'uri'
])
self
.
assertGreater
(
len
(
submodule
[
'uri'
]),
0
)
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_modules_uri
+
'/'
+
submodule
[
'id'
]
self
.
assertEqual
(
submodule
[
'uri'
],
confirm_uri
)
matched_submodule
=
True
self
.
assertTrue
(
matched_submodule
)
matched_child
=
False
for
child
in
response
.
data
:
if
matched_child
is
False
and
child
[
'id'
]
==
self
.
test_content_child_id
:
self
.
assertIsNotNone
(
child
[
'uri'
])
self
.
assertGreater
(
len
(
child
[
'uri'
]),
0
)
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_course_content_uri
+
'/'
+
child
[
'id'
]
self
.
assertEqual
(
child
[
'uri'
],
confirm_uri
)
matched_child
=
True
self
.
assertTrue
(
matched_child
)
def
test_course_content_list_get_invalid_content
(
self
):
test_uri
=
'{}/{}/children'
.
format
(
self
.
base_course_content_uri
,
self
.
test_bogus_content_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_
modules
_detail_get
(
self
):
test_uri
=
self
.
base_
modules_uri
+
'/'
+
self
.
test_module
_id
def
test_
course_content
_detail_get
(
self
):
test_uri
=
self
.
base_
course_content_uri
+
'/'
+
self
.
test_course_content
_id
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
self
.
assertEqual
(
response
.
data
[
'id'
],
self
.
test_
module
_id
)
self
.
assertEqual
(
response
.
data
[
'id'
],
self
.
test_
course_content
_id
)
confirm_uri
=
self
.
test_server_prefix
+
test_uri
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertGreater
(
len
(
response
.
data
[
'
modules
'
]),
0
)
self
.
assertGreater
(
len
(
response
.
data
[
'
children
'
]),
0
)
def
test_
modules
_detail_get_course
(
self
):
test_uri
=
self
.
base_
modules
_uri
+
'/'
+
self
.
test_course_id
def
test_
course_content
_detail_get_course
(
self
):
test_uri
=
self
.
base_
course_content
_uri
+
'/'
+
self
.
test_course_id
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
self
.
assertEqual
(
response
.
data
[
'id'
],
self
.
test_course_id
)
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertGreater
(
len
(
response
.
data
[
'
modules
'
]),
0
)
self
.
assertGreater
(
len
(
response
.
data
[
'
content
'
]),
0
)
def
test_
modules
_detail_get_notfound
(
self
):
test_uri
=
self
.
base_
modules_uri
+
'/'
+
'2p38fp2hjfp9283'
def
test_
course_content
_detail_get_notfound
(
self
):
test_uri
=
self
.
base_
course_content_uri
+
'/'
+
self
.
test_bogus_content_id
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_
modules_list_get_filtered_submodules_for_module
(
self
):
test_uri
=
self
.
base_
modules_uri
+
'/'
+
self
.
test_module_id
+
'/submodules
?type=video'
def
test_
course_content_list_get_filtered_children_for_child
(
self
):
test_uri
=
self
.
base_
course_content_uri
+
'/'
+
self
.
test_course_content_id
+
'/children
?type=video'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
matched_
submodule
=
False
for
submodule
in
response
.
data
:
if
matched_
submodule
is
False
and
submodule
[
'id'
]
==
self
.
test_submodule
_id
:
confirm_uri
=
self
.
test_server_prefix
+
self
.
base_modules_uri
+
'/'
+
submodule
[
'id'
]
self
.
assertEqual
(
submodule
[
'uri'
],
confirm_uri
)
matched_
submodule
=
True
self
.
assertTrue
(
matched_
submodule
)
matched_
child
=
False
for
child
in
response
.
data
:
if
matched_
child
is
False
and
child
[
'id'
]
==
self
.
test_content_child
_id
:
confirm_uri
=
'{}{}/{}'
.
format
(
self
.
test_server_prefix
,
self
.
base_course_content_uri
,
child
[
'id'
])
self
.
assertEqual
(
child
[
'uri'
],
confirm_uri
)
matched_
child
=
True
self
.
assertTrue
(
matched_
child
)
def
test_
modules
_list_get_notfound
(
self
):
test_uri
=
self
.
base_modules_uri
+
'/2p38fp2hjfp9283/submodules?type=video'
def
test_
course_content
_list_get_notfound
(
self
):
test_uri
=
'{}{}/children?type=video'
.
format
(
self
.
base_course_content_uri
,
self
.
test_bogus_content_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_groups_list_post
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
...
...
@@ -320,7 +340,7 @@ class CoursesApiTests(TestCase):
course_fail_uri
=
'{}/{}/groups'
.
format
(
self
.
base_courses_uri
,
'/ed/Open_DemoX/edx_demo_course'
)
for
i
in
xrange
(
2
):
data_dict
=
{
'name'
:
'Alpha Group {}'
.
format
(
i
),
'
group_
type'
:
'Programming'
,
'name'
:
'Alpha Group {}'
.
format
(
i
),
'type'
:
'Programming'
,
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data_dict
)
group_id
=
response
.
data
[
'id'
]
...
...
@@ -329,7 +349,7 @@ class CoursesApiTests(TestCase):
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data_dict
[
'
group_
type'
]
=
'Calculus'
data_dict
[
'type'
]
=
'Calculus'
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data_dict
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
...
...
@@ -360,7 +380,7 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_groups_list_post_duplicate
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
)
...
...
@@ -377,7 +397,7 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_courses_groups_detail_get
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
)
...
...
@@ -402,7 +422,7 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
response
.
data
[
'group_id'
],
group_id
)
def
test_courses_groups_detail_delete
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
)
data
=
{
'group_id'
:
response
.
data
[
'id'
]}
...
...
@@ -426,7 +446,7 @@ class CoursesApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
204
)
def
test_courses_groups_detail_get_undefined
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups/{}'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
group_id
)
...
...
@@ -792,3 +812,187 @@ class CoursesApiTests(TestCase):
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'/users/213432'
response
=
self
.
do_delete
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
204
)
def
test_course_content_groups_list_post
(
self
):
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
)
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
confirm_uri
=
self
.
test_server_prefix
+
test_uri
+
'/'
+
str
(
group_id
)
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertEqual
(
response
.
data
[
'course_id'
],
str
(
self
.
test_course_id
))
self
.
assertEqual
(
response
.
data
[
'content_id'
],
str
(
self
.
course_project
.
id
))
self
.
assertEqual
(
response
.
data
[
'group_id'
],
str
(
group_id
))
def
test_course_content_groups_list_post_duplicate
(
self
):
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
)
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
409
)
def
test_course_content_groups_list_post_invalid_course
(
self
):
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_bogus_course_id
,
self
.
course_project
.
id
)
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_post_invalid_content
(
self
):
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
test_bogus_content_id
)
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_post_invalid_group
(
self
):
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
course_project
.
id
)
data
=
{
'group_id'
:
'12398721'
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_post_missing_group
(
self
):
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
course_project
.
id
)
response
=
self
.
do_post
(
test_uri
,
{})
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_get
(
self
):
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
)
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
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
),
2
)
test_uri
=
test_uri
+
'?type=project'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
def
test_course_content_groups_list_get_invalid_course
(
self
):
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_bogus_course_id
,
self
.
course_project
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_get_invalid_content
(
self
):
test_uri
=
'{}/{}/content/{}/groups'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
test_bogus_content_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_list_get_filter_by_type
(
self
):
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
)
data
=
{
'group_id'
:
group_id
}
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
)
self
.
assertEqual
(
response
.
data
[
0
][
'group_id'
],
2
)
def
test_course_content_groups_detail_get
(
self
):
test_uri
=
'{}/{}/groups'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
)
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'project'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
response
=
self
.
do_get
(
response
.
data
[
'uri'
])
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'group_id'
],
str
(
group_id
))
def
test_course_content_groups_detail_get_invalid_relationship
(
self
):
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'{}/{}/groups/{}'
.
format
(
self
.
base_course_content_uri
,
self
.
course_project
.
id
,
group_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_detail_get_invalid_course
(
self
):
test_uri
=
'{}/{}/content/{}/groups/123456'
.
format
(
self
.
base_courses_uri
,
self
.
test_bogus_course_id
,
self
.
course_project
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_detail_get_invalid_content
(
self
):
test_uri
=
'{}/{}/content/{}/groups/123456'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
test_bogus_content_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_content_groups_detail_get_invalid_group
(
self
):
test_uri
=
'{}/{}/content/{}/groups/123456'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
self
.
course_project
.
id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
lms/djangoapps/api_manager/courses/urls.py
View file @
03517b26
...
...
@@ -12,9 +12,11 @@ urlpatterns = patterns(
''
,
url
(
r'/*$^'
,
courses_views
.
CoursesList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)$'
,
courses_views
.
CoursesDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/modules/(?P<module_id>[a-zA-Z0-9/_:]+)/submodules/*$'
,
courses_views
.
ModulesList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/modules/(?P<module_id>[a-zA-Z0-9/_:]+)$'
,
courses_views
.
ModulesDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/modules/*$'
,
courses_views
.
ModulesList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)/groups/(?P<group_id>[0-9]+)$'
,
courses_views
.
CourseContentGroupsDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)/groups/*$'
,
courses_views
.
CourseContentGroupsList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)/children/*$'
,
courses_views
.
CourseContentList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)$'
,
courses_views
.
CourseContentDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/content/*$'
,
courses_views
.
CourseContentList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/groups/(?P<group_id>[0-9]+)$'
,
courses_views
.
CoursesGroupsDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/groups/*$'
,
courses_views
.
CoursesGroupsList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/overview$'
,
courses_views
.
CoursesOverview
.
as_view
()),
...
...
lms/djangoapps/api_manager/courses/views.py
View file @
03517b26
...
...
@@ -14,7 +14,7 @@ from rest_framework.response import Response
from
rest_framework.views
import
APIView
from
api_manager.permissions
import
ApiKeyHeaderPermission
from
api_manager.models
import
CourseGroupRelationship
,
GroupProfile
from
api_manager.models
import
CourseGroupRelationship
,
CourseContentGroupRelationship
,
GroupProfile
from
courseware
import
module_render
from
courseware.courses
import
get_course
,
get_course_about_section
,
get_course_info_section
from
courseware.model_data
import
FieldDataCache
...
...
@@ -42,90 +42,101 @@ def _generate_base_uri(request):
return
resource_uri
def
_get_
module_submodules
(
module
,
submodule
_type
=
None
):
def
_get_
content_children
(
content
,
content
_type
=
None
):
"""
Parses the provided
module looking for child modules
Matches on
submodule
type (category) when specified
Parses the provided
content object looking for children
Matches on
child
type (category) when specified
"""
submodules
=
[]
if
hasattr
(
module
,
'children'
):
child_
modules
=
module
.
get_children
()
for
child
_module
in
child_modules
:
if
submodule
_type
:
if
getattr
(
child
_module
,
'category'
)
==
submodule
_type
:
submodules
.
append
(
child_module
)
children
=
[]
if
hasattr
(
content
,
'children'
):
child_
content
=
content
.
get_children
()
for
child
in
child_content
:
if
content
_type
:
if
getattr
(
child
,
'category'
)
==
content
_type
:
children
.
append
(
child
)
else
:
submodules
.
append
(
child_module
)
return
submodules
children
.
append
(
child
)
return
children
def
_serialize_
module
(
request
,
course_id
,
module
):
def
_serialize_
content
(
request
,
course_id
,
content
):
"""
Loads the specified
module data
into the response dict
Loads the specified
content object
into the response dict
This should probably evolve to use DRF serializers
"""
data
=
{}
if
getattr
(
module
,
'id'
)
==
course_id
:
module_id
=
module
.
id
if
getattr
(
content
,
'id'
)
==
course_id
:
content_id
=
content
.
id
else
:
module_id
=
module
.
location
.
url
()
data
[
'id'
]
=
module
_id
content_id
=
content
.
location
.
url
()
data
[
'id'
]
=
content
_id
if
hasattr
(
module
,
'display_name'
):
data
[
'name'
]
=
module
.
display_name
if
hasattr
(
content
,
'display_name'
):
data
[
'name'
]
=
content
.
display_name
data
[
'category'
]
=
module
.
location
.
category
data
[
'category'
]
=
content
.
location
.
category
protocol
=
'http'
if
request
.
is_secure
():
protocol
=
protocol
+
's'
module
_uri
=
'{}://{}/api/courses/{}'
.
format
(
content
_uri
=
'{}://{}/api/courses/{}'
.
format
(
protocol
,
request
.
get_host
(),
course_id
.
encode
(
'utf-8'
)
)
# Some things we do only if the
module
is a course
if
(
course_id
==
module
_id
):
data
[
'number'
]
=
module
.
location
.
course
data
[
'org'
]
=
module
.
location
.
org
# Some things we do only if the
content object
is a course
if
(
course_id
==
content
_id
):
data
[
'number'
]
=
content
.
location
.
course
data
[
'org'
]
=
content
.
location
.
org
# Other things we do only if the
module
is not a course
# Other things we do only if the
content object
is not a course
else
:
module_uri
=
'{}/modules/{}'
.
format
(
module_uri
,
module
_id
)
data
[
'uri'
]
=
module
_uri
content_uri
=
'{}/content/{}'
.
format
(
content_uri
,
content
_id
)
data
[
'uri'
]
=
content
_uri
return
data
def
_serialize_
module_submodules
(
request
,
course_id
,
submodules
):
def
_serialize_
content_children
(
request
,
course_id
,
children
):
"""
Loads the specified
module submodule
data into the response dict
Loads the specified
content child
data into the response dict
This should probably evolve to use DRF serializers
"""
data
=
[]
if
submodules
:
for
submodule
in
submodules
:
submodule_data
=
_serialize_module
(
if
children
:
for
child
in
children
:
child_data
=
_serialize_content
(
request
,
course_id
,
submodule
child
)
data
.
append
(
submodule
_data
)
data
.
append
(
child
_data
)
return
data
def
_serialize_module_with_children
(
request
,
course_descriptor
,
descriptor
,
depth
):
data
=
_serialize_module
(
def
_serialize_content_groups
(
request
,
course_id
,
content_id
,
groups
):
"""
Loads the specified content group data into the response dict
This should probably evolve to use DRF serializers
"""
return
[
{
'course_id'
:
course_id
,
'content_id'
:
content_id
,
'group_id'
:
group
.
group_id
}
for
group
in
groups
]
def
_serialize_content_with_children
(
request
,
course_descriptor
,
descriptor
,
depth
):
data
=
_serialize_content
(
request
,
course_descriptor
.
id
,
descriptor
)
if
depth
>
0
:
data
[
'
modules
'
]
=
[]
data
[
'
children
'
]
=
[]
for
child
in
descriptor
.
get_children
():
data
[
'
modules'
]
.
append
(
_serialize_module
_with_children
(
data
[
'
children'
]
.
append
(
_serialize_content
_with_children
(
request
,
course_descriptor
,
child
,
...
...
@@ -237,32 +248,32 @@ def _parse_updates_html(html):
return
result
class
Modules
List
(
APIView
):
class
CourseContent
List
(
APIView
):
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
course_id
,
module
_id
=
None
):
def
get
(
self
,
request
,
course_id
,
content
_id
=
None
):
"""
GET retrieves the list of
submodules for a given module
We don't know where in the
module
hierarchy we are -- could even be the top
GET retrieves the list of
children for a given content object
We don't know where in the
content
hierarchy we are -- could even be the top
"""
if
module
_id
is
None
:
module
_id
=
course_id
if
content
_id
is
None
:
content
_id
=
course_id
response_data
=
[]
submodule
_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
,
None
)
content
_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
,
None
)
store
=
modulestore
()
if
course_id
!=
module
_id
:
if
course_id
!=
content
_id
:
try
:
module
=
store
.
get_instance
(
course_id
,
Location
(
module
_id
))
content
=
store
.
get_instance
(
course_id
,
Location
(
content
_id
))
except
InvalidLocationError
:
module
=
None
content
=
None
else
:
module
=
get_course
(
course_id
)
if
module
:
submodules
=
_get_module_submodules
(
module
,
submodule
_type
)
response_data
=
_serialize_
module_submodules
(
content
=
get_course
(
course_id
)
if
content
:
children
=
_get_content_children
(
content
,
content
_type
)
response_data
=
_serialize_
content_children
(
request
,
course_id
,
submodules
children
)
status_code
=
status
.
HTTP_200_OK
else
:
...
...
@@ -270,34 +281,36 @@ class ModulesList(APIView):
return
Response
(
response_data
,
status
=
status_code
)
class
Modules
Detail
(
APIView
):
class
CourseContent
Detail
(
APIView
):
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
course_id
,
module
_id
):
def
get
(
self
,
request
,
course_id
,
content
_id
):
"""
GET retrieves an existing
module
from the system
GET retrieves an existing
content object
from the system
"""
store
=
modulestore
()
response_data
=
{}
submodule_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
,
None
)
if
course_id
!=
module_id
:
content_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
,
None
)
if
course_id
!=
content_id
:
element_name
=
'children'
try
:
module
=
store
.
get_instance
(
course_id
,
Location
(
module
_id
))
content
=
store
.
get_instance
(
course_id
,
Location
(
content
_id
))
except
InvalidLocationError
:
module
=
None
content
=
None
else
:
module
=
get_course
(
course_id
)
if
module
:
response_data
=
_serialize_module
(
element_name
=
'content'
content
=
get_course
(
course_id
)
if
content
:
response_data
=
_serialize_content
(
request
,
course_id
,
module
content
)
submodules
=
_get_module_submodules
(
module
,
submodule
_type
)
response_data
[
'modules'
]
=
_serialize_module_submodules
(
children
=
_get_content_children
(
content
,
content
_type
)
response_data
[
element_name
]
=
_serialize_content_children
(
request
,
course_id
,
submodules
children
)
status_code
=
status
.
HTTP_200_OK
else
:
...
...
@@ -316,7 +329,7 @@ class CoursesList(APIView):
store
=
modulestore
()
course_descriptors
=
store
.
get_courses
()
for
course_descriptor
in
course_descriptors
:
course_data
=
_serialize_
module
(
course_data
=
_serialize_
content
(
request
,
course_descriptor
.
id
,
course_descriptor
...
...
@@ -330,8 +343,8 @@ class CoursesDetail(APIView):
def
get
(
self
,
request
,
course_id
):
"""
GET retrieves an existing course from the system and returns
summary information about the submodules
to the specified depth
GET retrieves an existing course from the system and returns
summary information about its content children
to the specified depth
"""
depth
=
request
.
QUERY_PARAMS
.
get
(
'depth'
,
0
)
depth_int
=
int
(
depth
)
...
...
@@ -343,14 +356,16 @@ class CoursesDetail(APIView):
course_descriptor
=
None
if
course_descriptor
:
if
depth_int
>
0
:
response_data
=
_serialize_
module
_with_children
(
response_data
=
_serialize_
content
_with_children
(
request
,
course_descriptor
,
course_descriptor
,
# Primer for recursive function
depth_int
)
response_data
[
'content'
]
=
response_data
[
'children'
]
response_data
.
pop
(
'children'
)
else
:
response_data
=
_serialize_
module
(
response_data
=
_serialize_
content
(
request
,
course_descriptor
.
id
,
course_descriptor
...
...
@@ -472,7 +487,7 @@ class CoursesOverview(APIView):
def
get
(
self
,
request
,
course_id
):
"""
GET retrieves the course overview
module
, which - in MongoDB - is stored with the following
GET retrieves the course overview
content
, which - in MongoDB - is stored with the following
naming convention {"_id.org":"i4x", "_id.course":<course_num>, "_id.category":"about", "_id.name":"overview"}
"""
response_data
=
OrderedDict
()
...
...
@@ -499,7 +514,7 @@ class CoursesUpdates(APIView):
def
get
(
self
,
request
,
course_id
):
"""
GET retrieves the course overview
module
, which - in MongoDB - is stored with the following
GET retrieves the course overview
content
, which - in MongoDB - is stored with the following
naming convention {"_id.org":"i4x", "_id.course":<course_num>, "_id.category":"course_info", "_id.name":"updates"}
"""
response_data
=
OrderedDict
()
...
...
@@ -685,13 +700,13 @@ class CoursesUsersDetail(APIView):
user
=
None
if
user
and
CourseEnrollment
.
is_enrolled
(
user
,
course_id
):
field_data_cache
=
FieldDataCache
([
course_descriptor
],
course_id
,
user
)
course_
module
=
module_render
.
get_module
(
course_
content
=
module_render
.
get_module
(
user
,
request
,
course_descriptor
.
location
,
field_data_cache
,
course_id
)
response_data
[
'position'
]
=
course_
module
.
position
response_data
[
'position'
]
=
course_
content
.
position
response_status
=
status
.
HTTP_200_OK
else
:
response_status
=
status
.
HTTP_404_NOT_FOUND
...
...
@@ -717,3 +732,111 @@ class CoursesUsersDetail(APIView):
base_uri
=
_generate_base_uri
(
request
)
response_data
[
'uri'
]
=
base_uri
return
Response
(
response_data
,
status
=
status
.
HTTP_204_NO_CONTENT
)
class
CourseContentGroupsList
(
APIView
):
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
,
course_id
,
content_id
):
try
:
course_descriptor
=
get_course
(
course_id
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
store
=
modulestore
()
try
:
existing_content
=
store
.
get_instance
(
course_id
,
Location
(
content_id
))
except
InvalidLocationError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
group_id
=
request
.
DATA
.
get
(
'group_id'
)
if
group_id
is
None
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
try
:
existing_group
=
GroupProfile
.
objects
.
get
(
group_id
=
group_id
)
except
ObjectDoesNotExist
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
try
:
existing_relationship
=
CourseContentGroupRelationship
.
objects
.
get
(
course_id
=
course_id
,
content_id
=
content_id
,
group
=
existing_group
)
except
ObjectDoesNotExist
:
existing_relationship
=
None
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
response_data
[
'uri'
]
=
'{}/{}'
.
format
(
base_uri
,
existing_group
.
group_id
)
if
existing_relationship
:
response_data
[
'message'
]
=
"Relationship already exists."
return
Response
(
response_data
,
status
=
status
.
HTTP_409_CONFLICT
)
CourseContentGroupRelationship
.
objects
.
create
(
course_id
=
course_id
,
content_id
=
content_id
,
group
=
existing_group
)
response_data
[
'course_id'
]
=
course_descriptor
.
id
response_data
[
'content_id'
]
=
existing_content
.
id
response_data
[
'group_id'
]
=
str
(
existing_group
.
group_id
)
return
Response
(
response_data
,
status
=
status
.
HTTP_201_CREATED
)
def
get
(
self
,
request
,
course_id
,
content_id
):
"""
GET retrieves the list of groups for a given content object.
The 'type' query parameter is available for filtering by group_type
"""
response_data
=
[]
group_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
)
try
:
course_descriptor
=
get_course
(
course_id
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
try
:
store
=
modulestore
()
existing_content
=
store
.
get_instance
(
course_id
,
Location
(
content_id
))
except
InvalidLocationError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
groups
=
CourseContentGroupRelationship
.
objects
.
filter
(
course_id
=
course_id
,
content_id
=
content_id
)
if
group_type
:
groups
=
groups
.
filter
(
group__group_type
=
group_type
)
response_data
=
_serialize_content_groups
(
request
,
course_id
,
content_id
,
groups
)
return
Response
(
response_data
,
status
=
status
.
HTTP_200_OK
)
class
CourseContentGroupsDetail
(
APIView
):
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
course_id
,
content_id
,
group_id
):
"""
GET retrieves an existing content-group relationship from the system
"""
try
:
course_descriptor
=
get_course
(
course_id
)
except
ValueError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
try
:
store
=
modulestore
()
existing_content
=
store
.
get_instance
(
course_id
,
Location
(
content_id
))
except
InvalidLocationError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
try
:
groups
=
CourseContentGroupRelationship
.
objects
.
get
(
course_id
=
course_id
,
content_id
=
content_id
,
group
=
group_id
)
except
ObjectDoesNotExist
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
response_data
=
{
'course_id'
:
course_id
,
'content_id'
:
content_id
,
'group_id'
:
group_id
,
}
return
Response
(
response_data
,
status
=
status
.
HTTP_200_OK
)
lms/djangoapps/api_manager/groups/tests.py
View file @
03517b26
...
...
@@ -74,7 +74,7 @@ class GroupsApiTests(ModuleStoreTestCase):
return
response
def
test_group_list_post
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
self
.
assertGreater
(
response
.
data
[
'id'
],
0
)
...
...
@@ -83,12 +83,12 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertGreater
(
len
(
response
.
data
[
'name'
]),
0
)
def
test_group_list_get_with_profile
(
self
):
group_type
=
'series'
profile_data
=
{
'display_name'
:
'My first series'
}
data
=
{
'name'
:
self
.
test_group_name
,
'group_type'
:
'series'
,
'data'
:
{
'display_name'
:
'My first series'
}
'type'
:
group_type
,
'data'
:
profile_data
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertGreater
(
response
.
data
[
'id'
],
0
)
...
...
@@ -97,45 +97,41 @@ class GroupsApiTests(ModuleStoreTestCase):
# query for list of groups, but don't put the type filter
test_uri
=
self
.
base_groups_uri
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
2
00
)
self
.
assertEqual
(
response
.
status_code
,
4
00
)
# try again with filter
test_uri
=
self
.
base_groups_uri
+
'?type=series'
test_uri
=
'{}?type={}'
.
format
(
self
.
base_groups_uri
,
group_type
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
self
.
assertEqual
(
response
.
data
[
0
][
'
group_
id'
],
group_id
)
self
.
assertEqual
(
response
.
data
[
0
][
'
group_
type'
],
'series'
)
self
.
assertEqual
(
response
.
data
[
0
][
'id'
],
group_id
)
self
.
assertEqual
(
response
.
data
[
0
][
'type'
],
'series'
)
self
.
assertEqual
(
response
.
data
[
0
][
'name'
],
self
.
test_group_name
)
self
.
assertEqual
(
response
.
data
[
0
][
'data'
][
'display_name'
],
'My first series'
)
response_profile_data
=
response
.
data
[
0
][
'data'
]
self
.
assertEqual
(
response_profile_data
[
'display_name'
],
'My first series'
)
# query the group detail
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
group_id
)
test_uri
=
'{}/{}'
.
format
(
self
.
base_groups_uri
,
str
(
group_id
)
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'id'
],
group_id
)
confirm_uri
=
self
.
test_server_prefix
+
test_uri
self
.
assertEqual
(
response
.
data
[
'uri'
],
confirm_uri
)
self
.
assertEqual
(
response
.
data
[
'name'
],
self
.
test_group_name
)
self
.
assertEqual
(
response
.
data
[
'group_type'
],
'series'
)
self
.
assertEqual
(
response
.
data
[
'data'
][
'display_name'
],
'My first series'
)
self
.
assertEqual
(
response
.
data
[
'type'
],
'series'
)
response_profile_data
=
response
.
data
[
'data'
]
self
.
assertEqual
(
response_profile_data
[
'display_name'
],
'My first series'
)
# update the profile
# first with missing data
response
=
self
.
do_post
(
test_uri
,
{})
self
.
assertEqual
(
response
.
status_code
,
400
)
profile_data
=
{
'display_name'
:
'My updated series'
}
data
=
{
'name'
:
self
.
test_group_name
,
'group_type'
:
'seriesX'
,
'data'
:
{
'display_name'
:
'My updated series'
}
'type'
:
'seriesX'
,
'data'
:
profile_data
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
20
1
)
self
.
assertEqual
(
response
.
status_code
,
20
0
)
# requery the filter
test_uri
=
self
.
base_groups_uri
+
'?type=series'
...
...
@@ -147,35 +143,42 @@ class GroupsApiTests(ModuleStoreTestCase):
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
self
.
assertEqual
(
response
.
data
[
0
][
'
group_
id'
],
group_id
)
self
.
assertEqual
(
response
.
data
[
0
][
'
group_
type'
],
'seriesX'
)
self
.
assertEqual
(
response
.
data
[
0
][
'id'
],
group_id
)
self
.
assertEqual
(
response
.
data
[
0
][
'type'
],
'seriesX'
)
self
.
assertEqual
(
response
.
data
[
0
][
'name'
],
self
.
test_group_name
)
self
.
assertEqual
(
response
.
data
[
0
][
'data'
][
'display_name'
],
'My updated series'
)
response_profile_data
=
response
.
data
[
0
][
'data'
]
self
.
assertEqual
(
response_profile_data
[
'display_name'
],
'My updated series'
)
def
test_group_list_post_invalid_name
(
self
):
data
=
{
'name'
:
''
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_group_list_post_missing_type
(
self
):
data
=
{
'name'
:
''
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_group_list_get_uses_base_group_name
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
group_id
=
response
.
data
[
'id'
]
profile
=
GroupProfile
.
objects
.
get
(
group_id
=
group_id
)
profile
.
name
=
''
profile
.
save
()
response
=
self
.
do_get
(
self
.
base_groups_uri
)
test_uri
=
'{}?type=test'
.
format
(
self
.
base_groups_uri
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
0
][
'name'
],
'{:04d}: {}'
.
format
(
group_id
,
self
.
test_group_name
))
profile
.
name
=
None
profile
.
save
()
response
=
self
.
do_get
(
self
.
base_groups
_uri
)
response
=
self
.
do_get
(
test
_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
0
][
'name'
],
'{:04d}: {}'
.
format
(
group_id
,
self
.
test_group_name
))
def
test_group_detail_get
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
self
.
assertGreater
(
response
.
data
[
'id'
],
0
)
...
...
@@ -189,7 +192,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'name'
],
self
.
test_group_name
)
def
test_group_detail_get_uses_base_group_name
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
self
.
assertGreater
(
response
.
data
[
'id'
],
0
)
...
...
@@ -206,7 +209,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'name'
],
'{:04d}: {}'
.
format
(
group_id
,
self
.
test_group_name
))
def
test_group_detail_get_with_missing_profile
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
self
.
assertGreater
(
response
.
data
[
'id'
],
0
)
...
...
@@ -226,7 +229,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_detail_post
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
response
.
data
[
'uri'
]
...
...
@@ -234,13 +237,13 @@ class GroupsApiTests(ModuleStoreTestCase):
group_type
=
'seriesX'
data
=
{
'name'
:
self
.
test_group_name
,
'
group_
type'
:
group_type
,
'type'
:
group_type
,
'data'
:
{
'display_name'
:
'My updated series'
}
}
response
=
self
.
do_post
(
test_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
20
1
)
self
.
assertEqual
(
response
.
status_code
,
20
0
)
self
.
assertEqual
(
response
.
data
[
'id'
],
group_id
)
self
.
assertEqual
(
response
.
data
[
'name'
],
self
.
test_group_name
)
self
.
assertEqual
(
response
.
data
[
'uri'
],
test_uri
)
...
...
@@ -250,7 +253,7 @@ class GroupsApiTests(ModuleStoreTestCase):
group_type
=
'seriesX'
data
=
{
'name'
:
self
.
test_group_name
,
'
group_
type'
:
group_type
,
'type'
:
group_type
,
'data'
:
{
'display_name'
:
'My updated series'
}
...
...
@@ -269,7 +272,7 @@ class GroupsApiTests(ModuleStoreTestCase):
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
group_id
)
...
...
@@ -288,7 +291,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data
=
{
'email'
:
self
.
test_email
,
'username'
:
local_username
,
'password'
:
self
.
test_password
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
response
.
data
[
'id'
])
response
=
self
.
do_get
(
test_uri
)
...
...
@@ -307,7 +310,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_users_list_post_invalid_user
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
test_uri
=
'{}/{}/users'
.
format
(
self
.
base_groups_uri
,
str
(
response
.
data
[
'id'
]))
data
=
{
'user_id'
:
"98723896"
}
...
...
@@ -325,7 +328,7 @@ class GroupsApiTests(ModuleStoreTestCase):
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
group_id
)
...
...
@@ -354,7 +357,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data
=
{
'email'
:
self
.
test_email
,
'username'
:
local_username
,
'password'
:
self
.
test_password
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
response
.
data
[
'id'
])
...
...
@@ -376,7 +379,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data
=
{
'email'
:
self
.
test_email
,
'username'
:
local_username
,
'password'
:
self
.
test_password
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
response
.
data
[
'id'
])
response
=
self
.
do_get
(
test_uri
)
...
...
@@ -397,7 +400,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
204
)
def
test_group_users_detail_delete_invalid_user
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
response
.
data
[
'id'
])
test_uri
=
test_uri
+
'/users/123124'
...
...
@@ -409,7 +412,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data
=
{
'email'
:
self
.
test_email
,
'username'
:
local_username
,
'password'
:
self
.
test_password
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
user_id
=
response
.
data
[
'id'
]
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
self
.
base_groups_uri
+
'/'
+
str
(
group_id
)
+
'/users/'
+
str
(
user_id
)
...
...
@@ -417,13 +420,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_groups_list_post_hierarchical
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -439,13 +442,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'relationship_type'
],
relationship_type
)
def
test_group_groups_list_post_linked
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -461,13 +464,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'relationship_type'
],
relationship_type
)
def
test_group_groups_list_post_linked_duplicate
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -488,13 +491,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_groups_list_post_invalid_relationship_type
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -505,13 +508,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
406
)
def
test_group_groups_list_get
(
self
):
data
=
{
'name'
:
'Bravo Group'
}
data
=
{
'name'
:
'Bravo Group'
,
'type'
:
'test'
}
bravo_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
bravo_response
.
status_code
,
201
)
bravo_group_id
=
bravo_response
.
data
[
'id'
]
bravo_groups_uri
=
bravo_response
.
data
[
'uri'
]
+
'/groups'
data
=
{
'name'
:
'Charlie Group'
}
data
=
{
'name'
:
'Charlie Group'
,
'type'
:
'test'
}
charlie_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
charlie_response
.
status_code
,
201
)
charlie_group_id
=
charlie_response
.
data
[
'id'
]
...
...
@@ -520,7 +523,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response
=
self
.
do_post
(
bravo_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Foxtrot Group'
}
data
=
{
'name'
:
'Foxtrot Group'
,
'type'
:
'test'
}
foxtrot_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
foxtrot_response
.
status_code
,
201
)
foxtrot_group_id
=
foxtrot_response
.
data
[
'id'
]
...
...
@@ -529,7 +532,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response
=
self
.
do_post
(
bravo_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Tango Group'
}
data
=
{
'name'
:
'Tango Group'
,
'type'
:
'test'
}
tango_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
tango_response
.
status_code
,
201
)
tango_group_id
=
tango_response
.
data
[
'id'
]
...
...
@@ -554,13 +557,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
relationship_count
,
len
(
group_idlist
))
def
test_group_groups_list_get_with_profile_type
(
self
):
data
=
{
'name'
:
'Bravo Group'
}
data
=
{
'name'
:
'Bravo Group'
,
'type'
:
'test'
}
bravo_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
bravo_response
.
status_code
,
201
)
bravo_group_id
=
bravo_response
.
data
[
'id'
]
bravo_groups_uri
=
bravo_response
.
data
[
'uri'
]
+
'/groups?type=test_group'
data
=
{
'name'
:
'Charlie Group'
,
'
group_
type'
:
'test_group'
}
data
=
{
'name'
:
'Charlie Group'
,
'type'
:
'test_group'
}
charlie_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
charlie_response
.
status_code
,
201
)
charlie_group_id
=
charlie_response
.
data
[
'id'
]
...
...
@@ -569,7 +572,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response
=
self
.
do_post
(
bravo_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Foxtrot Group'
,
'
group_
type'
:
'test_group'
}
data
=
{
'name'
:
'Foxtrot Group'
,
'type'
:
'test_group'
}
foxtrot_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
foxtrot_response
.
status_code
,
201
)
foxtrot_group_id
=
foxtrot_response
.
data
[
'id'
]
...
...
@@ -578,7 +581,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response
=
self
.
do_post
(
bravo_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
data
=
{
'name'
:
'Tango Group'
}
data
=
{
'name'
:
'Tango Group'
,
'type'
:
'test'
}
tango_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
tango_response
.
status_code
,
201
)
tango_uri
=
tango_response
.
data
[
'uri'
]
...
...
@@ -607,14 +610,14 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_groups_detail_get_hierarchical
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
alpha_group_id
=
alpha_response
.
data
[
'id'
]
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -634,14 +637,14 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'relationship_type'
],
relationship_type
)
def
test_group_groups_detail_get_linked
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
alpha_group_id
=
alpha_response
.
data
[
'id'
]
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
delta_group_id
=
delta_response
.
data
[
'id'
]
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
...
...
@@ -664,7 +667,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'relationship_type'
],
relationship_type
)
def
test_group_groups_detail_get_notfound
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups/gaois89sdf98'
...
...
@@ -672,16 +675,16 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_groups_detail_delete_hierarchical
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Gamma Group'
}
data
=
{
'name'
:
'Gamma Group'
,
'type'
:
'test'
}
gamma_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
gamma_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -703,16 +706,16 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_groups_detail_delete_linked
(
self
):
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
alpha_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
alpha_response
.
status_code
,
201
)
data
=
{
'name'
:
'Beta Group'
}
data
=
{
'name'
:
'Beta Group'
,
'type'
:
'test'
}
beta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
beta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Delta Group'
}
data
=
{
'name'
:
'Delta Group'
,
'type'
:
'test'
}
delta_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
delta_response
.
status_code
,
201
)
data
=
{
'name'
:
'Gamma Group'
}
data
=
{
'name'
:
'Gamma Group'
,
'type'
:
'test'
}
gamma_response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
gamma_response
.
status_code
,
201
)
test_uri
=
alpha_response
.
data
[
'uri'
]
+
'/groups'
...
...
@@ -737,7 +740,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_courses_list_post
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
group_id
=
response
.
data
[
'id'
]
...
...
@@ -751,7 +754,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'course_id'
],
self
.
test_course_id
)
def
test_group_courses_list_post_duplicate
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
test_uri
=
response
.
data
[
'uri'
]
+
'/courses'
...
...
@@ -768,7 +771,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_courses_list_post_invalid_course
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
test_uri
=
response
.
data
[
'uri'
]
+
'/courses'
...
...
@@ -777,7 +780,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_courses_list_get
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
group_id
=
response
.
data
[
'id'
]
...
...
@@ -791,9 +794,9 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'course_id'
],
self
.
test_course_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
[
'courses'
]
),
1
)
self
.
assertEqual
(
response
.
data
[
'courses'
][
0
][
'course_id'
],
self
.
test_course_id
)
self
.
assertEqual
(
response
.
data
[
'courses'
][
0
][
'display_name'
],
self
.
course
.
display_name
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
self
.
assertEqual
(
response
.
data
[
0
][
'course_id'
],
self
.
test_course_id
)
self
.
assertEqual
(
response
.
data
[
0
][
'display_name'
],
self
.
course
.
display_name
)
def
test_group_courses_list_get_invalid_group
(
self
):
test_uri
=
self
.
base_groups_uri
+
'/1231241/courses'
...
...
@@ -801,7 +804,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_group_courses_detail_get
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
group_id
=
response
.
data
[
'id'
]
...
...
@@ -823,7 +826,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
data
[
'course_id'
],
self
.
test_course_id
)
def
test_group_courses_detail_delete
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
test_uri
=
response
.
data
[
'uri'
]
+
'/courses'
...
...
@@ -844,7 +847,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
204
)
def
test_group_courses_detail_delete_invalid_course
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
test_uri
=
response
.
data
[
'uri'
]
+
'/courses/123124'
...
...
@@ -852,7 +855,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
204
)
def
test_group_courses_detail_get_undefined
(
self
):
data
=
{
'name'
:
self
.
test_group_name
}
data
=
{
'name'
:
self
.
test_group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
self
.
base_groups_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
test_uri
=
'{}/courses/{}'
.
format
(
response
.
data
[
'uri'
],
self
.
course
.
id
)
...
...
lms/djangoapps/api_manager/groups/views.py
View file @
03517b26
...
...
@@ -20,113 +20,182 @@ from xmodule.modulestore import Location, InvalidLocationError
RELATIONSHIP_TYPES
=
{
'hierarchical'
:
'h'
,
'graph'
:
'g'
}
def
_generate_base_uri
(
request
):
def
_generate_base_uri
(
request
,
include_query_string
=
True
):
"""
Constructs the protocol:host:path component of the resource uri
"""
protocol
=
'http'
if
request
.
is_secure
():
protocol
=
protocol
+
's'
if
include_query_string
:
path_to_use
=
request
.
get_full_path
()
else
:
path_to_use
=
request
.
path_info
resource_uri
=
'{}://{}{}'
.
format
(
protocol
,
request
.
get_host
(),
request
.
get_full_path
()
path_to_use
)
return
resource_uri
class
GroupsList
(
APIView
):
"""
### The GroupsList view allows clients to retrieve/append a list of Group entities
- URI: ```/api/groups/```
- GET: Returns a JSON representation (array) of the set of Group entities
* type: __required__, Set filtering parameter
- POST: Provides the ability to append to the Group entity set
* name: The name of the group being added
* type: __required__, Client-specified Group entity type, used for set filtering
* data: Free-form, JSON-formatted metadata attached to this Group entity
- POST Example:
{
"name" : "Alpha Series",
"type" : "series",
"data" : {
"display_name" : "Demo Program",
"start_date" : "2014-01-01",
"end_date" : "2014-12-31"
}
}
### Use Cases/Notes:
* GET requests for _all_ groups are not currently allowed via the API
* If no 'type' parameter is specified during GET, the server will return a 400 Bad Request
* 'type' is a free-form field used to tag/filter group entities.
* Some sample of types include:
** workgroup: a group of users working on a project together
** series: a group of related courses
** company: a group of groups (such as departments)
* 'data' is a free-form field containing type-specific metadata in JSON format, which bypasses the need for extensive database modeling
* Some sample 'data' elements include:
** series: display_name, start_date, end_date
** organization: display_name, contact_name, phone, email
* Ultimately, both 'type' and 'data' are determined by the client/caller. Open edX has no type or data specifications at the present time.
"""
permissions_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
):
"""
POST
creates a new group in the system
POST
/api/groups
"""
group_type
=
request
.
DATA
.
get
(
'type'
,
None
)
if
group_type
is
None
:
return
Response
({},
status
=
status
.
HTTP_400_BAD_REQUEST
)
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
# Group name must be unique, but we need to support dupes
group
=
Group
.
objects
.
create
(
name
=
str
(
uuid
.
uuid4
()))
if
request
.
DATA
.
get
(
'name'
):
original_group_name
=
request
.
DATA
.
get
(
'name'
)
else
:
original_group_name
=
request
.
DATA
.
get
(
'name'
,
None
)
if
original_group_name
is
None
or
len
(
original_group_name
)
==
0
:
return
Response
(
response_data
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
group
.
name
=
'{:04d}: {}'
.
format
(
group
.
id
,
original_group_name
)
group
.
record_active
=
True
group
.
record_date_created
=
timezone
.
now
()
group
.
record_date_modified
=
timezone
.
now
()
group
.
save
()
# Create a corresponding relationship management record
GroupRelationship
.
objects
.
create
(
group_id
=
group
.
id
,
parent_group
=
None
)
# Create a corresponding profile record (for extra meta info)
group_type
=
request
.
DATA
.
get
(
'group_type'
,
None
)
data
=
json
.
dumps
(
request
.
DATA
.
get
(
'data'
))
if
request
.
DATA
.
get
(
'data'
)
else
{}
profile
,
_
=
GroupProfile
.
objects
.
get_or_create
(
group_id
=
group
.
id
,
group_type
=
group_type
,
name
=
original_group_name
,
data
=
data
)
data
=
request
.
DATA
.
get
(
'data'
,
{})
profile
,
_
=
GroupProfile
.
objects
.
get_or_create
(
group_id
=
group
.
id
,
group_type
=
group_type
,
name
=
original_group_name
,
data
=
json
.
dumps
(
data
)
)
response_data
=
{
'id'
:
group
.
id
,
'name'
:
original_group_name
,
'type'
:
group_type
,
'name'
:
profile
.
name
,
}
base_uri
=
_generate_base_uri
(
request
)
response_data
[
'uri'
]
=
'{}/{}'
.
format
(
base_uri
,
group
.
id
)
response_data
[
'uri'
]
=
'{}/{}'
.
format
(
_generate_base_uri
(
request
,
False
)
,
group
.
id
)
response_status
=
status
.
HTTP_201_CREATED
return
Response
(
response_data
,
status
=
response_status
)
def
get
(
self
,
request
):
"""
GET
retrieves a list of groups in the system filtered by type
GET
/api/groups
"""
response_data
=
[]
if
'type'
in
request
.
GET
:
profiles
=
GroupProfile
.
objects
.
filter
(
group_type
=
request
.
GET
[
'type'
])
else
:
profiles
=
GroupProfile
.
objects
.
all
(
)
group_type
=
request
.
QUERY_PARAMS
.
get
(
'type'
,
None
)
if
group_type
is
None
:
return
Response
({},
status
=
status
.
HTTP_400_BAD_REQUEST
)
profiles
=
GroupProfile
.
objects
.
filter
(
group_type
=
request
.
GET
[
'type'
])
.
select_related
(
'group'
)
for
profile
in
profiles
:
item_data
=
{}
item_data
[
'
group_
id'
]
=
profile
.
group_id
item_data
[
'id'
]
=
profile
.
group_id
if
profile
.
name
and
len
(
profile
.
name
):
group_name
=
profile
.
name
else
:
group
=
Group
.
objects
.
get
(
id
=
profile
.
group_id
)
group_name
=
group
.
name
group_name
=
profile
.
group
.
name
item_data
[
'name'
]
=
group_name
if
profile
.
group_type
:
item_data
[
'group_type'
]
=
profile
.
group_type
item_data
[
'type'
]
=
profile
.
group_type
if
profile
.
data
:
item_data
[
'data'
]
=
json
.
loads
(
profile
.
data
)
item_data
[
'uri'
]
=
'{}/{}'
.
format
(
_generate_base_uri
(
request
,
False
),
profile
.
group_id
)
response_data
.
append
(
item_data
)
return
Response
(
response_data
,
status
=
status
.
HTTP_200_OK
)
class
GroupsDetail
(
APIView
):
"""
### The GroupsDetail view allows clients to interact with a specific Group entity
- URI: ```/api/groups/{group_id}```
- GET: Returns a JSON representation of the specified Group entity
- POST: Provides the ability to modify specific fields for this Group entity
* type: Client-specified Group entity type
* data: Free-form, JSON-formatted metadata attached to this Group entity
- POST Example:
{
"type" : "series",
"data" : {
"display_name" : "Demo Program",
"start_date" : "2014-01-01",
"end_date" : "2014-12-31"
}
}
### Use Cases/Notes:
* Use the GroupsDetail view to obtain the current state for a specific Group
* For POSTs, you may include only those parameters you wish to modify, for example:
** Modifying the start_date for a 'series' without changing the 'type' field
* A GET response will additionally include a list of URIs to available sub-resources:
** Related Users (/api/groups/{group_id}/users)
** Related Courses (/api/groups/{group_id}/courses)
** Related Groups(/api/groups/{group_id}/groups)
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
,
group_id
):
"""
POST /api/groups/{group_id}
"""
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
print
base_uri
try
:
existing_group
=
Group
.
objects
.
get
(
id
=
group_id
)
except
ObjectDoesNotExist
:
return
Response
({},
status
.
HTTP_404_NOT_FOUND
)
group_type
=
request
.
DATA
.
get
(
'group_type'
)
data
=
json
.
dumps
(
request
.
DATA
.
get
(
'data'
))
if
request
.
DATA
.
get
(
'data'
)
else
None
if
not
group_type
and
not
data
:
return
Response
({},
status
.
HTTP_400_BAD_REQUEST
)
profile
,
_
=
GroupProfile
.
objects
.
get_or_create
(
group_id
=
group_id
)
profile
.
group_type
=
group_type
profile
.
data
=
data
group_type
=
request
.
DATA
.
get
(
'type'
,
None
)
if
group_type
:
profile
.
group_type
=
group_type
data
=
request
.
DATA
.
get
(
'data'
,
None
)
if
data
:
profile
.
data
=
json
.
dumps
(
data
)
profile
.
save
()
response_data
[
'id'
]
=
existing_group
.
id
response_data
[
'name'
]
=
profile
.
name
response_data
[
'type'
]
=
profile
.
group_type
response_data
[
'uri'
]
=
_generate_base_uri
(
request
)
return
Response
(
response_data
,
status
=
status
.
HTTP_20
1_CREATED
)
return
Response
(
response_data
,
status
=
status
.
HTTP_20
0_OK
)
def
get
(
self
,
request
,
group_id
):
"""
GET
retrieves an existing group from the system
GET
/api/groups/{group_id}
"""
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
...
...
@@ -141,6 +210,8 @@ class GroupsDetail(APIView):
response_data
[
'resources'
]
.
append
({
'uri'
:
resource_uri
})
resource_uri
=
'{}/groups'
.
format
(
base_uri
)
response_data
[
'resources'
]
.
append
({
'uri'
:
resource_uri
})
resource_uri
=
'{}/courses'
.
format
(
base_uri
)
response_data
[
'resources'
]
.
append
({
'uri'
:
resource_uri
})
try
:
group_profile
=
GroupProfile
.
objects
.
get
(
group_id
=
group_id
)
except
ObjectDoesNotExist
:
...
...
@@ -151,7 +222,7 @@ class GroupsDetail(APIView):
else
:
response_data
[
'name'
]
=
existing_group
.
name
if
group_profile
.
group_type
:
response_data
[
'
group_
type'
]
=
group_profile
.
group_type
response_data
[
'type'
]
=
group_profile
.
group_type
data
=
group_profile
.
data
if
data
:
response_data
[
'data'
]
=
json
.
loads
(
data
)
...
...
@@ -161,11 +232,27 @@ class GroupsDetail(APIView):
class
GroupsUsersList
(
APIView
):
"""
### The GroupsUserList view allows clients to interact with the set of User entities related to the specified Group
- URI: ```/api/groups/{group_id}/users/```
- GET: Returns a JSON representation (array) of the set of related User entities
- POST: Append a User entity to the set of related User entities for the specified group
* user_id: __required__, The identifier for the User being added
- POST Example:
{
"user_id" : 123
}
### Use Cases/Notes:
* Use the GroupsUsersList view to manage User membership for a specific Group
* For example, as a newly-added member of a 'workgroup' group, a User could be presented with a list of their peers
* Once a User Group exists, you can additionally link to Courses and other Groups (see GroupsCoursesList, GroupsGroupsList)
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
,
group_id
):
"""
POST
creates a new group-user relationship in the system
POST
/api/groups/{group_id}/users/
"""
base_uri
=
_generate_base_uri
(
request
)
try
:
...
...
@@ -178,7 +265,7 @@ class GroupsUsersList(APIView):
except
ObjectDoesNotExist
:
return
Response
({},
status
.
HTTP_404_NOT_FOUND
)
try
:
existing_relationship
=
Group
.
objects
.
get
(
user
=
existing_user
)
existing_relationship
=
Group
.
objects
.
filter
(
id
=
existing_group
.
id
)
.
get
(
user
=
existing_user
)
except
ObjectDoesNotExist
:
existing_relationship
=
None
response_data
=
{}
...
...
@@ -196,7 +283,7 @@ class GroupsUsersList(APIView):
def
get
(
self
,
request
,
group_id
):
"""
GET
retrieves the list of users related to the specified group
GET
/api/groups/{group_id}/users/
"""
try
:
existing_group
=
Group
.
objects
.
get
(
id
=
group_id
)
...
...
@@ -218,11 +305,20 @@ class GroupsUsersList(APIView):
class
GroupsUsersDetail
(
APIView
):
"""
### The GroupsUsersDetail view allows clients to interact with a specific Group-User relationship
- URI: ```/api/groups/{group_id}/users/{user_id}```
- GET: Returns a JSON representation of the specified Group-User relationship
- DELETE: Removes an existing Group-User relationship
### Use Cases/Notes:
* Use the GroupsUsersDetail to validate that a User is a member of a specific Group
* Cancelling a User's membership in a Group is as simple as calling DELETE on the URI
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
group_id
,
user_id
):
"""
GET
retrieves an existing group-user relationship from the system
GET
/api/groups/{group_id}/users/{user_id}
"""
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
...
...
@@ -255,11 +351,41 @@ class GroupsUsersDetail(APIView):
class
GroupsGroupsList
(
APIView
):
"""
### The GroupsGroupsList view allows clients to interact with the set of Groups related to the specified Group
- URI: ```/api/groups/{group_id}/groups/```
- GET: Returns a JSON representation (array) of the set of related Group entities
- POST: Provides the ability to append to the related Group entity set
* group_id: __required__, The name of the Group being added
* relationship_type: __required__, Relationship paradigm, select from the following values:
** 'g', _graph_, create a graph(aka, linked) relationship with the specified Group
** 'h', _hierarchical_, create a parent-child relationship with the specified Group
- POST Example:
{
"group_id" : 1234,
"relationship_type" : "g"
}
### Use Cases/Notes:
* Use a graph-type relationship when you simply want to indicate a link between two groups:
** Linking a course series with a particular company
** Linking a user workgroup with a particular course series
* Use a hierarchical-type relationship when you want to enforce a parent-child link between two groups:
** Linking a company (parent) to a department (child)
** Linking a user workgroup (parent) to a breakout user workgroup (child)
* Note that posting a new hierarchical relationship for a child group having a parent will overwrite the current relationship:
** POST /groups/123/groups { "group_id": 246}
** GET /groups/123/groups/246 -> 200 OK
** POST /groups/987/groups {"group_id": 246}
** GET /groups/123/groups/246 -> 404 NOT FOUND
** GET /groups/987/groups/246 -> 200 OK
* Once a Group Group exists, you can additionally link to Users and Courses (see GroupsUsersList, GroupsCoursesList)
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
,
group_id
):
"""
POST
creates a new group-group relationship in the system
POST
/api/groups/{group_id}/groups/{related_group_id}
"""
response_data
=
{}
to_group_id
=
request
.
DATA
[
'group_id'
]
...
...
@@ -291,7 +417,7 @@ class GroupsGroupsList(APIView):
def
get
(
self
,
request
,
group_id
):
"""
GET
retrieves the existing group-group relationships for the specified group
GET
/api/groups/{group_id}/groups/{related_group_id}
"""
try
:
from_group_relationship
=
GroupRelationship
.
objects
.
get
(
group__id
=
group_id
)
...
...
@@ -329,11 +455,22 @@ class GroupsGroupsList(APIView):
class
GroupsGroupsDetail
(
APIView
):
"""
### The GroupsGroupsDetail view allows clients to interact with a specific Group-Group relationship
- URI: ```/api/groups/{group_id}/groups/{related_group_id}```
- GET: Returns a JSON representation of the specified Group-Group relationship
- DELETE: Removes an existing Group-Group relationship
### Use Cases/Notes:
* Use the GroupsGroupsDetail operation to confirm that a relationship exists between two Groups
** Is the current workgroup linked to the specified company?
** Is the current course series linked to the specified workgroup?
* To remove an existing Group-Group relationship, simply call DELETE on the URI
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
group_id
,
related_group_id
):
"""
GET
retrieves an existing group-group relationship from the system
GET
/api/groups/{group_id}/groups/{related_group_id}
"""
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
...
...
@@ -357,7 +494,7 @@ class GroupsGroupsDetail(APIView):
def
delete
(
self
,
request
,
group_id
,
related_group_id
):
"""
DELETE
removes/inactivates/etc. an existing group-group relationship
DELETE
/api/groups/{group_id}/groups/{related_group_id}
"""
try
:
from_group_relationship
=
GroupRelationship
.
objects
.
get
(
group__id
=
group_id
)
...
...
@@ -384,11 +521,26 @@ class GroupsGroupsDetail(APIView):
class
GroupsCoursesList
(
APIView
):
"""
### The GroupsCoursesList view allows clients to interact with the set of Courses related to the specified Group
- URI: ```/api/groups/{group_id}/courses/```
- GET: Returns a JSON representation (array) of the set of related Course entities
- POST: Provides the ability to append to the related Course entity set
* course_id: __required__, The name of the Course being added
- POST Example:
{
"course_id" : "edx/demo/course",
}
### Use Cases/Notes:
* Create a Group of Courses to model cases such as an academic program or topical series
* Once a Course Group exists, you can additionally link to Users and other Groups (see GroupsUsersList, GroupsGroupsList)
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
post
(
self
,
request
,
group_id
):
"""
POST
creates a new group-course relationship in the system
POST
/api/groups/{group_id}/courses/{course_id}
"""
response_data
=
{}
try
:
...
...
@@ -422,7 +574,7 @@ class GroupsCoursesList(APIView):
def
get
(
self
,
request
,
group_id
):
"""
GET
returns all courses that has a relationship to the group
GET
/api/groups/{group_id}/courses/{course_id}
"""
response_data
=
{}
try
:
...
...
@@ -431,24 +583,36 @@ class GroupsCoursesList(APIView):
return
Response
({},
status
.
HTTP_404_NOT_FOUND
)
store
=
modulestore
()
members
=
CourseGroupRelationship
.
objects
.
filter
(
group
=
existing_group
)
response_data
[
'courses'
]
=
[]
response_data
=
[]
for
member
in
members
:
course
=
store
.
get_course
(
member
.
course_id
)
course_data
=
{
'course_id'
:
member
.
course_id
,
'display_name'
:
course
.
display_name
}
response_data
[
'courses'
]
.
append
(
course_data
)
response_data
.
append
(
course_data
)
response_status
=
status
.
HTTP_200_OK
return
Response
(
response_data
,
status
=
response_status
)
class
GroupsCoursesDetail
(
APIView
):
"""
### The GroupsCoursesDetail view allows clients to interact with a specific Group-Course relationship
- URI: ```/api/groups/{group_id}/courses/{course_id}```
- GET: Returns a JSON representation of the specified Group-Course relationship
- DELETE: Removes an existing Group-Course relationship
### Use Cases/Notes:
* Use the GroupsCoursesDetail to validate that a Course is linked to a specific Group
* Is Course part of the specified series?
* Is Course linked to the specified workgroup?
* Removing a Course from a Group is as simple as calling DELETE on the URI
* Remove a course from the specified academic program
"""
permission_classes
=
(
ApiKeyHeaderPermission
,)
def
get
(
self
,
request
,
group_id
,
course_id
):
"""
GET
retrieves an existing group-course relationship from the system
GET
/api/groups/{group_id}/courses/{course_id}
"""
response_data
=
{}
base_uri
=
_generate_base_uri
(
request
)
...
...
@@ -469,7 +633,7 @@ class GroupsCoursesDetail(APIView):
def
delete
(
self
,
request
,
group_id
,
course_id
):
"""
DELETE
removes/inactivates/etc. an existing group-course relationship
DELETE
/api/groups/{group_id}/courses/{course_id}
"""
try
:
existing_group
=
Group
.
objects
.
get
(
id
=
group_id
)
...
...
lms/djangoapps/api_manager/migrations/0005_coursecontentgrouprelationship.py
0 → 100644
View file @
03517b26
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding model 'CourseContentGroupRelationship'
db
.
create_table
(
'api_manager_coursecontentgrouprelationship'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'content_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'group'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'api_manager.GroupProfile'
])),
(
'record_active'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
True
)),
))
db
.
send_create_signal
(
'api_manager'
,
[
'CourseContentGroupRelationship'
])
db
.
create_index
(
'api_manager_coursecontentgrouprelationship'
,
[
'course_id'
,
'content_id'
],
unique
=
False
,
db_tablespace
=
''
)
def
backwards
(
self
,
orm
):
# Deleting model 'CourseContentGroupRelationship'
db
.
delete_table
(
'api_manager_coursecontentgrouprelationship'
)
models
=
{
'api_manager.coursegrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'CourseGroupRelationship'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.Group']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.groupprofile'
:
{
'Meta'
:
{
'object_name'
:
'GroupProfile'
,
'db_table'
:
"'auth_groupprofile'"
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'data'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.Group']"
}),
'group_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.grouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'GroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'parent_group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'0'
,
'related_name'
:
"'child_groups'"
,
'null'
:
'True'
,
'blank'
:
'True'
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.linkedgrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'LinkedGroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'from_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'from_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'to_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'to_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
})
},
'api_manager.coursecontentgrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'CourseContentGroupRelationship'
},
'content_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['api_manager.GroupProfile']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
}
}
complete_apps
=
[
'api_manager'
]
lms/djangoapps/api_manager/migrations/0006_contentgroupuniqueconstraint.py
0 → 100644
View file @
03517b26
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding unique constraint on 'CourseContentGroupRelationship', fields ['course_id', 'content_id', 'group']
db
.
create_unique
(
'api_manager_coursecontentgrouprelationship'
,
[
'course_id'
,
'content_id'
,
'group_id'
])
def
backwards
(
self
,
orm
):
# Removing unique constraint on 'CourseContentGroupRelationship', fields ['course_id', 'content_id', 'group']
db
.
delete_unique
(
'api_manager_coursecontentgrouprelationship'
,
[
'course_id'
,
'content_id'
,
'group_id'
])
models
=
{
'api_manager.coursecontentgrouprelationship'
:
{
'Meta'
:
{
'unique_together'
:
"(('course_id', 'content_id', 'group'),)"
,
'object_name'
:
'CourseContentGroupRelationship'
},
'content_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['api_manager.GroupProfile']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.coursegrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'CourseGroupRelationship'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.Group']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.groupprofile'
:
{
'Meta'
:
{
'object_name'
:
'GroupProfile'
,
'db_table'
:
"'auth_groupprofile'"
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'data'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.Group']"
}),
'group_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.grouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'GroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'parent_group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'0'
,
'related_name'
:
"'child_groups'"
,
'null'
:
'True'
,
'blank'
:
'True'
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.linkedgrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'LinkedGroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'from_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'from_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'to_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'to_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
})
},
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
}
}
complete_apps
=
[
'api_manager'
]
\ No newline at end of file
lms/djangoapps/api_manager/models.py
View file @
03517b26
...
...
@@ -106,3 +106,20 @@ class GroupProfile(TimeStampedModel):
name
=
models
.
CharField
(
max_length
=
255
,
null
=
True
,
blank
=
True
)
data
=
models
.
TextField
(
blank
=
True
)
# JSON dictionary for generic key/value pairs
record_active
=
models
.
BooleanField
(
default
=
True
)
class
CourseContentGroupRelationship
(
TimeStampedModel
):
"""
The CourseContentGroupRelationship model contains information describing the
link between a particular courseware element (chapter, unit, video, etc.)
and a group. A typical use case for this table is to support the concept
of a student workgroup for a given course, where the project is actually
a Chapter courseware element.
"""
course_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
content_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
group
=
models
.
ForeignKey
(
GroupProfile
,
db_index
=
True
)
record_active
=
models
.
BooleanField
(
default
=
True
)
class
Meta
:
unique_together
=
(
"course_id"
,
"content_id"
,
"group"
)
lms/djangoapps/api_manager/users/tests.py
View file @
03517b26
...
...
@@ -239,7 +239,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_list_post
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -262,7 +262,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_list_post_duplicate
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -281,7 +281,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_list_post_invalid_user
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users/897698769/groups'
...
...
@@ -292,7 +292,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_list_get
(
self
):
test_uri
=
'/api/groups'
group_name
=
'Alpha Group'
data
=
{
'name'
:
group_name
}
data
=
{
'name'
:
group_name
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -327,7 +327,7 @@ class UsersApiTests(TestCase):
group_url
=
'/api/groups'
group_name
=
'Alpha Group'
data
=
{
'name'
:
group_name
,
'
group_
type'
:
'Engineer'
}
data
=
{
'name'
:
group_name
,
'type'
:
'Engineer'
}
response
=
self
.
do_post
(
group_url
,
data
)
group_id
=
response
.
data
[
'id'
]
user_groups_uri
=
'{}/groups'
.
format
(
test_uri
)
...
...
@@ -336,7 +336,7 @@ class UsersApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
201
)
group_name
=
'Beta Group'
data
=
{
'name'
:
group_name
,
'
group_
type'
:
'Architect'
}
data
=
{
'name'
:
group_name
,
'type'
:
'Architect'
}
response
=
self
.
do_post
(
group_url
,
data
)
group_id
=
response
.
data
[
'id'
]
data
=
{
'group_id'
:
group_id
}
...
...
@@ -367,7 +367,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_detail_get
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -390,7 +390,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_detail_delete
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -419,7 +419,7 @@ class UsersApiTests(TestCase):
def
test_user_groups_detail_get_undefined
(
self
):
test_uri
=
'/api/groups'
data
=
{
'name'
:
'Alpha Group'
}
data
=
{
'name'
:
'Alpha Group'
,
'type'
:
'test'
}
response
=
self
.
do_post
(
test_uri
,
data
)
group_id
=
response
.
data
[
'id'
]
test_uri
=
'/api/users'
...
...
@@ -529,8 +529,8 @@ class UsersApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
201
)
position_data
=
{
'position'
:
{
'parent_
module
_id'
:
str
(
course
.
id
),
'child_
module
_id'
:
str
(
chapter3
.
location
)
'parent_
content
_id'
:
str
(
course
.
id
),
'child_
content
_id'
:
str
(
chapter3
.
location
)
}
}
...
...
@@ -551,15 +551,15 @@ class UsersApiTests(TestCase):
test_uri
=
'/api/users/{}/courses/{}'
.
format
(
str
(
user_id
),
course_id
)
position_data
=
{
'position'
:
{
'parent_
module
_id'
:
course_id
,
'child_
module
_id'
:
str
(
chapter1
.
location
)
'parent_
content
_id'
:
course_id
,
'child_
content
_id'
:
str
(
chapter1
.
location
)
}
}
response
=
self
.
do_post
(
test_uri
,
data
=
position_data
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_user_courses_detail_post_position_course_as_
module
(
self
):
def
test_user_courses_detail_post_position_course_as_
content
(
self
):
course
=
CourseFactory
.
create
()
test_data
=
'<html>{}</html>'
.
format
(
str
(
uuid
.
uuid4
()))
chapter1
=
ItemFactory
.
create
(
...
...
@@ -581,8 +581,8 @@ class UsersApiTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
201
)
position_data
=
{
'position'
:
{
'parent_
module
_id'
:
str
(
course
.
location
),
'child_
module
_id'
:
str
(
chapter1
.
location
)
'parent_
content
_id'
:
str
(
course
.
location
),
'child_
content
_id'
:
str
(
chapter1
.
location
)
}
}
...
...
@@ -617,8 +617,8 @@ class UsersApiTests(TestCase):
self
.
assertEqual
(
response
.
data
[
'user_id'
],
user_id
)
position_data
=
{
'position'
:
{
'parent_
module
_id'
:
str
(
course
.
location
),
'child_
module
_id'
:
str
(
chapter1
.
location
)
'parent_
content
_id'
:
str
(
course
.
location
),
'child_
content
_id'
:
str
(
chapter1
.
location
)
}
}
...
...
lms/djangoapps/api_manager/users/views.py
View file @
03517b26
...
...
@@ -31,6 +31,7 @@ from util.bad_request_rate_limiter import BadRequestRateLimiter
log
=
logging
.
getLogger
(
__name__
)
AUDIT_LOG
=
logging
.
getLogger
(
"audit"
)
def
_generate_base_uri
(
request
):
"""
Constructs the protocol:host:path component of the resource uri
...
...
@@ -57,6 +58,7 @@ def _serialize_user_profile(response_data, user_profile):
return
response_data
def
_serialize_user
(
response_data
,
user
):
"""
Loads the object data into the response dict
...
...
@@ -70,14 +72,15 @@ def _serialize_user(response_data, user):
response_data
[
'is_active'
]
=
user
.
is_active
return
response_data
def
_save_module_position
(
request
,
user
,
course_id
,
course_descriptor
,
position
):
def
_save_content_position
(
request
,
user
,
course_id
,
course_descriptor
,
position
):
"""
Records the indicated position for the specified course
Really no reason to generalize this out of user_courses_detail aside from pylint complaining
"""
field_data_cache
=
FieldDataCache
([
course_descriptor
],
course_id
,
user
)
if
course_id
==
position
[
'parent_
module
_id'
]:
parent_
module
=
get_module_for_descriptor
(
if
course_id
==
position
[
'parent_
content
_id'
]:
parent_
content
=
get_module_for_descriptor
(
user
,
request
,
course_descriptor
,
...
...
@@ -85,23 +88,23 @@ def _save_module_position(request, user, course_id, course_descriptor, position)
course_id
)
else
:
parent_
module
=
module_render
.
get_module
(
parent_
content
=
module_render
.
get_module
(
user
,
request
,
position
[
'parent_
module
_id'
],
position
[
'parent_
content
_id'
],
field_data_cache
,
course_id
)
child_
module
=
module_render
.
get_module
(
child_
content
=
module_render
.
get_module
(
user
,
request
,
position
[
'child_
module
_id'
],
position
[
'child_
content
_id'
],
field_data_cache
,
course_id
)
save_child_position
(
parent_
module
,
child_module
.
location
.
name
)
saved_
module
=
get_current_child
(
parent_module
)
return
saved_
module
.
id
save_child_position
(
parent_
content
,
child_content
.
location
.
name
)
saved_
content
=
get_current_child
(
parent_content
)
return
saved_
content
.
id
class
UsersList
(
APIView
):
...
...
@@ -539,7 +542,7 @@ class UsersCoursesDetail(APIView):
response_data
[
'course_id'
]
=
course_id
response_status
=
status
.
HTTP_201_CREATED
if
request
.
DATA
[
'position'
]:
response_data
[
'position'
]
=
_save_
module
_position
(
response_data
[
'position'
]
=
_save_
content
_position
(
request
,
user
,
course_id
,
...
...
@@ -568,13 +571,13 @@ class UsersCoursesDetail(APIView):
response_data
[
'course_id'
]
=
course_id
response_data
[
'uri'
]
=
base_uri
field_data_cache
=
FieldDataCache
([
course_descriptor
],
course_id
,
user
)
course_
module
=
module_render
.
get_module
(
course_
content
=
module_render
.
get_module
(
user
,
request
,
course_descriptor
.
location
,
field_data_cache
,
course_id
)
response_data
[
'position'
]
=
course_
module
.
position
response_data
[
'position'
]
=
course_
content
.
position
response_status
=
status
.
HTTP_200_OK
else
:
response_status
=
status
.
HTTP_404_NOT_FOUND
...
...
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