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
e39cc5df
Commit
e39cc5df
authored
Jul 02, 2014
by
Mat Peterson
Committed by
Diana Huang
Jul 11, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Switch offering to course and run
parent
78a29595
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
206 additions
and
158 deletions
+206
-158
cms/djangoapps/contentstore/tests/test_crud.py
+8
-7
common/lib/xmodule/xmodule/modulestore/__init__.py
+10
-7
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
+33
-24
common/lib/xmodule/xmodule/modulestore/mixed.py
+7
-3
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+7
-3
common/lib/xmodule/xmodule/modulestore/split_migrator.py
+4
-1
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
+5
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
+7
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+31
-22
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+5
-5
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
+2
-2
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
+4
-4
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
+79
-70
common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py
+3
-3
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+1
-1
No files found.
cms/djangoapps/contentstore/tests/test_crud.py
View file @
e39cc5df
...
@@ -57,14 +57,15 @@ class TemplateTests(unittest.TestCase):
...
@@ -57,14 +57,15 @@ class TemplateTests(unittest.TestCase):
def
test_factories
(
self
):
def
test_factories
(
self
):
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'tempcourse
'
,
org
=
'testx'
,
course
=
'course'
,
run
=
'2014
'
,
org
=
'testx'
,
display_name
=
'fun test course'
,
user_id
=
'testbot'
display_name
=
'fun test course'
,
user_id
=
'testbot'
)
)
self
.
assertIsInstance
(
test_course
,
CourseDescriptor
)
self
.
assertIsInstance
(
test_course
,
CourseDescriptor
)
self
.
assertEqual
(
test_course
.
display_name
,
'fun test course'
)
self
.
assertEqual
(
test_course
.
display_name
,
'fun test course'
)
index_info
=
self
.
split_store
.
get_course_index_info
(
test_course
.
id
)
index_info
=
self
.
split_store
.
get_course_index_info
(
test_course
.
id
)
self
.
assertEqual
(
index_info
[
'org'
],
'testx'
)
self
.
assertEqual
(
index_info
[
'org'
],
'testx'
)
self
.
assertEqual
(
index_info
[
'offering'
],
'tempcourse'
)
self
.
assertEqual
(
index_info
[
'course'
],
'course'
)
self
.
assertEqual
(
index_info
[
'run'
],
'2014'
)
test_chapter
=
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'chapter 1'
,
test_chapter
=
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'chapter 1'
,
parent_location
=
test_course
.
location
)
parent_location
=
test_course
.
location
)
...
@@ -75,7 +76,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -75,7 +76,7 @@ class TemplateTests(unittest.TestCase):
with
self
.
assertRaises
(
DuplicateCourseError
):
with
self
.
assertRaises
(
DuplicateCourseError
):
persistent_factories
.
PersistentCourseFactory
.
create
(
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'tempcourse
'
,
org
=
'testx'
,
course
=
'course'
,
run
=
'2014
'
,
org
=
'testx'
,
display_name
=
'fun test course'
,
user_id
=
'testbot'
display_name
=
'fun test course'
,
user_id
=
'testbot'
)
)
...
@@ -84,7 +85,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -84,7 +85,7 @@ class TemplateTests(unittest.TestCase):
Test create_xblock to create non persisted xblocks
Test create_xblock to create non persisted xblocks
"""
"""
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'tempcourse
'
,
org
=
'testx'
,
course
=
'course'
,
run
=
'2014
'
,
org
=
'testx'
,
display_name
=
'fun test course'
,
user_id
=
'testbot'
display_name
=
'fun test course'
,
user_id
=
'testbot'
)
)
...
@@ -111,7 +112,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -111,7 +112,7 @@ class TemplateTests(unittest.TestCase):
try saving temporary xblocks
try saving temporary xblocks
"""
"""
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'tempcourse
'
,
org
=
'testx'
,
course
=
'course'
,
run
=
'2014
'
,
org
=
'testx'
,
display_name
=
'fun test course'
,
user_id
=
'testbot'
display_name
=
'fun test course'
,
user_id
=
'testbot'
)
)
test_chapter
=
self
.
split_store
.
create_xblock
(
test_chapter
=
self
.
split_store
.
create_xblock
(
...
@@ -150,7 +151,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -150,7 +151,7 @@ class TemplateTests(unittest.TestCase):
def
test_delete_course
(
self
):
def
test_delete_course
(
self
):
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'history.
doomed'
,
org
=
'edu.harvard'
,
course
=
'history'
,
run
=
'
doomed'
,
org
=
'edu.harvard'
,
display_name
=
'doomed test course'
,
display_name
=
'doomed test course'
,
user_id
=
'testbot'
)
user_id
=
'testbot'
)
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'chapter 1'
,
persistent_factories
.
ItemFactory
.
create
(
display_name
=
'chapter 1'
,
...
@@ -173,7 +174,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -173,7 +174,7 @@ class TemplateTests(unittest.TestCase):
Test get_block_generations
Test get_block_generations
"""
"""
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
test_course
=
persistent_factories
.
PersistentCourseFactory
.
create
(
offering
=
'history.
hist101'
,
org
=
'edu.harvard'
,
course
=
'history'
,
run
=
'
hist101'
,
org
=
'edu.harvard'
,
display_name
=
'history test course'
,
display_name
=
'history test course'
,
user_id
=
'testbot'
user_id
=
'testbot'
)
)
...
...
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
e39cc5df
...
@@ -317,7 +317,7 @@ class ModuleStoreWrite(ModuleStoreRead):
...
@@ -317,7 +317,7 @@ class ModuleStoreWrite(ModuleStoreRead):
:param force: fork the structure and don't update the course draftVersion if there's a version
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org,
offering,
and version_guid given and the current
:raises VersionConflictError: if org,
course, run,
and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
"""
pass
pass
...
@@ -336,19 +336,20 @@ class ModuleStoreWrite(ModuleStoreRead):
...
@@ -336,19 +336,20 @@ class ModuleStoreWrite(ModuleStoreRead):
:param force: fork the structure and don't update the course draftVersion if there's a version
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org,
offering,
and version_guid given and the current
:raises VersionConflictError: if org,
course, run,
and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
"""
pass
pass
@abstractmethod
@abstractmethod
def
create_course
(
self
,
org
,
offering
,
user_id
,
fields
=
None
,
**
kwargs
):
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
fields
=
None
,
**
kwargs
):
"""
"""
Creates and returns the course.
Creates and returns the course.
Args:
Args:
org (str): the organization that owns the course
org (str): the organization that owns the course
offering (str): the name of the course offering
course (str): the name of the course
run (str): the name of the run
user_id: id of the user creating the course
user_id: id of the user creating the course
fields (dict): Fields to set on the course at initialization
fields (dict): Fields to set on the course at initialization
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
...
@@ -458,7 +459,9 @@ class ModuleStoreReadBase(ModuleStoreRead):
...
@@ -458,7 +459,9 @@ class ModuleStoreReadBase(ModuleStoreRead):
return
next
(
return
next
(
(
(
c
.
id
for
c
in
self
.
get_courses
()
c
.
id
for
c
in
self
.
get_courses
()
if
c
.
id
.
org
.
lower
()
==
course_id
.
org
.
lower
()
and
c
.
id
.
offering
.
lower
()
==
course_id
.
offering
.
lower
()
if
c
.
id
.
org
.
lower
()
==
course_id
.
org
.
lower
()
and
\
c
.
id
.
course
.
lower
()
==
course_id
.
course
.
lower
()
and
\
c
.
id
.
run
.
lower
()
==
course_id
.
run
.
lower
()
),
),
None
None
)
)
...
@@ -542,7 +545,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
...
@@ -542,7 +545,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
:param force: fork the structure and don't update the course draftVersion if there's a version
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org,
offering,
and version_guid given and the current
:raises VersionConflictError: if org,
course, run,
and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
"""
raise
NotImplementedError
raise
NotImplementedError
...
@@ -556,7 +559,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
...
@@ -556,7 +559,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
:param force: fork the structure and don't update the course draftVersion if there's a version
:param force: fork the structure and don't update the course draftVersion if there's a version
conflict (only applicable to version tracking and conflict detecting persistence stores)
conflict (only applicable to version tracking and conflict detecting persistence stores)
:raises VersionConflictError: if org,
offering,
and version_guid given and the current
:raises VersionConflictError: if org,
course, run,
and version_guid given and the current
version head != version_guid and force is not True. (only applicable to version tracking stores)
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
"""
raise
NotImplementedError
raise
NotImplementedError
...
...
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
View file @
e39cc5df
...
@@ -55,13 +55,13 @@ class LocMapperStore(object):
...
@@ -55,13 +55,13 @@ class LocMapperStore(object):
self
.
cache
=
cache
self
.
cache
=
cache
# location_map functions
# location_map functions
def
create_map_entry
(
self
,
course_key
,
org
=
None
,
offering
=
None
,
def
create_map_entry
(
self
,
course_key
,
org
=
None
,
course
=
None
,
run
=
None
,
draft_branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
draft_branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
prod_branch
=
ModuleStoreEnum
.
BranchName
.
published
,
prod_branch
=
ModuleStoreEnum
.
BranchName
.
published
,
block_map
=
None
):
block_map
=
None
):
"""
"""
Add a new entry to map this SlashSeparatedCourseKey to the new style CourseLocator.org &
offering
. If
Add a new entry to map this SlashSeparatedCourseKey to the new style CourseLocator.org &
course & run
. If
org and
offering
are not provided, it defaults them based on course_key.
org and
course and run
are not provided, it defaults them based on course_key.
WARNING: Exactly 1 CourseLocator key should index a given SlashSeparatedCourseKey.
WARNING: Exactly 1 CourseLocator key should index a given SlashSeparatedCourseKey.
We provide no mechanism to enforce this assertion.
We provide no mechanism to enforce this assertion.
...
@@ -71,7 +71,8 @@ class LocMapperStore(object):
...
@@ -71,7 +71,8 @@ class LocMapperStore(object):
:param course_key (SlashSeparatedCourseKey): a SlashSeparatedCourseKey
:param course_key (SlashSeparatedCourseKey): a SlashSeparatedCourseKey
:param org (string): the CourseLocator style org
:param org (string): the CourseLocator style org
:param offering (string): the CourseLocator offering
:param course (string): the CourseLocator course number
:param run (string): the CourseLocator run of this course
:param draft_branch: the branch name to assign for drafts. This is hardcoded because old mongo had
:param draft_branch: the branch name to assign for drafts. This is hardcoded because old mongo had
a fixed notion that there was 2 and only 2 versions for modules: draft and production. The old mongo
a fixed notion that there was 2 and only 2 versions for modules: draft and production. The old mongo
did not, however, require that a draft version exist. The new one, however, does require a draft to
did not, however, require that a draft version exist. The new one, however, does require a draft to
...
@@ -86,15 +87,16 @@ class LocMapperStore(object):
...
@@ -86,15 +87,16 @@ class LocMapperStore(object):
:class:`CourseLocator` representing the new id for the course
:class:`CourseLocator` representing the new id for the course
Raises:
Raises:
ValueError if one and only one of org and
offering is provided. Provide either both or neither
.
ValueError if one and only one of org and
course and run is provided. Provide all of them or none of them
.
"""
"""
if
org
is
None
and
offering
is
None
:
if
org
is
None
and
course
is
None
and
run
is
None
:
assert
(
isinstance
(
course_key
,
SlashSeparated
CourseKey
))
assert
(
isinstance
(
course_key
,
CourseKey
))
org
=
course_key
.
org
org
=
course_key
.
org
offering
=
u"{0.course}.{0.run}"
.
format
(
course_key
)
course
=
course_key
.
course
elif
org
is
None
or
offering
is
None
:
run
=
course_key
.
run
elif
org
is
None
or
course
is
None
or
run
is
None
:
raise
ValueError
(
raise
ValueError
(
u"Either supply
both org and offering or neither. Not just one: {}, {}"
.
format
(
org
,
offering
)
u"Either supply
org, course and run or none of them. Not just some of them: {}, {}, {}"
.
format
(
org
,
course
,
run
)
)
)
# very like _interpret_location_id but using mongo subdoc lookup (more performant)
# very like _interpret_location_id but using mongo subdoc lookup (more performant)
...
@@ -103,14 +105,15 @@ class LocMapperStore(object):
...
@@ -103,14 +105,15 @@ class LocMapperStore(object):
self
.
location_map
.
insert
({
self
.
location_map
.
insert
({
'_id'
:
course_son
,
'_id'
:
course_son
,
'org'
:
org
,
'org'
:
org
,
'offering'
:
offering
,
'course'
:
course
,
'run'
:
run
,
'draft_branch'
:
draft_branch
,
'draft_branch'
:
draft_branch
,
'prod_branch'
:
prod_branch
,
'prod_branch'
:
prod_branch
,
'block_map'
:
block_map
or
{},
'block_map'
:
block_map
or
{},
'schema'
:
self
.
SCHEMA_VERSION
,
'schema'
:
self
.
SCHEMA_VERSION
,
})
})
return
CourseLocator
(
org
,
offering
)
return
CourseLocator
(
org
,
course
,
run
)
def
translate_location
(
self
,
location
,
published
=
True
,
def
translate_location
(
self
,
location
,
published
=
True
,
add_entry_if_missing
=
True
,
passed_block_id
=
None
):
add_entry_if_missing
=
True
,
passed_block_id
=
None
):
...
@@ -177,7 +180,8 @@ class LocMapperStore(object):
...
@@ -177,7 +180,8 @@ class LocMapperStore(object):
prod_course_locator
=
CourseLocator
(
prod_course_locator
=
CourseLocator
(
org
=
entry
[
'org'
],
org
=
entry
[
'org'
],
offering
=
entry
[
'offering'
],
course
=
entry
[
'course'
],
run
=
entry
[
'run'
],
branch
=
entry
[
'prod_branch'
]
branch
=
entry
[
'prod_branch'
]
)
)
published_usage
=
BlockUsageLocator
(
published_usage
=
BlockUsageLocator
(
...
@@ -223,7 +227,7 @@ class LocMapperStore(object):
...
@@ -223,7 +227,7 @@ class LocMapperStore(object):
if
cached_value
:
if
cached_value
:
return
cached_value
return
cached_value
# migrate any records which don't have the org and
offering
fields as
# migrate any records which don't have the org and
course and run
fields as
# this won't be able to find what it wants. (only needs to be run once ever per db,
# this won't be able to find what it wants. (only needs to be run once ever per db,
# I'm not sure how to control that, but I'm putting some check here for once per launch)
# I'm not sure how to control that, but I'm putting some check here for once per launch)
if
not
getattr
(
self
,
'offering_migrated'
,
False
):
if
not
getattr
(
self
,
'offering_migrated'
,
False
):
...
@@ -235,7 +239,8 @@ class LocMapperStore(object):
...
@@ -235,7 +239,8 @@ class LocMapperStore(object):
entry
=
self
.
location_map
.
find_one
(
bson
.
son
.
SON
([
entry
=
self
.
location_map
.
find_one
(
bson
.
son
.
SON
([
(
'org'
,
locator
.
org
),
(
'org'
,
locator
.
org
),
(
'offering'
,
locator
.
offering
),
(
'course'
,
locator
.
course
),
(
'run'
,
locator
.
run
),
]))
]))
# look for one which maps to this block block_id
# look for one which maps to this block block_id
...
@@ -257,11 +262,14 @@ class LocMapperStore(object):
...
@@ -257,11 +262,14 @@ class LocMapperStore(object):
)
)
entry_org
=
"org"
entry_org
=
"org"
entry_offering
=
"offering"
entry_course
=
"course"
entry_run
=
"run"
published_locator
=
BlockUsageLocator
(
published_locator
=
BlockUsageLocator
(
CourseLocator
(
CourseLocator
(
org
=
entry
[
entry_org
],
offering
=
entry
[
entry_offering
],
org
=
entry
[
entry_org
],
course
=
entry
[
entry_course
],
run
=
entry
[
entry_run
],
branch
=
entry
[
'prod_branch'
]
branch
=
entry
[
'prod_branch'
]
),
),
block_type
=
category
,
block_type
=
category
,
...
@@ -269,7 +277,7 @@ class LocMapperStore(object):
...
@@ -269,7 +277,7 @@ class LocMapperStore(object):
)
)
draft_locator
=
BlockUsageLocator
(
draft_locator
=
BlockUsageLocator
(
CourseLocator
(
CourseLocator
(
org
=
entry
[
entry_org
],
offering
=
entry
[
entry_offering
],
org
=
entry
[
entry_org
],
course
=
entry
[
entry_course
],
run
=
entry
[
entry_run
],
branch
=
entry
[
'draft_branch'
]
branch
=
entry
[
'draft_branch'
]
),
),
block_type
=
category
,
block_type
=
category
,
...
@@ -303,10 +311,10 @@ class LocMapperStore(object):
...
@@ -303,10 +311,10 @@ class LocMapperStore(object):
raise
ItemNotFoundError
(
course_key
)
raise
ItemNotFoundError
(
course_key
)
published_course_locator
=
CourseLocator
(
published_course_locator
=
CourseLocator
(
org
=
entry
[
'org'
],
offering
=
entry
[
'offering
'
],
branch
=
entry
[
'prod_branch'
]
org
=
entry
[
'org'
],
course
=
entry
[
'course'
],
run
=
entry
[
'run
'
],
branch
=
entry
[
'prod_branch'
]
)
)
draft_course_locator
=
CourseLocator
(
draft_course_locator
=
CourseLocator
(
org
=
entry
[
'org'
],
offering
=
entry
[
'offering
'
],
branch
=
entry
[
'draft_branch'
]
org
=
entry
[
'org'
],
course
=
entry
[
'course'
],
run
=
entry
[
'run
'
],
branch
=
entry
[
'draft_branch'
]
)
)
self
.
_cache_course_locator
(
course_key
,
published_course_locator
,
draft_course_locator
)
self
.
_cache_course_locator
(
course_key
,
published_course_locator
,
draft_course_locator
)
if
published
:
if
published
:
...
@@ -441,7 +449,7 @@ class LocMapperStore(object):
...
@@ -441,7 +449,7 @@ class LocMapperStore(object):
"""
"""
Return the string used to cache the course key
Return the string used to cache the course key
"""
"""
return
u'{0.org}+{0.
offering
}'
.
format
(
course_key
)
return
u'{0.org}+{0.
course}+{0.run
}'
.
format
(
course_key
)
def
_cache_course_locator
(
self
,
old_course_id
,
published_course_locator
,
draft_course_locator
):
def
_cache_course_locator
(
self
,
old_course_id
,
published_course_locator
,
draft_course_locator
):
"""
"""
...
@@ -534,7 +542,7 @@ class LocMapperStore(object):
...
@@ -534,7 +542,7 @@ class LocMapperStore(object):
"""
"""
If entry had an '_id' without a run, remove the whole record.
If entry had an '_id' without a run, remove the whole record.
Add fields: schema, org,
offering
Add fields: schema, org,
course, run
Remove: course_id, lower_course_id
Remove: course_id, lower_course_id
:param entry:
:param entry:
"""
"""
...
@@ -547,13 +555,14 @@ class LocMapperStore(object):
...
@@ -547,13 +555,14 @@ class LocMapperStore(object):
self
.
location_map
.
remove
({
'_id'
:
entry_id
})
self
.
location_map
.
remove
({
'_id'
:
entry_id
})
return
None
return
None
# add schema, org,
offering
, etc, remove old fields
# add schema, org,
course, run
, etc, remove old fields
entry
[
'schema'
]
=
0
entry
[
'schema'
]
=
0
entry
.
pop
(
'course_id'
,
None
)
entry
.
pop
(
'course_id'
,
None
)
entry
.
pop
(
'lower_course_id'
,
None
)
entry
.
pop
(
'lower_course_id'
,
None
)
old_course_id
=
SlashSeparatedCourseKey
(
entry
[
'_id'
][
'org'
],
entry
[
'_id'
][
'course'
],
entry
[
'_id'
][
'name'
])
old_course_id
=
SlashSeparatedCourseKey
(
entry
[
'_id'
][
'org'
],
entry
[
'_id'
][
'course'
],
entry
[
'_id'
][
'name'
])
entry
[
'org'
]
=
old_course_id
.
org
entry
[
'org'
]
=
old_course_id
.
org
entry
[
'offering'
]
=
old_course_id
.
offering
.
replace
(
'/'
,
'+'
)
entry
[
'course'
]
=
old_course_id
.
course
entry
[
'run'
]
=
old_course_id
.
run
return
self
.
_migrate_1
(
entry
,
True
)
return
self
.
_migrate_1
(
entry
,
True
)
# insert new migrations just before _migrate_top. _migrate_top sets the schema version and
# insert new migrations just before _migrate_top. _migrate_top sets the schema version and
...
...
common/lib/xmodule/xmodule/modulestore/mixed.py
View file @
e39cc5df
...
@@ -273,13 +273,14 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -273,13 +273,14 @@ class MixedModuleStore(ModuleStoreWriteBase):
errs
.
update
(
store
.
get_errored_courses
())
errs
.
update
(
store
.
get_errored_courses
())
return
errs
return
errs
def
create_course
(
self
,
org
,
offering
,
user_id
,
fields
=
None
,
**
kwargs
):
def
create_course
(
self
,
org
,
course
,
run
,
user_id
,
fields
=
None
,
**
kwargs
):
"""
"""
Creates and returns the course.
Creates and returns the course.
Args:
Args:
org (str): the organization that owns the course
org (str): the organization that owns the course
offering (str): the name of the course offering
course (str): the name of the course
run (str): the name of the run
user_id: id of the user creating the course
user_id: id of the user creating the course
fields (dict): Fields to set on the course at initialization
fields (dict): Fields to set on the course at initialization
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
...
@@ -287,7 +288,10 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -287,7 +288,10 @@ class MixedModuleStore(ModuleStoreWriteBase):
Returns: a CourseDescriptor
Returns: a CourseDescriptor
"""
"""
store
=
self
.
_get_modulestore_for_courseid
(
None
)
store
=
self
.
_get_modulestore_for_courseid
(
None
)
return
store
.
create_course
(
org
,
offering
,
user_id
,
fields
,
**
kwargs
)
if
not
hasattr
(
store
,
'create_course'
):
raise
NotImplementedError
(
u"Cannot create a course on store {}"
.
format
(
store
))
return
store
.
create_course
(
org
,
course
,
run
,
user_id
,
fields
,
**
kwargs
)
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
def
clone_course
(
self
,
source_course_id
,
dest_course_id
,
user_id
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
e39cc5df
...
@@ -845,13 +845,18 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -845,13 +845,18 @@ class MongoModuleStore(ModuleStoreWriteBase):
modules
=
self
.
_load_items
(
course_id
,
list
(
items
))
modules
=
self
.
_load_items
(
course_id
,
list
(
items
))
return
modules
return
modules
<<<<<<<
HEAD
def
create_course
(
self
,
org
,
offering
,
user_id
,
fields
=
None
,
**
kwargs
):
def
create_course
(
self
,
org
,
offering
,
user_id
,
fields
=
None
,
**
kwargs
):
=======
def
create_course
(
self
,
org
,
course
,
run
,
user_id
=
None
,
fields
=
None
,
**
kwargs
):
>>>>>>>
Fix
up
the
signature
to
use
course
and
run
instead
of
just
offering
"""
"""
Creates and returns the course.
Creates and returns the course.
Args:
Args:
org (str): the organization that owns the course
org (str): the organization that owns the course
offering (str): the name of the course offering
course (str): the name of the course
run (str): the name of the run
user_id: id of the user creating the course
user_id: id of the user creating the course
fields (dict): Fields to set on the course at initialization
fields (dict): Fields to set on the course at initialization
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
...
@@ -859,9 +864,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -859,9 +864,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
Returns: a CourseDescriptor
Returns: a CourseDescriptor
Raises:
Raises:
InvalidLocationError: If a course with the same org
and offering
already exists
InvalidLocationError: If a course with the same org
, course, and run
already exists
"""
"""
course
,
_
,
run
=
offering
.
partition
(
'/'
)
course_id
=
SlashSeparatedCourseKey
(
org
,
course
,
run
)
course_id
=
SlashSeparatedCourseKey
(
org
,
course
,
run
)
# Check if a course with this org/course has been defined before (case-insensitive)
# Check if a course with this org/course has been defined before (case-insensitive)
...
...
common/lib/xmodule/xmodule/modulestore/split_migrator.py
View file @
e39cc5df
...
@@ -45,7 +45,10 @@ class SplitMigrator(object):
...
@@ -45,7 +45,10 @@ class SplitMigrator(object):
original_course
=
self
.
draft_modulestore
.
get_course
(
course_key
)
original_course
=
self
.
draft_modulestore
.
get_course
(
course_key
)
new_course_root_locator
=
self
.
loc_mapper
.
translate_location
(
original_course
.
location
)
new_course_root_locator
=
self
.
loc_mapper
.
translate_location
(
original_course
.
location
)
new_course
=
self
.
split_modulestore
.
create_course
(
new_course
=
self
.
split_modulestore
.
create_course
(
new_course_root_locator
.
org
,
new_course_root_locator
.
offering
,
user
.
id
,
new_course_root_locator
.
org
,
new_course_root_locator
.
course
,
new_course_root_locator
.
run
,
user
.
id
,
fields
=
self
.
_get_json_fields_translate_references
(
original_course
,
course_key
,
True
),
fields
=
self
.
_get_json_fields_translate_references
(
original_course
,
course_key
,
True
),
root_block_id
=
new_course_root_locator
.
block_id
,
root_block_id
=
new_course_root_locator
.
block_id
,
master_branch
=
new_course_root_locator
.
branch
master_branch
=
new_course_root_locator
.
branch
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
View file @
e39cc5df
...
@@ -68,7 +68,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -68,7 +68,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# deeper than initial descendant fetch or doesn't exist
# deeper than initial descendant fetch or doesn't exist
course_info
=
course_entry_override
or
self
.
course_entry
course_info
=
course_entry_override
or
self
.
course_entry
course_key
=
CourseLocator
(
course_key
=
CourseLocator
(
course_info
.
get
(
'org'
),
course_info
.
get
(
'
offering
'
),
course_info
.
get
(
'branch'
),
course_info
.
get
(
'org'
),
course_info
.
get
(
'
course'
),
course_info
.
get
(
'run
'
),
course_info
.
get
(
'branch'
),
course_info
[
'structure'
][
'_id'
]
course_info
[
'structure'
][
'_id'
]
)
)
self
.
modulestore
.
cache_items
(
self
,
[
block_id
],
course_key
,
lazy
=
self
.
lazy
)
self
.
modulestore
.
cache_items
(
self
,
[
block_id
],
course_key
,
lazy
=
self
.
lazy
)
...
@@ -97,7 +97,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -97,7 +97,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# most recent retrieval is most likely the right one for next caller (see comment above fn)
# most recent retrieval is most likely the right one for next caller (see comment above fn)
self
.
course_entry
[
'branch'
]
=
course_entry_override
[
'branch'
]
self
.
course_entry
[
'branch'
]
=
course_entry_override
[
'branch'
]
self
.
course_entry
[
'org'
]
=
course_entry_override
[
'org'
]
self
.
course_entry
[
'org'
]
=
course_entry_override
[
'org'
]
self
.
course_entry
[
'offering'
]
=
course_entry_override
[
'offering'
]
self
.
course_entry
[
'course'
]
=
course_entry_override
[
'course'
]
self
.
course_entry
[
'run'
]
=
course_entry_override
[
'run'
]
# most likely a lazy loader or the id directly
# most likely a lazy loader or the id directly
definition
=
json_data
.
get
(
'definition'
,
{})
definition
=
json_data
.
get
(
'definition'
,
{})
definition_id
=
self
.
modulestore
.
definition_locator
(
definition
)
definition_id
=
self
.
modulestore
.
definition_locator
(
definition
)
...
@@ -110,7 +111,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -110,7 +111,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
CourseLocator
(
CourseLocator
(
version_guid
=
course_entry_override
[
'structure'
][
'_id'
],
version_guid
=
course_entry_override
[
'structure'
][
'_id'
],
org
=
course_entry_override
.
get
(
'org'
),
org
=
course_entry_override
.
get
(
'org'
),
offering
=
course_entry_override
.
get
(
'offering'
),
course
=
course_entry_override
.
get
(
'course'
),
run
=
course_entry_override
.
get
(
'run'
),
branch
=
course_entry_override
.
get
(
'branch'
),
branch
=
course_entry_override
.
get
(
'branch'
),
),
),
block_type
=
json_data
.
get
(
'category'
),
block_type
=
json_data
.
get
(
'category'
),
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
View file @
e39cc5df
...
@@ -84,7 +84,7 @@ class MongoConnection(object):
...
@@ -84,7 +84,7 @@ class MongoConnection(object):
return
self
.
course_index
.
find_one
(
return
self
.
course_index
.
find_one
(
son
.
SON
([
son
.
SON
([
(
key_attr
,
re
.
compile
(
case_regex
.
format
(
getattr
(
key
,
key_attr
))))
(
key_attr
,
re
.
compile
(
case_regex
.
format
(
getattr
(
key
,
key_attr
))))
for
key_attr
in
(
'org'
,
'
offering
'
)
for
key_attr
in
(
'org'
,
'
course'
,
'run
'
)
])
])
)
)
...
@@ -106,7 +106,7 @@ class MongoConnection(object):
...
@@ -106,7 +106,7 @@ class MongoConnection(object):
Update the db record for course_index
Update the db record for course_index
"""
"""
self
.
course_index
.
update
(
self
.
course_index
.
update
(
son
.
SON
([(
'org'
,
course_index
[
'org'
]),
(
'
offering'
,
course_index
[
'offering
'
])]),
son
.
SON
([(
'org'
,
course_index
[
'org'
]),
(
'
course'
,
course_index
[
'course'
]),
(
'run'
,
course_index
[
'run
'
])]),
course_index
course_index
)
)
...
@@ -114,7 +114,11 @@ class MongoConnection(object):
...
@@ -114,7 +114,11 @@ class MongoConnection(object):
"""
"""
Delete the course_index from the persistence mechanism whose id is the given course_index
Delete the course_index from the persistence mechanism whose id is the given course_index
"""
"""
return
self
.
course_index
.
remove
(
son
.
SON
([(
'org'
,
course_index
[
'org'
]),
(
'offering'
,
course_index
[
'offering'
])]))
return
self
.
course_index
.
remove
(
son
.
SON
([
(
'org'
,
course_index
[
'org'
]),
(
'course'
,
course_index
[
'course'
]),
(
'run'
,
course_index
[
'run'
])
]))
def
get_definition
(
self
,
key
):
def
get_definition
(
self
,
key
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
e39cc5df
...
@@ -5,7 +5,8 @@ Representation:
...
@@ -5,7 +5,8 @@ Representation:
* course_index: a dictionary:
* course_index: a dictionary:
** '_id': a unique id which cannot change,
** '_id': a unique id which cannot change,
** 'org': the org's id. Only used for searching not identity,
** 'org': the org's id. Only used for searching not identity,
** 'offering': the course's catalog number and run id or whatever user decides,
** 'course': the course's catalog number
** 'run': the course's run id or whatever user decides,
** 'edited_by': user_id of user who created the original entry,
** 'edited_by': user_id of user who created the original entry,
** 'edited_on': the datetime of the original creation,
** 'edited_on': the datetime of the original creation,
** 'versions': versions_dict: {branch_id: structure_id, ...}
** 'versions': versions_dict: {branch_id: structure_id, ...}
...
@@ -215,7 +216,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -215,7 +216,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
course_key
=
CourseLocator
(
course_key
=
CourseLocator
(
version_guid
=
course_entry
[
'structure'
][
'_id'
],
version_guid
=
course_entry
[
'structure'
][
'_id'
],
org
=
course_entry
.
get
(
'org'
),
org
=
course_entry
.
get
(
'org'
),
offering
=
course_entry
.
get
(
'offering'
),
course
=
course_entry
.
get
(
'course'
),
run
=
course_entry
.
get
(
'run'
),
branch
=
course_entry
.
get
(
'branch'
),
branch
=
course_entry
.
get
(
'branch'
),
)
)
self
.
cache_items
(
system
,
block_ids
,
course_key
,
depth
,
lazy
)
self
.
cache_items
(
system
,
block_ids
,
course_key
,
depth
,
lazy
)
...
@@ -265,7 +267,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -265,7 +267,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param course_locator: any subclass of CourseLocator
:param course_locator: any subclass of CourseLocator
'''
'''
if
course_locator
.
org
and
course_locator
.
offering
and
course_locator
.
branch
:
if
course_locator
.
org
and
course_locator
.
course
and
course_locator
.
run
and
course_locator
.
branch
:
# use the course id
# use the course id
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
if
index
is
None
:
if
index
is
None
:
...
@@ -286,12 +288,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -286,12 +288,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
version_guid
=
course_locator
.
as_object_id
(
version_guid
)
version_guid
=
course_locator
.
as_object_id
(
version_guid
)
entry
=
self
.
db_connection
.
get_structure
(
version_guid
)
entry
=
self
.
db_connection
.
get_structure
(
version_guid
)
# b/c more than one course can use same structure, the 'org', 'offering', and 'branch' are not intrinsic to structure
# b/c more than one course can use same structure, the 'org', 'course',
# '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_locator
.
org
,
'offering'
:
course_locator
.
offering
,
'course'
:
course_locator
.
course
,
'run'
:
course_locator
.
run
,
'branch'
:
course_locator
.
branch
,
'branch'
:
course_locator
.
branch
,
'structure'
:
entry
,
'structure'
:
entry
,
}
}
...
@@ -331,7 +335,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -331,7 +335,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
course_info
=
id_version_map
[
entry
[
'_id'
]]
course_info
=
id_version_map
[
entry
[
'_id'
]]
envelope
=
{
envelope
=
{
'org'
:
course_info
[
'org'
],
'org'
:
course_info
[
'org'
],
'offering'
:
course_info
[
'offering'
],
'course'
:
course_info
[
'course'
],
'run'
:
course_info
[
'run'
],
'branch'
:
branch
,
'branch'
:
branch
,
'structure'
:
entry
,
'structure'
:
entry
,
}
}
...
@@ -362,7 +367,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -362,7 +367,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'''
'''
assert
(
isinstance
(
course_id
,
CourseLocator
))
assert
(
isinstance
(
course_id
,
CourseLocator
))
course_index
=
self
.
db_connection
.
get_course_index
(
course_id
,
ignore_case
)
course_index
=
self
.
db_connection
.
get_course_index
(
course_id
,
ignore_case
)
return
CourseLocator
(
course_index
[
'org'
],
course_index
[
'
offering
'
],
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
):
"""
"""
...
@@ -514,7 +519,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -514,7 +519,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
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
and offering
set
:param course_locator: 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,
...
@@ -523,7 +528,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -523,7 +528,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'edited_on': when the course was originally created
'edited_on': when the course was originally created
}
}
"""
"""
if
not
(
course_locator
.
offering
and
course_locator
.
org
):
if
not
(
course_locator
.
course
and
course_locator
.
run
and
course_locator
.
org
):
return
None
return
None
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
index
=
self
.
db_connection
.
get_course_index
(
course_locator
)
return
index
return
index
...
@@ -749,7 +754,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -749,7 +754,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param course_or_parent_locator: If BlockUsageLocator, then it's assumed to be the parent.
:param course_or_parent_locator: If BlockUsageLocator, then it's assumed to be the parent.
If it's a CourseLocator, then it's
If it's a CourseLocator, then it's
merely the containing course. If it has a version_guid and a course org +
offering
+ branch, this
merely the containing course. If it has a version_guid and a course org +
course + run
+ branch, this
method ensures that the version is the head of the given course branch before making the change.
method ensures that the version is the head of the given course branch before making the change.
raises InsufficientSpecificationError if there is no course locator.
raises InsufficientSpecificationError if there is no course locator.
...
@@ -779,11 +784,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -779,11 +784,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
Rules for course locator:
Rules for course locator:
* If the course locator specifies a org and
offering
and either it doesn't
* If the course locator specifies a org and
course and run
and either it doesn't
specify version_guid or the one it specifies == the current head of the branch,
specify version_guid or the one it specifies == the current head of the branch,
it progresses the course to point
it progresses the course to point
to the new head and sets the active version to point to the new head
to the new head and sets the active version to point to the new head
* If the locator has a org and
offering
but its version_guid != current head, it raises VersionConflictError.
* If the locator has a org and
course and run
but its version_guid != current head, it raises VersionConflictError.
NOTE: using a version_guid will end up creating a new version of the course. Your new item won't be in
NOTE: using a version_guid will end up creating a new version of the course. Your new item won't be in
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
...
@@ -886,7 +891,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -886,7 +891,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
)
)
def
create_course
(
def
create_course
(
self
,
org
,
offering
,
user_id
,
fields
=
None
,
self
,
org
,
course
,
run
,
user_id
,
fields
=
None
,
master_branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
versions_dict
=
None
,
root_category
=
'course'
,
master_branch
=
ModuleStoreEnum
.
BranchName
.
draft
,
versions_dict
=
None
,
root_category
=
'course'
,
root_block_id
=
'course'
,
**
kwargs
root_block_id
=
'course'
,
**
kwargs
):
):
...
@@ -897,12 +902,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -897,12 +902,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
Arguments:
Arguments:
org (str): the organization that owns the course
org (str): the organization that owns the course
offering (str): the name of the course offering
course (str): the course number of the course
run (str): the particular run of the course (e.g. 2013_T1)
user_id: id of the user creating the course
user_id: id of the user creating the course
fields (dict): Fields to set on the course at initialization
fields (dict): Fields to set on the course at initialization
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation
offering: If it's already taken
, this method will raise DuplicateCourseError
course + run: If there are duplicates
, this method will raise DuplicateCourseError
fields: if scope.settings fields provided, will set the fields of the root course object in the
fields: if scope.settings fields provided, will set the fields of the root course object in the
new course. If both
new course. If both
...
@@ -928,8 +934,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -928,8 +934,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
provide any fields overrides, see above). if not provided, will create a mostly empty course
provide any fields overrides, see above). if not provided, will create a mostly empty course
structure with just a category course root xblock.
structure with just a category course root xblock.
"""
"""
# check
offering
's uniqueness
# check
course and run
's uniqueness
locator
=
CourseLocator
(
org
=
org
,
offering
=
offering
,
branch
=
master_branch
)
locator
=
CourseLocator
(
org
=
org
,
course
=
course
,
run
=
run
,
branch
=
master_branch
)
index
=
self
.
db_connection
.
get_course_index
(
locator
)
index
=
self
.
db_connection
.
get_course_index
(
locator
)
if
index
is
not
None
:
if
index
is
not
None
:
raise
DuplicateCourseError
(
locator
,
index
)
raise
DuplicateCourseError
(
locator
,
index
)
...
@@ -1003,7 +1009,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1003,7 +1009,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
index_entry
=
{
index_entry
=
{
'_id'
:
ObjectId
(),
'_id'
:
ObjectId
(),
'org'
:
org
,
'org'
:
org
,
'offering'
:
offering
,
'course'
:
course
,
'run'
:
run
,
'edited_by'
:
user_id
,
'edited_by'
:
user_id
,
'edited_on'
:
datetime
.
datetime
.
now
(
UTC
),
'edited_on'
:
datetime
.
datetime
.
now
(
UTC
),
'versions'
:
versions_dict
,
'versions'
:
versions_dict
,
...
@@ -1019,7 +1026,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1019,7 +1026,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
raises ItemNotFoundError if the location does not exist.
raises ItemNotFoundError if the location does not exist.
Creates a new course version. If the descriptor's location has a org and
offering
, it moves the course head
Creates a new course version. If the descriptor's location has a org and
course and run
, it moves the course head
pointer. If the version_guid of the descriptor points to a non-head version and there's been an intervening
pointer. If the version_guid of the descriptor points to a non-head version and there's been an intervening
change to this item, it raises a VersionConflictError unless force is True. In the force case, it forks
change to this item, it raises a VersionConflictError unless force is True. In the force case, it forks
the course but leaves the head pointer where it is (this change will not be in the course head).
the course but leaves the head pointer where it is (this change will not be in the course head).
...
@@ -1067,7 +1074,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1067,7 +1074,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
index_entry
is
not
None
:
if
index_entry
is
not
None
:
self
.
_update_head
(
index_entry
,
descriptor
.
location
.
branch
,
new_id
)
self
.
_update_head
(
index_entry
,
descriptor
.
location
.
branch
,
new_id
)
course_key
=
CourseLocator
(
course_key
=
CourseLocator
(
org
=
index_entry
[
'org'
],
offering
=
index_entry
[
'offering'
],
org
=
index_entry
[
'org'
],
course
=
index_entry
[
'course'
],
run
=
index_entry
[
'run'
],
branch
=
descriptor
.
location
.
branch
,
branch
=
descriptor
.
location
.
branch
,
version_guid
=
new_id
version_guid
=
new_id
)
)
...
@@ -1336,7 +1345,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1336,7 +1345,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
raises ItemNotFoundError if the location does not exist.
raises ItemNotFoundError if the location does not exist.
raises ValueError if usage_locator points to the structure root
raises ValueError if usage_locator points to the structure root
Creates a new course version. If the descriptor's location has a org
and offering
, it moves the course head
Creates a new course version. If the descriptor's location has a org
, a course, and a run
, it moves the course head
pointer. If the version_guid of the descriptor points to a non-head version and there's been an intervening
pointer. If the version_guid of the descriptor points to a non-head version and there's been an intervening
change to this item, it raises a VersionConflictError unless force is True. In the force case, it forks
change to this item, it raises a VersionConflictError unless force is True. In the force case, it forks
the course but leaves the head pointer where it is (this change will not be in the course head).
the course but leaves the head pointer where it is (this change will not be in the course head).
...
@@ -1560,7 +1569,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1560,7 +1569,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param continue_version: if True, assumes this operation requires a head version and will not create a new
: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.
version but instead continue an existing transaction on this version. This flag cannot be True if force is True.
"""
"""
if
locator
.
org
is
None
or
locator
.
offering
is
None
or
locator
.
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
:
if
continue_version
:
raise
InsufficientSpecificationError
(
raise
InsufficientSpecificationError
(
"To continue a version, the locator must point to one ({})."
.
format
(
locator
)
"To continue a version, the locator must point to one ({})."
.
format
(
locator
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
e39cc5df
...
@@ -123,11 +123,7 @@ class TestMixedModuleStore(LocMapperSetupSansDjango):
...
@@ -123,11 +123,7 @@ class TestMixedModuleStore(LocMapperSetupSansDjango):
"""
"""
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.
"""
"""
if
default
==
'split'
:
course
=
self
.
store
.
create_course
(
course_key
.
org
,
course_key
.
course
,
course_key
.
run
,
self
.
user_id
)
offering
=
course_key
.
offering
.
replace
(
'/'
,
'.'
)
else
:
offering
=
course_key
.
offering
course
=
self
.
store
.
create_course
(
course_key
.
org
,
offering
,
self
.
user_id
)
category
=
self
.
writable_chapter_location
.
category
category
=
self
.
writable_chapter_location
.
category
block_id
=
self
.
writable_chapter_location
.
name
block_id
=
self
.
writable_chapter_location
.
name
chapter
=
self
.
store
.
create_item
(
chapter
=
self
.
store
.
create_item
(
...
@@ -367,7 +363,11 @@ class TestMixedModuleStore(LocMapperSetupSansDjango):
...
@@ -367,7 +363,11 @@ class TestMixedModuleStore(LocMapperSetupSansDjango):
xml_store
=
self
.
store
.
_get_modulestore_by_type
(
ModuleStoreEnum
.
Type
.
xml
)
xml_store
=
self
.
store
.
_get_modulestore_by_type
(
ModuleStoreEnum
.
Type
.
xml
)
# the important thing is not which exception it raises but that it raises an exception
# the important thing is not which exception it raises but that it raises an exception
with
self
.
assertRaises
(
AttributeError
):
with
self
.
assertRaises
(
AttributeError
):
<<<<<<<
HEAD
xml_store
.
create_course
(
"org"
,
"course/run"
,
self
.
user_id
)
xml_store
.
create_course
(
"org"
,
"course/run"
,
self
.
user_id
)
=======
xml_store
.
create_course
(
"org"
,
"course"
,
"run"
,
999
)
>>>>>>>
Fix
up
the
signature
to
use
course
and
run
instead
of
just
offering
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
'draft'
,
'split'
)
def
test_get_course
(
self
,
default_ms
):
def
test_get_course
(
self
,
default_ms
):
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
View file @
e39cc5df
...
@@ -31,9 +31,9 @@ class TestOrphan(SplitWMongoCourseBoostrapper):
...
@@ -31,9 +31,9 @@ class TestOrphan(SplitWMongoCourseBoostrapper):
"""
"""
orphans
=
self
.
old_mongo
.
get_orphans
(
self
.
old_course_key
)
orphans
=
self
.
old_mongo
.
get_orphans
(
self
.
old_course_key
)
self
.
assertEqual
(
len
(
orphans
),
3
,
"Wrong # {}"
.
format
(
orphans
))
self
.
assertEqual
(
len
(
orphans
),
3
,
"Wrong # {}"
.
format
(
orphans
))
location
=
self
.
old_course_key
.
make_usage_key
(
'chapter'
,
name
=
'OrphanChapter'
)
location
=
self
.
old_course_key
.
make_usage_key
(
'chapter'
,
'OrphanChapter'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
location
=
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
name
=
'OrphanVert'
)
location
=
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
'OrphanVert'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
location
=
self
.
old_course_key
.
make_usage_key
(
'html'
,
'OrphanHtml'
)
location
=
self
.
old_course_key
.
make_usage_key
(
'html'
,
'OrphanHtml'
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
self
.
assertIn
(
location
.
to_deprecated_string
(),
orphans
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
View file @
e39cc5df
...
@@ -63,7 +63,7 @@ class TestPublish(SplitWMongoCourseBoostrapper):
...
@@ -63,7 +63,7 @@ class TestPublish(SplitWMongoCourseBoostrapper):
To reproduce a bug (STUD-811) publish a vertical, convert to draft, delete a child, move a child, publish.
To reproduce a bug (STUD-811) publish a vertical, convert to draft, delete a child, move a child, publish.
See if deleted and moved children still is connected or exists in db (bug was disconnected but existed)
See if deleted and moved children still is connected or exists in db (bug was disconnected but existed)
"""
"""
vert_location
=
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
name
=
'Vert1'
)
vert_location
=
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
block_id
=
'Vert1'
)
item
=
self
.
draft_mongo
.
get_item
(
vert_location
,
2
)
item
=
self
.
draft_mongo
.
get_item
(
vert_location
,
2
)
# Vert1 has 3 children; so, publishes 4 nodes which may mean 4 inserts & 1 bulk remove
# Vert1 has 3 children; so, publishes 4 nodes which may mean 4 inserts & 1 bulk remove
# 25-June-2014 find calls are 19. Probably due to inheritance recomputation?
# 25-June-2014 find calls are 19. Probably due to inheritance recomputation?
...
@@ -78,16 +78,16 @@ class TestPublish(SplitWMongoCourseBoostrapper):
...
@@ -78,16 +78,16 @@ class TestPublish(SplitWMongoCourseBoostrapper):
# however, children are still draft, but I'm not sure that's by design
# however, children are still draft, but I'm not sure that's by design
# delete the draft version of the discussion
# delete the draft version of the discussion
location
=
self
.
old_course_key
.
make_usage_key
(
'discussion'
,
name
=
'Discussion1'
)
location
=
self
.
old_course_key
.
make_usage_key
(
'discussion'
,
block_id
=
'Discussion1'
)
self
.
draft_mongo
.
delete_item
(
location
,
self
.
userid
)
self
.
draft_mongo
.
delete_item
(
location
,
self
.
userid
)
draft_vert
=
self
.
draft_mongo
.
get_item
(
vert_location
,
0
)
draft_vert
=
self
.
draft_mongo
.
get_item
(
vert_location
,
0
)
self
.
assertTrue
(
getattr
(
draft_vert
,
'is_draft'
,
False
),
"Deletion didn't convert parent to draft"
)
self
.
assertTrue
(
getattr
(
draft_vert
,
'is_draft'
,
False
),
"Deletion didn't convert parent to draft"
)
self
.
assertNotIn
(
location
,
draft_vert
.
children
)
self
.
assertNotIn
(
location
,
draft_vert
.
children
)
# move the other child
# move the other child
other_child_loc
=
self
.
old_course_key
.
make_usage_key
(
'html'
,
name
=
'Html2'
)
other_child_loc
=
self
.
old_course_key
.
make_usage_key
(
'html'
,
block_id
=
'Html2'
)
draft_vert
.
children
.
remove
(
other_child_loc
)
draft_vert
.
children
.
remove
(
other_child_loc
)
other_vert
=
self
.
draft_mongo
.
get_item
(
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
name
=
'Vert2'
),
0
)
other_vert
=
self
.
draft_mongo
.
get_item
(
self
.
old_course_key
.
make_usage_key
(
'vertical'
,
block_id
=
'Vert2'
),
0
)
other_vert
.
children
.
append
(
other_child_loc
)
other_vert
.
children
.
append
(
other_child_loc
)
self
.
draft_mongo
.
update_item
(
draft_vert
,
self
.
userid
)
self
.
draft_mongo
.
update_item
(
draft_vert
,
self
.
userid
)
self
.
draft_mongo
.
update_item
(
other_vert
,
self
.
userid
)
self
.
draft_mongo
.
update_item
(
other_vert
,
self
.
userid
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
View file @
e39cc5df
...
@@ -61,7 +61,8 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -61,7 +61,8 @@ class SplitModuleTest(unittest.TestCase):
COURSE_CONTENT
=
{
COURSE_CONTENT
=
{
"testx.GreekHero"
:
{
"testx.GreekHero"
:
{
"org"
:
"testx"
,
"org"
:
"testx"
,
"offering"
:
"GreekHero"
,
"course"
:
"GreekHero"
,
"run"
:
"run"
,
"root_block_id"
:
"head12345"
,
"root_block_id"
:
"head12345"
,
"user_id"
:
"test@edx.org"
,
"user_id"
:
"test@edx.org"
,
"fields"
:
{
"fields"
:
{
...
@@ -281,7 +282,8 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -281,7 +282,8 @@ class SplitModuleTest(unittest.TestCase):
},
},
"testx.wonderful"
:
{
"testx.wonderful"
:
{
"org"
:
"testx"
,
"org"
:
"testx"
,
"offering"
:
"wonderful"
,
"course"
:
"wonderful"
,
"run"
:
"run"
,
"root_block_id"
:
"head23456"
,
"root_block_id"
:
"head23456"
,
"user_id"
:
"test@edx.org"
,
"user_id"
:
"test@edx.org"
,
"fields"
:
{
"fields"
:
{
...
@@ -387,7 +389,8 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -387,7 +389,8 @@ class SplitModuleTest(unittest.TestCase):
},
},
"guestx.contender"
:
{
"guestx.contender"
:
{
"org"
:
"guestx"
,
"org"
:
"guestx"
,
"offering"
:
"contender"
,
"course"
:
"contender"
,
"run"
:
"run"
,
"root_block_id"
:
"head345679"
,
"root_block_id"
:
"head345679"
,
"user_id"
:
"test@guestx.edu"
,
"user_id"
:
"test@guestx.edu"
,
"fields"
:
{
"fields"
:
{
...
@@ -450,7 +453,10 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -450,7 +453,10 @@ class SplitModuleTest(unittest.TestCase):
split_store
=
modulestore
()
split_store
=
modulestore
()
for
_course_id
,
course_spec
in
SplitModuleTest
.
COURSE_CONTENT
.
iteritems
():
for
_course_id
,
course_spec
in
SplitModuleTest
.
COURSE_CONTENT
.
iteritems
():
course
=
split_store
.
create_course
(
course
=
split_store
.
create_course
(
course_spec
[
'org'
],
course_spec
[
'offering'
],
course_spec
[
'user_id'
],
course_spec
[
'org'
],
course_spec
[
'course'
],
course_spec
[
'run'
],
course_spec
[
'user_id'
],
fields
=
course_spec
[
'fields'
],
fields
=
course_spec
[
'fields'
],
root_block_id
=
course_spec
[
'root_block_id'
]
root_block_id
=
course_spec
[
'root_block_id'
]
)
)
...
@@ -483,11 +489,11 @@ class SplitModuleTest(unittest.TestCase):
...
@@ -483,11 +489,11 @@ class SplitModuleTest(unittest.TestCase):
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"
to_publish
=
BlockUsageLocator
(
to_publish
=
BlockUsageLocator
(
CourseLocator
(
org
=
"testx"
,
offering
=
"wonderful
"
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
),
block_type
=
'course'
,
block_type
=
'course'
,
block_id
=
"head23456"
block_id
=
"head23456"
)
)
destination
=
CourseLocator
(
org
=
"testx"
,
offering
=
"wonderful
"
,
branch
=
BRANCH_NAME_PUBLISHED
)
destination
=
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_PUBLISHED
)
split_store
.
xblock_publish
(
"test@edx.org"
,
to_publish
,
destination
,
[
to_publish
],
None
)
split_store
.
xblock_publish
(
"test@edx.org"
,
to_publish
,
destination
,
[
to_publish
],
None
)
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -520,7 +526,7 @@ class TestHasChildrenAtDepth(SplitModuleTest):
...
@@ -520,7 +526,7 @@ class TestHasChildrenAtDepth(SplitModuleTest):
def
test_has_children_at_depth
(
self
):
def
test_has_children_at_depth
(
self
):
course_locator
=
CourseLocator
(
course_locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
)
block_locator
=
BlockUsageLocator
(
block_locator
=
BlockUsageLocator
(
course_locator
,
'course'
,
'head12345'
course_locator
,
'course'
,
'head12345'
...
@@ -586,7 +592,7 @@ class SplitModuleCourseTests(SplitModuleTest):
...
@@ -586,7 +592,7 @@ class SplitModuleCourseTests(SplitModuleTest):
course
=
self
.
findByIdInResult
(
courses_published
,
"head23456"
)
course
=
self
.
findByIdInResult
(
courses_published
,
"head23456"
)
self
.
assertIsNotNone
(
course
,
"published courses"
)
self
.
assertIsNotNone
(
course
,
"published courses"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
org
,
"testx"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
org
,
"testx"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
offering
,
"wonderful"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
course
,
"wonderful"
)
self
.
assertEqual
(
course
.
category
,
'course'
,
'wrong category'
)
self
.
assertEqual
(
course
.
category
,
'course'
,
'wrong category'
)
self
.
assertEqual
(
len
(
course
.
tabs
),
4
,
"wrong number of tabs"
)
self
.
assertEqual
(
len
(
course
.
tabs
),
4
,
"wrong number of tabs"
)
self
.
assertEqual
(
course
.
display_name
,
"The most wonderful course"
,
self
.
assertEqual
(
course
.
display_name
,
"The most wonderful course"
,
...
@@ -611,15 +617,15 @@ class SplitModuleCourseTests(SplitModuleTest):
...
@@ -611,15 +617,15 @@ class SplitModuleCourseTests(SplitModuleTest):
check_has_course_method
(
check_has_course_method
(
modulestore
(),
modulestore
(),
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
locator_key_fields
=
[
'org'
,
'
offering
'
]
locator_key_fields
=
[
'org'
,
'
course'
,
'run
'
]
)
)
def
test_get_course
(
self
):
def
test_get_course
(
self
):
'''
'''
Test the various calling forms for get_course
Test the various calling forms for get_course
'''
'''
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
head_course
=
modulestore
()
.
get_course
(
locator
)
head_course
=
modulestore
()
.
get_course
(
locator
)
self
.
assertNotEqual
(
head_course
.
location
.
version_guid
,
head_course
.
previous_version
)
self
.
assertNotEqual
(
head_course
.
location
.
version_guid
,
head_course
.
previous_version
)
locator
=
CourseLocator
(
version_guid
=
head_course
.
previous_version
)
locator
=
CourseLocator
(
version_guid
=
head_course
.
previous_version
)
...
@@ -637,10 +643,11 @@ class SplitModuleCourseTests(SplitModuleTest):
...
@@ -637,10 +643,11 @@ class SplitModuleCourseTests(SplitModuleTest):
self
.
assertEqual
(
course
.
edited_by
,
"testassist@edx.org"
)
self
.
assertEqual
(
course
.
edited_by
,
"testassist@edx.org"
)
self
.
assertDictEqual
(
course
.
grade_cutoffs
,
{
"Pass"
:
0.55
})
self
.
assertDictEqual
(
course
.
grade_cutoffs
,
{
"Pass"
:
0.55
})
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
self
.
assertEqual
(
course
.
location
.
course_key
.
org
,
"testx"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
org
,
"testx"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
offering
,
"GreekHero"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
course
,
"GreekHero"
)
self
.
assertEqual
(
course
.
location
.
course_key
.
run
,
"run"
)
self
.
assertEqual
(
course
.
category
,
'course'
)
self
.
assertEqual
(
course
.
category
,
'course'
)
self
.
assertEqual
(
len
(
course
.
tabs
),
6
)
self
.
assertEqual
(
len
(
course
.
tabs
),
6
)
self
.
assertEqual
(
course
.
display_name
,
"The Ancient Greek Hero"
)
self
.
assertEqual
(
course
.
display_name
,
"The Ancient Greek Hero"
)
...
@@ -650,28 +657,28 @@ class SplitModuleCourseTests(SplitModuleTest):
...
@@ -650,28 +657,28 @@ class SplitModuleCourseTests(SplitModuleTest):
self
.
assertEqual
(
course
.
edited_by
,
"testassist@edx.org"
)
self
.
assertEqual
(
course
.
edited_by
,
"testassist@edx.org"
)
self
.
assertDictEqual
(
course
.
grade_cutoffs
,
{
"Pass"
:
0.45
})
self
.
assertDictEqual
(
course
.
grade_cutoffs
,
{
"Pass"
:
0.45
})
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_PUBLISHED
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
published_version
=
course
.
location
.
version_guid
published_version
=
course
.
location
.
version_guid
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
self
.
assertNotEqual
(
course
.
location
.
version_guid
,
published_version
)
self
.
assertNotEqual
(
course
.
location
.
version_guid
,
published_version
)
def
test_get_course_negative
(
self
):
def
test_get_course_negative
(
self
):
# Now negative testing
# Now negative testing
with
self
.
assertRaises
(
InsufficientSpecificationError
):
with
self
.
assertRaises
(
InsufficientSpecificationError
):
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'edu'
,
offering
=
'meh.
blah'
))
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'edu'
,
course
=
'meh'
,
run
=
'
blah'
))
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'edu'
,
offering
=
'nosuchthing'
,
branch
=
BRANCH_NAME_DRAFT
))
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'edu'
,
course
=
'nosuchthing'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
))
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_PUBLISHED
))
modulestore
()
.
get_course
(
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
))
def
test_cache
(
self
):
def
test_cache
(
self
):
"""
"""
Test that the mechanics of caching work.
Test that the mechanics of caching work.
"""
"""
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
block_map
=
modulestore
()
.
cache_items
(
block_map
=
modulestore
()
.
cache_items
(
course
.
system
,
[
child
.
block_id
for
child
in
course
.
children
],
course
.
id
,
depth
=
3
course
.
system
,
[
child
.
block_id
for
child
in
course
.
children
],
course
.
id
,
depth
=
3
...
@@ -683,7 +690,7 @@ class SplitModuleCourseTests(SplitModuleTest):
...
@@ -683,7 +690,7 @@ class SplitModuleCourseTests(SplitModuleTest):
"""
"""
get_course_successors(course_locator, version_history_depth=1)
get_course_successors(course_locator, version_history_depth=1)
"""
"""
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
locator
)
course
=
modulestore
()
.
get_course
(
locator
)
versions
=
[
course
.
location
.
version_guid
,
course
.
previous_version
]
versions
=
[
course
.
location
.
version_guid
,
course
.
previous_version
]
locator
=
CourseLocator
(
version_guid
=
course
.
previous_version
)
locator
=
CourseLocator
(
version_guid
=
course
.
previous_version
)
...
@@ -720,8 +727,9 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -720,8 +727,9 @@ class SplitModuleItemTests(SplitModuleTest):
has_item(BlockUsageLocator)
has_item(BlockUsageLocator)
'''
'''
org
=
'testx'
org
=
'testx'
offering
=
'GreekHero'
course
=
'GreekHero'
course_locator
=
CourseLocator
(
org
=
org
,
offering
=
offering
,
branch
=
BRANCH_NAME_DRAFT
)
run
=
'run'
course_locator
=
CourseLocator
(
org
=
org
,
course
=
course
,
run
=
run
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
course_locator
)
course
=
modulestore
()
.
get_course
(
course_locator
)
previous_version
=
course
.
previous_version
previous_version
=
course
.
previous_version
# positive tests of various forms
# positive tests of various forms
...
@@ -754,7 +762,7 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -754,7 +762,7 @@ class SplitModuleItemTests(SplitModuleTest):
# in published course
# in published course
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
"testx"
,
offering
=
"wonderful
"
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
),
block_type
=
"course"
,
block_type
=
"course"
,
block_id
=
"head23456"
block_id
=
"head23456"
)
)
...
@@ -766,13 +774,13 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -766,13 +774,13 @@ class SplitModuleItemTests(SplitModuleTest):
# negative tests--not found
# negative tests--not found
# no such course or block
# no such course or block
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
"foo"
,
offering
=
"doesnotexist
"
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
"foo"
,
course
=
"doesnotexist"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
),
block_type
=
"course"
,
block_type
=
"course"
,
block_id
=
"head23456"
block_id
=
"head23456"
)
)
self
.
assertFalse
(
modulestore
()
.
has_item
(
locator
))
self
.
assertFalse
(
modulestore
()
.
has_item
(
locator
))
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
"testx"
,
offering
=
"wonderful
"
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
"testx"
,
course
=
"wonderful"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
),
block_type
=
"vertical"
,
block_type
=
"vertical"
,
block_id
=
"doesnotexist"
block_id
=
"doesnotexist"
)
)
...
@@ -782,7 +790,7 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -782,7 +790,7 @@ class SplitModuleItemTests(SplitModuleTest):
'''
'''
get_item(blocklocator)
get_item(blocklocator)
'''
'''
hero_locator
=
CourseLocator
(
org
=
"testx"
,
offering
=
"GreekHero
"
,
branch
=
BRANCH_NAME_DRAFT
)
hero_locator
=
CourseLocator
(
org
=
"testx"
,
course
=
"GreekHero"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
)
course
=
modulestore
()
.
get_course
(
hero_locator
)
course
=
modulestore
()
.
get_course
(
hero_locator
)
previous_version
=
course
.
previous_version
previous_version
=
course
.
previous_version
...
@@ -797,7 +805,8 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -797,7 +805,8 @@ class SplitModuleItemTests(SplitModuleTest):
Check contents of block
Check contents of block
"""
"""
self
.
assertEqual
(
block
.
location
.
org
,
"testx"
)
self
.
assertEqual
(
block
.
location
.
org
,
"testx"
)
self
.
assertEqual
(
block
.
location
.
offering
,
"GreekHero"
)
self
.
assertEqual
(
block
.
location
.
course
,
"GreekHero"
)
self
.
assertEqual
(
block
.
location
.
run
,
"run"
)
self
.
assertEqual
(
len
(
block
.
tabs
),
6
,
"wrong number of tabs"
)
self
.
assertEqual
(
len
(
block
.
tabs
),
6
,
"wrong number of tabs"
)
self
.
assertEqual
(
block
.
display_name
,
"The Ancient Greek Hero"
)
self
.
assertEqual
(
block
.
display_name
,
"The Ancient Greek Hero"
)
self
.
assertEqual
(
block
.
advertised_start
,
"Fall 2013"
)
self
.
assertEqual
(
block
.
advertised_start
,
"Fall 2013"
)
...
@@ -818,8 +827,8 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -818,8 +827,8 @@ class SplitModuleItemTests(SplitModuleTest):
"""
"""
Tests that has_changes() only returns true when changes are present
Tests that has_changes() only returns true when changes are present
"""
"""
draft_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
draft_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
published_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_PUBLISHED
)
published_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
head
=
draft_course
.
make_usage_key
(
'course'
,
'head12345'
)
head
=
draft_course
.
make_usage_key
(
'course'
,
'head12345'
)
dummy_user
=
ModuleStoreEnum
.
UserID
.
test
dummy_user
=
ModuleStoreEnum
.
UserID
.
test
...
@@ -843,18 +852,18 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -843,18 +852,18 @@ class SplitModuleItemTests(SplitModuleTest):
def
test_get_non_root
(
self
):
def
test_get_non_root
(
self
):
# not a course obj
# not a course obj
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
'chapter1'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
'chapter1'
)
)
block
=
modulestore
()
.
get_item
(
locator
)
block
=
modulestore
()
.
get_item
(
locator
)
self
.
assertEqual
(
block
.
location
.
org
,
"testx"
)
self
.
assertEqual
(
block
.
location
.
org
,
"testx"
)
self
.
assertEqual
(
block
.
location
.
offering
,
"GreekHero"
)
self
.
assertEqual
(
block
.
location
.
course
,
"GreekHero"
)
self
.
assertEqual
(
block
.
category
,
'chapter'
)
self
.
assertEqual
(
block
.
category
,
'chapter'
)
self
.
assertEqual
(
block
.
display_name
,
"Hercules"
)
self
.
assertEqual
(
block
.
display_name
,
"Hercules"
)
self
.
assertEqual
(
block
.
edited_by
,
"testassist@edx.org"
)
self
.
assertEqual
(
block
.
edited_by
,
"testassist@edx.org"
)
# in published course
# in published course
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_PUBLISHED
),
'course'
,
'head23456'
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
),
'course'
,
'head23456'
)
)
self
.
assertIsInstance
(
self
.
assertIsInstance
(
modulestore
()
.
get_item
(
locator
),
modulestore
()
.
get_item
(
locator
),
...
@@ -864,12 +873,12 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -864,12 +873,12 @@ class SplitModuleItemTests(SplitModuleTest):
# negative tests--not found
# negative tests--not found
# no such course or block
# no such course or block
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'doesnotexist'
,
offering
=
'doesnotexist'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head23456'
CourseLocator
(
org
=
'doesnotexist'
,
course
=
'doesnotexist'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head23456'
)
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
locator
)
modulestore
()
.
get_item
(
locator
)
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_DRAFT
),
'html'
,
'doesnotexist'
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'html'
,
'doesnotexist'
)
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
get_item
(
locator
)
modulestore
()
.
get_item
(
locator
)
...
@@ -903,7 +912,7 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -903,7 +912,7 @@ class SplitModuleItemTests(SplitModuleTest):
'''
'''
get_items(locator, qualifiers, [branch])
get_items(locator, qualifiers, [branch])
'''
'''
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
# get all modules
# get all modules
matches
=
modulestore
()
.
get_items
(
locator
)
matches
=
modulestore
()
.
get_items
(
locator
)
self
.
assertEqual
(
len
(
matches
),
6
)
self
.
assertEqual
(
len
(
matches
),
6
)
...
@@ -929,14 +938,14 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -929,14 +938,14 @@ class SplitModuleItemTests(SplitModuleTest):
get_parent_location(locator): BlockUsageLocator
get_parent_location(locator): BlockUsageLocator
'''
'''
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
block_id
=
'chapter1'
'chapter'
,
block_id
=
'chapter1'
)
)
parent
=
modulestore
()
.
get_parent_location
(
locator
)
parent
=
modulestore
()
.
get_parent_location
(
locator
)
self
.
assertIsNotNone
(
parent
)
self
.
assertIsNotNone
(
parent
)
self
.
assertEqual
(
parent
.
block_id
,
'head12345'
)
self
.
assertEqual
(
parent
.
block_id
,
'head12345'
)
self
.
assertEqual
(
parent
.
org
,
"testx"
)
self
.
assertEqual
(
parent
.
org
,
"testx"
)
self
.
assertEqual
(
parent
.
offering
,
"GreekHero"
)
self
.
assertEqual
(
parent
.
course
,
"GreekHero"
)
locator
=
locator
.
course_key
.
make_usage_key
(
'Chapter'
,
'chapter2'
)
locator
=
locator
.
course_key
.
make_usage_key
(
'Chapter'
,
'chapter2'
)
parent
=
modulestore
()
.
get_parent_location
(
locator
)
parent
=
modulestore
()
.
get_parent_location
(
locator
)
self
.
assertEqual
(
parent
.
block_id
,
'head12345'
)
self
.
assertEqual
(
parent
.
block_id
,
'head12345'
)
...
@@ -949,7 +958,7 @@ class SplitModuleItemTests(SplitModuleTest):
...
@@ -949,7 +958,7 @@ class SplitModuleItemTests(SplitModuleTest):
Test the existing get_children method on xdescriptors
Test the existing get_children method on xdescriptors
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head12345'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head12345'
)
)
block
=
modulestore
()
.
get_item
(
locator
)
block
=
modulestore
()
.
get_item
(
locator
)
children
=
block
.
get_children
()
children
=
block
.
get_children
()
...
@@ -996,7 +1005,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -996,7 +1005,7 @@ class TestItemCrud(SplitModuleTest):
create_item(course_or_parent_locator, category, user, definition_locator=None, fields): new_desciptor
create_item(course_or_parent_locator, category, user, definition_locator=None, fields): new_desciptor
"""
"""
# grab link to course to ensure new versioning works
# grab link to course to ensure new versioning works
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
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
(
premod_course
.
location
)
# add minimal one w/o a parent
# add minimal one w/o a parent
...
@@ -1006,7 +1015,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1006,7 +1015,7 @@ class TestItemCrud(SplitModuleTest):
fields
=
{
'display_name'
:
'new sequential'
}
fields
=
{
'display_name'
:
'new sequential'
}
)
)
# check that course version changed and course's previous is the other one
# check that course version changed and course's previous is the other one
self
.
assertEqual
(
new_module
.
location
.
offering
,
"GreekHero"
)
self
.
assertEqual
(
new_module
.
location
.
course
,
"GreekHero"
)
self
.
assertNotEqual
(
new_module
.
location
.
version_guid
,
premod_course
.
location
.
version_guid
)
self
.
assertNotEqual
(
new_module
.
location
.
version_guid
,
premod_course
.
location
.
version_guid
)
self
.
assertIsNone
(
locator
.
version_guid
,
"Version inadvertently filled in"
)
self
.
assertIsNone
(
locator
.
version_guid
,
"Version inadvertently filled in"
)
current_course
=
modulestore
()
.
get_course
(
locator
)
current_course
=
modulestore
()
.
get_course
(
locator
)
...
@@ -1032,13 +1041,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1032,13 +1041,13 @@ class TestItemCrud(SplitModuleTest):
Test create_item w/ specifying the parent of the new item
Test create_item w/ specifying the parent of the new item
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
block_id
=
'chapter2'
'chapter'
,
block_id
=
'chapter2'
)
)
original
=
modulestore
()
.
get_item
(
locator
)
original
=
modulestore
()
.
get_item
(
locator
)
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head23456'
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head23456'
)
)
premod_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
premod_course
=
modulestore
()
.
get_course
(
locator
.
course_key
)
category
=
'chapter'
category
=
'chapter'
...
@@ -1061,13 +1070,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1061,13 +1070,13 @@ class TestItemCrud(SplitModuleTest):
Actually, this tries to test all create_item features not tested above.
Actually, this tries to test all create_item features not tested above.
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
block_id
=
'problem1'
'problem'
,
block_id
=
'problem1'
)
)
original
=
modulestore
()
.
get_item
(
locator
)
original
=
modulestore
()
.
get_item
(
locator
)
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'guestx'
,
offering
=
'contender'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head345679'
CourseLocator
(
org
=
'guestx'
,
course
=
'contender'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head345679'
)
)
category
=
'problem'
category
=
'problem'
new_payload
=
"<problem>empty</problem>"
new_payload
=
"<problem>empty</problem>"
...
@@ -1100,7 +1109,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1100,7 +1109,7 @@ class TestItemCrud(SplitModuleTest):
"""
"""
Check that using odd characters in block id don't break ability to add and retrieve block.
Check that using odd characters in block id don't break ability to add and retrieve block.
"""
"""
course_key
=
CourseLocator
(
org
=
'guestx'
,
offering
=
'contender'
,
branch
=
BRANCH_NAME_DRAFT
)
course_key
=
CourseLocator
(
org
=
'guestx'
,
course
=
'contender'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
parent_locator
=
BlockUsageLocator
(
course_key
,
'course'
,
block_id
=
"head345679"
)
parent_locator
=
BlockUsageLocator
(
course_key
,
'course'
,
block_id
=
"head345679"
)
chapter_locator
=
BlockUsageLocator
(
course_key
,
'chapter'
,
block_id
=
"foo.bar_-~:0"
)
chapter_locator
=
BlockUsageLocator
(
course_key
,
'chapter'
,
block_id
=
"foo.bar_-~:0"
)
modulestore
()
.
create_item
(
modulestore
()
.
create_item
(
...
@@ -1131,7 +1140,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1131,7 +1140,7 @@ class TestItemCrud(SplitModuleTest):
"""
"""
# start transaction w/ simple creation
# start transaction w/ simple creation
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
user
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
'test_run'
,
user
)
new_course_locator
=
new_course
.
id
new_course_locator
=
new_course
.
id
index_history_info
=
modulestore
()
.
get_course_history_info
(
new_course
.
location
)
index_history_info
=
modulestore
()
.
get_course_history_info
(
new_course
.
location
)
course_block_prev_version
=
new_course
.
previous_version
course_block_prev_version
=
new_course
.
previous_version
...
@@ -1209,7 +1218,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1209,7 +1218,7 @@ class TestItemCrud(SplitModuleTest):
test updating an items metadata ensuring the definition doesn't version but the course does if it should
test updating an items metadata ensuring the definition doesn't version but the course does if it should
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
"testx"
,
offering
=
"GreekHero
"
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
org
=
"testx"
,
course
=
"GreekHero"
,
run
=
"run
"
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
block_id
=
"problem3_2"
'problem'
,
block_id
=
"problem3_2"
)
)
problem
=
modulestore
()
.
get_item
(
locator
)
problem
=
modulestore
()
.
get_item
(
locator
)
...
@@ -1243,7 +1252,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1243,7 +1252,7 @@ class TestItemCrud(SplitModuleTest):
test updating an item's children ensuring the definition doesn't version but the course does if it should
test updating an item's children ensuring the definition doesn't version but the course does if it should
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
'chapter3'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'chapter'
,
'chapter3'
)
)
block
=
modulestore
()
.
get_item
(
locator
)
block
=
modulestore
()
.
get_item
(
locator
)
pre_def_id
=
block
.
definition_locator
.
definition_id
pre_def_id
=
block
.
definition_locator
.
definition_id
...
@@ -1270,7 +1279,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1270,7 +1279,7 @@ class TestItemCrud(SplitModuleTest):
test updating an item's definition: ensure it gets versioned as well as the course getting versioned
test updating an item's definition: ensure it gets versioned as well as the course getting versioned
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head12345'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
'head12345'
)
)
block
=
modulestore
()
.
get_item
(
locator
)
block
=
modulestore
()
.
get_item
(
locator
)
pre_def_id
=
block
.
definition_locator
.
definition_id
pre_def_id
=
block
.
definition_locator
.
definition_id
...
@@ -1289,13 +1298,13 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1289,13 +1298,13 @@ class TestItemCrud(SplitModuleTest):
Test updating metadata, children, and definition in a single call ensuring all the versioning occurs
Test updating metadata, children, and definition in a single call ensuring all the versioning occurs
"""
"""
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
'testx'
,
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
'testx'
,
'GreekHero'
,
'run'
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
block_id
=
'problem1'
'problem'
,
block_id
=
'problem1'
)
)
original
=
modulestore
()
.
get_item
(
locator
)
original
=
modulestore
()
.
get_item
(
locator
)
# first add 2 children to the course for the update to manipulate
# first add 2 children to the course for the update to manipulate
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
'guestx'
,
'contender'
,
branch
=
BRANCH_NAME_DRAFT
),
CourseLocator
(
'guestx'
,
'contender'
,
'run'
,
branch
=
BRANCH_NAME_DRAFT
),
'course'
,
block_id
=
"head345679"
'course'
,
block_id
=
"head345679"
)
)
category
=
'problem'
category
=
'problem'
...
@@ -1373,7 +1382,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -1373,7 +1382,7 @@ class TestItemCrud(SplitModuleTest):
"""
"""
Create a course we can delete
Create a course we can delete
"""
"""
course
=
modulestore
()
.
create_course
(
'nihilx'
,
'deletion'
,
'deleting_user'
)
course
=
modulestore
()
.
create_course
(
'nihilx'
,
'deletion'
,
'
run'
,
'
deleting_user'
)
root
=
course
.
location
.
version_agnostic
()
.
for_branch
(
BRANCH_NAME_DRAFT
)
root
=
course
.
location
.
version_agnostic
()
.
for_branch
(
BRANCH_NAME_DRAFT
)
for
_
in
range
(
4
):
for
_
in
range
(
4
):
self
.
create_subtree_for_deletion
(
root
,
[
'chapter'
,
'vertical'
,
'problem'
])
self
.
create_subtree_for_deletion
(
root
,
[
'chapter'
,
'vertical'
,
'problem'
])
...
@@ -1400,7 +1409,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1400,7 +1409,7 @@ class TestCourseCreation(SplitModuleTest):
The simplest case but probing all expected results from it.
The simplest case but probing all expected results from it.
"""
"""
# Oddly getting differences of 200nsec
# Oddly getting differences of 200nsec
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_course'
,
'create_user'
)
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_course'
,
'
test_run'
,
'
create_user'
)
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
)
...
@@ -1425,10 +1434,10 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1425,10 +1434,10 @@ class TestCourseCreation(SplitModuleTest):
"""
"""
Test making a course which points to an existing draft and published but not making any changes to either.
Test making a course which points to an existing draft and published but not making any changes to either.
"""
"""
original_locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'wonderful'
,
branch
=
BRANCH_NAME_DRAFT
)
original_locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'wonderful'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
new_draft
=
modulestore
()
.
create_course
(
new_draft
=
modulestore
()
.
create_course
(
'best'
,
'leech'
,
'leech_master'
,
'best'
,
'leech'
,
'leech_
run'
,
'leech_
master'
,
versions_dict
=
original_index
[
'versions'
])
versions_dict
=
original_index
[
'versions'
])
new_draft_locator
=
new_draft
.
location
new_draft_locator
=
new_draft
.
location
self
.
assertRegexpMatches
(
new_draft_locator
.
org
,
'best'
)
self
.
assertRegexpMatches
(
new_draft_locator
.
org
,
'best'
)
...
@@ -1467,7 +1476,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1467,7 +1476,7 @@ class TestCourseCreation(SplitModuleTest):
"""
"""
Create a new course which overrides metadata and course_data
Create a new course which overrides metadata and course_data
"""
"""
original_locator
=
CourseLocator
(
org
=
'guestx'
,
offering
=
'contender'
,
branch
=
BRANCH_NAME_DRAFT
)
original_locator
=
CourseLocator
(
org
=
'guestx'
,
course
=
'contender'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
original
=
modulestore
()
.
get_course
(
original_locator
)
original
=
modulestore
()
.
get_course
(
original_locator
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
original_index
=
modulestore
()
.
get_course_index_info
(
original_locator
)
fields
=
{}
fields
=
{}
...
@@ -1484,7 +1493,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1484,7 +1493,7 @@ class TestCourseCreation(SplitModuleTest):
fields
[
'grading_policy'
][
'GRADE_CUTOFFS'
]
=
{
'A'
:
.
9
,
'B'
:
.
8
,
'C'
:
.
65
}
fields
[
'grading_policy'
][
'GRADE_CUTOFFS'
]
=
{
'A'
:
.
9
,
'B'
:
.
8
,
'C'
:
.
65
}
fields
[
'display_name'
]
=
'Derivative'
fields
[
'display_name'
]
=
'Derivative'
new_draft
=
modulestore
()
.
create_course
(
new_draft
=
modulestore
()
.
create_course
(
'counter'
,
'leech'
,
'leech_master'
,
'counter'
,
'leech'
,
'leech_
run'
,
'leech_
master'
,
versions_dict
=
{
BRANCH_NAME_DRAFT
:
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
]},
versions_dict
=
{
BRANCH_NAME_DRAFT
:
original_index
[
'versions'
][
BRANCH_NAME_DRAFT
]},
fields
=
fields
fields
=
fields
)
)
...
@@ -1504,11 +1513,11 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1504,11 +1513,11 @@ class TestCourseCreation(SplitModuleTest):
def
test_update_course_index
(
self
):
def
test_update_course_index
(
self
):
"""
"""
Test the versions pointers. NOTE: you can change the org,
offering
, or other things, but
Test the versions pointers. NOTE: you can change the org,
course
, or other things, but
it's not clear how you'd find them again or associate them w/ existing student history since
it's not clear how you'd find them again or associate them w/ existing student history since
we use course_key so many places as immutable.
we use course_key so many places as immutable.
"""
"""
locator
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
locator
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
course_info
=
modulestore
()
.
get_course_index_info
(
locator
)
course_info
=
modulestore
()
.
get_course_index_info
(
locator
)
# an allowed but not necessarily recommended way to revert the draft version
# an allowed but not necessarily recommended way to revert the draft version
...
@@ -1531,7 +1540,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1531,7 +1540,7 @@ class TestCourseCreation(SplitModuleTest):
"""
"""
user
=
random
.
getrandbits
(
32
)
user
=
random
.
getrandbits
(
32
)
new_course
=
modulestore
()
.
create_course
(
new_course
=
modulestore
()
.
create_course
(
'test_org'
,
'test_transaction'
,
user
,
'test_org'
,
'test_transaction'
,
'test_run'
,
user
,
root_block_id
=
'top'
,
root_category
=
'chapter'
root_block_id
=
'top'
,
root_category
=
'chapter'
)
)
self
.
assertEqual
(
new_course
.
location
.
block_id
,
'top'
)
self
.
assertEqual
(
new_course
.
location
.
block_id
,
'top'
)
...
@@ -1553,7 +1562,7 @@ class TestCourseCreation(SplitModuleTest):
...
@@ -1553,7 +1562,7 @@ class TestCourseCreation(SplitModuleTest):
courses
=
modulestore
()
.
get_courses
()
courses
=
modulestore
()
.
get_courses
()
with
self
.
assertRaises
(
DuplicateCourseError
):
with
self
.
assertRaises
(
DuplicateCourseError
):
dupe_course_key
=
courses
[
0
]
.
location
.
course_key
dupe_course_key
=
courses
[
0
]
.
location
.
course_key
modulestore
()
.
create_course
(
dupe_course_key
.
org
,
dupe_course_key
.
offering
,
user
)
modulestore
()
.
create_course
(
dupe_course_key
.
org
,
dupe_course_key
.
course
,
dupe_course_key
.
run
,
user
)
class
TestInheritance
(
SplitModuleTest
):
class
TestInheritance
(
SplitModuleTest
):
...
@@ -1567,13 +1576,13 @@ class TestInheritance(SplitModuleTest):
...
@@ -1567,13 +1576,13 @@ class TestInheritance(SplitModuleTest):
# Note, not testing value where defined (course) b/c there's no
# Note, not testing value where defined (course) b/c there's no
# defined accessor for it on CourseDescriptor.
# defined accessor for it on CourseDescriptor.
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
'problem3_2'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
'problem3_2'
)
)
node
=
modulestore
()
.
get_item
(
locator
)
node
=
modulestore
()
.
get_item
(
locator
)
# inherited
# inherited
self
.
assertEqual
(
node
.
graceperiod
,
datetime
.
timedelta
(
hours
=
2
))
self
.
assertEqual
(
node
.
graceperiod
,
datetime
.
timedelta
(
hours
=
2
))
locator
=
BlockUsageLocator
(
locator
=
BlockUsageLocator
(
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
'problem1'
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
),
'problem'
,
'problem1'
)
)
node
=
modulestore
()
.
get_item
(
locator
)
node
=
modulestore
()
.
get_item
(
locator
)
# overridden
# overridden
...
@@ -1594,8 +1603,8 @@ class TestPublish(SplitModuleTest):
...
@@ -1594,8 +1603,8 @@ class TestPublish(SplitModuleTest):
"""
"""
Test the standard patterns: publish to new branch, revise and publish
Test the standard patterns: publish to new branch, revise and publish
"""
"""
source_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
source_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
dest_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_PUBLISHED
)
dest_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
chapter1
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter1'
)
chapter1
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter1'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
...
@@ -1640,16 +1649,16 @@ class TestPublish(SplitModuleTest):
...
@@ -1640,16 +1649,16 @@ class TestPublish(SplitModuleTest):
"""
"""
Test the exceptions which preclude successful publication
Test the exceptions which preclude successful publication
"""
"""
source_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero'
,
branch
=
BRANCH_NAME_DRAFT
)
source_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
"run"
,
branch
=
BRANCH_NAME_DRAFT
)
# destination does not exist
# destination does not exist
destination_course
=
CourseLocator
(
org
=
'fake'
,
offering
=
'Unknown'
,
branch
=
BRANCH_NAME_PUBLISHED
)
destination_course
=
CourseLocator
(
org
=
'fake'
,
course
=
'Unknown'
,
run
=
"run"
,
branch
=
BRANCH_NAME_PUBLISHED
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
chapter3
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter3'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
# publishing into a new branch w/o publishing the root
# publishing into a new branch w/o publishing the root
destination_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero
'
,
branch
=
BRANCH_NAME_PUBLISHED
)
destination_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run
'
,
branch
=
BRANCH_NAME_PUBLISHED
)
with
self
.
assertRaises
(
ItemNotFoundError
):
with
self
.
assertRaises
(
ItemNotFoundError
):
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
modulestore
()
.
xblock_publish
(
self
.
user_id
,
source_course
,
destination_course
,
[
chapter3
],
None
)
# publishing a subdag w/o the parent already in course
# publishing a subdag w/o the parent already in course
...
@@ -1661,8 +1670,8 @@ class TestPublish(SplitModuleTest):
...
@@ -1661,8 +1670,8 @@ class TestPublish(SplitModuleTest):
"""
"""
Test publishing moves and deletes.
Test publishing moves and deletes.
"""
"""
source_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero
'
,
branch
=
BRANCH_NAME_DRAFT
)
source_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run
'
,
branch
=
BRANCH_NAME_DRAFT
)
dest_course
=
CourseLocator
(
org
=
'testx'
,
offering
=
'GreekHero
'
,
branch
=
BRANCH_NAME_PUBLISHED
)
dest_course
=
CourseLocator
(
org
=
'testx'
,
course
=
'GreekHero'
,
run
=
'run
'
,
branch
=
BRANCH_NAME_PUBLISHED
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
head
=
source_course
.
make_usage_key
(
'course'
,
"head12345"
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
chapter2
=
source_course
.
make_usage_key
(
'chapter'
,
'chapter2'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
problem1
=
source_course
.
make_usage_key
(
'problem'
,
'problem1'
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py
View file @
e39cc5df
...
@@ -40,7 +40,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
...
@@ -40,7 +40,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
'xblock_mixins'
:
(
InheritanceMixin
,)
'xblock_mixins'
:
(
InheritanceMixin
,)
}
}
split_course_key
=
CourseLocator
(
'test_org'
,
'test_course
.
runid'
,
branch
=
ModuleStoreEnum
.
BranchName
.
draft
)
split_course_key
=
CourseLocator
(
'test_org'
,
'test_course
'
,
'
runid'
,
branch
=
ModuleStoreEnum
.
BranchName
.
draft
)
def
setUp
(
self
):
def
setUp
(
self
):
self
.
db_config
[
'collection'
]
=
'modulestore{0}'
.
format
(
uuid
.
uuid4
()
.
hex
[:
5
])
self
.
db_config
[
'collection'
]
=
'modulestore{0}'
.
format
(
uuid
.
uuid4
()
.
hex
[:
5
])
...
@@ -135,8 +135,8 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
...
@@ -135,8 +135,8 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
if
split
:
if
split
:
# split requires the course to be created separately from creating items
# split requires the course to be created separately from creating items
self
.
split_mongo
.
create_course
(
self
.
split_mongo
.
create_course
(
self
.
split_course_key
.
org
,
self
.
split_course_key
.
offering
,
self
.
userid
,
fields
=
fields
,
root_block_id
=
'runid'
self
.
split_course_key
.
org
,
self
.
split_course_key
.
course
,
self
.
split_course_key
.
run
,
self
.
userid
,
fields
=
fields
,
root_block_id
=
'runid'
)
)
old_course
=
self
.
old_mongo
.
create_course
(
self
.
split_course_key
.
org
,
'test_course
/runid'
,
self
.
user
id
,
fields
=
fields
)
old_course
=
self
.
old_mongo
.
create_course
(
self
.
split_course_key
.
org
,
'test_course
'
,
'runid'
,
self
.
user_
id
,
fields
=
fields
)
self
.
old_course_key
=
old_course
.
id
self
.
old_course_key
=
old_course
.
id
self
.
runtime
=
old_course
.
runtime
self
.
runtime
=
old_course
.
runtime
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
e39cc5df
...
@@ -172,7 +172,7 @@ def import_from_xml(
...
@@ -172,7 +172,7 @@ def import_from_xml(
# Creates a new course if it doesn't already exist
# Creates a new course if it doesn't already exist
if
create_new_course_if_not_present
and
not
store
.
has_course
(
dest_course_id
,
ignore_case
=
True
):
if
create_new_course_if_not_present
and
not
store
.
has_course
(
dest_course_id
,
ignore_case
=
True
):
try
:
try
:
store
.
create_course
(
dest_course_id
.
org
,
dest_course_id
.
offering
,
user_id
)
store
.
create_course
(
dest_course_id
.
org
,
dest_course_id
.
course
,
dest_course_id
.
run
,
user_id
)
except
InvalidLocationError
:
except
InvalidLocationError
:
# course w/ same org and course exists
# course w/ same org and course exists
log
.
debug
(
log
.
debug
(
...
...
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