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
f0b60717
Commit
f0b60717
authored
Jul 01, 2015
by
Jonathan Piacenti
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Courseware Navigation API update for jump_to_children
parent
7ed5672e
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
108 additions
and
17 deletions
+108
-17
lms/djangoapps/api_manager/courses/tests.py
+43
-4
lms/djangoapps/api_manager/courses/urls.py
+1
-0
lms/djangoapps/api_manager/courses/views.py
+47
-2
lms/djangoapps/courseware/views.py
+17
-11
No files found.
lms/djangoapps/api_manager/courses/tests.py
View file @
f0b60717
...
@@ -6,6 +6,7 @@ Run these tests @ Devstack:
...
@@ -6,6 +6,7 @@ Run these tests @ Devstack:
from
datetime
import
datetime
from
datetime
import
datetime
import
json
import
json
import
uuid
import
uuid
from
django.utils
import
timezone
import
mock
import
mock
from
random
import
randint
from
random
import
randint
from
urllib
import
urlencode
from
urllib
import
urlencode
...
@@ -23,6 +24,7 @@ from courseware import module_render
...
@@ -23,6 +24,7 @@ from courseware import module_render
from
courseware.tests.factories
import
StudentModuleFactory
from
courseware.tests.factories
import
StudentModuleFactory
from
courseware.model_data
import
FieldDataCache
from
courseware.model_data
import
FieldDataCache
from
django_comment_common.models
import
Role
,
FORUM_ROLE_MODERATOR
from
django_comment_common.models
import
Role
,
FORUM_ROLE_MODERATOR
from
gradebook.models
import
StudentGradebook
from
instructor.access
import
allow_access
from
instructor.access
import
allow_access
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
...
@@ -92,7 +94,6 @@ class CoursesApiTests(ModuleStoreTestCase):
...
@@ -92,7 +94,6 @@ class CoursesApiTests(ModuleStoreTestCase):
)
)
return
module
return
module
def
setUp
(
self
):
def
setUp
(
self
):
self
.
test_server_prefix
=
'https://testserver'
self
.
test_server_prefix
=
'https://testserver'
self
.
base_courses_uri
=
'/api/server/courses'
self
.
base_courses_uri
=
'/api/server/courses'
...
@@ -141,11 +142,32 @@ class CoursesApiTests(ModuleStoreTestCase):
...
@@ -141,11 +142,32 @@ class CoursesApiTests(ModuleStoreTestCase):
display_name
=
"Video_Sequence"
display_name
=
"Video_Sequence"
)
)
self
.
course_content2
=
ItemFactory
.
create
(
category
=
"sequential"
,
parent_location
=
self
.
chapter
.
location
,
data
=
self
.
test_data
,
display_name
=
"Sequential"
,
)
self
.
content_child
=
ItemFactory
.
create
(
self
.
content_child
=
ItemFactory
.
create
(
category
=
"video"
,
category
=
"video"
,
parent_location
=
self
.
course_content
.
location
,
parent_location
=
self
.
course_content
.
location
,
data
=
self
.
test_data
,
data
=
self
.
test_data
,
display_name
=
"Video_Resources"
display_name
=
"Video"
)
self
.
content_child2
=
ItemFactory
.
create
(
category
=
"vertical"
,
parent_location
=
self
.
course_content2
.
location
,
data
=
self
.
test_data
,
display_name
=
"Vertical Sequence"
)
self
.
content_subchild
=
ItemFactory
.
create
(
category
=
"video"
,
parent_location
=
self
.
content_child2
.
location
,
data
=
self
.
test_data
,
display_name
=
"Child Video"
,
)
)
self
.
overview
=
ItemFactory
.
create
(
self
.
overview
=
ItemFactory
.
create
(
...
@@ -330,7 +352,6 @@ class CoursesApiTests(ModuleStoreTestCase):
...
@@ -330,7 +352,6 @@ class CoursesApiTests(ModuleStoreTestCase):
self
.
assertIsNotNone
(
course
[
'course_image_url'
])
self
.
assertIsNotNone
(
course
[
'course_image_url'
])
self
.
assertItemsEqual
(
courses
,
courses_in_result
)
self
.
assertItemsEqual
(
courses
,
courses_in_result
)
def
test_course_detail_without_date_values
(
self
):
def
test_course_detail_without_date_values
(
self
):
create_course_with_out_date_values
=
CourseFactory
.
create
()
# pylint: disable=C0103
create_course_with_out_date_values
=
CourseFactory
.
create
()
# pylint: disable=C0103
test_uri
=
self
.
base_courses_uri
+
'/'
+
unicode
(
create_course_with_out_date_values
.
id
)
test_uri
=
self
.
base_courses_uri
+
'/'
+
unicode
(
create_course_with_out_date_values
.
id
)
...
@@ -387,7 +408,7 @@ class CoursesApiTests(ModuleStoreTestCase):
...
@@ -387,7 +408,7 @@ class CoursesApiTests(ModuleStoreTestCase):
chapter
=
response
.
data
[
'content'
][
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
[
'children'
]),
5
)
self
.
assertEqual
(
len
(
chapter
[
'children'
]),
6
)
sequence
=
chapter
[
'children'
][
0
]
sequence
=
chapter
[
'children'
][
0
]
self
.
assertEqual
(
sequence
[
'category'
],
'videosequence'
)
self
.
assertEqual
(
sequence
[
'category'
],
'videosequence'
)
...
@@ -2456,3 +2477,21 @@ class CoursesApiTests(ModuleStoreTestCase):
...
@@ -2456,3 +2477,21 @@ class CoursesApiTests(ModuleStoreTestCase):
delete_uri
=
'{}invalid_role/users/{}'
.
format
(
test_uri
,
self
.
users
[
0
]
.
id
)
delete_uri
=
'{}invalid_role/users/{}'
.
format
(
test_uri
,
self
.
users
[
0
]
.
id
)
response
=
self
.
do_delete
(
delete_uri
)
response
=
self
.
do_delete
(
delete_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_course_navigation
(
self
):
test_uri
=
'{}/{}/navigation/{}'
.
format
(
self
.
base_courses_uri
,
unicode
(
self
.
course
.
id
),
self
.
content_subchild
.
location
.
block_id
)
response
=
self
.
do_get
(
test_uri
)
self
.
maxDiff
=
None
self
.
assertEqual
(
{
'chapter'
:
unicode
(
self
.
chapter
.
location
),
'vertical'
:
unicode
(
self
.
content_child2
.
location
),
'section'
:
unicode
(
self
.
course_content2
.
location
),
'course_key'
:
unicode
(
self
.
course
.
id
),
'final_target_id'
:
unicode
(
self
.
content_subchild
.
location
),
'position'
:
'1'
,
},
response
.
data
)
lms/djangoapps/api_manager/courses/urls.py
View file @
f0b60717
...
@@ -40,6 +40,7 @@ urlpatterns = patterns(
...
@@ -40,6 +40,7 @@ urlpatterns = patterns(
url
(
r'^{0}/users/(?P<user_id>[0-9]+)$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesUsersDetail
.
as_view
()),
url
(
r'^{0}/users/(?P<user_id>[0-9]+)$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesUsersDetail
.
as_view
()),
url
(
r'^{0}/users/*$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesUsersList
.
as_view
()),
url
(
r'^{0}/users/*$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesUsersList
.
as_view
()),
url
(
r'^{0}/workgroups/*$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesWorkgroupsList
.
as_view
()),
url
(
r'^{0}/workgroups/*$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesWorkgroupsList
.
as_view
()),
url
(
r'^{0}/navigation/{1}$'
.
format
(
COURSE_ID_PATTERN
,
settings
.
USAGE_KEY_PATTERN
),
courses_views
.
CourseNavView
.
as_view
()),
url
(
r'^{0}$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesDetail
.
as_view
()),
url
(
r'^{0}$'
.
format
(
COURSE_ID_PATTERN
),
courses_views
.
CoursesDetail
.
as_view
()),
url
(
r'/*$^'
,
courses_views
.
CoursesList
.
as_view
()),
url
(
r'/*$^'
,
courses_views
.
CoursesList
.
as_view
()),
)
)
...
...
lms/djangoapps/api_manager/courses/views.py
View file @
f0b60717
...
@@ -21,7 +21,7 @@ from rest_framework.response import Response
...
@@ -21,7 +21,7 @@ from rest_framework.response import Response
from
courseware.courses
import
get_course_about_section
,
get_course_info_section
,
course_image_url
from
courseware.courses
import
get_course_about_section
,
get_course_info_section
,
course_image_url
from
courseware.models
import
StudentModule
from
courseware.models
import
StudentModule
from
courseware.views
import
get_static_tab_contents
from
courseware.views
import
get_static_tab_contents
,
item_finder
from
django_comment_common.models
import
FORUM_ROLE_MODERATOR
from
django_comment_common.models
import
FORUM_ROLE_MODERATOR
from
gradebook.models
import
StudentGradebook
from
gradebook.models
import
StudentGradebook
from
instructor.access
import
revoke_access
,
update_forum_role
from
instructor.access
import
revoke_access
,
update_forum_role
...
@@ -29,13 +29,14 @@ from lms.lib.comment_client.user import get_course_social_stats
...
@@ -29,13 +29,14 @@ from lms.lib.comment_client.user import get_course_social_stats
from
lms.lib.comment_client.thread
import
get_course_thread_stats
from
lms.lib.comment_client.thread
import
get_course_thread_stats
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
progress.models
import
StudentProgress
from
progress.models
import
StudentProgress
from
projects.models
import
Project
,
Workgroup
from
projects.models
import
Project
,
Workgroup
from
projects.serializers
import
ProjectSerializer
,
BasicWorkgroupSerializer
from
projects.serializers
import
ProjectSerializer
,
BasicWorkgroupSerializer
from
student.models
import
CourseEnrollment
,
CourseEnrollmentAllowed
from
student.models
import
CourseEnrollment
,
CourseEnrollmentAllowed
from
student.roles
import
CourseRole
,
CourseAccessRole
,
CourseInstructorRole
,
CourseStaffRole
,
CourseObserverRole
,
CourseAssistantRole
,
UserBasedRole
,
get_aggregate_exclusion_user_ids
from
student.roles
import
CourseRole
,
CourseAccessRole
,
CourseInstructorRole
,
CourseStaffRole
,
CourseObserverRole
,
CourseAssistantRole
,
UserBasedRole
,
get_aggregate_exclusion_user_ids
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.search
import
path_to_location
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_leaf_nodes
,
get_course_key
,
\
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_leaf_nodes
,
get_course_key
,
\
course_exists
,
get_modulestore
,
get_course_descriptor
course_exists
,
get_modulestore
,
get_course_descriptor
...
@@ -1961,3 +1962,47 @@ class CoursesRolesUsersDetail(SecureAPIView):
...
@@ -1961,3 +1962,47 @@ class CoursesRolesUsersDetail(SecureAPIView):
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
({},
status
=
status
.
HTTP_204_NO_CONTENT
)
return
Response
({},
status
=
status
.
HTTP_204_NO_CONTENT
)
class
CourseNavView
(
SecureAPIView
):
"""
### The CourseNavView view exposes navigation information for particular usage id: course, chapter, section and
vertical keys, position in innermost container and last addressable block/module on the path (usually the same
usage id that was passed as an argument)
- URI: ```/api/courses/{course_id}/navigation/{module_id}```
- GET: Gets navigation information
"""
def
_get_full_location_key_by_module_id
(
self
,
request
,
course_key
,
module_id
):
"""
Gets full location id by module id
"""
items
=
item_finder
(
request
,
course_key
,
module_id
)
return
items
[
0
]
.
location
def
get
(
self
,
request
,
course_id
,
usage_key_string
):
# pylint: disable=W0613
"""
GET /api/courses/{course_id}/navigation/{module_id}
"""
try
:
_
,
course_key
,
__
=
get_course
(
request
,
request
.
user
,
course_id
)
usage_key
=
self
.
_get_full_location_key_by_module_id
(
request
,
course_key
,
usage_key_string
)
except
InvalidKeyError
:
raise
Http404
(
u"Invalid course_key or usage_key"
)
(
course_key
,
chapter
,
section
,
vertical
,
position
,
final_target_id
)
=
path_to_location
(
modulestore
(),
usage_key
)
chapter_key
=
course_key
.
make_usage_key
(
'chapter'
,
chapter
)
section_key
=
course_key
.
make_usage_key
(
'sequential'
,
section
)
vertical_key
=
course_key
.
make_usage_key
(
'vertical'
,
vertical
)
result
=
{
'course_key'
:
unicode
(
course_key
),
'chapter'
:
unicode
(
chapter_key
),
'section'
:
unicode
(
section_key
),
'vertical'
:
unicode
(
vertical_key
),
'position'
:
unicode
(
position
),
'final_target_id'
:
unicode
(
final_target_id
)
}
return
Response
(
result
,
status
=
status
.
HTTP_200_OK
)
lms/djangoapps/courseware/views.py
View file @
f0b60717
...
@@ -623,28 +623,34 @@ def _index_bulk_op(request, course_key, chapter, section, position):
...
@@ -623,28 +623,34 @@ def _index_bulk_op(request, course_key, chapter, section, position):
return
result
return
result
@ensure_csrf_cookie
def
item_finder
(
request
,
course_key
,
module_id
):
@ensure_valid_course_key
def
jump_to_id
(
request
,
course_id
,
module_id
):
"""
This entry point allows for a shorter version of a jump to where just the id of the element is
passed in. This assumes that id is unique within the course_id namespace
"""
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
items
=
modulestore
()
.
get_items
(
course_key
,
qualifiers
=
{
'name'
:
module_id
})
items
=
modulestore
()
.
get_items
(
course_key
,
qualifiers
=
{
'name'
:
module_id
})
if
len
(
items
)
==
0
:
if
len
(
items
)
==
0
:
raise
Http404
(
raise
Http404
(
u"Could not find id: {0} in course_id: {1}. Referer: {2}"
.
format
(
u"Could not find id: {0} in course_id: {1}. Referer: {2}"
.
format
(
module_id
,
course_
id
,
request
.
META
.
get
(
"HTTP_REFERER"
,
""
)
module_id
,
course_
key
,
request
.
META
.
get
(
"HTTP_REFERER"
,
""
)
))
))
if
len
(
items
)
>
1
:
if
len
(
items
)
>
1
:
log
.
warning
(
log
.
warning
(
u"Multiple items found with id: {0} in course_id: {1}. Referer: {2}. Using first: {3}"
.
format
(
u"Multiple items found with id: {0} in course_id: {1}. Referer: {2}. Using first: {3}"
.
format
(
module_id
,
course_
id
,
request
.
META
.
get
(
"HTTP_REFERER"
,
""
),
items
[
0
]
.
location
.
to_deprecated_string
()
module_id
,
course_
key
,
request
.
META
.
get
(
"HTTP_REFERER"
,
""
),
items
[
0
]
.
location
.
to_deprecated_string
()
))
))
return
jump_to
(
request
,
course_id
,
items
[
0
]
.
location
.
to_deprecated_string
())
return
items
@ensure_csrf_cookie
@ensure_valid_course_key
def
jump_to_id
(
request
,
course_id
,
module_id
):
"""
This entry point allows for a shorter version of a jump to where just the id of the element is
passed in. This assumes that id is unique within the course_id namespace
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
items
=
item_finder
(
request
,
course_key
,
module_id
)
return
jump_to
(
request
,
course_id
,
unicode
(
items
[
0
]
.
location
))
@ensure_csrf_cookie
@ensure_csrf_cookie
...
...
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