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
d53a6669
Commit
d53a6669
authored
Jul 19, 2014
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4381 from edx/nimisha/split-converge-api
Nimisha/split converge api
parents
40625c45
483e2a6a
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
1054 additions
and
588 deletions
+1054
-588
cms/djangoapps/contentstore/course_info_model.py
+2
-2
cms/djangoapps/contentstore/tests/test_contentstore.py
+2
-5
cms/djangoapps/contentstore/tests/test_crud.py
+4
-4
cms/djangoapps/contentstore/tests/test_orphan.py
+8
-2
cms/djangoapps/contentstore/tests/utils.py
+2
-2
cms/djangoapps/contentstore/views/item.py
+13
-18
cms/djangoapps/contentstore/views/tests/test_course_updates.py
+13
-4
cms/envs/test.py
+1
-1
common/lib/xmodule/xmodule/modulestore/__init__.py
+41
-44
common/lib/xmodule/xmodule/modulestore/draft_and_published.py
+49
-0
common/lib/xmodule/xmodule/modulestore/mixed.py
+67
-70
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+66
-8
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
+5
-3
common/lib/xmodule/xmodule/modulestore/split_migrator.py
+5
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+86
-124
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
+198
-0
common/lib/xmodule/xmodule/modulestore/tests/factories.py
+9
-1
common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py
+12
-5
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+199
-92
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
+109
-48
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
+0
-52
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
+1
-1
common/lib/xmodule/xmodule/modulestore/tests/test_split_draft_modulestore.py
+62
-0
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
+62
-82
common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py
+29
-12
common/lib/xmodule/xmodule/modulestore/tests/test_xml_importer.py
+3
-1
common/lib/xmodule/xmodule/split_test_module.py
+6
-4
No files found.
cms/djangoapps/contentstore/course_info_model.py
View file @
d53a6669
...
@@ -36,7 +36,7 @@ def get_course_updates(location, provided_id, user_id):
...
@@ -36,7 +36,7 @@ def get_course_updates(location, provided_id, user_id):
try
:
try
:
course_updates
=
modulestore
()
.
get_item
(
location
)
course_updates
=
modulestore
()
.
get_item
(
location
)
except
ItemNotFoundError
:
except
ItemNotFoundError
:
course_updates
=
modulestore
()
.
create_
and_save_xmodule
(
location
,
user
_id
)
course_updates
=
modulestore
()
.
create_
item
(
user_id
,
location
.
course_key
,
location
.
block_type
,
location
.
block
_id
)
course_update_items
=
get_course_update_items
(
course_updates
,
provided_id
)
course_update_items
=
get_course_update_items
(
course_updates
,
provided_id
)
return
_get_visible_update
(
course_update_items
)
return
_get_visible_update
(
course_update_items
)
...
@@ -51,7 +51,7 @@ def update_course_updates(location, update, passed_id=None, user=None):
...
@@ -51,7 +51,7 @@ def update_course_updates(location, update, passed_id=None, user=None):
try
:
try
:
course_updates
=
modulestore
()
.
get_item
(
location
)
course_updates
=
modulestore
()
.
get_item
(
location
)
except
ItemNotFoundError
:
except
ItemNotFoundError
:
course_updates
=
modulestore
()
.
create_
and_save_xmodule
(
location
,
user
.
id
)
course_updates
=
modulestore
()
.
create_
item
(
user
.
id
,
location
.
course_key
,
location
.
block_type
,
location
.
block_
id
)
course_update_items
=
list
(
reversed
(
get_course_update_items
(
course_updates
)))
course_update_items
=
list
(
reversed
(
get_course_update_items
(
course_updates
)))
...
...
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
d53a6669
...
@@ -575,7 +575,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
...
@@ -575,7 +575,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase):
location
=
course
.
id
.
make_usage_key
(
'chapter'
,
'neuvo'
)
location
=
course
.
id
.
make_usage_key
(
'chapter'
,
'neuvo'
)
# Ensure draft mongo store does not create drafts for things that shouldn't be draft
# Ensure draft mongo store does not create drafts for things that shouldn't be draft
newobject
=
draft_store
.
create_
and_save_xmodule
(
location
,
self
.
user
.
id
)
newobject
=
draft_store
.
create_
item
(
self
.
user
.
id
,
location
.
course_key
,
location
.
block_type
,
location
.
block_
id
)
self
.
assertFalse
(
getattr
(
newobject
,
'is_draft'
,
False
))
self
.
assertFalse
(
getattr
(
newobject
,
'is_draft'
,
False
))
with
self
.
assertRaises
(
InvalidVersionError
):
with
self
.
assertRaises
(
InvalidVersionError
):
draft_store
.
convert_to_draft
(
location
,
self
.
user
.
id
)
draft_store
.
convert_to_draft
(
location
,
self
.
user
.
id
)
...
@@ -1392,12 +1392,9 @@ class ContentStoreTest(ContentStoreTestCase):
...
@@ -1392,12 +1392,9 @@ class ContentStoreTest(ContentStoreTestCase):
def
test_forum_id_generation
(
self
):
def
test_forum_id_generation
(
self
):
course
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
course
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
new_component_location
=
course
.
id
.
make_usage_key
(
'discussion'
,
'new_component'
)
# crate a new module and add it as a child to a vertical
# crate a new module and add it as a child to a vertical
self
.
store
.
create_and_save_xmodule
(
new_component_location
,
self
.
user
.
id
)
new_discussion_item
=
self
.
store
.
create_item
(
self
.
user
.
id
,
course
.
id
,
'discussion'
,
'new_component'
)
new_discussion_item
=
self
.
store
.
get_item
(
new_component_location
)
self
.
assertNotEquals
(
new_discussion_item
.
discussion_id
,
'$$GUID$$'
)
self
.
assertNotEquals
(
new_discussion_item
.
discussion_id
,
'$$GUID$$'
)
...
...
cms/djangoapps/contentstore/tests/test_crud.py
View file @
d53a6669
...
@@ -162,7 +162,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -162,7 +162,7 @@ class TemplateTests(unittest.TestCase):
self
.
assertIsInstance
(
self
.
split_store
.
get_course
(
id_locator
),
CourseDescriptor
)
self
.
assertIsInstance
(
self
.
split_store
.
get_course
(
id_locator
),
CourseDescriptor
)
# and by guid
# and by guid
self
.
assertIsInstance
(
self
.
split_store
.
get_item
(
guid_locator
),
CourseDescriptor
)
self
.
assertIsInstance
(
self
.
split_store
.
get_item
(
guid_locator
),
CourseDescriptor
)
self
.
split_store
.
delete_course
(
id_locator
,
ModuleStoreEnum
.
UserID
.
test
)
self
.
split_store
.
delete_course
(
id_locator
,
'testbot'
)
# test can no longer retrieve by id
# test can no longer retrieve by id
self
.
assertRaises
(
ItemNotFoundError
,
self
.
split_store
.
get_course
,
id_locator
)
self
.
assertRaises
(
ItemNotFoundError
,
self
.
split_store
.
get_course
,
id_locator
)
# but can by guid
# but can by guid
...
@@ -187,16 +187,16 @@ class TemplateTests(unittest.TestCase):
...
@@ -187,16 +187,16 @@ class TemplateTests(unittest.TestCase):
)
)
first_problem
.
max_attempts
=
3
first_problem
.
max_attempts
=
3
first_problem
.
save
()
# decache the above into the kvs
first_problem
.
save
()
# decache the above into the kvs
updated_problem
=
self
.
split_store
.
update_item
(
first_problem
,
ModuleStoreEnum
.
UserID
.
test
)
updated_problem
=
self
.
split_store
.
update_item
(
first_problem
,
'testbot'
)
self
.
assertIsNotNone
(
updated_problem
.
previous_version
)
self
.
assertIsNotNone
(
updated_problem
.
previous_version
)
self
.
assertEqual
(
updated_problem
.
previous_version
,
first_problem
.
update_version
)
self
.
assertEqual
(
updated_problem
.
previous_version
,
first_problem
.
update_version
)
self
.
assertNotEqual
(
updated_problem
.
update_version
,
first_problem
.
update_version
)
self
.
assertNotEqual
(
updated_problem
.
update_version
,
first_problem
.
update_version
)
updated_loc
=
self
.
split_store
.
delete_item
(
updated_problem
.
location
,
ModuleStoreEnum
.
UserID
.
test
,
'testbot'
)
self
.
split_store
.
delete_item
(
updated_problem
.
location
,
'testbot'
)
second_problem
=
persistent_factories
.
ItemFactory
.
create
(
second_problem
=
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'problem 2'
,
display_name
=
'problem 2'
,
parent_location
=
BlockUsageLocator
.
make_relative
(
parent_location
=
BlockUsageLocator
.
make_relative
(
updated_loc
,
block_type
=
'problem'
,
block_id
=
sub
.
location
.
block_id
test_course
.
location
.
version_agnostic
()
,
block_type
=
'problem'
,
block_id
=
sub
.
location
.
block_id
),
),
user_id
=
'testbot'
,
category
=
'problem'
,
user_id
=
'testbot'
,
category
=
'problem'
,
data
=
"<problem></problem>"
data
=
"<problem></problem>"
...
...
cms/djangoapps/contentstore/tests/test_orphan.py
View file @
d53a6669
...
@@ -33,8 +33,14 @@ class TestOrphan(CourseTestCase):
...
@@ -33,8 +33,14 @@ class TestOrphan(CourseTestCase):
def
_create_item
(
self
,
category
,
name
,
data
,
metadata
,
parent_category
,
parent_name
,
runtime
):
def
_create_item
(
self
,
category
,
name
,
data
,
metadata
,
parent_category
,
parent_name
,
runtime
):
location
=
self
.
course
.
location
.
replace
(
category
=
category
,
name
=
name
)
location
=
self
.
course
.
location
.
replace
(
category
=
category
,
name
=
name
)
store
=
modulestore
()
store
=
modulestore
()
store
.
create_and_save_xmodule
(
store
.
create_item
(
location
,
self
.
user
.
id
,
definition_data
=
data
,
metadata
=
metadata
,
runtime
=
runtime
self
.
user
.
id
,
location
.
course_key
,
location
.
block_type
,
location
.
block_id
,
definition_data
=
data
,
metadata
=
metadata
,
runtime
=
runtime
)
)
if
parent_name
:
if
parent_name
:
# add child to parent in mongo
# add child to parent in mongo
...
...
cms/djangoapps/contentstore/tests/utils.py
View file @
d53a6669
...
@@ -151,11 +151,11 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -151,11 +151,11 @@ class CourseTestCase(ModuleStoreTestCase):
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
draft_vertical
),
PublishState
.
draft
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
draft_vertical
),
PublishState
.
draft
)
# create a Private (draft only) vertical
# create a Private (draft only) vertical
private_vertical
=
self
.
store
.
create_
and_save_xmodule
(
course_id
.
make_usage_key
(
'vertical'
,
self
.
PRIVATE_VERTICAL
),
self
.
user
.
id
)
private_vertical
=
self
.
store
.
create_
item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PRIVATE_VERTICAL
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
private_vertical
),
PublishState
.
private
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
private_vertical
),
PublishState
.
private
)
# create a Published (no draft) vertical
# create a Published (no draft) vertical
public_vertical
=
self
.
store
.
create_
and_save_xmodule
(
course_id
.
make_usage_key
(
'vertical'
,
self
.
PUBLISHED_VERTICAL
),
self
.
user
.
id
)
public_vertical
=
self
.
store
.
create_
item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PUBLISHED_VERTICAL
)
public_vertical
=
self
.
store
.
publish
(
public_vertical
.
location
,
self
.
user
.
id
)
public_vertical
=
self
.
store
.
publish
(
public_vertical
.
location
,
self
.
user
.
id
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
public_vertical
),
PublishState
.
public
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
public_vertical
),
PublishState
.
public
)
...
...
cms/djangoapps/contentstore/views/item.py
View file @
d53a6669
...
@@ -287,7 +287,7 @@ def _save_item(user, usage_key, data=None, children=None, metadata=None, nullout
...
@@ -287,7 +287,7 @@ def _save_item(user, usage_key, data=None, children=None, metadata=None, nullout
if
usage_key
.
category
in
CREATE_IF_NOT_FOUND
:
if
usage_key
.
category
in
CREATE_IF_NOT_FOUND
:
# New module at this location, for pages that are not pre-created.
# New module at this location, for pages that are not pre-created.
# Used for course info handouts.
# Used for course info handouts.
existing_item
=
store
.
create_
and_save_xmodule
(
usage_key
,
user
.
id
)
existing_item
=
store
.
create_
item
(
user
.
id
,
usage_key
.
course_key
,
usage_key
.
block_type
,
usage_key
.
block_
id
)
else
:
else
:
raise
raise
except
InvalidLocationError
:
except
InvalidLocationError
:
...
@@ -416,9 +416,11 @@ def _create_item(request):
...
@@ -416,9 +416,11 @@ def _create_item(request):
if
display_name
is
not
None
:
if
display_name
is
not
None
:
metadata
[
'display_name'
]
=
display_name
metadata
[
'display_name'
]
=
display_name
created_block
=
store
.
create_and_save_xmodule
(
created_block
=
store
.
create_child
(
dest_usage_key
,
request
.
user
.
id
,
request
.
user
.
id
,
usage_key
,
dest_usage_key
.
block_type
,
block_id
=
dest_usage_key
.
block_id
,
definition_data
=
data
,
definition_data
=
data
,
metadata
=
metadata
,
metadata
=
metadata
,
runtime
=
parent
.
runtime
,
runtime
=
parent
.
runtime
,
...
@@ -437,11 +439,6 @@ def _create_item(request):
...
@@ -437,11 +439,6 @@ def _create_item(request):
)
)
store
.
update_item
(
course
,
request
.
user
.
id
)
store
.
update_item
(
course
,
request
.
user
.
id
)
# TODO replace w/ nicer accessor
if
not
'detached'
in
parent
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
parent
.
children
.
append
(
created_block
.
location
)
store
.
update_item
(
parent
,
request
.
user
.
id
)
return
JsonResponse
({
"locator"
:
unicode
(
created_block
.
location
),
"courseKey"
:
unicode
(
created_block
.
location
.
course_key
)})
return
JsonResponse
({
"locator"
:
unicode
(
created_block
.
location
),
"courseKey"
:
unicode
(
created_block
.
location
.
course_key
)})
...
@@ -465,11 +462,11 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
...
@@ -465,11 +462,11 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
else
:
else
:
duplicate_metadata
[
'display_name'
]
=
_
(
"Duplicate of '{0}'"
)
.
format
(
source_item
.
display_name
)
duplicate_metadata
[
'display_name'
]
=
_
(
"Duplicate of '{0}'"
)
.
format
(
source_item
.
display_name
)
dest_module
=
store
.
create_and_save_xmodule
(
dest_module
=
store
.
create_item
(
dest_usage_key
,
user
.
id
,
user
.
id
,
dest_usage_key
.
course_key
,
dest_usage_key
.
block_type
,
block_id
=
dest_usage_key
.
block_id
,
definition_data
=
source_item
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
),
definition_data
=
source_item
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
),
metadata
=
duplicate_metadata
,
metadata
=
duplicate_metadata
,
runtime
=
source_item
.
runtime
,
runtime
=
source_item
.
runtime
,
...
@@ -531,7 +528,7 @@ def orphan_handler(request, course_key_string):
...
@@ -531,7 +528,7 @@ def orphan_handler(request, course_key_string):
course_usage_key
=
CourseKey
.
from_string
(
course_key_string
)
course_usage_key
=
CourseKey
.
from_string
(
course_key_string
)
if
request
.
method
==
'GET'
:
if
request
.
method
==
'GET'
:
if
has_course_access
(
request
.
user
,
course_usage_key
):
if
has_course_access
(
request
.
user
,
course_usage_key
):
return
JsonResponse
(
modulestore
()
.
get_orphans
(
course_usage_key
)
)
return
JsonResponse
(
[
unicode
(
item
)
for
item
in
modulestore
()
.
get_orphans
(
course_usage_key
)]
)
else
:
else
:
raise
PermissionDenied
()
raise
PermissionDenied
()
if
request
.
method
==
'DELETE'
:
if
request
.
method
==
'DELETE'
:
...
@@ -539,11 +536,9 @@ def orphan_handler(request, course_key_string):
...
@@ -539,11 +536,9 @@ def orphan_handler(request, course_key_string):
store
=
modulestore
()
store
=
modulestore
()
items
=
store
.
get_orphans
(
course_usage_key
)
items
=
store
.
get_orphans
(
course_usage_key
)
for
itemloc
in
items
:
for
itemloc
in
items
:
# get_orphans returns the deprecated string format w/o revision
usage_key
=
course_usage_key
.
make_usage_key_from_deprecated_string
(
itemloc
)
# need to delete all versions
# need to delete all versions
store
.
delete_item
(
usage_key
,
request
.
user
.
id
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
all
)
store
.
delete_item
(
itemloc
,
request
.
user
.
id
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
all
)
return
JsonResponse
({
'deleted'
:
items
})
return
JsonResponse
({
'deleted'
:
[
unicode
(
item
)
for
item
in
items
]
})
else
:
else
:
raise
PermissionDenied
()
raise
PermissionDenied
()
...
@@ -559,7 +554,7 @@ def _get_module_info(usage_key, user, rewrite_static_links=True):
...
@@ -559,7 +554,7 @@ def _get_module_info(usage_key, user, rewrite_static_links=True):
except
ItemNotFoundError
:
except
ItemNotFoundError
:
if
usage_key
.
category
in
CREATE_IF_NOT_FOUND
:
if
usage_key
.
category
in
CREATE_IF_NOT_FOUND
:
# Create a new one for certain categories only. Used for course info handouts.
# Create a new one for certain categories only. Used for course info handouts.
module
=
store
.
create_
and_save_xmodule
(
usage_key
,
user
.
id
)
module
=
store
.
create_
item
(
user
.
id
,
usage_key
.
course_key
,
usage_key
.
block_type
,
block_id
=
usage_key
.
block_
id
)
else
:
else
:
raise
raise
...
...
cms/djangoapps/contentstore/views/tests/test_course_updates.py
View file @
d53a6669
...
@@ -130,7 +130,12 @@ class CourseUpdateTest(CourseTestCase):
...
@@ -130,7 +130,12 @@ class CourseUpdateTest(CourseTestCase):
'''
'''
# get the updates and populate 'data' field with some data.
# get the updates and populate 'data' field with some data.
location
=
self
.
course
.
id
.
make_usage_key
(
'course_info'
,
'updates'
)
location
=
self
.
course
.
id
.
make_usage_key
(
'course_info'
,
'updates'
)
course_updates
=
modulestore
()
.
create_and_save_xmodule
(
location
,
self
.
user
.
id
)
course_updates
=
modulestore
()
.
create_item
(
self
.
user
.
id
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
update_date
=
u"January 23, 2014"
update_date
=
u"January 23, 2014"
update_content
=
u"Hello world!"
update_content
=
u"Hello world!"
update_data
=
u"<ol><li><h2>"
+
update_date
+
"</h2>"
+
update_content
+
"</li></ol>"
update_data
=
u"<ol><li><h2>"
+
update_date
+
"</h2>"
+
update_content
+
"</li></ol>"
...
@@ -204,7 +209,12 @@ class CourseUpdateTest(CourseTestCase):
...
@@ -204,7 +209,12 @@ class CourseUpdateTest(CourseTestCase):
'''Test trying to add to a saved course_update which is not an ol.'''
'''Test trying to add to a saved course_update which is not an ol.'''
# get the updates and set to something wrong
# get the updates and set to something wrong
location
=
self
.
course
.
id
.
make_usage_key
(
'course_info'
,
'updates'
)
location
=
self
.
course
.
id
.
make_usage_key
(
'course_info'
,
'updates'
)
modulestore
()
.
create_and_save_xmodule
(
location
,
self
.
user
.
id
)
modulestore
()
.
create_item
(
self
.
user
.
id
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
course_updates
=
modulestore
()
.
get_item
(
location
)
course_updates
=
modulestore
()
.
get_item
(
location
)
course_updates
.
data
=
'bad news'
course_updates
.
data
=
'bad news'
modulestore
()
.
update_item
(
course_updates
,
self
.
user
.
id
)
modulestore
()
.
update_item
(
course_updates
,
self
.
user
.
id
)
...
@@ -229,8 +239,7 @@ class CourseUpdateTest(CourseTestCase):
...
@@ -229,8 +239,7 @@ class CourseUpdateTest(CourseTestCase):
"""
"""
Test that a user can successfully post on course updates and handouts of a course
Test that a user can successfully post on course updates and handouts of a course
"""
"""
course_key
=
SlashSeparatedCourseKey
(
'Org1'
,
'Course_1'
,
'Run_1'
)
course_update_url
=
self
.
create_update_url
(
course_key
=
self
.
course
.
id
)
course_update_url
=
self
.
create_update_url
(
course_key
=
course_key
)
# create a course via the view handler
# create a course via the view handler
self
.
client
.
ajax_post
(
course_update_url
)
self
.
client
.
ajax_post
(
course_update_url
)
...
...
cms/envs/test.py
View file @
d53a6669
...
@@ -63,7 +63,7 @@ STATICFILES_DIRS += [
...
@@ -63,7 +63,7 @@ STATICFILES_DIRS += [
MODULESTORE
[
'default'
][
'OPTIONS'
][
'stores'
]
.
append
(
MODULESTORE
[
'default'
][
'OPTIONS'
][
'stores'
]
.
append
(
{
{
'NAME'
:
'split'
,
'NAME'
:
'split'
,
'ENGINE'
:
'xmodule.modulestore.split_mongo.split
.SplitMongo
ModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.split_mongo.split
_draft.DraftVersioning
ModuleStore'
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'OPTIONS'
:
{
'OPTIONS'
:
{
'render_template'
:
'edxmako.shortcuts.render_to_string'
,
'render_template'
:
'edxmako.shortcuts.render_to_string'
,
...
...
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
d53a6669
...
@@ -7,6 +7,7 @@ import logging
...
@@ -7,6 +7,7 @@ import logging
import
re
import
re
import
json
import
json
import
datetime
import
datetime
from
uuid
import
uuid4
from
collections
import
namedtuple
,
defaultdict
from
collections
import
namedtuple
,
defaultdict
import
collections
import
collections
...
@@ -367,6 +368,25 @@ class ModuleStoreWrite(ModuleStoreRead):
...
@@ -367,6 +368,25 @@ class ModuleStoreWrite(ModuleStoreRead):
pass
pass
@abstractmethod
@abstractmethod
def
create_item
(
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
fields
=
None
,
**
kwargs
):
"""
Creates and saves a new item in a course.
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
course_key: A :class:`~opaque_keys.edx.CourseKey` identifying which course to create
this item in
block_type: The type of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
"""
pass
@abstractmethod
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
"""
"""
Sets up source_course_id to point a course with the same content as the desct_course_id. This
Sets up source_course_id to point a course with the same content as the desct_course_id. This
...
@@ -560,50 +580,6 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
...
@@ -560,50 +580,6 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
result
[
field
.
scope
][
field_name
]
=
value
result
[
field
.
scope
][
field_name
]
=
value
return
result
return
result
def
update_item
(
self
,
xblock
,
user_id
,
allow_not_found
=
False
,
force
=
False
):
"""
Update the given xblock's persisted repr. Pass the user's unique id which the persistent store
should save with the update if it has that ability.
:param allow_not_found: whether this method should raise an exception if the given xblock
has not been persisted before.
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org, course, run, and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
raise
NotImplementedError
def
delete_item
(
self
,
location
,
user_id
,
force
=
False
):
"""
Delete an item from persistence. Pass the user's unique id which the persistent store
should save with the update if it has that ability.
:param user_id: ID of the user deleting the item
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org, course, run, and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
raise
NotImplementedError
def
create_and_save_xmodule
(
self
,
location
,
user_id
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}):
"""
Create the new xmodule and save it.
:param location: a Location--must have a category
:param user_id: ID of the user creating and saving the xmodule
:param definition_data: can be empty. The initial definition_data for the kvs
:param metadata: can be empty, the initial metadata for the kvs
:param runtime: if you already have an xblock from the course, the xblock.runtime value
:param fields: a dictionary of field names and values for the new xmodule
"""
new_object
=
self
.
create_xmodule
(
location
,
definition_data
,
metadata
,
runtime
,
fields
)
self
.
update_item
(
new_object
,
user_id
,
allow_not_found
=
True
)
return
new_object
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
"""
"""
This base method just copies the assets. The lower level impls must do the actual cloning of
This base method just copies the assets. The lower level impls must do the actual cloning of
...
@@ -634,6 +610,27 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
...
@@ -634,6 +610,27 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
self
.
contentstore
.
_drop_database
()
# pylint: disable=protected-access
self
.
contentstore
.
_drop_database
()
# pylint: disable=protected-access
super
(
ModuleStoreWriteBase
,
self
)
.
_drop_database
()
# pylint: disable=protected-access
super
(
ModuleStoreWriteBase
,
self
)
.
_drop_database
()
# pylint: disable=protected-access
def
create_child
(
self
,
user_id
,
parent_usage_key
,
block_type
,
block_id
=
None
,
fields
=
None
,
**
kwargs
):
"""
Creates and saves a new xblock that as a child of the specified block
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
parent_usage_key: a :class:`~opaque_key.edx.UsageKey` identifing the
block that this item should be parented under
block_type: The type of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
"""
item
=
self
.
create_item
(
user_id
,
parent_usage_key
.
course_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
**
kwargs
)
parent
=
self
.
get_item
(
parent_usage_key
)
parent
.
children
.
append
(
item
.
location
)
self
.
update_item
(
parent
,
user_id
)
@contextmanager
@contextmanager
def
bulk_write_operations
(
self
,
course_id
):
def
bulk_write_operations
(
self
,
course_id
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/draft_and_published.py
0 → 100644
View file @
d53a6669
"""
This module provides an abstraction for Module Stores that support Draft and Published branches.
"""
from
abc
import
ABCMeta
,
abstractmethod
from
.
import
ModuleStoreEnum
class
ModuleStoreDraftAndPublished
(
object
):
"""
A mixin for a read-write database backend that supports two branches, Draft and Published, with
options to prefer Draft and fallback to Published.
"""
__metaclass__
=
ABCMeta
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
ModuleStoreDraftAndPublished
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
branch_setting_func
=
kwargs
.
pop
(
'branch_setting_func'
,
lambda
:
ModuleStoreEnum
.
Branch
.
published_only
)
@abstractmethod
def
delete_item
(
self
,
location
,
user_id
,
revision
=
None
,
**
kwargs
):
raise
NotImplementedError
@abstractmethod
def
get_parent_location
(
self
,
location
,
revision
=
None
,
**
kwargs
):
raise
NotImplementedError
@abstractmethod
def
has_changes
(
self
,
usage_key
):
raise
NotImplementedError
@abstractmethod
def
publish
(
self
,
location
,
user_id
):
raise
NotImplementedError
@abstractmethod
def
unpublish
(
self
,
location
,
user_id
):
raise
NotImplementedError
@abstractmethod
def
revert_to_published
(
self
,
location
,
user_id
):
raise
NotImplementedError
@abstractmethod
def
compute_publish_state
(
self
,
xblock
):
raise
NotImplementedError
@abstractmethod
def
convert_to_draft
(
self
,
location
,
user_id
):
raise
NotImplementedError
common/lib/xmodule/xmodule/modulestore/mixed.py
View file @
d53a6669
...
@@ -6,26 +6,24 @@ In this way, courses can be served up both - say - XMLModuleStore or MongoModule
...
@@ -6,26 +6,24 @@ In this way, courses can be served up both - say - XMLModuleStore or MongoModule
"""
"""
import
logging
import
logging
from
uuid
import
uuid4
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
import
itertools
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
.
import
ModuleStoreWriteBase
from
.
import
ModuleStoreWriteBase
from
xmodule.modulestore
import
ModuleStoreEnum
from
.
import
ModuleStoreEnum
from
opaque_keys.edx.locator
import
CourseLocator
,
BlockUsageLocator
from
.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
.draft_and_published
import
ModuleStoreDraftAndPublished
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
.split_migrator
import
SplitMigrator
from
xmodule.modulestore.mongo.base
import
MongoModuleStore
from
xmodule.modulestore.split_mongo.split
import
SplitMongoModuleStore
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
import
itertools
from
xmodule.modulestore.split_migrator
import
SplitMigrator
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
class
MixedModuleStore
(
ModuleStoreWriteBase
):
class
MixedModuleStore
(
ModuleStore
DraftAndPublished
,
ModuleStore
WriteBase
):
"""
"""
ModuleStore knows how to route requests to the right persistence ms
ModuleStore knows how to route requests to the right persistence ms
"""
"""
...
@@ -172,7 +170,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -172,7 +170,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
raise
Exception
(
"Must pass in a course_key when calling get_items()"
)
raise
Exception
(
"Must pass in a course_key when calling get_items()"
)
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
get_items
(
course_key
,
settings
,
content
,
**
kwargs
)
return
store
.
get_items
(
course_key
,
settings
=
settings
,
content
=
content
,
**
kwargs
)
def
get_courses
(
self
):
def
get_courses
(
self
):
'''
'''
...
@@ -272,7 +270,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -272,7 +270,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
errs
.
update
(
store
.
get_errored_courses
())
errs
.
update
(
store
.
get_errored_courses
())
return
errs
return
errs
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
fields
=
None
,
**
kwargs
):
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
**
kwargs
):
"""
"""
Creates and returns the course.
Creates and returns the course.
...
@@ -286,11 +284,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -286,11 +284,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
Returns: a CourseDescriptor
Returns: a CourseDescriptor
"""
"""
store
=
self
.
_get_modulestore_for_courseid
(
None
)
store
=
self
.
_verify_modulestore_support
(
None
,
'create_course'
)
if
not
hasattr
(
store
,
'create_course'
):
return
store
.
create_course
(
org
,
course
,
run
,
user_id
,
**
kwargs
)
raise
NotImplementedError
(
u"Cannot create a course on store {}"
.
format
(
store
))
return
store
.
create_course
(
org
,
course
,
run
,
user_id
,
fields
,
**
kwargs
)
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
"""
"""
...
@@ -319,66 +314,60 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -319,66 +314,60 @@ class MixedModuleStore(ModuleStoreWriteBase):
source_course_id
,
user_id
,
dest_course_id
.
org
,
dest_course_id
.
course
,
dest_course_id
.
run
source_course_id
,
user_id
,
dest_course_id
.
org
,
dest_course_id
.
course
,
dest_course_id
.
run
)
)
def
create_item
(
self
,
course_or_parent_loc
,
category
,
user_id
,
**
kwargs
):
def
create_item
(
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
fields
=
None
,
**
kwargs
):
"""
"""
Create
and return the item. If parent_loc is a specific location v a course id,
Create
s and saves a new item in a course.
it installs the new item as a child of the parent (if the parent_loc is a specific
xblock reference)
.
Returns the newly created item
.
:param course_or_parent_loc: Can be a CourseKey or UsageKey
Args:
:param category (str): The block_type of the item we are creating
user_id: ID of the user creating and saving the xmodule
course_key: A :class:`~opaque_keys.edx.CourseKey` identifying which course to create
this item in
block_type: The typo of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
"""
"""
# find the store for the course
modulestore
=
self
.
_verify_modulestore_support
(
course_key
,
'create_item'
)
course_id
=
getattr
(
course_or_parent_loc
,
'course_key'
,
course_or_parent_loc
)
return
modulestore
.
create_item
(
user_id
,
course_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
**
kwargs
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
location
=
kwargs
.
pop
(
'location'
,
None
)
def
create_child
(
self
,
user_id
,
parent_usage_key
,
block_type
,
block_id
=
None
,
fields
=
None
,
**
kwargs
):
# invoke its create_item
"""
if
isinstance
(
store
,
MongoModuleStore
):
Creates and saves a new xblock that is a child of the specified block
block_id
=
kwargs
.
pop
(
'block_id'
,
getattr
(
location
,
'name'
,
uuid4
()
.
hex
))
parent_loc
=
course_or_parent_loc
if
isinstance
(
course_or_parent_loc
,
UsageKey
)
else
None
# must have a legitimate location, compute if appropriate
if
location
is
None
:
location
=
course_id
.
make_usage_key
(
category
,
block_id
)
# do the actual creation
xblock
=
self
.
create_and_save_xmodule
(
location
,
user_id
,
**
kwargs
)
# don't forget to attach to parent
if
parent_loc
is
not
None
and
not
'detached'
in
xblock
.
_class_tags
:
parent
=
store
.
get_item
(
parent_loc
)
parent
.
children
.
append
(
location
)
store
.
update_item
(
parent
,
user_id
)
elif
isinstance
(
store
,
SplitMongoModuleStore
):
if
not
isinstance
(
course_or_parent_loc
,
(
CourseLocator
,
BlockUsageLocator
)):
raise
ValueError
(
u"Cannot create a child of {} in split. Wrong repr."
.
format
(
course_or_parent_loc
))
# split handles all the fields in one dict not separated by scope
fields
=
kwargs
.
get
(
'fields'
,
{})
fields
.
update
(
kwargs
.
pop
(
'metadata'
,
{}))
fields
.
update
(
kwargs
.
pop
(
'definition_data'
,
{}))
kwargs
[
'fields'
]
=
fields
xblock
=
store
.
create_item
(
course_or_parent_loc
,
category
,
user_id
,
**
kwargs
)
else
:
raise
NotImplementedError
(
u"Cannot create an item on store
%
s"
%
store
)
return
xblock
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
parent_usage_key: a :class:`~opaque_key.edx.UsageKey` identifying the
block that this item should be parented under
block_type: The typo of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
"""
modulestore
=
self
.
_verify_modulestore_support
(
parent_usage_key
.
course_key
,
'create_child'
)
return
modulestore
.
create_child
(
user_id
,
parent_usage_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
**
kwargs
)
def
update_item
(
self
,
xblock
,
user_id
,
allow_not_found
=
False
):
def
update_item
(
self
,
xblock
,
user_id
,
allow_not_found
=
False
):
"""
"""
Update the xblock persisted to be the same as the given for all types of fields
Update the xblock persisted to be the same as the given for all types of fields
(content, children, and metadata) attribute the change to the given user.
(content, children, and metadata) attribute the change to the given user.
"""
"""
store
=
self
.
_verify_modulestore_support
(
xblock
.
location
,
'update_item'
)
store
=
self
.
_verify_modulestore_support
(
xblock
.
location
.
course_key
,
'update_item'
)
return
store
.
update_item
(
xblock
,
user_id
,
allow_not_found
)
return
store
.
update_item
(
xblock
,
user_id
,
allow_not_found
)
def
delete_item
(
self
,
location
,
user_id
,
**
kwargs
):
def
delete_item
(
self
,
location
,
user_id
,
**
kwargs
):
"""
"""
Delete the given item from persistence. kwargs allow modulestore specific parameters.
Delete the given item from persistence. kwargs allow modulestore specific parameters.
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'delete_item'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'delete_item'
)
store
.
delete_item
(
location
,
user_id
=
user_id
,
**
kwargs
)
store
.
delete_item
(
location
,
user_id
=
user_id
,
**
kwargs
)
def
revert_to_published
(
self
,
location
,
user_id
=
None
):
def
revert_to_published
(
self
,
location
,
user_id
):
"""
"""
Reverts an item to its last published version (recursively traversing all of its descendants).
Reverts an item to its last published version (recursively traversing all of its descendants).
If no published version exists, a VersionConflictError is thrown.
If no published version exists, a VersionConflictError is thrown.
...
@@ -388,8 +377,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -388,8 +377,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
:raises InvalidVersionError: if no published version exists for the location specified
:raises InvalidVersionError: if no published version exists for the location specified
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'revert_to_published'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'revert_to_published'
)
return
store
.
revert_to_published
(
location
,
user_id
=
user_id
)
return
store
.
revert_to_published
(
location
,
user_id
)
def
close_all_connections
(
self
):
def
close_all_connections
(
self
):
"""
"""
...
@@ -408,7 +397,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -408,7 +397,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
if
hasattr
(
modulestore
,
'_drop_database'
):
if
hasattr
(
modulestore
,
'_drop_database'
):
modulestore
.
_drop_database
()
# pylint: disable=protected-access
modulestore
.
_drop_database
()
# pylint: disable=protected-access
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}):
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}
,
**
kwargs
):
"""
"""
Create the new xmodule but don't save it. Returns the new module.
Create the new xmodule but don't save it. Returns the new module.
...
@@ -418,8 +407,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -418,8 +407,8 @@ class MixedModuleStore(ModuleStoreWriteBase):
:param runtime: if you already have an xblock from the course, the xblock.runtime value
:param runtime: if you already have an xblock from the course, the xblock.runtime value
:param fields: a dictionary of field names and values for the new xmodule
:param fields: a dictionary of field names and values for the new xmodule
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'create_xmodule'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'create_xmodule'
)
return
store
.
create_xmodule
(
location
,
definition_data
,
metadata
,
runtime
,
fields
)
return
store
.
create_xmodule
(
location
,
definition_data
,
metadata
,
runtime
,
fields
,
**
kwargs
)
def
get_courses_for_wiki
(
self
,
wiki_slug
):
def
get_courses_for_wiki
(
self
,
wiki_slug
):
"""
"""
...
@@ -463,7 +452,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -463,7 +452,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
Save a current draft to the underlying modulestore
Save a current draft to the underlying modulestore
Returns the newly published item.
Returns the newly published item.
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'publish'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'publish'
)
return
store
.
publish
(
location
,
user_id
)
return
store
.
publish
(
location
,
user_id
)
def
unpublish
(
self
,
location
,
user_id
):
def
unpublish
(
self
,
location
,
user_id
):
...
@@ -471,7 +460,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -471,7 +460,7 @@ class MixedModuleStore(ModuleStoreWriteBase):
Save a current draft to the underlying modulestore
Save a current draft to the underlying modulestore
Returns the newly unpublished item.
Returns the newly unpublished item.
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'unpublish'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'unpublish'
)
return
store
.
unpublish
(
location
,
user_id
)
return
store
.
unpublish
(
location
,
user_id
)
def
convert_to_draft
(
self
,
location
,
user_id
):
def
convert_to_draft
(
self
,
location
,
user_id
):
...
@@ -481,18 +470,26 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -481,18 +470,26 @@ class MixedModuleStore(ModuleStoreWriteBase):
:param source: the location of the source (its revision must be None)
:param source: the location of the source (its revision must be None)
"""
"""
store
=
self
.
_verify_modulestore_support
(
location
,
'convert_to_draft'
)
store
=
self
.
_verify_modulestore_support
(
location
.
course_key
,
'convert_to_draft'
)
return
store
.
convert_to_draft
(
location
,
user_id
)
return
store
.
convert_to_draft
(
location
,
user_id
)
def
_verify_modulestore_support
(
self
,
location
,
method
):
def
has_changes
(
self
,
usage_key
):
"""
Checks if the given block has unpublished changes
:param usage_key: the block to check
:return: True if the draft and published versions differ
"""
store
=
self
.
_verify_modulestore_support
(
usage_key
.
course_key
,
'has_changes'
)
return
store
.
has_changes
(
usage_key
)
def
_verify_modulestore_support
(
self
,
course_key
,
method
):
"""
"""
Finds and returns the store that contains the course for the given location, and verifying
Finds and returns the store that contains the course for the given location, and verifying
that the store supports the given method.
that the store supports the given method.
Raises NotImplementedError if the found store does not support the given method.
Raises NotImplementedError if the found store does not support the given method.
"""
"""
course_id
=
location
.
course_key
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
if
hasattr
(
store
,
method
):
if
hasattr
(
store
,
method
):
return
store
return
store
else
:
else
:
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
d53a6669
...
@@ -17,6 +17,7 @@ import sys
...
@@ -17,6 +17,7 @@ import sys
import
logging
import
logging
import
copy
import
copy
import
re
import
re
from
uuid
import
uuid4
from
bson.son
import
SON
from
bson.son
import
SON
from
fs.osfs
import
OSFS
from
fs.osfs
import
OSFS
...
@@ -34,6 +35,7 @@ from xblock.exceptions import InvalidScopeError
...
@@ -34,6 +35,7 @@ from xblock.exceptions import InvalidScopeError
from
xblock.fields
import
Scope
,
ScopeIds
,
Reference
,
ReferenceList
,
ReferenceValueDict
from
xblock.fields
import
Scope
,
ScopeIds
,
Reference
,
ReferenceList
,
ReferenceValueDict
from
xmodule.modulestore
import
ModuleStoreWriteBase
,
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreWriteBase
,
ModuleStoreEnum
from
xmodule.modulestore.draft_and_published
import
ModuleStoreDraftAndPublished
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
,
ReferentialIntegrityError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
,
ReferentialIntegrityError
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceMixin
,
inherit_metadata
,
InheritanceKeyValueStore
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceMixin
,
inherit_metadata
,
InheritanceKeyValueStore
...
@@ -335,7 +337,7 @@ def as_published(location):
...
@@ -335,7 +337,7 @@ def as_published(location):
return
location
.
replace
(
revision
=
MongoRevisionKey
.
published
)
return
location
.
replace
(
revision
=
MongoRevisionKey
.
published
)
class
MongoModuleStore
(
ModuleStoreWriteBase
):
class
MongoModuleStore
(
ModuleStore
DraftAndPublished
,
ModuleStore
WriteBase
):
"""
"""
A Mongodb backed ModuleStore
A Mongodb backed ModuleStore
"""
"""
...
@@ -353,7 +355,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -353,7 +355,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
:param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware.
:param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware.
"""
"""
super
(
MongoModuleStore
,
self
)
.
__init__
(
contentstore
,
**
kwargs
)
super
(
MongoModuleStore
,
self
)
.
__init__
(
contentstore
=
contentstore
,
**
kwargs
)
def
do_connection
(
def
do_connection
(
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
**
kwargs
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
**
kwargs
...
@@ -903,7 +905,12 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -903,7 +905,12 @@ class MongoModuleStore(ModuleStoreWriteBase):
]))
]))
location
=
course_id
.
make_usage_key
(
'course'
,
course_id
.
run
)
location
=
course_id
.
make_usage_key
(
'course'
,
course_id
.
run
)
course
=
self
.
create_and_save_xmodule
(
location
,
user_id
,
fields
=
fields
,
**
kwargs
)
course
=
self
.
create_xmodule
(
location
,
fields
=
fields
,
**
kwargs
)
self
.
update_item
(
course
,
user_id
,
allow_not_found
=
True
)
# clone a default 'about' overview module as well
# clone a default 'about' overview module as well
about_location
=
location
.
replace
(
about_location
=
location
.
replace
(
...
@@ -911,16 +918,18 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -911,16 +918,18 @@ class MongoModuleStore(ModuleStoreWriteBase):
name
=
'overview'
name
=
'overview'
)
)
overview_template
=
AboutDescriptor
.
get_template
(
'overview.yaml'
)
overview_template
=
AboutDescriptor
.
get_template
(
'overview.yaml'
)
self
.
create_and_save_xmodule
(
self
.
create_item
(
about_location
,
user_id
,
user_id
,
about_location
.
course_key
,
about_location
.
block_type
,
block_id
=
about_location
.
block_id
,
definition_data
=
overview_template
.
get
(
'data'
),
definition_data
=
overview_template
.
get
(
'data'
),
runtime
=
course
.
system
runtime
=
course
.
system
)
)
return
course
return
course
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}):
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}
,
**
kwargs
):
"""
"""
Create the new xmodule but don't save it. Returns the new module.
Create the new xmodule but don't save it. Returns the new module.
...
@@ -974,6 +983,52 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -974,6 +983,52 @@ class MongoModuleStore(ModuleStoreWriteBase):
xmodule
.
save
()
xmodule
.
save
()
return
xmodule
return
xmodule
def
create_item
(
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
**
kwargs
):
"""
Creates and saves a new item in a course.
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
course_key: A :class:`~opaque_keys.edx.CourseKey` identifying which course to create
this item in
block_type: The typo of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
"""
if
block_id
is
None
:
block_id
=
uuid4
()
.
hex
location
=
course_key
.
make_usage_key
(
block_type
,
block_id
)
xblock
=
self
.
create_xmodule
(
location
,
**
kwargs
)
self
.
update_item
(
xblock
,
user_id
,
allow_not_found
=
True
)
return
xblock
def
create_child
(
self
,
user_id
,
parent_usage_key
,
block_type
,
block_id
=
None
,
**
kwargs
):
"""
Creates and saves a new xblock that as a child of the specified block
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
parent_usage_key: a :class:`~opaque_key.edx.UsageKey` identifing the
block that this item should be parented under
block_type: The typo of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
"""
xblock
=
self
.
create_item
(
user_id
,
parent_usage_key
.
course_key
,
block_type
,
block_id
=
block_id
,
**
kwargs
)
# attach to parent if given
if
'detached'
not
in
xblock
.
_class_tags
:
parent
=
self
.
get_item
(
parent_usage_key
)
parent
.
children
.
append
(
xblock
.
location
)
self
.
update_item
(
parent
,
user_id
)
return
xblock
def
_get_course_for_item
(
self
,
location
,
depth
=
0
):
def
_get_course_for_item
(
self
,
location
,
depth
=
0
):
'''
'''
for a given Xmodule, return the course that it belongs to
for a given Xmodule, return the course that it belongs to
...
@@ -1064,6 +1119,9 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -1064,6 +1119,9 @@ class MongoModuleStore(ModuleStoreWriteBase):
except
ItemNotFoundError
:
except
ItemNotFoundError
:
if
not
allow_not_found
:
if
not
allow_not_found
:
raise
raise
elif
not
self
.
has_course
(
xblock
.
location
.
course_key
):
raise
ItemNotFoundError
(
xblock
.
location
.
course_key
)
return
xblock
return
xblock
...
@@ -1161,7 +1219,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -1161,7 +1219,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
def
get_orphans
(
self
,
course_key
):
def
get_orphans
(
self
,
course_key
):
"""
"""
Return an array of all of the locations
(deprecated string format)
for orphans in the course.
Return an array of all of the locations for orphans in the course.
"""
"""
course_key
=
self
.
fill_in_run
(
course_key
)
course_key
=
self
.
fill_in_run
(
course_key
)
detached_categories
=
[
name
for
name
,
__
in
XBlock
.
load_tagged_classes
(
"detached"
)]
detached_categories
=
[
name
for
name
,
__
in
XBlock
.
load_tagged_classes
(
"detached"
)]
...
@@ -1178,7 +1236,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -1178,7 +1236,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
)
)
all_reachable
=
all_reachable
.
union
(
item
.
get
(
'definition'
,
{})
.
get
(
'children'
,
[]))
all_reachable
=
all_reachable
.
union
(
item
.
get
(
'definition'
,
{})
.
get
(
'children'
,
[]))
item_locs
-=
all_reachable
item_locs
-=
all_reachable
return
list
(
item_locs
)
return
[
course_key
.
make_usage_key_from_deprecated_string
(
item_loc
)
for
item_loc
in
item_locs
]
def
get_courses_for_wiki
(
self
,
wiki_slug
):
def
get_courses_for_wiki
(
self
,
wiki_slug
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
View file @
d53a6669
...
@@ -54,7 +54,6 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -54,7 +54,6 @@ class DraftModuleStore(MongoModuleStore):
This should be an attribute from ModuleStoreEnum.Branch
This should be an attribute from ModuleStoreEnum.Branch
"""
"""
super
(
DraftModuleStore
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
DraftModuleStore
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
branch_setting_func
=
kwargs
.
pop
(
'branch_setting_func'
,
lambda
:
ModuleStoreEnum
.
Branch
.
published_only
)
def
get_item
(
self
,
usage_key
,
depth
=
0
,
revision
=
None
):
def
get_item
(
self
,
usage_key
,
depth
=
0
,
revision
=
None
):
"""
"""
...
@@ -287,7 +286,7 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -287,7 +286,7 @@ class DraftModuleStore(MongoModuleStore):
else
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
else
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
return
super
(
DraftModuleStore
,
self
)
.
get_parent_location
(
location
,
revision
,
**
kwargs
)
return
super
(
DraftModuleStore
,
self
)
.
get_parent_location
(
location
,
revision
,
**
kwargs
)
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}):
def
create_xmodule
(
self
,
location
,
definition_data
=
None
,
metadata
=
None
,
runtime
=
None
,
fields
=
{}
,
**
kwargs
):
"""
"""
Create the new xmodule but don't save it. Returns the new module with a draft locator if
Create the new xmodule but don't save it. Returns the new module with a draft locator if
the category allows drafts. If the category does not allow drafts, just creates a published module.
the category allows drafts. If the category does not allow drafts, just creates a published module.
...
@@ -483,6 +482,7 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -483,6 +482,7 @@ class DraftModuleStore(MongoModuleStore):
ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
currently only provided by contentstore.views.item.orphan_handler
currently only provided by contentstore.views.item.orphan_handler
Otherwise, raises a ValueError.
"""
"""
self
.
_verify_branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
)
self
.
_verify_branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
)
_verify_revision_is_published
(
location
)
_verify_revision_is_published
(
location
)
...
@@ -527,8 +527,10 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -527,8 +527,10 @@ class DraftModuleStore(MongoModuleStore):
as_functions
=
[
as_draft
,
as_published
]
as_functions
=
[
as_draft
,
as_published
]
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
as_functions
=
[
as_published
]
as_functions
=
[
as_published
]
el
s
e
:
el
if
revision
is
Non
e
:
as_functions
=
[
as_draft
]
as_functions
=
[
as_draft
]
else
:
raise
ValueError
(
'revision not one of None, ModuleStoreEnum.RevisionOption.published_only, or ModuleStoreEnum.RevisionOption.all'
)
self
.
_delete_subtree
(
location
,
as_functions
)
self
.
_delete_subtree
(
location
,
as_functions
)
def
_delete_subtree
(
self
,
location
,
as_functions
):
def
_delete_subtree
(
self
,
location
,
as_functions
):
...
...
common/lib/xmodule/xmodule/modulestore/split_migrator.py
View file @
d53a6669
...
@@ -84,12 +84,13 @@ class SplitMigrator(object):
...
@@ -84,12 +84,13 @@ class SplitMigrator(object):
# it doesn't need the parent as the first arg. That is, it translates and populates
# it doesn't need the parent as the first arg. That is, it translates and populates
# the 'children' field as it goes.
# the 'children' field as it goes.
_new_module
=
self
.
split_modulestore
.
create_item
(
_new_module
=
self
.
split_modulestore
.
create_item
(
course_version_locator
,
module
.
category
,
user_id
,
user_id
,
course_version_locator
,
module
.
location
.
block_type
,
block_id
=
module
.
location
.
block_id
,
block_id
=
module
.
location
.
block_id
,
fields
=
self
.
_get_json_fields_translate_references
(
fields
=
self
.
_get_json_fields_translate_references
(
module
,
course_version_locator
,
new_course
.
location
.
block_id
module
,
course_version_locator
,
new_course
.
location
.
block_id
),
),
# TODO remove continue_version when bulk write is impl'd
continue_version
=
True
continue_version
=
True
)
)
# after done w/ published items, add version for DRAFT pointing to the published structure
# after done w/ published items, add version for DRAFT pointing to the published structure
...
@@ -130,7 +131,8 @@ class SplitMigrator(object):
...
@@ -130,7 +131,8 @@ class SplitMigrator(object):
else
:
else
:
# only a draft version (aka, 'private').
# only a draft version (aka, 'private').
_new_module
=
self
.
split_modulestore
.
create_item
(
_new_module
=
self
.
split_modulestore
.
create_item
(
new_draft_course_loc
,
module
.
category
,
user_id
,
user_id
,
new_draft_course_loc
,
new_locator
.
block_type
,
block_id
=
new_locator
.
block_id
,
block_id
=
new_locator
.
block_id
,
fields
=
self
.
_get_json_fields_translate_references
(
fields
=
self
.
_get_json_fields_translate_references
(
module
,
new_draft_course_loc
,
published_course_usage_key
.
block_id
module
,
new_draft_course_loc
,
published_course_usage_key
.
block_id
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
d53a6669
...
@@ -64,7 +64,9 @@ from opaque_keys.edx.locator import (
...
@@ -64,7 +64,9 @@ from opaque_keys.edx.locator import (
)
)
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
,
VersionConflictError
,
DuplicateItemError
,
\
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
,
VersionConflictError
,
DuplicateItemError
,
\
DuplicateCourseError
DuplicateCourseError
from
xmodule.modulestore
import
inheritance
,
ModuleStoreWriteBase
,
ModuleStoreEnum
,
PublishState
from
xmodule.modulestore
import
(
inheritance
,
ModuleStoreWriteBase
,
ModuleStoreEnum
)
from
..exceptions
import
ItemNotFoundError
from
..exceptions
import
ItemNotFoundError
from
.definition_lazy_loader
import
DefinitionLazyLoader
from
.definition_lazy_loader
import
DefinitionLazyLoader
...
@@ -265,7 +267,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -265,7 +267,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param course_version_guid: if provided, clear only this entry
:param course_version_guid: if provided, clear only this entry
"""
"""
if
course_version_guid
:
if
course_version_guid
:
del
self
.
thread_cache
.
course_cache
[
course_version_guid
]
try
:
del
self
.
thread_cache
.
course_cache
[
course_version_guid
]
except
KeyError
:
pass
else
:
else
:
self
.
thread_cache
.
course_cache
=
{}
self
.
thread_cache
.
course_cache
=
{}
...
@@ -325,17 +330,17 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -325,17 +330,17 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
}
}
return
envelope
return
envelope
def
get_courses
(
self
,
branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
qualifiers
=
None
):
def
get_courses
(
self
,
branch
,
qualifiers
=
None
):
'''
'''
Returns a list of course descriptors matching any given qualifiers.
Returns a list of course descriptors matching any given qualifiers.
qualifiers should be a dict of keywords matching the db fields or any
qualifiers should be a dict of keywords matching the db fields or any
legal query for mongo to use against the active_versions collection.
legal query for mongo to use against the active_versions collection.
Note, this is to find the current head of the named branch type
Note, this is to find the current head of the named branch type
.
(e.g., ModuleStoreEnum.BranchName.draft).
To get specific versions via guid use get_course.
To get specific versions via guid use get_course.
:param branch: the branch for which to return courses.
Default value is ModuleStoreEnum.BranchName.draft.
:param branch: the branch for which to return courses.
:param qualifiers: an optional dict restricting which elements should match
:param qualifiers: an optional dict restricting which elements should match
'''
'''
if
qualifiers
is
None
:
if
qualifiers
is
None
:
...
@@ -415,20 +420,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -415,20 +420,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
return
self
.
_get_block_from_structure
(
course_structure
,
usage_key
.
block_id
)
is
not
None
return
self
.
_get_block_from_structure
(
course_structure
,
usage_key
.
block_id
)
is
not
None
def
has_changes
(
self
,
usage_key
):
"""
Checks if the given block has unpublished changes
:param usage_key: the block to check
:return: True if the draft and published versions differ
"""
draft
=
self
.
get_item
(
usage_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
))
try
:
published
=
self
.
get_item
(
usage_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
))
except
ItemNotFoundError
:
return
True
return
draft
.
update_version
!=
published
.
update_version
def
get_item
(
self
,
usage_key
,
depth
=
0
):
def
get_item
(
self
,
usage_key
,
depth
=
0
):
"""
"""
depth (int): An argument that some module stores may use to prefetch
depth (int): An argument that some module stores may use to prefetch
...
@@ -543,7 +534,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -543,7 +534,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
block_data
[
'category'
]
in
detached_categories
:
if
block_data
[
'category'
]
in
detached_categories
:
items
.
discard
(
decode_key_from_mongo
(
block_id
))
items
.
discard
(
decode_key_from_mongo
(
block_id
))
return
[
return
[
BlockUsageLocator
(
course_key
=
course_key
,
block_type
=
blocks
[
block_id
][
'category'
],
block_id
=
block_id
)
BlockUsageLocator
(
course_key
=
course_key
,
block_type
=
blocks
[
block_id
][
'category'
],
block_id
=
block_id
)
.
version_agnostic
()
for
block_id
in
items
for
block_id
in
items
]
]
...
@@ -640,7 +633,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -640,7 +633,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
Find the history of this block. Return as a VersionTree of each place the block changed (except
Find the history of this block. Return as a VersionTree of each place the block changed (except
deletion).
deletion).
The block's history tracks its explicit changes but not the changes in its children.
The block's history tracks its explicit changes but not the changes in its children starting
from when the block was created.
'''
'''
# course_agnostic means we don't care if the head and version don't align, trust the version
# course_agnostic means we don't care if the head and version don't align, trust the version
...
@@ -650,7 +644,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -650,7 +644,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
all_versions_with_block
=
self
.
db_connection
.
find_matching_structures
(
all_versions_with_block
=
self
.
db_connection
.
find_matching_structures
(
{
{
'original_version'
:
course_struct
[
'original_version'
],
'original_version'
:
course_struct
[
'original_version'
],
update_version_field
:
{
'$exists'
:
True
}
update_version_field
:
{
'$exists'
:
True
}
,
}
}
)
)
# find (all) root versions and build map {previous: {successors}..}
# find (all) root versions and build map {previous: {successors}..}
...
@@ -660,6 +654,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -660,6 +654,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
block_payload
=
self
.
_get_block_from_structure
(
version
,
block_id
)
block_payload
=
self
.
_get_block_from_structure
(
version
,
block_id
)
if
version
[
'_id'
]
==
block_payload
[
'edit_info'
][
'update_version'
]:
if
version
[
'_id'
]
==
block_payload
[
'edit_info'
][
'update_version'
]:
if
block_payload
[
'edit_info'
]
.
get
(
'previous_version'
)
is
None
:
if
block_payload
[
'edit_info'
]
.
get
(
'previous_version'
)
is
None
:
# this was when this block was created
possible_roots
.
append
(
block_payload
[
'edit_info'
][
'update_version'
])
possible_roots
.
append
(
block_payload
[
'edit_info'
][
'update_version'
])
else
:
# map previous to {update..}
else
:
# map previous to {update..}
result
.
setdefault
(
block_payload
[
'edit_info'
][
'previous_version'
],
set
())
.
add
(
result
.
setdefault
(
block_payload
[
'edit_info'
][
'previous_version'
],
set
())
.
add
(
...
@@ -773,21 +768,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -773,21 +768,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
serial
+=
1
serial
+=
1
return
category
+
str
(
serial
)
return
category
+
str
(
serial
)
# DHM: Should I rewrite this to take a new xblock instance rather than to construct it? That is, require the
# caller to use XModuleDescriptor.load_from_json thus reducing similar code and making the object creation and
# validation behavior a responsibility of the model layer rather than the persistence layer.
def
create_item
(
def
create_item
(
self
,
course_or_parent_locator
,
category
,
user_id
,
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
block_id
=
None
,
definition_locator
=
None
,
fields
=
None
,
definition_locator
=
None
,
fields
=
None
,
force
=
False
,
continue_version
=
False
force
=
False
,
continue_version
=
False
,
**
kwargs
):
):
"""
"""
Add a descriptor to persistence as
the last child of the optional parent_location or just as
an element
Add a descriptor to persistence as an element
of the course
(if no parent provided)
. Return the resulting post saved version with populated locators.
of the course. Return the resulting post saved version with populated locators.
:param course_or_parent_locator: If BlockUsageLocator, then it's assumed to be the parent.
:param course_key: If it has a version_guid and a course org + course + run + branch, this
If it's a CourseLocator, then it's
merely the containing course. If it has a version_guid and a course org + course + run + branch, this
method ensures that the version is the head of the given course branch before making the change.
method ensures that the version is the head of the given course branch before making the change.
raises InsufficientSpecificationError if there is no course locator.
raises InsufficientSpecificationError if there is no course locator.
...
@@ -828,14 +818,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -828,14 +818,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
the new version_guid from the locator in the returned object!
the new version_guid from the locator in the returned object!
"""
"""
# find course_index entry if applicable and structures entry
# find course_index entry if applicable and structures entry
index_entry
=
self
.
_get_index_if_valid
(
course_
or_parent_locator
,
force
,
continue_version
)
index_entry
=
self
.
_get_index_if_valid
(
course_
key
,
force
,
continue_version
)
structure
=
self
.
_lookup_course
(
course_
or_parent_locator
)[
'structure'
]
structure
=
self
.
_lookup_course
(
course_
key
)[
'structure'
]
partitioned_fields
=
self
.
partition_fields_by_scope
(
category
,
fields
)
partitioned_fields
=
self
.
partition_fields_by_scope
(
block_type
,
fields
)
new_def_data
=
partitioned_fields
.
get
(
Scope
.
content
,
{})
new_def_data
=
partitioned_fields
.
get
(
Scope
.
content
,
{})
# persist the definition if persisted != passed
# persist the definition if persisted != passed
if
(
definition_locator
is
None
or
isinstance
(
definition_locator
.
definition_id
,
LocalId
)):
if
(
definition_locator
is
None
or
isinstance
(
definition_locator
.
definition_id
,
LocalId
)):
definition_locator
=
self
.
create_definition_from_data
(
new_def_data
,
category
,
user_id
)
definition_locator
=
self
.
create_definition_from_data
(
new_def_data
,
block_type
,
user_id
)
elif
new_def_data
is
not
None
:
elif
new_def_data
is
not
None
:
definition_locator
,
_
=
self
.
update_definition_from_data
(
definition_locator
,
new_def_data
,
user_id
)
definition_locator
,
_
=
self
.
update_definition_from_data
(
definition_locator
,
new_def_data
,
user_id
)
...
@@ -854,15 +844,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -854,15 +844,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else
:
else
:
new_block_id
=
block_id
new_block_id
=
block_id
else
:
else
:
new_block_id
=
self
.
_generate_block_id
(
new_structure
[
'blocks'
],
category
)
new_block_id
=
self
.
_generate_block_id
(
new_structure
[
'blocks'
],
block_type
)
block_fields
=
partitioned_fields
.
get
(
Scope
.
settings
,
{})
block_fields
=
partitioned_fields
.
get
(
Scope
.
settings
,
{})
if
Scope
.
children
in
partitioned_fields
:
if
Scope
.
children
in
partitioned_fields
:
block_fields
.
update
(
partitioned_fields
[
Scope
.
children
])
block_fields
.
update
(
partitioned_fields
[
Scope
.
children
])
self
.
_update_block_in_structure
(
new_structure
,
new_block_id
,
{
self
.
_update_block_in_structure
(
new_structure
,
new_block_id
,
{
"category"
:
category
,
"category"
:
block_type
,
"definition"
:
definition_locator
.
definition_id
,
"definition"
:
definition_locator
.
definition_id
,
"fields"
:
self
.
_serialize_fields
(
category
,
block_fields
),
"fields"
:
self
.
_serialize_fields
(
block_type
,
block_fields
),
'edit_info'
:
{
'edit_info'
:
{
'edited_on'
:
datetime
.
datetime
.
now
(
UTC
),
'edited_on'
:
datetime
.
datetime
.
now
(
UTC
),
'edited_by'
:
user_id
,
'edited_by'
:
user_id
,
...
@@ -871,17 +861,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -871,17 +861,6 @@ 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
.
block_id
is
not
None
:
encoded_block_id
=
encode_key_for_mongo
(
course_or_parent_locator
.
block_id
)
parent
=
new_structure
[
'blocks'
][
encoded_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
parent
[
'edit_info'
][
'previous_version'
]
=
parent
[
'edit_info'
][
'update_version'
]
parent
[
'edit_info'
][
'update_version'
]
=
new_id
if
continue_version
:
if
continue_version
:
# db update
# db update
self
.
db_connection
.
update_structure
(
new_structure
)
self
.
db_connection
.
update_structure
(
new_structure
)
...
@@ -893,22 +872,68 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -893,22 +872,68 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# update the index entry if appropriate
# update the index entry if appropriate
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
if
not
continue_version
:
if
not
continue_version
:
self
.
_update_head
(
index_entry
,
course_
or_parent_locator
.
branch
,
new_id
)
self
.
_update_head
(
index_entry
,
course_
key
.
branch
,
new_id
)
item_loc
=
BlockUsageLocator
(
item_loc
=
BlockUsageLocator
(
course_
or_parent_locator
.
version_agnostic
(),
course_
key
.
version_agnostic
(),
block_type
=
category
,
block_type
=
block_type
,
block_id
=
new_block_id
,
block_id
=
new_block_id
,
)
)
else
:
else
:
item_loc
=
BlockUsageLocator
(
item_loc
=
BlockUsageLocator
(
CourseLocator
(
version_guid
=
new_id
),
CourseLocator
(
version_guid
=
new_id
),
block_type
=
category
,
block_type
=
block_type
,
block_id
=
new_block_id
,
block_id
=
new_block_id
,
)
)
# reconstruct the new_item from the cache
# reconstruct the new_item from the cache
return
self
.
get_item
(
item_loc
)
return
self
.
get_item
(
item_loc
)
def
create_child
(
self
,
user_id
,
parent_usage_key
,
block_type
,
block_id
=
None
,
fields
=
None
,
continue_version
=
False
,
**
kwargs
):
"""
Creates and saves a new xblock that as a child of the specified block
Returns the newly created item.
Args:
user_id: ID of the user creating and saving the xmodule
parent_usage_key: a :class:`~opaque_key.edx.UsageKey` identifying the
block that this item should be parented under
block_type: The typo of block to create
block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
"""
xblock
=
self
.
create_item
(
user_id
,
parent_usage_key
.
course_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
continue_version
=
continue_version
,
**
kwargs
)
# don't version the structure as create_item handled that already.
new_structure
=
self
.
_lookup_course
(
xblock
.
location
.
course_key
)[
'structure'
]
# add new block as child and update parent's version
encoded_block_id
=
encode_key_for_mongo
(
parent_usage_key
.
block_id
)
parent
=
new_structure
[
'blocks'
][
encoded_block_id
]
parent
[
'fields'
]
.
setdefault
(
'children'
,
[])
.
append
(
xblock
.
location
.
block_id
)
if
parent
[
'edit_info'
][
'update_version'
]
!=
new_structure
[
'_id'
]:
# if the parent hadn't been previously changed in this bulk transaction, indicate that it's
# part of the bulk transaction
parent
[
'edit_info'
]
=
{
'edited_on'
:
datetime
.
datetime
.
now
(
UTC
),
'edited_by'
:
user_id
,
'previous_version'
:
parent
[
'edit_info'
][
'update_version'
],
'update_version'
:
new_structure
[
'_id'
],
}
# db update
self
.
db_connection
.
update_structure
(
new_structure
)
# clear cache so things get refetched and inheritance recomputed
self
.
_clear_cache
(
new_structure
[
'_id'
])
# don't need to update the index b/c create_item did it for this version
return
xblock
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
"""
"""
See :meth: `.ModuleStoreWrite.clone_course` for documentation.
See :meth: `.ModuleStoreWrite.clone_course` for documentation.
...
@@ -924,8 +949,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -924,8 +949,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
)
)
def
create_course
(
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
fields
=
None
,
self
,
org
,
course
,
run
,
user_id
,
master_branch
=
None
,
fields
=
None
,
master_branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
versions_dict
=
None
,
root_category
=
'course'
,
versions_dict
=
None
,
root_category
=
'course'
,
root_block_id
=
'course'
,
**
kwargs
root_block_id
=
'course'
,
**
kwargs
):
):
...
@@ -1271,15 +1295,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1271,15 +1295,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
key
not
in
new_keys
or
original_fields
[
key
]
!=
settings
[
key
]:
if
key
not
in
new_keys
or
original_fields
[
key
]
!=
settings
[
key
]:
return
True
return
True
def
xblock_publish
(
self
,
user_id
,
source_course
,
destination_course
,
subtree_list
,
blacklist
):
def
copy
(
self
,
user_id
,
source_course
,
destination_course
,
subtree_list
=
None
,
blacklist
=
None
):
"""
"""
Publish
es each xblock in subtree_list and those blocks descendants excluding blacklist
Copi
es each xblock in subtree_list and those blocks descendants excluding blacklist
from source_course to destination_course.
from source_course to destination_course.
To delete a block, publish its parent. You can blacklist the other sibs to keep them from
To delete a block in the destination_course, copy its parent and blacklist the other
being refreshed. You can also just call delete_item on the destination.
sibs to keep them from being copies. You can also just call delete_item on the destination.
To unpublish a block, call delete_item on the destination.
Ensures that each subtree occurs in the same place in destination as it does in source. If any
Ensures that each subtree occurs in the same place in destination as it does in source. If any
of the source's subtree parents are missing from destination, it raises ItemNotFound([parent_ids]).
of the source's subtree parents are missing from destination, it raises ItemNotFound([parent_ids]).
...
@@ -1355,10 +1377,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1355,10 +1377,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self
.
db_connection
.
insert_structure
(
destination_structure
)
self
.
db_connection
.
insert_structure
(
destination_structure
)
self
.
_update_head
(
index_entry
,
destination_course
.
branch
,
destination_structure
[
'_id'
])
self
.
_update_head
(
index_entry
,
destination_course
.
branch
,
destination_structure
[
'_id'
])
def
unpublish
(
self
,
location
,
user_id
):
published_location
=
location
.
replace
(
branch
=
ModuleStoreEnum
.
BranchName
.
published
)
self
.
delete_item
(
published_location
,
user_id
)
def
update_course_index
(
self
,
updated_index_entry
):
def
update_course_index
(
self
,
updated_index_entry
):
"""
"""
Change the given course's index entry.
Change the given course's index entry.
...
@@ -1445,18 +1463,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1445,18 +1463,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# in case the course is later restored.
# in case the course is later restored.
# super(SplitMongoModuleStore, self).delete_course(course_key, user_id)
# super(SplitMongoModuleStore, self).delete_course(course_key, user_id)
def
revert_to_published
(
self
,
location
,
user_id
=
None
):
"""
Reverts an item to its last published version (recursively traversing all of its descendants).
If no published version exists, a VersionConflictError is thrown.
If a published version exists but there is no draft version of this item or any of its descendants, this
method is a no-op.
:raises InvalidVersionError: if no published version exists for the location specified
"""
raise
NotImplementedError
()
def
inherit_settings
(
self
,
block_map
,
block_json
,
inheriting_settings
=
None
):
def
inherit_settings
(
self
,
block_map
,
block_json
,
inheriting_settings
=
None
):
"""
"""
Updates block_json with any inheritable setting set by an ancestor and recurses to children.
Updates block_json with any inheritable setting set by an ancestor and recurses to children.
...
@@ -1863,47 +1869,3 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1863,47 +1869,3 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
Check that the db is reachable.
Check that the db is reachable.
"""
"""
return
{
ModuleStoreEnum
.
Type
.
split
:
self
.
db_connection
.
heartbeat
()}
return
{
ModuleStoreEnum
.
Type
.
split
:
self
.
db_connection
.
heartbeat
()}
def
compute_publish_state
(
self
,
xblock
):
"""
Returns whether this xblock is draft, public, or private.
Returns:
PublishState.draft - published exists and is different from draft
PublishState.public - published exists and is the same as draft
PublishState.private - no published version exists
"""
# TODO figure out what to say if xblock is not from the HEAD of its branch
def
get_head
(
branch
):
course_structure
=
self
.
_lookup_course
(
xblock
.
location
.
course_key
.
for_branch
(
branch
))[
'structure'
]
return
self
.
_get_block_from_structure
(
course_structure
,
xblock
.
location
.
block_id
)
if
xblock
.
location
.
branch
is
None
:
raise
ValueError
(
u'{} is not in a branch; so, this is nonsensical'
.
format
(
xblock
.
location
))
if
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
draft
:
other
=
get_head
(
ModuleStoreEnum
.
BranchName
.
published
)
elif
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
published
:
other
=
get_head
(
ModuleStoreEnum
.
BranchName
.
draft
)
else
:
raise
ValueError
(
u'{} is not in a branch other than draft or published; so, this is nonsensical'
.
format
(
xblock
.
location
))
if
not
other
:
if
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
draft
:
return
PublishState
.
private
else
:
return
PublishState
.
public
# a bit nonsensical
elif
xblock
.
update_version
!=
other
[
'edit_info'
][
'update_version'
]:
return
PublishState
.
draft
else
:
return
PublishState
.
public
def
convert_to_draft
(
self
,
location
,
user_id
):
"""
Create a copy of the source and mark its revision as draft.
:param source: the location of the source (its revision must be None)
"""
# This is a no-op in Split since a draft version of the data always remains
pass
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
0 → 100644
View file @
d53a6669
"""
Module for the dual-branch fall-back Draft->Published Versioning ModuleStore
"""
from
..exceptions
import
ItemNotFoundError
from
split
import
SplitMongoModuleStore
from
xmodule.modulestore
import
ModuleStoreEnum
,
PublishState
from
xmodule.modulestore.draft_and_published
import
ModuleStoreDraftAndPublished
from
xmodule.modulestore.draft
import
DIRECT_ONLY_CATEGORIES
class
DraftVersioningModuleStore
(
ModuleStoreDraftAndPublished
,
SplitMongoModuleStore
):
"""
A subclass of Split that supports a dual-branch fall-back versioning framework
with a Draft branch that falls back to a Published branch.
"""
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
**
kwargs
):
master_branch
=
kwargs
.
pop
(
'master_branch'
,
ModuleStoreEnum
.
BranchName
.
draft
)
return
super
(
DraftVersioningModuleStore
,
self
)
.
create_course
(
org
,
course
,
run
,
user_id
,
master_branch
=
master_branch
,
**
kwargs
)
def
get_courses
(
self
):
"""
Returns all the courses on the Draft branch (which is a superset of the courses on the Published branch).
"""
return
super
(
DraftVersioningModuleStore
,
self
)
.
get_courses
(
ModuleStoreEnum
.
BranchName
.
draft
)
def
delete_item
(
self
,
location
,
user_id
,
revision
=
None
,
**
kwargs
):
"""
Delete the given item from persistence. kwargs allow modulestore specific parameters.
Args:
location: UsageKey of the item to be deleted
user_id: id of the user deleting the item
revision:
None - deletes the item and its subtree, and updates the parents per description above
ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
currently only provided by contentstore.views.item.orphan_handler
Otherwise, raises a ValueError.
"""
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
]
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
all
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
,
ModuleStoreEnum
.
BranchName
.
draft
]
elif
revision
is
None
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
draft
]
else
:
raise
ValueError
(
'revision not one of None, ModuleStoreEnum.RevisionOption.published_only, or ModuleStoreEnum.RevisionOption.all'
)
for
branch
in
branches_to_delete
:
SplitMongoModuleStore
.
delete_item
(
self
,
location
.
for_branch
(
branch
),
user_id
,
**
kwargs
)
def
_map_revision_to_branch
(
self
,
key
,
revision
=
None
):
"""
Maps RevisionOptions to BranchNames, inserting them into the key
"""
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
return
key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
)
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
draft_only
:
return
key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
)
else
:
return
key
def
has_item
(
self
,
usage_key
,
revision
=
None
):
"""
Returns True if location exists in this ModuleStore.
"""
usage_key
=
self
.
_map_revision_to_branch
(
usage_key
,
revision
=
revision
)
return
super
(
DraftVersioningModuleStore
,
self
)
.
has_item
(
usage_key
)
def
get_item
(
self
,
usage_key
,
depth
=
0
,
revision
=
None
):
"""
Returns the item identified by usage_key and revision.
"""
usage_key
=
self
.
_map_revision_to_branch
(
usage_key
,
revision
=
revision
)
return
super
(
DraftVersioningModuleStore
,
self
)
.
get_item
(
usage_key
,
depth
=
depth
)
def
get_items
(
self
,
course_locator
,
settings
=
None
,
content
=
None
,
revision
=
None
,
**
kwargs
):
"""
Returns a list of XModuleDescriptor instances for the matching items within the course with
the given course_locator.
"""
course_locator
=
self
.
_map_revision_to_branch
(
course_locator
,
revision
=
revision
)
return
super
(
DraftVersioningModuleStore
,
self
)
.
get_items
(
course_locator
,
settings
=
settings
,
content
=
content
,
**
kwargs
)
def
get_parent_location
(
self
,
location
,
revision
=
None
,
**
kwargs
):
'''
Returns the given location's parent location in this course.
Args:
revision:
None - uses the branch setting for the revision
ModuleStoreEnum.RevisionOption.published_only
- return only the PUBLISHED parent if it exists, else returns None
ModuleStoreEnum.RevisionOption.draft_preferred
- return either the DRAFT or PUBLISHED parent, preferring DRAFT, if parent(s) exists,
else returns None
'''
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
:
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
location
=
self
.
_map_revision_to_branch
(
location
,
revision
=
revision
)
return
SplitMongoModuleStore
.
get_parent_location
(
self
,
location
,
**
kwargs
)
def
has_changes
(
self
,
usage_key
):
"""
Checks if the given block has unpublished changes
:param usage_key: the block to check
:return: True if the draft and published versions differ
"""
# TODO for better performance: lookup the courses and get the block entry, don't create the instances
draft
=
self
.
get_item
(
usage_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
))
try
:
published
=
self
.
get_item
(
usage_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
))
except
ItemNotFoundError
:
return
True
return
draft
.
update_version
!=
published
.
update_version
def
publish
(
self
,
location
,
user_id
,
**
kwargs
):
"""
Save a current draft to the underlying modulestore.
Returns the newly published item.
"""
SplitMongoModuleStore
.
copy
(
self
,
user_id
,
location
.
course_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
),
location
.
course_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
),
[
location
],
)
def
unpublish
(
self
,
location
,
user_id
):
"""
Deletes the published version of the item.
Returns the newly unpublished item.
"""
self
.
delete_item
(
location
,
user_id
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
return
self
.
get_item
(
location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
))
def
revert_to_published
(
self
,
location
,
user_id
):
"""
Reverts an item to its last published version (recursively traversing all of its descendants).
If no published version exists, a VersionConflictError is thrown.
If a published version exists but there is no draft version of this item or any of its descendants, this
method is a no-op.
:raises InvalidVersionError: if no published version exists for the location specified
"""
raise
NotImplementedError
()
def
compute_publish_state
(
self
,
xblock
):
"""
Returns whether this xblock is draft, public, or private.
Returns:
PublishState.draft - published exists and is different from draft
PublishState.public - published exists and is the same as draft
PublishState.private - no published version exists
"""
# TODO figure out what to say if xblock is not from the HEAD of its branch
def
get_head
(
branch
):
course_structure
=
self
.
_lookup_course
(
xblock
.
location
.
course_key
.
for_branch
(
branch
))[
'structure'
]
return
self
.
_get_block_from_structure
(
course_structure
,
xblock
.
location
.
block_id
)
if
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
draft
:
try
:
other
=
get_head
(
ModuleStoreEnum
.
BranchName
.
published
)
except
ItemNotFoundError
:
return
PublishState
.
private
elif
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
published
:
other
=
get_head
(
ModuleStoreEnum
.
BranchName
.
draft
)
else
:
raise
ValueError
(
u'{} is in a branch other than draft or published.'
.
format
(
xblock
.
location
))
if
not
other
:
if
xblock
.
location
.
branch
==
ModuleStoreEnum
.
BranchName
.
draft
:
return
PublishState
.
private
else
:
return
PublishState
.
public
elif
xblock
.
update_version
!=
other
[
'edit_info'
][
'update_version'
]:
return
PublishState
.
draft
else
:
return
PublishState
.
public
def
convert_to_draft
(
self
,
location
,
user_id
):
"""
Create a copy of the source and mark its revision as draft.
:param source: the location of the source (its revision must be None)
"""
# This is a no-op in Split since a draft version of the data always remains
pass
common/lib/xmodule/xmodule/modulestore/tests/factories.py
View file @
d53a6669
...
@@ -174,7 +174,15 @@ class ItemFactory(XModuleFactory):
...
@@ -174,7 +174,15 @@ class ItemFactory(XModuleFactory):
if
display_name
is
not
None
:
if
display_name
is
not
None
:
metadata
[
'display_name'
]
=
display_name
metadata
[
'display_name'
]
=
display_name
runtime
=
parent
.
runtime
if
parent
else
None
runtime
=
parent
.
runtime
if
parent
else
None
store
.
create_and_save_xmodule
(
location
,
user_id
,
metadata
=
metadata
,
definition_data
=
data
,
runtime
=
runtime
)
store
.
create_item
(
user_id
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
,
metadata
=
metadata
,
definition_data
=
data
,
runtime
=
runtime
)
module
=
store
.
get_item
(
location
)
module
=
store
.
get_item
(
location
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py
View file @
d53a6669
...
@@ -4,6 +4,7 @@ from xmodule.course_module import CourseDescriptor
...
@@ -4,6 +4,7 @@ from xmodule.course_module import CourseDescriptor
from
xmodule.x_module
import
XModuleDescriptor
from
xmodule.x_module
import
XModuleDescriptor
import
factory
import
factory
from
factory.helpers
import
lazy_attribute
from
factory.helpers
import
lazy_attribute
from
opaque_keys.edx.keys
import
UsageKey
# Factories don't have __init__ methods, and are self documenting
# Factories don't have __init__ methods, and are self documenting
# pylint: disable=W0232, C0111
# pylint: disable=W0232, C0111
...
@@ -61,7 +62,7 @@ class ItemFactory(SplitFactory):
...
@@ -61,7 +62,7 @@ class ItemFactory(SplitFactory):
# pylint: disable=W0613
# pylint: disable=W0613
@classmethod
@classmethod
def
_create
(
cls
,
target_class
,
parent_location
,
category
=
'chapter'
,
def
_create
(
cls
,
target_class
,
parent_location
,
category
=
'chapter'
,
user_id
=
ModuleStoreEnum
.
UserID
.
test
,
block_id
=
None
,
definition_locator
=
None
,
force
=
False
,
user_id
=
ModuleStoreEnum
.
UserID
.
test
,
definition_locator
=
None
,
force
=
False
,
continue_version
=
False
,
**
kwargs
):
continue_version
=
False
,
**
kwargs
):
"""
"""
passes *kwargs* as the new item's field values:
passes *kwargs* as the new item's field values:
...
@@ -73,10 +74,16 @@ class ItemFactory(SplitFactory):
...
@@ -73,10 +74,16 @@ class ItemFactory(SplitFactory):
:param definition_locator (optional): the DescriptorLocator for the definition this uses or branches
:param definition_locator (optional): the DescriptorLocator for the definition this uses or branches
"""
"""
modulestore
=
kwargs
.
pop
(
'modulestore'
)
modulestore
=
kwargs
.
pop
(
'modulestore'
)
return
modulestore
.
create_item
(
if
isinstance
(
parent_location
,
UsageKey
):
parent_location
,
category
,
user_id
,
definition_locator
=
definition_locator
,
return
modulestore
.
create_child
(
block_id
=
block_id
,
force
=
force
,
continue_version
=
continue_version
,
fields
=
kwargs
user_id
,
parent_location
,
category
,
defintion_locator
=
definition_locator
,
)
force
=
force
,
continue_version
=
continue_version
,
**
kwargs
)
else
:
return
modulestore
.
create_item
(
user_id
,
parent_location
,
category
,
defintion_locator
=
definition_locator
,
force
=
force
,
continue_version
=
continue_version
,
**
kwargs
)
@classmethod
@classmethod
def
_build
(
cls
,
target_class
,
*
args
,
**
kwargs
):
def
_build
(
cls
,
target_class
,
*
args
,
**
kwargs
):
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
d53a6669
...
@@ -8,7 +8,7 @@ import unittest
...
@@ -8,7 +8,7 @@ import unittest
from
xmodule.tests
import
DATA_DIR
from
xmodule.tests
import
DATA_DIR
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreEnum
,
PublishState
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
...
@@ -67,7 +67,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -67,7 +67,7 @@ class TestMixedModuleStore(unittest.TestCase):
},
},
{
{
'NAME'
:
'split'
,
'NAME'
:
'split'
,
'ENGINE'
:
'xmodule.modulestore.split_mongo.split
.SplitMongo
ModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.split_mongo.split
_draft.DraftVersioning
ModuleStore'
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'OPTIONS'
:
modulestore_options
'OPTIONS'
:
modulestore_options
},
},
...
@@ -117,26 +117,21 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -117,26 +117,21 @@ class TestMixedModuleStore(unittest.TestCase):
"""
"""
Create a course w/ one item in the persistence store using the given course & item location.
Create a course w/ one item in the persistence store using the given course & item location.
"""
"""
course
=
self
.
store
.
create_course
(
course_key
.
org
,
course_key
.
course
,
course_key
.
run
,
self
.
user_id
)
# create course
category
=
self
.
writable_chapter_location
.
category
self
.
course
=
self
.
store
.
create_course
(
course_key
.
org
,
course_key
.
course
,
course_key
.
run
,
self
.
user_id
)
block_id
=
self
.
writable_chapter_location
.
name
if
isinstance
(
self
.
course
.
id
,
CourseLocator
):
chapter
=
self
.
store
.
create_item
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
=
self
.
course
.
location
.
version_agnostic
()
# don't use course_location as it may not be the repr
course
.
location
,
category
,
self
.
user_id
,
location
=
self
.
writable_chapter_location
,
block_id
=
block_id
)
if
isinstance
(
course
.
id
,
CourseLocator
):
self
.
course_locations
[
self
.
MONGO_COURSEID
]
=
course
.
location
.
version_agnostic
()
self
.
writable_chapter_location
=
chapter
.
location
.
version_agnostic
()
else
:
else
:
self
.
assertEqual
(
course
.
id
,
course_key
)
self
.
assertEqual
(
self
.
course
.
id
,
course_key
)
self
.
assertEqual
(
chapter
.
location
,
self
.
writable_chapter_location
)
self
.
course
=
course
# create chapter
chapter
=
self
.
store
.
create_child
(
self
.
user_id
,
self
.
course
.
location
,
'chapter'
,
block_id
=
'Overview'
)
self
.
writable_chapter_location
=
chapter
.
location
.
version_agnostic
()
def
_create_block_hierarchy
(
self
):
def
_create_block_hierarchy
(
self
):
"""
"""
Creates a hierarchy of blocks for testing
Creates a hierarchy of blocks for testing
Each block is assigned as a field of the class and can be easily accessed
Each block
's (version_agnostic) location
is assigned as a field of the class and can be easily accessed
"""
"""
BlockInfo
=
namedtuple
(
'BlockInfo'
,
'field_name, category, display_name, sub_tree'
)
BlockInfo
=
namedtuple
(
'BlockInfo'
,
'field_name, category, display_name, sub_tree'
)
...
@@ -150,6 +145,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -150,6 +145,7 @@ class TestMixedModuleStore(unittest.TestCase):
BlockInfo
(
'problem_x1a_1'
,
'problem'
,
'Problem_x1a_1'
,
[]),
BlockInfo
(
'problem_x1a_1'
,
'problem'
,
'Problem_x1a_1'
,
[]),
BlockInfo
(
'problem_x1a_2'
,
'problem'
,
'Problem_x1a_2'
,
[]),
BlockInfo
(
'problem_x1a_2'
,
'problem'
,
'Problem_x1a_2'
,
[]),
BlockInfo
(
'problem_x1a_3'
,
'problem'
,
'Problem_x1a_3'
,
[]),
BlockInfo
(
'problem_x1a_3'
,
'problem'
,
'Problem_x1a_3'
,
[]),
BlockInfo
(
'html_x1a_1'
,
'html'
,
'HTML_x1a_1'
,
[]),
]
]
)
)
]
]
...
@@ -174,12 +170,13 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -174,12 +170,13 @@ class TestMixedModuleStore(unittest.TestCase):
]
]
def
create_sub_tree
(
parent
,
block_info
):
def
create_sub_tree
(
parent
,
block_info
):
block
=
self
.
store
.
create_item
(
parent
.
location
,
block_info
.
category
,
self
.
user_id
,
block_id
=
block_info
.
display_name
)
block
=
self
.
store
.
create_child
(
self
.
user_id
,
parent
.
location
.
version_agnostic
(),
block_info
.
category
,
block_id
=
block_info
.
display_name
)
for
tree
in
block_info
.
sub_tree
:
for
tree
in
block_info
.
sub_tree
:
create_sub_tree
(
block
,
tree
)
create_sub_tree
(
block
,
tree
)
# reload the block to update its children field
setattr
(
self
,
block_info
.
field_name
,
block
.
location
.
version_agnostic
())
block
=
self
.
store
.
get_item
(
block
.
location
)
setattr
(
self
,
block_info
.
field_name
,
block
)
for
tree
in
trees
:
for
tree
in
trees
:
create_sub_tree
(
self
.
course
,
tree
)
create_sub_tree
(
self
.
course
,
tree
)
...
@@ -206,7 +203,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -206,7 +203,7 @@ class TestMixedModuleStore(unittest.TestCase):
# convert to CourseKeys
# convert to CourseKeys
self
.
course_locations
=
{
self
.
course_locations
=
{
course_id
:
SlashSeparatedCourseKey
.
from_deprecated
_string
(
course_id
)
course_id
:
CourseLocator
.
from
_string
(
course_id
)
for
course_id
in
[
self
.
MONGO_COURSEID
,
self
.
XML_COURSEID1
,
self
.
XML_COURSEID2
]
for
course_id
in
[
self
.
MONGO_COURSEID
,
self
.
XML_COURSEID1
,
self
.
XML_COURSEID2
]
}
}
# and then to the root UsageKey
# and then to the root UsageKey
...
@@ -220,14 +217,9 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -220,14 +217,9 @@ class TestMixedModuleStore(unittest.TestCase):
)
.
make_usage_key
(
'vertical'
,
'baz'
)
)
.
make_usage_key
(
'vertical'
,
'baz'
)
else
:
else
:
self
.
fake_location
=
Location
(
'foo'
,
'bar'
,
'slowly'
,
'vertical'
,
'baz'
)
self
.
fake_location
=
Location
(
'foo'
,
'bar'
,
'slowly'
,
'vertical'
,
'baz'
)
self
.
writable_chapter_location
=
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
replace
(
category
=
'chapter'
,
name
=
'Overview'
)
self
.
xml_chapter_location
=
self
.
course_locations
[
self
.
XML_COURSEID1
]
.
replace
(
self
.
xml_chapter_location
=
self
.
course_locations
[
self
.
XML_COURSEID1
]
.
replace
(
category
=
'chapter'
,
name
=
'Overview'
category
=
'chapter'
,
name
=
'Overview'
)
)
self
.
_create_course
(
default
,
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
self
.
_create_course
(
default
,
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
'draft'
,
'split'
)
...
@@ -323,13 +315,14 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -323,13 +315,14 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
store
.
get_item
(
self
.
writable_chapter_location
)
self
.
store
.
get_item
(
self
.
writable_chapter_location
)
# create and delete a private vertical with private children
# create and delete a private vertical with private children
private_vert
=
self
.
store
.
create_
item
(
private_vert
=
self
.
store
.
create_
child
(
# don't use course_location as it may not be the repr
# don't use course_location as it may not be the repr
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'vertical'
,
user_id
=
self
.
user_id
,
block_id
=
'private'
self
.
user_id
,
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'vertical'
,
block_id
=
'private'
)
)
private_leaf
=
self
.
store
.
create_
item
(
private_leaf
=
self
.
store
.
create_
child
(
# don't use course_location as it may not be the repr
# don't use course_location as it may not be the repr
private_vert
.
location
,
'html'
,
user_id
=
self
.
user_id
,
block_id
=
'private_leaf'
self
.
user_id
,
private_vert
.
location
,
'html'
,
block_id
=
'private_leaf'
)
)
# verify pre delete state (just to verify that the test is valid)
# verify pre delete state (just to verify that the test is valid)
...
@@ -359,18 +352,18 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -359,18 +352,18 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
assertFalse
(
self
.
store
.
has_item
(
leaf_loc
))
self
.
assertFalse
(
self
.
store
.
has_item
(
leaf_loc
))
self
.
assertNotIn
(
vert_loc
,
course
.
children
)
self
.
assertNotIn
(
vert_loc
,
course
.
children
)
#
NAATODO enable for split after your converge merge
#
TODO can remove this once LMS-2869 is implemented
if
default_ms
==
'split'
:
# first create a Published branch
return
self
.
store
.
publish
(
self
.
course_locations
[
self
.
MONGO_COURSEID
],
self
.
user_id
)
# reproduce bug STUD-1965
# reproduce bug STUD-1965
# create and delete a private vertical with private children
# create and delete a private vertical with private children
private_vert
=
self
.
store
.
create_
item
(
private_vert
=
self
.
store
.
create_
child
(
# don't use course_location as it may not be the repr
# don't use course_location as it may not be the repr
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'vertical'
,
user_id
=
self
.
user_id
,
block_id
=
'publish'
self
.
user_id
,
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'vertical'
,
block_id
=
'publish'
)
)
private_leaf
=
self
.
store
.
create_
item
(
private_leaf
=
self
.
store
.
create_
child
(
private_vert
.
location
,
'html'
,
user_id
=
self
.
user_id
,
block_id
=
'bug_leaf'
self
.
user_id
,
private_vert
.
location
,
'html'
,
block_id
=
'bug_leaf'
)
)
self
.
store
.
publish
(
private_vert
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
private_vert
.
location
,
self
.
user_id
)
...
@@ -437,14 +430,13 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -437,14 +430,13 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
assertEqual
(
parent
,
self
.
course_locations
[
self
.
XML_COURSEID1
])
self
.
assertEqual
(
parent
,
self
.
course_locations
[
self
.
XML_COURSEID1
])
def
verify_get_parent_locations_results
(
self
,
expected_results
):
def
verify_get_parent_locations_results
(
self
,
expected_results
):
# expected_results should be a list of (child, parent, revision)
for
child_location
,
parent_location
,
revision
in
expected_results
:
for
test
in
expected_results
:
self
.
assertEqual
(
self
.
assertEqual
(
test
[
1
]
.
location
if
test
[
1
]
else
None
,
parent_location
,
self
.
store
.
get_parent_location
(
test
[
0
]
.
location
,
revision
=
test
[
2
]
)
self
.
store
.
get_parent_location
(
child_location
,
revision
=
revision
)
)
)
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
,
'split'
)
def
test_get_parent_locations_moved_child
(
self
,
default_ms
):
def
test_get_parent_locations_moved_child
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
...
@@ -453,30 +445,34 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -453,30 +445,34 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
# make drafts of verticals
# make drafts of verticals
self
.
store
.
convert_to_draft
(
self
.
vertical_x1a
.
location
,
self
.
user_id
)
self
.
store
.
convert_to_draft
(
self
.
vertical_x1a
,
self
.
user_id
)
self
.
store
.
convert_to_draft
(
self
.
vertical_y1a
.
location
,
self
.
user_id
)
self
.
store
.
convert_to_draft
(
self
.
vertical_y1a
,
self
.
user_id
)
# move child problem_x1a_1 to vertical_y1a
# move child problem_x1a_1 to vertical_y1a
child_to_move
=
self
.
problem_x1a_1
child_to_move_location
=
self
.
problem_x1a_1
old_parent
=
self
.
vertical_x1a
new_parent_location
=
self
.
vertical_y1a
new_parent
=
self
.
vertical_y1a
old_parent_location
=
self
.
vertical_x1a
old_parent
.
children
.
remove
(
child_to_move
.
location
)
new_parent
.
children
.
append
(
child_to_move
.
location
)
old_parent
=
self
.
store
.
get_item
(
old_parent_location
)
old_parent
.
children
.
remove
(
child_to_move_location
.
replace
(
version_guid
=
old_parent
.
location
.
version_guid
))
self
.
store
.
update_item
(
old_parent
,
self
.
user_id
)
self
.
store
.
update_item
(
old_parent
,
self
.
user_id
)
new_parent
=
self
.
store
.
get_item
(
new_parent_location
)
new_parent
.
children
.
append
(
child_to_move_location
.
replace
(
version_guid
=
new_parent
.
location
.
version_guid
))
self
.
store
.
update_item
(
new_parent
,
self
.
user_id
)
self
.
store
.
update_item
(
new_parent
,
self
.
user_id
)
self
.
verify_get_parent_locations_results
([
self
.
verify_get_parent_locations_results
([
(
child_to_move
,
new_parent
,
None
),
(
child_to_move
_location
,
new_parent_location
,
None
),
(
child_to_move
,
new_parent
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_move
_location
,
new_parent_location
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_move
,
old_parent
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
(
child_to_move
_location
,
old_parent_location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
)
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
])
# publish the course again
# publish the course again
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
verify_get_parent_locations_results
([
self
.
verify_get_parent_locations_results
([
(
child_to_move
,
new_parent
,
None
),
(
child_to_move
_location
,
new_parent_location
,
None
),
(
child_to_move
,
new_parent
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_move
_location
,
new_parent_location
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_move
,
new_parent
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
(
child_to_move
_location
,
new_parent_location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
)
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
])
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
...
@@ -488,29 +484,28 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -488,29 +484,28 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
# make draft of vertical
# make draft of vertical
self
.
store
.
convert_to_draft
(
self
.
vertical_y1a
.
location
,
self
.
user_id
)
self
.
store
.
convert_to_draft
(
self
.
vertical_y1a
,
self
.
user_id
)
# delete child problem_y1a_1
# delete child problem_y1a_1
child_to_delete
=
self
.
problem_y1a_1
child_to_delete
_location
=
self
.
problem_y1a_1
old_parent
=
self
.
vertical_y1a
old_parent
_location
=
self
.
vertical_y1a
self
.
store
.
delete_item
(
child_to_delete
.
location
,
self
.
user_id
)
self
.
store
.
delete_item
(
child_to_delete
_
location
,
self
.
user_id
)
self
.
verify_get_parent_locations_results
([
self
.
verify_get_parent_locations_results
([
(
child_to_delete
,
old_parent
,
None
),
(
child_to_delete
_location
,
old_parent_location
,
None
),
# Note: The following could be an unexpected result, but we want to avoid an extra database call
# Note: The following could be an unexpected result, but we want to avoid an extra database call
(
child_to_delete
,
old_parent
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_delete
_location
,
old_parent_location
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_delete
,
old_parent
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
(
child_to_delete
_location
,
old_parent_location
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
])
# publish the course again
# publish the course again
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
verify_get_parent_locations_results
([
self
.
verify_get_parent_locations_results
([
(
child_to_delete
,
None
,
None
),
(
child_to_delete
_location
,
None
,
None
),
(
child_to_delete
,
None
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_delete
_location
,
None
,
ModuleStoreEnum
.
RevisionOption
.
draft_preferred
),
(
child_to_delete
,
None
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
(
child_to_delete
_location
,
None
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
])
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
def
test_revert_to_published_root_draft
(
self
,
default_ms
):
def
test_revert_to_published_root_draft
(
self
,
default_ms
):
"""
"""
...
@@ -518,22 +513,26 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -518,22 +513,26 @@ class TestMixedModuleStore(unittest.TestCase):
"""
"""
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
vertical
=
self
.
store
.
get_item
(
self
.
vertical_x1a
)
vertical_children_num
=
len
(
vertical
.
children
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
# delete leaf problem (will make parent vertical a draft)
# delete leaf problem (will make parent vertical a draft)
self
.
store
.
delete_item
(
self
.
problem_x1a_1
.
location
,
self
.
user_id
)
self
.
store
.
delete_item
(
self
.
problem_x1a_1
,
self
.
user_id
)
draft_parent
=
self
.
store
.
get_item
(
self
.
vertical_x1a
.
location
)
draft_parent
=
self
.
store
.
get_item
(
self
.
vertical_x1a
)
self
.
assertEqual
(
2
,
len
(
draft_parent
.
children
))
self
.
assertEqual
(
vertical_children_num
-
1
,
len
(
draft_parent
.
children
))
published_parent
=
self
.
store
.
get_item
(
published_parent
=
self
.
store
.
get_item
(
self
.
vertical_x1a
.
location
,
self
.
vertical_x1a
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
)
self
.
assertEqual
(
3
,
len
(
published_parent
.
children
))
self
.
assertEqual
(
vertical_children_num
,
len
(
published_parent
.
children
))
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
.
location
,
self
.
user_id
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
,
self
.
user_id
)
reverted_parent
=
self
.
store
.
get_item
(
self
.
vertical_x1a
.
location
)
reverted_parent
=
self
.
store
.
get_item
(
self
.
vertical_x1a
)
self
.
assertEqual
(
3
,
len
(
published_parent
.
children
))
self
.
assertEqual
(
vertical_children_num
,
len
(
published_parent
.
children
))
self
.
assertEqual
(
reverted_parent
,
published_parent
)
self
.
assertEqual
(
reverted_parent
,
published_parent
)
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
...
@@ -545,14 +544,15 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -545,14 +544,15 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
orig_display_name
=
self
.
problem_x1a_1
.
display_name
problem
=
self
.
store
.
get_item
(
self
.
problem_x1a_1
)
orig_display_name
=
problem
.
display_name
# Change display name of problem and update just it (so parent remains published)
# Change display name of problem and update just it (so parent remains published)
self
.
problem_x1a_1
.
display_name
=
"updated before calling revert"
problem
.
display_name
=
"updated before calling revert"
self
.
store
.
update_item
(
self
.
problem_x1a_1
,
self
.
user_id
)
self
.
store
.
update_item
(
problem
,
self
.
user_id
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
.
location
,
self
.
user_id
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
,
self
.
user_id
)
reverted_problem
=
self
.
store
.
get_item
(
self
.
problem_x1a_1
.
location
)
reverted_problem
=
self
.
store
.
get_item
(
self
.
problem_x1a_1
)
self
.
assertEqual
(
orig_display_name
,
reverted_problem
.
display_name
)
self
.
assertEqual
(
orig_display_name
,
reverted_problem
.
display_name
)
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
...
@@ -564,9 +564,9 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -564,9 +564,9 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
orig_vertical
=
self
.
vertical_x1a
orig_vertical
=
self
.
store
.
get_item
(
self
.
vertical_x1a
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
.
location
,
self
.
user_id
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
,
self
.
user_id
)
reverted_vertical
=
self
.
store
.
get_item
(
self
.
vertical_x1a
.
location
)
reverted_vertical
=
self
.
store
.
get_item
(
self
.
vertical_x1a
)
self
.
assertEqual
(
orig_vertical
,
reverted_vertical
)
self
.
assertEqual
(
orig_vertical
,
reverted_vertical
)
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
...
@@ -577,7 +577,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -577,7 +577,7 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
with
self
.
assertRaises
(
InvalidVersionError
):
with
self
.
assertRaises
(
InvalidVersionError
):
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
.
location
)
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
,
self
.
user_id
)
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
def
test_revert_to_published_direct_only
(
self
,
default_ms
):
def
test_revert_to_published_direct_only
(
self
,
default_ms
):
...
@@ -586,22 +586,44 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -586,22 +586,44 @@ class TestMixedModuleStore(unittest.TestCase):
"""
"""
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
self
.
_create_block_hierarchy
()
self
.
store
.
revert_to_published
(
self
.
sequential_x1
.
location
)
self
.
store
.
revert_to_published
(
self
.
sequential_x1
,
self
.
user_id
)
reverted_parent
=
self
.
store
.
get_item
(
self
.
sequential_x1
.
location
)
reverted_parent
=
self
.
store
.
get_item
(
self
.
sequential_x1
)
# It does not discard the child vertical, even though that child is a draft (with no published version)
# It does not discard the child vertical, even though that child is a draft (with no published version)
self
.
assertEqual
(
1
,
len
(
reverted_parent
.
children
))
self
.
assertEqual
(
1
,
len
(
reverted_parent
.
children
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
'draft'
,
'split'
)
def
test_get_orphans
(
self
,
default_ms
):
def
test_get_orphans
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
# create an orphan
course_id
=
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
course_id
=
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
orphan
=
self
.
store
.
create_item
(
course_id
,
'problem'
,
self
.
user_id
,
block_id
=
'orphan'
)
# create parented children
self
.
_create_block_hierarchy
()
# orphans
orphan_locations
=
[
course_id
.
make_usage_key
(
'chapter'
,
'OrphanChapter'
),
course_id
.
make_usage_key
(
'vertical'
,
'OrphanVertical'
),
course_id
.
make_usage_key
(
'problem'
,
'OrphanProblem'
),
course_id
.
make_usage_key
(
'html'
,
'OrphanHTML'
),
]
# detached items (not considered as orphans)
detached_locations
=
[
course_id
.
make_usage_key
(
'static_tab'
,
'StaticTab'
),
course_id
.
make_usage_key
(
'about'
,
'overview'
),
course_id
.
make_usage_key
(
'course_info'
,
'updates'
),
]
for
location
in
(
orphan_locations
+
detached_locations
):
self
.
store
.
create_item
(
self
.
user_id
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
found_orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
found_orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
if
default_ms
==
'split'
:
self
.
assertEqual
(
set
(
found_orphans
),
set
(
orphan_locations
))
self
.
assertEqual
(
found_orphans
,
[
orphan
.
location
.
version_agnostic
()])
else
:
self
.
assertEqual
(
found_orphans
,
[
orphan
.
location
.
to_deprecated_string
()])
@ddt.data
(
'draft'
)
@ddt.data
(
'draft'
)
def
test_create_item_from_parent_location
(
self
,
default_ms
):
def
test_create_item_from_parent_location
(
self
,
default_ms
):
...
@@ -610,7 +632,12 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -610,7 +632,12 @@ class TestMixedModuleStore(unittest.TestCase):
new location for the child
new location for the child
"""
"""
self
.
initdb
(
default_ms
)
self
.
initdb
(
default_ms
)
self
.
store
.
create_item
(
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'problem'
,
self
.
user_id
,
block_id
=
'orphan'
)
self
.
store
.
create_child
(
self
.
user_id
,
self
.
course_locations
[
self
.
MONGO_COURSEID
],
'problem'
,
block_id
=
'orphan'
)
orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
self
.
assertEqual
(
len
(
orphans
),
0
,
"unexpected orphans: {}"
.
format
(
orphans
))
self
.
assertEqual
(
len
(
orphans
),
0
,
"unexpected orphans: {}"
.
format
(
orphans
))
...
@@ -631,6 +658,86 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -631,6 +658,86 @@ class TestMixedModuleStore(unittest.TestCase):
self
.
assertEqual
(
len
(
self
.
store
.
get_courses_for_wiki
(
'edX.simple.2012_Fall'
)),
0
)
self
.
assertEqual
(
len
(
self
.
store
.
get_courses_for_wiki
(
'edX.simple.2012_Fall'
)),
0
)
self
.
assertEqual
(
len
(
self
.
store
.
get_courses_for_wiki
(
'no_such_wiki'
)),
0
)
self
.
assertEqual
(
len
(
self
.
store
.
get_courses_for_wiki
(
'no_such_wiki'
)),
0
)
@ddt.data
(
'draft'
,
'split'
)
def
test_unpublish
(
self
,
default_ms
):
"""
Test calling unpublish
"""
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
# publish
self
.
store
.
publish
(
self
.
course
.
location
,
self
.
user_id
)
published_xblock
=
self
.
store
.
get_item
(
self
.
vertical_x1a
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
self
.
assertIsNotNone
(
published_xblock
)
# unpublish
self
.
store
.
unpublish
(
self
.
vertical_x1a
,
self
.
user_id
)
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
vertical_x1a
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
# make sure draft version still exists
draft_xblock
=
self
.
store
.
get_item
(
self
.
vertical_x1a
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
)
self
.
assertIsNotNone
(
draft_xblock
)
@ddt.data
(
'draft'
,
'split'
)
def
test_compute_publish_state
(
self
,
default_ms
):
"""
Test the compute_publish_state method
"""
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
# TODO - Remove this call to explicitly Publish the course once LMS-2869 is implemented
# For now, we need this since we can't publish a child item without its course already been published
course_location
=
self
.
course_locations
[
self
.
MONGO_COURSEID
]
self
.
store
.
publish
(
course_location
,
self
.
user_id
)
# start off as Private
item
=
self
.
store
.
create_child
(
self
.
user_id
,
self
.
writable_chapter_location
,
'problem'
,
'test_compute_publish_state'
)
item_location
=
item
.
location
.
version_agnostic
()
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
private
)
# Private -> Public
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
public
)
# Public -> Private
self
.
store
.
unpublish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
private
)
# Private -> Public
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
public
)
# Public -> Draft with NO changes
# Note: This is where Split and Mongo differ
self
.
store
.
convert_to_draft
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
draft
if
default_ms
==
'draft'
else
PublishState
.
public
)
# Draft WITH changes
item
.
display_name
=
'new name'
item
=
self
.
store
.
update_item
(
item
,
self
.
user_id
)
self
.
assertTrue
(
self
.
store
.
has_changes
(
item
.
location
))
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
draft
)
#=============================================================================================================
#=============================================================================================================
# General utils for not using django settings
# General utils for not using django settings
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
View file @
d53a6669
...
@@ -23,7 +23,7 @@ from xblock.plugin import Plugin
...
@@ -23,7 +23,7 @@ from xblock.plugin import Plugin
from
xmodule.tests
import
DATA_DIR
from
xmodule.tests
import
DATA_DIR
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.mongo
import
Mongo
ModuleStore
,
Mongo
KeyValueStore
from
xmodule.modulestore.mongo
import
MongoKeyValueStore
from
xmodule.modulestore.draft
import
DraftModuleStore
from
xmodule.modulestore.draft
import
DraftModuleStore
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
,
AssetLocation
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
,
AssetLocation
from
opaque_keys.edx.keys
import
UsageKey
from
opaque_keys.edx.keys
import
UsageKey
...
@@ -148,7 +148,7 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -148,7 +148,7 @@ class TestMongoModuleStore(unittest.TestCase):
assert_greater
(
len
(
ids
),
12
)
assert_greater
(
len
(
ids
),
12
)
def
test_mongo_modulestore_type
(
self
):
def
test_mongo_modulestore_type
(
self
):
store
=
Mongo
ModuleStore
(
store
=
Draft
ModuleStore
(
None
,
None
,
{
'host'
:
HOST
,
'db'
:
DB
,
'collection'
:
COLLECTION
},
{
'host'
:
HOST
,
'db'
:
DB
,
'collection'
:
COLLECTION
},
FS_ROOT
,
RENDER_TEMPLATE
,
default_class
=
DEFAULT_CLASS
FS_ROOT
,
RENDER_TEMPLATE
,
default_class
=
DEFAULT_CLASS
...
@@ -390,13 +390,28 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -390,13 +390,28 @@ class TestMongoModuleStore(unittest.TestCase):
def
setup_test
():
def
setup_test
():
course
=
self
.
draft_store
.
get_course
(
course_key
)
course
=
self
.
draft_store
.
get_course
(
course_key
)
# can't use item factory as it depends on django settings
# can't use item factory as it depends on django settings
p1ele
=
self
.
draft_store
.
create_and_save_xmodule
(
p1ele
=
self
.
draft_store
.
create_item
(
course
.
id
.
make_usage_key
(
'problem'
,
'p1'
),
99
,
runtime
=
course
.
runtime
)
99
,
p2ele
=
self
.
draft_store
.
create_and_save_xmodule
(
course_key
,
course
.
id
.
make_usage_key
(
'problem'
,
'p2'
),
99
,
runtime
=
course
.
runtime
)
'problem'
,
block_id
=
'p1'
,
runtime
=
course
.
runtime
)
p2ele
=
self
.
draft_store
.
create_item
(
99
,
course_key
,
'problem'
,
block_id
=
'p2'
,
runtime
=
course
.
runtime
)
self
.
refloc
=
course
.
id
.
make_usage_key
(
'ref_test'
,
'ref_test'
)
self
.
refloc
=
course
.
id
.
make_usage_key
(
'ref_test'
,
'ref_test'
)
self
.
draft_store
.
create_and_save_xmodule
(
self
.
draft_store
.
create_item
(
self
.
refloc
,
99
,
runtime
=
course
.
runtime
,
fields
=
{
99
,
self
.
refloc
.
course_key
,
self
.
refloc
.
block_type
,
block_id
=
self
.
refloc
.
block_id
,
runtime
=
course
.
runtime
,
fields
=
{
'reference_link'
:
p1ele
.
location
,
'reference_link'
:
p1ele
.
location
,
'reference_list'
:
[
p1ele
.
location
,
p2ele
.
location
],
'reference_list'
:
[
p1ele
.
location
,
p2ele
.
location
],
'reference_dict'
:
{
'p1'
:
p1ele
.
location
,
'p2'
:
p2ele
.
location
},
'reference_dict'
:
{
'p1'
:
p1ele
.
location
,
'p2'
:
p2ele
.
location
},
...
@@ -497,12 +512,16 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -497,12 +512,16 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that has_changes() returns false when a new xblock in a direct only category is checked
Tests that has_changes() returns false when a new xblock in a direct only category is checked
"""
"""
course_location
=
Location
(
'ed
x'
,
'direct'
,
'2012_Fall'
,
'course'
,
'test_course
'
)
course_location
=
Location
(
'ed
X'
,
'toy'
,
'2012_Fall'
,
'course'
,
'2012_Fall
'
)
chapter_location
=
Location
(
'ed
x'
,
'direct'
,
'2012_Fall'
,
'chapter'
,
'test_chapt
er'
)
chapter_location
=
Location
(
'ed
X'
,
'toy'
,
'2012_Fall'
,
'chapter'
,
'vertical_contain
er'
)
# Create dummy direct only xblocks
# Create dummy direct only xblocks
self
.
draft_store
.
create_and_save_xmodule
(
course_location
,
user_id
=
self
.
dummy_user
)
self
.
draft_store
.
create_item
(
self
.
draft_store
.
create_and_save_xmodule
(
chapter_location
,
user_id
=
self
.
dummy_user
)
self
.
dummy_user
,
chapter_location
.
course_key
,
chapter_location
.
block_type
,
block_id
=
chapter_location
.
block_id
)
# Check that neither xblock has changes
# Check that neither xblock has changes
self
.
assertFalse
(
self
.
draft_store
.
has_changes
(
course_location
))
self
.
assertFalse
(
self
.
draft_store
.
has_changes
(
course_location
))
...
@@ -512,10 +531,15 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -512,10 +531,15 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that has_changes() only returns true when changes are present
Tests that has_changes() only returns true when changes are present
"""
"""
location
=
Location
(
'edX'
,
'
changes
'
,
'2012_Fall'
,
'vertical'
,
'test_vertical'
)
location
=
Location
(
'edX'
,
'
toy
'
,
'2012_Fall'
,
'vertical'
,
'test_vertical'
)
# Create a dummy component to test against
# Create a dummy component to test against
self
.
draft_store
.
create_and_save_xmodule
(
location
,
user_id
=
self
.
dummy_user
)
self
.
draft_store
.
create_item
(
self
.
dummy_user
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
# Not yet published, so changes are present
# Not yet published, so changes are present
self
.
assertTrue
(
self
.
draft_store
.
has_changes
(
location
))
self
.
assertTrue
(
self
.
draft_store
.
has_changes
(
location
))
...
@@ -538,11 +562,16 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -538,11 +562,16 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that has_changes() returns False when a published parent points to a child that doesn't exist.
Tests that has_changes() returns False when a published parent points to a child that doesn't exist.
"""
"""
location
=
Location
(
'edX'
,
'
missing
'
,
'2012_Fall'
,
'sequential'
,
'parent'
)
location
=
Location
(
'edX'
,
'
toy
'
,
'2012_Fall'
,
'sequential'
,
'parent'
)
# Create the parent and point it to a fake child
# Create the parent and point it to a fake child
parent
=
self
.
draft_store
.
create_and_save_xmodule
(
location
,
user_id
=
self
.
dummy_user
)
parent
=
self
.
draft_store
.
create_item
(
parent
.
children
+=
[
Location
(
'edX'
,
'missing'
,
'2012_Fall'
,
'vertical'
,
'does_not_exist'
)]
self
.
dummy_user
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
parent
.
children
+=
[
Location
(
'edX'
,
'toy'
,
'2012_Fall'
,
'vertical'
,
'does_not_exist'
)]
self
.
draft_store
.
update_item
(
parent
,
self
.
dummy_user
)
self
.
draft_store
.
update_item
(
parent
,
self
.
dummy_user
)
# Check the parent for changes should return False and not throw an exception
# Check the parent for changes should return False and not throw an exception
...
@@ -561,27 +590,39 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -561,27 +590,39 @@ class TestMongoModuleStore(unittest.TestCase):
if
user_id
is
None
:
if
user_id
is
None
:
user_id
=
self
.
dummy_user
user_id
=
self
.
dummy_user
locations
=
{
org
=
'edX'
'grandparent'
:
Location
(
'edX'
,
'tree'
,
name
,
'chapter'
,
'grandparent'
),
course
=
'tree{}'
.
format
(
name
)
'parent_sibling'
:
Location
(
'edX'
,
'tree'
,
name
,
'sequential'
,
'parent_sibling'
),
run
=
name
'parent'
:
Location
(
'edX'
,
'tree'
,
name
,
'sequential'
,
'parent'
),
'child_sibling'
:
Location
(
'edX'
,
'tree'
,
name
,
'vertical'
,
'child_sibling'
),
'child'
:
Location
(
'edX'
,
'tree'
,
name
,
'vertical'
,
'child'
),
}
for
key
in
locations
:
if
not
self
.
draft_store
.
has_course
(
SlashSeparatedCourseKey
(
org
,
course
,
run
))
:
self
.
draft_store
.
create_
and_save_xmodule
(
locations
[
key
],
user_id
=
user_id
)
self
.
draft_store
.
create_
course
(
org
,
course
,
run
,
user_id
)
grandparent
=
self
.
draft_store
.
get_item
(
locations
[
'grandparent'
])
locations
=
{
grandparent
.
children
+=
[
locations
[
'parent_sibling'
],
locations
[
'parent'
]]
'grandparent'
:
Location
(
org
,
course
,
run
,
'chapter'
,
'grandparent'
),
self
.
draft_store
.
update_item
(
grandparent
,
user_id
=
user_id
)
'parent_sibling'
:
Location
(
org
,
course
,
run
,
'sequential'
,
'parent_sibling'
),
'parent'
:
Location
(
org
,
course
,
run
,
'sequential'
,
'parent'
),
'child_sibling'
:
Location
(
org
,
course
,
run
,
'vertical'
,
'child_sibling'
),
'child'
:
Location
(
org
,
course
,
run
,
'vertical'
,
'child'
),
}
parent
=
self
.
draft_store
.
get_item
(
locations
[
'parent'
])
for
key
in
locations
:
parent
.
children
+=
[
locations
[
'child_sibling'
],
locations
[
'child'
]]
self
.
draft_store
.
create_item
(
self
.
draft_store
.
update_item
(
parent
,
user_id
=
user_id
)
user_id
,
locations
[
key
]
.
course_key
,
locations
[
key
]
.
block_type
,
block_id
=
locations
[
key
]
.
block_id
)
grandparent
=
self
.
draft_store
.
get_item
(
locations
[
'grandparent'
])
grandparent
.
children
+=
[
locations
[
'parent_sibling'
],
locations
[
'parent'
]]
self
.
draft_store
.
update_item
(
grandparent
,
user_id
=
user_id
)
self
.
draft_store
.
publish
(
locations
[
'parent'
],
user_id
)
parent
=
self
.
draft_store
.
get_item
(
locations
[
'parent'
])
self
.
draft_store
.
publish
(
locations
[
'parent_sibling'
],
user_id
)
parent
.
children
+=
[
locations
[
'child_sibling'
],
locations
[
'child'
]]
self
.
draft_store
.
update_item
(
parent
,
user_id
=
user_id
)
self
.
draft_store
.
publish
(
locations
[
'parent'
],
user_id
)
self
.
draft_store
.
publish
(
locations
[
'parent_sibling'
],
user_id
)
return
locations
return
locations
...
@@ -663,10 +704,12 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -663,10 +704,12 @@ class TestMongoModuleStore(unittest.TestCase):
# Create a new child and attach it to parent
# Create a new child and attach it to parent
new_child_location
=
Location
(
'edX'
,
'tree'
,
'has_changes_add_remove_child'
,
'vertical'
,
'new_child'
)
new_child_location
=
Location
(
'edX'
,
'tree'
,
'has_changes_add_remove_child'
,
'vertical'
,
'new_child'
)
self
.
draft_store
.
create_and_save_xmodule
(
new_child_location
,
user_id
=
self
.
dummy_user
)
self
.
draft_store
.
create_child
(
parent
=
self
.
draft_store
.
get_item
(
locations
[
'parent'
])
self
.
dummy_user
,
parent
.
children
+=
[
new_child_location
]
locations
[
'parent'
],
self
.
draft_store
.
update_item
(
parent
,
user_id
=
self
.
dummy_user
)
new_child_location
.
block_type
,
block_id
=
new_child_location
.
block_id
)
# Verify that the ancestors now have changes
# Verify that the ancestors now have changes
self
.
assertTrue
(
self
.
draft_store
.
has_changes
(
locations
[
'grandparent'
]))
self
.
assertTrue
(
self
.
draft_store
.
has_changes
(
locations
[
'grandparent'
]))
...
@@ -685,13 +728,21 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -685,13 +728,21 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that has_changes() returns true after editing the child of a vertical (both not direct only categories).
Tests that has_changes() returns true after editing the child of a vertical (both not direct only categories).
"""
"""
parent_location
=
Location
(
'edX'
,
't
est'
,
'non_direct_only_children
'
,
'vertical'
,
'parent'
)
parent_location
=
Location
(
'edX'
,
't
oy'
,
'2012_Fall
'
,
'vertical'
,
'parent'
)
child_location
=
Location
(
'edX'
,
't
est'
,
'non_direct_only_children
'
,
'html'
,
'child'
)
child_location
=
Location
(
'edX'
,
't
oy'
,
'2012_Fall
'
,
'html'
,
'child'
)
parent
=
self
.
draft_store
.
create_and_save_xmodule
(
parent_location
,
user_id
=
self
.
dummy_user
)
parent
=
self
.
draft_store
.
create_item
(
child
=
self
.
draft_store
.
create_and_save_xmodule
(
child_location
,
user_id
=
self
.
dummy_user
)
self
.
dummy_user
,
parent
.
children
+=
[
child_location
]
parent_location
.
course_key
,
self
.
draft_store
.
update_item
(
parent
,
user_id
=
self
.
dummy_user
)
parent_location
.
block_type
,
block_id
=
parent_location
.
block_id
)
child
=
self
.
draft_store
.
create_child
(
self
.
dummy_user
,
parent_location
,
child_location
.
block_type
,
block_id
=
child_location
.
block_id
)
self
.
draft_store
.
publish
(
parent_location
,
self
.
dummy_user
)
self
.
draft_store
.
publish
(
parent_location
,
self
.
dummy_user
)
# Verify that there are no changes
# Verify that there are no changes
...
@@ -757,10 +808,15 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -757,10 +808,15 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that edited_on and edited_by are set correctly during an update
Tests that edited_on and edited_by are set correctly during an update
"""
"""
location
=
Location
(
'edX'
,
'
editInfoTest
'
,
'2012_Fall'
,
'html'
,
'test_html'
)
location
=
Location
(
'edX'
,
'
toy
'
,
'2012_Fall'
,
'html'
,
'test_html'
)
# Create a dummy component to test against
# Create a dummy component to test against
self
.
draft_store
.
create_and_save_xmodule
(
location
,
user_id
=
self
.
dummy_user
)
self
.
draft_store
.
create_item
(
self
.
dummy_user
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
# Store the current edit time and verify that dummy_user created the component
# Store the current edit time and verify that dummy_user created the component
component
=
self
.
draft_store
.
get_item
(
location
)
component
=
self
.
draft_store
.
get_item
(
location
)
...
@@ -780,12 +836,17 @@ class TestMongoModuleStore(unittest.TestCase):
...
@@ -780,12 +836,17 @@ class TestMongoModuleStore(unittest.TestCase):
"""
"""
Tests that published_date and published_by are set correctly
Tests that published_date and published_by are set correctly
"""
"""
location
=
Location
(
'edX'
,
'
publishInfo
'
,
'2012_Fall'
,
'html'
,
'test_html'
)
location
=
Location
(
'edX'
,
'
toy
'
,
'2012_Fall'
,
'html'
,
'test_html'
)
create_user
=
123
create_user
=
123
publish_user
=
456
publish_user
=
456
# Create a dummy component to test against
# Create a dummy component to test against
self
.
draft_store
.
create_and_save_xmodule
(
location
,
user_id
=
create_user
)
self
.
draft_store
.
create_item
(
create_user
,
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
)
# Store the current time, then publish
# Store the current time, then publish
old_time
=
datetime
.
now
(
UTC
)
old_time
=
datetime
.
now
(
UTC
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
deleted
100644 → 0
View file @
40625c45
from
xmodule.modulestore.tests.test_split_w_old_mongo
import
SplitWMongoCourseBoostrapper
class
TestOrphan
(
SplitWMongoCourseBoostrapper
):
"""
Test the orphan finding code
"""
def
_create_course
(
self
):
"""
* some detached items
* some attached children
* some orphans
"""
super
(
TestOrphan
,
self
)
.
_create_course
()
self
.
_create_item
(
'chapter'
,
'Chapter1'
,
{},
{
'display_name'
:
'Chapter 1'
},
'course'
,
'runid'
)
self
.
_create_item
(
'chapter'
,
'Chapter2'
,
{},
{
'display_name'
:
'Chapter 2'
},
'course'
,
'runid'
)
self
.
_create_item
(
'chapter'
,
'OrphanChapter'
,
{},
{
'display_name'
:
'Orphan Chapter'
},
None
,
None
)
self
.
_create_item
(
'vertical'
,
'Vert1'
,
{},
{
'display_name'
:
'Vertical 1'
},
'chapter'
,
'Chapter1'
)
self
.
_create_item
(
'vertical'
,
'OrphanVert'
,
{},
{
'display_name'
:
'Orphan Vertical'
},
None
,
None
)
self
.
_create_item
(
'html'
,
'Html1'
,
"<p>Goodbye</p>"
,
{
'display_name'
:
'Parented Html'
},
'vertical'
,
'Vert1'
)
self
.
_create_item
(
'html'
,
'OrphanHtml'
,
"<p>Hello</p>"
,
{
'display_name'
:
'Orphan html'
},
None
,
None
)
self
.
_create_item
(
'static_tab'
,
'staticuno'
,
"<p>tab</p>"
,
{
'display_name'
:
'Tab uno'
},
None
,
None
)
self
.
_create_item
(
'about'
,
'overview'
,
"<p>overview</p>"
,
{},
None
,
None
)
self
.
_create_item
(
'course_info'
,
'updates'
,
"<ol><li><h2>Sep 22</h2><p>test</p></li></ol>"
,
{},
None
,
None
)
def
test_mongo_orphan
(
self
):
"""
Test that old mongo finds the orphans
"""
orphans
=
self
.
draft_mongo
.
get_orphans
(
self
.
old_course_key
)
self
.
assertEqual
(
len
(
orphans
),
3
,
"Wrong # {}"
.
format
(
orphans
))
location
=
self
.
old_course_key
.
make_usage_key
(
'chapter'
,
'OrphanChapter'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
location
=
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
'OrphanVert'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
location
=
self
.
old_course_key
.
make_usage_key
(
'html'
,
'OrphanHtml'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
def
test_split_orphan
(
self
):
"""
Test that split mongo finds the orphans
"""
orphans
=
self
.
split_mongo
.
get_orphans
(
self
.
split_course_key
)
self
.
assertEqual
(
len
(
orphans
),
3
,
"Wrong # {}"
.
format
(
orphans
))
location
=
self
.
split_course_key
.
make_usage_key
(
'chapter'
,
'OrphanChapter'
)
self
.
assertIn
(
location
,
orphans
)
location
=
self
.
split_course_key
.
make_usage_key
(
'vertical'
,
'OrphanVert'
)
self
.
assertIn
(
location
,
orphans
)
location
=
self
.
split_course_key
.
make_usage_key
(
'html'
,
'OrphanHtml'
)
self
.
assertIn
(
location
,
orphans
)
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
View file @
d53a6669
...
@@ -19,7 +19,7 @@ class TestPublish(SplitWMongoCourseBoostrapper):
...
@@ -19,7 +19,7 @@ class TestPublish(SplitWMongoCourseBoostrapper):
# There are 12 created items and 7 parent updates
# There are 12 created items and 7 parent updates
# create course: finds: 1 to verify uniqueness, 1 to find parents
# create course: finds: 1 to verify uniqueness, 1 to find parents
# sends: 1 to create course, 1 to create overview
# sends: 1 to create course, 1 to create overview
with
check_mongo_calls
(
self
.
draft_mongo
,
5
,
2
):
with
check_mongo_calls
(
self
.
draft_mongo
,
6
,
2
):
super
(
TestPublish
,
self
)
.
_create_course
(
split
=
False
)
# 2 inserts (course and overview)
super
(
TestPublish
,
self
)
.
_create_course
(
split
=
False
)
# 2 inserts (course and overview)
# with bulk will delay all inheritance computations which won't be added into the mongo_calls
# with bulk will delay all inheritance computations which won't be added into the mongo_calls
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_draft_modulestore.py
0 → 100644
View file @
d53a6669
"""
Test split_draft modulestore
"""
import
unittest
import
uuid
from
xmodule.modulestore.split_mongo.split_draft
import
DraftVersioningModuleStore
from
xmodule.modulestore
import
ModuleStoreEnum
from
opaque_keys.edx.locator
import
CourseLocator
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.x_module
import
XModuleMixin
from
xmodule.modulestore.tests.test_split_modulestore
import
SplitModuleTest
# pylint: disable=W0613
def
render_to_template_mock
(
*
args
):
pass
class
TestDraftVersioningModuleStore
(
unittest
.
TestCase
):
def
setUp
(
self
):
super
(
TestDraftVersioningModuleStore
,
self
)
.
setUp
()
self
.
module_store
=
DraftVersioningModuleStore
(
contentstore
=
None
,
doc_store_config
=
{
'host'
:
'localhost'
,
'db'
:
'test_xmodule'
,
'collection'
:
'modulestore{0}'
.
format
(
uuid
.
uuid4
()
.
hex
[:
5
]),
},
fs_root
=
''
,
default_class
=
'xmodule.raw_module.RawDescriptor'
,
render_template
=
render_to_template_mock
,
xblock_mixins
=
(
InheritanceMixin
,
XModuleMixin
),
)
self
.
addCleanup
(
self
.
module_store
.
_drop_database
)
SplitModuleTest
.
bootstrapDB
(
self
.
module_store
)
def
test_has_changes
(
self
):
"""
Tests that has_changes() only returns true when changes are present
"""
draft_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run'
,
branch
=
ModuleStoreEnum
.
BranchName
.
draft
)
head
=
draft_course
.
make_usage_key
(
'course'
,
'head12345'
)
dummy_user
=
ModuleStoreEnum
.
UserID
.
test
# Not yet published, so changes are present
self
.
assertTrue
(
self
.
module_store
.
has_changes
(
head
))
# Publish and verify that there are no unpublished changes
self
.
module_store
.
publish
(
head
,
dummy_user
)
self
.
assertFalse
(
self
.
module_store
.
has_changes
(
head
))
# Change the course, then check that there now are changes
course
=
self
.
module_store
.
get_item
(
head
)
course
.
show_calculator
=
not
course
.
show_calculator
self
.
module_store
.
update_item
(
course
,
dummy_user
)
self
.
assertTrue
(
self
.
module_store
.
has_changes
(
head
))
# Publish and verify again
self
.
module_store
.
publish
(
head
,
dummy_user
)
self
.
assertFalse
(
self
.
module_store
.
has_changes
(
head
))
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
View file @
d53a6669
...
@@ -444,19 +444,19 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -444,19 +444,19 @@ class SplitModuleTest(unittest.TestCase):
}
}
},
},
}
}
@staticmethod
@staticmethod
def
bootstrapDB
():
def
bootstrapDB
(
split_store
):
'''
'''
Sets up the initial data into the db
Sets up the initial data into the db
'''
'''
split_store
=
modulestore
()
for
_course_id
,
course_spec
in
SplitModuleTest
.
COURSE_CONTENT
.
iteritems
():
for
_course_id
,
course_spec
in
SplitModuleTest
.
COURSE_CONTENT
.
iteritems
():
course
=
split_store
.
create_course
(
course
=
split_store
.
create_course
(
course_spec
[
'org'
],
course_spec
[
'org'
],
course_spec
[
'course'
],
course_spec
[
'course'
],
course_spec
[
'run'
],
course_spec
[
'run'
],
course_spec
[
'user_id'
],
course_spec
[
'user_id'
],
master_branch
=
BRANCH_NAME_DRAFT
,
fields
=
course_spec
[
'fields'
],
fields
=
course_spec
[
'fields'
],
root_block_id
=
course_spec
[
'root_block_id'
]
root_block_id
=
course_spec
[
'root_block_id'
]
)
)
...
@@ -494,7 +494,7 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -494,7 +494,7 @@ class SplitModuleTest(unittest.TestCase):
block_id
=
"head23456"
block_id
=
"head23456"
)
)
destination
=
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
destination
=
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
split_store
.
xblock_publish
(
"test@edx.org"
,
to_publish
,
destination
,
[
to_publish
],
None
)
split_store
.
copy
(
"test@edx.org"
,
to_publish
,
destination
,
[
to_publish
],
None
)
def
setUp
(
self
):
def
setUp
(
self
):
self
.
user_id
=
random
.
getrandbits
(
32
)
self
.
user_id
=
random
.
getrandbits
(
32
)
...
@@ -823,32 +823,6 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -823,32 +823,6 @@ class SplitModuleItemTests(SplitModuleTest):
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
course
.
location
.
for_branch
(
BRANCH_NAME_PUBLISHED
))
modulestore
()
.
get_item
(
course
.
location
.
for_branch
(
BRANCH_NAME_PUBLISHED
))
def
test_has_changes
(
self
):
"""
Tests that has_changes() only returns true when changes are present
"""
draft_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
published_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
head
=
draft_course
.
make_usage_key
(
'course'
,
'head12345'
)
dummy_user
=
ModuleStoreEnum
.
UserID
.
test
# Not yet published, so changes are present
self
.
assertTrue
(
modulestore
()
.
has_changes
(
head
))
# Publish and verify that there are no unpublished changes
modulestore
()
.
xblock_publish
(
dummy_user
,
draft_course
,
published_course
,
[
head
],
None
)
self
.
assertFalse
(
modulestore
()
.
has_changes
(
head
))
# Change the course, then check that there now are changes
course
=
modulestore
()
.
get_item
(
head
)
course
.
show_calculator
=
not
course
.
show_calculator
modulestore
()
.
update_item
(
course
,
dummy_user
)
self
.
assertTrue
(
modulestore
()
.
has_changes
(
head
))
# Publish and verify again
modulestore
()
.
xblock_publish
(
dummy_user
,
draft_course
,
published_course
,
[
head
],
None
)
self
.
assertFalse
(
modulestore
()
.
has_changes
(
head
))
def
test_get_non_root
(
self
):
def
test_get_non_root
(
self
):
# not a course obj
# not a course obj
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
...
@@ -1002,7 +976,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1002,7 +976,7 @@ class TestItemCrud(SplitModuleTest):
def
test_create_minimal_item
(
self
):
def
test_create_minimal_item
(
self
):
"""
"""
create_item(
course_or_parent_locator, category, user
, definition_locator=None, fields): new_desciptor
create_item(
user, location, category
, definition_locator=None, fields): new_desciptor
"""
"""
# grab link to course to ensure new versioning works
# grab link to course to ensure new versioning works
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
...
@@ -1011,7 +985,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1011,7 +985,7 @@ class TestItemCrud(SplitModuleTest):
# add minimal one w/o a parent
# add minimal one w/o a parent
category
=
'sequential'
category
=
'sequential'
new_module
=
modulestore
()
.
create_item
(
new_module
=
modulestore
()
.
create_item
(
locator
,
category
,
'user123'
,
'user123'
,
locator
,
category
,
fields
=
{
'display_name'
:
'new sequential'
}
fields
=
{
'display_name'
:
'new sequential'
}
)
)
# check that course version changed and course's previous is the other one
# check that course version changed and course's previous is the other one
...
@@ -1051,8 +1025,8 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1051,8 +1025,8 @@ class TestItemCrud(SplitModuleTest):
)
)
premod_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
premod_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
category
=
'chapter'
category
=
'chapter'
new_module
=
modulestore
()
.
create_
item
(
new_module
=
modulestore
()
.
create_
child
(
locator
,
category
,
'user123'
,
'user123'
,
locator
,
category
,
fields
=
{
'display_name'
:
'new chapter'
},
fields
=
{
'display_name'
:
'new chapter'
},
definition_locator
=
original
.
definition_locator
definition_locator
=
original
.
definition_locator
)
)
...
@@ -1080,13 +1054,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1080,13 +1054,13 @@ class TestItemCrud(SplitModuleTest):
)
)
category
=
'problem'
category
=
'problem'
new_payload
=
"<problem>empty</problem>"
new_payload
=
"<problem>empty</problem>"
new_module
=
modulestore
()
.
create_
item
(
new_module
=
modulestore
()
.
create_
child
(
locator
,
category
,
'anotheruser'
,
'anotheruser'
,
locator
,
category
,
fields
=
{
'display_name'
:
'problem 1'
,
'data'
:
new_payload
},
fields
=
{
'display_name'
:
'problem 1'
,
'data'
:
new_payload
},
)
)
another_payload
=
"<problem>not empty</problem>"
another_payload
=
"<problem>not empty</problem>"
another_module
=
modulestore
()
.
create_
item
(
another_module
=
modulestore
()
.
create_
child
(
locator
,
category
,
'anotheruser'
,
'anotheruser'
,
locator
,
category
,
fields
=
{
'display_name'
:
'problem 2'
,
'data'
:
another_payload
},
fields
=
{
'display_name'
:
'problem 2'
,
'data'
:
another_payload
},
definition_locator
=
original
.
definition_locator
,
definition_locator
=
original
.
definition_locator
,
)
)
...
@@ -1112,8 +1086,8 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1112,8 +1086,8 @@ class TestItemCrud(SplitModuleTest):
course_key
=
CourseLocator
(
org
=
'guestx'
,
course
=
'contender'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course_key
=
CourseLocator
(
org
=
'guestx'
,
course
=
'contender'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
parent_locator
=
BlockUsageLocator
(
course_key
,
'course'
,
block_id
=
"head345679"
)
parent_locator
=
BlockUsageLocator
(
course_key
,
'course'
,
block_id
=
"head345679"
)
chapter_locator
=
BlockUsageLocator
(
course_key
,
'chapter'
,
block_id
=
"foo.bar_-~:0"
)
chapter_locator
=
BlockUsageLocator
(
course_key
,
'chapter'
,
block_id
=
"foo.bar_-~:0"
)
modulestore
()
.
create_
item
(
modulestore
()
.
create_
child
(
parent_locator
,
'chapter'
,
'anotherus
er'
,
'anotheruser'
,
parent_locator
,
'chapt
er'
,
block_id
=
chapter_locator
.
block_id
,
block_id
=
chapter_locator
.
block_id
,
fields
=
{
'display_name'
:
'chapter 99'
},
fields
=
{
'display_name'
:
'chapter 99'
},
)
)
...
@@ -1123,8 +1097,8 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1123,8 +1097,8 @@ class TestItemCrud(SplitModuleTest):
# now try making that a parent of something
# now try making that a parent of something
new_payload
=
"<problem>empty</problem>"
new_payload
=
"<problem>empty</problem>"
problem_locator
=
BlockUsageLocator
(
course_key
,
'problem'
,
block_id
=
"prob.bar_-~:99a"
)
problem_locator
=
BlockUsageLocator
(
course_key
,
'problem'
,
block_id
=
"prob.bar_-~:99a"
)
modulestore
()
.
create_
item
(
modulestore
()
.
create_
child
(
chapter_locator
,
'problem'
,
'anotheruser
'
,
'anotheruser'
,
chapter_locator
,
'problem
'
,
block_id
=
problem_locator
.
block_id
,
block_id
=
problem_locator
.
block_id
,
fields
=
{
'display_name'
:
'chapter 99'
,
'data'
:
new_payload
},
fields
=
{
'display_name'
:
'chapter 99'
,
'data'
:
new_payload
},
)
)
...
@@ -1140,7 +1114,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1140,7 +1114,7 @@ class TestItemCrud(SplitModuleTest):
"""
"""
# start transaction w/ simple creation
# start transaction w/ simple creation
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
'test_run'
,
user
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
'test_run'
,
user
,
BRANCH_NAME_DRAFT
)
new_course_locator
=
new_course
.
id
new_course_locator
=
new_course
.
id
index_history_info
=
modulestore
()
.
get_course_history_info
(
new_course
.
location
)
index_history_info
=
modulestore
()
.
get_course_history_info
(
new_course
.
location
)
course_block_prev_version
=
new_course
.
previous_version
course_block_prev_version
=
new_course
.
previous_version
...
@@ -1149,8 +1123,8 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1149,8 +1123,8 @@ class TestItemCrud(SplitModuleTest):
versionless_course_locator
=
new_course_locator
.
version_agnostic
()
versionless_course_locator
=
new_course_locator
.
version_agnostic
()
# positive simple case: no force, add chapter
# positive simple case: no force, add chapter
new_ele
=
modulestore
()
.
create_
item
(
new_ele
=
modulestore
()
.
create_
child
(
new_course
.
location
,
'chapter'
,
user
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 1'
},
fields
=
{
'display_name'
:
'chapter 1'
},
continue_version
=
True
continue_version
=
True
)
)
...
@@ -1167,40 +1141,40 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1167,40 +1141,40 @@ class TestItemCrud(SplitModuleTest):
# try to create existing item
# try to create existing item
with
self
.
assertRaises
(
DuplicateItemError
):
with
self
.
assertRaises
(
DuplicateItemError
):
_fail
=
modulestore
()
.
create_
item
(
_fail
=
modulestore
()
.
create_
child
(
new_course
.
location
,
'chapter'
,
user
,
user
,
new_course
.
location
,
'chapter'
,
block_id
=
new_ele
.
location
.
block_id
,
block_id
=
new_ele
.
location
.
block_id
,
fields
=
{
'display_name'
:
'chapter 2'
},
fields
=
{
'display_name'
:
'chapter 2'
},
continue_version
=
True
continue_version
=
True
)
)
# start a new transaction
# start a new transaction
new_ele
=
modulestore
()
.
create_
item
(
new_ele
=
modulestore
()
.
create_
child
(
new_course
.
location
,
'chapter'
,
user
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 2'
},
fields
=
{
'display_name'
:
'chapter 2'
},
continue_version
=
False
continue_version
=
False
)
)
transaction_guid
=
new_ele
.
location
.
version_guid
transaction_guid
=
new_ele
.
location
.
version_guid
# ensure force w/ continue gives exception
# ensure force w/ continue gives exception
with
self
.
assertRaises
(
VersionConflictError
):
with
self
.
assertRaises
(
VersionConflictError
):
_fail
=
modulestore
()
.
create_
item
(
_fail
=
modulestore
()
.
create_
child
(
new_course
.
location
,
'chapter'
,
user
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 2'
},
fields
=
{
'display_name'
:
'chapter 2'
},
force
=
True
,
continue_version
=
True
force
=
True
,
continue_version
=
True
)
)
# ensure trying to continue the old one gives exception
# ensure trying to continue the old one gives exception
with
self
.
assertRaises
(
VersionConflictError
):
with
self
.
assertRaises
(
VersionConflictError
):
_fail
=
modulestore
()
.
create_
item
(
_fail
=
modulestore
()
.
create_
child
(
new_course
.
location
,
'chapter'
,
user
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 3'
},
fields
=
{
'display_name'
:
'chapter 3'
},
continue_version
=
True
continue_version
=
True
)
)
# add new child to old parent in continued (leave off version_guid)
# add new child to old parent in continued (leave off version_guid)
course_module_locator
=
new_course
.
location
.
version_agnostic
()
course_module_locator
=
new_course
.
location
.
version_agnostic
()
new_ele
=
modulestore
()
.
create_
item
(
new_ele
=
modulestore
()
.
create_
child
(
course_module_locator
,
'chapter'
,
user
,
user
,
course_module_locator
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 4'
},
fields
=
{
'display_name'
:
'chapter 4'
},
continue_version
=
True
continue_version
=
True
)
)
...
@@ -1309,13 +1283,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1309,13 +1283,13 @@ class TestItemCrud(SplitModuleTest):
)
)
category
=
'problem'
category
=
'problem'
new_payload
=
"<problem>empty</problem>"
new_payload
=
"<problem>empty</problem>"
modulestore
()
.
create_
item
(
modulestore
()
.
create_
child
(
locator
,
category
,
'test_update_manifold'
,
'test_update_manifold'
,
locator
,
category
,
fields
=
{
'display_name'
:
'problem 1'
,
'data'
:
new_payload
},
fields
=
{
'display_name'
:
'problem 1'
,
'data'
:
new_payload
},
)
)
another_payload
=
"<problem>not empty</problem>"
another_payload
=
"<problem>not empty</problem>"
modulestore
()
.
create_
item
(
modulestore
()
.
create_
child
(
locator
,
category
,
'test_update_manifold'
,
'test_update_manifold'
,
locator
,
category
,
fields
=
{
'display_name'
:
'problem 2'
,
'data'
:
another_payload
},
fields
=
{
'display_name'
:
'problem 2'
,
'data'
:
another_payload
},
definition_locator
=
original
.
definition_locator
,
definition_locator
=
original
.
definition_locator
,
)
)
...
@@ -1382,7 +1356,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1382,7 +1356,7 @@ class TestItemCrud(SplitModuleTest):
"""
"""
Create a course we can delete
Create a course we can delete
"""
"""
course
=
modulestore
()
.
create_course
(
'nihilx'
,
'deletion'
,
'run'
,
'deleting_user'
)
course
=
modulestore
()
.
create_course
(
'nihilx'
,
'deletion'
,
'run'
,
'deleting_user'
,
BRANCH_NAME_DRAFT
)
root
=
course
.
location
.
version_agnostic
()
.
for_branch
(
BRANCH_NAME_DRAFT
)
root
=
course
.
location
.
version_agnostic
()
.
for_branch
(
BRANCH_NAME_DRAFT
)
for
_
in
range
(
4
):
for
_
in
range
(
4
):
self
.
create_subtree_for_deletion
(
root
,
[
'chapter'
,
'vertical'
,
'problem'
])
self
.
create_subtree_for_deletion
(
root
,
[
'chapter'
,
'vertical'
,
'problem'
])
...
@@ -1394,7 +1368,9 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1394,7 +1368,9 @@ class TestItemCrud(SplitModuleTest):
"""
"""
if
not
category_queue
:
if
not
category_queue
:
return
return
node
=
modulestore
()
.
create_item
(
parent
.
version_agnostic
(),
category_queue
[
0
],
'deleting_user'
)
node
=
modulestore
()
.
create_child
(
'deleting_user'
,
parent
.
version_agnostic
(),
category_queue
[
0
]
)
node_loc
=
node
.
location
.
map_into_course
(
parent
.
course_key
)
node_loc
=
node
.
location
.
map_into_course
(
parent
.
course_key
)
for
_
in
range
(
4
):
for
_
in
range
(
4
):
self
.
create_subtree_for_deletion
(
node_loc
,
category_queue
[
1
:])
self
.
create_subtree_for_deletion
(
node_loc
,
category_queue
[
1
:])
...
@@ -1409,7 +1385,9 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1409,7 +1385,9 @@ class TestCourseCreation(SplitModuleTest):
The simplest case but probing all expected results from it.
The simplest case but probing all expected results from it.
"""
"""
# Oddly getting differences of 200nsec
# Oddly getting differences of 200nsec
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_course'
,
'test_run'
,
'create_user'
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_course'
,
'test_run'
,
'create_user'
,
BRANCH_NAME_DRAFT
)
new_locator
=
new_course
.
location
new_locator
=
new_course
.
location
# check index entry
# check index entry
index_info
=
modulestore
()
.
get_course_index_info
(
new_locator
)
index_info
=
modulestore
()
.
get_course_index_info
(
new_locator
)
...
@@ -1437,7 +1415,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1437,7 +1415,7 @@ class TestCourseCreation(SplitModuleTest):
original_locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
original_locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
new_draft
=
modulestore
()
.
create_course
(
new_draft
=
modulestore
()
.
create_course
(
'best'
,
'leech'
,
'leech_run'
,
'leech_master'
,
'best'
,
'leech'
,
'leech_run'
,
'leech_master'
,
BRANCH_NAME_DRAFT
,
versions_dict
=
original_index
[
'versions'
])
versions_dict
=
original_index
[
'versions'
])
new_draft_locator
=
new_draft
.
location
new_draft_locator
=
new_draft
.
location
self
.
assertRegexpMatches
(
new_draft_locator
.
org
,
'best'
)
self
.
assertRegexpMatches
(
new_draft_locator
.
org
,
'best'
)
...
@@ -1455,8 +1433,8 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1455,8 +1433,8 @@ class TestCourseCreation(SplitModuleTest):
# changing this course will not change the original course
# changing this course will not change the original course
# using new_draft.location will insert the chapter under the course root
# using new_draft.location will insert the chapter under the course root
new_item
=
modulestore
()
.
create_
item
(
new_item
=
modulestore
()
.
create_
child
(
new_draft
.
location
,
'chapter'
,
'leech_mas
ter'
,
'leech_master'
,
new_draft
.
location
,
'chap
ter'
,
fields
=
{
'display_name'
:
'new chapter'
}
fields
=
{
'display_name'
:
'new chapter'
}
)
)
new_draft_locator
=
new_draft_locator
.
course_key
.
version_agnostic
()
new_draft_locator
=
new_draft_locator
.
course_key
.
version_agnostic
()
...
@@ -1493,7 +1471,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1493,7 +1471,7 @@ class TestCourseCreation(SplitModuleTest):
fields
[
'grading_policy'
][
'GRADE_CUTOFFS'
]
=
{
'A'
:
.
9
,
'B'
:
.
8
,
'C'
:
.
65
}
fields
[
'grading_policy'
][
'GRADE_CUTOFFS'
]
=
{
'A'
:
.
9
,
'B'
:
.
8
,
'C'
:
.
65
}
fields
[
'display_name'
]
=
'Derivative'
fields
[
'display_name'
]
=
'Derivative'
new_draft
=
modulestore
()
.
create_course
(
new_draft
=
modulestore
()
.
create_course
(
'counter'
,
'leech'
,
'leech_run'
,
'leech_master'
,
'counter'
,
'leech'
,
'leech_run'
,
'leech_master'
,
BRANCH_NAME_DRAFT
,
versions_dict
=
{
BRANCH_NAME_DRAFT
:
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
]},
versions_dict
=
{
BRANCH_NAME_DRAFT
:
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
]},
fields
=
fields
fields
=
fields
)
)
...
@@ -1540,7 +1518,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1540,7 +1518,7 @@ class TestCourseCreation(SplitModuleTest):
"""
"""
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
new_course
=
modulestore
()
.
create_course
(
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
'test_run'
,
user
,
'test_org'
,
'test_transaction'
,
'test_run'
,
user
,
BRANCH_NAME_DRAFT
,
root_block_id
=
'top'
,
root_category
=
'chapter'
root_block_id
=
'top'
,
root_category
=
'chapter'
)
)
self
.
assertEqual
(
new_course
.
location
.
block_id
,
'top'
)
self
.
assertEqual
(
new_course
.
location
.
block_id
,
'top'
)
...
@@ -1559,10 +1537,12 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1559,10 +1537,12 @@ class TestCourseCreation(SplitModuleTest):
Test create_course rejects duplicate id
Test create_course rejects duplicate id
"""
"""
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
courses
=
modulestore
()
.
get_courses
()
courses
=
modulestore
()
.
get_courses
(
BRANCH_NAME_DRAFT
)
with
self
.
assertRaises
(
DuplicateCourseError
):
with
self
.
assertRaises
(
DuplicateCourseError
):
dupe_course_key
=
courses
[
0
]
.
location
.
course_key
dupe_course_key
=
courses
[
0
]
.
location
.
course_key
modulestore
()
.
create_course
(
dupe_course_key
.
org
,
dupe_course_key
.
course
,
dupe_course_key
.
run
,
user
)
modulestore
()
.
create_course
(
dupe_course_key
.
org
,
dupe_course_key
.
course
,
dupe_course_key
.
run
,
user
,
BRANCH_NAME_DRAFT
)
class
TestInheritance
(
SplitModuleTest
):
class
TestInheritance
(
SplitModuleTest
):
...
@@ -1609,14 +1589,14 @@ class TestPublish(SplitModuleTest):
...
@@ -1609,14 +1589,14 @@ class TestPublish(SplitModuleTest):
chapter1
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter1'
)
chapter1
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter1'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
,
chapter3
])
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
,
chapter3
])
expected
=
[
head
.
block_id
,
chapter1
.
block_id
]
expected
=
[
head
.
block_id
,
chapter1
.
block_id
]
self
.
_check_course
(
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
chapter2
.
block_id
,
chapter3
.
block_id
,
"problem1"
,
"problem3_2"
]
source_course
,
dest_course
,
expected
,
[
chapter2
.
block_id
,
chapter3
.
block_id
,
"problem1"
,
"problem3_2"
]
)
)
# add a child under chapter1
# add a child under chapter1
new_module
=
modulestore
()
.
create_
item
(
new_module
=
modulestore
()
.
create_
child
(
chapter1
,
"sequential"
,
self
.
user_id
,
self
.
user_id
,
chapter1
,
"sequential"
,
fields
=
{
'display_name'
:
'new sequential'
},
fields
=
{
'display_name'
:
'new sequential'
},
)
)
# remove chapter1 from expected b/c its pub'd version != the source anymore since source changed
# remove chapter1 from expected b/c its pub'd version != the source anymore since source changed
...
@@ -1625,7 +1605,7 @@ class TestPublish(SplitModuleTest):
...
@@ -1625,7 +1605,7 @@ class TestPublish(SplitModuleTest):
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
# publish it
# publish it
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
dest_course
,
[
new_module
.
location
],
None
)
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
dest_course
,
[
new_module
.
location
],
None
)
expected
.
append
(
new_module
.
location
.
block_id
)
expected
.
append
(
new_module
.
location
.
block_id
)
# check that it is in the published course and that its parent is the chapter
# check that it is in the published course and that its parent is the chapter
pub_module
=
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
pub_module
=
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
...
@@ -1634,10 +1614,10 @@ class TestPublish(SplitModuleTest):
...
@@ -1634,10 +1614,10 @@ class TestPublish(SplitModuleTest):
)
)
# ensure intentionally orphaned blocks work (e.g., course_info)
# ensure intentionally orphaned blocks work (e.g., course_info)
new_module
=
modulestore
()
.
create_item
(
new_module
=
modulestore
()
.
create_item
(
s
ource_course
,
"course_info"
,
self
.
user_id
,
block_id
=
"handouts"
s
elf
.
user_id
,
source_course
,
"course_info"
,
block_id
=
"handouts"
)
)
# publish it
# publish it
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
dest_course
,
[
new_module
.
location
],
None
)
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
dest_course
,
[
new_module
.
location
],
None
)
expected
.
append
(
new_module
.
location
.
block_id
)
expected
.
append
(
new_module
.
location
.
block_id
)
# check that it is in the published course (no error means it worked)
# check that it is in the published course (no error means it worked)
pub_module
=
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
pub_module
=
modulestore
()
.
get_item
(
new_module
.
location
.
map_into_course
(
dest_course
))
...
@@ -1656,15 +1636,15 @@ class TestPublish(SplitModuleTest):
...
@@ -1656,15 +1636,15 @@ class TestPublish(SplitModuleTest):
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
# publishing into a new branch w/o publishing the root
# publishing into a new branch w/o publishing the root
destination_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run'
,
branch
=
BRANCH_NAME_PUBLISHED
)
destination_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run'
,
branch
=
BRANCH_NAME_PUBLISHED
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
# publishing a subdag w/o the parent already in course
# publishing a subdag w/o the parent already in course
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
head
],
[
chapter3
])
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
destination_course
,
[
head
],
[
chapter3
])
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
problem1
],
[])
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
destination_course
,
[
problem1
],
[])
def
test_move_delete
(
self
):
def
test_move_delete
(
self
):
"""
"""
...
@@ -1675,7 +1655,7 @@ class TestPublish(SplitModuleTest):
...
@@ -1675,7 +1655,7 @@ class TestPublish(SplitModuleTest):
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
])
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
])
expected
=
[
"head12345"
,
"chapter1"
,
"chapter3"
,
"problem1"
,
"problem3_2"
]
expected
=
[
"head12345"
,
"chapter1"
,
"chapter3"
,
"problem1"
,
"problem3_2"
]
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
"chapter2"
])
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
"chapter2"
])
# now move problem1 and delete problem3_2
# now move problem1 and delete problem3_2
...
@@ -1684,7 +1664,7 @@ class TestPublish(SplitModuleTest):
...
@@ -1684,7 +1664,7 @@ class TestPublish(SplitModuleTest):
chapter1
.
children
.
append
(
problem1
)
chapter1
.
children
.
append
(
problem1
)
chapter3
.
children
.
remove
(
problem1
.
map_into_course
(
chapter3
.
location
.
course_key
))
chapter3
.
children
.
remove
(
problem1
.
map_into_course
(
chapter3
.
location
.
course_key
))
modulestore
()
.
delete_item
(
source_course
.
make_usage_key
(
"problem"
,
"problem3_2"
),
self
.
user_id
)
modulestore
()
.
delete_item
(
source_course
.
make_usage_key
(
"problem"
,
"problem3_2"
),
self
.
user_id
)
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
])
modulestore
()
.
copy
(
self
.
user_id
,
source_course
,
dest_course
,
[
head
],
[
chapter2
])
expected
=
[
"head12345"
,
"chapter1"
,
"chapter3"
,
"problem1"
]
expected
=
[
"head12345"
,
"chapter1"
,
"chapter3"
,
"problem1"
]
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
"chapter2"
,
"problem3_2"
])
self
.
_check_course
(
source_course
,
dest_course
,
expected
,
[
"chapter2"
,
"problem3_2"
])
...
@@ -1776,7 +1756,7 @@ def modulestore():
...
@@ -1776,7 +1756,7 @@ def modulestore():
**
options
**
options
)
)
SplitModuleTest
.
bootstrapDB
()
SplitModuleTest
.
bootstrapDB
(
SplitModuleTest
.
modulestore
)
return
SplitModuleTest
.
modulestore
return
SplitModuleTest
.
modulestore
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py
View file @
d53a6669
...
@@ -86,9 +86,14 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
...
@@ -86,9 +86,14 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
existing draft for both the new item and the parent
existing draft for both the new item and the parent
"""
"""
location
=
self
.
old_course_key
.
make_usage_key
(
category
,
name
)
location
=
self
.
old_course_key
.
make_usage_key
(
category
,
name
)
self
.
draft_mongo
.
create_item
(
self
.
draft_mongo
.
create_and_save_xmodule
(
self
.
user_id
,
location
,
self
.
user_id
,
definition_data
=
data
,
metadata
=
metadata
,
runtime
=
self
.
runtime
location
.
course_key
,
location
.
block_type
,
block_id
=
location
.
block_id
,
definition_data
=
data
,
metadata
=
metadata
,
runtime
=
self
.
runtime
)
)
if
not
draft
:
if
not
draft
:
self
.
draft_mongo
.
publish
(
location
,
self
.
user_id
)
self
.
draft_mongo
.
publish
(
location
,
self
.
user_id
)
...
@@ -105,16 +110,28 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
...
@@ -105,16 +110,28 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
self
.
draft_mongo
.
update_item
(
parent
,
self
.
user_id
)
self
.
draft_mongo
.
update_item
(
parent
,
self
.
user_id
)
if
not
draft
:
if
not
draft
:
self
.
draft_mongo
.
publish
(
parent_location
,
self
.
user_id
)
self
.
draft_mongo
.
publish
(
parent_location
,
self
.
user_id
)
# create pointer for split
# create child for split
course_or_parent_locator
=
BlockUsageLocator
(
if
split
:
course_key
=
self
.
split_course_key
,
self
.
split_mongo
.
create_child
(
block_type
=
parent_category
,
self
.
user_id
,
block_id
=
parent_name
BlockUsageLocator
(
)
course_key
=
self
.
split_course_key
,
block_type
=
parent_category
,
block_id
=
parent_name
),
category
,
block_id
=
name
,
fields
=
fields
)
else
:
else
:
course_or_parent_locator
=
self
.
split_course_key
if
split
:
if
split
:
self
.
split_mongo
.
create_item
(
self
.
split_mongo
.
create_item
(
course_or_parent_locator
,
category
,
self
.
user_id
,
block_id
=
name
,
fields
=
fields
)
self
.
user_id
,
self
.
split_course_key
,
category
,
block_id
=
name
,
fields
=
fields
)
def
_create_course
(
self
,
split
=
True
):
def
_create_course
(
self
,
split
=
True
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_xml_importer.py
View file @
d53a6669
...
@@ -7,6 +7,7 @@ from xblock.fields import String, Scope, ScopeIds
...
@@ -7,6 +7,7 @@ from xblock.fields import String, Scope, ScopeIds
from
xblock.runtime
import
Runtime
,
KvsFieldData
,
DictKeyValueStore
from
xblock.runtime
import
Runtime
,
KvsFieldData
,
DictKeyValueStore
from
xmodule.x_module
import
XModuleMixin
from
xmodule.x_module
import
XModuleMixin
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.modulestore.xml_importer
import
_import_module_and_update_references
from
xmodule.modulestore.xml_importer
import
_import_module_and_update_references
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
...
@@ -39,7 +40,7 @@ class ModuleStoreNoSettings(unittest.TestCase):
...
@@ -39,7 +40,7 @@ class ModuleStoreNoSettings(unittest.TestCase):
'collection'
:
COLLECTION
,
'collection'
:
COLLECTION
,
}
}
MODULESTORE
=
{
MODULESTORE
=
{
'ENGINE'
:
'xmodule.modulestore.mongo.MongoModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mongo.
Draft
MongoModuleStore'
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'OPTIONS'
:
modulestore_options
'OPTIONS'
:
modulestore_options
}
}
...
@@ -85,6 +86,7 @@ def modulestore():
...
@@ -85,6 +86,7 @@ def modulestore():
ModuleStoreNoSettings
.
modulestore
=
class_
(
ModuleStoreNoSettings
.
modulestore
=
class_
(
None
,
# contentstore
None
,
# contentstore
ModuleStoreNoSettings
.
MODULESTORE
[
'DOC_STORE_CONFIG'
],
ModuleStoreNoSettings
.
MODULESTORE
[
'DOC_STORE_CONFIG'
],
branch_setting_func
=
lambda
:
ModuleStoreEnum
.
Branch
.
draft_preferred
,
**
options
**
options
)
)
...
...
common/lib/xmodule/xmodule/split_test_module.py
View file @
d53a6669
...
@@ -569,16 +569,18 @@ class SplitTestDescriptor(SplitTestFields, SequenceDescriptor, StudioEditableDes
...
@@ -569,16 +569,18 @@ class SplitTestDescriptor(SplitTestFields, SequenceDescriptor, StudioEditableDes
This appends the new vertical to the end of children, and updates group_id_to_child.
This appends the new vertical to the end of children, and updates group_id_to_child.
A mutable modulestore is needed to call this method (will need to update after mixed
A mutable modulestore is needed to call this method (will need to update after mixed
modulestore work, currently relies on mongo's create_
and_save_xmodule
method).
modulestore work, currently relies on mongo's create_
item
method).
"""
"""
assert
hasattr
(
self
.
system
,
'modulestore'
)
and
hasattr
(
self
.
system
.
modulestore
,
'create_
and_save_xmodule
'
),
\
assert
hasattr
(
self
.
system
,
'modulestore'
)
and
hasattr
(
self
.
system
.
modulestore
,
'create_
item
'
),
\
"editor_saved should only be called when a mutable modulestore is available"
"editor_saved should only be called when a mutable modulestore is available"
modulestore
=
self
.
system
.
modulestore
modulestore
=
self
.
system
.
modulestore
dest_usage_key
=
self
.
location
.
replace
(
category
=
"vertical"
,
name
=
uuid4
()
.
hex
)
dest_usage_key
=
self
.
location
.
replace
(
category
=
"vertical"
,
name
=
uuid4
()
.
hex
)
metadata
=
{
'display_name'
:
group
.
name
}
metadata
=
{
'display_name'
:
group
.
name
}
modulestore
.
create_and_save_xmodule
(
modulestore
.
create_item
(
dest_usage_key
,
user_id
,
user_id
,
self
.
location
.
course_key
,
dest_usage_key
.
block_type
,
block_id
=
dest_usage_key
.
block_id
,
definition_data
=
None
,
definition_data
=
None
,
metadata
=
metadata
,
metadata
=
metadata
,
runtime
=
self
.
system
,
runtime
=
self
.
system
,
...
...
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