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
6c4aae73
Commit
6c4aae73
authored
Dec 20, 2013
by
Don Mitchell
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1980 from edx/dhm/locator_block_id
Rename BlockUsageLocator's block_id field from usage_id
parents
1295e763
a46e112a
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
310 additions
and
296 deletions
+310
-296
cms/djangoapps/contentstore/tests/test_crud.py
+14
-5
cms/djangoapps/contentstore/views/assets.py
+1
-1
cms/djangoapps/contentstore/views/checklist.py
+1
-1
cms/djangoapps/contentstore/views/component.py
+2
-2
cms/djangoapps/contentstore/views/course.py
+5
-5
cms/djangoapps/contentstore/views/import_export.py
+3
-3
cms/djangoapps/contentstore/views/item.py
+2
-2
cms/djangoapps/contentstore/views/tabs.py
+1
-1
cms/djangoapps/contentstore/views/user.py
+1
-1
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
+23
-21
common/lib/xmodule/xmodule/modulestore/locator.py
+25
-26
common/lib/xmodule/xmodule/modulestore/split_migrator.py
+11
-11
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
+14
-14
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+89
-113
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
+29
-11
common/lib/xmodule/xmodule/modulestore/tests/test_locators.py
+5
-5
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
+6
-6
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
+77
-67
common/lib/xmodule/xmodule/x_module.py
+1
-1
No files found.
cms/djangoapps/contentstore/tests/test_crud.py
View file @
6c4aae73
...
...
@@ -64,7 +64,7 @@ class TemplateTests(unittest.TestCase):
self
.
assertIsInstance
(
test_chapter
,
SequenceDescriptor
)
# refetch parent which should now point to child
test_course
=
modulestore
(
'split'
)
.
get_course
(
test_chapter
.
location
)
self
.
assertIn
(
test_chapter
.
location
.
usage
_id
,
test_course
.
children
)
self
.
assertIn
(
test_chapter
.
location
.
block
_id
,
test_course
.
children
)
def
test_temporary_xblocks
(
self
):
"""
...
...
@@ -95,15 +95,20 @@ class TemplateTests(unittest.TestCase):
"""
try saving temporary xblocks
"""
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
org
=
'testx'
,
prettyid
=
'tempcourse'
,
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
org
=
'testx'
,
prettyid
=
'tempcourse'
,
display_name
=
'fun test course'
,
user_id
=
'testbot'
)
test_chapter
=
self
.
load_from_json
({
'category'
:
'chapter'
,
'fields'
:
{
'display_name'
:
'chapter n'
}},
test_course
.
system
,
parent_xblock
=
test_course
)
test_def_content
=
'<problem>boo</problem>'
# create child
_
=
self
.
load_from_json
({
'category'
:
'problem'
,
'fields'
:
{
'data'
:
test_def_content
}},
self
.
load_from_json
({
'category'
:
'problem'
,
'fields'
:
{
'data'
:
test_def_content
,
'display_name'
:
'problem'
}},
test_course
.
system
,
parent_xblock
=
test_chapter
)
# better to pass in persisted parent over the subdag so
# subdag gets the parent pointer (otherwise 2 ops, persist dag, update parent children,
...
...
@@ -117,6 +122,10 @@ class TemplateTests(unittest.TestCase):
persisted_problem
=
persisted_chapter
.
get_children
()[
0
]
self
.
assertEqual
(
persisted_problem
.
category
,
'problem'
)
self
.
assertEqual
(
persisted_problem
.
data
,
test_def_content
)
# update it
persisted_problem
.
display_name
=
'altered problem'
persisted_problem
=
modulestore
(
'split'
)
.
persist_xblock_dag
(
persisted_problem
,
'testbot'
)
self
.
assertEqual
(
persisted_problem
.
display_name
,
'altered problem'
)
def
test_delete_course
(
self
):
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
...
...
@@ -166,7 +175,7 @@ class TemplateTests(unittest.TestCase):
second_problem
=
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'problem 2'
,
parent_location
=
BlockUsageLocator
(
updated_loc
,
usage_id
=
sub
.
location
.
usage
_id
),
parent_location
=
BlockUsageLocator
(
updated_loc
,
block_id
=
sub
.
location
.
block
_id
),
user_id
=
'testbot'
,
category
=
'problem'
,
data
=
"<problem></problem>"
)
...
...
cms/djangoapps/contentstore/views/assets.py
View file @
6c4aae73
...
...
@@ -51,7 +51,7 @@ def assets_handler(request, tag=None, course_id=None, branch=None, version_guid=
DELETE
json: delete an asset
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
cms/djangoapps/contentstore/views/checklist.py
View file @
6c4aae73
...
...
@@ -36,7 +36,7 @@ def checklists_handler(request, tag=None, course_id=None, branch=None, version_g
POST or PUT
json: updates the checked state for items within a particular checklist. checklist_index is required.
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
cms/djangoapps/contentstore/views/component.py
View file @
6c4aae73
...
...
@@ -62,7 +62,7 @@ def subsection_handler(request, tag=None, course_id=None, branch=None, version_g
json: not currently supported
"""
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
try
:
old_location
,
course
,
item
,
lms_link
=
_get_item_in_course
(
request
,
locator
)
except
ItemNotFoundError
:
...
...
@@ -151,7 +151,7 @@ def unit_handler(request, tag=None, course_id=None, branch=None, version_guid=No
json: not currently supported
"""
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
try
:
old_location
,
course
,
item
,
lms_link
=
_get_item_in_course
(
request
,
locator
)
except
ItemNotFoundError
:
...
...
cms/djangoapps/contentstore/views/course.py
View file @
6c4aae73
...
...
@@ -11,7 +11,7 @@ from django.utils.translation import ugettext as _
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django.conf
import
settings
from
django.views.decorators.http
import
require_http_methods
,
require_POST
from
django.views.decorators.http
import
require_http_methods
from
django.core.exceptions
import
PermissionDenied
from
django.core.urlresolvers
import
reverse
from
django.http
import
HttpResponseBadRequest
,
HttpResponseNotFound
...
...
@@ -60,12 +60,12 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler'
'textbooks_list_handler'
,
'textbooks_detail_handler'
]
def
_get_locator_and_course
(
course_id
,
branch
,
version_guid
,
usage
_id
,
user
,
depth
=
0
):
def
_get_locator_and_course
(
course_id
,
branch
,
version_guid
,
block
_id
,
user
,
depth
=
0
):
"""
Internal method used to calculate and return the locator and course module
for the view functions in this file.
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
usage
_id
)
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block_id
=
block
_id
)
if
not
has_access
(
user
,
locator
):
raise
PermissionDenied
()
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
...
...
@@ -104,7 +104,7 @@ def course_handler(request, tag=None, course_id=None, branch=None, version_guid=
return
create_new_course
(
request
)
elif
not
has_access
(
request
.
user
,
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
):
raise
PermissionDenied
()
elif
request
.
method
==
'PUT'
:
...
...
@@ -366,7 +366,7 @@ def course_info_update_handler(request, tag=None, course_id=None, branch=None, v
"""
if
'application/json'
not
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'application/json'
):
return
HttpResponseBadRequest
(
"Only supports json requests"
)
updates_locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
updates_locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
updates_location
=
loc_mapper
()
.
translate_locator_to_location
(
updates_locator
)
if
provided_id
==
''
:
provided_id
=
None
...
...
cms/djangoapps/contentstore/views/import_export.py
View file @
6c4aae73
...
...
@@ -60,7 +60,7 @@ def import_handler(request, tag=None, course_id=None, branch=None, version_guid=
POST or PUT
json: import a course via the .tar.gz file specified in request.FILES
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
@@ -271,7 +271,7 @@ def import_status_handler(request, tag=None, course_id=None, branch=None, versio
3 : Importing to mongo
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
@@ -302,7 +302,7 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
If the tar.gz file has been requested but the export operation fails, an HTML page will be returned
which describes the error.
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
cms/djangoapps/contentstore/views/item.py
View file @
6c4aae73
...
...
@@ -79,7 +79,7 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
The locator (and old-style id) for the created xblock (minus children) is returned.
"""
if
course_id
is
not
None
:
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
locator
):
raise
PermissionDenied
()
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
...
...
@@ -330,7 +330,7 @@ def orphan_handler(request, tag=None, course_id=None, branch=None, version_guid=
:param request:
:param course_id: Locator syntax course_id
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
# DHM: when split becomes back-end, move or conditionalize this conversion
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
location
)
if
request
.
method
==
'GET'
:
...
...
cms/djangoapps/contentstore/views/tabs.py
View file @
6c4aae73
...
...
@@ -62,7 +62,7 @@ def tabs_handler(request, tag=None, course_id=None, branch=None, version_guid=No
Creating a tab, deleting a tab, or changing its contents is not supported through this method.
Instead use the general xblock URL (see item.xblock_handler).
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
locator
):
raise
PermissionDenied
()
...
...
cms/djangoapps/contentstore/views/user.py
View file @
6c4aae73
...
...
@@ -52,7 +52,7 @@ def course_team_handler(request, tag=None, course_id=None, branch=None, version_
DELETE:
json: remove a particular course team member from the course team (email is required).
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage
_id
=
block
)
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
block
_id
=
block
)
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
...
...
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
View file @
6c4aae73
...
...
@@ -6,7 +6,7 @@ import re
import
pymongo
import
bson.son
from
xmodule.modulestore.exceptions
import
InvalidLocationError
,
ItemNotFoundError
,
DuplicateItemError
from
xmodule.modulestore.exceptions
import
InvalidLocationError
,
ItemNotFoundError
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore
import
Location
import
urllib
...
...
@@ -156,27 +156,27 @@ class LocMapperStore(object):
entry
=
item
break
usage
_id
=
entry
[
'block_map'
]
.
get
(
self
.
_encode_for_mongo
(
location
.
name
))
if
usage
_id
is
None
:
block
_id
=
entry
[
'block_map'
]
.
get
(
self
.
_encode_for_mongo
(
location
.
name
))
if
block
_id
is
None
:
if
add_entry_if_missing
:
usage
_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
block
_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
else
:
raise
ItemNotFoundError
(
location
)
elif
isinstance
(
usage
_id
,
dict
):
elif
isinstance
(
block
_id
,
dict
):
# name is not unique, look through for the right category
if
location
.
category
in
usage
_id
:
usage_id
=
usage
_id
[
location
.
category
]
if
location
.
category
in
block
_id
:
block_id
=
block
_id
[
location
.
category
]
elif
add_entry_if_missing
:
usage
_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
block
_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
else
:
raise
ItemNotFoundError
()
else
:
raise
InvalidLocationError
()
published_usage
=
BlockUsageLocator
(
course_id
=
entry
[
'course_id'
],
branch
=
entry
[
'prod_branch'
],
usage_id
=
usage
_id
)
course_id
=
entry
[
'course_id'
],
branch
=
entry
[
'prod_branch'
],
block_id
=
block
_id
)
draft_usage
=
BlockUsageLocator
(
course_id
=
entry
[
'course_id'
],
branch
=
entry
[
'draft_branch'
],
usage_id
=
usage
_id
)
course_id
=
entry
[
'course_id'
],
branch
=
entry
[
'draft_branch'
],
block_id
=
block
_id
)
if
published
:
result
=
published_usage
else
:
...
...
@@ -191,7 +191,7 @@ class LocMapperStore(object):
Returns an old style Location for the given Locator if there's an appropriate entry in the
mapping collection. Note, it requires that the course was previously mapped (a side effect of
translate_location or explicitly via create_map_entry) and
the block's
usage
_id was previously stored in the
the block's
block
_id was previously stored in the
map (a side effect of translate_location or via add|update_block_location).
If get_course, then rather than finding the map for this locator, it finds the 'course' root
...
...
@@ -200,7 +200,7 @@ class LocMapperStore(object):
If there are no matches, it returns None.
If there's more than one location to locator mapping to the same course_id, it looks for the first
one with a mapping for the block
usage
_id and picks that arbitrary course location.
one with a mapping for the block
block
_id and picks that arbitrary course location.
:param locator: a BlockUsageLocator
"""
...
...
@@ -214,14 +214,14 @@ class LocMapperStore(object):
# This does not require that the course exist in any modulestore
# only that it has a mapping entry.
maps
=
self
.
location_map
.
find
({
'course_id'
:
locator
.
course_id
})
# look for one which maps to this block
usage
_id
# look for one which maps to this block
block
_id
if
maps
.
count
()
==
0
:
return
None
result
=
None
for
candidate
in
maps
:
old_course_id
=
self
.
_generate_location_course_id
(
candidate
[
'_id'
])
for
old_name
,
cat_to_usage
in
candidate
[
'block_map'
]
.
iteritems
():
for
category
,
usage
_id
in
cat_to_usage
.
iteritems
():
for
category
,
block
_id
in
cat_to_usage
.
iteritems
():
# cache all entries and then figure out if we have the one we want
# Always return revision=None because the
# old draft module store wraps locations as draft before
...
...
@@ -234,16 +234,16 @@ class LocMapperStore(object):
self
.
_decode_from_mongo
(
old_name
),
None
)
published_locator
=
BlockUsageLocator
(
candidate
[
'course_id'
],
branch
=
candidate
[
'prod_branch'
],
usage_id
=
usage
_id
candidate
[
'course_id'
],
branch
=
candidate
[
'prod_branch'
],
block_id
=
block
_id
)
draft_locator
=
BlockUsageLocator
(
candidate
[
'course_id'
],
branch
=
candidate
[
'draft_branch'
],
usage_id
=
usage
_id
candidate
[
'course_id'
],
branch
=
candidate
[
'draft_branch'
],
block_id
=
block
_id
)
self
.
_cache_location_map_entry
(
old_course_id
,
location
,
published_locator
,
draft_locator
)
if
get_course
and
category
==
'course'
:
result
=
location
elif
not
get_course
and
usage_id
==
locator
.
usage
_id
:
elif
not
get_course
and
block_id
==
locator
.
block
_id
:
result
=
location
if
result
is
not
None
:
return
result
...
...
@@ -256,13 +256,15 @@ class LocMapperStore(object):
# The downside is that if there's more than one course mapped to from the same org/course root
# the block ids will likely be out of sync and collide from an id perspective. HOWEVER,
# if there are few == org/course roots or their content is unrelated, this will work well.
usage
_id
=
self
.
_verify_uniqueness
(
location
.
category
+
location
.
name
[:
3
],
block_map
)
block
_id
=
self
.
_verify_uniqueness
(
location
.
category
+
location
.
name
[:
3
],
block_map
)
else
:
usage_id
=
location
.
name
# if 2 different category locations had same name, then they'll collide. Make the later
# mapped ones unique
block_id
=
self
.
_verify_uniqueness
(
location
.
name
,
block_map
)
encoded_location_name
=
self
.
_encode_for_mongo
(
location
.
name
)
block_map
.
setdefault
(
encoded_location_name
,
{})[
location
.
category
]
=
usage
_id
block_map
.
setdefault
(
encoded_location_name
,
{})[
location
.
category
]
=
block
_id
self
.
location_map
.
update
(
location_id
,
{
'$set'
:
{
'block_map'
:
block_map
}})
return
usage
_id
return
block
_id
def
_interpret_location_course_id
(
self
,
course_id
,
location
):
"""
...
...
common/lib/xmodule/xmodule/modulestore/locator.py
View file @
6c4aae73
...
...
@@ -257,7 +257,7 @@ class CourseLocator(Locator):
Returns a copy of itself (downcasting) as a CourseLocator.
The copy has the same CourseLocator fields as the original.
The copy does not include subclass information, such as
a
usage
_id (a property of BlockUsageLocator).
a
block
_id (a property of BlockUsageLocator).
"""
return
CourseLocator
(
course_id
=
self
.
course_id
,
version_guid
=
self
.
version_guid
,
...
...
@@ -298,8 +298,8 @@ class CourseLocator(Locator):
self
.
_set_value
(
parse
,
'version_guid'
,
lambda
(
new_guid
):
self
.
set_version_guid
(
self
.
as_object_id
(
new_guid
))
)
self
.
_set_value
(
parse
,
'course_id'
,
lambda
(
new_id
):
self
.
set_course_id
(
new_id
)
)
self
.
_set_value
(
parse
,
'branch'
,
lambda
(
new_branch
):
self
.
set_branch
(
new_branch
)
)
self
.
_set_value
(
parse
,
'course_id'
,
self
.
set_course_id
)
self
.
_set_value
(
parse
,
'branch'
,
self
.
set_branch
)
def
init_from_version_guid
(
self
,
version_guid
):
"""
...
...
@@ -390,23 +390,23 @@ class BlockUsageLocator(CourseLocator):
"""
# Default value
usage
_id
=
None
block
_id
=
None
def
__init__
(
self
,
url
=
None
,
version_guid
=
None
,
course_id
=
None
,
branch
=
None
,
usage
_id
=
None
):
branch
=
None
,
block
_id
=
None
):
"""
Construct a BlockUsageLocator
Caller may provide url, version_guid, or course_id, and optionally provide branch.
The
usage
_id may be specified, either explictly or as part of
The
block
_id may be specified, either explictly or as part of
the url or course_id. If omitted, the locator is created but it
has not yet been initialized.
Resulting BlockUsageLocator will have a
usage
_id property.
Resulting BlockUsageLocator will have a
block
_id property.
It will have either a version_guid property or a course_id (with optional branch) property, or both.
version_guid must be an instance of bson.objectid.ObjectId or None
url, course_id, branch, and
usage
_id must be strings or None
url, course_id, branch, and
block
_id must be strings or None
"""
self
.
_validate_args
(
url
,
version_guid
,
course_id
)
...
...
@@ -414,8 +414,8 @@ class BlockUsageLocator(CourseLocator):
self
.
init_block_ref_from_str
(
url
)
if
course_id
:
self
.
init_block_ref_from_course_id
(
course_id
)
if
usage
_id
:
self
.
init_block_ref
(
usage
_id
)
if
block
_id
:
self
.
init_block_ref
(
block
_id
)
super
(
BlockUsageLocator
,
self
)
.
__init__
(
url
=
url
,
version_guid
=
version_guid
,
...
...
@@ -425,9 +425,9 @@ class BlockUsageLocator(CourseLocator):
def
is_initialized
(
self
):
"""
Returns True if
usage
_id has been initialized, else returns False
Returns True if
block
_id has been initialized, else returns False
"""
return
self
.
usage
_id
is
not
None
return
self
.
block
_id
is
not
None
def
version_agnostic
(
self
):
"""
...
...
@@ -442,58 +442,57 @@ class BlockUsageLocator(CourseLocator):
if
self
.
version_guid
:
return
BlockUsageLocator
(
version_guid
=
self
.
version_guid
,
branch
=
self
.
branch
,
usage_id
=
self
.
usage
_id
)
block_id
=
self
.
block
_id
)
else
:
return
BlockUsageLocator
(
course_id
=
self
.
course_id
,
branch
=
self
.
branch
,
usage_id
=
self
.
usage
_id
)
block_id
=
self
.
block
_id
)
def
set_
usage
_id
(
self
,
new
):
def
set_
block
_id
(
self
,
new
):
"""
Initialize
usage
_id to new value.
If
usage
_id has already been initialized to a different value, raise an exception.
Initialize
block
_id to new value.
If
block
_id has already been initialized to a different value, raise an exception.
"""
self
.
set_property
(
'
usage
_id'
,
new
)
self
.
set_property
(
'
block
_id'
,
new
)
def
init_block_ref
(
self
,
block_ref
):
if
isinstance
(
block_ref
,
LocalId
):
self
.
set_
usage
_id
(
block_ref
)
self
.
set_
block
_id
(
block_ref
)
else
:
parse
=
parse_block_ref
(
block_ref
)
if
not
parse
:
raise
ValueError
(
'Could not parse "
%
s" as a block_ref'
%
block_ref
)
self
.
set_
usage
_id
(
parse
[
'block'
])
self
.
set_
block
_id
(
parse
[
'block'
])
def
init_block_ref_from_str
(
self
,
value
):
"""
Create a block locator from the given string which may be a url or just the repr (no tag)
"""
if
hasattr
(
value
,
'
usage
_id'
):
self
.
init_block_ref
(
value
.
usage
_id
)
if
hasattr
(
value
,
'
block
_id'
):
self
.
init_block_ref
(
value
.
block
_id
)
return
if
not
isinstance
(
value
,
basestring
):
return
None
parse
=
parse_url
(
value
,
tag_optional
=
True
)
if
parse
is
None
:
raise
ValueError
(
'Could not parse "
%
s" as a url'
%
value
)
self
.
_set_value
(
parse
,
'block'
,
lambda
(
new_block
):
self
.
set_usage_id
(
new_block
)
)
self
.
_set_value
(
parse
,
'block'
,
self
.
set_block_id
)
def
init_block_ref_from_course_id
(
self
,
course_id
):
if
isinstance
(
course_id
,
CourseLocator
):
# FIXME the parsed course_id should never contain a block ref
course_id
=
course_id
.
course_id
assert
course_id
,
"
%
s does not have a valid course_id"
parse
=
parse_course_id
(
course_id
)
if
parse
is
None
:
raise
ValueError
(
'Could not parse "
%
s" as a course_id'
%
course_id
)
self
.
_set_value
(
parse
,
'block'
,
lambda
(
new_block
):
self
.
set_usage_id
(
new_block
)
)
self
.
_set_value
(
parse
,
'block'
,
self
.
set_block_id
)
def
__unicode__
(
self
):
"""
Return a string representing this location.
"""
rep
=
super
(
BlockUsageLocator
,
self
)
.
__unicode__
()
return
rep
+
'/'
+
BLOCK_PREFIX
+
unicode
(
self
.
usage
_id
)
return
rep
+
'/'
+
BLOCK_PREFIX
+
unicode
(
self
.
block
_id
)
class
DefinitionLocator
(
Locator
):
...
...
common/lib/xmodule/xmodule/modulestore/split_migrator.py
View file @
6c4aae73
...
...
@@ -50,7 +50,7 @@ class SplitMigrator(object):
course_location
.
org
,
original_course
.
display_name
,
user_id
,
id_root
=
new_course_id
,
fields
=
self
.
_get_json_fields_translate_children
(
original_course
,
old_course_id
,
True
),
root_
usage_id
=
new_course_root_locator
.
usage
_id
,
root_
block_id
=
new_course_root_locator
.
block
_id
,
master_branch
=
new_course_root_locator
.
branch
)
...
...
@@ -74,13 +74,13 @@ class SplitMigrator(object):
# don't copy the course again. No drafts should get here but check
if
module
.
location
!=
old_course_loc
and
not
getattr
(
module
,
'is_draft'
,
False
):
# create split_xblock using split.create_item
# where
usage
_id is computed by translate_location_to_locator
# where
block
_id is computed by translate_location_to_locator
new_locator
=
self
.
loc_mapper
.
translate_location
(
old_course_id
,
module
.
location
,
True
,
add_entry_if_missing
=
True
)
_new_module
=
self
.
split_modulestore
.
create_item
(
course_version_locator
,
module
.
category
,
user_id
,
usage_id
=
new_locator
.
usage
_id
,
block_id
=
new_locator
.
block
_id
,
fields
=
self
.
_get_json_fields_translate_children
(
module
,
old_course_id
,
True
),
continue_version
=
True
)
...
...
@@ -130,18 +130,18 @@ class SplitMigrator(object):
# create a new course version just in case the current head is also the prod head
_new_module
=
self
.
split_modulestore
.
create_item
(
new_draft_course_loc
,
module
.
category
,
user_id
,
usage_id
=
new_locator
.
usage
_id
,
block_id
=
new_locator
.
block
_id
,
fields
=
self
.
_get_json_fields_translate_children
(
module
,
old_course_id
,
True
)
)
awaiting_adoption
[
module
.
location
]
=
new_locator
.
usage
_id
for
draft_location
,
new_
usage
_id
in
awaiting_adoption
.
iteritems
():
awaiting_adoption
[
module
.
location
]
=
new_locator
.
block
_id
for
draft_location
,
new_
block
_id
in
awaiting_adoption
.
iteritems
():
for
parent_loc
in
self
.
draft_modulestore
.
get_parent_locations
(
draft_location
,
old_course_id
):
old_parent
=
self
.
draft_modulestore
.
get_item
(
parent_loc
)
new_parent
=
self
.
split_modulestore
.
get_item
(
self
.
loc_mapper
.
translate_location
(
old_course_id
,
old_parent
.
location
,
False
)
)
# this only occurs if the parent was also awaiting adoption
if
new_
usage
_id
in
new_parent
.
children
:
if
new_
block
_id
in
new_parent
.
children
:
break
# find index for module: new_parent may be missing quite a few of old_parent's children
new_parent_cursor
=
0
...
...
@@ -152,15 +152,15 @@ class SplitMigrator(object):
sibling_loc
=
self
.
loc_mapper
.
translate_location
(
old_course_id
,
Location
(
old_child_loc
),
False
)
# sibling may move cursor
for
idx
in
range
(
new_parent_cursor
,
len
(
new_parent
.
children
)):
if
new_parent
.
children
[
idx
]
==
sibling_loc
.
usage
_id
:
if
new_parent
.
children
[
idx
]
==
sibling_loc
.
block
_id
:
new_parent_cursor
=
idx
+
1
break
new_parent
.
children
.
insert
(
new_parent_cursor
,
new_
usage
_id
)
new_parent
.
children
.
insert
(
new_parent_cursor
,
new_
block
_id
)
new_parent
=
self
.
split_modulestore
.
update_item
(
new_parent
,
user_id
)
def
_get_json_fields_translate_children
(
self
,
xblock
,
old_course_id
,
published
):
"""
Return the json repr for explicitly set fields but convert all children to their
usage
_id's
Return the json repr for explicitly set fields but convert all children to their
block
_id's
"""
fields
=
self
.
get_json_fields_explicitly_set
(
xblock
)
# this will too generously copy the children even for ones that don't exist in the published b/c the old mongo
...
...
@@ -169,7 +169,7 @@ class SplitMigrator(object):
fields
[
'children'
]
=
[
self
.
loc_mapper
.
translate_location
(
old_course_id
,
Location
(
child
),
published
,
add_entry_if_missing
=
True
)
.
usage
_id
)
.
block
_id
for
child
in
fields
[
'children'
]]
return
fields
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
View file @
6c4aae73
...
...
@@ -47,26 +47,26 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
self
.
default_class
=
default_class
self
.
local_modules
=
{}
def
_load_item
(
self
,
usage
_id
,
course_entry_override
=
None
):
if
isinstance
(
usage_id
,
BlockUsageLocator
)
and
isinstance
(
usage_id
.
usage
_id
,
LocalId
):
def
_load_item
(
self
,
block
_id
,
course_entry_override
=
None
):
if
isinstance
(
block_id
,
BlockUsageLocator
)
and
isinstance
(
block_id
.
block
_id
,
LocalId
):
try
:
return
self
.
local_modules
[
usage
_id
]
return
self
.
local_modules
[
block
_id
]
except
KeyError
:
raise
ItemNotFoundError
json_data
=
self
.
module_data
.
get
(
usage
_id
)
json_data
=
self
.
module_data
.
get
(
block
_id
)
if
json_data
is
None
:
# deeper than initial descendant fetch or doesn't exist
self
.
modulestore
.
cache_items
(
self
,
[
usage
_id
],
lazy
=
self
.
lazy
)
json_data
=
self
.
module_data
.
get
(
usage
_id
)
self
.
modulestore
.
cache_items
(
self
,
[
block
_id
],
lazy
=
self
.
lazy
)
json_data
=
self
.
module_data
.
get
(
block
_id
)
if
json_data
is
None
:
raise
ItemNotFoundError
(
usage
_id
)
raise
ItemNotFoundError
(
block
_id
)
class_
=
XModuleDescriptor
.
load_class
(
json_data
.
get
(
'category'
),
self
.
default_class
)
return
self
.
xblock_from_json
(
class_
,
usage
_id
,
json_data
,
course_entry_override
)
return
self
.
xblock_from_json
(
class_
,
block
_id
,
json_data
,
course_entry_override
)
# xblock's runtime does not always pass enough contextual information to figure out
# which named container (course x branch) or which parent is requesting an item. Because split allows
...
...
@@ -79,7 +79,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# low; thus, the course_entry is most likely correct. If the thread is looking at > 1 named container
# pointing to the same structure, the access is likely to be chunky enough that the last known container
# is the intended one when not given a course_entry_override; thus, the caching of the last branch/course_id.
def
xblock_from_json
(
self
,
class_
,
usage
_id
,
json_data
,
course_entry_override
=
None
):
def
xblock_from_json
(
self
,
class_
,
block
_id
,
json_data
,
course_entry_override
=
None
):
if
course_entry_override
is
None
:
course_entry_override
=
self
.
course_entry
else
:
...
...
@@ -91,12 +91,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
definition_id
=
self
.
modulestore
.
definition_locator
(
definition
)
# If no usage id is provided, generate an in-memory id
if
usage
_id
is
None
:
usage
_id
=
LocalId
()
if
block
_id
is
None
:
block
_id
=
LocalId
()
block_locator
=
BlockUsageLocator
(
version_guid
=
course_entry_override
[
'structure'
][
'_id'
],
usage_id
=
usage
_id
,
block_id
=
block
_id
,
course_id
=
course_entry_override
.
get
(
'course_id'
),
branch
=
course_entry_override
.
get
(
'branch'
)
)
...
...
@@ -121,7 +121,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
self
,
BlockUsageLocator
(
version_guid
=
course_entry_override
[
'structure'
][
'_id'
],
usage_id
=
usage
_id
block_id
=
block
_id
),
error_msg
=
exc_info_to_str
(
sys
.
exc_info
())
)
...
...
@@ -136,7 +136,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
module
.
save
()
# If this is an in-memory block, store it in this system
if
isinstance
(
block_locator
.
usage
_id
,
LocalId
):
if
isinstance
(
block_locator
.
block
_id
,
LocalId
):
self
.
local_modules
[
block_locator
]
=
module
return
module
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
6c4aae73
...
...
@@ -11,7 +11,7 @@ Representation:
** 'versions': versions_dict: {branch_id: structure_id, ...}
* structure:
** '_id': an ObjectId (guid),
** 'root': root_
usage
_id (string of key in 'blocks' for the root of this structure,
** 'root': root_
block
_id (string of key in 'blocks' for the root of this structure,
** 'previous_version': the structure from which this one was derived. For published courses, this
points to the previously published version of the structure not the draft published to this.
** 'original_version': the original structure id in the previous_version relation. Is a pseudo object
...
...
@@ -130,20 +130,20 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# the split mongo store is used for item creation as well as item persistence
self
.
mixologist
=
Mixologist
(
self
.
xblock_mixins
)
def
cache_items
(
self
,
system
,
base_
usage
_ids
,
depth
=
0
,
lazy
=
True
):
def
cache_items
(
self
,
system
,
base_
block
_ids
,
depth
=
0
,
lazy
=
True
):
'''
Handles caching of items once inheritance and any other one time
per course per fetch operations are done.
:param system: a CachingDescriptorSystem
:param base_
usage_ids: list of usage
_ids to fetch
:param base_
block_ids: list of block
_ids to fetch
:param depth: how deep below these to prefetch
:param lazy: whether to fetch definitions or use placeholders
'''
new_module_data
=
{}
for
usage_id
in
base_usage
_ids
:
for
block_id
in
base_block
_ids
:
new_module_data
=
self
.
descendants
(
system
.
course_entry
[
'structure'
][
'blocks'
],
usage
_id
,
block
_id
,
depth
,
new_module_data
)
...
...
@@ -167,7 +167,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
system
.
module_data
.
update
(
new_module_data
)
return
system
.
module_data
def
_load_items
(
self
,
course_entry
,
usage
_ids
,
depth
=
0
,
lazy
=
True
):
def
_load_items
(
self
,
course_entry
,
block
_ids
,
depth
=
0
,
lazy
=
True
):
'''
Load & cache the given blocks from the course. Prefetch down to the
given depth. Load the definitions into each block if lazy is False;
...
...
@@ -187,8 +187,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
mixins
=
self
.
xblock_mixins
)
self
.
_add_cache
(
course_entry
[
'structure'
][
'_id'
],
system
)
self
.
cache_items
(
system
,
usage
_ids
,
depth
,
lazy
)
return
[
system
.
load_item
(
usage_id
,
course_entry
)
for
usage_id
in
usage
_ids
]
self
.
cache_items
(
system
,
block
_ids
,
depth
,
lazy
)
return
[
system
.
load_item
(
block_id
,
course_entry
)
for
block_id
in
block
_ids
]
def
_get_cache
(
self
,
course_version_guid
):
"""
...
...
@@ -333,7 +333,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
the course or the block w/in the course do not exist for the given version.
raises InsufficientSpecificationError if the locator does not id a block
"""
if
block_location
.
usage
_id
is
None
:
if
block_location
.
block
_id
is
None
:
raise
InsufficientSpecificationError
(
block_location
)
try
:
course_structure
=
self
.
_lookup_course
(
block_location
)[
'structure'
]
...
...
@@ -341,7 +341,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# this error only occurs if the course does not exist
return
False
return
course_structure
[
'blocks'
]
.
get
(
block_location
.
usage
_id
)
is
not
None
return
course_structure
[
'blocks'
]
.
get
(
block_location
.
block
_id
)
is
not
None
def
get_item
(
self
,
location
,
depth
=
0
):
"""
...
...
@@ -365,7 +365,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
not
location
.
is_initialized
():
raise
InsufficientSpecificationError
(
"Not yet initialized:
%
s"
%
location
)
course
=
self
.
_lookup_course
(
location
)
items
=
self
.
_load_items
(
course
,
[
location
.
usage
_id
],
depth
,
lazy
=
True
)
items
=
self
.
_load_items
(
course
,
[
location
.
block
_id
],
depth
,
lazy
=
True
)
if
len
(
items
)
==
0
:
raise
ItemNotFoundError
(
location
)
return
items
[
0
]
...
...
@@ -397,9 +397,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
qualifiers
=
{}
course
=
self
.
_lookup_course
(
locator
)
items
=
[]
for
usage
_id
,
value
in
course
[
'structure'
][
'blocks'
]
.
iteritems
():
for
block
_id
,
value
in
course
[
'structure'
][
'blocks'
]
.
iteritems
():
if
self
.
_block_matches
(
value
,
qualifiers
):
items
.
append
(
usage
_id
)
items
.
append
(
block
_id
)
if
len
(
items
)
>
0
:
return
self
.
_load_items
(
course
,
items
,
0
,
lazy
=
True
)
...
...
@@ -421,16 +421,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def
get_parent_locations
(
self
,
locator
,
course_id
=
None
):
'''
Return the locations (Locators w/
usage
_ids) for the parents of this location in this
course. Could use get_items(location, {'children':
usage
_id}) but this is slightly faster.
NOTE: the locator must contain the
usage_id, and this code does not actually ensure usage
_id exists
Return the locations (Locators w/
block
_ids) for the parents of this location in this
course. Could use get_items(location, {'children':
block
_id}) but this is slightly faster.
NOTE: the locator must contain the
block_id, and this code does not actually ensure block
_id exists
:param locator: BlockUsageLocator restricting search scope
:param
usage_id: ignored. Only included for API compatibility. Specify the usag
e_id within the locator.
:param
course_id: ignored. Only included for API compatibility. Specify the cours
e_id within the locator.
'''
course
=
self
.
_lookup_course
(
locator
)
items
=
self
.
_get_parents_from_structure
(
locator
.
usage
_id
,
course
[
'structure'
])
return
[
BlockUsageLocator
(
url
=
locator
.
as_course_locator
(),
usage
_id
=
parent_id
)
items
=
self
.
_get_parents_from_structure
(
locator
.
block
_id
,
course
[
'structure'
])
return
[
BlockUsageLocator
(
url
=
locator
.
as_course_locator
(),
block
_id
=
parent_id
)
for
parent_id
in
items
]
def
get_orphans
(
self
,
course_id
,
detached_categories
,
branch
):
...
...
@@ -447,7 +447,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
block_data
[
'category'
]
in
detached_categories
:
items
.
discard
(
block_id
)
return
[
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
usage
_id
=
block_id
)
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
block
_id
=
block_id
)
for
block_id
in
items
]
...
...
@@ -547,25 +547,25 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'''
# version_agnostic means we don't care if the head and version don't align, trust the version
course_struct
=
self
.
_lookup_course
(
block_locator
.
version_agnostic
())[
'structure'
]
usage_id
=
block_locator
.
usage
_id
update_version_field
=
'blocks.{}.edit_info.update_version'
.
format
(
usage
_id
)
block_id
=
block_locator
.
block
_id
update_version_field
=
'blocks.{}.edit_info.update_version'
.
format
(
block
_id
)
all_versions_with_block
=
self
.
db_connection
.
find_matching_structures
({
'original_version'
:
course_struct
[
'original_version'
],
update_version_field
:
{
'$exists'
:
True
}})
# find (all) root versions and build map previous: [successors]
possible_roots
=
[]
result
=
{}
for
version
in
all_versions_with_block
:
if
version
[
'_id'
]
==
version
[
'blocks'
][
usage
_id
][
'edit_info'
][
'update_version'
]:
if
version
[
'blocks'
][
usage
_id
][
'edit_info'
]
.
get
(
'previous_version'
)
is
None
:
possible_roots
.
append
(
version
[
'blocks'
][
usage
_id
][
'edit_info'
][
'update_version'
])
if
version
[
'_id'
]
==
version
[
'blocks'
][
block
_id
][
'edit_info'
][
'update_version'
]:
if
version
[
'blocks'
][
block
_id
][
'edit_info'
]
.
get
(
'previous_version'
)
is
None
:
possible_roots
.
append
(
version
[
'blocks'
][
block
_id
][
'edit_info'
][
'update_version'
])
else
:
result
.
setdefault
(
version
[
'blocks'
][
usage
_id
][
'edit_info'
][
'previous_version'
],
set
())
.
add
(
version
[
'blocks'
][
usage
_id
][
'edit_info'
][
'update_version'
])
result
.
setdefault
(
version
[
'blocks'
][
block
_id
][
'edit_info'
][
'previous_version'
],
set
())
.
add
(
version
[
'blocks'
][
block
_id
][
'edit_info'
][
'update_version'
])
# more than one possible_root means usage was added and deleted > 1x.
if
len
(
possible_roots
)
>
1
:
# find the history segment including block_locator's version
element_to_find
=
course_struct
[
'blocks'
][
usage
_id
][
'edit_info'
][
'update_version'
]
element_to_find
=
course_struct
[
'blocks'
][
block
_id
][
'edit_info'
][
'update_version'
]
if
element_to_find
in
possible_roots
:
possible_roots
=
[
element_to_find
]
for
possibility
in
possible_roots
:
...
...
@@ -576,9 +576,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
return
None
# convert the results value sets to locators
for
k
,
versions
in
result
.
iteritems
():
result
[
k
]
=
[
BlockUsageLocator
(
version_guid
=
version
,
usage_id
=
usage
_id
)
result
[
k
]
=
[
BlockUsageLocator
(
version_guid
=
version
,
block_id
=
block
_id
)
for
version
in
versions
]
return
VersionTree
(
BlockUsageLocator
(
version_guid
=
possible_roots
[
0
],
usage_id
=
usage
_id
),
result
)
return
VersionTree
(
BlockUsageLocator
(
version_guid
=
possible_roots
[
0
],
block_id
=
block
_id
),
result
)
def
get_definition_successors
(
self
,
definition_locator
,
version_history_depth
=
1
):
'''
...
...
@@ -646,7 +646,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else
:
return
definition_locator
,
False
def
_generate_
usage
_id
(
self
,
course_blocks
,
category
):
def
_generate_
block
_id
(
self
,
course_blocks
,
category
):
"""
Generate a somewhat readable block id unique w/in this course using the category
:param course_blocks: the current list of blocks.
...
...
@@ -687,7 +687,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# validation behavior a responsibility of the model layer rather than the persistence layer.
def
create_item
(
self
,
course_or_parent_locator
,
category
,
user_id
,
usage
_id
=
None
,
definition_locator
=
None
,
fields
=
None
,
block
_id
=
None
,
definition_locator
=
None
,
fields
=
None
,
force
=
False
,
continue_version
=
False
):
"""
...
...
@@ -712,7 +712,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
to the existing definition. If fields contains Scope.content and definition_locator is not None, then
the Scope.content fields are assumed to be a new payload for definition_locator.
:param
usage
_id: if provided, must not already exist in the structure. Provides the block id for the
:param
block
_id: if provided, must not already exist in the structure. Provides the block id for the
new item in this structure. Otherwise, one is computed using the category appended w/ a few digits.
:param continue_version: continue changing the current structure at the head of the course. Very dangerous
...
...
@@ -755,18 +755,18 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
new_id
=
new_structure
[
'_id'
]
# generate usage id
if
usage
_id
is
not
None
:
if
usage
_id
in
new_structure
[
'blocks'
]:
raise
DuplicateItemError
(
usage
_id
,
self
,
'structures'
)
if
block
_id
is
not
None
:
if
block
_id
in
new_structure
[
'blocks'
]:
raise
DuplicateItemError
(
block
_id
,
self
,
'structures'
)
else
:
new_
usage_id
=
usage
_id
new_
block_id
=
block
_id
else
:
new_
usage_id
=
self
.
_generate_usage
_id
(
new_structure
[
'blocks'
],
category
)
new_
block_id
=
self
.
_generate_block
_id
(
new_structure
[
'blocks'
],
category
)
block_fields
=
partitioned_fields
.
get
(
Scope
.
settings
,
{})
if
Scope
.
children
in
partitioned_fields
:
block_fields
.
update
(
partitioned_fields
[
Scope
.
children
])
new_structure
[
'blocks'
][
new_
usage
_id
]
=
{
new_structure
[
'blocks'
][
new_
block
_id
]
=
{
"category"
:
category
,
"definition"
:
definition_locator
.
definition_id
,
"fields"
:
block_fields
,
...
...
@@ -780,9 +780,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# if given parent, add new block as child and update parent's version
parent
=
None
if
isinstance
(
course_or_parent_locator
,
BlockUsageLocator
)
and
course_or_parent_locator
.
usage
_id
is
not
None
:
parent
=
new_structure
[
'blocks'
][
course_or_parent_locator
.
usage
_id
]
parent
[
'fields'
]
.
setdefault
(
'children'
,
[])
.
append
(
new_
usage
_id
)
if
isinstance
(
course_or_parent_locator
,
BlockUsageLocator
)
and
course_or_parent_locator
.
block
_id
is
not
None
:
parent
=
new_structure
[
'blocks'
][
course_or_parent_locator
.
block
_id
]
parent
[
'fields'
]
.
setdefault
(
'children'
,
[])
.
append
(
new_
block
_id
)
if
not
continue_version
or
parent
[
'edit_info'
][
'update_version'
]
!=
structure
[
'_id'
]:
parent
[
'edit_info'
][
'edited_on'
]
=
datetime
.
datetime
.
now
(
UTC
)
parent
[
'edit_info'
][
'edited_by'
]
=
user_id
...
...
@@ -806,13 +806,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# reconstruct the new_item from the cache
return
self
.
get_item
(
BlockUsageLocator
(
course_id
=
course_parent
,
usage_id
=
new_usage
_id
,
block_id
=
new_block
_id
,
version_guid
=
new_id
))
def
create_course
(
self
,
org
,
prettyid
,
user_id
,
id_root
=
None
,
fields
=
None
,
master_branch
=
'draft'
,
versions_dict
=
None
,
root_category
=
'course'
,
root_
usage
_id
=
'course'
root_
block
_id
=
'course'
):
"""
Create a new entry in the active courses index which points to an existing or new structure. Returns
...
...
@@ -870,7 +870,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self
.
db_connection
.
insert_definition
(
definition_entry
)
draft_structure
=
self
.
_new_structure
(
user_id
,
root_
usage
_id
,
root_category
,
block_fields
,
definition_id
user_id
,
root_
block
_id
,
root_category
,
block_fields
,
definition_id
)
new_id
=
draft_structure
[
'_id'
]
...
...
@@ -944,10 +944,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
descriptor
.
definition_locator
,
is_updated
=
self
.
update_definition_from_data
(
descriptor
.
definition_locator
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
),
user_id
)
# check children
original_entry
=
original_structure
[
'blocks'
][
descriptor
.
location
.
usage
_id
]
i
f
(
not
is_updated
and
descriptor
.
has_children
and
not
self
.
_xblock_lists_equal
(
original_entry
[
'fields'
][
'children'
],
descriptor
.
children
)):
is_updated
=
True
original_entry
=
original_structure
[
'blocks'
][
descriptor
.
location
.
block
_id
]
i
s_updated
=
is_updated
or
(
descriptor
.
has_children
and
original_entry
[
'fields'
][
'children'
]
!=
descriptor
.
children
)
# check metadata
if
not
is_updated
:
is_updated
=
self
.
_compare_settings
(
...
...
@@ -958,12 +958,12 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# if updated, rev the structure
if
is_updated
:
new_structure
=
self
.
_version_structure
(
original_structure
,
user_id
)
block_data
=
new_structure
[
'blocks'
][
descriptor
.
location
.
usage
_id
]
block_data
=
new_structure
[
'blocks'
][
descriptor
.
location
.
block
_id
]
block_data
[
"definition"
]
=
descriptor
.
definition_locator
.
definition_id
block_data
[
"fields"
]
=
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
if
descriptor
.
has_children
:
block_data
[
'fields'
][
"children"
]
=
[
self
.
_usage_id
(
child
)
for
child
in
descriptor
.
children
]
block_data
[
'fields'
][
"children"
]
=
descriptor
.
children
new_id
=
new_structure
[
'_id'
]
block_data
[
'edit_info'
]
=
{
...
...
@@ -990,7 +990,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
create or update the xblock and all of its children. The xblock's location must specify a course.
If it doesn't specify a usage_id, then it's presumed to be new and need creation. This function
descends the children performing the same operation for any that are xblocks. Any children which
are
usage
_ids just update the children pointer.
are
block
_ids just update the children pointer.
All updates go into the same course version (bulk updater).
...
...
@@ -1020,7 +1020,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
return
self
.
get_item
(
BlockUsageLocator
(
course_id
=
xblock
.
location
.
course_id
,
usage_id
=
xblock
.
location
.
usage
_id
,
block_id
=
xblock
.
location
.
block
_id
,
branch
=
xblock
.
location
.
branch
,
version_guid
=
new_id
)
...
...
@@ -1039,38 +1039,38 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
xblock
.
definition_locator
,
is_updated
=
self
.
update_definition_from_data
(
xblock
.
definition_locator
,
new_def_data
,
user_id
)
if
isinstance
(
xblock
.
scope_ids
.
usage_id
.
usage
_id
,
LocalId
):
if
isinstance
(
xblock
.
scope_ids
.
usage_id
.
block
_id
,
LocalId
):
# generate an id
is_new
=
True
is_updated
=
True
usage_id
=
self
.
_generate_usage
_id
(
structure_blocks
,
xblock
.
category
)
xblock
.
scope_ids
.
usage_id
.
usage_id
=
usage
_id
block_id
=
self
.
_generate_block
_id
(
structure_blocks
,
xblock
.
category
)
xblock
.
scope_ids
.
usage_id
.
block_id
=
block
_id
else
:
is_new
=
False
usage_id
=
xblock
.
location
.
usage
_id
i
f
(
not
is_updated
and
xblock
.
has_children
and
not
self
.
_xblock_lists_equal
(
structure_blocks
[
usage_id
][
'fields'
][
'children'
],
xblock
.
children
)):
is_updated
=
True
block_id
=
xblock
.
location
.
block
_id
i
s_updated
=
is_updated
or
(
xblock
.
has_children
and
structure_blocks
[
block_id
][
'fields'
][
'children'
]
!=
xblock
.
children
)
children
=
[]
if
xblock
.
has_children
:
for
child
in
xblock
.
children
:
if
isinstance
(
child
.
usage
_id
,
LocalId
):
if
isinstance
(
child
.
block
_id
,
LocalId
):
child_block
=
xblock
.
system
.
get_block
(
child
)
is_updated
=
self
.
_persist_subdag
(
child_block
,
user_id
,
structure_blocks
,
new_id
)
or
is_updated
children
.
append
(
child_block
.
location
.
usage
_id
)
children
.
append
(
child_block
.
location
.
block
_id
)
else
:
children
.
append
(
child
)
block_fields
=
xblock
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
if
not
is_new
and
not
is_updated
:
is_updated
=
self
.
_compare_settings
(
block_fields
,
structure_blocks
[
usage
_id
][
'fields'
])
is_updated
=
self
.
_compare_settings
(
block_fields
,
structure_blocks
[
block
_id
][
'fields'
])
if
children
:
block_fields
[
'children'
]
=
children
if
is_updated
:
previous_version
=
None
if
is_new
else
structure_blocks
[
usage
_id
][
'edit_info'
]
.
get
(
'update_version'
)
structure_blocks
[
usage
_id
]
=
{
previous_version
=
None
if
is_new
else
structure_blocks
[
block
_id
][
'edit_info'
]
.
get
(
'update_version'
)
structure_blocks
[
block
_id
]
=
{
"category"
:
xblock
.
category
,
"definition"
:
xblock
.
definition_locator
.
definition_id
,
"fields"
:
block_fields
,
...
...
@@ -1214,7 +1214,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
assert
isinstance
(
usage_locator
,
BlockUsageLocator
)
and
usage_locator
.
is_initialized
()
original_structure
=
self
.
_lookup_course
(
usage_locator
)[
'structure'
]
if
original_structure
[
'root'
]
==
usage_locator
.
usage
_id
:
if
original_structure
[
'root'
]
==
usage_locator
.
block
_id
:
raise
ValueError
(
"Cannot delete the root of a course"
)
index_entry
=
self
.
_get_index_if_valid
(
usage_locator
,
force
)
new_structure
=
self
.
_version_structure
(
original_structure
,
user_id
)
...
...
@@ -1222,22 +1222,24 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
new_id
=
new_structure
[
'_id'
]
parents
=
self
.
get_parent_locations
(
usage_locator
)
for
parent
in
parents
:
parent_block
=
new_blocks
[
parent
.
usage
_id
]
parent_block
[
'fields'
][
'children'
]
.
remove
(
usage_locator
.
usage
_id
)
parent_block
=
new_blocks
[
parent
.
block
_id
]
parent_block
[
'fields'
][
'children'
]
.
remove
(
usage_locator
.
block
_id
)
parent_block
[
'edit_info'
][
'edited_on'
]
=
datetime
.
datetime
.
now
(
UTC
)
parent_block
[
'edit_info'
][
'edited_by'
]
=
user_id
parent_block
[
'edit_info'
][
'previous_version'
]
=
parent_block
[
'edit_info'
][
'update_version'
]
parent_block
[
'edit_info'
][
'update_version'
]
=
new_id
# remove subtree
def
remove_subtree
(
usage_id
):
for
child
in
new_blocks
[
usage_id
][
'fields'
]
.
get
(
'children'
,
[]):
def
remove_subtree
(
block_id
):
"""
Remove the subtree rooted at block_id
"""
for
child
in
new_blocks
[
block_id
][
'fields'
]
.
get
(
'children'
,
[]):
remove_subtree
(
child
)
del
new_blocks
[
usage
_id
]
del
new_blocks
[
block
_id
]
if
delete_children
:
remove_subtree
(
usage_locator
.
usage
_id
)
remove_subtree
(
usage_locator
.
block
_id
)
else
:
del
new_blocks
[
usage_locator
.
usage
_id
]
del
new_blocks
[
usage_locator
.
block
_id
]
# update index if appropriate and structures
self
.
db_connection
.
insert_structure
(
new_structure
)
...
...
@@ -1307,22 +1309,22 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# migration where the old mongo published had pointers to privates
pass
def
descendants
(
self
,
block_map
,
usage
_id
,
depth
,
descendent_map
):
def
descendants
(
self
,
block_map
,
block
_id
,
depth
,
descendent_map
):
"""
adds block and its descendants out to depth to descendent_map
Depth specifies the number of levels of descendants to return
(0 => this usage only, 1 => this usage and its children, etc...)
A depth of None returns all descendants
"""
if
usage
_id
not
in
block_map
:
if
block
_id
not
in
block_map
:
return
descendent_map
if
usage
_id
not
in
descendent_map
:
descendent_map
[
usage_id
]
=
block_map
[
usage
_id
]
if
block
_id
not
in
descendent_map
:
descendent_map
[
block_id
]
=
block_map
[
block
_id
]
if
depth
is
None
or
depth
>
0
:
depth
=
depth
-
1
if
depth
is
not
None
else
None
for
child
in
block_map
[
usage
_id
][
'fields'
]
.
get
(
'children'
,
[]):
for
child
in
block_map
[
block
_id
][
'fields'
]
.
get
(
'children'
,
[]):
descendent_map
=
self
.
descendants
(
block_map
,
child
,
depth
,
descendent_map
)
...
...
@@ -1343,7 +1345,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def
internal_clean_children
(
self
,
course_locator
):
"""
Only intended for rather low level methods to use. Goes through the children attrs of
each block removing any whose
usage
_id is not a member of the course. Does not generate
each block removing any whose
block
_id is not a member of the course. Does not generate
a new version of the course but overwrites the existing one.
:param course_locator: the course to clean
...
...
@@ -1352,7 +1354,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
for
block
in
original_structure
[
'blocks'
]
.
itervalues
():
if
'fields'
in
block
and
'children'
in
block
[
'fields'
]:
block
[
'fields'
][
"children"
]
=
[
usage_id
for
usage_id
in
block
[
'fields'
][
"children"
]
if
usage
_id
in
original_structure
[
'blocks'
]
block_id
for
block_id
in
block
[
'fields'
][
"children"
]
if
block
_id
in
original_structure
[
'blocks'
]
]
self
.
db_connection
.
update_structure
(
original_structure
)
# clear cache again b/c inheritance may be wrong over orphans
...
...
@@ -1390,32 +1392,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else
:
return
criteria
==
target
def
_xblock_lists_equal
(
self
,
lista
,
listb
):
"""
Do the 2 lists refer to the same xblocks in the same order (presumes they're from the
same course)
:param lista:
:param listb:
"""
if
len
(
lista
)
!=
len
(
listb
):
return
False
for
ele_a
,
ele_b
in
zip
(
lista
,
listb
):
if
ele_a
!=
ele_b
:
if
self
.
_usage_id
(
ele_a
)
!=
self
.
_usage_id
(
ele_b
):
return
False
return
True
def
_usage_id
(
self
,
xblock_or_id
):
"""
arg is either an xblock or an id. If an xblock, get the usage_id from its location. Otherwise, return itself.
:param xblock_or_id:
"""
if
isinstance
(
xblock_or_id
,
XModuleDescriptor
):
return
xblock_or_id
.
location
.
usage_id
else
:
return
xblock_or_id
def
_get_index_if_valid
(
self
,
locator
,
force
=
False
,
continue_version
=
False
):
"""
If the locator identifies a course and points to its draft (or plausibly its draft),
...
...
@@ -1523,7 +1499,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
return
SPLIT_MONGO_MODULESTORE_TYPE
def
_new_structure
(
self
,
user_id
,
root_
usage
_id
,
def
_new_structure
(
self
,
user_id
,
root_
block
_id
,
root_category
=
None
,
block_fields
=
None
,
definition_id
=
None
):
"""
Internal function: create a structure element with no previous version. Must provide the root id
...
...
@@ -1533,13 +1509,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
new_id
=
ObjectId
()
if
root_category
is
not
None
:
blocks
=
{
root_
usage
_id
:
self
.
_new_block
(
user_id
,
root_category
,
block_fields
,
definition_id
,
new_id
)
root_
block
_id
:
self
.
_new_block
(
user_id
,
root_category
,
block_fields
,
definition_id
,
new_id
)
}
else
:
blocks
=
{}
return
{
'_id'
:
new_id
,
'root'
:
root_
usage
_id
,
'root'
:
root_
block
_id
,
'previous_version'
:
None
,
'original_version'
:
new_id
,
'edited_by'
:
user_id
,
...
...
@@ -1547,14 +1523,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'blocks'
:
blocks
}
def
_get_parents_from_structure
(
self
,
usage
_id
,
structure
):
def
_get_parents_from_structure
(
self
,
block
_id
,
structure
):
"""
Given a structure, find all of
usage
_id's parents in that structure
Given a structure, find all of
block
_id's parents in that structure
"""
items
=
[]
for
parent_id
,
value
in
structure
[
'blocks'
]
.
iteritems
():
for
child_id
in
value
[
'fields'
]
.
get
(
'children'
,
[]):
if
usage
_id
==
child_id
:
if
block
_id
==
child_id
:
items
.
append
(
parent_id
)
return
items
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
View file @
6c4aae73
...
...
@@ -75,10 +75,9 @@ class TestLocationMapper(unittest.TestCase):
self
.
assertEqual
(
entry
[
'prod_branch'
],
'live'
)
self
.
assertEqual
(
entry
[
'block_map'
],
block_map
)
def
translate_n_check
(
self
,
location
,
old_style_course_id
,
new_style_course_id
,
usage_id
,
branch
,
add_entry
=
False
):
def
translate_n_check
(
self
,
location
,
old_style_course_id
,
new_style_course_id
,
block_id
,
branch
,
add_entry
=
False
):
"""
Request translation, check course_id,
usage
_id, and branch
Request translation, check course_id,
block
_id, and branch
"""
prob_locator
=
loc_mapper
()
.
translate_location
(
old_style_course_id
,
...
...
@@ -87,7 +86,7 @@ class TestLocationMapper(unittest.TestCase):
add_entry_if_missing
=
add_entry
)
self
.
assertEqual
(
prob_locator
.
course_id
,
new_style_course_id
)
self
.
assertEqual
(
prob_locator
.
usage_id
,
usage
_id
)
self
.
assertEqual
(
prob_locator
.
block_id
,
block
_id
)
self
.
assertEqual
(
prob_locator
.
branch
,
branch
)
def
test_translate_location_read_only
(
self
):
...
...
@@ -243,7 +242,7 @@ class TestLocationMapper(unittest.TestCase):
new_style_course_id
=
'{}.geek_dept.{}.baz_run'
.
format
(
org
,
course
)
prob_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
usage
_id
=
'problem2'
,
block
_id
=
'problem2'
,
branch
=
'published'
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
...
...
@@ -267,21 +266,21 @@ class TestLocationMapper(unittest.TestCase):
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'course'
,
'baz_run'
,
None
))
# explicit branch
prob_locator
=
BlockUsageLocator
(
course_id
=
prob_locator
.
course_id
,
branch
=
'draft'
,
usage_id
=
prob_locator
.
usage
_id
course_id
=
prob_locator
.
course_id
,
branch
=
'draft'
,
block_id
=
prob_locator
.
block
_id
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
# Even though the problem was set as draft, we always return revision=None to work
# with old mongo/draft modulestores.
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'abc123'
,
None
))
prob_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
usage
_id
=
'problem2'
,
branch
=
'production'
course_id
=
new_style_course_id
,
block
_id
=
'problem2'
,
branch
=
'production'
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'abc123'
,
None
))
# same for chapter except chapter cannot be draft in old system
chap_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
usage
_id
=
'chapter48f'
,
block
_id
=
'chapter48f'
,
branch
=
'production'
)
chap_location
=
loc_mapper
()
.
translate_locator_to_location
(
chap_locator
)
...
...
@@ -291,7 +290,7 @@ class TestLocationMapper(unittest.TestCase):
chap_location
=
loc_mapper
()
.
translate_locator_to_location
(
chap_locator
)
self
.
assertEqual
(
chap_location
,
Location
(
'i4x'
,
org
,
course
,
'chapter'
,
'48f23a10395384929234'
))
chap_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
usage
_id
=
'chapter48f'
,
branch
=
'production'
course_id
=
new_style_course_id
,
block
_id
=
'chapter48f'
,
branch
=
'production'
)
chap_location
=
loc_mapper
()
.
translate_locator_to_location
(
chap_locator
)
self
.
assertEqual
(
chap_location
,
Location
(
'i4x'
,
org
,
course
,
'chapter'
,
'48f23a10395384929234'
))
...
...
@@ -300,7 +299,7 @@ class TestLocationMapper(unittest.TestCase):
prob_locator2
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
branch
=
'draft'
,
usage
_id
=
'problem3'
block
_id
=
'problem3'
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator2
)
self
.
assertIsNone
(
prob_location
,
'Found non-existent problem'
)
...
...
@@ -325,7 +324,7 @@ class TestLocationMapper(unittest.TestCase):
prob_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
branch
=
'production'
,
usage
_id
=
'problem3'
block
_id
=
'problem3'
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'abc123'
))
...
...
@@ -348,6 +347,25 @@ class TestLocationMapper(unittest.TestCase):
reverted_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
self
.
assertEqual
(
location
,
reverted_location
)
def
test_name_collision
(
self
):
"""
Test dwim translation when the old name was not unique
"""
org
=
"myorg"
course
=
"another_course"
name
=
"running_again"
course_location
=
Location
(
'i4x'
,
org
,
course
,
'course'
,
name
)
course_xlate
=
loc_mapper
()
.
translate_location
(
None
,
course_location
,
add_entry_if_missing
=
True
)
self
.
assertEqual
(
course_location
,
loc_mapper
()
.
translate_locator_to_location
(
course_xlate
))
eponymous_block
=
course_location
.
replace
(
category
=
'chapter'
)
chapter_xlate
=
loc_mapper
()
.
translate_location
(
None
,
eponymous_block
,
add_entry_if_missing
=
True
)
self
.
assertEqual
(
course_location
,
loc_mapper
()
.
translate_locator_to_location
(
course_xlate
))
self
.
assertEqual
(
eponymous_block
,
loc_mapper
()
.
translate_locator_to_location
(
chapter_xlate
))
# and a non-existent one w/o add
eponymous_block
=
course_location
.
replace
(
category
=
'problem'
)
with
self
.
assertRaises
(
ItemNotFoundError
):
chapter_xlate
=
loc_mapper
()
.
translate_location
(
None
,
eponymous_block
,
add_entry_if_missing
=
False
)
#==================================
# functions to mock existing services
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_locators.py
View file @
6c4aae73
...
...
@@ -255,9 +255,9 @@ class LocatorTest(TestCase):
"""
course_id
=
'mit.eecs-1'
branch
=
'foo'
usage
_id
=
'problem:with-colon~2'
testobj
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
usage_id
=
usage
_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot handle colon'
,
course_id
=
course_id
,
branch
=
branch
,
block
=
usage
_id
)
block
_id
=
'problem:with-colon~2'
testobj
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
block_id
=
block
_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot handle colon'
,
course_id
=
course_id
,
branch
=
branch
,
block
=
block
_id
)
def
test_repr
(
self
):
testurn
=
'mit.eecs.6002x/'
+
BRANCH_PREFIX
+
'published/'
+
BLOCK_PREFIX
+
'HW3'
...
...
@@ -275,7 +275,7 @@ class LocatorTest(TestCase):
self
.
assertEqual
(
location
,
Locator
.
to_locator_or_location
(
list
(
location_tuple
)))
self
.
assertEqual
(
location
,
Locator
.
to_locator_or_location
(
location
.
dict
()))
locator
=
BlockUsageLocator
(
course_id
=
'foo.bar'
,
branch
=
'alpha'
,
usage
_id
=
'deep'
)
locator
=
BlockUsageLocator
(
course_id
=
'foo.bar'
,
branch
=
'alpha'
,
block
_id
=
'deep'
)
self
.
assertEqual
(
locator
,
Locator
.
to_locator_or_location
(
locator
))
self
.
assertEqual
(
locator
.
as_course_locator
(),
Locator
.
to_locator_or_location
(
locator
.
as_course_locator
()))
self
.
assertEqual
(
location
,
Locator
.
to_locator_or_location
(
location
.
url
()))
...
...
@@ -347,4 +347,4 @@ class LocatorTest(TestCase):
"""
self
.
check_course_locn_fields
(
testobj
,
msg
,
version_guid
,
course_id
,
branch
)
self
.
assertEqual
(
testobj
.
usage
_id
,
block
)
self
.
assertEqual
(
testobj
.
block
_id
,
block
)
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
View file @
6c4aae73
...
...
@@ -87,14 +87,14 @@ class TestOrphan(unittest.TestCase):
course_or_parent_locator
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
usage
_id
=
parent_name
block
_id
=
parent_name
)
else
:
course_or_parent_locator
=
CourseLocator
(
course_id
=
'test_org.test_course.runid'
,
branch
=
'draft'
,
)
self
.
split_mongo
.
create_item
(
course_or_parent_locator
,
category
,
self
.
userid
,
usage
_id
=
name
,
fields
=
fields
)
self
.
split_mongo
.
create_item
(
course_or_parent_locator
,
category
,
self
.
userid
,
block
_id
=
name
,
fields
=
fields
)
def
_create_course
(
self
):
"""
...
...
@@ -114,7 +114,7 @@ class TestOrphan(unittest.TestCase):
fields
.
update
(
data
)
# split requires the course to be created separately from creating items
self
.
split_mongo
.
create_course
(
'test_org'
,
'my course'
,
self
.
userid
,
self
.
split_course_id
,
fields
=
fields
,
root_
usage
_id
=
'runid'
'test_org'
,
'my course'
,
self
.
userid
,
self
.
split_course_id
,
fields
=
fields
,
root_
block
_id
=
'runid'
)
self
.
course_location
=
Location
(
'i4x'
,
'test_org'
,
'test_course'
,
'course'
,
'runid'
)
self
.
old_mongo
.
create_and_save_xmodule
(
self
.
course_location
,
data
,
metadata
)
...
...
@@ -150,9 +150,9 @@ class TestOrphan(unittest.TestCase):
"""
orphans
=
self
.
split_mongo
.
get_orphans
(
self
.
split_course_id
,
[
'static_tab'
,
'about'
,
'course_info'
],
'draft'
)
self
.
assertEqual
(
len
(
orphans
),
3
,
"Wrong # {}"
.
format
(
orphans
))
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
usage
_id
=
'OrphanChapter'
)
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
block
_id
=
'OrphanChapter'
)
self
.
assertIn
(
location
,
orphans
)
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
usage
_id
=
'OrphanVert'
)
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
block
_id
=
'OrphanVert'
)
self
.
assertIn
(
location
,
orphans
)
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
usage
_id
=
'OrphanHtml'
)
location
=
BlockUsageLocator
(
course_id
=
self
.
split_course_id
,
branch
=
'draft'
,
block
_id
=
'OrphanHtml'
)
self
.
assertIn
(
location
,
orphans
)
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
View file @
6c4aae73
...
...
@@ -105,7 +105,7 @@ class SplitModuleTest(unittest.TestCase):
matches the _id.
"""
for
element
in
collection
:
if
element
.
location
.
usage
_id
==
_id
:
if
element
.
location
.
block
_id
==
_id
:
return
element
...
...
@@ -235,6 +235,16 @@ class SplitModuleCourseTests(SplitModuleTest):
modulestore
()
.
get_course
,
CourseLocator
(
course_id
=
'GreekHero'
,
branch
=
'published'
))
def
test_cache
(
self
):
"""
Test that the mechanics of caching work.
"""
locator
=
CourseLocator
(
version_guid
=
self
.
GUID_D0
)
course
=
modulestore
()
.
get_course
(
locator
)
block_map
=
modulestore
()
.
cache_items
(
course
.
system
,
course
.
children
,
depth
=
3
)
self
.
assertIn
(
'chapter1'
,
block_map
)
self
.
assertIn
(
'problem3_2'
,
block_map
)
def
test_course_successors
(
self
):
"""
get_course_successors(course_locator, version_history_depth=1)
...
...
@@ -267,11 +277,11 @@ class SplitModuleItemTests(SplitModuleTest):
'''
course_id
=
'GreekHero'
# positive tests of various forms
locator
=
BlockUsageLocator
(
version_guid
=
self
.
GUID_D1
,
usage
_id
=
'head12345'
)
locator
=
BlockUsageLocator
(
version_guid
=
self
.
GUID_D1
,
block
_id
=
'head12345'
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
course_id
,
locator
),
"couldn't find in
%
s"
%
self
.
GUID_D1
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
usage
_id
=
'head12345'
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
block
_id
=
'head12345'
,
branch
=
'draft'
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
locator
.
course_id
,
locator
),
"couldn't find in 12345"
...
...
@@ -280,7 +290,7 @@ class SplitModuleItemTests(SplitModuleTest):
modulestore
()
.
has_item
(
locator
.
course_id
,
BlockUsageLocator
(
course_id
=
locator
.
course_id
,
branch
=
'draft'
,
usage_id
=
locator
.
usage
_id
block_id
=
locator
.
block
_id
)),
"couldn't find in draft 12345"
)
...
...
@@ -288,7 +298,7 @@ class SplitModuleItemTests(SplitModuleTest):
modulestore
()
.
has_item
(
locator
.
course_id
,
BlockUsageLocator
(
course_id
=
locator
.
course_id
,
branch
=
'published'
,
usage_id
=
locator
.
usage
_id
)),
block_id
=
locator
.
block
_id
)),
"found in published 12345"
)
locator
.
branch
=
'draft'
...
...
@@ -298,18 +308,18 @@ class SplitModuleItemTests(SplitModuleTest):
)
# not a course obj
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
usage
_id
=
'chapter1'
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
block
_id
=
'chapter1'
,
branch
=
'draft'
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
locator
.
course_id
,
locator
),
"couldn't find chapter1"
)
# in published course
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
usage
_id
=
"head23456"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
block
_id
=
"head23456"
,
branch
=
'draft'
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
locator
.
course_id
,
BlockUsageLocator
(
course_id
=
locator
.
course_id
,
usage_id
=
locator
.
usage
_id
,
branch
=
'published'
)
BlockUsageLocator
(
course_id
=
locator
.
course_id
,
block_id
=
locator
.
block
_id
,
branch
=
'published'
)
),
"couldn't find in 23456"
)
locator
.
branch
=
'published'
...
...
@@ -319,9 +329,9 @@ class SplitModuleItemTests(SplitModuleTest):
# negative tests--not found
# no such course or block
course_id
=
'GreekHero'
locator
=
BlockUsageLocator
(
course_id
=
"doesnotexist"
,
usage
_id
=
"head23456"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"doesnotexist"
,
block
_id
=
"head23456"
,
branch
=
'draft'
)
self
.
assertFalse
(
modulestore
()
.
has_item
(
course_id
,
locator
))
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
usage
_id
=
"doesnotexist"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
block
_id
=
"doesnotexist"
,
branch
=
'draft'
)
self
.
assertFalse
(
modulestore
()
.
has_item
(
course_id
,
locator
))
# negative tests--insufficient specification
...
...
@@ -336,7 +346,7 @@ class SplitModuleItemTests(SplitModuleTest):
get_item(blocklocator)
'''
# positive tests of various forms
locator
=
BlockUsageLocator
(
version_guid
=
self
.
GUID_D1
,
usage
_id
=
'head12345'
)
locator
=
BlockUsageLocator
(
version_guid
=
self
.
GUID_D1
,
block
_id
=
'head12345'
)
block
=
modulestore
()
.
get_item
(
locator
)
self
.
assertIsInstance
(
block
,
CourseDescriptor
)
# get_instance just redirects to get_item, ignores course_id
...
...
@@ -355,7 +365,7 @@ class SplitModuleItemTests(SplitModuleTest):
block
.
grade_cutoffs
,
{
"Pass"
:
0.45
},
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
usage
_id
=
'head12345'
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
block
_id
=
'head12345'
,
branch
=
'draft'
)
verify_greek_hero
(
modulestore
()
.
get_item
(
locator
))
# get_instance just redirects to get_item, ignores course_id
verify_greek_hero
(
modulestore
()
.
get_instance
(
"course_id"
,
locator
))
...
...
@@ -364,7 +374,7 @@ class SplitModuleItemTests(SplitModuleTest):
self
.
assertRaises
(
ItemNotFoundError
,
modulestore
()
.
get_item
,
BlockUsageLocator
(
course_id
=
locator
.
as_course_locator
(),
usage_id
=
locator
.
usage
_id
,
block_id
=
locator
.
block
_id
,
branch
=
'published'
))
locator
.
branch
=
'draft'
self
.
assertIsInstance
(
...
...
@@ -374,7 +384,7 @@ class SplitModuleItemTests(SplitModuleTest):
def
test_get_non_root
(
self
):
# not a course obj
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
usage
_id
=
'chapter1'
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
'GreekHero'
,
block
_id
=
'chapter1'
,
branch
=
'draft'
)
block
=
modulestore
()
.
get_item
(
locator
)
self
.
assertEqual
(
block
.
location
.
course_id
,
"GreekHero"
)
self
.
assertEqual
(
block
.
category
,
'chapter'
)
...
...
@@ -383,7 +393,7 @@ class SplitModuleItemTests(SplitModuleTest):
self
.
assertEqual
(
block
.
edited_by
,
"testassist@edx.org"
)
# in published course
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
usage
_id
=
"head23456"
,
branch
=
'published'
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
block
_id
=
"head23456"
,
branch
=
'published'
)
self
.
assertIsInstance
(
modulestore
()
.
get_item
(
locator
),
CourseDescriptor
...
...
@@ -391,10 +401,10 @@ class SplitModuleItemTests(SplitModuleTest):
# negative tests--not found
# no such course or block
locator
=
BlockUsageLocator
(
course_id
=
"doesnotexist"
,
usage
_id
=
"head23456"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"doesnotexist"
,
block
_id
=
"head23456"
,
branch
=
'draft'
)
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
locator
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
usage
_id
=
"doesnotexist"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
block
_id
=
"doesnotexist"
,
branch
=
'draft'
)
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
locator
)
...
...
@@ -457,22 +467,22 @@ class SplitModuleItemTests(SplitModuleTest):
matches
=
modulestore
()
.
get_items
(
locator
,
qualifiers
=
{
'fields'
:
{
'children'
:
'chapter2'
}})
self
.
assertEqual
(
len
(
matches
),
1
)
self
.
assertEqual
(
matches
[
0
]
.
location
.
usage
_id
,
'head12345'
)
self
.
assertEqual
(
matches
[
0
]
.
location
.
block
_id
,
'head12345'
)
def
test_get_parents
(
self
):
'''
get_parent_locations(locator, [
usage
_id], [branch]): [BlockUsageLocator]
get_parent_locations(locator, [
block
_id], [branch]): [BlockUsageLocator]
'''
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
branch
=
'draft'
,
usage
_id
=
'chapter1'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
branch
=
'draft'
,
block
_id
=
'chapter1'
)
parents
=
modulestore
()
.
get_parent_locations
(
locator
)
self
.
assertEqual
(
len
(
parents
),
1
)
self
.
assertEqual
(
parents
[
0
]
.
usage
_id
,
'head12345'
)
self
.
assertEqual
(
parents
[
0
]
.
block
_id
,
'head12345'
)
self
.
assertEqual
(
parents
[
0
]
.
course_id
,
"GreekHero"
)
locator
.
usage
_id
=
'chapter2'
locator
.
block
_id
=
'chapter2'
parents
=
modulestore
()
.
get_parent_locations
(
locator
)
self
.
assertEqual
(
len
(
parents
),
1
)
self
.
assertEqual
(
parents
[
0
]
.
usage
_id
,
'head12345'
)
locator
.
usage
_id
=
'nosuchblock'
self
.
assertEqual
(
parents
[
0
]
.
block
_id
,
'head12345'
)
locator
.
block
_id
=
'nosuchblock'
parents
=
modulestore
()
.
get_parent_locations
(
locator
)
self
.
assertEqual
(
len
(
parents
),
0
)
...
...
@@ -480,7 +490,7 @@ class SplitModuleItemTests(SplitModuleTest):
"""
Test the existing get_children method on xdescriptors
"""
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"head12345"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"head12345"
,
branch
=
'draft'
)
block
=
modulestore
()
.
get_item
(
locator
)
children
=
block
.
get_children
()
expected_ids
=
[
...
...
@@ -488,8 +498,8 @@ class SplitModuleItemTests(SplitModuleTest):
]
for
child
in
children
:
self
.
assertEqual
(
child
.
category
,
"chapter"
)
self
.
assertIn
(
child
.
location
.
usage
_id
,
expected_ids
)
expected_ids
.
remove
(
child
.
location
.
usage
_id
)
self
.
assertIn
(
child
.
location
.
block
_id
,
expected_ids
)
expected_ids
.
remove
(
child
.
location
.
block
_id
)
self
.
assertEqual
(
len
(
expected_ids
),
0
)
...
...
@@ -551,7 +561,7 @@ class TestItemCrud(SplitModuleTest):
# check that block does not exist in previous version
locator
=
BlockUsageLocator
(
version_guid
=
premod_course
.
location
.
version_guid
,
usage_id
=
new_module
.
location
.
usage
_id
block_id
=
new_module
.
location
.
block
_id
)
self
.
assertRaises
(
ItemNotFoundError
,
modulestore
()
.
get_item
,
locator
)
...
...
@@ -559,7 +569,7 @@ class TestItemCrud(SplitModuleTest):
"""
Test create_item w/ specifying the parent of the new item
"""
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
usage
_id
=
"head23456"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"wonderful"
,
block
_id
=
"head23456"
,
branch
=
'draft'
)
premod_course
=
modulestore
()
.
get_course
(
locator
)
category
=
'chapter'
new_module
=
modulestore
()
.
create_item
(
...
...
@@ -570,16 +580,16 @@ class TestItemCrud(SplitModuleTest):
# check that course version changed and course's previous is the other one
self
.
assertNotEqual
(
new_module
.
location
.
version_guid
,
premod_course
.
location
.
version_guid
)
parent
=
modulestore
()
.
get_item
(
locator
)
self
.
assertIn
(
new_module
.
location
.
usage
_id
,
parent
.
children
)
self
.
assertIn
(
new_module
.
location
.
block
_id
,
parent
.
children
)
self
.
assertEqual
(
str
(
new_module
.
definition_locator
.
definition_id
),
"cd00000000000000dddd0022"
)
def
test_unique_naming
(
self
):
"""
Check that 2 modules of same type get unique
usage
_ids. Also check that if creation provides
Check that 2 modules of same type get unique
block
_ids. Also check that if creation provides
a definition id and new def data that it branches the definition in the db.
Actually, this tries to test all create_item features not tested above.
"""
locator
=
BlockUsageLocator
(
course_id
=
"contender"
,
usage
_id
=
"head345679"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"contender"
,
block
_id
=
"head345679"
,
branch
=
'draft'
)
category
=
'problem'
premod_time
=
datetime
.
datetime
.
now
(
UTC
)
-
datetime
.
timedelta
(
seconds
=
1
)
new_payload
=
"<problem>empty</problem>"
...
...
@@ -595,9 +605,9 @@ class TestItemCrud(SplitModuleTest):
)
# check that course version changed and course's previous is the other one
parent
=
modulestore
()
.
get_item
(
locator
)
self
.
assertNotEqual
(
new_module
.
location
.
usage_id
,
another_module
.
location
.
usage
_id
)
self
.
assertIn
(
new_module
.
location
.
usage
_id
,
parent
.
children
)
self
.
assertIn
(
another_module
.
location
.
usage
_id
,
parent
.
children
)
self
.
assertNotEqual
(
new_module
.
location
.
block_id
,
another_module
.
location
.
block
_id
)
self
.
assertIn
(
new_module
.
location
.
block
_id
,
parent
.
children
)
self
.
assertIn
(
another_module
.
location
.
block
_id
,
parent
.
children
)
self
.
assertEqual
(
new_module
.
data
,
new_payload
)
self
.
assertEqual
(
another_module
.
data
,
another_payload
)
# check definition histories
...
...
@@ -641,13 +651,13 @@ class TestItemCrud(SplitModuleTest):
self
.
assertEqual
(
refetch_course
.
update_version
,
course_block_update_version
)
refetch_index_history_info
=
modulestore
()
.
get_course_history_info
(
refetch_course
.
location
)
self
.
assertEqual
(
refetch_index_history_info
,
index_history_info
)
self
.
assertIn
(
new_ele
.
location
.
usage
_id
,
refetch_course
.
children
)
self
.
assertIn
(
new_ele
.
location
.
block
_id
,
refetch_course
.
children
)
# try to create existing item
with
self
.
assertRaises
(
DuplicateItemError
):
_fail
=
modulestore
()
.
create_item
(
new_course
.
location
,
'chapter'
,
user
,
usage_id
=
new_ele
.
location
.
usage
_id
,
block_id
=
new_ele
.
location
.
block
_id
,
fields
=
{
'display_name'
:
'chapter 2'
},
continue_version
=
True
)
...
...
@@ -678,7 +688,7 @@ class TestItemCrud(SplitModuleTest):
# add new child to old parent in continued (leave off version_guid)
course_module_locator
=
BlockUsageLocator
(
course_id
=
new_course
.
location
.
course_id
,
usage_id
=
new_course
.
location
.
usage
_id
,
block_id
=
new_course
.
location
.
block
_id
,
branch
=
new_course
.
location
.
branch
)
new_ele
=
modulestore
()
.
create_item
(
...
...
@@ -691,7 +701,7 @@ class TestItemCrud(SplitModuleTest):
# check children, previous_version
refetch_course
=
modulestore
()
.
get_course
(
versionless_course_locator
)
self
.
assertIn
(
new_ele
.
location
.
usage
_id
,
refetch_course
.
children
)
self
.
assertIn
(
new_ele
.
location
.
block
_id
,
refetch_course
.
children
)
self
.
assertEqual
(
refetch_course
.
previous_version
,
course_block_update_version
)
self
.
assertEqual
(
refetch_course
.
update_version
,
transaction_guid
)
...
...
@@ -699,7 +709,7 @@ class TestItemCrud(SplitModuleTest):
"""
test updating an items metadata ensuring the definition doesn't version but the course does if it should
"""
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"problem3_2"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"problem3_2"
,
branch
=
'draft'
)
problem
=
modulestore
()
.
get_item
(
locator
)
pre_def_id
=
problem
.
definition_locator
.
definition_id
pre_version_guid
=
problem
.
location
.
version_guid
...
...
@@ -718,7 +728,7 @@ class TestItemCrud(SplitModuleTest):
# refetch to ensure original didn't change
original_location
=
BlockUsageLocator
(
version_guid
=
pre_version_guid
,
usage_id
=
problem
.
location
.
usage
_id
block_id
=
problem
.
location
.
block
_id
)
problem
=
modulestore
()
.
get_item
(
original_location
)
self
.
assertNotEqual
(
problem
.
max_attempts
,
4
,
"original changed"
)
...
...
@@ -737,7 +747,7 @@ class TestItemCrud(SplitModuleTest):
"""
test updating an item's children ensuring the definition doesn't version but the course does if it should
"""
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"chapter3"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"chapter3"
,
branch
=
'draft'
)
block
=
modulestore
()
.
get_item
(
locator
)
pre_def_id
=
block
.
definition_locator
.
definition_id
pre_version_guid
=
block
.
location
.
version_guid
...
...
@@ -752,7 +762,7 @@ class TestItemCrud(SplitModuleTest):
self
.
assertNotEqual
(
updated_problem
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertEqual
(
updated_problem
.
children
,
block
.
children
)
self
.
assertNotIn
(
moved_child
,
updated_problem
.
children
)
locator
.
usage
_id
=
"chapter1"
locator
.
block
_id
=
"chapter1"
other_block
=
modulestore
()
.
get_item
(
locator
)
other_block
.
children
.
append
(
moved_child
)
other_block
.
save
()
# decache model changes
...
...
@@ -763,7 +773,7 @@ class TestItemCrud(SplitModuleTest):
"""
test updating an item's definition: ensure it gets versioned as well as the course getting versioned
"""
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"head12345"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"head12345"
,
branch
=
'draft'
)
block
=
modulestore
()
.
get_item
(
locator
)
pre_def_id
=
block
.
definition_locator
.
definition_id
pre_version_guid
=
block
.
location
.
version_guid
...
...
@@ -781,7 +791,7 @@ class TestItemCrud(SplitModuleTest):
Test updating metadata, children, and definition in a single call ensuring all the versioning occurs
"""
# first add 2 children to the course for the update to manipulate
locator
=
BlockUsageLocator
(
course_id
=
"contender"
,
usage
_id
=
"head345679"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"contender"
,
block
_id
=
"head345679"
,
branch
=
'draft'
)
category
=
'problem'
new_payload
=
"<problem>empty</problem>"
modulestore
()
.
create_item
(
...
...
@@ -823,7 +833,7 @@ class TestItemCrud(SplitModuleTest):
'deleting_user'
)
reusable_location
=
BlockUsageLocator
(
course_id
=
course
.
location
.
course_id
,
usage_id
=
course
.
location
.
usage
_id
,
block_id
=
course
.
location
.
block
_id
,
branch
=
'draft'
)
# delete a leaf
...
...
@@ -832,12 +842,12 @@ class TestItemCrud(SplitModuleTest):
new_course_loc
=
modulestore
()
.
delete_item
(
locn_to_del
,
'deleting_user'
,
delete_children
=
False
)
deleted
=
BlockUsageLocator
(
course_id
=
reusable_location
.
course_id
,
branch
=
reusable_location
.
branch
,
usage_id
=
locn_to_del
.
usage
_id
)
block_id
=
locn_to_del
.
block
_id
)
self
.
assertFalse
(
modulestore
()
.
has_item
(
reusable_location
.
course_id
,
deleted
))
self
.
assertRaises
(
VersionConflictError
,
modulestore
()
.
has_item
,
reusable_location
.
course_id
,
locn_to_del
)
locator
=
BlockUsageLocator
(
version_guid
=
locn_to_del
.
version_guid
,
usage_id
=
locn_to_del
.
usage
_id
block_id
=
locn_to_del
.
block
_id
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
reusable_location
.
course_id
,
locator
))
self
.
assertNotEqual
(
new_course_loc
.
version_guid
,
course
.
location
.
version_guid
)
...
...
@@ -854,10 +864,10 @@ class TestItemCrud(SplitModuleTest):
BlockUsageLocator
(
course_id
=
node_loc
.
course_id
,
branch
=
node_loc
.
branch
,
usage_id
=
node
.
location
.
usage
_id
)))
block_id
=
node
.
location
.
block
_id
)))
locator
=
BlockUsageLocator
(
version_guid
=
node
.
location
.
version_guid
,
usage_id
=
node
.
location
.
usage
_id
)
block_id
=
node
.
location
.
block
_id
)
self
.
assertTrue
(
modulestore
()
.
has_item
(
reusable_location
.
course_id
,
locator
))
if
node
.
has_children
:
for
sub
in
node
.
get_children
():
...
...
@@ -868,7 +878,7 @@ class TestItemCrud(SplitModuleTest):
course
=
modulestore
()
.
create_course
(
'nihilx'
,
'deletion'
,
'deleting_user'
)
root
=
BlockUsageLocator
(
course_id
=
course
.
location
.
course_id
,
usage_id
=
course
.
location
.
usage
_id
,
block_id
=
course
.
location
.
block
_id
,
branch
=
'draft'
)
for
_
in
range
(
4
):
self
.
create_subtree_for_deletion
(
root
,
[
'chapter'
,
'vertical'
,
'problem'
])
...
...
@@ -878,7 +888,7 @@ class TestItemCrud(SplitModuleTest):
if
not
category_queue
:
return
node
=
modulestore
()
.
create_item
(
parent
,
category_queue
[
0
],
'deleting_user'
)
node_loc
=
BlockUsageLocator
(
parent
.
as_course_locator
(),
usage_id
=
node
.
location
.
usage
_id
)
node_loc
=
BlockUsageLocator
(
parent
.
as_course_locator
(),
block_id
=
node
.
location
.
block
_id
)
for
_
in
range
(
4
):
self
.
create_subtree_for_deletion
(
node_loc
,
category_queue
[
1
:])
...
...
@@ -971,7 +981,7 @@ class TestCourseCreation(SplitModuleTest):
self
.
assertFalse
(
modulestore
()
.
has_item
(
new_draft_locator
.
course_id
,
BlockUsageLocator
(
original_locator
,
usage_id
=
new_item
.
location
.
usage
_id
block_id
=
new_item
.
location
.
block
_id
))
)
...
...
@@ -1051,9 +1061,9 @@ class TestCourseCreation(SplitModuleTest):
user
=
random
.
getrandbits
(
32
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
user
,
root_
usage
_id
=
'top'
,
root_category
=
'chapter'
root_
block
_id
=
'top'
,
root_category
=
'chapter'
)
self
.
assertEqual
(
new_course
.
location
.
usage
_id
,
'top'
)
self
.
assertEqual
(
new_course
.
location
.
block
_id
,
'top'
)
self
.
assertEqual
(
new_course
.
category
,
'chapter'
)
# look at db to verify
db_structure
=
modulestore
()
.
db_connection
.
get_structure
(
...
...
@@ -1076,11 +1086,11 @@ class TestInheritance(SplitModuleTest):
"""
# Note, not testing value where defined (course) b/c there's no
# defined accessor for it on CourseDescriptor.
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"problem3_2"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"problem3_2"
,
branch
=
'draft'
)
node
=
modulestore
()
.
get_item
(
locator
)
# inherited
self
.
assertEqual
(
node
.
graceperiod
,
datetime
.
timedelta
(
hours
=
2
))
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
usage
_id
=
"problem1"
,
branch
=
'draft'
)
locator
=
BlockUsageLocator
(
course_id
=
"GreekHero"
,
block
_id
=
"problem1"
,
branch
=
'draft'
)
node
=
modulestore
()
.
get_item
(
locator
)
# overridden
self
.
assertEqual
(
node
.
graceperiod
,
datetime
.
timedelta
(
hours
=
4
))
...
...
@@ -1117,24 +1127,24 @@ class TestPublish(SplitModuleTest):
expected
.
remove
(
"chapter1"
)
# check that it's not in published course
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
usage
_id
))
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
block
_id
))
# publish it
modulestore
()
.
xblock_publish
(
self
.
user
,
source_course
,
dest_course
,
[
new_module
.
location
.
usage
_id
],
None
)
expected
.
append
(
new_module
.
location
.
usage
_id
)
modulestore
()
.
xblock_publish
(
self
.
user
,
source_course
,
dest_course
,
[
new_module
.
location
.
block
_id
],
None
)
expected
.
append
(
new_module
.
location
.
block
_id
)
# check that it is in the published course and that its parent is the chapter
pub_module
=
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
usage
_id
))
pub_module
=
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
block
_id
))
self
.
assertEqual
(
modulestore
()
.
get_parent_locations
(
pub_module
.
location
)[
0
]
.
usage
_id
,
"chapter1"
modulestore
()
.
get_parent_locations
(
pub_module
.
location
)[
0
]
.
block
_id
,
"chapter1"
)
# ensure intentionally orphaned blocks work (e.g., course_info)
new_module
=
modulestore
()
.
create_item
(
source_course
,
"course_info"
,
self
.
user
,
usage
_id
=
"handouts"
source_course
,
"course_info"
,
self
.
user
,
block
_id
=
"handouts"
)
# publish it
modulestore
()
.
xblock_publish
(
self
.
user
,
source_course
,
dest_course
,
[
new_module
.
location
.
usage
_id
],
None
)
expected
.
append
(
new_module
.
location
.
usage
_id
)
modulestore
()
.
xblock_publish
(
self
.
user
,
source_course
,
dest_course
,
[
new_module
.
location
.
block
_id
],
None
)
expected
.
append
(
new_module
.
location
.
block
_id
)
# check that it is in the published course (no error means it worked)
pub_module
=
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
usage
_id
))
pub_module
=
modulestore
()
.
get_item
(
self
.
_usage
(
dest_course
,
new_module
.
location
.
block
_id
))
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
"chapter2"
,
"chapter3"
,
"problem1"
,
"problem3_2"
]
)
...
...
@@ -1200,7 +1210,7 @@ class TestPublish(SplitModuleTest):
"""
Generate a BlockUsageLocator for the combo of the course location and block id
"""
return
BlockUsageLocator
(
course_id
=
course_loc
.
course_id
,
branch
=
course_loc
.
branch
,
usage
_id
=
block_id
)
return
BlockUsageLocator
(
course_id
=
course_loc
.
course_id
,
branch
=
course_loc
.
branch
,
block
_id
=
block_id
)
def
_compare_children
(
self
,
source_children
,
dest_children
,
unexpected
):
"""
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
6c4aae73
...
...
@@ -168,7 +168,7 @@ class XModuleMixin(XBlockMixin):
if
isinstance
(
self
.
location
,
Location
):
return
self
.
location
.
name
elif
isinstance
(
self
.
location
,
BlockUsageLocator
):
return
self
.
location
.
usage
_id
return
self
.
location
.
block
_id
else
:
raise
InsufficientSpecificationError
()
...
...
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