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
562f0e31
Commit
562f0e31
authored
Jul 31, 2014
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add bulk operations to split modulestore
parent
f731d5fe
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
564 additions
and
238 deletions
+564
-238
common/lib/xmodule/xmodule/modulestore/exceptions.py
+5
-9
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+6
-5
common/lib/xmodule/xmodule/modulestore/split_migrator.py
+3
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
+24
-8
common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py
+7
-0
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
+18
-4
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+339
-166
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
+12
-2
common/lib/xmodule/xmodule/modulestore/tests/factories.py
+9
-1
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+12
-9
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
+124
-29
common/lib/xmodule/xmodule/tests/__init__.py
+2
-2
pavelib/tests.py
+3
-0
No files found.
common/lib/xmodule/xmodule/modulestore/exceptions.py
View file @
562f0e31
...
@@ -54,20 +54,16 @@ class DuplicateItemError(Exception):
...
@@ -54,20 +54,16 @@ class DuplicateItemError(Exception):
self
,
Exception
.
__str__
(
self
,
*
args
,
**
kwargs
)
self
,
Exception
.
__str__
(
self
,
*
args
,
**
kwargs
)
)
)
class
VersionConflictError
(
Exception
):
class
VersionConflictError
(
Exception
):
"""
"""
The caller asked for either draft or published head and gave a version which conflicted with it.
The caller asked for either draft or published head and gave a version which conflicted with it.
"""
"""
def
__init__
(
self
,
requestedLocation
,
currentHeadVersionGuid
):
def
__init__
(
self
,
requestedLocation
,
currentHeadVersionGuid
):
super
(
VersionConflictError
,
self
)
.
__init__
()
super
(
VersionConflictError
,
self
)
.
__init__
(
u'Requested {}, but current head is {}'
.
format
(
self
.
requestedLocation
=
requestedLocation
requestedLocation
,
self
.
currentHeadVersionGuid
=
currentHeadVersionGuid
currentHeadVersionGuid
))
def
__str__
(
self
,
*
args
,
**
kwargs
):
"""
Print requested and current head info
"""
return
u'Requested {} but {} is current head'
.
format
(
self
.
requestedLocation
,
self
.
currentHeadVersionGuid
)
class
DuplicateCourseError
(
Exception
):
class
DuplicateCourseError
(
Exception
):
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
562f0e31
...
@@ -17,6 +17,7 @@ import sys
...
@@ -17,6 +17,7 @@ import sys
import
logging
import
logging
import
copy
import
copy
import
re
import
re
import
threading
from
uuid
import
uuid4
from
uuid
import
uuid4
from
bson.son
import
SON
from
bson.son
import
SON
...
@@ -439,7 +440,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
...
@@ -439,7 +440,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
"""
"""
Prevent updating the meta-data inheritance cache for the given course
Prevent updating the meta-data inheritance cache for the given course
"""
"""
if
not
hasattr
(
self
.
ignore_write_events_on_courses
.
courses
):
if
not
hasattr
(
self
.
ignore_write_events_on_courses
,
'courses'
):
self
.
ignore_write_events_on_courses
.
courses
=
set
()
self
.
ignore_write_events_on_courses
.
courses
=
set
()
self
.
ignore_write_events_on_courses
.
courses
.
add
(
course_id
)
self
.
ignore_write_events_on_courses
.
courses
.
add
(
course_id
)
...
@@ -449,18 +450,18 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
...
@@ -449,18 +450,18 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
Restart updating the meta-data inheritance cache for the given course.
Restart updating the meta-data inheritance cache for the given course.
Refresh the meta-data inheritance cache now since it was temporarily disabled.
Refresh the meta-data inheritance cache now since it was temporarily disabled.
"""
"""
if
not
hasattr
(
self
.
ignore_write_events_on_courses
.
courses
):
if
not
hasattr
(
self
.
ignore_write_events_on_courses
,
'courses'
):
return
return
if
course_id
in
self
.
ignore_write_events_on_courses
:
if
course_id
in
self
.
ignore_write_events_on_courses
.
courses
:
self
.
ignore_write_events_on_courses
.
remove
(
course_id
)
self
.
ignore_write_events_on_courses
.
courses
.
remove
(
course_id
)
self
.
refresh_cached_metadata_inheritance_tree
(
course_id
)
self
.
refresh_cached_metadata_inheritance_tree
(
course_id
)
def
_is_bulk_write_in_progress
(
self
,
course_id
):
def
_is_bulk_write_in_progress
(
self
,
course_id
):
"""
"""
Returns whether a bulk write operation is in progress for the given course.
Returns whether a bulk write operation is in progress for the given course.
"""
"""
if
not
hasattr
(
self
.
ignore_write_events_on_courses
.
courses
):
if
not
hasattr
(
self
.
ignore_write_events_on_courses
,
'courses'
):
return
False
return
False
course_id
=
course_id
.
for_branch
(
None
)
course_id
=
course_id
.
for_branch
(
None
)
...
...
common/lib/xmodule/xmodule/modulestore/split_migrator.py
View file @
562f0e31
...
@@ -55,6 +55,7 @@ class SplitMigrator(object):
...
@@ -55,6 +55,7 @@ class SplitMigrator(object):
new_run
=
source_course_key
.
run
new_run
=
source_course_key
.
run
new_course_key
=
CourseLocator
(
new_org
,
new_course
,
new_run
,
branch
=
ModuleStoreEnum
.
BranchName
.
published
)
new_course_key
=
CourseLocator
(
new_org
,
new_course
,
new_run
,
branch
=
ModuleStoreEnum
.
BranchName
.
published
)
with
self
.
split_modulestore
.
bulk_write_operations
(
new_course_key
):
new_fields
=
self
.
_get_fields_translate_references
(
original_course
,
new_course_key
,
None
)
new_fields
=
self
.
_get_fields_translate_references
(
original_course
,
new_course_key
,
None
)
if
fields
:
if
fields
:
new_fields
.
update
(
fields
)
new_fields
.
update
(
fields
)
...
@@ -70,8 +71,8 @@ class SplitMigrator(object):
...
@@ -70,8 +71,8 @@ class SplitMigrator(object):
self
.
_copy_published_modules_to_course
(
self
.
_copy_published_modules_to_course
(
new_course
,
original_course
.
location
,
source_course_key
,
user_id
,
**
kwargs
new_course
,
original_course
.
location
,
source_course_key
,
user_id
,
**
kwargs
)
)
# create a new version for the drafts
# create a new version for the drafts
with
self
.
split_modulestore
.
bulk_write_operations
(
new_course
.
id
):
self
.
_add_draft_modules_to_course
(
new_course
.
location
,
source_course_key
,
user_id
,
**
kwargs
)
self
.
_add_draft_modules_to_course
(
new_course
.
location
,
source_course_key
,
user_id
,
**
kwargs
)
return
new_course
.
id
return
new_course
.
id
...
@@ -101,7 +102,6 @@ class SplitMigrator(object):
...
@@ -101,7 +102,6 @@ class SplitMigrator(object):
fields
=
self
.
_get_fields_translate_references
(
fields
=
self
.
_get_fields_translate_references
(
module
,
course_version_locator
,
new_course
.
location
.
block_id
module
,
course_version_locator
,
new_course
.
location
.
block_id
),
),
continue_version
=
True
,
skip_auto_publish
=
True
,
skip_auto_publish
=
True
,
**
kwargs
**
kwargs
)
)
...
@@ -109,7 +109,7 @@ class SplitMigrator(object):
...
@@ -109,7 +109,7 @@ class SplitMigrator(object):
index_info
=
self
.
split_modulestore
.
get_course_index_info
(
course_version_locator
)
index_info
=
self
.
split_modulestore
.
get_course_index_info
(
course_version_locator
)
versions
=
index_info
[
'versions'
]
versions
=
index_info
[
'versions'
]
versions
[
ModuleStoreEnum
.
BranchName
.
draft
]
=
versions
[
ModuleStoreEnum
.
BranchName
.
published
]
versions
[
ModuleStoreEnum
.
BranchName
.
draft
]
=
versions
[
ModuleStoreEnum
.
BranchName
.
published
]
self
.
split_modulestore
.
update_course_index
(
index_info
)
self
.
split_modulestore
.
update_course_index
(
course_version_locator
,
index_info
)
# clean up orphans in published version: in old mongo, parents pointed to the union of their published and draft
# clean up orphans in published version: in old mongo, parents pointed to the union of their published and draft
# children which meant some pointers were to non-existent locations in 'direct'
# children which meant some pointers were to non-existent locations in 'direct'
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
View file @
562f0e31
...
@@ -2,7 +2,7 @@ import sys
...
@@ -2,7 +2,7 @@ import sys
import
logging
import
logging
from
xblock.runtime
import
KvsFieldData
from
xblock.runtime
import
KvsFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
opaque_keys.edx.locator
import
BlockUsageLocator
,
LocalId
,
CourseLocator
from
opaque_keys.edx.locator
import
BlockUsageLocator
,
LocalId
,
CourseLocator
,
DefinitionLocator
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.errortracker
import
exc_info_to_str
from
xmodule.errortracker
import
exc_info_to_str
...
@@ -10,6 +10,7 @@ from xmodule.modulestore.split_mongo import encode_key_for_mongo
...
@@ -10,6 +10,7 @@ from xmodule.modulestore.split_mongo import encode_key_for_mongo
from
..exceptions
import
ItemNotFoundError
from
..exceptions
import
ItemNotFoundError
from
.split_mongo_kvs
import
SplitMongoKVS
from
.split_mongo_kvs
import
SplitMongoKVS
from
fs.osfs
import
OSFS
from
fs.osfs
import
OSFS
from
.definition_lazy_loader
import
DefinitionLazyLoader
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -120,9 +121,24 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -120,9 +121,24 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
self
.
course_entry
[
'org'
]
=
course_entry_override
[
'org'
]
self
.
course_entry
[
'org'
]
=
course_entry_override
[
'org'
]
self
.
course_entry
[
'course'
]
=
course_entry_override
[
'course'
]
self
.
course_entry
[
'course'
]
=
course_entry_override
[
'course'
]
self
.
course_entry
[
'run'
]
=
course_entry_override
[
'run'
]
self
.
course_entry
[
'run'
]
=
course_entry_override
[
'run'
]
# most likely a lazy loader or the id directly
definition
=
json_data
.
get
(
'definition'
,
{})
definition_id
=
json_data
.
get
(
'definition'
)
definition_id
=
self
.
modulestore
.
definition_locator
(
definition
)
block_type
=
json_data
[
'category'
]
if
definition_id
is
not
None
and
not
json_data
.
get
(
'definition_loaded'
,
False
):
definition_loader
=
DefinitionLazyLoader
(
self
.
modulestore
,
block_type
,
definition_id
,
lambda
fields
:
self
.
modulestore
.
convert_references_to_keys
(
course_key
,
self
.
load_block_type
(
block_type
),
fields
,
self
.
course_entry
[
'structure'
][
'blocks'
],
)
)
else
:
definition_loader
=
None
# If no definition id is provide, generate an in-memory id
if
definition_id
is
None
:
definition_id
=
LocalId
()
# If no usage id is provided, generate an in-memory id
# If no usage id is provided, generate an in-memory id
if
block_id
is
None
:
if
block_id
is
None
:
...
@@ -130,7 +146,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -130,7 +146,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
block_locator
=
BlockUsageLocator
(
block_locator
=
BlockUsageLocator
(
course_key
,
course_key
,
block_type
=
json_data
.
get
(
'category'
)
,
block_type
=
block_type
,
block_id
=
block_id
,
block_id
=
block_id
,
)
)
...
@@ -138,7 +154,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -138,7 +154,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
block_locator
.
course_key
,
class_
,
json_data
.
get
(
'fields'
,
{}),
self
.
course_entry
[
'structure'
][
'blocks'
],
block_locator
.
course_key
,
class_
,
json_data
.
get
(
'fields'
,
{}),
self
.
course_entry
[
'structure'
][
'blocks'
],
)
)
kvs
=
SplitMongoKVS
(
kvs
=
SplitMongoKVS
(
definition
,
definition
_loader
,
converted_fields
,
converted_fields
,
json_data
.
get
(
'_inherited_settings'
),
json_data
.
get
(
'_inherited_settings'
),
**
kwargs
**
kwargs
...
@@ -148,7 +164,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -148,7 +164,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
try
:
try
:
module
=
self
.
construct_xblock_from_class
(
module
=
self
.
construct_xblock_from_class
(
class_
,
class_
,
ScopeIds
(
None
,
json_data
.
get
(
'category'
)
,
definition_id
,
block_locator
),
ScopeIds
(
None
,
block_type
,
definition_id
,
block_locator
),
field_data
,
field_data
,
)
)
except
Exception
:
except
Exception
:
...
@@ -174,7 +190,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -174,7 +190,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
module
.
previous_version
=
edit_info
.
get
(
'previous_version'
)
module
.
previous_version
=
edit_info
.
get
(
'previous_version'
)
module
.
update_version
=
edit_info
.
get
(
'update_version'
)
module
.
update_version
=
edit_info
.
get
(
'update_version'
)
module
.
source_version
=
edit_info
.
get
(
'source_version'
,
None
)
module
.
source_version
=
edit_info
.
get
(
'source_version'
,
None
)
module
.
definition_locator
=
definition_id
module
.
definition_locator
=
DefinitionLocator
(
block_type
,
definition_id
)
# decache any pending field settings
# decache any pending field settings
module
.
save
()
module
.
save
()
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py
View file @
562f0e31
from
opaque_keys.edx.locator
import
DefinitionLocator
from
opaque_keys.edx.locator
import
DefinitionLocator
from
bson
import
SON
class
DefinitionLazyLoader
(
object
):
class
DefinitionLazyLoader
(
object
):
...
@@ -24,3 +25,9 @@ class DefinitionLazyLoader(object):
...
@@ -24,3 +25,9 @@ class DefinitionLazyLoader(object):
loader pointer with the result so as not to fetch more than once
loader pointer with the result so as not to fetch more than once
"""
"""
return
self
.
modulestore
.
db_connection
.
get_definition
(
self
.
definition_locator
.
definition_id
)
return
self
.
modulestore
.
db_connection
.
get_definition
(
self
.
definition_locator
.
definition_id
)
def
as_son
(
self
):
return
SON
((
(
'category'
,
self
.
definition_locator
.
block_type
),
(
'definition'
,
self
.
definition_locator
.
definition_id
)
))
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
View file @
562f0e31
...
@@ -76,6 +76,12 @@ class MongoConnection(object):
...
@@ -76,6 +76,12 @@ class MongoConnection(object):
"""
"""
self
.
structures
.
update
({
'_id'
:
structure
[
'_id'
]},
structure
)
self
.
structures
.
update
({
'_id'
:
structure
[
'_id'
]},
structure
)
def
upsert_structure
(
self
,
structure
):
"""
Update the db record for structure, creating that record if it doesn't already exist
"""
self
.
structures
.
update
({
'_id'
:
structure
[
'_id'
]},
structure
,
upsert
=
True
)
def
get_course_index
(
self
,
key
,
ignore_case
=
False
):
def
get_course_index
(
self
,
key
,
ignore_case
=
False
):
"""
"""
Get the course_index from the persistence mechanism whose id is the given key
Get the course_index from the persistence mechanism whose id is the given key
...
@@ -101,13 +107,21 @@ class MongoConnection(object):
...
@@ -101,13 +107,21 @@ class MongoConnection(object):
"""
"""
self
.
course_index
.
insert
(
course_index
)
self
.
course_index
.
insert
(
course_index
)
def
update_course_index
(
self
,
course_index
):
def
update_course_index
(
self
,
course_index
,
from_index
=
None
):
"""
"""
Update the db record for course_index
Update the db record for course_index.
Arguments:
from_index: If set, only update an index if it matches the one specified in `from_index`.
"""
"""
self
.
course_index
.
update
(
self
.
course_index
.
update
(
son
.
SON
([(
'org'
,
course_index
[
'org'
]),
(
'course'
,
course_index
[
'course'
]),
(
'run'
,
course_index
[
'run'
])]),
from_index
or
son
.
SON
([
course_index
(
'org'
,
course_index
[
'org'
]),
(
'course'
,
course_index
[
'course'
]),
(
'run'
,
course_index
[
'run'
])
]),
course_index
,
upsert
=
False
,
)
)
def
delete_course_index
(
self
,
course_index
):
def
delete_course_index
(
self
,
course_index
):
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
562f0e31
...
@@ -57,6 +57,7 @@ from path import path
...
@@ -57,6 +57,7 @@ from path import path
import
copy
import
copy
from
pytz
import
UTC
from
pytz
import
UTC
from
bson.objectid
import
ObjectId
from
bson.objectid
import
ObjectId
from
pymongo.errors
import
DuplicateKeyError
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
Reference
,
ReferenceList
,
ReferenceValueDict
from
xblock.fields
import
Scope
,
Reference
,
ReferenceList
,
ReferenceValueDict
...
@@ -72,7 +73,6 @@ from xmodule.modulestore import (
...
@@ -72,7 +73,6 @@ from xmodule.modulestore import (
)
)
from
..exceptions
import
ItemNotFoundError
from
..exceptions
import
ItemNotFoundError
from
.definition_lazy_loader
import
DefinitionLazyLoader
from
.caching_descriptor_system
import
CachingDescriptorSystem
from
.caching_descriptor_system
import
CachingDescriptorSystem
from
xmodule.modulestore.split_mongo.mongo_connection
import
MongoConnection
from
xmodule.modulestore.split_mongo.mongo_connection
import
MongoConnection
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
...
@@ -82,6 +82,7 @@ from types import NoneType
...
@@ -82,6 +82,7 @@ from types import NoneType
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
#==============================================================================
#==============================================================================
#
#
# Known issue:
# Known issue:
...
@@ -104,7 +105,220 @@ log = logging.getLogger(__name__)
...
@@ -104,7 +105,220 @@ log = logging.getLogger(__name__)
EXCLUDE_ALL
=
'*'
EXCLUDE_ALL
=
'*'
class
SplitMongoModuleStore
(
ModuleStoreWriteBase
):
class
BulkWriteRecord
(
object
):
def
__init__
(
self
):
self
.
active_count
=
0
self
.
dirty_branches
=
set
()
self
.
initial_index
=
None
self
.
index
=
None
self
.
_structures
=
{}
def
set_structure
(
self
,
branch
,
structure
):
self
.
index
[
'versions'
][
branch
]
=
structure
[
'_id'
]
self
.
_structures
[
branch
]
=
structure
self
.
dirty_branches
.
add
(
branch
)
def
get_structure
(
self
,
branch
):
return
self
.
_structures
.
get
(
branch
)
def
__repr__
(
self
):
return
u"BulkWriteRecord<{}, {}, {}, {}, {}>"
.
format
(
self
.
active_count
,
self
.
dirty_branches
,
self
.
initial_index
,
self
.
index
,
self
.
_structures
,
)
class
BulkWriteMixin
(
object
):
"""
This implements the :meth:`bulk_write_operations` modulestore semantics for the :class:`SplitMongoModuleStore`.
In particular, it implements :meth:`_begin_bulk_write_operation` and
:meth:`_end_bulk_write_operation` to provide the external interface, and then exposes a set of methods
for interacting with course_indexes and structures that can be used by :class:`SplitMongoModuleStore`.
Internally, this mixin records the set of all active bulk operations (keyed on the active course),
and only writes those values to ``self.mongo_connection`` when :meth:`_end_bulk_write_operation` is called.
If a bulk write operation isn't active, then the changes are immediately written to the underlying
mongo_connection.
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
BulkWriteMixin
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
_active_bulk_writes
=
threading
.
local
()
def
_get_bulk_write_record
(
self
,
course_key
,
ignore_case
=
False
):
"""
Return the :class:`.BulkWriteRecord` for this course.
"""
if
course_key
is
None
:
return
BulkWriteRecord
()
if
not
isinstance
(
course_key
,
CourseLocator
):
raise
TypeError
(
u'{!r} is not a CourseLocator'
.
format
(
course_key
))
if
not
hasattr
(
self
.
_active_bulk_writes
,
'records'
):
self
.
_active_bulk_writes
.
records
=
defaultdict
(
BulkWriteRecord
)
# Retrieve the bulk record based on matching org/course/run (possibly ignoring case)
if
course_key
.
org
and
course_key
.
course
and
course_key
.
run
:
if
ignore_case
:
for
key
,
record
in
self
.
_active_bulk_writes
.
records
.
iteritems
():
if
(
key
.
org
.
lower
()
==
course_key
.
org
.
lower
()
and
key
.
course
.
lower
()
==
course_key
.
course
.
lower
()
and
key
.
run
.
lower
()
==
course_key
.
run
.
lower
()
):
return
record
# If nothing matches case-insesitively, fall through to creating a new record with the passed in case
return
self
.
_active_bulk_writes
.
records
[
course_key
.
replace
(
branch
=
None
,
version_guid
=
None
)]
else
:
# If nothing org/course/run aren't set, use a bulk record that is identified just by the version_guid
return
self
.
_active_bulk_writes
.
records
[
course_key
.
replace
(
org
=
None
,
course
=
None
,
run
=
None
,
branch
=
None
)]
def
_clear_bulk_write_record
(
self
,
course_key
):
if
not
isinstance
(
course_key
,
CourseLocator
):
raise
TypeError
(
'{!r} is not a CourseLocator'
.
format
(
course_key
))
if
not
hasattr
(
self
.
_active_bulk_writes
,
'records'
):
return
if
course_key
.
org
and
course_key
.
course
and
course_key
.
run
:
del
self
.
_active_bulk_writes
.
records
[
course_key
.
replace
(
branch
=
None
,
version_guid
=
None
)]
else
:
del
self
.
_active_bulk_writes
.
records
[
course_key
.
replace
(
org
=
None
,
course
=
None
,
run
=
None
,
branch
=
None
)]
def
_begin_bulk_write_operation
(
self
,
course_key
):
"""
Begin a bulk write operation on course_key.
"""
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
bulk_write_record
.
active_count
+=
1
if
bulk_write_record
.
active_count
>
1
:
return
bulk_write_record
.
initial_index
=
bulk_write_record
.
index
=
self
.
db_connection
.
get_course_index
(
course_key
)
def
_end_bulk_write_operation
(
self
,
course_key
):
"""
End the active bulk write operation on course_key.
"""
# If no bulk write is active, return
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
if
bulk_write_record
.
active_count
==
0
:
return
bulk_write_record
.
active_count
-=
1
# If more than one nested bulk write is active, decrement and continue
if
bulk_write_record
.
active_count
>
0
:
return
# If this is the last active bulk write, and the content is dirty,
# then mark it as inactive, and update the database
if
bulk_write_record
.
dirty_branches
:
for
branch
in
bulk_write_record
.
dirty_branches
:
try
:
self
.
db_connection
.
insert_structure
(
bulk_write_record
.
get_structure
(
branch
))
except
DuplicateKeyError
:
pass
# The structure already exists, so we don't have to write it out again
if
bulk_write_record
.
index
is
not
None
and
bulk_write_record
.
index
!=
bulk_write_record
.
initial_index
:
if
bulk_write_record
.
initial_index
is
None
:
self
.
db_connection
.
insert_course_index
(
bulk_write_record
.
index
)
else
:
self
.
db_connection
.
update_course_index
(
bulk_write_record
.
index
,
from_index
=
bulk_write_record
.
initial_index
)
self
.
_clear_bulk_write_record
(
course_key
)
def
_is_in_bulk_write_operation
(
self
,
course_key
,
ignore_case
=
False
):
"""
Return whether a bulk write is active on `course_key`.
"""
return
self
.
_get_bulk_write_record
(
course_key
,
ignore_case
)
.
active_count
>
0
def
get_course_index
(
self
,
course_key
,
ignore_case
=
False
):
"""
Return the index for course_key.
"""
if
self
.
_is_in_bulk_write_operation
(
course_key
,
ignore_case
):
return
self
.
_get_bulk_write_record
(
course_key
,
ignore_case
)
.
index
else
:
return
self
.
db_connection
.
get_course_index
(
course_key
,
ignore_case
)
def
insert_course_index
(
self
,
course_key
,
index_entry
):
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
if
bulk_write_record
.
active_count
>
0
:
bulk_write_record
.
index
=
index_entry
else
:
self
.
db_connection
.
insert_course_index
(
index_entry
)
def
update_course_index
(
self
,
course_key
,
updated_index_entry
):
"""
Change the given course's index entry.
Note, this operation can be dangerous and break running courses.
Does not return anything useful.
"""
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
if
bulk_write_record
.
active_count
>
0
:
bulk_write_record
.
index
=
updated_index_entry
else
:
self
.
db_connection
.
update_course_index
(
updated_index_entry
)
def
get_structure
(
self
,
course_key
,
version_guid
):
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
if
bulk_write_record
.
active_count
>
0
:
structure
=
bulk_write_record
.
get_structure
(
course_key
.
branch
)
# The structure hasn't been loaded from the db yet, so load it
if
structure
is
None
:
structure
=
self
.
db_connection
.
get_structure
(
bulk_write_record
.
index
[
'versions'
][
course_key
.
branch
])
bulk_write_record
.
set_structure
(
course_key
.
branch
,
structure
)
return
structure
else
:
# cast string to ObjectId if necessary
version_guid
=
course_key
.
as_object_id
(
version_guid
)
return
self
.
db_connection
.
get_structure
(
version_guid
)
def
update_structure
(
self
,
course_key
,
structure
):
self
.
_clear_cache
(
structure
[
'_id'
])
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
if
bulk_write_record
.
active_count
>
0
:
bulk_write_record
.
set_structure
(
course_key
.
branch
,
structure
)
else
:
self
.
db_connection
.
upsert_structure
(
structure
)
def
version_structure
(
self
,
course_key
,
structure
,
user_id
):
"""
Copy the structure and update the history info (edited_by, edited_on, previous_version)
:param structure:
:param user_id:
"""
bulk_write_record
=
self
.
_get_bulk_write_record
(
course_key
)
# If we have an active bulk write, and it's already been edited, then just use that structure
if
bulk_write_record
.
active_count
>
0
and
course_key
.
branch
in
bulk_write_record
.
dirty_branches
:
return
bulk_write_record
.
get_structure
(
course_key
.
branch
)
# Otherwise, make a new structure
new_structure
=
copy
.
deepcopy
(
structure
)
new_structure
[
'_id'
]
=
ObjectId
()
new_structure
[
'previous_version'
]
=
structure
[
'_id'
]
new_structure
[
'edited_by'
]
=
user_id
new_structure
[
'edited_on'
]
=
datetime
.
datetime
.
now
(
UTC
)
new_structure
[
'schema_version'
]
=
self
.
SCHEMA_VERSION
# If we're in a bulk write, update the structure used there, and mark it as dirty
if
bulk_write_record
.
active_count
>
0
:
bulk_write_record
.
set_structure
(
course_key
.
branch
,
new_structure
)
return
new_structure
class
SplitMongoModuleStore
(
ModuleStoreWriteBase
,
BulkWriteMixin
):
"""
"""
A Mongodb backed ModuleStore supporting versions, inheritance,
A Mongodb backed ModuleStore supporting versions, inheritance,
and sharing.
and sharing.
...
@@ -173,12 +387,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -173,12 +387,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'''
'''
Handles caching of items once inheritance and any other one time
Handles caching of items once inheritance and any other one time
per course per fetch operations are done.
per course per fetch operations are done.
:param system: a CachingDescriptorSystem
:param base_block_ids: list of block_ids to fetch
Arguments:
:param course_key: the destination course providing the context
system: a CachingDescriptorSystem
:param depth: how deep below these to prefetch
base_block_ids: list of block_ids to fetch
:param lazy: whether to fetch definitions or use placeholders
course_key: the destination course providing the context
depth: how deep below these to prefetch
lazy: whether to fetch definitions or use placeholders
'''
'''
with
self
.
bulk_write_operations
(
course_key
):
new_module_data
=
{}
new_module_data
=
{}
for
block_id
in
base_block_ids
:
for
block_id
in
base_block_ids
:
new_module_data
=
self
.
descendants
(
new_module_data
=
self
.
descendants
(
...
@@ -188,16 +405,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -188,16 +405,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
new_module_data
new_module_data
)
)
if
lazy
:
if
not
lazy
:
for
block
in
new_module_data
.
itervalues
():
block
[
'definition'
]
=
DefinitionLazyLoader
(
self
,
block
[
'category'
],
block
[
'definition'
],
lambda
fields
:
self
.
convert_references_to_keys
(
course_key
,
system
.
load_block_type
(
block
[
'category'
]),
fields
,
system
.
course_entry
[
'structure'
][
'blocks'
],
)
)
else
:
# Load all descendants by id
# Load all descendants by id
descendent_definitions
=
self
.
db_connection
.
find_matching_definitions
({
descendent_definitions
=
self
.
db_connection
.
find_matching_definitions
({
'_id'
:
{
'$in'
:
[
block
[
'definition'
]
'_id'
:
{
'$in'
:
[
block
[
'definition'
]
...
@@ -214,6 +422,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -214,6 +422,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
system
.
course_entry
[
'structure'
][
'blocks'
],
system
.
course_entry
[
'structure'
][
'blocks'
],
)
)
block
[
'fields'
]
.
update
(
converted_fields
)
block
[
'fields'
]
.
update
(
converted_fields
)
block
[
'definition_loaded'
]
=
True
system
.
module_data
.
update
(
new_module_data
)
system
.
module_data
.
update
(
new_module_data
)
return
system
.
module_data
return
system
.
module_data
...
@@ -265,6 +474,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -265,6 +474,8 @@ 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
:
if
not
hasattr
(
self
.
thread_cache
,
'course_cache'
):
self
.
thread_cache
.
course_cache
=
{}
try
:
try
:
del
self
.
thread_cache
.
course_cache
[
course_version_guid
]
del
self
.
thread_cache
.
course_cache
[
course_version_guid
]
except
KeyError
:
except
KeyError
:
...
@@ -272,7 +483,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -272,7 +483,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else
:
else
:
self
.
thread_cache
.
course_cache
=
{}
self
.
thread_cache
.
course_cache
=
{}
def
_lookup_course
(
self
,
course_
locator
):
def
_lookup_course
(
self
,
course_
key
):
'''
'''
Decode the locator into the right series of db access. Does not
Decode the locator into the right series of db access. Does not
return the CourseDescriptor! It returns the actual db json from
return the CourseDescriptor! It returns the actual db json from
...
@@ -283,40 +494,45 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -283,40 +494,45 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
it raises VersionConflictError (the version now differs from what it was when you got your
it raises VersionConflictError (the version now differs from what it was when you got your
reference)
reference)
:param course_
locator
: any subclass of CourseLocator
:param course_
key
: any subclass of CourseLocator
'''
'''
if
course_locator
.
org
and
course_locator
.
course
and
course_locator
.
run
:
if
course_key
.
org
and
course_key
.
course
and
course_key
.
run
:
if
course_locator
.
branch
is
None
:
if
course_key
.
branch
is
None
:
raise
InsufficientSpecificationError
(
course_locator
)
raise
InsufficientSpecificationError
(
course_key
)
# use the course id
# use the course id
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
index
=
self
.
get_course_index
(
course_key
)
if
index
is
None
:
if
index
is
None
:
raise
ItemNotFoundError
(
course_locator
)
raise
ItemNotFoundError
(
course_key
)
if
course_locator
.
branch
not
in
index
[
'versions'
]:
if
course_key
.
branch
not
in
index
[
'versions'
]:
raise
ItemNotFoundError
(
course_locator
)
raise
ItemNotFoundError
(
course_key
)
version_guid
=
index
[
'versions'
][
course_locator
.
branch
]
if
course_locator
.
version_guid
is
not
None
and
version_guid
!=
course_locator
.
version_guid
:
version_guid
=
index
[
'versions'
][
course_key
.
branch
]
if
course_key
.
version_guid
is
not
None
and
version_guid
!=
course_key
.
version_guid
:
# This may be a bit too touchy but it's hard to infer intent
# This may be a bit too touchy but it's hard to infer intent
raise
VersionConflictError
(
course_locator
,
version_guid
)
raise
VersionConflictError
(
course_key
,
version_guid
)
elif
course_locator
.
version_guid
is
None
:
raise
InsufficientSpecificationError
(
course_locator
)
elif
course_key
.
version_guid
is
None
:
raise
InsufficientSpecificationError
(
course_key
)
else
:
else
:
# TODO should this raise an exception if branch was provided?
# TODO should this raise an exception if branch was provided?
version_guid
=
course_
locator
.
version_guid
version_guid
=
course_
key
.
version_guid
# cast string to ObjectId if necessary
entry
=
self
.
get_structure
(
course_key
,
version_guid
)
version_guid
=
course_locator
.
as_object_id
(
version_guid
)
if
entry
is
None
:
entry
=
self
.
db_connection
.
get_structure
(
version_guid
)
raise
ItemNotFoundError
(
'Structure: {}'
.
format
(
version_guid
)
)
# b/c more than one course can use same structure, the 'org', 'course',
# b/c more than one course can use same structure, the 'org', 'course',
# 'run', and 'branch' are not intrinsic to structure
# 'run', and 'branch' are not intrinsic to structure
# and the one assoc'd w/ it by another fetch may not be the one relevant to this fetch; so,
# and the one assoc'd w/ it by another fetch may not be the one relevant to this fetch; so,
# add it in the envelope for the structure.
# add it in the envelope for the structure.
envelope
=
{
envelope
=
{
'org'
:
course_
locator
.
org
,
'org'
:
course_
key
.
org
,
'course'
:
course_
locator
.
course
,
'course'
:
course_
key
.
course
,
'run'
:
course_
locator
.
run
,
'run'
:
course_
key
.
run
,
'branch'
:
course_
locator
.
branch
,
'branch'
:
course_
key
.
branch
,
'structure'
:
entry
,
'structure'
:
entry
,
}
}
return
envelope
return
envelope
...
@@ -401,7 +617,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -401,7 +617,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# The supplied CourseKey is of the wrong type, so it can't possibly be stored in this modulestore.
# The supplied CourseKey is of the wrong type, so it can't possibly be stored in this modulestore.
return
False
return
False
course_index
=
self
.
db_connection
.
get_course_index
(
course_id
,
ignore_case
)
course_index
=
self
.
get_course_index
(
course_id
,
ignore_case
)
return
CourseLocator
(
course_index
[
'org'
],
course_index
[
'course'
],
course_index
[
'run'
],
course_id
.
branch
)
if
course_index
else
None
return
CourseLocator
(
course_index
[
'org'
],
course_index
[
'course'
],
course_index
[
'run'
],
course_id
.
branch
)
if
course_index
else
None
def
has_item
(
self
,
usage_key
):
def
has_item
(
self
,
usage_key
):
...
@@ -413,7 +629,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -413,7 +629,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
usage_key
.
block_id
is
None
:
if
usage_key
.
block_id
is
None
:
raise
InsufficientSpecificationError
(
usage_key
)
raise
InsufficientSpecificationError
(
usage_key
)
try
:
try
:
course_structure
=
self
.
_lookup_course
(
usage_key
)[
'structure'
]
course_structure
=
self
.
_lookup_course
(
usage_key
.
course_key
)[
'structure'
]
except
ItemNotFoundError
:
except
ItemNotFoundError
:
# this error only occurs if the course does not exist
# this error only occurs if the course does not exist
return
False
return
False
...
@@ -433,7 +649,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -433,7 +649,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# The supplied UsageKey is of the wrong type, so it can't possibly be stored in this modulestore.
# The supplied UsageKey is of the wrong type, so it can't possibly be stored in this modulestore.
raise
ItemNotFoundError
(
usage_key
)
raise
ItemNotFoundError
(
usage_key
)
course
=
self
.
_lookup_course
(
usage_key
)
course
=
self
.
_lookup_course
(
usage_key
.
course_key
)
items
=
self
.
_load_items
(
course
,
[
usage_key
.
block_id
],
depth
,
lazy
=
True
,
**
kwargs
)
items
=
self
.
_load_items
(
course
,
[
usage_key
.
block_id
],
depth
,
lazy
=
True
,
**
kwargs
)
if
len
(
items
)
==
0
:
if
len
(
items
)
==
0
:
raise
ItemNotFoundError
(
usage_key
)
raise
ItemNotFoundError
(
usage_key
)
...
@@ -511,7 +727,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -511,7 +727,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param locator: BlockUsageLocator restricting search scope
:param locator: BlockUsageLocator restricting search scope
'''
'''
course
=
self
.
_lookup_course
(
locator
)
course
=
self
.
_lookup_course
(
locator
.
course_key
)
parent_id
=
self
.
_get_parent_from_structure
(
locator
.
block_id
,
course
[
'structure'
])
parent_id
=
self
.
_get_parent_from_structure
(
locator
.
block_id
,
course
[
'structure'
])
if
parent_id
is
None
:
if
parent_id
is
None
:
return
None
return
None
...
@@ -541,12 +757,12 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -541,12 +757,12 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
for
block_id
in
items
for
block_id
in
items
]
]
def
get_course_index_info
(
self
,
course_
locator
):
def
get_course_index_info
(
self
,
course_
key
):
"""
"""
The index records the initial creation of the indexed course and tracks the current version
The index records the initial creation of the indexed course and tracks the current version
heads. This function is primarily for test verification but may serve some
heads. This function is primarily for test verification but may serve some
more general purpose.
more general purpose.
:param course_
locator
: must have a org, course, and run set
:param course_
key
: must have a org, course, and run set
:return {'org': string,
:return {'org': string,
versions: {'draft': the head draft version id,
versions: {'draft': the head draft version id,
'published': the head published version id if any,
'published': the head published version id if any,
...
@@ -555,24 +771,24 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -555,24 +771,24 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'edited_on': when the course was originally created
'edited_on': when the course was originally created
}
}
"""
"""
if
not
(
course_
locator
.
course
and
course_locator
.
run
and
course_locator
.
org
):
if
not
(
course_
key
.
course
and
course_key
.
run
and
course_key
.
org
):
return
None
return
None
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
index
=
self
.
get_course_index
(
course_key
)
return
index
return
index
# TODO figure out a way to make this info accessible from the course descriptor
# TODO figure out a way to make this info accessible from the course descriptor
def
get_course_history_info
(
self
,
course_
locator
):
def
get_course_history_info
(
self
,
course_
key
):
"""
"""
Because xblocks doesn't give a means to separate the course structure's meta information from
Because xblocks doesn't give a means to separate the course structure's meta information from
the course xblock's, this method will get that info for the structure as a whole.
the course xblock's, this method will get that info for the structure as a whole.
:param course_
locator
:
:param course_
key
:
:return {'original_version': the version guid of the original version of this course,
:return {'original_version': the version guid of the original version of this course,
'previous_version': the version guid of the previous version,
'previous_version': the version guid of the previous version,
'edited_by': who made the last change,
'edited_by': who made the last change,
'edited_on': when the change was made
'edited_on': when the change was made
}
}
"""
"""
course
=
self
.
_lookup_course
(
course_
locator
)[
'structure'
]
course
=
self
.
_lookup_course
(
course_
key
)[
'structure'
]
return
{
return
{
'original_version'
:
course
[
'original_version'
],
'original_version'
:
course
[
'original_version'
],
'previous_version'
:
course
[
'previous_version'
],
'previous_version'
:
course
[
'previous_version'
],
...
@@ -628,7 +844,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -628,7 +844,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
CourseLocator
(
version_guid
=
struct
[
'_id'
]))
CourseLocator
(
version_guid
=
struct
[
'_id'
]))
return
VersionTree
(
course_locator
,
result
)
return
VersionTree
(
course_locator
,
result
)
def
get_block_generations
(
self
,
block_locator
):
def
get_block_generations
(
self
,
block_locator
):
'''
'''
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
...
@@ -639,7 +854,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -639,7 +854,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'''
'''
# 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
course_struct
=
self
.
_lookup_course
(
block_locator
.
course_agnostic
())[
'structure'
]
course_struct
=
self
.
_lookup_course
(
block_locator
.
course_
key
.
course_
agnostic
())[
'structure'
]
block_id
=
block_locator
.
block_id
block_id
=
block_locator
.
block_id
update_version_field
=
'blocks.{}.edit_info.update_version'
.
format
(
block_id
)
update_version_field
=
'blocks.{}.edit_info.update_version'
.
format
(
block_id
)
all_versions_with_block
=
self
.
db_connection
.
find_matching_structures
(
all_versions_with_block
=
self
.
db_connection
.
find_matching_structures
(
...
@@ -772,7 +987,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -772,7 +987,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def
create_item
(
def
create_item
(
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
self
,
user_id
,
course_key
,
block_type
,
block_id
=
None
,
definition_locator
=
None
,
fields
=
None
,
definition_locator
=
None
,
fields
=
None
,
force
=
False
,
continue_version
=
False
,
**
kwargs
force
=
False
,
**
kwargs
):
):
"""
"""
Add a descriptor to persistence as an element
Add a descriptor to persistence as an element
...
@@ -799,10 +1014,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -799,10 +1014,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param block_id: if provided, must not already exist in the structure. Provides the block id for the
:param block_id: if provided, must not already exist in the structure. Provides the block id for the
new item in this structure. Otherwise, one is computed using the category appended w/ a few digits.
new item in this structure. Otherwise, one is computed using the category appended w/ a few digits.
:param continue_version: continue changing the current structure at the head of the course. Very dangerous
This method creates a new version of the course structure unless the course has a bulk_write operation
unless used in the same request as started the change! See below about version conflicts.
active.
This method creates a new version of the course structure unless continue_version is True.
It creates and inserts the new block, makes the block point
It creates and inserts the new block, makes the block point
to the definition which may be new or a new version of an existing or an existing.
to the definition which may be new or a new version of an existing or an existing.
...
@@ -818,6 +1031,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -818,6 +1031,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
the course id'd by version_guid but instead in one w/ a new version_guid. Ensure in this case that you get
the course id'd by version_guid but instead in one w/ a new version_guid. Ensure in this case that you get
the new version_guid from the locator in the returned object!
the new version_guid from the locator in the returned object!
"""
"""
with
self
.
bulk_write_operations
(
course_key
):
# split handles all the fields in one dict not separated by scope
# split handles all the fields in one dict not separated by scope
fields
=
fields
or
{}
fields
=
fields
or
{}
fields
.
update
(
kwargs
.
pop
(
'metadata'
,
{})
or
{})
fields
.
update
(
kwargs
.
pop
(
'metadata'
,
{})
or
{})
...
@@ -828,7 +1042,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -828,7 +1042,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
fields
.
update
(
definition_data
)
fields
.
update
(
definition_data
)
# 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_key
,
force
,
continue_version
)
index_entry
=
self
.
_get_index_if_valid
(
course_key
,
force
)
structure
=
self
.
_lookup_course
(
course_key
)[
'structure'
]
structure
=
self
.
_lookup_course
(
course_key
)[
'structure'
]
partitioned_fields
=
self
.
partition_fields_by_scope
(
block_type
,
fields
)
partitioned_fields
=
self
.
partition_fields_by_scope
(
block_type
,
fields
)
...
@@ -840,10 +1054,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -840,10 +1054,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
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
)
# copy the structure and modify the new one
# copy the structure and modify the new one
if
continue_version
:
new_structure
=
self
.
version_structure
(
course_key
,
structure
,
user_id
)
new_structure
=
structure
else
:
new_structure
=
self
.
_version_structure
(
structure
,
user_id
)
new_id
=
new_structure
[
'_id'
]
new_id
=
new_structure
[
'_id'
]
...
@@ -872,21 +1083,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -872,21 +1083,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'edit_info'
:
edit_info
,
'edit_info'
:
edit_info
,
})
})
if
continue_version
:
self
.
update_structure
(
course_key
,
new_structure
)
# db update
self
.
db_connection
.
update_structure
(
new_structure
)
# clear cache so things get refetched and inheritance recomputed
self
.
_clear_cache
(
new_id
)
else
:
self
.
db_connection
.
insert_structure
(
new_structure
)
# update the index entry if appropriate
# update the index entry if appropriate
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
# see if any search targets changed
# see if any search targets changed
if
fields
is
not
None
:
if
fields
is
not
None
:
self
.
_update_search_targets
(
index_entry
,
fields
)
self
.
_update_search_targets
(
index_entry
,
fields
)
if
not
continue_version
:
self
.
_update_head
(
course_key
,
index_entry
,
course_key
.
branch
,
new_id
)
self
.
_update_head
(
index_entry
,
course_key
.
branch
,
new_id
)
item_loc
=
BlockUsageLocator
(
item_loc
=
BlockUsageLocator
(
course_key
.
version_agnostic
(),
course_key
.
version_agnostic
(),
block_type
=
block_type
,
block_type
=
block_type
,
...
@@ -902,7 +1106,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -902,7 +1106,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# 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
):
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
Creates and saves a new xblock that as a child of the specified block
...
@@ -918,9 +1122,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -918,9 +1122,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
fields (dict): A dictionary specifying initial values for some or all fields
fields (dict): A dictionary specifying initial values for some or all fields
in the newly created block
in the newly created block
"""
"""
with
self
.
bulk_write_operations
(
parent_usage_key
.
course_key
):
xblock
=
self
.
create_item
(
xblock
=
self
.
create_item
(
user_id
,
parent_usage_key
.
course_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
user_id
,
parent_usage_key
.
course_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
continue_version
=
continue_version
,
**
kwargs
)
**
kwargs
)
# don't version the structure as create_item handled that already.
# don't version the structure as create_item handled that already.
...
@@ -928,6 +1132,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -928,6 +1132,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# add new block as child and update parent's version
# add new block as child and update parent's version
encoded_block_id
=
encode_key_for_mongo
(
parent_usage_key
.
block_id
)
encoded_block_id
=
encode_key_for_mongo
(
parent_usage_key
.
block_id
)
if
encoded_block_id
not
in
new_structure
[
'blocks'
]:
raise
ItemNotFoundError
(
parent_usage_key
)
parent
=
new_structure
[
'blocks'
][
encoded_block_id
]
parent
=
new_structure
[
'blocks'
][
encoded_block_id
]
parent
[
'fields'
]
.
setdefault
(
'children'
,
[])
.
append
(
xblock
.
location
.
block_id
)
parent
[
'fields'
]
.
setdefault
(
'children'
,
[])
.
append
(
xblock
.
location
.
block_id
)
if
parent
[
'edit_info'
][
'update_version'
]
!=
new_structure
[
'_id'
]:
if
parent
[
'edit_info'
][
'update_version'
]
!=
new_structure
[
'_id'
]:
...
@@ -941,9 +1148,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -941,9 +1148,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
}
}
# db update
# db update
self
.
db_connection
.
update_structure
(
new_structure
)
self
.
update_structure
(
parent_usage_key
.
course_key
,
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
# don't need to update the index b/c create_item did it for this version
return
xblock
return
xblock
...
@@ -1023,7 +1228,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1023,7 +1228,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
assert
master_branch
is
not
None
assert
master_branch
is
not
None
# check course and run's uniqueness
# check course and run's uniqueness
locator
=
CourseLocator
(
org
=
org
,
course
=
course
,
run
=
run
,
branch
=
master_branch
)
locator
=
CourseLocator
(
org
=
org
,
course
=
course
,
run
=
run
,
branch
=
master_branch
)
index
=
self
.
db_connection
.
get_course_index
(
locator
)
index
=
self
.
get_course_index
(
locator
)
if
index
is
not
None
:
if
index
is
not
None
:
raise
DuplicateCourseError
(
locator
,
index
)
raise
DuplicateCourseError
(
locator
,
index
)
...
@@ -1061,18 +1266,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1061,18 +1266,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
)
)
new_id
=
draft_structure
[
'_id'
]
new_id
=
draft_structure
[
'_id'
]
self
.
db_connection
.
insert_structure
(
draft_structure
)
if
versions_dict
is
None
:
if
versions_dict
is
None
:
versions_dict
=
{
master_branch
:
new_id
}
versions_dict
=
{
master_branch
:
new_id
}
else
:
else
:
versions_dict
[
master_branch
]
=
new_id
versions_dict
[
master_branch
]
=
new_id
elif
definition_fields
or
block
_fields
:
# pointing to existing course w/ some overrides
elif
block_fields
or
definition
_fields
:
# pointing to existing course w/ some overrides
# just get the draft_version structure
# just get the draft_version structure
draft_version
=
CourseLocator
(
version_guid
=
versions_dict
[
master_branch
])
draft_version
=
CourseLocator
(
version_guid
=
versions_dict
[
master_branch
])
draft_structure
=
self
.
_lookup_course
(
draft_version
)[
'structure'
]
draft_structure
=
self
.
_lookup_course
(
draft_version
)[
'structure'
]
draft_structure
=
self
.
_version_structure
(
draft_structure
,
user_id
)
draft_structure
=
self
.
version_structure
(
locator
,
draft_structure
,
user_id
)
new_id
=
draft_structure
[
'_id'
]
new_id
=
draft_structure
[
'_id'
]
encoded_block_id
=
encode_key_for_mongo
(
draft_structure
[
'root'
])
encoded_block_id
=
encode_key_for_mongo
(
draft_structure
[
'root'
])
root_block
=
draft_structure
[
'blocks'
][
encoded_block_id
]
root_block
=
draft_structure
[
'blocks'
][
encoded_block_id
]
...
@@ -1093,9 +1296,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1093,9 +1296,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
root_block
[
'edit_info'
][
'previous_version'
]
=
root_block
[
'edit_info'
]
.
get
(
'update_version'
)
root_block
[
'edit_info'
][
'previous_version'
]
=
root_block
[
'edit_info'
]
.
get
(
'update_version'
)
root_block
[
'edit_info'
][
'update_version'
]
=
new_id
root_block
[
'edit_info'
][
'update_version'
]
=
new_id
self
.
db_connection
.
insert_structure
(
draft_structure
)
versions_dict
[
master_branch
]
=
new_id
versions_dict
[
master_branch
]
=
new_id
else
:
# Pointing to an existing course structure
new_id
=
versions_dict
[
master_branch
]
draft_version
=
CourseLocator
(
version_guid
=
new_id
)
draft_structure
=
self
.
_lookup_course
(
draft_version
)[
'structure'
]
locator
=
locator
.
replace
(
version_guid
=
new_id
)
with
self
.
bulk_write_operations
(
locator
):
self
.
update_structure
(
locator
,
draft_structure
)
index_entry
=
{
index_entry
=
{
'_id'
:
ObjectId
(),
'_id'
:
ObjectId
(),
'org'
:
org
,
'org'
:
org
,
...
@@ -1109,7 +1318,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1109,7 +1318,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
}
}
if
fields
is
not
None
:
if
fields
is
not
None
:
self
.
_update_search_targets
(
index_entry
,
fields
)
self
.
_update_search_targets
(
index_entry
,
fields
)
self
.
db_connection
.
insert_course_index
(
index_entry
)
self
.
insert_course_index
(
locator
,
index_entry
)
# expensive hack to persist default field values set in __init__ method (e.g., wiki_slug)
# expensive hack to persist default field values set in __init__ method (e.g., wiki_slug)
course
=
self
.
get_course
(
locator
,
**
kwargs
)
course
=
self
.
get_course
(
locator
,
**
kwargs
)
...
@@ -1143,6 +1352,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1143,6 +1352,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
"""
Broke out guts of update_item for short-circuited internal use only
Broke out guts of update_item for short-circuited internal use only
"""
"""
with
self
.
bulk_write_operations
(
course_key
):
if
allow_not_found
and
isinstance
(
block_id
,
(
LocalId
,
NoneType
)):
if
allow_not_found
and
isinstance
(
block_id
,
(
LocalId
,
NoneType
)):
fields
=
{}
fields
=
{}
for
subfields
in
partitioned_fields
.
itervalues
():
for
subfields
in
partitioned_fields
.
itervalues
():
...
@@ -1190,7 +1400,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1190,7 +1400,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# if updated, rev the structure
# if updated, rev the structure
if
is_updated
:
if
is_updated
:
new_structure
=
self
.
_version_structure
(
original_structure
,
user_id
)
new_structure
=
self
.
version_structure
(
course_key
,
original_structure
,
user_id
)
block_data
=
self
.
_get_block_from_structure
(
new_structure
,
block_id
)
block_data
=
self
.
_get_block_from_structure
(
new_structure
,
block_id
)
block_data
[
"definition"
]
=
definition_locator
.
definition_id
block_data
[
"definition"
]
=
definition_locator
.
definition_id
...
@@ -1203,12 +1413,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1203,12 +1413,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'previous_version'
:
block_data
[
'edit_info'
][
'update_version'
],
'previous_version'
:
block_data
[
'edit_info'
][
'update_version'
],
'update_version'
:
new_id
,
'update_version'
:
new_id
,
}
}
self
.
db_connection
.
insert_structure
(
new_structure
)
self
.
update_structure
(
course_key
,
new_structure
)
# update the index entry if appropriate
# update the index entry if appropriate
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
self
.
_update_search_targets
(
index_entry
,
definition_fields
)
self
.
_update_search_targets
(
index_entry
,
definition_fields
)
self
.
_update_search_targets
(
index_entry
,
settings
)
self
.
_update_search_targets
(
index_entry
,
settings
)
self
.
_update_head
(
index_entry
,
course_key
.
branch
,
new_id
)
course_key
=
CourseLocator
(
course_key
=
CourseLocator
(
org
=
index_entry
[
'org'
],
org
=
index_entry
[
'org'
],
course
=
index_entry
[
'course'
],
course
=
index_entry
[
'course'
],
...
@@ -1216,6 +1425,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1216,6 +1425,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
branch
=
course_key
.
branch
,
branch
=
course_key
.
branch
,
version_guid
=
new_id
version_guid
=
new_id
)
)
self
.
_update_head
(
course_key
,
index_entry
,
course_key
.
branch
,
new_id
)
else
:
else
:
course_key
=
CourseLocator
(
version_guid
=
new_id
)
course_key
=
CourseLocator
(
version_guid
=
new_id
)
...
@@ -1286,18 +1496,20 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1286,18 +1496,20 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param user_id: who's doing the change
:param user_id: who's doing the change
"""
"""
# 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
(
xblock
.
location
,
force
)
course_key
=
xblock
.
location
.
course_key
structure
=
self
.
_lookup_course
(
xblock
.
location
)[
'structure'
]
with
self
.
bulk_write_operations
(
course_key
):
new_structure
=
self
.
_version_structure
(
structure
,
user_id
)
index_entry
=
self
.
_get_index_if_valid
(
course_key
,
force
)
structure
=
self
.
_lookup_course
(
course_key
)[
'structure'
]
new_structure
=
self
.
version_structure
(
course_key
,
structure
,
user_id
)
new_id
=
new_structure
[
'_id'
]
new_id
=
new_structure
[
'_id'
]
is_updated
=
self
.
_persist_subdag
(
xblock
,
user_id
,
new_structure
[
'blocks'
],
new_id
)
is_updated
=
self
.
_persist_subdag
(
xblock
,
user_id
,
new_structure
[
'blocks'
],
new_id
)
if
is_updated
:
if
is_updated
:
self
.
db_connection
.
insert_structure
(
new_structure
)
self
.
update_structure
(
course_key
,
new_structure
)
# update the index entry if appropriate
# update the index entry if appropriate
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
self
.
_update_head
(
index_entry
,
xblock
.
location
.
branch
,
new_id
)
self
.
_update_head
(
course_key
,
index_entry
,
xblock
.
location
.
branch
,
new_id
)
# fetch and return the new item--fetching is unnecessary but a good qc step
# fetch and return the new item--fetching is unnecessary but a good qc step
return
self
.
get_item
(
xblock
.
location
.
for_version
(
new_id
))
return
self
.
get_item
(
xblock
.
location
.
for_version
(
new_id
))
...
@@ -1415,8 +1627,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1415,8 +1627,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
subtree but the ancestors up to and including the course root are not published.
subtree but the ancestors up to and including the course root are not published.
"""
"""
# get the destination's index, and source and destination structures.
# get the destination's index, and source and destination structures.
with
self
.
bulk_write_operations
(
source_course
):
with
self
.
bulk_write_operations
(
destination_course
):
source_structure
=
self
.
_lookup_course
(
source_course
)[
'structure'
]
source_structure
=
self
.
_lookup_course
(
source_course
)[
'structure'
]
index_entry
=
self
.
db_connection
.
get_course_index
(
destination_course
)
index_entry
=
self
.
get_course_index
(
destination_course
)
if
index_entry
is
None
:
if
index_entry
is
None
:
# brand new course
# brand new course
raise
ItemNotFoundError
(
destination_course
)
raise
ItemNotFoundError
(
destination_course
)
...
@@ -1434,7 +1648,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1434,7 +1648,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
)
)
else
:
else
:
destination_structure
=
self
.
_lookup_course
(
destination_course
)[
'structure'
]
destination_structure
=
self
.
_lookup_course
(
destination_course
)[
'structure'
]
destination_structure
=
self
.
_version_structure
(
destination_structure
,
user_id
)
destination_structure
=
self
.
version_structure
(
destination_course
,
destination_structure
,
user_id
)
if
blacklist
!=
EXCLUDE_ALL
:
if
blacklist
!=
EXCLUDE_ALL
:
blacklist
=
[
shunned
.
block_id
for
shunned
in
blacklist
or
[]]
blacklist
=
[
shunned
.
block_id
for
shunned
in
blacklist
or
[]]
...
@@ -1468,18 +1682,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1468,18 +1682,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self
.
_delete_if_true_orphan
(
orphan
,
destination_structure
)
self
.
_delete_if_true_orphan
(
orphan
,
destination_structure
)
# update the db
# update the db
self
.
db_connection
.
insert_structure
(
destination_structure
)
self
.
update_structure
(
destination_course
,
destination_structure
)
self
.
_update_head
(
index_entry
,
destination_course
.
branch
,
destination_structure
[
'_id'
])
self
.
_update_head
(
destination_course
,
index_entry
,
destination_course
.
branch
,
destination_structure
[
'_id'
])
def
update_course_index
(
self
,
updated_index_entry
):
"""
Change the given course's index entry.
Note, this operation can be dangerous and break running courses.
Does not return anything useful.
"""
self
.
db_connection
.
update_course_index
(
updated_index_entry
)
def
delete_item
(
self
,
usage_locator
,
user_id
,
force
=
False
):
def
delete_item
(
self
,
usage_locator
,
user_id
,
force
=
False
):
"""
"""
...
@@ -1500,13 +1704,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1500,13 +1704,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# The supplied UsageKey is of the wrong type, so it can't possibly be stored in this modulestore.
# The supplied UsageKey is of the wrong type, so it can't possibly be stored in this modulestore.
raise
ItemNotFoundError
(
usage_locator
)
raise
ItemNotFoundError
(
usage_locator
)
with
self
.
bulk_write_operations
(
usage_locator
.
course_key
):
original_structure
=
self
.
_lookup_course
(
usage_locator
.
course_key
)[
'structure'
]
original_structure
=
self
.
_lookup_course
(
usage_locator
.
course_key
)[
'structure'
]
if
original_structure
[
'root'
]
==
usage_locator
.
block_id
:
if
original_structure
[
'root'
]
==
usage_locator
.
block_id
:
raise
ValueError
(
"Cannot delete the root of a course"
)
raise
ValueError
(
"Cannot delete the root of a course"
)
if
encode_key_for_mongo
(
usage_locator
.
block_id
)
not
in
original_structure
[
'blocks'
]:
if
encode_key_for_mongo
(
usage_locator
.
block_id
)
not
in
original_structure
[
'blocks'
]:
raise
ValueError
(
"Cannot delete a block that does not exist"
)
raise
ValueError
(
"Cannot delete a block that does not exist"
)
index_entry
=
self
.
_get_index_if_valid
(
usage_locator
,
force
)
index_entry
=
self
.
_get_index_if_valid
(
usage_locator
.
course_key
,
force
)
new_structure
=
self
.
_version_structure
(
original_structure
,
user_id
)
new_structure
=
self
.
version_structure
(
usage_locator
.
course_key
,
original_structure
,
user_id
)
new_blocks
=
new_structure
[
'blocks'
]
new_blocks
=
new_structure
[
'blocks'
]
new_id
=
new_structure
[
'_id'
]
new_id
=
new_structure
[
'_id'
]
encoded_block_id
=
self
.
_get_parent_from_structure
(
usage_locator
.
block_id
,
original_structure
)
encoded_block_id
=
self
.
_get_parent_from_structure
(
usage_locator
.
block_id
,
original_structure
)
...
@@ -1530,11 +1735,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1530,11 +1735,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
remove_subtree
(
usage_locator
.
block_id
)
remove_subtree
(
usage_locator
.
block_id
)
# update index if appropriate and structures
# update index if appropriate and structures
self
.
db_connection
.
insert_structure
(
new_structure
)
self
.
update_structure
(
usage_locator
.
course_key
,
new_structure
)
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
# update the index entry if appropriate
# update the index entry if appropriate
self
.
_update_head
(
index_entry
,
usage_locator
.
branch
,
new_id
)
self
.
_update_head
(
usage_locator
.
course_key
,
index_entry
,
usage_locator
.
branch
,
new_id
)
result
=
usage_locator
.
course_key
.
for_version
(
new_id
)
result
=
usage_locator
.
course_key
.
for_version
(
new_id
)
else
:
else
:
result
=
CourseLocator
(
version_guid
=
new_id
)
result
=
CourseLocator
(
version_guid
=
new_id
)
...
@@ -1549,7 +1754,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1549,7 +1754,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
with a versions hash to restore the course; however, the edited_on and
with a versions hash to restore the course; however, the edited_on and
edited_by won't reflect the originals, of course.
edited_by won't reflect the originals, of course.
"""
"""
index
=
self
.
db_connection
.
get_course_index
(
course_key
)
index
=
self
.
get_course_index
(
course_key
)
if
index
is
None
:
if
index
is
None
:
raise
ItemNotFoundError
(
course_key
)
raise
ItemNotFoundError
(
course_key
)
# this is the only real delete in the system. should it do something else?
# this is the only real delete in the system. should it do something else?
...
@@ -1610,23 +1815,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1610,23 +1815,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
depth
is
None
or
depth
>
0
:
if
depth
is
None
or
depth
>
0
:
depth
=
depth
-
1
if
depth
is
not
None
else
None
depth
=
depth
-
1
if
depth
is
not
None
else
None
for
child
in
descendent_map
[
block_id
][
'fields'
]
.
get
(
'children'
,
[]):
for
child
in
descendent_map
[
block_id
][
'fields'
]
.
get
(
'children'
,
[]):
descendent_map
=
self
.
descendants
(
block_map
,
child
,
depth
,
descendent_map
=
self
.
descendants
(
block_map
,
child
,
depth
,
descendent_map
)
descendent_map
)
return
descendent_map
return
descendent_map
def
definition_locator
(
self
,
definition
):
'''
Pull the id out of the definition w/ correct semantics for its
representation
'''
if
isinstance
(
definition
,
DefinitionLazyLoader
):
return
definition
.
definition_locator
elif
'_id'
not
in
definition
:
return
DefinitionLocator
(
definition
.
get
(
'category'
),
LocalId
())
else
:
return
DefinitionLocator
(
definition
[
'category'
],
definition
[
'_id'
])
def
get_modulestore_type
(
self
,
course_key
=
None
):
def
get_modulestore_type
(
self
,
course_key
=
None
):
"""
"""
Returns an enumeration-like type reflecting the type of this modulestore, per ModuleStoreEnum.Type.
Returns an enumeration-like type reflecting the type of this modulestore, per ModuleStoreEnum.Type.
...
@@ -1651,9 +1843,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1651,9 +1843,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
block_id
for
block_id
in
block
[
'fields'
][
"children"
]
block_id
for
block_id
in
block
[
'fields'
][
"children"
]
if
encode_key_for_mongo
(
block_id
)
in
original_structure
[
'blocks'
]
if
encode_key_for_mongo
(
block_id
)
in
original_structure
[
'blocks'
]
]
]
self
.
db_connection
.
update_structure
(
original_structure
)
self
.
update_structure
(
course_locator
,
original_structure
)
# clear cache again b/c inheritance may be wrong over orphans
self
.
_clear_cache
(
original_structure
[
'_id'
])
def
convert_references_to_keys
(
self
,
course_key
,
xblock_class
,
jsonfields
,
blocks
):
def
convert_references_to_keys
(
self
,
course_key
,
xblock_class
,
jsonfields
,
blocks
):
"""
"""
...
@@ -1681,69 +1871,50 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1681,69 +1871,50 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
return
course_key
.
make_usage_key
(
'unknown'
,
block_id
)
return
course_key
.
make_usage_key
(
'unknown'
,
block_id
)
xblock_class
=
self
.
mixologist
.
mix
(
xblock_class
)
xblock_class
=
self
.
mixologist
.
mix
(
xblock_class
)
for
field_name
,
value
in
jsonfields
.
iteritems
():
# Make a shallow copy, so that we aren't manipulating a cached field dictionary
output_fields
=
dict
(
jsonfields
)
for
field_name
,
value
in
output_fields
.
iteritems
():
if
value
:
if
value
:
field
=
xblock_class
.
fields
.
get
(
field_name
)
field
=
xblock_class
.
fields
.
get
(
field_name
)
if
field
is
None
:
if
field
is
None
:
continue
continue
elif
isinstance
(
field
,
Reference
):
elif
isinstance
(
field
,
Reference
):
json
fields
[
field_name
]
=
robust_usage_key
(
value
)
output_
fields
[
field_name
]
=
robust_usage_key
(
value
)
elif
isinstance
(
field
,
ReferenceList
):
elif
isinstance
(
field
,
ReferenceList
):
json
fields
[
field_name
]
=
[
robust_usage_key
(
ele
)
for
ele
in
value
]
output_
fields
[
field_name
]
=
[
robust_usage_key
(
ele
)
for
ele
in
value
]
elif
isinstance
(
field
,
ReferenceValueDict
):
elif
isinstance
(
field
,
ReferenceValueDict
):
for
key
,
subvalue
in
value
.
iteritems
():
for
key
,
subvalue
in
value
.
iteritems
():
assert
isinstance
(
subvalue
,
basestring
)
assert
isinstance
(
subvalue
,
basestring
)
value
[
key
]
=
robust_usage_key
(
subvalue
)
value
[
key
]
=
robust_usage_key
(
subvalue
)
return
json
fields
return
output_
fields
def
_get_index_if_valid
(
self
,
locator
,
force
=
False
,
continue_version
=
False
):
def
_get_index_if_valid
(
self
,
course_key
,
force
=
False
):
"""
"""
If the
locator
identifies a course and points to its draft (or plausibly its draft),
If the
course_key
identifies a course and points to its draft (or plausibly its draft),
then return the index entry.
then return the index entry.
raises VersionConflictError if not the right version
raises VersionConflictError if not the right version
:param
locator: a coursel
ocator
:param
course_key: a CourseL
ocator
:param force: if false, raises VersionConflictError if the current head of the course != the one identified
:param force: if false, raises VersionConflictError if the current head of the course != the one identified
by locator. Cannot be True if continue_version is True
by course_key
:param continue_version: if True, assumes this operation requires a head version and will not create a new
"""
version but instead continue an existing transaction on this version. This flag cannot be True if force is True.
if
course_key
.
org
is
None
or
course_key
.
course
is
None
or
course_key
.
run
is
None
or
course_key
.
branch
is
None
:
"""
if
locator
.
org
is
None
or
locator
.
course
is
None
or
locator
.
run
is
None
or
locator
.
branch
is
None
:
if
continue_version
:
raise
InsufficientSpecificationError
(
"To continue a version, the locator must point to one ({})."
.
format
(
locator
)
)
else
:
return
None
return
None
else
:
else
:
index_entry
=
self
.
db_connection
.
get_course_index
(
locator
)
index_entry
=
self
.
get_course_index
(
course_key
)
is_head
=
(
is_head
=
(
locator
.
version_guid
is
None
or
course_key
.
version_guid
is
None
or
index_entry
[
'versions'
][
locator
.
branch
]
==
locator
.
version_guid
index_entry
[
'versions'
][
course_key
.
branch
]
==
course_key
.
version_guid
)
)
if
(
is_head
or
(
force
and
not
continue_version
)
):
if
(
is_head
or
force
):
return
index_entry
return
index_entry
else
:
else
:
raise
VersionConflictError
(
raise
VersionConflictError
(
locator
,
course_key
,
index_entry
[
'versions'
][
locator
.
branch
]
index_entry
[
'versions'
][
course_key
.
branch
]
)
)
def
_version_structure
(
self
,
structure
,
user_id
):
"""
Copy the structure and update the history info (edited_by, edited_on, previous_version)
:param structure:
:param user_id:
"""
new_structure
=
copy
.
deepcopy
(
structure
)
new_structure
[
'_id'
]
=
ObjectId
()
new_structure
[
'previous_version'
]
=
structure
[
'_id'
]
new_structure
[
'edited_by'
]
=
user_id
new_structure
[
'edited_on'
]
=
datetime
.
datetime
.
now
(
UTC
)
new_structure
[
'schema_version'
]
=
self
.
SCHEMA_VERSION
return
new_structure
def
_find_local_root
(
self
,
element_to_find
,
possibility
,
tree
):
def
_find_local_root
(
self
,
element_to_find
,
possibility
,
tree
):
if
possibility
not
in
tree
:
if
possibility
not
in
tree
:
return
False
return
False
...
@@ -1766,7 +1937,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1766,7 +1937,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
field_name
in
self
.
SEARCH_TARGET_DICT
:
if
field_name
in
self
.
SEARCH_TARGET_DICT
:
index_entry
.
setdefault
(
'search_targets'
,
{})[
field_name
]
=
field_value
index_entry
.
setdefault
(
'search_targets'
,
{})[
field_name
]
=
field_value
def
_update_head
(
self
,
index_entry
,
branch
,
new_id
):
def
_update_head
(
self
,
course_key
,
index_entry
,
branch
,
new_id
):
"""
"""
Update the active index for the given course's branch to point to new_id
Update the active index for the given course's branch to point to new_id
...
@@ -1774,8 +1945,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1774,8 +1945,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param course_locator:
:param course_locator:
:param new_id:
:param new_id:
"""
"""
if
not
isinstance
(
new_id
,
ObjectId
):
raise
TypeError
(
'new_id must be an ObjectId, but is {!r}'
.
format
(
new_id
))
index_entry
[
'versions'
][
branch
]
=
new_id
index_entry
[
'versions'
][
branch
]
=
new_id
self
.
db_connection
.
update_course_index
(
index_entry
)
self
.
insert_course_index
(
course_key
,
index_entry
)
def
partition_xblock_fields_by_scope
(
self
,
xblock
):
def
partition_xblock_fields_by_scope
(
self
,
xblock
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
View file @
562f0e31
...
@@ -9,6 +9,7 @@ from xmodule.modulestore.exceptions import InsufficientSpecificationError
...
@@ -9,6 +9,7 @@ from xmodule.modulestore.exceptions import InsufficientSpecificationError
from
xmodule.modulestore.draft_and_published
import
(
from
xmodule.modulestore.draft_and_published
import
(
ModuleStoreDraftAndPublished
,
DIRECT_ONLY_CATEGORIES
,
UnsupportedRevisionError
ModuleStoreDraftAndPublished
,
DIRECT_ONLY_CATEGORIES
,
UnsupportedRevisionError
)
)
from
opaque_keys.edx.locator
import
CourseLocator
class
DraftVersioningModuleStore
(
ModuleStoreDraftAndPublished
,
SplitMongoModuleStore
):
class
DraftVersioningModuleStore
(
ModuleStoreDraftAndPublished
,
SplitMongoModuleStore
):
...
@@ -30,6 +31,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -30,6 +31,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
Returns: a CourseDescriptor
Returns: a CourseDescriptor
"""
"""
master_branch
=
kwargs
.
pop
(
'master_branch'
,
ModuleStoreEnum
.
BranchName
.
draft
)
master_branch
=
kwargs
.
pop
(
'master_branch'
,
ModuleStoreEnum
.
BranchName
.
draft
)
with
self
.
bulk_write_operations
(
CourseLocator
(
org
,
course
,
run
)):
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_course
(
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_course
(
org
,
course
,
run
,
user_id
,
master_branch
=
master_branch
,
**
kwargs
org
,
course
,
run
,
user_id
,
master_branch
=
master_branch
,
**
kwargs
)
)
...
@@ -87,6 +89,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -87,6 +89,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
def
update_item
(
self
,
descriptor
,
user_id
,
allow_not_found
=
False
,
force
=
False
,
**
kwargs
):
def
update_item
(
self
,
descriptor
,
user_id
,
allow_not_found
=
False
,
force
=
False
,
**
kwargs
):
descriptor
.
location
=
self
.
_map_revision_to_branch
(
descriptor
.
location
)
descriptor
.
location
=
self
.
_map_revision_to_branch
(
descriptor
.
location
)
with
self
.
bulk_write_operations
(
descriptor
.
location
.
course_key
):
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
update_item
(
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
update_item
(
descriptor
,
descriptor
,
user_id
,
user_id
,
...
@@ -106,6 +109,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -106,6 +109,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
See :py:meth `ModuleStoreDraftAndPublished.create_item`
See :py:meth `ModuleStoreDraftAndPublished.create_item`
"""
"""
course_key
=
self
.
_map_revision_to_branch
(
course_key
)
course_key
=
self
.
_map_revision_to_branch
(
course_key
)
with
self
.
bulk_write_operations
(
course_key
):
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_item
(
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_item
(
user_id
,
course_key
,
block_type
,
block_id
=
block_id
,
user_id
,
course_key
,
block_type
,
block_id
=
block_id
,
definition_locator
=
definition_locator
,
fields
=
fields
,
definition_locator
=
definition_locator
,
fields
=
fields
,
...
@@ -120,6 +124,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -120,6 +124,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
fields
=
None
,
continue_version
=
False
,
**
kwargs
fields
=
None
,
continue_version
=
False
,
**
kwargs
):
):
parent_usage_key
=
self
.
_map_revision_to_branch
(
parent_usage_key
)
parent_usage_key
=
self
.
_map_revision_to_branch
(
parent_usage_key
)
with
self
.
bulk_write_operations
(
parent_usage_key
.
course_key
):
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_child
(
item
=
super
(
DraftVersioningModuleStore
,
self
)
.
create_child
(
user_id
,
parent_usage_key
,
block_type
,
block_id
=
block_id
,
user_id
,
parent_usage_key
,
block_type
,
block_id
=
block_id
,
fields
=
fields
,
continue_version
=
continue_version
,
**
kwargs
fields
=
fields
,
continue_version
=
continue_version
,
**
kwargs
...
@@ -141,6 +146,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -141,6 +146,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
currently only provided by contentstore.views.item.orphan_handler
currently only provided by contentstore.views.item.orphan_handler
Otherwise, raises a ValueError.
Otherwise, raises a ValueError.
"""
"""
with
self
.
bulk_write_operations
(
location
.
course_key
):
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
]
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
]
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
all
:
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
all
:
...
@@ -231,7 +237,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -231,7 +237,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
:return: True if the draft and published versions differ
:return: True if the draft and published versions differ
"""
"""
def
get_block
(
branch_name
):
def
get_block
(
branch_name
):
course_structure
=
self
.
_lookup_course
(
xblock
.
location
.
for_branch
(
branch_name
))[
'structure'
]
course_structure
=
self
.
_lookup_course
(
xblock
.
location
.
course_key
.
for_branch
(
branch_name
))[
'structure'
]
return
self
.
_get_block_from_structure
(
course_structure
,
xblock
.
location
.
block_id
)
return
self
.
_get_block_from_structure
(
course_structure
,
xblock
.
location
.
block_id
)
draft_block
=
get_block
(
ModuleStoreEnum
.
BranchName
.
draft
)
draft_block
=
get_block
(
ModuleStoreEnum
.
BranchName
.
draft
)
...
@@ -255,7 +261,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -255,7 +261,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
# because for_branch obliterates the version_guid and will lead to missed version conflicts.
# because for_branch obliterates the version_guid and will lead to missed version conflicts.
# TODO Instead, the for_branch implementation should be fixed in the Opaque Keys library.
# TODO Instead, the for_branch implementation should be fixed in the Opaque Keys library.
location
.
course_key
.
replace
(
branch
=
ModuleStoreEnum
.
BranchName
.
draft
),
location
.
course_key
.
replace
(
branch
=
ModuleStoreEnum
.
BranchName
.
draft
),
location
.
course_key
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
),
# We clear out the version_guid here because the location here is from the draft branch, and that
# won't have the same version guid
location
.
course_key
.
replace
(
branch
=
ModuleStoreEnum
.
BranchName
.
published
,
version_guid
=
None
),
[
location
],
[
location
],
blacklist
=
blacklist
blacklist
=
blacklist
)
)
...
@@ -266,6 +274,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -266,6 +274,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
Deletes the published version of the item.
Deletes the published version of the item.
Returns the newly unpublished item.
Returns the newly unpublished item.
"""
"""
with
self
.
bulk_write_operations
(
location
.
course_key
):
self
.
delete_item
(
location
,
user_id
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
self
.
delete_item
(
location
,
user_id
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
return
self
.
get_item
(
location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
),
**
kwargs
)
return
self
.
get_item
(
location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
),
**
kwargs
)
...
@@ -348,6 +357,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -348,6 +357,7 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
"""
"""
Split-based modulestores need to import published blocks to both branches
Split-based modulestores need to import published blocks to both branches
"""
"""
with
self
.
bulk_write_operations
(
course_key
):
# hardcode course root block id
# hardcode course root block id
if
block_type
==
'course'
:
if
block_type
==
'course'
:
block_id
=
self
.
DEFAULT_ROOT_BLOCK_ID
block_id
=
self
.
DEFAULT_ROOT_BLOCK_ID
...
...
common/lib/xmodule/xmodule/modulestore/tests/factories.py
View file @
562f0e31
...
@@ -298,4 +298,12 @@ def check_mongo_calls(mongo_store, num_finds=0, num_sends=None):
...
@@ -298,4 +298,12 @@ def check_mongo_calls(mongo_store, num_finds=0, num_sends=None):
finally
:
finally
:
map
(
lambda
wrap_patch
:
wrap_patch
.
stop
(),
wrap_patches
)
map
(
lambda
wrap_patch
:
wrap_patch
.
stop
(),
wrap_patches
)
call_count
=
sum
([
find_wrap
.
call_count
for
find_wrap
in
find_wraps
])
call_count
=
sum
([
find_wrap
.
call_count
for
find_wrap
in
find_wraps
])
assert_equal
(
call_count
,
num_finds
)
assert_equal
(
call_count
,
num_finds
,
"Expected {} calls, {} were made. Calls: {}"
.
format
(
num_finds
,
call_count
,
[
find_wrap
.
call_args_list
for
find_wrap
in
find_wraps
]
)
)
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
562f0e31
...
@@ -127,6 +127,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -127,6 +127,7 @@ 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.
"""
"""
# create course
# create course
with
self
.
store
.
bulk_write_operations
(
course_key
):
self
.
course
=
self
.
store
.
create_course
(
course_key
.
org
,
course_key
.
course
,
course_key
.
run
,
self
.
user_id
)
self
.
course
=
self
.
store
.
create_course
(
course_key
.
org
,
course_key
.
course
,
course_key
.
run
,
self
.
user_id
)
if
isinstance
(
self
.
course
.
id
,
CourseLocator
):
if
isinstance
(
self
.
course
.
id
,
CourseLocator
):
self
.
course_locations
[
self
.
MONGO_COURSEID
]
=
self
.
course
.
location
self
.
course_locations
[
self
.
MONGO_COURSEID
]
=
self
.
course
.
location
...
@@ -188,6 +189,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -188,6 +189,7 @@ class TestMixedModuleStore(unittest.TestCase):
create_sub_tree
(
block
,
tree
)
create_sub_tree
(
block
,
tree
)
setattr
(
self
,
block_info
.
field_name
,
block
.
location
)
setattr
(
self
,
block_info
.
field_name
,
block
.
location
)
with
self
.
store
.
bulk_write_operations
(
self
.
course
.
id
):
for
tree
in
trees
:
for
tree
in
trees
:
create_sub_tree
(
self
.
course
,
tree
)
create_sub_tree
(
self
.
course
,
tree
)
...
@@ -349,10 +351,9 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -349,10 +351,9 @@ class TestMixedModuleStore(unittest.TestCase):
)
)
# draft: 2 to look in draft and then published and then 5 for updating ancestors.
# draft: 2 to look in draft and then published and then 5 for updating ancestors.
# split: 3 to get the course structure & the course definition (show_calculator is scope content)
# split: 1 for the course index, 1 for the course structure before the change, 1 for the structure after the change
# before the change. 1 during change to refetch the definition. 3 afterward (b/c it calls get_item to return the "new" object).
# 2 sends to update index & structure (calculator is a setting field)
# 2 sends to update index & structure (calculator is a setting field)
@ddt.data
((
'draft'
,
7
,
5
),
(
'split'
,
6
,
2
))
@ddt.data
((
'draft'
,
7
,
5
),
(
'split'
,
3
,
2
))
@ddt.unpack
@ddt.unpack
def
test_update_item
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_update_item
(
self
,
default_ms
,
max_find
,
max_send
):
"""
"""
...
@@ -434,7 +435,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -434,7 +435,7 @@ class TestMixedModuleStore(unittest.TestCase):
component
=
self
.
store
.
publish
(
component
.
location
,
self
.
user_id
)
component
=
self
.
store
.
publish
(
component
.
location
,
self
.
user_id
)
self
.
assertFalse
(
self
.
store
.
has_changes
(
component
))
self
.
assertFalse
(
self
.
store
.
has_changes
(
component
))
@ddt.data
((
'draft'
,
7
,
2
),
(
'split'
,
13
,
4
))
@ddt.data
((
'draft'
,
7
,
2
),
(
'split'
,
2
,
4
))
@ddt.unpack
@ddt.unpack
def
test_delete_item
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_delete_item
(
self
,
default_ms
,
max_find
,
max_send
):
"""
"""
...
@@ -453,7 +454,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -453,7 +454,7 @@ class TestMixedModuleStore(unittest.TestCase):
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
writable_chapter_location
)
self
.
store
.
get_item
(
self
.
writable_chapter_location
)
@ddt.data
((
'draft'
,
8
,
2
),
(
'split'
,
13
,
4
))
@ddt.data
((
'draft'
,
8
,
2
),
(
'split'
,
2
,
4
))
@ddt.unpack
@ddt.unpack
def
test_delete_private_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_delete_private_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
"""
"""
...
@@ -499,7 +500,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -499,7 +500,7 @@ 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
)
@ddt.data
((
'draft'
,
4
,
1
),
(
'split'
,
5
,
2
))
@ddt.data
((
'draft'
,
4
,
1
),
(
'split'
,
1
,
2
))
@ddt.unpack
@ddt.unpack
def
test_delete_draft_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_delete_draft_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
"""
"""
...
@@ -579,7 +580,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -579,7 +580,7 @@ class TestMixedModuleStore(unittest.TestCase):
xml_store
.
create_course
(
"org"
,
"course"
,
"run"
,
self
.
user_id
)
xml_store
.
create_course
(
"org"
,
"course"
,
"run"
,
self
.
user_id
)
# draft is 2 to compute inheritance
# draft is 2 to compute inheritance
# split is 3
b/c it gets the definition to check whether wiki is set
# split is 3
(one for the index, one for the definition to check if the wiki is set, and one for the course structure
@ddt.data
((
'draft'
,
2
,
0
),
(
'split'
,
3
,
0
))
@ddt.data
((
'draft'
,
2
,
0
),
(
'split'
,
3
,
0
))
@ddt.unpack
@ddt.unpack
def
test_get_course
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_get_course
(
self
,
default_ms
,
max_find
,
max_send
):
...
@@ -884,7 +885,7 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -884,7 +885,7 @@ class TestMixedModuleStore(unittest.TestCase):
mongo_store
=
self
.
store
.
_get_modulestore_for_courseid
(
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
))
mongo_store
=
self
.
store
.
_get_modulestore_for_courseid
(
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
))
with
check_mongo_calls
(
mongo_store
,
max_find
,
max_send
):
with
check_mongo_calls
(
mongo_store
,
max_find
,
max_send
):
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
)
self
.
assert
Equal
(
set
(
found_orphans
),
set
(
orphan_locations
)
)
self
.
assert
ItemsEqual
(
found_orphans
,
orphan_locations
)
@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
):
...
@@ -953,7 +954,9 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -953,7 +954,9 @@ 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'
,
2
,
6
),
(
'split'
,
7
,
2
))
# Split takes 1 query to read the course structure, deletes all of the entries in memory, and loads the module from an in-memory cache
# Only writes the course structure back to the database once
@ddt.data
((
'draft'
,
2
,
6
),
(
'split'
,
1
,
1
))
@ddt.unpack
@ddt.unpack
def
test_unpublish
(
self
,
default_ms
,
max_find
,
max_send
):
def
test_unpublish
(
self
,
default_ms
,
max_find
,
max_send
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
View file @
562f0e31
...
@@ -2,12 +2,13 @@
...
@@ -2,12 +2,13 @@
Test split modulestore w/o using any django stuff.
Test split modulestore w/o using any django stuff.
"""
"""
import
datetime
import
datetime
import
random
import
re
import
unittest
import
unittest
import
uuid
import
uuid
from
importlib
import
import_module
from
importlib
import
import_module
from
mock
import
MagicMock
,
Mock
,
call
from
path
import
path
from
path
import
path
import
re
import
random
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreEnum
...
@@ -20,7 +21,8 @@ from opaque_keys.edx.locator import CourseLocator, BlockUsageLocator, VersionTre
...
@@ -20,7 +21,8 @@ from opaque_keys.edx.locator import CourseLocator, BlockUsageLocator, VersionTre
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.x_module
import
XModuleMixin
from
xmodule.x_module
import
XModuleMixin
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.modulestore.split_mongo.split
import
SplitMongoModuleStore
from
xmodule.modulestore.split_mongo.split
import
SplitMongoModuleStore
,
BulkWriteMixin
from
xmodule.modulestore.split_mongo.mongo_connection
import
MongoConnection
from
xmodule.modulestore.tests.test_modulestore
import
check_has_course_method
from
xmodule.modulestore.tests.test_modulestore
import
check_has_course_method
from
xmodule.modulestore.tests.mongo_connection
import
MONGO_PORT_NUM
,
MONGO_HOST
from
xmodule.modulestore.tests.mongo_connection
import
MONGO_PORT_NUM
,
MONGO_HOST
...
@@ -492,13 +494,14 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -492,13 +494,14 @@ class SplitModuleTest(unittest.TestCase):
new_ele_dict
[
spec
[
'id'
]]
=
child
new_ele_dict
[
spec
[
'id'
]]
=
child
course
=
split_store
.
persist_xblock_dag
(
course
,
revision
[
'user_id'
])
course
=
split_store
.
persist_xblock_dag
(
course
,
revision
[
'user_id'
])
# publish "testx.wonderful"
# publish "testx.wonderful"
source_course
=
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
to_publish
=
BlockUsageLocator
(
to_publish
=
BlockUsageLocator
(
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
,
source_course
,
block_type
=
'course'
,
block_type
=
'course'
,
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
.
copy
(
"test@edx.org"
,
to_publish
,
destination
,
[
to_publish
],
None
)
split_store
.
copy
(
"test@edx.org"
,
source_course
,
destination
,
[
to_publish
],
None
)
def
setUp
(
self
):
def
setUp
(
self
):
self
.
user_id
=
random
.
getrandbits
(
32
)
self
.
user_id
=
random
.
getrandbits
(
32
)
...
@@ -985,7 +988,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -985,7 +988,7 @@ class TestItemCrud(SplitModuleTest):
# 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
)
premod_course
=
modulestore
()
.
get_course
(
locator
)
premod_course
=
modulestore
()
.
get_course
(
locator
)
premod_history
=
modulestore
()
.
get_course_history_info
(
premod_course
.
location
)
premod_history
=
modulestore
()
.
get_course_history_info
(
locator
)
# 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
(
...
@@ -999,7 +1002,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -999,7 +1002,7 @@ class TestItemCrud(SplitModuleTest):
current_course
=
modulestore
()
.
get_course
(
locator
)
current_course
=
modulestore
()
.
get_course
(
locator
)
self
.
assertEqual
(
new_module
.
location
.
version_guid
,
current_course
.
location
.
version_guid
)
self
.
assertEqual
(
new_module
.
location
.
version_guid
,
current_course
.
location
.
version_guid
)
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
)
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
.
course_key
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
premod_course
.
location
.
version_guid
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
premod_course
.
location
.
version_guid
)
self
.
assertEqual
(
history_info
[
'original_version'
],
premod_history
[
'original_version'
])
self
.
assertEqual
(
history_info
[
'original_version'
],
premod_history
[
'original_version'
])
self
.
assertEqual
(
history_info
[
'edited_by'
],
"user123"
)
self
.
assertEqual
(
history_info
[
'edited_by'
],
"user123"
)
...
@@ -1112,15 +1115,17 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1112,15 +1115,17 @@ class TestItemCrud(SplitModuleTest):
chapter
=
modulestore
()
.
get_item
(
chapter_locator
)
chapter
=
modulestore
()
.
get_item
(
chapter_locator
)
self
.
assertIn
(
problem_locator
,
version_agnostic
(
chapter
.
children
))
self
.
assertIn
(
problem_locator
,
version_agnostic
(
chapter
.
children
))
def
test_create_
continue_version
(
self
):
def
test_create_
bulk_write_operations
(
self
):
"""
"""
Test create_item using
the continue_version flag
Test create_item using
bulk_write_operations
"""
"""
# start transaction w/ simple creation
# start transaction w/ simple creation
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
course_key
=
CourseLocator
(
'test_org'
,
'test_transaction'
,
'test_run'
)
with
modulestore
()
.
bulk_write_operations
(
course_key
):
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
'test_run'
,
user
,
BRANCH_NAME_DRAFT
)
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_key
)
course_block_prev_version
=
new_course
.
previous_version
course_block_prev_version
=
new_course
.
previous_version
course_block_update_version
=
new_course
.
update_version
course_block_update_version
=
new_course
.
update_version
self
.
assertIsNotNone
(
new_course_locator
.
version_guid
,
"Want to test a definite version"
)
self
.
assertIsNotNone
(
new_course_locator
.
version_guid
,
"Want to test a definite version"
)
...
@@ -1130,7 +1135,6 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1130,7 +1135,6 @@ class TestItemCrud(SplitModuleTest):
new_ele
=
modulestore
()
.
create_child
(
new_ele
=
modulestore
()
.
create_child
(
user
,
new_course
.
location
,
'chapter'
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 1'
},
fields
=
{
'display_name'
:
'chapter 1'
},
continue_version
=
True
)
)
# version info shouldn't change
# version info shouldn't change
self
.
assertEqual
(
new_ele
.
update_version
,
course_block_update_version
)
self
.
assertEqual
(
new_ele
.
update_version
,
course_block_update_version
)
...
@@ -1139,7 +1143,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1139,7 +1143,7 @@ class TestItemCrud(SplitModuleTest):
self
.
assertEqual
(
refetch_course
.
location
.
version_guid
,
new_course
.
location
.
version_guid
)
self
.
assertEqual
(
refetch_course
.
location
.
version_guid
,
new_course
.
location
.
version_guid
)
self
.
assertEqual
(
refetch_course
.
previous_version
,
course_block_prev_version
)
self
.
assertEqual
(
refetch_course
.
previous_version
,
course_block_prev_version
)
self
.
assertEqual
(
refetch_course
.
update_version
,
course_block_update_version
)
self
.
assertEqual
(
refetch_course
.
update_version
,
course_block_update_version
)
refetch_index_history_info
=
modulestore
()
.
get_course_history_info
(
refetch_course
.
location
)
refetch_index_history_info
=
modulestore
()
.
get_course_history_info
(
refetch_course
.
location
.
course_key
)
self
.
assertEqual
(
refetch_index_history_info
,
index_history_info
)
self
.
assertEqual
(
refetch_index_history_info
,
index_history_info
)
self
.
assertIn
(
new_ele
.
location
.
version_agnostic
(),
version_agnostic
(
refetch_course
.
children
))
self
.
assertIn
(
new_ele
.
location
.
version_agnostic
(),
version_agnostic
(
refetch_course
.
children
))
...
@@ -1149,14 +1153,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1149,14 +1153,13 @@ class TestItemCrud(SplitModuleTest):
user
,
new_course
.
location
,
'chapter'
,
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
)
)
# start a new transaction
# start a new transaction
with
modulestore
()
.
bulk_write_operations
(
course_key
):
new_ele
=
modulestore
()
.
create_child
(
new_ele
=
modulestore
()
.
create_child
(
user
,
new_course
.
location
,
'chapter'
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 2'
},
fields
=
{
'display_name'
:
'chapter 2'
},
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
...
@@ -1164,7 +1167,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1164,7 +1167,7 @@ class TestItemCrud(SplitModuleTest):
_fail
=
modulestore
()
.
create_child
(
_fail
=
modulestore
()
.
create_child
(
user
,
new_course
.
location
,
'chapter'
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 2'
},
fields
=
{
'display_name'
:
'chapter 2'
},
force
=
True
,
continue_version
=
True
force
=
True
)
)
# ensure trying to continue the old one gives exception
# ensure trying to continue the old one gives exception
...
@@ -1172,7 +1175,6 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1172,7 +1175,6 @@ class TestItemCrud(SplitModuleTest):
_fail
=
modulestore
()
.
create_child
(
_fail
=
modulestore
()
.
create_child
(
user
,
new_course
.
location
,
'chapter'
,
user
,
new_course
.
location
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 3'
},
fields
=
{
'display_name'
:
'chapter 3'
},
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)
...
@@ -1180,7 +1182,6 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1180,7 +1182,6 @@ class TestItemCrud(SplitModuleTest):
new_ele
=
modulestore
()
.
create_child
(
new_ele
=
modulestore
()
.
create_child
(
user
,
course_module_locator
,
'chapter'
,
user
,
course_module_locator
,
'chapter'
,
fields
=
{
'display_name'
:
'chapter 4'
},
fields
=
{
'display_name'
:
'chapter 4'
},
continue_version
=
True
)
)
self
.
assertNotEqual
(
new_ele
.
update_version
,
course_block_update_version
)
self
.
assertNotEqual
(
new_ele
.
update_version
,
course_block_update_version
)
self
.
assertEqual
(
new_ele
.
location
.
version_guid
,
transaction_guid
)
self
.
assertEqual
(
new_ele
.
location
.
version_guid
,
transaction_guid
)
...
@@ -1221,7 +1222,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1221,7 +1222,7 @@ class TestItemCrud(SplitModuleTest):
current_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
current_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
self
.
assertEqual
(
updated_problem
.
location
.
version_guid
,
current_course
.
location
.
version_guid
)
self
.
assertEqual
(
updated_problem
.
location
.
version_guid
,
current_course
.
location
.
version_guid
)
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
)
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
.
course_key
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
pre_version_guid
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
pre_version_guid
)
self
.
assertEqual
(
history_info
[
'edited_by'
],
self
.
user_id
)
self
.
assertEqual
(
history_info
[
'edited_by'
],
self
.
user_id
)
...
@@ -1396,16 +1397,13 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1396,16 +1397,13 @@ class TestCourseCreation(SplitModuleTest):
)
)
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
.
course_key
)
self
.
assertEqual
(
index_info
[
'org'
],
'test_org'
)
self
.
assertEqual
(
index_info
[
'org'
],
'test_org'
)
self
.
assertEqual
(
index_info
[
'edited_by'
],
'create_user'
)
self
.
assertEqual
(
index_info
[
'edited_by'
],
'create_user'
)
# check structure info
# check structure info
structure_info
=
modulestore
()
.
get_course_history_info
(
new_locator
)
structure_info
=
modulestore
()
.
get_course_history_info
(
new_locator
.
course_key
)
# TODO LMS-11098 "Implement bulk_write in Split"
self
.
assertEqual
(
structure_info
[
'original_version'
],
index_info
[
'versions'
][
BRANCH_NAME_DRAFT
])
# Right now, these assertions will not pass because create_course calls update_item,
self
.
assertIsNone
(
structure_info
[
'previous_version'
])
# resulting in two versions. Bulk updater will fix this.
# self.assertEqual(structure_info['original_version'], index_info['versions'][BRANCH_NAME_DRAFT])
# self.assertIsNone(structure_info['previous_version'])
self
.
assertEqual
(
structure_info
[
'edited_by'
],
'create_user'
)
self
.
assertEqual
(
structure_info
[
'edited_by'
],
'create_user'
)
# check the returned course object
# check the returned course object
...
@@ -1433,7 +1431,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1433,7 +1431,7 @@ class TestCourseCreation(SplitModuleTest):
self
.
assertEqual
(
new_draft
.
edited_by
,
'test@edx.org'
)
self
.
assertEqual
(
new_draft
.
edited_by
,
'test@edx.org'
)
self
.
assertEqual
(
new_draft_locator
.
version_guid
,
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
])
self
.
assertEqual
(
new_draft_locator
.
version_guid
,
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
])
# however the edited_by and other meta fields on course_index will be this one
# however the edited_by and other meta fields on course_index will be this one
new_index
=
modulestore
()
.
get_course_index_info
(
new_draft_locator
)
new_index
=
modulestore
()
.
get_course_index_info
(
new_draft_locator
.
course_key
)
self
.
assertEqual
(
new_index
[
'edited_by'
],
'leech_master'
)
self
.
assertEqual
(
new_index
[
'edited_by'
],
'leech_master'
)
new_published_locator
=
new_draft_locator
.
course_key
.
for_branch
(
BRANCH_NAME_PUBLISHED
)
new_published_locator
=
new_draft_locator
.
course_key
.
for_branch
(
BRANCH_NAME_PUBLISHED
)
...
@@ -1483,7 +1481,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1483,7 +1481,7 @@ class TestCourseCreation(SplitModuleTest):
self
.
assertEqual
(
new_draft
.
edited_by
,
'leech_master'
)
self
.
assertEqual
(
new_draft
.
edited_by
,
'leech_master'
)
self
.
assertNotEqual
(
new_draft_locator
.
version_guid
,
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
])
self
.
assertNotEqual
(
new_draft_locator
.
version_guid
,
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
])
# however the edited_by and other meta fields on course_index will be this one
# however the edited_by and other meta fields on course_index will be this one
new_index
=
modulestore
()
.
get_course_index_info
(
new_draft_locator
)
new_index
=
modulestore
()
.
get_course_index_info
(
new_draft_locator
.
course_key
)
self
.
assertEqual
(
new_index
[
'edited_by'
],
'leech_master'
)
self
.
assertEqual
(
new_index
[
'edited_by'
],
'leech_master'
)
self
.
assertEqual
(
new_draft
.
display_name
,
fields
[
'display_name'
])
self
.
assertEqual
(
new_draft
.
display_name
,
fields
[
'display_name'
])
self
.
assertDictEqual
(
self
.
assertDictEqual
(
...
@@ -1504,13 +1502,13 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1504,13 +1502,13 @@ class TestCourseCreation(SplitModuleTest):
head_course
=
modulestore
()
.
get_course
(
locator
)
head_course
=
modulestore
()
.
get_course
(
locator
)
versions
=
course_info
[
'versions'
]
versions
=
course_info
[
'versions'
]
versions
[
BRANCH_NAME_DRAFT
]
=
head_course
.
previous_version
versions
[
BRANCH_NAME_DRAFT
]
=
head_course
.
previous_version
modulestore
()
.
update_course_index
(
course_info
)
modulestore
()
.
update_course_index
(
None
,
course_info
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
self
.
assertEqual
(
course
.
location
.
version_guid
,
versions
[
BRANCH_NAME_DRAFT
])
self
.
assertEqual
(
course
.
location
.
version_guid
,
versions
[
BRANCH_NAME_DRAFT
])
# an allowed but not recommended way to publish a course
# an allowed but not recommended way to publish a course
versions
[
BRANCH_NAME_PUBLISHED
]
=
versions
[
BRANCH_NAME_DRAFT
]
versions
[
BRANCH_NAME_PUBLISHED
]
=
versions
[
BRANCH_NAME_DRAFT
]
modulestore
()
.
update_course_index
(
course_info
)
modulestore
()
.
update_course_index
(
None
,
course_info
)
course
=
modulestore
()
.
get_course
(
locator
.
for_branch
(
BRANCH_NAME_PUBLISHED
))
course
=
modulestore
()
.
get_course
(
locator
.
for_branch
(
BRANCH_NAME_PUBLISHED
))
self
.
assertEqual
(
course
.
location
.
version_guid
,
versions
[
BRANCH_NAME_DRAFT
])
self
.
assertEqual
(
course
.
location
.
version_guid
,
versions
[
BRANCH_NAME_DRAFT
])
...
@@ -1715,6 +1713,7 @@ class TestPublish(SplitModuleTest):
...
@@ -1715,6 +1713,7 @@ class TestPublish(SplitModuleTest):
dest_cursor
+=
1
dest_cursor
+=
1
self
.
assertEqual
(
dest_cursor
,
len
(
dest_children
))
self
.
assertEqual
(
dest_cursor
,
len
(
dest_children
))
class
TestSchema
(
SplitModuleTest
):
class
TestSchema
(
SplitModuleTest
):
"""
"""
Test the db schema (and possibly eventually migrations?)
Test the db schema (and possibly eventually migrations?)
...
@@ -1736,6 +1735,102 @@ class TestSchema(SplitModuleTest):
...
@@ -1736,6 +1735,102 @@ class TestSchema(SplitModuleTest):
"{0.name} has records with wrong schema_version"
.
format
(
collection
)
"{0.name} has records with wrong schema_version"
.
format
(
collection
)
)
)
class
TestBulkWriteMixin
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
bulk
=
BulkWriteMixin
()
self
.
clear_cache
=
self
.
bulk
.
_clear_cache
=
Mock
(
name
=
'_clear_cache'
)
self
.
conn
=
self
.
bulk
.
db_connection
=
MagicMock
(
name
=
'db_connection'
,
spec
=
MongoConnection
)
self
.
course_key
=
Mock
(
name
=
'course_key'
,
spec
=
CourseLocator
)
self
.
course_key_b
=
Mock
(
name
=
'course_key_b'
,
spec
=
CourseLocator
)
self
.
version_guid
=
Mock
(
name
=
'version_guid'
)
self
.
structure
=
MagicMock
(
name
=
'structure'
)
self
.
index_entry
=
MagicMock
(
name
=
'index_entry'
)
def
assertConnCalls
(
self
,
*
calls
):
self
.
assertEqual
(
list
(
calls
),
self
.
conn
.
mock_calls
)
def
assertCacheNotCleared
(
self
):
self
.
assertFalse
(
self
.
clear_cache
.
called
)
def
test_no_bulk_read_structure
(
self
):
# Reading a structure when no bulk operation is active should just call
# through to the db_connection
result
=
self
.
bulk
.
get_structure
(
self
.
course_key
,
self
.
version_guid
)
self
.
assertConnCalls
(
call
.
get_structure
(
self
.
course_key
.
as_object_id
.
return_value
))
self
.
assertEqual
(
result
,
self
.
conn
.
get_structure
.
return_value
)
self
.
assertCacheNotCleared
()
def
test_no_bulk_write_structure
(
self
):
# Writing a structure when no bulk operation is active should just
# call through to the db_connection. It should also clear the
# system cache
self
.
bulk
.
update_structure
(
self
.
course_key
,
self
.
structure
)
self
.
assertConnCalls
(
call
.
upsert_structure
(
self
.
structure
))
self
.
clear_cache
.
assert_called_once_with
(
self
.
structure
[
'_id'
])
def
test_no_bulk_read_index
(
self
):
# Reading a course index when no bulk operation is active should just call
# through to the db_connection
result
=
self
.
bulk
.
get_course_index
(
self
.
course_key
,
ignore_case
=
True
)
self
.
assertConnCalls
(
call
.
get_course_index
(
self
.
course_key
,
True
))
self
.
assertEqual
(
result
,
self
.
conn
.
get_course_index
.
return_value
)
self
.
assertCacheNotCleared
()
def
test_no_bulk_write_index
(
self
):
# Writing a course index when no bulk operation is active should just call
# through to the db_connection
self
.
bulk
.
insert_course_index
(
self
.
course_key
,
self
.
index_entry
)
self
.
assertConnCalls
(
call
.
insert_course_index
(
self
.
index_entry
))
self
.
assertCacheNotCleared
()
def
test_read_structure_without_write_from_db
(
self
):
# Reading a structure before it's been written (while in bulk operation mode)
# returns the structure from the database
self
.
bulk
.
_begin_bulk_write_operation
(
self
.
course_key
)
result
=
self
.
bulk
.
get_structure
(
self
.
course_key
,
self
.
version_guid
)
self
.
assertEquals
(
self
.
conn
.
get_structure
.
call_count
,
1
)
self
.
assertEqual
(
result
,
self
.
conn
.
get_structure
.
return_value
)
self
.
assertCacheNotCleared
()
def
test_read_structure_without_write_only_reads_once
(
self
):
# Reading the same structure multiple times shouldn't hit the database
# more than once
self
.
test_read_structure_without_write_from_db
()
result
=
self
.
bulk
.
get_structure
(
self
.
course_key
,
self
.
version_guid
)
self
.
assertEquals
(
self
.
conn
.
get_structure
.
call_count
,
1
)
self
.
assertEqual
(
result
,
self
.
conn
.
get_structure
.
return_value
)
self
.
assertCacheNotCleared
()
def
test_read_structure_after_write_no_db
(
self
):
# Reading a structure that's already been written shouldn't hit the db at all
self
.
bulk
.
_begin_bulk_write_operation
(
self
.
course_key
)
self
.
bulk
.
update_structure
(
self
.
course_key
,
self
.
structure
)
result
=
self
.
bulk
.
get_structure
(
self
.
course_key
,
self
.
version_guid
)
self
.
assertEquals
(
self
.
conn
.
get_structure
.
call_count
,
0
)
self
.
assertEqual
(
result
,
self
.
structure
)
def
test_read_structure_after_write_after_read
(
self
):
# Reading a structure that's been updated after being pulled from the db should
# still get the updated value
self
.
test_read_structure_without_write_only_reads_once
()
self
.
conn
.
get_structure
.
reset_mock
()
self
.
bulk
.
update_structure
(
self
.
course_key
,
self
.
structure
)
result
=
self
.
bulk
.
get_structure
(
self
.
course_key
,
self
.
version_guid
)
self
.
assertEquals
(
self
.
conn
.
get_structure
.
call_count
,
0
)
self
.
assertEqual
(
result
,
self
.
structure
)
# read index after close
# read structure after close
# write index on close
# write structure on close
# close with new index
# close with existing index
# read index after update
# read index without update
#===========================================
#===========================================
def
modulestore
():
def
modulestore
():
"""
"""
...
...
common/lib/xmodule/xmodule/tests/__init__.py
View file @
562f0e31
...
@@ -287,9 +287,9 @@ class CourseComparisonTest(unittest.TestCase):
...
@@ -287,9 +287,9 @@ class CourseComparisonTest(unittest.TestCase):
self
.
assertEqual
(
expected_item
.
has_children
,
actual_item
.
has_children
)
self
.
assertEqual
(
expected_item
.
has_children
,
actual_item
.
has_children
)
if
expected_item
.
has_children
:
if
expected_item
.
has_children
:
expected_children
=
[
expected_children
=
[
(
course1_item_child
.
location
.
block_type
,
course1
_item_child
.
location
.
block_id
)
(
expected_item_child
.
location
.
block_type
,
expected
_item_child
.
location
.
block_id
)
# get_children() rather than children to strip privates from public parents
# get_children() rather than children to strip privates from public parents
for
course1
_item_child
in
expected_item
.
get_children
()
for
expected
_item_child
in
expected_item
.
get_children
()
]
]
actual_children
=
[
actual_children
=
[
(
item_child
.
location
.
block_type
,
item_child
.
location
.
block_id
)
(
item_child
.
location
.
block_type
,
item_child
.
location
.
block_id
)
...
...
pavelib/tests.py
View file @
562f0e31
...
@@ -91,7 +91,10 @@ def test_lib(options):
...
@@ -91,7 +91,10 @@ def test_lib(options):
}
}
if
test_id
:
if
test_id
:
if
'/'
in
test_id
:
lib
=
'/'
.
join
(
test_id
.
split
(
'/'
)[
0
:
3
])
lib
=
'/'
.
join
(
test_id
.
split
(
'/'
)[
0
:
3
])
else
:
lib
=
'common/lib/'
+
test_id
.
split
(
'.'
)[
0
]
opts
[
'test_id'
]
=
test_id
opts
[
'test_id'
]
=
test_id
lib_tests
=
[
suites
.
LibTestSuite
(
lib
,
**
opts
)]
lib_tests
=
[
suites
.
LibTestSuite
(
lib
,
**
opts
)]
else
:
else
:
...
...
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