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
3893a9ae
Commit
3893a9ae
authored
Jul 02, 2014
by
Calen Pennington
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4258 from cpennington/studio-deprecated-urls
Make Studio able to handle deprecated key formats in urls
parents
0765fbb4
1c882da3
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
97 additions
and
82 deletions
+97
-82
cms/djangoapps/contentstore/tests/test_contentstore.py
+1
-32
cms/djangoapps/contentstore/tests/test_import.py
+1
-1
cms/djangoapps/contentstore/views/component.py
+7
-7
cms/djangoapps/contentstore/views/item.py
+14
-14
cms/djangoapps/contentstore/views/tests/test_course_index.py
+2
-1
cms/djangoapps/contentstore/views/tests/test_item.py
+4
-1
cms/urls.py
+26
-22
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+42
-4
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
3893a9ae
...
...
@@ -131,7 +131,6 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
descriptor
=
store
.
get_items
(
course
.
id
,
category
=
'vertical'
,)
resp
=
self
.
client
.
get_html
(
get_url
(
'unit_handler'
,
descriptor
[
0
]
.
location
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
for
expected
in
expected_types
:
self
.
assertIn
(
expected
,
resp
.
content
)
...
...
@@ -157,7 +156,6 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
resp
=
self
.
client
.
get_html
(
get_url
(
'unit_handler'
,
usage_key
))
self
.
assertEqual
(
resp
.
status_code
,
400
)
_test_no_locations
(
self
,
resp
,
status_code
=
400
)
def
check_edit_unit
(
self
,
test_course_name
):
_
,
course_items
=
import_from_xml
(
modulestore
(),
self
.
user
.
id
,
'common/test/data/'
,
[
test_course_name
])
...
...
@@ -364,8 +362,6 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
get_url
(
'xblock_view_handler'
,
usage_key
,
kwargs
=
{
'view_name'
:
'container_preview'
})
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when preview no longer has locations being returned.
# _test_no_locations(self, resp)
# These are the data-ids of the xblocks contained in the vertical.
self
.
assertContains
(
resp
,
'edX+toy+2012_Fall+video+sample_video'
)
...
...
@@ -534,7 +530,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
url
=
reverse_course_url
(
'assets_handler'
,
course
.
id
,
kwargs
=
{
'asset_key_string'
:
course
.
id
.
make_asset_key
(
'asset'
,
'sample_static.txt'
)}
kwargs
=
{
'asset_key_string'
:
unicode
(
course
.
id
.
make_asset_key
(
'asset'
,
'sample_static.txt'
)
)}
)
resp
=
self
.
client
.
delete
(
url
)
self
.
assertEqual
(
resp
.
status_code
,
204
)
...
...
@@ -761,7 +757,6 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
def
test_bad_contentstore_request
(
self
):
resp
=
self
.
client
.
get_html
(
'http://localhost:8001/c4x/CDX/123123/asset/&images_circuits_Lab7Solution2.png'
)
self
.
assertEqual
(
resp
.
status_code
,
400
)
_test_no_locations
(
self
,
resp
,
400
)
def
test_rewrite_nonportable_links_on_import
(
self
):
module_store
=
modulestore
()
...
...
@@ -1196,7 +1191,6 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
for
descriptor
in
items
:
resp
=
self
.
client
.
get_html
(
get_url
(
'unit_handler'
,
descriptor
.
location
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
class
ContentStoreTest
(
ContentStoreTestCase
):
...
...
@@ -1475,7 +1469,6 @@ class ContentStoreTest(ContentStoreTestCase):
status_code
=
200
,
html
=
True
)
_test_no_locations
(
self
,
resp
)
def
test_course_factory
(
self
):
"""Test that the course factory works correctly."""
...
...
@@ -1498,7 +1491,6 @@ class ContentStoreTest(ContentStoreTestCase):
status_code
=
200
,
html
=
True
)
_test_no_locations
(
self
,
resp
)
def
test_course_overview_view_with_course
(
self
):
"""Test viewing the course overview page with an existing course"""
...
...
@@ -1522,7 +1514,6 @@ class ContentStoreTest(ContentStoreTestCase):
}
resp
=
self
.
client
.
ajax_post
(
reverse_url
(
'xblock_handler'
),
section_data
)
_test_no_locations
(
self
,
resp
,
html
=
False
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
...
...
@@ -1563,7 +1554,6 @@ class ContentStoreTest(ContentStoreTestCase):
get_url
(
handler
,
course_key
,
'course_key_string'
)
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
_
,
course_items
=
import_from_xml
(
modulestore
(),
self
.
user
.
id
,
'common/test/data/'
,
[
'simple'
])
course_key
=
course_items
[
0
]
.
id
...
...
@@ -1589,20 +1579,17 @@ class ContentStoreTest(ContentStoreTestCase):
subsection_key
=
course_key
.
make_usage_key
(
'sequential'
,
'test_sequence'
)
resp
=
self
.
client
.
get_html
(
get_url
(
'subsection_handler'
,
subsection_key
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
# go look at the Edit page
unit_key
=
course_key
.
make_usage_key
(
'vertical'
,
'test_vertical'
)
resp
=
self
.
client
.
get_html
(
get_url
(
'unit_handler'
,
unit_key
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
def
delete_item
(
category
,
name
):
""" Helper method for testing the deletion of an xblock item. """
item_key
=
course_key
.
make_usage_key
(
category
,
name
)
resp
=
self
.
client
.
delete
(
get_url
(
'xblock_handler'
,
item_key
))
self
.
assertEqual
(
resp
.
status_code
,
204
)
_test_no_locations
(
self
,
resp
,
status_code
=
204
,
html
=
False
)
# delete a component
delete_item
(
category
=
'html'
,
name
=
'test_html'
)
...
...
@@ -1805,7 +1792,6 @@ class ContentStoreTest(ContentStoreTestCase):
Show the course overview page.
"""
resp
=
self
.
client
.
get_html
(
get_url
(
'course_handler'
,
course_key
,
'course_key_string'
))
_test_no_locations
(
self
,
resp
)
return
resp
def
test_wiki_slug
(
self
):
...
...
@@ -1887,7 +1873,6 @@ class EntryPageTestCase(TestCase):
def
_test_page
(
self
,
page
,
status_code
=
200
):
resp
=
self
.
client
.
get_html
(
page
)
self
.
assertEqual
(
resp
.
status_code
,
status_code
)
_test_no_locations
(
self
,
resp
,
status_code
)
def
test_how_it_works
(
self
):
self
.
_test_page
(
"/howitworks"
)
...
...
@@ -1925,19 +1910,3 @@ def _course_factory_create_course():
def
_get_course_id
(
course_data
):
"""Returns the course ID (org/number/run)."""
return
SlashSeparatedCourseKey
(
course_data
[
'org'
],
course_data
[
'number'
],
course_data
[
'run'
])
def
_test_no_locations
(
test
,
resp
,
status_code
=
200
,
html
=
True
):
"""
Verifies that "i4x", which appears in old locations, but not
new locators, does not appear in the HTML response output.
Used to verify that database refactoring is complete.
"""
test
.
assertNotContains
(
resp
,
'i4x'
,
status_code
=
status_code
,
html
=
html
)
if
html
:
# For HTML pages, it is nice to call the method with html=True because
# it checks that the HTML properly parses. However, it won't find i4x usages
# in JavaScript blocks.
content
=
resp
.
content
hits
=
len
(
re
.
findall
(
r"(?<!jump_to/)i4x://"
,
content
))
test
.
assertEqual
(
hits
,
0
,
"i4x found outside of LMS jump-to links"
)
cms/djangoapps/contentstore/tests/test_import.py
View file @
3893a9ae
...
...
@@ -189,7 +189,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
target_course_id
.
make_usage_key
(
'conditional'
,
'condone'
)
)
self
.
assertIsNotNone
(
conditional_module
)
different_course_id
=
SlashSeparatedCourseKey
(
'edX'
,
'different_course'
,
'copy_run'
)
different_course_id
=
SlashSeparatedCourseKey
(
'edX'
,
'different_course'
,
None
)
self
.
assertListEqual
(
[
target_course_id
.
make_usage_key
(
'problem'
,
'choiceprob'
),
...
...
cms/djangoapps/contentstore/views/component.py
View file @
3893a9ae
...
...
@@ -61,7 +61,7 @@ else:
# XBlocks from pmitros repos are prototypes. They should not be used
# except for edX Learning Sciences experiments on edge.edx.org without
# further work to make them robust, maintainable, finalize data formats,
# etc.
# etc.
'concept'
,
# Concept mapper. See https://github.com/pmitros/ConceptXBlock
'done'
,
# Lets students mark things as done. See https://github.com/pmitros/DoneXBlock
'audio'
,
# Embed an audio file. See https://github.com/pmitros/AudioXBlock
...
...
@@ -97,7 +97,7 @@ def subsection_handler(request, usage_key_string):
except
ItemNotFoundError
:
return
HttpResponseBadRequest
()
preview_link
=
get_lms_link_for_item
(
usage_key
,
preview
=
True
)
preview_link
=
get_lms_link_for_item
(
item
.
location
,
preview
=
True
)
# make sure that location references a 'sequential', otherwise return
# BadRequest
...
...
@@ -134,9 +134,9 @@ def subsection_handler(request, usage_key_string):
'new_unit_category'
:
'vertical'
,
'lms_link'
:
lms_link
,
'preview_link'
:
preview_link
,
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
usage_key
.
course_key
)
.
graders
),
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
item
.
location
.
course_key
)
.
graders
),
'parent_item'
:
parent
,
'locator'
:
usage_key
,
'locator'
:
item
.
location
,
'policy_metadata'
:
policy_metadata
,
'subsection_units'
:
subsection_units
,
'can_view_live'
:
can_view_live
...
...
@@ -211,7 +211,7 @@ def unit_handler(request, usage_key_string):
return
render_to_response
(
'unit.html'
,
{
'context_course'
:
course
,
'unit'
:
item
,
'unit_usage_key'
:
usage_key
,
'unit_usage_key'
:
item
.
location
,
'child_usage_keys'
:
[
block
.
scope_ids
.
usage_id
for
block
in
xblocks
],
'component_templates'
:
json
.
dumps
(
component_templates
),
'draft_preview_link'
:
preview_lms_link
,
...
...
@@ -267,7 +267,7 @@ def container_handler(request, usage_key_string):
'context_course'
:
course
,
# Needed only for display of menus at top of page.
'xblock'
:
xblock
,
'unit_publish_state'
:
unit_publish_state
,
'xblock_locator'
:
usage_key
,
'xblock_locator'
:
xblock
.
location
,
'unit'
:
None
if
not
ancestor_xblocks
else
ancestor_xblocks
[
0
],
'ancestor_xblocks'
:
ancestor_xblocks
,
'component_templates'
:
json
.
dumps
(
component_templates
),
...
...
@@ -415,7 +415,7 @@ def _get_item_in_course(request, usage_key):
course
=
modulestore
()
.
get_course
(
course_key
)
item
=
modulestore
()
.
get_item
(
usage_key
,
depth
=
1
)
lms_link
=
get_lms_link_for_item
(
usage_key
)
lms_link
=
get_lms_link_for_item
(
item
.
location
)
return
course
,
item
,
lms_link
...
...
cms/djangoapps/contentstore/views/item.py
View file @
3893a9ae
...
...
@@ -144,7 +144,7 @@ def xblock_handler(request, usage_key_string):
request
.
user
,
)
return
JsonResponse
({
"locator"
:
unicode
(
dest_usage_key
)})
return
JsonResponse
({
"locator"
:
unicode
(
dest_usage_key
)
,
"courseKey"
:
unicode
(
dest_usage_key
.
course_key
)
})
else
:
return
_create_item
(
request
)
else
:
...
...
@@ -403,7 +403,7 @@ def _create_item(request):
if
display_name
is
not
None
:
metadata
[
'display_name'
]
=
display_name
store
.
create_and_save_xmodule
(
created_block
=
store
.
create_and_save_xmodule
(
dest_usage_key
,
request
.
user
.
id
,
definition_data
=
data
,
...
...
@@ -426,10 +426,10 @@ def _create_item(request):
# TODO replace w/ nicer accessor
if
not
'detached'
in
parent
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
parent
.
children
.
append
(
dest_usage_key
)
parent
.
children
.
append
(
created_block
.
location
)
store
.
update_item
(
parent
,
request
.
user
.
id
)
return
JsonResponse
({
"locator"
:
unicode
(
dest_usage_key
),
"courseKey"
:
unicode
(
dest_usage_key
.
course_key
)})
return
JsonResponse
({
"locator"
:
unicode
(
created_block
.
location
),
"courseKey"
:
unicode
(
created_block
.
location
.
course_key
)})
def
_duplicate_item
(
parent_usage_key
,
duplicate_source_usage_key
,
display_name
=
None
,
user
=
None
):
...
...
@@ -439,8 +439,8 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, display_name=N
store
=
modulestore
()
source_item
=
store
.
get_item
(
duplicate_source_usage_key
)
# Change the blockID to be unique.
dest_usage_key
=
duplicate_source_usage_key
.
replace
(
name
=
uuid4
()
.
hex
)
category
=
dest_usage_key
.
category
dest_usage_key
=
source_item
.
location
.
replace
(
name
=
uuid4
()
.
hex
)
category
=
dest_usage_key
.
block_type
# Update the display name to indicate this is a duplicate (unless display name provided).
duplicate_metadata
=
own_metadata
(
source_item
)
...
...
@@ -465,7 +465,7 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, display_name=N
if
source_item
.
has_children
:
dest_module
.
children
=
[]
for
child
in
source_item
.
children
:
dupe
=
_duplicate_item
(
dest_
usage_key
,
child
,
user
=
user
)
dupe
=
_duplicate_item
(
dest_
module
.
location
,
child
,
user
=
user
)
dest_module
.
children
.
append
(
dupe
)
store
.
update_item
(
dest_module
,
user
.
id
if
user
else
None
)
...
...
@@ -473,14 +473,14 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, display_name=N
parent
=
store
.
get_item
(
parent_usage_key
)
# If source was already a child of the parent, add duplicate immediately afterward.
# Otherwise, add child to end.
if
duplicate_source_usage_key
in
parent
.
children
:
source_index
=
parent
.
children
.
index
(
duplicate_source_usage_key
)
parent
.
children
.
insert
(
source_index
+
1
,
dest_
usage_key
)
if
source_item
.
location
in
parent
.
children
:
source_index
=
parent
.
children
.
index
(
source_item
.
location
)
parent
.
children
.
insert
(
source_index
+
1
,
dest_
module
.
location
)
else
:
parent
.
children
.
append
(
dest_
usage_key
)
parent
.
children
.
append
(
dest_
module
.
location
)
store
.
update_item
(
parent
,
user
.
id
if
user
else
None
)
return
dest_
usage_key
return
dest_
module
.
location
def
_delete_item
(
usage_key
,
user
):
...
...
@@ -553,12 +553,12 @@ def _get_module_info(usage_key, user, rewrite_static_links=True):
data
=
replace_static_urls
(
data
,
None
,
course_id
=
usage_key
.
course_key
course_id
=
module
.
location
.
course_key
)
# Note that children aren't being returned until we have a use case.
return
{
'id'
:
unicode
(
usage_key
),
'id'
:
unicode
(
module
.
location
),
'data'
:
data
,
'metadata'
:
own_metadata
(
module
)
}
cms/djangoapps/contentstore/views/tests/test_course_index.py
View file @
3893a9ae
...
...
@@ -4,6 +4,7 @@ Unit tests for getting the list of courses and the course outline.
import
json
import
lxml
from
cms.urls
import
COURSE_KEY_PATTERN
from
contentstore.tests.utils
import
CourseTestCase
from
contentstore.utils
import
reverse_course_url
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
...
@@ -38,7 +39,7 @@ class TestCourseIndex(CourseTestCase):
for
link
in
course_link_eles
:
self
.
assertRegexpMatches
(
link
.
get
(
"href"
),
'course/
slashes:{0}'
.
format
(
Locator
.
ALLOWED_ID_CHARS
)
'course/
{}'
.
format
(
COURSE_KEY_PATTERN
)
)
# now test that url
outline_response
=
authed_client
.
get
(
link
.
get
(
"href"
),
{},
HTTP_ACCEPT
=
'text/html'
)
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
3893a9ae
...
...
@@ -53,7 +53,10 @@ class ItemTest(CourseTestCase):
"""
parsed
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response
.
status_code
,
200
)
return
UsageKey
.
from_string
(
parsed
[
'locator'
])
key
=
UsageKey
.
from_string
(
parsed
[
'locator'
])
if
key
.
course_key
.
run
is
None
:
key
=
key
.
map_into_course
(
CourseKey
.
from_string
(
parsed
[
'courseKey'
]))
return
key
def
create_xblock
(
self
,
parent_usage_key
=
None
,
display_name
=
None
,
category
=
None
,
boilerplate
=
None
):
data
=
{
...
...
cms/urls.py
View file @
3893a9ae
...
...
@@ -5,6 +5,10 @@ from django.conf.urls import patterns, include, url
from
ratelimitbackend
import
admin
admin
.
autodiscover
()
COURSE_KEY_PATTERN
=
r'(?P<course_key_string>(?:[^/]+/[^/]+/[^/]+)|(?:[^/]+))'
USAGE_KEY_PATTERN
=
r'(?P<usage_key_string>(?:i4x://?[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
ASSET_KEY_PATTERN
=
r'(?P<asset_key_string>(?:/?c4x(:/)?/[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
urlpatterns
=
patterns
(
''
,
# nopep8
url
(
r'^transcripts/upload$'
,
'contentstore.views.upload_transcripts'
,
name
=
'upload_transcripts'
),
...
...
@@ -66,30 +70,30 @@ urlpatterns += patterns(
url
(
r'^signin$'
,
'login_page'
,
name
=
'login'
),
url
(
r'^request_course_creator$'
,
'request_course_creator'
),
url
(
r'^course_team/
(?P<course_key_string>[^/]+)/(?P<email>.+)?$'
,
'course_team_handler'
),
url
(
r'^course_info/
(?P<course_key_string>[^/]+)$'
,
'course_info_handler'
),
url
(
r'^course_team/
{}/(?P<email>.+)?$'
.
format
(
COURSE_KEY_PATTERN
)
,
'course_team_handler'
),
url
(
r'^course_info/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'course_info_handler'
),
url
(
r'^course_info_update/
(?P<course_key_string>[^/]+)/(?P<provided_id>\d+)?$'
,
r'^course_info_update/
{}/(?P<provided_id>\d+)?$'
.
format
(
COURSE_KEY_PATTERN
)
,
'course_info_update_handler'
),
url
(
r'^course/
(?P<course_key_string>[^/]+)?$'
,
'course_handler'
,
name
=
'course_handler'
),
url
(
r'^subsection/
(?P<usage_key_string>[^/]+)$'
,
'subsection_handler'
),
url
(
r'^unit/
(?P<usage_key_string>[^/]+)$'
,
'unit_handler'
),
url
(
r'^container/
(?P<usage_key_string>[^/]+)$'
,
'container_handler'
),
url
(
r'^checklists/
(?P<course_key_string>[^/]+)/(?P<checklist_index>\d+)?$'
,
'checklists_handler'
),
url
(
r'^orphan/
(?P<course_key_string>[^/]+)$'
,
'orphan_handler'
),
url
(
r'^assets/
(?P<course_key_string>[^/]+)/(?P<asset_key_string>.+)?$'
,
'assets_handler'
),
url
(
r'^import/
(?P<course_key_string>[^/]+)$'
,
'import_handler'
),
url
(
r'^import_status/
(?P<course_key_string>[^/]+)/(?P<filename>.+)$'
,
'import_status_handler'
),
url
(
r'^export/
(?P<course_key_string>[^/]+)$'
,
'export_handler'
),
url
(
r'^xblock/
(?P<usage_key_string>[^/]+)/(?P<view_name>[^/]+)$'
,
'xblock_view_handler'
),
url
(
r'^xblock/
(?P<usage_key_string>[^/]+)?$'
,
'xblock_handler'
),
url
(
r'^tabs/
(?P<course_key_string>[^/]+)$'
,
'tabs_handler'
),
url
(
r'^settings/details/
(?P<course_key_string>[^/]+)$'
,
'settings_handler'
),
url
(
r'^settings/grading/
(?P<course_key_string>[^/]+)(/)?(?P<grader_index>\d+)?$'
,
'grading_handler'
),
url
(
r'^settings/advanced/
(?P<course_key_string>[^/]+)$'
,
'advanced_settings_handler'
),
url
(
r'^textbooks/
(?P<course_key_string>[^/]+)$'
,
'textbooks_list_handler'
),
url
(
r'^textbooks/
(?P<course_key_string>[^/]+)/(?P<textbook_id>\d[^/]*)$'
,
'textbooks_detail_handler'
),
url
(
r'^course/
{}?$'
.
format
(
COURSE_KEY_PATTERN
)
,
'course_handler'
,
name
=
'course_handler'
),
url
(
r'^subsection/
{}$'
.
format
(
USAGE_KEY_PATTERN
)
,
'subsection_handler'
),
url
(
r'^unit/
{}$'
.
format
(
USAGE_KEY_PATTERN
)
,
'unit_handler'
),
url
(
r'^container/
{}$'
.
format
(
USAGE_KEY_PATTERN
)
,
'container_handler'
),
url
(
r'^checklists/
{}/(?P<checklist_index>\d+)?$'
.
format
(
COURSE_KEY_PATTERN
)
,
'checklists_handler'
),
url
(
r'^orphan/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'orphan_handler'
),
url
(
r'^assets/
{}/{}?$'
.
format
(
COURSE_KEY_PATTERN
,
ASSET_KEY_PATTERN
)
,
'assets_handler'
),
url
(
r'^import/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'import_handler'
),
url
(
r'^import_status/
{}/(?P<filename>.+)$'
.
format
(
COURSE_KEY_PATTERN
)
,
'import_status_handler'
),
url
(
r'^export/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'export_handler'
),
url
(
r'^xblock/
{}/(?P<view_name>[^/]+)$'
.
format
(
USAGE_KEY_PATTERN
)
,
'xblock_view_handler'
),
url
(
r'^xblock/
{}?$'
.
format
(
USAGE_KEY_PATTERN
)
,
'xblock_handler'
),
url
(
r'^tabs/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'tabs_handler'
),
url
(
r'^settings/details/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'settings_handler'
),
url
(
r'^settings/grading/
{}(/)?(?P<grader_index>\d+)?$'
.
format
(
COURSE_KEY_PATTERN
)
,
'grading_handler'
),
url
(
r'^settings/advanced/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'advanced_settings_handler'
),
url
(
r'^textbooks/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'textbooks_list_handler'
),
url
(
r'^textbooks/
{}/(?P<textbook_id>\d[^/]*)$'
.
format
(
COURSE_KEY_PATTERN
)
,
'textbooks_detail_handler'
),
)
js_info_dict
=
{
...
...
@@ -105,7 +109,7 @@ urlpatterns += patterns('',
if
settings
.
FEATURES
.
get
(
'ENABLE_EXPORT_GIT'
):
urlpatterns
+=
(
url
(
r'^export_git/
(?P<course_key_string>[^/]+)$'
,
urlpatterns
+=
(
url
(
r'^export_git/
{}$'
.
format
(
COURSE_KEY_PATTERN
)
,
'contentstore.views.export_git'
,
name
=
'export_git'
),)
if
settings
.
FEATURES
.
get
(
'ENABLE_SERVICE_STATUS'
):
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
3893a9ae
...
...
@@ -197,7 +197,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
del
metadata
[
old_name
]
children
=
[
location
.
course_key
.
make_usage_key_from_deprecated_string
(
childloc
)
self
.
_convert_reference_to_key
(
childloc
)
for
childloc
in
definition
.
get
(
'children'
,
[])
]
data
=
definition
.
get
(
'data'
,
{})
...
...
@@ -254,6 +254,13 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
error_msg
=
exc_info_to_str
(
sys
.
exc_info
())
)
def
_convert_reference_to_key
(
self
,
ref_string
):
"""
Convert a single serialized UsageKey string in a ReferenceField into a UsageKey.
"""
key
=
Location
.
from_deprecated_string
(
ref_string
)
return
key
.
replace
(
run
=
self
.
modulestore
.
_fill_in_run
(
key
.
course_key
)
.
run
)
def
_convert_reference_fields_to_keys
(
self
,
class_
,
course_key
,
jsonfields
):
"""
Find all fields of type reference and convert the payload into UsageKeys
...
...
@@ -267,15 +274,15 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
if
field
is
None
:
continue
elif
isinstance
(
field
,
Reference
):
jsonfields
[
field_name
]
=
course_key
.
make_usage_key_from_deprecated_string
(
value
)
jsonfields
[
field_name
]
=
self
.
_convert_reference_to_key
(
value
)
elif
isinstance
(
field
,
ReferenceList
):
jsonfields
[
field_name
]
=
[
course_key
.
make_usage_key_from_deprecated_string
(
ele
)
for
ele
in
value
self
.
_convert_reference_to_key
(
ele
)
for
ele
in
value
]
elif
isinstance
(
field
,
ReferenceValueDict
):
for
key
,
subvalue
in
value
.
iteritems
():
assert
isinstance
(
subvalue
,
basestring
)
value
[
key
]
=
course_key
.
make_usage_key_from_deprecated_string
(
subvalue
)
value
[
key
]
=
self
.
_convert_reference_to_key
(
subvalue
)
return
jsonfields
...
...
@@ -378,6 +385,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
# performance optimization to prevent updating the meta-data inheritance tree during
# bulk write operations
self
.
ignore_write_events_on_courses
=
set
()
self
.
_course_run_cache
=
{}
def
begin_bulk_write_operation_on_course
(
self
,
course_id
):
"""
...
...
@@ -394,6 +402,27 @@ class MongoModuleStore(ModuleStoreWriteBase):
self
.
ignore_write_events_on_courses
.
remove
(
course_id
)
self
.
refresh_cached_metadata_inheritance_tree
(
course_id
)
def
_fill_in_run
(
self
,
course_key
):
if
course_key
.
run
is
not
None
:
return
course_key
cache_key
=
(
course_key
.
org
,
course_key
.
course
)
if
cache_key
not
in
self
.
_course_run_cache
:
matching_courses
=
list
(
self
.
collection
.
find
(
SON
([
(
'_id.tag'
,
'i4x'
),
(
'_id.org'
,
course_key
.
org
),
(
'_id.course'
,
course_key
.
course
),
(
'_id.category'
,
'course'
),
]))
.
limit
(
1
))
if
not
matching_courses
:
return
course_key
self
.
_course_run_cache
[
cache_key
]
=
matching_courses
[
0
][
'_id'
][
'name'
]
return
course_key
.
replace
(
run
=
self
.
_course_run_cache
[
cache_key
])
def
_compute_metadata_inheritance_tree
(
self
,
course_id
):
'''
TODO (cdodge) This method can be deleted when the 'split module store' work has been completed
...
...
@@ -401,6 +430,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
# get all collections in the course, this query should not return any leaf nodes
# note this is a bit ugly as when we add new categories of containers, we have to add it here
course_id
=
self
.
_fill_in_run
(
course_id
)
block_types_with_children
=
set
(
name
for
name
,
class_
in
XBlock
.
load_classes
()
if
getattr
(
class_
,
'has_children'
,
False
)
)
...
...
@@ -476,6 +506,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
'''
tree
=
{}
course_id
=
self
.
_fill_in_run
(
course_id
)
if
not
force_refresh
:
# see if we are first in the request cache (if present)
if
self
.
request_cache
is
not
None
and
course_id
in
self
.
request_cache
.
data
.
get
(
'metadata_inheritance'
,
{}):
...
...
@@ -554,6 +585,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
data
=
{}
to_process
=
list
(
items
)
course_key
=
self
.
_fill_in_run
(
course_key
)
while
to_process
and
depth
is
None
or
depth
>=
0
:
children
=
[]
for
item
in
to_process
:
...
...
@@ -581,6 +613,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
Load an XModuleDescriptor from item, using the children stored in data_cache
"""
course_key
=
self
.
_fill_in_run
(
course_key
)
location
=
Location
.
_from_deprecated_son
(
item
[
'location'
],
course_key
.
run
)
data_dir
=
getattr
(
item
,
'data_dir'
,
location
.
course
)
root
=
self
.
fs_root
/
data_dir
...
...
@@ -617,6 +650,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
Load a list of xmodules from the data in items, with children cached up
to specified depth
"""
course_key
=
self
.
_fill_in_run
(
course_key
)
data_cache
=
self
.
_cache_children
(
course_key
,
items
,
depth
)
# if we are loading a course object, if we're not prefetching children (depth != 0) then don't
...
...
@@ -669,6 +703,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
Get the course with the given courseid (org/course/run)
"""
assert
(
isinstance
(
course_key
,
SlashSeparatedCourseKey
))
course_key
=
self
.
_fill_in_run
(
course_key
)
location
=
course_key
.
make_usage_key
(
'course'
,
course_key
.
run
)
try
:
return
self
.
get_item
(
location
,
depth
=
depth
)
...
...
@@ -685,6 +720,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
otherwise, do a case sensitive search
"""
assert
(
isinstance
(
course_key
,
SlashSeparatedCourseKey
))
course_key
=
self
.
_fill_in_run
(
course_key
)
location
=
course_key
.
make_usage_key
(
'course'
,
course_key
.
run
)
if
ignore_case
:
course_query
=
location
.
to_deprecated_son
(
'_id.'
)
...
...
@@ -873,6 +909,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
:param runtime: if you already have an xblock from the course, the xblock.runtime value
:param fields: a dictionary of field names and values for the new xmodule
"""
location
=
location
.
replace
(
run
=
self
.
_fill_in_run
(
location
.
course_key
)
.
run
)
# differs from split mongo in that I believe most of this logic should be above the persistence
# layer but added it here to enable quick conversion. I'll need to reconcile these.
if
metadata
is
None
:
...
...
@@ -1073,6 +1110,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
Return an array of all of the locations (deprecated string format) for orphans in the course.
"""
course_key
=
self
.
_fill_in_run
(
course_key
)
detached_categories
=
[
name
for
name
,
__
in
XBlock
.
load_tagged_classes
(
"detached"
)]
query
=
self
.
_course_key_to_son
(
course_key
)
query
[
'_id.category'
]
=
{
'$nin'
:
detached_categories
}
...
...
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