Commit cd121cf4 by Chris Dodge

initial implementation

parent eda70ad7
...@@ -12,6 +12,6 @@ urlpatterns = patterns( ...@@ -12,6 +12,6 @@ urlpatterns = patterns(
url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/modules/*$', 'modules_list'), url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/modules/*$', 'modules_list'),
url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/groups/(?P<group_id>[0-9]+)$', 'courses_groups_detail'), url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/groups/(?P<group_id>[0-9]+)$', 'courses_groups_detail'),
url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/groups/*$', 'courses_groups_list'), url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/groups/*$', 'courses_groups_list'),
url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/about$', 'course_about'), url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)/overview$', 'course_overview'),
url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)$', 'courses_detail'), url(r'^(?P<course_id>[a-zA-Z0-9/_:]+)$', 'courses_detail'),
) )
...@@ -221,6 +221,7 @@ def courses_detail(request, course_id): ...@@ -221,6 +221,7 @@ def courses_detail(request, course_id):
return Response(response_data, status=status_code) return Response(response_data, status=status_code)
<<<<<<< HEAD
@api_view(['POST']) @api_view(['POST'])
@permission_classes((ApiKeyHeaderPermission,)) @permission_classes((ApiKeyHeaderPermission,))
def courses_groups_list(request, course_id): def courses_groups_list(request, course_id):
...@@ -302,6 +303,19 @@ def courses_groups_detail(request, course_id, group_id): ...@@ -302,6 +303,19 @@ def courses_groups_detail(request, course_id, group_id):
def _parse_about_html(html): def _parse_about_html(html):
=======
def _inner_content(tag):
"""
Helper method
"""
inner_content = None
if tag is not None:
inner_content = u''.join(etree.tostring(e) for e in tag)
return inner_content
def _parse_overview_html(html):
>>>>>>> initial implementation
""" """
Helper method to break up the course about HTML into components Helper method to break up the course about HTML into components
""" """
...@@ -346,11 +360,11 @@ def _parse_about_html(html): ...@@ -346,11 +360,11 @@ def _parse_about_html(html):
if bio_html: if bio_html:
article_data['bio'] = bio_html article_data['bio'] = bio_html
else: else:
article_data['body'] = etree.tostring(article) article_data['body'] = _inner_content(article)
section_data['articles'].append(article_data) section_data['articles'].append(article_data)
else: else:
section_data['body'] = etree.tostring(section) section_data['body'] = _inner_content(section)
result.append(section_data) result.append(section_data)
...@@ -359,7 +373,7 @@ def _parse_about_html(html): ...@@ -359,7 +373,7 @@ def _parse_about_html(html):
@api_view(['GET']) @api_view(['GET'])
@permission_classes((ApiKeyHeaderPermission,)) @permission_classes((ApiKeyHeaderPermission,))
def course_about(request, course_id): def course_overview(request, course_id):
""" """
GET retrieves the course overview module, which - in MongoDB - is stored with the following GET retrieves the course overview module, 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"}
...@@ -374,8 +388,11 @@ def course_about(request, course_id): ...@@ -374,8 +388,11 @@ def course_about(request, course_id):
overview = get_course_about_section(course_module, 'overview') overview = get_course_about_section(course_module, 'overview')
if request.GET.get('parsed'): if request.GET.get('parse') and request.GET.get('parse') in ['True', 'true']:
response_data['sections'] = _parse_about_html(overview) try:
response_data['sections'] = _parse_overview_html(overview)
except:
return Response({'err': 'could_not_parse'}, status=status.HTTP_409_CONFLICT)
else: else:
response_data['overview_html'] = overview response_data['overview_html'] = overview
......
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
Run these tests @ Devstack: Run these tests @ Devstack:
rake fasttest_lms[common/djangoapps/api_manager/tests/test_group_views.py] rake fasttest_lms[common/djangoapps/api_manager/tests/test_group_views.py]
""" """
<<<<<<< HEAD
import simplejson as json import simplejson as json
import unittest import unittest
=======
>>>>>>> initial implementation
import uuid import uuid
from django.conf import settings from textwrap import dedent
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase, Client from django.test import TestCase, Client
from django.test.utils import override_settings from django.test.utils import override_settings
...@@ -19,6 +23,45 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory ...@@ -19,6 +23,45 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
TEST_API_KEY = str(uuid.uuid4()) TEST_API_KEY = str(uuid.uuid4())
TEST_COURSE_OVERVIEW_CONTENT = dedent("""
<section class="about">
<h2>About This Course</h2>
<p>Include your long course description here. The long course description should contain 150-400 words.</p>
<p>This is paragraph 2 of the long course description. Add more paragraphs as needed. Make sure to enclose them in paragraph tags.</p>
</section>
<section class="prerequisites">
<h2>Prerequisites</h2>
<p>Add information about course prerequisites here.</p>
</section>
<section class="course-staff">
<h2>Course Staff</h2>
<article class="teacher">
<div class="teacher-image">
<img src="/images/pl-faculty.png" align="left" style="margin:0 20 px 0" alt="Course Staff Image #1">
</div>
<h3>Staff Member #1</h3>
<p>Biography of instructor/staff member #1</p>
</article>
<article class="teacher">
<div class="teacher-image">
<img src="/images/pl-faculty.png" align="left" style="margin:0 20 px 0" alt="Course Staff Image #2">
</div>
<h3>Staff Member #2</h3>
<p>Biography of instructor/staff member #2</p>
</article>
</section>
<section class="faq">
<p>Some text here</p>
</section>
""")
class SecureClient(Client): class SecureClient(Client):
""" Django test client using a "secure" connection. """ """ Django test client using a "secure" connection. """
...@@ -34,6 +77,7 @@ class CoursesApiTests(TestCase): ...@@ -34,6 +77,7 @@ class CoursesApiTests(TestCase):
""" Test suite for Courses API views """ """ Test suite for Courses API views """
def setUp(self): def setUp(self):
self.maxDiff = 3000
self.test_server_prefix = 'https://testserver' self.test_server_prefix = 'https://testserver'
self.base_courses_uri = '/api/courses' self.base_courses_uri = '/api/courses'
self.base_groups_uri = '/api/groups' self.base_groups_uri = '/api/groups'
...@@ -63,6 +107,13 @@ class CoursesApiTests(TestCase): ...@@ -63,6 +107,13 @@ class CoursesApiTests(TestCase):
display_name="Video_Resources" display_name="Video_Resources"
) )
self.overview = ItemFactory.create(
category="about",
parent_location=self.course.location,
data=TEST_COURSE_OVERVIEW_CONTENT,
display_name="overview"
)
self.test_course_id = self.course.id self.test_course_id = self.course.id
self.test_course_name = self.course.display_name self.test_course_name = self.course.display_name
self.test_course_number = self.course.number self.test_course_number = self.course.number
...@@ -86,6 +137,7 @@ class CoursesApiTests(TestCase): ...@@ -86,6 +137,7 @@ class CoursesApiTests(TestCase):
response = self.client.get(uri, headers=headers) response = self.client.get(uri, headers=headers)
return response return response
<<<<<<< HEAD
def do_post(self, uri, data): def do_post(self, uri, data):
"""Submit an HTTP POST request""" """Submit an HTTP POST request"""
headers = { headers = {
...@@ -109,6 +161,8 @@ class CoursesApiTests(TestCase): ...@@ -109,6 +161,8 @@ class CoursesApiTests(TestCase):
return response return response
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
=======
>>>>>>> initial implementation
def test_course_list_get(self): def test_course_list_get(self):
test_uri = self.base_courses_uri test_uri = self.base_courses_uri
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -125,7 +179,6 @@ class CoursesApiTests(TestCase): ...@@ -125,7 +179,6 @@ class CoursesApiTests(TestCase):
matched_course = True matched_course = True
self.assertTrue(matched_course) self.assertTrue(matched_course)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_course_detail_get(self): def test_course_detail_get(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id test_uri = self.base_courses_uri + '/' + self.test_course_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -139,13 +192,11 @@ class CoursesApiTests(TestCase): ...@@ -139,13 +192,11 @@ class CoursesApiTests(TestCase):
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['modules']), 0)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_course_detail_get_notfound(self): def test_course_detail_get_notfound(self):
test_uri = self.base_courses_uri + '/' + 'p29038cvp9hjwefion' test_uri = self.base_courses_uri + '/' + 'p29038cvp9hjwefion'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_chapter_list_get(self): def test_chapter_list_get(self):
test_uri = self.base_chapters_uri test_uri = self.base_chapters_uri
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -161,7 +212,6 @@ class CoursesApiTests(TestCase): ...@@ -161,7 +212,6 @@ class CoursesApiTests(TestCase):
matched_chapter = True matched_chapter = True
self.assertTrue(matched_chapter) self.assertTrue(matched_chapter)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
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_modules_uri + '/' + self.test_chapter_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -172,7 +222,6 @@ class CoursesApiTests(TestCase): ...@@ -172,7 +222,6 @@ class CoursesApiTests(TestCase):
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['modules']), 0)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_modules_list_get(self): def test_modules_list_get(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id test_uri = self.base_modules_uri + '/' + self.test_module_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -188,7 +237,6 @@ class CoursesApiTests(TestCase): ...@@ -188,7 +237,6 @@ class CoursesApiTests(TestCase):
matched_submodule = True matched_submodule = True
self.assertTrue(matched_submodule) self.assertTrue(matched_submodule)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_modules_detail_get(self): def test_modules_detail_get(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id test_uri = self.base_modules_uri + '/' + self.test_module_id
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -199,13 +247,11 @@ class CoursesApiTests(TestCase): ...@@ -199,13 +247,11 @@ class CoursesApiTests(TestCase):
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['modules']), 0)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_modules_detail_get_notfound(self): def test_modules_detail_get_notfound(self):
test_uri = self.base_modules_uri + '/' + '2p38fp2hjfp9283' test_uri = self.base_modules_uri + '/' + '2p38fp2hjfp9283'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_modules_list_get_filtered_submodules_for_module(self): def test_modules_list_get_filtered_submodules_for_module(self):
test_uri = self.base_modules_uri + '/' + self.test_module_id + '/submodules?type=video' test_uri = self.base_modules_uri + '/' + self.test_module_id + '/submodules?type=video'
response = self.do_get(test_uri) response = self.do_get(test_uri)
...@@ -219,13 +265,11 @@ class CoursesApiTests(TestCase): ...@@ -219,13 +265,11 @@ class CoursesApiTests(TestCase):
matched_submodule = True matched_submodule = True
self.assertTrue(matched_submodule) self.assertTrue(matched_submodule)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_modules_list_get_notfound(self): def test_modules_list_get_notfound(self):
test_uri = self.base_modules_uri + '/2p38fp2hjfp9283/submodules?type=video' test_uri = self.base_modules_uri + '/2p38fp2hjfp9283/submodules?type=video'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_course_groups_list_post(self): def test_course_groups_list_post(self):
data = {'name': self.test_group_name} data = {'name': self.test_group_name}
...@@ -311,3 +355,46 @@ class CoursesApiTests(TestCase): ...@@ -311,3 +355,46 @@ class CoursesApiTests(TestCase):
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)
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_get_course_overview_unparsed(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/overview'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
self.assertEqual(response.data['overview_html'], self.overview.data)
def _find_item_by_class(self, items, class_name):
for item in items:
if item['class'] == class_name:
return item
return None
def test_get_course_overview_parsed(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/overview?parse=true'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
sections = response.data['sections']
self.assertEqual(len(sections), 4)
self.assertIsNotNone(self._find_item_by_class(sections, 'about'))
self.assertIsNotNone(self._find_item_by_class(sections, 'prerequisites'))
self.assertIsNotNone(self._find_item_by_class(sections, 'course-staff'))
self.assertIsNotNone(self._find_item_by_class(sections, 'faq'))
course_staff = self._find_item_by_class(sections, 'course-staff')
teachers = course_staff['articles']
self.assertEqual(len(teachers), 2)
self.assertEqual(teachers[0]['name'], "Staff Member #1")
self.assertEqual(teachers[0]['image_src'], "/images/pl-faculty.png")
self.assertIn("<p>Biography of instructor/staff member #1</p>", teachers[0]['bio'])
self.assertEqual(teachers[1]['name'], "Staff Member #2")
self.assertEqual(teachers[1]['image_src'], "/images/pl-faculty.png")
self.assertIn("<p>Biography of instructor/staff member #2</p>", teachers[1]['bio'])
about = self._find_item_by_class(sections, 'about')
self.assertGreater(len(about['body']), 0)
prerequisites = self._find_item_by_class(sections, 'prerequisites')
self.assertGreater(len(prerequisites['body']), 0)
faq = self._find_item_by_class(sections, 'faq')
self.assertGreater(len(faq['body']), 0)
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