Commit 03517b26 by Matt Drayer Committed by Jonathan Piacenti

mattdrayer/api-modulesgroups: API - Added grouping capability for courseware content

parent b4f9b84d
...@@ -50,16 +50,30 @@ class CoursesApiTests(TestCase): ...@@ -50,16 +50,30 @@ class CoursesApiTests(TestCase):
display_name="Overview" 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", category="videosequence",
parent_location=self.chapter.location, parent_location=self.chapter.location,
data=self.test_data, data=self.test_data,
display_name="Video_Sequence" display_name="Video_Sequence"
) )
self.submodule = ItemFactory.create( self.content_child = ItemFactory.create(
category="video", category="video",
parent_location=self.module.location, parent_location=self.course_content.location,
data=self.test_data, data=self.test_data,
display_name="Video_Resources" display_name="Video_Resources"
) )
...@@ -98,10 +112,11 @@ class CoursesApiTests(TestCase): ...@@ -98,10 +112,11 @@ class CoursesApiTests(TestCase):
self.test_course_number = self.course.number self.test_course_number = self.course.number
self.test_course_org = self.course.org self.test_course_org = self.course.org
self.test_chapter_id = self.chapter.id self.test_chapter_id = self.chapter.id
self.test_module_id = self.module.id self.test_course_content_id = self.course_content.id
self.test_submodule_id = self.submodule.id self.test_bogus_content_id = "j5y://foo/bar/baz"
self.base_modules_uri = '/api/courses/' + self.test_course_id + '/modules' self.test_content_child_id = self.content_child.id
self.base_chapters_uri = self.base_modules_uri + '?type=chapter' 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() self.client = SecureClient()
cache.clear() cache.clear()
...@@ -169,7 +184,7 @@ class CoursesApiTests(TestCase): ...@@ -169,7 +184,7 @@ class CoursesApiTests(TestCase):
confirm_uri = self.test_server_prefix + test_uri confirm_uri = self.test_server_prefix + test_uri
self.assertEqual(response.data['uri'], confirm_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' test_uri = self.base_courses_uri + '/' + self.test_course_id + '?depth=100'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -180,7 +195,7 @@ class CoursesApiTests(TestCase): ...@@ -180,7 +195,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.data['org'], self.test_course_org) self.assertEqual(response.data['org'], self.test_course_org)
confirm_uri = self.test_server_prefix + test_uri confirm_uri = self.test_server_prefix + test_uri
self.assertEqual(response.data['uri'], confirm_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): def test_courses_detail_get_notfound(self):
test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id
...@@ -195,17 +210,17 @@ class CoursesApiTests(TestCase): ...@@ -195,17 +210,17 @@ class CoursesApiTests(TestCase):
self.assertGreater(len(response.data), 0) self.assertGreater(len(response.data), 0)
self.assertEqual(response.data['category'], 'course') self.assertEqual(response.data['category'], 'course')
self.assertEqual(response.data['name'], self.course.display_name) 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['category'], 'chapter')
self.assertEqual(chapter['name'], 'Overview') 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['category'], 'videosequence')
self.assertEqual(sequence['name'], 'Video_Sequence') self.assertEqual(sequence['name'], 'Video_Sequence')
self.assertNotIn('modules', sequence) self.assertNotIn('children', sequence)
def test_courses_tree_get_root(self): def test_courses_tree_get_root(self):
# query the course tree to quickly get naviation information # query the course tree to quickly get naviation information
...@@ -215,7 +230,7 @@ class CoursesApiTests(TestCase): ...@@ -215,7 +230,7 @@ class CoursesApiTests(TestCase):
self.assertGreater(len(response.data), 0) self.assertGreater(len(response.data), 0)
self.assertEqual(response.data['category'], 'course') self.assertEqual(response.data['category'], 'course')
self.assertEqual(response.data['name'], self.course.display_name) 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): def test_chapter_list_get(self):
test_uri = self.base_chapters_uri test_uri = self.base_chapters_uri
...@@ -227,81 +242,86 @@ class CoursesApiTests(TestCase): ...@@ -227,81 +242,86 @@ class CoursesApiTests(TestCase):
if matched_chapter is False and chapter['id'] == self.test_chapter_id: if matched_chapter is False and chapter['id'] == self.test_chapter_id:
self.assertIsNotNone(chapter['uri']) self.assertIsNotNone(chapter['uri'])
self.assertGreater(len(chapter['uri']), 0) 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) self.assertEqual(chapter['uri'], confirm_uri)
matched_chapter = True matched_chapter = True
self.assertTrue(matched_chapter) self.assertTrue(matched_chapter)
def test_chapter_detail_get(self): 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) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data['id']), 0) self.assertGreater(len(response.data['id']), 0)
self.assertEqual(response.data['id'], self.test_chapter_id) self.assertEqual(response.data['id'], self.test_chapter_id)
confirm_uri = self.test_server_prefix + test_uri confirm_uri = self.test_server_prefix + test_uri
self.assertEqual(response.data['uri'], confirm_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): def test_course_content_list_get(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id test_uri = '{}/{}/children'.format(self.base_course_content_uri, self.test_course_content_id)
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0) self.assertGreater(len(response.data), 0)
matched_submodule = False matched_child = False
for submodule in response.data['modules']: for child in response.data:
if matched_submodule is False and submodule['id'] == self.test_submodule_id: if matched_child is False and child['id'] == self.test_content_child_id:
self.assertIsNotNone(submodule['uri']) self.assertIsNotNone(child['uri'])
self.assertGreater(len(submodule['uri']), 0) self.assertGreater(len(child['uri']), 0)
confirm_uri = self.test_server_prefix + self.base_modules_uri + '/' + submodule['id'] confirm_uri = self.test_server_prefix + self.base_course_content_uri + '/' + child['id']
self.assertEqual(submodule['uri'], confirm_uri) self.assertEqual(child['uri'], confirm_uri)
matched_submodule = True matched_child = True
self.assertTrue(matched_submodule) 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): def test_course_content_detail_get(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id test_uri = self.base_course_content_uri + '/' + self.test_course_content_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0) 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 confirm_uri = self.test_server_prefix + test_uri
self.assertEqual(response.data['uri'], confirm_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): def test_course_content_detail_get_course(self):
test_uri = self.base_modules_uri + '/' + self.test_course_id test_uri = self.base_course_content_uri + '/' + self.test_course_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0) self.assertGreater(len(response.data), 0)
self.assertEqual(response.data['id'], self.test_course_id) self.assertEqual(response.data['id'], self.test_course_id)
confirm_uri = self.test_server_prefix + self.base_courses_uri + '/' + 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.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): def test_course_content_detail_get_notfound(self):
test_uri = self.base_modules_uri + '/' + '2p38fp2hjfp9283' test_uri = self.base_course_content_uri + '/' + self.test_bogus_content_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_modules_list_get_filtered_submodules_for_module(self): def test_course_content_list_get_filtered_children_for_child(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id + '/submodules?type=video' test_uri = self.base_course_content_uri + '/' + self.test_course_content_id + '/children?type=video'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0) self.assertGreater(len(response.data), 0)
matched_submodule = False matched_child = False
for submodule in response.data: for child in response.data:
if matched_submodule is False and submodule['id'] == self.test_submodule_id: if matched_child is False and child['id'] == self.test_content_child_id:
confirm_uri = self.test_server_prefix + self.base_modules_uri + '/' + submodule['id'] confirm_uri = '{}{}/{}'.format(self.test_server_prefix, self.base_course_content_uri, child['id'])
self.assertEqual(submodule['uri'], confirm_uri) self.assertEqual(child['uri'], confirm_uri)
matched_submodule = True matched_child = True
self.assertTrue(matched_submodule) self.assertTrue(matched_child)
def test_modules_list_get_notfound(self): def test_course_content_list_get_notfound(self):
test_uri = self.base_modules_uri + '/2p38fp2hjfp9283/submodules?type=video' test_uri = '{}{}/children?type=video'.format(self.base_course_content_uri, self.test_bogus_content_id)
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_courses_groups_list_post(self): 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) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
...@@ -320,7 +340,7 @@ class CoursesApiTests(TestCase): ...@@ -320,7 +340,7 @@ class CoursesApiTests(TestCase):
course_fail_uri = '{}/{}/groups'.format(self.base_courses_uri, '/ed/Open_DemoX/edx_demo_course') course_fail_uri = '{}/{}/groups'.format(self.base_courses_uri, '/ed/Open_DemoX/edx_demo_course')
for i in xrange(2): for i in xrange(2):
data_dict = { 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) response = self.do_post(self.base_groups_uri, data_dict)
group_id = response.data['id'] group_id = response.data['id']
...@@ -329,7 +349,7 @@ class CoursesApiTests(TestCase): ...@@ -329,7 +349,7 @@ class CoursesApiTests(TestCase):
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
self.assertEqual(response.status_code, 201) 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) response = self.do_post(self.base_groups_uri, data_dict)
group_id = response.data['id'] group_id = response.data['id']
data = {'group_id': group_id} data = {'group_id': group_id}
...@@ -360,7 +380,7 @@ class CoursesApiTests(TestCase): ...@@ -360,7 +380,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_courses_groups_list_post_duplicate(self): 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) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id) test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id)
...@@ -377,7 +397,7 @@ class CoursesApiTests(TestCase): ...@@ -377,7 +397,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_courses_groups_detail_get(self): 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) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id) test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id)
...@@ -402,7 +422,7 @@ class CoursesApiTests(TestCase): ...@@ -402,7 +422,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.data['group_id'], group_id) self.assertEqual(response.data['group_id'], group_id)
def test_courses_groups_detail_delete(self): 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) response = self.do_post(self.base_groups_uri, data)
test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id) test_uri = '{}/{}/groups'.format(self.base_courses_uri, self.test_course_id)
data = {'group_id': response.data['id']} data = {'group_id': response.data['id']}
...@@ -426,7 +446,7 @@ class CoursesApiTests(TestCase): ...@@ -426,7 +446,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.status_code, 204) self.assertEqual(response.status_code, 204)
def test_courses_groups_detail_get_undefined(self): 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) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '{}/{}/groups/{}'.format(self.base_courses_uri, self.test_course_id, group_id) test_uri = '{}/{}/groups/{}'.format(self.base_courses_uri, self.test_course_id, group_id)
...@@ -792,3 +812,187 @@ class CoursesApiTests(TestCase): ...@@ -792,3 +812,187 @@ class CoursesApiTests(TestCase):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/users/213432' test_uri = self.base_courses_uri + '/' + self.test_course_id + '/users/213432'
response = self.do_delete(test_uri) response = self.do_delete(test_uri)
self.assertEqual(response.status_code, 204) 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)
...@@ -12,9 +12,11 @@ urlpatterns = patterns( ...@@ -12,9 +12,11 @@ urlpatterns = patterns(
'', '',
url(r'/*$^', courses_views.CoursesList.as_view()), url(r'/*$^', courses_views.CoursesList.as_view()),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)$', courses_views.CoursesDetail.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>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)/groups/(?P<group_id>[0-9]+)$', courses_views.CourseContentGroupsDetail.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>[^/]+/[^/]+/[^/]+)/content/(?P<content_id>[a-zA-Z0-9/_:]+)/groups/*$', courses_views.CourseContentGroupsList.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/_:]+)/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/(?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>[^/]+/[^/]+/[^/]+)/groups/*$', courses_views.CoursesGroupsList.as_view()),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/overview$', courses_views.CoursesOverview.as_view()), url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/overview$', courses_views.CoursesOverview.as_view()),
......
...@@ -14,7 +14,7 @@ from rest_framework.response import Response ...@@ -14,7 +14,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api_manager.permissions import ApiKeyHeaderPermission 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 import module_render
from courseware.courses import get_course, get_course_about_section, get_course_info_section from courseware.courses import get_course, get_course_about_section, get_course_info_section
from courseware.model_data import FieldDataCache from courseware.model_data import FieldDataCache
...@@ -42,90 +42,101 @@ def _generate_base_uri(request): ...@@ -42,90 +42,101 @@ def _generate_base_uri(request):
return resource_uri 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 Parses the provided content object looking for children
Matches on submodule type (category) when specified Matches on child type (category) when specified
""" """
submodules = [] children = []
if hasattr(module, 'children'): if hasattr(content, 'children'):
child_modules = module.get_children() child_content = content.get_children()
for child_module in child_modules: for child in child_content:
if submodule_type: if content_type:
if getattr(child_module, 'category') == submodule_type: if getattr(child, 'category') == content_type:
submodules.append(child_module) children.append(child)
else: else:
submodules.append(child_module) children.append(child)
return submodules 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 This should probably evolve to use DRF serializers
""" """
data = {} data = {}
if getattr(module, 'id') == course_id: if getattr(content, 'id') == course_id:
module_id = module.id content_id = content.id
else: else:
module_id = module.location.url() content_id = content.location.url()
data['id'] = module_id data['id'] = content_id
if hasattr(module, 'display_name'): if hasattr(content, 'display_name'):
data['name'] = module.display_name data['name'] = content.display_name
data['category'] = module.location.category data['category'] = content.location.category
protocol = 'http' protocol = 'http'
if request.is_secure(): if request.is_secure():
protocol = protocol + 's' protocol = protocol + 's'
module_uri = '{}://{}/api/courses/{}'.format( content_uri = '{}://{}/api/courses/{}'.format(
protocol, protocol,
request.get_host(), request.get_host(),
course_id.encode('utf-8') course_id.encode('utf-8')
) )
# Some things we do only if the module is a course # Some things we do only if the content object is a course
if (course_id == module_id): if (course_id == content_id):
data['number'] = module.location.course data['number'] = content.location.course
data['org'] = module.location.org 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: else:
module_uri = '{}/modules/{}'.format(module_uri, module_id) content_uri = '{}/content/{}'.format(content_uri, content_id)
data['uri'] = module_uri data['uri'] = content_uri
return data 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 This should probably evolve to use DRF serializers
""" """
data = [] data = []
if submodules: if children:
for submodule in submodules: for child in children:
submodule_data = _serialize_module( child_data = _serialize_content(
request, request,
course_id, course_id,
submodule child
) )
data.append(submodule_data) data.append(child_data)
return data return data
def _serialize_module_with_children(request, course_descriptor, descriptor, depth): def _serialize_content_groups(request, course_id, content_id, groups):
data = _serialize_module( """
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, request,
course_descriptor.id, course_descriptor.id,
descriptor descriptor
) )
if depth > 0: if depth > 0:
data['modules'] = [] data['children'] = []
for child in descriptor.get_children(): for child in descriptor.get_children():
data['modules'].append(_serialize_module_with_children( data['children'].append(_serialize_content_with_children(
request, request,
course_descriptor, course_descriptor,
child, child,
...@@ -237,32 +248,32 @@ def _parse_updates_html(html): ...@@ -237,32 +248,32 @@ def _parse_updates_html(html):
return result return result
class ModulesList(APIView): class CourseContentList(APIView):
permission_classes = (ApiKeyHeaderPermission,) 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 GET retrieves the list of children for a given content object
We don't know where in the module hierarchy we are -- could even be the top We don't know where in the content hierarchy we are -- could even be the top
""" """
if module_id is None: if content_id is None:
module_id = course_id content_id = course_id
response_data = [] response_data = []
submodule_type = request.QUERY_PARAMS.get('type', None) content_type = request.QUERY_PARAMS.get('type', None)
store = modulestore() store = modulestore()
if course_id != module_id: if course_id != content_id:
try: try:
module = store.get_instance(course_id, Location(module_id)) content = store.get_instance(course_id, Location(content_id))
except InvalidLocationError: except InvalidLocationError:
module = None content = None
else: else:
module = get_course(course_id) content = get_course(course_id)
if module: if content:
submodules = _get_module_submodules(module, submodule_type) children = _get_content_children(content, content_type)
response_data = _serialize_module_submodules( response_data = _serialize_content_children(
request, request,
course_id, course_id,
submodules children
) )
status_code = status.HTTP_200_OK status_code = status.HTTP_200_OK
else: else:
...@@ -270,34 +281,36 @@ class ModulesList(APIView): ...@@ -270,34 +281,36 @@ class ModulesList(APIView):
return Response(response_data, status=status_code) return Response(response_data, status=status_code)
class ModulesDetail(APIView): class CourseContentDetail(APIView):
permission_classes = (ApiKeyHeaderPermission,) 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() store = modulestore()
response_data = {} response_data = {}
submodule_type = request.QUERY_PARAMS.get('type', None) content_type = request.QUERY_PARAMS.get('type', None)
if course_id != module_id: if course_id != content_id:
element_name = 'children'
try: try:
module = store.get_instance(course_id, Location(module_id)) content = store.get_instance(course_id, Location(content_id))
except InvalidLocationError: except InvalidLocationError:
module = None content = None
else: else:
module = get_course(course_id) element_name = 'content'
if module: content = get_course(course_id)
response_data = _serialize_module( if content:
response_data = _serialize_content(
request, request,
course_id, course_id,
module content
) )
submodules = _get_module_submodules(module, submodule_type) children = _get_content_children(content, content_type)
response_data['modules'] = _serialize_module_submodules( response_data[element_name] = _serialize_content_children(
request, request,
course_id, course_id,
submodules children
) )
status_code = status.HTTP_200_OK status_code = status.HTTP_200_OK
else: else:
...@@ -316,7 +329,7 @@ class CoursesList(APIView): ...@@ -316,7 +329,7 @@ class CoursesList(APIView):
store = modulestore() store = modulestore()
course_descriptors = store.get_courses() course_descriptors = store.get_courses()
for course_descriptor in course_descriptors: for course_descriptor in course_descriptors:
course_data = _serialize_module( course_data = _serialize_content(
request, request,
course_descriptor.id, course_descriptor.id,
course_descriptor course_descriptor
...@@ -330,8 +343,8 @@ class CoursesDetail(APIView): ...@@ -330,8 +343,8 @@ class CoursesDetail(APIView):
def get(self, request, course_id): def get(self, request, course_id):
""" """
GET retrieves an existing course from the system and returns summary information about the submodules GET retrieves an existing course from the system and returns
to the specified depth summary information about its content children to the specified depth
""" """
depth = request.QUERY_PARAMS.get('depth', 0) depth = request.QUERY_PARAMS.get('depth', 0)
depth_int = int(depth) depth_int = int(depth)
...@@ -343,14 +356,16 @@ class CoursesDetail(APIView): ...@@ -343,14 +356,16 @@ class CoursesDetail(APIView):
course_descriptor = None course_descriptor = None
if course_descriptor: if course_descriptor:
if depth_int > 0: if depth_int > 0:
response_data = _serialize_module_with_children( response_data = _serialize_content_with_children(
request, request,
course_descriptor, course_descriptor,
course_descriptor, # Primer for recursive function course_descriptor, # Primer for recursive function
depth_int depth_int
) )
response_data['content'] = response_data['children']
response_data.pop('children')
else: else:
response_data = _serialize_module( response_data = _serialize_content(
request, request,
course_descriptor.id, course_descriptor.id,
course_descriptor course_descriptor
...@@ -472,7 +487,7 @@ class CoursesOverview(APIView): ...@@ -472,7 +487,7 @@ class CoursesOverview(APIView):
def get(self, request, course_id): 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"} naming convention {"_id.org":"i4x", "_id.course":<course_num>, "_id.category":"about", "_id.name":"overview"}
""" """
response_data = OrderedDict() response_data = OrderedDict()
...@@ -499,7 +514,7 @@ class CoursesUpdates(APIView): ...@@ -499,7 +514,7 @@ class CoursesUpdates(APIView):
def get(self, request, course_id): 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"} naming convention {"_id.org":"i4x", "_id.course":<course_num>, "_id.category":"course_info", "_id.name":"updates"}
""" """
response_data = OrderedDict() response_data = OrderedDict()
...@@ -685,13 +700,13 @@ class CoursesUsersDetail(APIView): ...@@ -685,13 +700,13 @@ class CoursesUsersDetail(APIView):
user = None user = None
if user and CourseEnrollment.is_enrolled(user, course_id): if user and CourseEnrollment.is_enrolled(user, course_id):
field_data_cache = FieldDataCache([course_descriptor], course_id, user) field_data_cache = FieldDataCache([course_descriptor], course_id, user)
course_module = module_render.get_module( course_content = module_render.get_module(
user, user,
request, request,
course_descriptor.location, course_descriptor.location,
field_data_cache, field_data_cache,
course_id) course_id)
response_data['position'] = course_module.position response_data['position'] = course_content.position
response_status = status.HTTP_200_OK response_status = status.HTTP_200_OK
else: else:
response_status = status.HTTP_404_NOT_FOUND response_status = status.HTTP_404_NOT_FOUND
...@@ -717,3 +732,111 @@ class CoursesUsersDetail(APIView): ...@@ -717,3 +732,111 @@ class CoursesUsersDetail(APIView):
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
response_data['uri'] = base_uri response_data['uri'] = base_uri
return Response(response_data, status=status.HTTP_204_NO_CONTENT) 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)
...@@ -74,7 +74,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -74,7 +74,7 @@ class GroupsApiTests(ModuleStoreTestCase):
return response return response
def test_group_list_post(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
...@@ -83,12 +83,12 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -83,12 +83,12 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertGreater(len(response.data['name']), 0) self.assertGreater(len(response.data['name']), 0)
def test_group_list_get_with_profile(self): def test_group_list_get_with_profile(self):
group_type = 'series'
profile_data = {'display_name': 'My first series'}
data = { data = {
'name': self.test_group_name, 'name': self.test_group_name,
'group_type': 'series', 'type': group_type,
'data': { 'data': profile_data
'display_name': 'My first series'
}
} }
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
...@@ -97,45 +97,41 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -97,45 +97,41 @@ class GroupsApiTests(ModuleStoreTestCase):
# query for list of groups, but don't put the type filter # query for list of groups, but don't put the type filter
test_uri = self.base_groups_uri test_uri = self.base_groups_uri
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 400)
# try again with filter # 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) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1) self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['group_id'], group_id) self.assertEqual(response.data[0]['id'], group_id)
self.assertEqual(response.data[0]['group_type'], 'series') self.assertEqual(response.data[0]['type'], 'series')
self.assertEqual(response.data[0]['name'], self.test_group_name) 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 # 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) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['id'], group_id) self.assertEqual(response.data['id'], group_id)
confirm_uri = self.test_server_prefix + test_uri confirm_uri = self.test_server_prefix + test_uri
self.assertEqual(response.data['uri'], confirm_uri) self.assertEqual(response.data['uri'], confirm_uri)
self.assertEqual(response.data['name'], self.test_group_name) self.assertEqual(response.data['name'], self.test_group_name)
self.assertEqual(response.data['group_type'], 'series') self.assertEqual(response.data['type'], 'series')
self.assertEqual(response.data['data']['display_name'], 'My first series') response_profile_data = response.data['data']
self.assertEqual(response_profile_data['display_name'], 'My first series')
# update the profile # update the profile
profile_data = {'display_name': 'My updated series'}
# first with missing data
response = self.do_post(test_uri, {})
self.assertEqual(response.status_code, 400)
data = { data = {
'name': self.test_group_name, 'name': self.test_group_name,
'group_type': 'seriesX', 'type': 'seriesX',
'data': { 'data': profile_data
'display_name': 'My updated series'
}
} }
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 200)
# requery the filter # requery the filter
test_uri = self.base_groups_uri + '?type=series' test_uri = self.base_groups_uri + '?type=series'
...@@ -147,35 +143,42 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -147,35 +143,42 @@ class GroupsApiTests(ModuleStoreTestCase):
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1) self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['group_id'], group_id) self.assertEqual(response.data[0]['id'], group_id)
self.assertEqual(response.data[0]['group_type'], 'seriesX') self.assertEqual(response.data[0]['type'], 'seriesX')
self.assertEqual(response.data[0]['name'], self.test_group_name) 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): 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': ''} data = {'name': ''}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
def test_group_list_get_uses_base_group_name(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
group_id = response.data['id'] group_id = response.data['id']
profile = GroupProfile.objects.get(group_id=group_id) profile = GroupProfile.objects.get(group_id=group_id)
profile.name = '' profile.name = ''
profile.save() 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.status_code, 200)
self.assertEqual(response.data[0]['name'], '{:04d}: {}'.format(group_id, self.test_group_name)) self.assertEqual(response.data[0]['name'], '{:04d}: {}'.format(group_id, self.test_group_name))
profile.name = None profile.name = None
profile.save() 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.status_code, 200)
self.assertEqual(response.data[0]['name'], '{:04d}: {}'.format(group_id, self.test_group_name)) self.assertEqual(response.data[0]['name'], '{:04d}: {}'.format(group_id, self.test_group_name))
def test_group_detail_get(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
...@@ -189,7 +192,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -189,7 +192,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['name'], self.test_group_name) self.assertEqual(response.data['name'], self.test_group_name)
def test_group_detail_get_uses_base_group_name(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
...@@ -206,7 +209,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -206,7 +209,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['name'], '{:04d}: {}'.format(group_id, self.test_group_name)) self.assertEqual(response.data['name'], '{:04d}: {}'.format(group_id, self.test_group_name))
def test_group_detail_get_with_missing_profile(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
...@@ -226,7 +229,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -226,7 +229,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_detail_post(self): 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) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = response.data['uri'] test_uri = response.data['uri']
...@@ -234,13 +237,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -234,13 +237,13 @@ class GroupsApiTests(ModuleStoreTestCase):
group_type = 'seriesX' group_type = 'seriesX'
data = { data = {
'name': self.test_group_name, 'name': self.test_group_name,
'group_type': group_type, 'type': group_type,
'data': { 'data': {
'display_name': 'My updated series' 'display_name': 'My updated series'
} }
} }
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['id'], group_id) self.assertEqual(response.data['id'], group_id)
self.assertEqual(response.data['name'], self.test_group_name) self.assertEqual(response.data['name'], self.test_group_name)
self.assertEqual(response.data['uri'], test_uri) self.assertEqual(response.data['uri'], test_uri)
...@@ -250,7 +253,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -250,7 +253,7 @@ class GroupsApiTests(ModuleStoreTestCase):
group_type = 'seriesX' group_type = 'seriesX'
data = { data = {
'name': self.test_group_name, 'name': self.test_group_name,
'group_type': group_type, 'type': group_type,
'data': { 'data': {
'display_name': 'My updated series' 'display_name': 'My updated series'
} }
...@@ -269,7 +272,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -269,7 +272,7 @@ class GroupsApiTests(ModuleStoreTestCase):
} }
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = self.base_groups_uri + '/' + str(group_id) test_uri = self.base_groups_uri + '/' + str(group_id)
...@@ -288,7 +291,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -288,7 +291,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data = {'email': self.test_email, 'username': local_username, 'password': self.test_password} data = {'email': self.test_email, 'username': local_username, 'password': self.test_password}
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
test_uri = self.base_groups_uri + '/' + str(response.data['id']) test_uri = self.base_groups_uri + '/' + str(response.data['id'])
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -307,7 +310,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -307,7 +310,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_users_list_post_invalid_user(self): 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) response = self.do_post(self.base_groups_uri, data)
test_uri = '{}/{}/users'.format(self.base_groups_uri, str(response.data['id'])) test_uri = '{}/{}/users'.format(self.base_groups_uri, str(response.data['id']))
data = {'user_id': "98723896"} data = {'user_id': "98723896"}
...@@ -325,7 +328,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -325,7 +328,7 @@ class GroupsApiTests(ModuleStoreTestCase):
} }
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = self.base_groups_uri + '/' + str(group_id) test_uri = self.base_groups_uri + '/' + str(group_id)
...@@ -354,7 +357,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -354,7 +357,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data = {'email': self.test_email, 'username': local_username, 'password': self.test_password} data = {'email': self.test_email, 'username': local_username, 'password': self.test_password}
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = self.base_groups_uri + '/' + str(response.data['id']) test_uri = self.base_groups_uri + '/' + str(response.data['id'])
...@@ -376,7 +379,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -376,7 +379,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data = {'email': self.test_email, 'username': local_username, 'password': self.test_password} data = {'email': self.test_email, 'username': local_username, 'password': self.test_password}
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
test_uri = self.base_groups_uri + '/' + str(response.data['id']) test_uri = self.base_groups_uri + '/' + str(response.data['id'])
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -397,7 +400,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -397,7 +400,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 204) self.assertEqual(response.status_code, 204)
def test_group_users_detail_delete_invalid_user(self): 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) response = self.do_post(self.base_groups_uri, data)
test_uri = self.base_groups_uri + '/' + str(response.data['id']) test_uri = self.base_groups_uri + '/' + str(response.data['id'])
test_uri = test_uri + '/users/123124' test_uri = test_uri + '/users/123124'
...@@ -409,7 +412,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -409,7 +412,7 @@ class GroupsApiTests(ModuleStoreTestCase):
data = {'email': self.test_email, 'username': local_username, 'password': self.test_password} data = {'email': self.test_email, 'username': local_username, 'password': self.test_password}
response = self.do_post(self.base_users_uri, data) response = self.do_post(self.base_users_uri, data)
user_id = response.data['id'] user_id = response.data['id']
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = self.base_groups_uri + '/' + str(group_id) + '/users/' + str(user_id) test_uri = self.base_groups_uri + '/' + str(group_id) + '/users/' + str(user_id)
...@@ -417,13 +420,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -417,13 +420,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_groups_list_post_hierarchical(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -439,13 +442,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -439,13 +442,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['relationship_type'], relationship_type) self.assertEqual(response.data['relationship_type'], relationship_type)
def test_group_groups_list_post_linked(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -461,13 +464,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -461,13 +464,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['relationship_type'], relationship_type) self.assertEqual(response.data['relationship_type'], relationship_type)
def test_group_groups_list_post_linked_duplicate(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -488,13 +491,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -488,13 +491,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_groups_list_post_invalid_relationship_type(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -505,13 +508,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -505,13 +508,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 406) self.assertEqual(response.status_code, 406)
def test_group_groups_list_get(self): 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) bravo_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(bravo_response.status_code, 201) self.assertEqual(bravo_response.status_code, 201)
bravo_group_id = bravo_response.data['id'] bravo_group_id = bravo_response.data['id']
bravo_groups_uri = bravo_response.data['uri'] + '/groups' 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) charlie_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(charlie_response.status_code, 201) self.assertEqual(charlie_response.status_code, 201)
charlie_group_id = charlie_response.data['id'] charlie_group_id = charlie_response.data['id']
...@@ -520,7 +523,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -520,7 +523,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response = self.do_post(bravo_groups_uri, data) response = self.do_post(bravo_groups_uri, data)
self.assertEqual(response.status_code, 201) 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) foxtrot_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(foxtrot_response.status_code, 201) self.assertEqual(foxtrot_response.status_code, 201)
foxtrot_group_id = foxtrot_response.data['id'] foxtrot_group_id = foxtrot_response.data['id']
...@@ -529,7 +532,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -529,7 +532,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response = self.do_post(bravo_groups_uri, data) response = self.do_post(bravo_groups_uri, data)
self.assertEqual(response.status_code, 201) 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) tango_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(tango_response.status_code, 201) self.assertEqual(tango_response.status_code, 201)
tango_group_id = tango_response.data['id'] tango_group_id = tango_response.data['id']
...@@ -554,13 +557,13 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -554,13 +557,13 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(relationship_count, len(group_idlist)) self.assertEqual(relationship_count, len(group_idlist))
def test_group_groups_list_get_with_profile_type(self): 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) bravo_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(bravo_response.status_code, 201) self.assertEqual(bravo_response.status_code, 201)
bravo_group_id = bravo_response.data['id'] bravo_group_id = bravo_response.data['id']
bravo_groups_uri = bravo_response.data['uri'] + '/groups?type=test_group' 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) charlie_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(charlie_response.status_code, 201) self.assertEqual(charlie_response.status_code, 201)
charlie_group_id = charlie_response.data['id'] charlie_group_id = charlie_response.data['id']
...@@ -569,7 +572,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -569,7 +572,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response = self.do_post(bravo_groups_uri, data) response = self.do_post(bravo_groups_uri, data)
self.assertEqual(response.status_code, 201) 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) foxtrot_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(foxtrot_response.status_code, 201) self.assertEqual(foxtrot_response.status_code, 201)
foxtrot_group_id = foxtrot_response.data['id'] foxtrot_group_id = foxtrot_response.data['id']
...@@ -578,7 +581,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -578,7 +581,7 @@ class GroupsApiTests(ModuleStoreTestCase):
response = self.do_post(bravo_groups_uri, data) response = self.do_post(bravo_groups_uri, data)
self.assertEqual(response.status_code, 201) 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) tango_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(tango_response.status_code, 201) self.assertEqual(tango_response.status_code, 201)
tango_uri = tango_response.data['uri'] tango_uri = tango_response.data['uri']
...@@ -607,14 +610,14 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -607,14 +610,14 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_groups_detail_get_hierarchical(self): 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_response = self.do_post(self.base_groups_uri, data)
alpha_group_id = alpha_response.data['id'] alpha_group_id = alpha_response.data['id']
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -634,14 +637,14 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -634,14 +637,14 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['relationship_type'], relationship_type) self.assertEqual(response.data['relationship_type'], relationship_type)
def test_group_groups_detail_get_linked(self): 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_response = self.do_post(self.base_groups_uri, data)
alpha_group_id = alpha_response.data['id'] alpha_group_id = alpha_response.data['id']
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
delta_group_id = delta_response.data['id'] delta_group_id = delta_response.data['id']
self.assertEqual(delta_response.status_code, 201) self.assertEqual(delta_response.status_code, 201)
...@@ -664,7 +667,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -664,7 +667,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['relationship_type'], relationship_type) self.assertEqual(response.data['relationship_type'], relationship_type)
def test_group_groups_detail_get_notfound(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) self.assertEqual(alpha_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups/gaois89sdf98' test_uri = alpha_response.data['uri'] + '/groups/gaois89sdf98'
...@@ -672,16 +675,16 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -672,16 +675,16 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_groups_detail_delete_hierarchical(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) 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) gamma_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(gamma_response.status_code, 201) self.assertEqual(gamma_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -703,16 +706,16 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -703,16 +706,16 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_groups_detail_delete_linked(self): 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) alpha_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(alpha_response.status_code, 201) 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) beta_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(beta_response.status_code, 201) 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_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(delta_response.status_code, 201) 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) gamma_response = self.do_post(self.base_groups_uri, data)
self.assertEqual(gamma_response.status_code, 201) self.assertEqual(gamma_response.status_code, 201)
test_uri = alpha_response.data['uri'] + '/groups' test_uri = alpha_response.data['uri'] + '/groups'
...@@ -737,7 +740,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -737,7 +740,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_courses_list_post(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
group_id = response.data['id'] group_id = response.data['id']
...@@ -751,7 +754,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -751,7 +754,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['course_id'], self.test_course_id) self.assertEqual(response.data['course_id'], self.test_course_id)
def test_group_courses_list_post_duplicate(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
test_uri = response.data['uri'] + '/courses' test_uri = response.data['uri'] + '/courses'
...@@ -768,7 +771,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -768,7 +771,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_courses_list_post_invalid_course(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
test_uri = response.data['uri'] + '/courses' test_uri = response.data['uri'] + '/courses'
...@@ -777,7 +780,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -777,7 +780,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_courses_list_get(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
group_id = response.data['id'] group_id = response.data['id']
...@@ -791,9 +794,9 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -791,9 +794,9 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['course_id'], self.test_course_id) self.assertEqual(response.data['course_id'], self.test_course_id)
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['courses']), 1) self.assertEqual(len(response.data), 1)
self.assertEqual(response.data['courses'][0]['course_id'], self.test_course_id) self.assertEqual(response.data[0]['course_id'], self.test_course_id)
self.assertEqual(response.data['courses'][0]['display_name'], self.course.display_name) self.assertEqual(response.data[0]['display_name'], self.course.display_name)
def test_group_courses_list_get_invalid_group(self): def test_group_courses_list_get_invalid_group(self):
test_uri = self.base_groups_uri + '/1231241/courses' test_uri = self.base_groups_uri + '/1231241/courses'
...@@ -801,7 +804,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -801,7 +804,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_group_courses_detail_get(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
group_id = response.data['id'] group_id = response.data['id']
...@@ -823,7 +826,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -823,7 +826,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['course_id'], self.test_course_id) self.assertEqual(response.data['course_id'], self.test_course_id)
def test_group_courses_detail_delete(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
test_uri = response.data['uri'] + '/courses' test_uri = response.data['uri'] + '/courses'
...@@ -844,7 +847,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -844,7 +847,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 204) self.assertEqual(response.status_code, 204)
def test_group_courses_detail_delete_invalid_course(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
test_uri = response.data['uri'] + '/courses/123124' test_uri = response.data['uri'] + '/courses/123124'
...@@ -852,7 +855,7 @@ class GroupsApiTests(ModuleStoreTestCase): ...@@ -852,7 +855,7 @@ class GroupsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 204) self.assertEqual(response.status_code, 204)
def test_group_courses_detail_get_undefined(self): 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) response = self.do_post(self.base_groups_uri, data)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
test_uri = '{}/courses/{}'.format(response.data['uri'], self.course.id) test_uri = '{}/courses/{}'.format(response.data['uri'], self.course.id)
......
...@@ -20,113 +20,182 @@ from xmodule.modulestore import Location, InvalidLocationError ...@@ -20,113 +20,182 @@ from xmodule.modulestore import Location, InvalidLocationError
RELATIONSHIP_TYPES = {'hierarchical': 'h', 'graph': 'g'} 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 Constructs the protocol:host:path component of the resource uri
""" """
protocol = 'http' protocol = 'http'
if request.is_secure(): if request.is_secure():
protocol = protocol + 's' protocol = protocol + 's'
if include_query_string:
path_to_use = request.get_full_path()
else:
path_to_use = request.path_info
resource_uri = '{}://{}{}'.format( resource_uri = '{}://{}{}'.format(
protocol, protocol,
request.get_host(), request.get_host(),
request.get_full_path() path_to_use
) )
return resource_uri return resource_uri
class GroupsList(APIView): 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,) permissions_classes = (ApiKeyHeaderPermission,)
def post(self, request): 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 = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
# Group name must be unique, but we need to support dupes # Group name must be unique, but we need to support dupes
group = Group.objects.create(name=str(uuid.uuid4())) group = Group.objects.create(name=str(uuid.uuid4()))
if request.DATA.get('name'): original_group_name = request.DATA.get('name', None)
original_group_name = request.DATA.get('name') if original_group_name is None or len(original_group_name) == 0:
else:
return Response(response_data, status=status.HTTP_400_BAD_REQUEST) return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
group.name = '{:04d}: {}'.format(group.id, original_group_name) group.name = '{:04d}: {}'.format(group.id, original_group_name)
group.record_active = True group.record_active = True
group.record_date_created = timezone.now()
group.record_date_modified = timezone.now()
group.save() group.save()
# Create a corresponding relationship management record # Create a corresponding relationship management record
GroupRelationship.objects.create(group_id=group.id, parent_group=None) GroupRelationship.objects.create(group_id=group.id, parent_group=None)
# Create a corresponding profile record (for extra meta info) # Create a corresponding profile record (for extra meta info)
group_type = request.DATA.get('group_type', None) data = request.DATA.get('data', {})
data = json.dumps(request.DATA.get('data')) if request.DATA.get('data') else {} profile, _ = GroupProfile.objects.get_or_create(
profile, _ = GroupProfile.objects.get_or_create(group_id=group.id, group_type=group_type, name=original_group_name, data=data) group_id=group.id,
group_type=group_type,
name=original_group_name,
data=json.dumps(data)
)
response_data = { response_data = {
'id': group.id, 'id': group.id,
'name': original_group_name, 'name': profile.name,
'type': group_type,
} }
base_uri = _generate_base_uri(request) 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 response_status = status.HTTP_201_CREATED
return Response(response_data, status=response_status) return Response(response_data, status=response_status)
def get(self, request): def get(self, request):
""" """
GET retrieves a list of groups in the system filtered by type GET /api/groups
""" """
response_data = [] response_data = []
if 'type' in request.GET: group_type = request.QUERY_PARAMS.get('type', None)
profiles = GroupProfile.objects.filter(group_type=request.GET['type']) if group_type is None:
else: return Response({}, status=status.HTTP_400_BAD_REQUEST)
profiles = GroupProfile.objects.all() profiles = GroupProfile.objects.filter(group_type=request.GET['type']).select_related('group')
for profile in profiles: for profile in profiles:
item_data = {} item_data = {}
item_data['group_id'] = profile.group_id item_data['id'] = profile.group_id
if profile.name and len(profile.name): if profile.name and len(profile.name):
group_name = profile.name group_name = profile.name
else: else:
group = Group.objects.get(id=profile.group_id) group_name = profile.group.name
group_name = group.name
item_data['name'] = group_name item_data['name'] = group_name
if profile.group_type: item_data['type'] = profile.group_type
item_data['group_type'] = profile.group_type
if profile.data: if profile.data:
item_data['data'] = json.loads(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) response_data.append(item_data)
return Response(response_data, status=status.HTTP_200_OK) return Response(response_data, status=status.HTTP_200_OK)
class GroupsDetail(APIView): 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,) permission_classes = (ApiKeyHeaderPermission,)
def post(self, request, group_id): def post(self, request, group_id):
"""
POST /api/groups/{group_id}
"""
response_data = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
print base_uri
try: try:
existing_group = Group.objects.get(id=group_id) existing_group = Group.objects.get(id=group_id)
except ObjectDoesNotExist: except ObjectDoesNotExist:
return Response({}, status.HTTP_404_NOT_FOUND) 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, _ = GroupProfile.objects.get_or_create(group_id=group_id)
profile.group_type = group_type group_type = request.DATA.get('type', None)
profile.data = data if group_type:
profile.group_type = group_type
data = request.DATA.get('data', None)
if data:
profile.data = json.dumps(data)
profile.save() profile.save()
response_data['id'] = existing_group.id response_data['id'] = existing_group.id
response_data['name'] = profile.name response_data['name'] = profile.name
response_data['type'] = profile.group_type
response_data['uri'] = _generate_base_uri(request) response_data['uri'] = _generate_base_uri(request)
return Response(response_data, status=status.HTTP_201_CREATED) return Response(response_data, status=status.HTTP_200_OK)
def get(self, request, group_id): def get(self, request, group_id):
""" """
GET retrieves an existing group from the system GET /api/groups/{group_id}
""" """
response_data = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
...@@ -141,6 +210,8 @@ class GroupsDetail(APIView): ...@@ -141,6 +210,8 @@ class GroupsDetail(APIView):
response_data['resources'].append({'uri': resource_uri}) response_data['resources'].append({'uri': resource_uri})
resource_uri = '{}/groups'.format(base_uri) resource_uri = '{}/groups'.format(base_uri)
response_data['resources'].append({'uri': resource_uri}) response_data['resources'].append({'uri': resource_uri})
resource_uri = '{}/courses'.format(base_uri)
response_data['resources'].append({'uri': resource_uri})
try: try:
group_profile = GroupProfile.objects.get(group_id=group_id) group_profile = GroupProfile.objects.get(group_id=group_id)
except ObjectDoesNotExist: except ObjectDoesNotExist:
...@@ -151,7 +222,7 @@ class GroupsDetail(APIView): ...@@ -151,7 +222,7 @@ class GroupsDetail(APIView):
else: else:
response_data['name'] = existing_group.name response_data['name'] = existing_group.name
if group_profile.group_type: if group_profile.group_type:
response_data['group_type'] = group_profile.group_type response_data['type'] = group_profile.group_type
data = group_profile.data data = group_profile.data
if data: if data:
response_data['data'] = json.loads(data) response_data['data'] = json.loads(data)
...@@ -161,11 +232,27 @@ class GroupsDetail(APIView): ...@@ -161,11 +232,27 @@ class GroupsDetail(APIView):
class GroupsUsersList(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,) permission_classes = (ApiKeyHeaderPermission,)
def post(self, request, group_id): 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) base_uri = _generate_base_uri(request)
try: try:
...@@ -178,7 +265,7 @@ class GroupsUsersList(APIView): ...@@ -178,7 +265,7 @@ class GroupsUsersList(APIView):
except ObjectDoesNotExist: except ObjectDoesNotExist:
return Response({}, status.HTTP_404_NOT_FOUND) return Response({}, status.HTTP_404_NOT_FOUND)
try: try:
existing_relationship = Group.objects.get(user=existing_user) existing_relationship = Group.objects.filter(id=existing_group.id).get(user=existing_user)
except ObjectDoesNotExist: except ObjectDoesNotExist:
existing_relationship = None existing_relationship = None
response_data = {} response_data = {}
...@@ -196,7 +283,7 @@ class GroupsUsersList(APIView): ...@@ -196,7 +283,7 @@ class GroupsUsersList(APIView):
def get(self, request, group_id): def get(self, request, group_id):
""" """
GET retrieves the list of users related to the specified group GET /api/groups/{group_id}/users/
""" """
try: try:
existing_group = Group.objects.get(id=group_id) existing_group = Group.objects.get(id=group_id)
...@@ -218,11 +305,20 @@ class GroupsUsersList(APIView): ...@@ -218,11 +305,20 @@ class GroupsUsersList(APIView):
class GroupsUsersDetail(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,) permission_classes = (ApiKeyHeaderPermission,)
def get(self, request, group_id, user_id): 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 = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
...@@ -255,11 +351,41 @@ class GroupsUsersDetail(APIView): ...@@ -255,11 +351,41 @@ class GroupsUsersDetail(APIView):
class GroupsGroupsList(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,) permission_classes = (ApiKeyHeaderPermission,)
def post(self, request, group_id): 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 = {} response_data = {}
to_group_id = request.DATA['group_id'] to_group_id = request.DATA['group_id']
...@@ -291,7 +417,7 @@ class GroupsGroupsList(APIView): ...@@ -291,7 +417,7 @@ class GroupsGroupsList(APIView):
def get(self, request, group_id): 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: try:
from_group_relationship = GroupRelationship.objects.get(group__id=group_id) from_group_relationship = GroupRelationship.objects.get(group__id=group_id)
...@@ -329,11 +455,22 @@ class GroupsGroupsList(APIView): ...@@ -329,11 +455,22 @@ class GroupsGroupsList(APIView):
class GroupsGroupsDetail(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,) permission_classes = (ApiKeyHeaderPermission,)
def get(self, request, group_id, related_group_id): 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 = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
...@@ -357,7 +494,7 @@ class GroupsGroupsDetail(APIView): ...@@ -357,7 +494,7 @@ class GroupsGroupsDetail(APIView):
def delete(self, request, group_id, related_group_id): 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: try:
from_group_relationship = GroupRelationship.objects.get(group__id=group_id) from_group_relationship = GroupRelationship.objects.get(group__id=group_id)
...@@ -384,11 +521,26 @@ class GroupsGroupsDetail(APIView): ...@@ -384,11 +521,26 @@ class GroupsGroupsDetail(APIView):
class GroupsCoursesList(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,) permission_classes = (ApiKeyHeaderPermission,)
def post(self, request, group_id): 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 = {} response_data = {}
try: try:
...@@ -422,7 +574,7 @@ class GroupsCoursesList(APIView): ...@@ -422,7 +574,7 @@ class GroupsCoursesList(APIView):
def get(self, request, group_id): 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 = {} response_data = {}
try: try:
...@@ -431,24 +583,36 @@ class GroupsCoursesList(APIView): ...@@ -431,24 +583,36 @@ class GroupsCoursesList(APIView):
return Response({}, status.HTTP_404_NOT_FOUND) return Response({}, status.HTTP_404_NOT_FOUND)
store = modulestore() store = modulestore()
members = CourseGroupRelationship.objects.filter(group=existing_group) members = CourseGroupRelationship.objects.filter(group=existing_group)
response_data['courses'] = [] response_data = []
for member in members: for member in members:
course = store.get_course(member.course_id) course = store.get_course(member.course_id)
course_data = { course_data = {
'course_id': member.course_id, 'course_id': member.course_id,
'display_name': course.display_name 'display_name': course.display_name
} }
response_data['courses'].append(course_data) response_data.append(course_data)
response_status = status.HTTP_200_OK response_status = status.HTTP_200_OK
return Response(response_data, status=response_status) return Response(response_data, status=response_status)
class GroupsCoursesDetail(APIView): 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,) permission_classes = (ApiKeyHeaderPermission,)
def get(self, request, group_id, course_id): 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 = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
...@@ -469,7 +633,7 @@ class GroupsCoursesDetail(APIView): ...@@ -469,7 +633,7 @@ class GroupsCoursesDetail(APIView):
def delete(self, request, group_id, course_id): 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: try:
existing_group = Group.objects.get(id=group_id) existing_group = Group.objects.get(id=group_id)
......
# -*- 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']
# -*- 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
...@@ -106,3 +106,20 @@ class GroupProfile(TimeStampedModel): ...@@ -106,3 +106,20 @@ class GroupProfile(TimeStampedModel):
name = models.CharField(max_length=255, null=True, blank=True) name = models.CharField(max_length=255, null=True, blank=True)
data = models.TextField(blank=True) # JSON dictionary for generic key/value pairs data = models.TextField(blank=True) # JSON dictionary for generic key/value pairs
record_active = models.BooleanField(default=True) 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")
...@@ -239,7 +239,7 @@ class UsersApiTests(TestCase): ...@@ -239,7 +239,7 @@ class UsersApiTests(TestCase):
def test_user_groups_list_post(self): def test_user_groups_list_post(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -262,7 +262,7 @@ class UsersApiTests(TestCase): ...@@ -262,7 +262,7 @@ class UsersApiTests(TestCase):
def test_user_groups_list_post_duplicate(self): def test_user_groups_list_post_duplicate(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -281,7 +281,7 @@ class UsersApiTests(TestCase): ...@@ -281,7 +281,7 @@ class UsersApiTests(TestCase):
def test_user_groups_list_post_invalid_user(self): def test_user_groups_list_post_invalid_user(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users/897698769/groups' test_uri = '/api/users/897698769/groups'
...@@ -292,7 +292,7 @@ class UsersApiTests(TestCase): ...@@ -292,7 +292,7 @@ class UsersApiTests(TestCase):
def test_user_groups_list_get(self): def test_user_groups_list_get(self):
test_uri = '/api/groups' test_uri = '/api/groups'
group_name = 'Alpha Group' group_name = 'Alpha Group'
data = {'name': group_name} data = {'name': group_name, 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -327,7 +327,7 @@ class UsersApiTests(TestCase): ...@@ -327,7 +327,7 @@ class UsersApiTests(TestCase):
group_url = '/api/groups' group_url = '/api/groups'
group_name = 'Alpha Group' group_name = 'Alpha Group'
data = {'name': group_name, 'group_type': 'Engineer'} data = {'name': group_name, 'type': 'Engineer'}
response = self.do_post(group_url, data) response = self.do_post(group_url, data)
group_id = response.data['id'] group_id = response.data['id']
user_groups_uri = '{}/groups'.format(test_uri) user_groups_uri = '{}/groups'.format(test_uri)
...@@ -336,7 +336,7 @@ class UsersApiTests(TestCase): ...@@ -336,7 +336,7 @@ class UsersApiTests(TestCase):
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
group_name = 'Beta Group' group_name = 'Beta Group'
data = {'name': group_name, 'group_type': 'Architect'} data = {'name': group_name, 'type': 'Architect'}
response = self.do_post(group_url, data) response = self.do_post(group_url, data)
group_id = response.data['id'] group_id = response.data['id']
data = {'group_id': group_id} data = {'group_id': group_id}
...@@ -367,7 +367,7 @@ class UsersApiTests(TestCase): ...@@ -367,7 +367,7 @@ class UsersApiTests(TestCase):
def test_user_groups_detail_get(self): def test_user_groups_detail_get(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -390,7 +390,7 @@ class UsersApiTests(TestCase): ...@@ -390,7 +390,7 @@ class UsersApiTests(TestCase):
def test_user_groups_detail_delete(self): def test_user_groups_detail_delete(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -419,7 +419,7 @@ class UsersApiTests(TestCase): ...@@ -419,7 +419,7 @@ class UsersApiTests(TestCase):
def test_user_groups_detail_get_undefined(self): def test_user_groups_detail_get_undefined(self):
test_uri = '/api/groups' test_uri = '/api/groups'
data = {'name': 'Alpha Group'} data = {'name': 'Alpha Group', 'type': 'test'}
response = self.do_post(test_uri, data) response = self.do_post(test_uri, data)
group_id = response.data['id'] group_id = response.data['id']
test_uri = '/api/users' test_uri = '/api/users'
...@@ -529,8 +529,8 @@ class UsersApiTests(TestCase): ...@@ -529,8 +529,8 @@ class UsersApiTests(TestCase):
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
position_data = { position_data = {
'position': { 'position': {
'parent_module_id': str(course.id), 'parent_content_id': str(course.id),
'child_module_id': str(chapter3.location) 'child_content_id': str(chapter3.location)
} }
} }
...@@ -551,15 +551,15 @@ class UsersApiTests(TestCase): ...@@ -551,15 +551,15 @@ class UsersApiTests(TestCase):
test_uri = '/api/users/{}/courses/{}'.format(str(user_id), course_id) test_uri = '/api/users/{}/courses/{}'.format(str(user_id), course_id)
position_data = { position_data = {
'position': { 'position': {
'parent_module_id': course_id, 'parent_content_id': course_id,
'child_module_id': str(chapter1.location) 'child_content_id': str(chapter1.location)
} }
} }
response = self.do_post(test_uri, data=position_data) response = self.do_post(test_uri, data=position_data)
self.assertEqual(response.status_code, 404) 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() course = CourseFactory.create()
test_data = '<html>{}</html>'.format(str(uuid.uuid4())) test_data = '<html>{}</html>'.format(str(uuid.uuid4()))
chapter1 = ItemFactory.create( chapter1 = ItemFactory.create(
...@@ -581,8 +581,8 @@ class UsersApiTests(TestCase): ...@@ -581,8 +581,8 @@ class UsersApiTests(TestCase):
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
position_data = { position_data = {
'position': { 'position': {
'parent_module_id': str(course.location), 'parent_content_id': str(course.location),
'child_module_id': str(chapter1.location) 'child_content_id': str(chapter1.location)
} }
} }
...@@ -617,8 +617,8 @@ class UsersApiTests(TestCase): ...@@ -617,8 +617,8 @@ class UsersApiTests(TestCase):
self.assertEqual(response.data['user_id'], user_id) self.assertEqual(response.data['user_id'], user_id)
position_data = { position_data = {
'position': { 'position': {
'parent_module_id': str(course.location), 'parent_content_id': str(course.location),
'child_module_id': str(chapter1.location) 'child_content_id': str(chapter1.location)
} }
} }
......
...@@ -31,6 +31,7 @@ from util.bad_request_rate_limiter import BadRequestRateLimiter ...@@ -31,6 +31,7 @@ from util.bad_request_rate_limiter import BadRequestRateLimiter
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
AUDIT_LOG = logging.getLogger("audit") AUDIT_LOG = logging.getLogger("audit")
def _generate_base_uri(request): def _generate_base_uri(request):
""" """
Constructs the protocol:host:path component of the resource uri Constructs the protocol:host:path component of the resource uri
...@@ -57,6 +58,7 @@ def _serialize_user_profile(response_data, user_profile): ...@@ -57,6 +58,7 @@ def _serialize_user_profile(response_data, user_profile):
return response_data return response_data
def _serialize_user(response_data, user): def _serialize_user(response_data, user):
""" """
Loads the object data into the response dict Loads the object data into the response dict
...@@ -70,14 +72,15 @@ def _serialize_user(response_data, user): ...@@ -70,14 +72,15 @@ def _serialize_user(response_data, user):
response_data['is_active'] = user.is_active response_data['is_active'] = user.is_active
return response_data 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 Records the indicated position for the specified course
Really no reason to generalize this out of user_courses_detail aside from pylint complaining Really no reason to generalize this out of user_courses_detail aside from pylint complaining
""" """
field_data_cache = FieldDataCache([course_descriptor], course_id, user) field_data_cache = FieldDataCache([course_descriptor], course_id, user)
if course_id == position['parent_module_id']: if course_id == position['parent_content_id']:
parent_module = get_module_for_descriptor( parent_content = get_module_for_descriptor(
user, user,
request, request,
course_descriptor, course_descriptor,
...@@ -85,23 +88,23 @@ def _save_module_position(request, user, course_id, course_descriptor, position) ...@@ -85,23 +88,23 @@ def _save_module_position(request, user, course_id, course_descriptor, position)
course_id course_id
) )
else: else:
parent_module = module_render.get_module( parent_content = module_render.get_module(
user, user,
request, request,
position['parent_module_id'], position['parent_content_id'],
field_data_cache, field_data_cache,
course_id course_id
) )
child_module = module_render.get_module( child_content = module_render.get_module(
user, user,
request, request,
position['child_module_id'], position['child_content_id'],
field_data_cache, field_data_cache,
course_id course_id
) )
save_child_position(parent_module, child_module.location.name) save_child_position(parent_content, child_content.location.name)
saved_module = get_current_child(parent_module) saved_content = get_current_child(parent_content)
return saved_module.id return saved_content.id
class UsersList(APIView): class UsersList(APIView):
...@@ -539,7 +542,7 @@ class UsersCoursesDetail(APIView): ...@@ -539,7 +542,7 @@ class UsersCoursesDetail(APIView):
response_data['course_id'] = course_id response_data['course_id'] = course_id
response_status = status.HTTP_201_CREATED response_status = status.HTTP_201_CREATED
if request.DATA['position']: if request.DATA['position']:
response_data['position'] = _save_module_position( response_data['position'] = _save_content_position(
request, request,
user, user,
course_id, course_id,
...@@ -568,13 +571,13 @@ class UsersCoursesDetail(APIView): ...@@ -568,13 +571,13 @@ class UsersCoursesDetail(APIView):
response_data['course_id'] = course_id response_data['course_id'] = course_id
response_data['uri'] = base_uri response_data['uri'] = base_uri
field_data_cache = FieldDataCache([course_descriptor], course_id, user) field_data_cache = FieldDataCache([course_descriptor], course_id, user)
course_module = module_render.get_module( course_content = module_render.get_module(
user, user,
request, request,
course_descriptor.location, course_descriptor.location,
field_data_cache, field_data_cache,
course_id) course_id)
response_data['position'] = course_module.position response_data['position'] = course_content.position
response_status = status.HTTP_200_OK response_status = status.HTTP_200_OK
else: else:
response_status = status.HTTP_404_NOT_FOUND response_status = status.HTTP_404_NOT_FOUND
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment