Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
812b0be5
Commit
812b0be5
authored
Apr 19, 2014
by
Chris Dodge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
get course updates API method
parent
cd121cf4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
189 additions
and
46 deletions
+189
-46
lms/djangoapps/api_manager/courses_urls.py
+1
-0
lms/djangoapps/api_manager/courses_views.py
+86
-5
lms/djangoapps/api_manager/tests/content.py
+69
-0
lms/djangoapps/api_manager/tests/test_courses_views.py
+33
-41
No files found.
lms/djangoapps/api_manager/courses_urls.py
View file @
812b0be5
...
...
@@ -13,5 +13,6 @@ urlpatterns = patterns(
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/_:]+)/overview$'
,
'course_overview'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9/_:]+)/updates$'
,
'course_updates'
),
url
(
r'^(?P<course_id>[a-zA-Z0-9/_:]+)$'
,
'courses_detail'
),
)
lms/djangoapps/api_manager/courses_views.py
View file @
812b0be5
...
...
@@ -5,6 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from
lxml
import
etree
from
StringIO
import
StringIO
from
collections
import
OrderedDict
import
logging
from
rest_framework
import
status
from
rest_framework.decorators
import
api_view
,
permission_classes
...
...
@@ -15,7 +16,9 @@ from api_manager.models import CourseGroupRelationship
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
Location
,
InvalidLocationError
from
courseware.courses
import
get_course_about_section
from
courseware.courses
import
get_course_about_section
,
get_course_info_section
log
=
logging
.
getLogger
(
__name__
)
def
_generate_base_uri
(
request
):
"""
...
...
@@ -310,7 +313,9 @@ def _inner_content(tag):
"""
inner_content
=
None
if
tag
is
not
None
:
inner_content
=
u''
.
join
(
etree
.
tostring
(
e
)
for
e
in
tag
)
inner_content
=
tag
.
text
if
tag
.
text
else
u''
inner_content
+=
u''
.
join
(
etree
.
tostring
(
e
)
for
e
in
tag
)
inner_content
+=
tag
.
tail
if
tag
.
tail
else
u''
return
inner_content
...
...
@@ -386,15 +391,91 @@ def course_overview(request, course_id):
if
not
course_module
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
overview
=
get_course_about_section
(
course_module
,
'overview'
)
content
=
get_course_about_section
(
course_module
,
'overview'
)
if
request
.
GET
.
get
(
'parse'
)
and
request
.
GET
.
get
(
'parse'
)
in
[
'True'
,
'true'
]:
try
:
response_data
[
'sections'
]
=
_parse_overview_html
(
content
)
except
:
log
.
exception
(
u"Error prasing course overview. Content = {0}"
.
format
(
content
))
return
Response
({
'err'
:
'could_not_parse'
},
status
=
status
.
HTTP_409_CONFLICT
)
else
:
response_data
[
'overview_html'
]
=
content
except
InvalidLocationError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
response_data
)
def
_parse_updates_html
(
html
):
"""
Helper method to break up the course updates HTML into components
"""
result
=
{}
parser
=
etree
.
HTMLParser
()
tree
=
etree
.
parse
(
StringIO
(
html
),
parser
)
# get all of the individual postings
postings
=
tree
.
findall
(
'/body/ol/li'
)
result
=
[]
for
posting
in
postings
:
posting_data
=
{}
posting_date_element
=
posting
.
find
(
'h2'
)
if
posting_date_element
is
not
None
:
posting_data
[
'date'
]
=
posting_date_element
.
text
content
=
u''
for
el
in
posting
:
# note, we can't delete or skip over the date element in
# the HTML tree because there might be some tailing content
if
el
!=
posting_date_element
:
content
+=
etree
.
tostring
(
el
)
else
:
content
+=
el
.
tail
if
el
.
tail
else
u''
posting_data
[
'content'
]
=
content
.
strip
()
result
.
append
(
posting_data
)
return
result
@api_view
([
'GET'
])
@permission_classes
((
ApiKeyHeaderPermission
,))
def
course_updates
(
request
,
course_id
):
"""
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":"course_info", "_id.name":"updates"}
"""
store
=
modulestore
()
response_data
=
OrderedDict
()
try
:
course_module
=
store
.
get_course
(
course_id
)
if
not
course_module
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
content
=
get_course_info_section
(
request
,
course_module
,
'updates'
)
if
not
content
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
if
request
.
GET
.
get
(
'parse'
)
and
request
.
GET
.
get
(
'parse'
)
in
[
'True'
,
'true'
]:
try
:
response_data
[
'
sections'
]
=
_parse_overview_html
(
overview
)
response_data
[
'
postings'
]
=
_parse_updates_html
(
content
)
except
:
log
.
exception
(
u"Error prasing course updates. Content = {0}"
.
format
(
content
))
return
Response
({
'err'
:
'could_not_parse'
},
status
=
status
.
HTTP_409_CONFLICT
)
else
:
response_data
[
'
overview_html'
]
=
overview
response_data
[
'
content'
]
=
content
except
InvalidLocationError
:
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
...
...
lms/djangoapps/api_manager/tests/content.py
0 → 100644
View file @
812b0be5
"""
Some test content strings. Best to keep them out of the test files because they take up a lot of
text space
"""
from
textwrap
import
dedent
TEST_COURSE_UPDATES_CONTENT
=
dedent
(
"""
<ol>
<li>
<h2>April 18, 2014</h2>
This does not have a paragraph tag around it
</li>
<li>
<h2>April 17, 2014</h2>
Some text before paragraph tag<p>This is inside paragraph tag</p>Some text after tag
</li>
<li>
<h2>April 16, 2014</h2>
Some text before paragraph tag<p>This is inside paragraph tag</p>Some text after tag<p>one more</p>
</li>
<li>
<h2>April 15, 2014</h2>
<p>A perfectly</p><p>formatted piece</p><p>of HTML</p>
</li>
</ol>
"""
)
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>
"""
)
lms/djangoapps/api_manager/tests/test_courses_views.py
View file @
812b0be5
...
...
@@ -11,8 +11,6 @@ import unittest
>>>>>>>
initial
implementation
import
uuid
from
textwrap
import
dedent
from
django.core.cache
import
cache
from
django.test
import
TestCase
,
Client
from
django.test.utils
import
override_settings
...
...
@@ -20,48 +18,10 @@ from django.test.utils import override_settings
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
.content
import
TEST_COURSE_OVERVIEW_CONTENT
,
TEST_COURSE_UPDATES_CONTENT
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
):
""" Django test client using a "secure" connection. """
...
...
@@ -114,6 +74,13 @@ class CoursesApiTests(TestCase):
display_name
=
"overview"
)
self
.
updates
=
ItemFactory
.
create
(
category
=
"course_info"
,
parent_location
=
self
.
course
.
location
,
data
=
TEST_COURSE_UPDATES_CONTENT
,
display_name
=
"updates"
)
self
.
test_course_id
=
self
.
course
.
id
self
.
test_course_name
=
self
.
course
.
display_name
self
.
test_course_number
=
self
.
course
.
number
...
...
@@ -398,3 +365,28 @@ class CoursesApiTests(TestCase):
self
.
assertGreater
(
len
(
prerequisites
[
'body'
]),
0
)
faq
=
self
.
_find_item_by_class
(
sections
,
'faq'
)
self
.
assertGreater
(
len
(
faq
[
'body'
]),
0
)
def
test_get_course_updates
(
self
):
# first try raw without any parsing
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'/updates'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
self
.
assertEqual
(
response
.
data
[
'content'
],
self
.
updates
.
data
)
# then try parsed
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'/updates?parse=True'
response
=
self
.
do_get
(
test_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
len
(
response
.
data
),
0
)
postings
=
response
.
data
[
'postings'
]
self
.
assertEqual
(
len
(
postings
),
4
)
self
.
assertEqual
(
postings
[
0
][
'date'
],
'April 18, 2014'
)
self
.
assertEqual
(
postings
[
0
][
'content'
],
'This does not have a paragraph tag around it'
)
self
.
assertEqual
(
postings
[
1
][
'date'
],
'April 17, 2014'
)
self
.
assertEqual
(
postings
[
1
][
'content'
],
'Some text before paragraph tag<p>This is inside paragraph tag</p>Some text after tag'
)
self
.
assertEqual
(
postings
[
2
][
'date'
],
'April 16, 2014'
)
self
.
assertEqual
(
postings
[
2
][
'content'
],
'Some text before paragraph tag<p>This is inside paragraph tag</p>Some text after tag<p>one more</p>'
)
self
.
assertEqual
(
postings
[
3
][
'date'
],
'April 15, 2014'
)
self
.
assertEqual
(
postings
[
3
][
'content'
],
'<p>A perfectly</p><p>formatted piece</p><p>of HTML</p>'
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment