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
16f0d12a
Commit
16f0d12a
authored
Feb 10, 2014
by
Don Mitchell
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2356 from edx/dhm/mixed_ms_wrapper
MixedModulestore wraps most getters, update_item, delete_item
parents
aab6c54a
1de9d558
Hide whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
782 additions
and
649 deletions
+782
-649
CHANGELOG.rst
+6
-0
cms/djangoapps/contentstore/course_info_model.py
+4
-4
cms/djangoapps/contentstore/tests/test_checklists.py
+1
-1
cms/djangoapps/contentstore/tests/test_contentstore.py
+18
-66
cms/djangoapps/contentstore/tests/test_course_settings.py
+50
-35
cms/djangoapps/contentstore/tests/test_course_updates.py
+1
-1
cms/djangoapps/contentstore/tests/test_crud.py
+1
-1
cms/djangoapps/contentstore/tests/test_orphan.py
+1
-2
cms/djangoapps/contentstore/tests/test_textbooks.py
+2
-5
cms/djangoapps/contentstore/tests/test_transcripts.py
+30
-27
cms/djangoapps/contentstore/transcripts_utils.py
+6
-6
cms/djangoapps/contentstore/utils.py
+0
-10
cms/djangoapps/contentstore/views/checklist.py
+2
-3
cms/djangoapps/contentstore/views/course.py
+14
-23
cms/djangoapps/contentstore/views/item.py
+29
-32
cms/djangoapps/contentstore/views/tabs.py
+6
-6
cms/djangoapps/contentstore/views/transcripts_ajax.py
+8
-8
cms/djangoapps/models/settings/course_details.py
+23
-18
cms/djangoapps/models/settings/course_grading.py
+15
-29
cms/djangoapps/models/settings/course_metadata.py
+2
-3
common/djangoapps/external_auth/tests/test_shib.py
+4
-12
common/djangoapps/student/tests/test_login.py
+1
-3
common/lib/xmodule/xmodule/modulestore/__init__.py
+28
-29
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
+8
-2
common/lib/xmodule/xmodule/modulestore/locator.py
+13
-0
common/lib/xmodule/xmodule/modulestore/mixed.py
+271
-39
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+65
-77
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
+17
-52
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+17
-21
common/lib/xmodule/xmodule/modulestore/store_utilities.py
+6
-11
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
+3
-5
common/lib/xmodule/xmodule/modulestore/tests/factories.py
+3
-7
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
+10
-2
common/lib/xmodule/xmodule/modulestore/tests/test_locators.py
+18
-0
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+29
-10
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
+1
-1
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
+3
-3
common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py
+5
-5
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
+6
-6
common/lib/xmodule/xmodule/modulestore/xml.py
+11
-21
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+7
-39
lms/djangoapps/courseware/tests/test_submitting_problems.py
+2
-2
lms/djangoapps/courseware/tests/test_view_authentication.py
+22
-18
lms/djangoapps/courseware/views.py
+2
-2
lms/djangoapps/instructor_task/tests/test_base.py
+3
-1
lms/djangoapps/instructor_task/tests/test_integration.py
+5
-1
lms/envs/acceptance.py
+1
-0
lms/envs/bok_choy.auth.json
+1
-0
lms/envs/cms/mixed_dev.py
+1
-0
No files found.
CHANGELOG.rst
View file @
16f0d12a
...
@@ -52,6 +52,12 @@ Blades: Fix comparison of float numbers. BLD-434.
...
@@ -52,6 +52,12 @@ Blades: Fix comparison of float numbers. BLD-434.
Blades: Allow regexp strings as the correct answer to a string response question. BLD-475.
Blades: Allow regexp strings as the correct answer to a string response question. BLD-475.
Common: MixedModulestore is now the only approved access to the persistence layer
- takes a new parameter 'reference_type' which can be 'Location' or 'Locator'. Mixed
then tries to ensure that every reference in any xblock gets converted to that type on
retrieval. Because we're moving to Locators, the default is Locator; so, you should change
all existing configurations to 'Location' (unless you're using split)
Common: Add feature flags to allow developer use of pure XBlocks
Common: Add feature flags to allow developer use of pure XBlocks
- ALLOW_ALL_ADVANCED_COMPONENTS disables the hard-coded list of advanced
- ALLOW_ALL_ADVANCED_COMPONENTS disables the hard-coded list of advanced
components in Studio, and allows any xblock to be added as an
components in Studio, and allows any xblock to be added as an
...
...
cms/djangoapps/contentstore/course_info_model.py
View file @
16f0d12a
...
@@ -56,7 +56,7 @@ def get_course_updates(location, provided_id):
...
@@ -56,7 +56,7 @@ def get_course_updates(location, provided_id):
return
course_upd_collection
return
course_upd_collection
def
update_course_updates
(
location
,
update
,
passed_id
=
None
):
def
update_course_updates
(
location
,
update
,
passed_id
=
None
,
user
=
None
):
"""
"""
Either add or update the given course update. It will add it if the passed_id is absent or None. It will update it if
Either add or update the given course update. It will add it if the passed_id is absent or None. It will update it if
it has an passed_id which has a valid value. Until updates have distinct values, the passed_id is the location url + an index
it has an passed_id which has a valid value. Until updates have distinct values, the passed_id is the location url + an index
...
@@ -102,7 +102,7 @@ def update_course_updates(location, update, passed_id=None):
...
@@ -102,7 +102,7 @@ def update_course_updates(location, update, passed_id=None):
# update db record
# update db record
course_updates
.
data
=
html
.
tostring
(
course_html_parsed
)
course_updates
.
data
=
html
.
tostring
(
course_html_parsed
)
modulestore
(
'direct'
)
.
update_item
(
location
,
course_updates
.
data
)
modulestore
(
'direct'
)
.
update_item
(
course_updates
,
user
.
id
if
user
else
None
)
return
{
return
{
"id"
:
idx
,
"id"
:
idx
,
...
@@ -125,7 +125,7 @@ def _course_info_content(html_parsed):
...
@@ -125,7 +125,7 @@ def _course_info_content(html_parsed):
# pylint: disable=unused-argument
# pylint: disable=unused-argument
def
delete_course_update
(
location
,
update
,
passed_id
):
def
delete_course_update
(
location
,
update
,
passed_id
,
user
):
"""
"""
Delete the given course_info update from the db.
Delete the given course_info update from the db.
Returns the resulting course_updates b/c their ids change.
Returns the resulting course_updates b/c their ids change.
...
@@ -158,7 +158,7 @@ def delete_course_update(location, update, passed_id):
...
@@ -158,7 +158,7 @@ def delete_course_update(location, update, passed_id):
# update db record
# update db record
course_updates
.
data
=
html
.
tostring
(
course_html_parsed
)
course_updates
.
data
=
html
.
tostring
(
course_html_parsed
)
store
=
modulestore
(
'direct'
)
store
=
modulestore
(
'direct'
)
store
.
update_item
(
location
,
course_updates
.
data
)
store
.
update_item
(
course_updates
,
user
.
id
)
return
get_course_updates
(
location
,
None
)
return
get_course_updates
(
location
,
None
)
...
...
cms/djangoapps/contentstore/tests/test_checklists.py
View file @
16f0d12a
...
@@ -54,7 +54,7 @@ class ChecklistTestCase(CourseTestCase):
...
@@ -54,7 +54,7 @@ class ChecklistTestCase(CourseTestCase):
# Save the changed `checklists` to the underlying KeyValueStore before updating the modulestore
# Save the changed `checklists` to the underlying KeyValueStore before updating the modulestore
self
.
course
.
save
()
self
.
course
.
save
()
modulestore
=
get_modulestore
(
self
.
course
.
location
)
modulestore
=
get_modulestore
(
self
.
course
.
location
)
modulestore
.
update_
metadata
(
self
.
course
.
location
,
own_metadata
(
self
.
course
)
)
modulestore
.
update_
item
(
self
.
course
,
self
.
user
.
id
)
self
.
assertEqual
(
self
.
get_persisted_checklists
(),
None
)
self
.
assertEqual
(
self
.
get_persisted_checklists
(),
None
)
response
=
self
.
client
.
get
(
self
.
checklists_url
)
response
=
self
.
client
.
get
(
self
.
checklists_url
)
self
.
assertEqual
(
payload
,
response
.
content
)
self
.
assertEqual
(
payload
,
response
.
content
)
...
...
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
16f0d12a
...
@@ -47,8 +47,6 @@ from xmodule.exceptions import NotFoundError
...
@@ -47,8 +47,6 @@ from xmodule.exceptions import NotFoundError
from
django_comment_common.utils
import
are_permissions_roles_seeded
from
django_comment_common.utils
import
are_permissions_roles_seeded
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
import
datetime
from
pytz
import
UTC
from
uuid
import
uuid4
from
uuid
import
uuid4
from
pymongo
import
MongoClient
from
pymongo
import
MongoClient
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
...
@@ -126,11 +124,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -126,11 +124,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
course
.
advanced_modules
=
component_types
course
.
advanced_modules
=
component_types
# Save the data that we've just changed to the underlying
store
.
update_item
(
course
,
self
.
user
.
id
)
# MongoKeyValueStore before we update the mongo datastore.
course
.
save
()
store
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
# just pick one vertical
# just pick one vertical
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
...
@@ -269,7 +263,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -269,7 +263,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self
.
assertIn
(
'graceperiod'
,
own_metadata
(
html_module
))
self
.
assertIn
(
'graceperiod'
,
own_metadata
(
html_module
))
self
.
assertEqual
(
html_module
.
graceperiod
,
new_graceperiod
)
self
.
assertEqual
(
html_module
.
graceperiod
,
new_graceperiod
)
draft_store
.
update_
metadata
(
html_module
.
location
,
own_metadata
(
html_module
)
)
draft_store
.
update_
item
(
html_module
,
self
.
user
.
id
)
# read back to make sure it reads as 'own-metadata'
# read back to make sure it reads as 'own-metadata'
html_module
=
draft_store
.
get_item
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'html'
,
'test_html'
,
None
))
html_module
=
draft_store
.
get_item
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'html'
,
'test_html'
,
None
))
...
@@ -385,8 +379,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -385,8 +379,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self
.
assertEqual
(
course
.
tabs
,
expected_tabs
)
self
.
assertEqual
(
course
.
tabs
,
expected_tabs
)
item
.
display_name
=
'Updated'
item
.
display_name
=
'Updated'
item
.
save
()
module_store
.
update_item
(
item
,
self
.
user
.
id
)
module_store
.
update_metadata
(
item
.
location
,
own_metadata
(
item
))
course
=
module_store
.
get_item
(
course_location
)
course
=
module_store
.
get_item
(
course_location
)
...
@@ -834,9 +827,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -834,9 +827,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
html_module
=
module_store
.
get_instance
(
source_location
.
course_id
,
html_module_location
)
html_module
=
module_store
.
get_instance
(
source_location
.
course_id
,
html_module_location
)
self
.
assertIsInstance
(
html_module
.
data
,
basestring
)
self
.
assertIsInstance
(
html_module
.
data
,
basestring
)
new_data
=
html_module
.
data
.
replace
(
'/static/'
,
'/c4x/{0}/{1}/asset/'
.
format
(
new_data
=
html_module
.
data
=
html_module
.
data
.
replace
(
'/static/'
,
'/c4x/{0}/{1}/asset/'
.
format
(
source_location
.
org
,
source_location
.
course
))
source_location
.
org
,
source_location
.
course
))
module_store
.
update_item
(
html_module
_location
,
new_data
)
module_store
.
update_item
(
html_module
,
self
.
user
.
id
)
html_module
=
module_store
.
get_instance
(
source_location
.
course_id
,
html_module_location
)
html_module
=
module_store
.
get_instance
(
source_location
.
course_id
,
html_module_location
)
self
.
assertEqual
(
new_data
,
html_module
.
data
)
self
.
assertEqual
(
new_data
,
html_module
.
data
)
...
@@ -858,22 +851,18 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -858,22 +851,18 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
draft_store
=
modulestore
(
'draft'
)
draft_store
=
modulestore
(
'draft'
)
direct_store
=
modulestore
(
'direct'
)
direct_store
=
modulestore
(
'direct'
)
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
course
=
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
location
=
Location
(
'i4x://MITx/999/chapter/neuvo'
)
location
=
Location
(
'i4x://MITx/999/chapter/neuvo'
)
# Ensure draft mongo store does not allow us to create chapters either directly or via convert to draft
# Ensure draft mongo store does not allow us to create chapters either directly or via convert to draft
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
create_and_save_xmodule
,
location
)
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
create_and_save_xmodule
,
location
)
direct_store
.
create_and_save_xmodule
(
location
)
direct_store
.
create_and_save_xmodule
(
location
)
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
convert_to_draft
,
location
)
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
convert_to_draft
,
location
)
chapter
=
draft_store
.
get_instance
(
course
.
id
,
location
)
chapter
.
data
=
'chapter data'
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
update_item
,
location
,
'chapter data'
)
with
self
.
assertRaises
(
InvalidVersionError
):
draft_store
.
update_item
(
chapter
,
self
.
user
.
id
)
# taking advantage of update_children and other functions never checking that the ids are valid
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
update_children
,
location
,
[
'i4x://MITx/999/problem/doesntexist'
])
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
update_metadata
,
location
,
{
'due'
:
datetime
.
datetime
.
now
(
UTC
)})
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
unpublish
,
location
)
self
.
assertRaises
(
InvalidVersionError
,
draft_store
.
unpublish
,
location
)
...
@@ -992,8 +981,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -992,8 +981,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
sequential
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
sequential
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
'sequential'
,
'vertical_sequential'
,
None
]))
'sequential'
,
'vertical_sequential'
,
None
]))
private_location_no_draft
=
private_vertical
.
location
.
replace
(
revision
=
None
)
private_location_no_draft
=
private_vertical
.
location
.
replace
(
revision
=
None
)
module_store
.
update_children
(
sequential
.
location
,
sequential
.
children
+
sequential
.
children
.
append
(
private_location_no_draft
.
url
())
[
private_location_no_draft
.
url
()]
)
module_store
.
update_item
(
sequential
,
self
.
user
.
id
)
# read back the sequential, to make sure we have a pointer to
# read back the sequential, to make sure we have a pointer to
sequential
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
sequential
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
...
@@ -1285,31 +1274,6 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -1285,31 +1274,6 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self
.
assertFalse
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
'vertical'
,
'vertical_test'
,
None
])
self
.
assertFalse
(
Location
([
'i4x'
,
'edX'
,
'toy'
,
'vertical'
,
'vertical_test'
,
None
])
in
course
.
system
.
module_data
)
in
course
.
system
.
module_data
)
def
test_export_course_with_unknown_metadata
(
self
):
module_store
=
modulestore
(
'direct'
)
content_store
=
contentstore
()
import_from_xml
(
module_store
,
'common/test/data/'
,
[
'toy'
])
location
=
CourseDescriptor
.
id_to_location
(
'edX/toy/2012_Fall'
)
root_dir
=
path
(
mkdtemp_clean
())
course
=
module_store
.
get_item
(
location
)
metadata
=
own_metadata
(
course
)
# add a bool piece of unknown metadata so we can verify we don't throw an exception
metadata
[
'new_metadata'
]
=
True
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
course
.
save
()
module_store
.
update_metadata
(
location
,
metadata
)
print
'Exporting to tempdir = {0}'
.
format
(
root_dir
)
# export out to a tempdir
export_to_xml
(
module_store
,
content_store
,
location
,
root_dir
,
'test_export'
)
def
test_export_course_without_content_store
(
self
):
def
test_export_course_without_content_store
(
self
):
module_store
=
modulestore
(
'direct'
)
module_store
=
modulestore
(
'direct'
)
content_store
=
contentstore
()
content_store
=
contentstore
()
...
@@ -1319,16 +1283,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -1319,16 +1283,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
import_from_xml
(
module_store
,
'common/test/data/'
,
[
'toy'
])
import_from_xml
(
module_store
,
'common/test/data/'
,
[
'toy'
])
location
=
CourseDescriptor
.
id_to_location
(
'edX/toy/2012_Fall'
)
location
=
CourseDescriptor
.
id_to_location
(
'edX/toy/2012_Fall'
)
# Add a sequence
stub_location
=
Location
([
'i4x'
,
'edX'
,
'toy'
,
'sequential'
,
'vertical_sequential'
])
stub_location
=
Location
([
'i4x'
,
'edX'
,
'toy'
,
'sequential'
,
'vertical_sequential'
])
sequential
=
module_store
.
get_item
(
stub_location
)
module_store
.
update_children
(
sequential
.
location
,
sequential
.
children
)
# Get course and export it without a content_store
course
=
module_store
.
get_item
(
location
)
course
.
save
()
root_dir
=
path
(
mkdtemp_clean
())
root_dir
=
path
(
mkdtemp_clean
())
...
@@ -1343,7 +1298,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -1343,7 +1298,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
module_store
,
root_dir
,
[
'test_export_no_content_store'
],
module_store
,
root_dir
,
[
'test_export_no_content_store'
],
draft_store
=
None
,
draft_store
=
None
,
static_content_store
=
None
,
static_content_store
=
None
,
target_location_namespace
=
course
.
location
target_location_namespace
=
location
)
)
# Verify reimported course
# Verify reimported course
...
@@ -1810,7 +1765,8 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1810,7 +1765,8 @@ class ContentStoreTest(ModuleStoreTestCase):
# crate a new module and add it as a child to a vertical
# crate a new module and add it as a child to a vertical
module_store
.
create_and_save_xmodule
(
new_component_location
)
module_store
.
create_and_save_xmodule
(
new_component_location
)
parent
=
verticals
[
0
]
parent
=
verticals
[
0
]
module_store
.
update_children
(
parent
.
location
,
parent
.
children
+
[
new_component_location
.
url
()])
parent
.
children
.
append
(
new_component_location
.
url
())
module_store
.
update_item
(
parent
,
self
.
user
.
id
)
# flush the cache
# flush the cache
module_store
.
refresh_cached_metadata_inheritance_tree
(
new_component_location
)
module_store
.
refresh_cached_metadata_inheritance_tree
(
new_component_location
)
...
@@ -1827,8 +1783,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1827,8 +1783,7 @@ class ContentStoreTest(ModuleStoreTestCase):
# now let's define an override at the leaf node level
# now let's define an override at the leaf node level
#
#
new_module
.
graceperiod
=
timedelta
(
1
)
new_module
.
graceperiod
=
timedelta
(
1
)
new_module
.
save
()
module_store
.
update_item
(
new_module
,
self
.
user
.
id
)
module_store
.
update_metadata
(
new_module
.
location
,
own_metadata
(
new_module
))
# flush the cache and refetch
# flush the cache and refetch
module_store
.
refresh_cached_metadata_inheritance_tree
(
new_component_location
)
module_store
.
refresh_cached_metadata_inheritance_tree
(
new_component_location
)
...
@@ -1942,10 +1897,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
...
@@ -1942,10 +1897,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
delattr
(
self
.
video_descriptor
,
field_name
)
delattr
(
self
.
video_descriptor
,
field_name
)
self
.
assertNotIn
(
'html5_sources'
,
own_metadata
(
self
.
video_descriptor
))
self
.
assertNotIn
(
'html5_sources'
,
own_metadata
(
self
.
video_descriptor
))
get_modulestore
(
location
)
.
update_metadata
(
get_modulestore
(
location
)
.
update_item
(
self
.
video_descriptor
,
'**replace_user**'
)
location
,
own_metadata
(
self
.
video_descriptor
)
)
module
=
get_modulestore
(
location
)
.
get_item
(
location
)
module
=
get_modulestore
(
location
)
.
get_item
(
location
)
self
.
assertNotIn
(
'html5_sources'
,
own_metadata
(
module
))
self
.
assertNotIn
(
'html5_sources'
,
own_metadata
(
module
))
...
@@ -2001,7 +1953,7 @@ def _course_factory_create_course():
...
@@ -2001,7 +1953,7 @@ def _course_factory_create_course():
Creates a course via the CourseFactory and returns the locator for it.
Creates a course via the CourseFactory and returns the locator for it.
"""
"""
course
=
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
course
=
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
return
loc_mapper
()
.
translate_location
(
course
.
location
.
course_id
,
course
.
location
,
Tru
e
,
True
)
return
loc_mapper
()
.
translate_location
(
course
.
id
,
course
.
location
,
Fals
e
,
True
)
def
_get_course_id
(
test_course_data
):
def
_get_course_id
(
test_course_data
):
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
16f0d12a
...
@@ -73,32 +73,32 @@ class CourseDetailsTestCase(CourseTestCase):
...
@@ -73,32 +73,32 @@ class CourseDetailsTestCase(CourseTestCase):
jsondetails
.
syllabus
=
"<a href='foo'>bar</a>"
jsondetails
.
syllabus
=
"<a href='foo'>bar</a>"
# encode - decode to convert date fields and other data which changes form
# encode - decode to convert date fields and other data which changes form
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
syllabus
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
syllabus
,
jsondetails
.
syllabus
,
"After set syllabus"
jsondetails
.
syllabus
,
"After set syllabus"
)
)
jsondetails
.
overview
=
"Overview"
jsondetails
.
overview
=
"Overview"
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
overview
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
overview
,
jsondetails
.
overview
,
"After set overview"
jsondetails
.
overview
,
"After set overview"
)
)
jsondetails
.
intro_video
=
"intro_video"
jsondetails
.
intro_video
=
"intro_video"
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
intro_video
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
intro_video
,
jsondetails
.
intro_video
,
"After set intro_video"
jsondetails
.
intro_video
,
"After set intro_video"
)
)
jsondetails
.
effort
=
"effort"
jsondetails
.
effort
=
"effort"
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
effort
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
effort
,
jsondetails
.
effort
,
"After set effort"
jsondetails
.
effort
,
"After set effort"
)
)
jsondetails
.
start_date
=
datetime
.
datetime
(
2010
,
10
,
1
,
0
,
tzinfo
=
UTC
())
jsondetails
.
start_date
=
datetime
.
datetime
(
2010
,
10
,
1
,
0
,
tzinfo
=
UTC
())
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
start_date
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
start_date
,
jsondetails
.
start_date
jsondetails
.
start_date
)
)
jsondetails
.
course_image_name
=
"an_image.jpg"
jsondetails
.
course_image_name
=
"an_image.jpg"
self
.
assertEqual
(
self
.
assertEqual
(
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
)
.
course_image_name
,
CourseDetails
.
update_from_json
(
self
.
course_locator
,
jsondetails
.
__dict__
,
self
.
user
)
.
course_image_name
,
jsondetails
.
course_image_name
jsondetails
.
course_image_name
)
)
...
@@ -120,8 +120,8 @@ class CourseDetailsTestCase(CourseTestCase):
...
@@ -120,8 +120,8 @@ class CourseDetailsTestCase(CourseTestCase):
self
.
assertContains
(
response
,
"Introducing Your Course"
)
self
.
assertContains
(
response
,
"Introducing Your Course"
)
self
.
assertContains
(
response
,
"Course Image"
)
self
.
assertContains
(
response
,
"Course Image"
)
self
.
assertNotContains
(
response
,
"Course Overview"
)
self
.
assertNotContains
(
response
,
"Course Overview"
)
self
.
assertNotContains
(
response
,
"Course Introduction Video"
)
self
.
assertNotContains
(
response
,
"Course Introduction Video"
)
self
.
assertNotContains
(
response
,
"Requirements"
)
self
.
assertNotContains
(
response
,
"Requirements"
)
def
test_regular_site_fetch
(
self
):
def
test_regular_site_fetch
(
self
):
...
@@ -141,8 +141,8 @@ class CourseDetailsTestCase(CourseTestCase):
...
@@ -141,8 +141,8 @@ class CourseDetailsTestCase(CourseTestCase):
self
.
assertContains
(
response
,
"Introducing Your Course"
)
self
.
assertContains
(
response
,
"Introducing Your Course"
)
self
.
assertContains
(
response
,
"Course Image"
)
self
.
assertContains
(
response
,
"Course Image"
)
self
.
assertContains
(
response
,
"Course Overview"
)
self
.
assertContains
(
response
,
"Course Overview"
)
self
.
assertContains
(
response
,
"Course Introduction Video"
)
self
.
assertContains
(
response
,
"Course Introduction Video"
)
self
.
assertContains
(
response
,
"Requirements"
)
self
.
assertContains
(
response
,
"Requirements"
)
...
@@ -241,67 +241,74 @@ class CourseGradingTest(CourseTestCase):
...
@@ -241,67 +241,74 @@ class CourseGradingTest(CourseTestCase):
def
test_update_from_json
(
self
):
def
test_update_from_json
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
,
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"Noop update"
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"Noop update"
)
test_grader
.
graders
[
0
][
'weight'
]
=
test_grader
.
graders
[
0
]
.
get
(
'weight'
)
*
2
test_grader
.
graders
[
0
][
'weight'
]
=
test_grader
.
graders
[
0
]
.
get
(
'weight'
)
*
2
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
,
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"Weight[0] * 2"
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"Weight[0] * 2"
)
test_grader
.
grade_cutoffs
[
'D'
]
=
0.3
test_grader
.
grade_cutoffs
[
'D'
]
=
0.3
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
,
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"cutoff add D"
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"cutoff add D"
)
test_grader
.
grace_period
=
{
'hours'
:
4
,
'minutes'
:
5
,
'seconds'
:
0
}
test_grader
.
grace_period
=
{
'hours'
:
4
,
'minutes'
:
5
,
'seconds'
:
0
}
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
self
.
course_locator
,
test_grader
.
__dict__
,
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"4 hour grace period"
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"4 hour grace period"
)
def
test_update_grader_from_json
(
self
):
def
test_update_grader_from_json
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
])
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
],
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"Noop update"
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"Noop update"
)
test_grader
.
graders
[
1
][
'min_count'
]
=
test_grader
.
graders
[
1
]
.
get
(
'min_count'
)
+
2
test_grader
.
graders
[
1
][
'min_count'
]
=
test_grader
.
graders
[
1
]
.
get
(
'min_count'
)
+
2
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
])
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
],
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"min_count[1] + 2"
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"min_count[1] + 2"
)
test_grader
.
graders
[
1
][
'drop_count'
]
=
test_grader
.
graders
[
1
]
.
get
(
'drop_count'
)
+
1
test_grader
.
graders
[
1
][
'drop_count'
]
=
test_grader
.
graders
[
1
]
.
get
(
'drop_count'
)
+
1
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
])
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
self
.
course_locator
,
test_grader
.
graders
[
1
],
self
.
user
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"drop_count[1] + 2"
)
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"drop_count[1] + 2"
)
def
test_update_cutoffs_from_json
(
self
):
def
test_update_cutoffs_from_json
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
)
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
,
self
.
user
)
# Unlike other tests, need to actually perform a db fetch for this test since update_cutoffs_from_json
# Unlike other tests, need to actually perform a db fetch for this test since update_cutoffs_from_json
# simply returns the cutoffs you send into it, rather than returning the db contents.
# simply returns the cutoffs you send into it, rather than returning the db contents.
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"Noop update"
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"Noop update"
)
test_grader
.
grade_cutoffs
[
'D'
]
=
0.3
test_grader
.
grade_cutoffs
[
'D'
]
=
0.3
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
)
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
,
self
.
user
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"cutoff add D"
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"cutoff add D"
)
test_grader
.
grade_cutoffs
[
'Pass'
]
=
0.75
test_grader
.
grade_cutoffs
[
'Pass'
]
=
0.75
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
)
CourseGradingModel
.
update_cutoffs_from_json
(
self
.
course_locator
,
test_grader
.
grade_cutoffs
,
self
.
user
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"cutoff change 'Pass'"
)
self
.
assertDictEqual
(
test_grader
.
grade_cutoffs
,
altered_grader
.
grade_cutoffs
,
"cutoff change 'Pass'"
)
def
test_delete_grace_period
(
self
):
def
test_delete_grace_period
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
CourseGradingModel
.
update_grace_period_from_json
(
self
.
course_locator
,
test_grader
.
grace_period
)
CourseGradingModel
.
update_grace_period_from_json
(
self
.
course_locator
,
test_grader
.
grace_period
,
self
.
user
)
# update_grace_period_from_json doesn't return anything, so query the db for its contents.
# update_grace_period_from_json doesn't return anything, so query the db for its contents.
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
self
.
assertEqual
(
test_grader
.
grace_period
,
altered_grader
.
grace_period
,
"Noop update"
)
self
.
assertEqual
(
test_grader
.
grace_period
,
altered_grader
.
grace_period
,
"Noop update"
)
test_grader
.
grace_period
=
{
'hours'
:
15
,
'minutes'
:
5
,
'seconds'
:
30
}
test_grader
.
grace_period
=
{
'hours'
:
15
,
'minutes'
:
5
,
'seconds'
:
30
}
CourseGradingModel
.
update_grace_period_from_json
(
self
.
course_locator
,
test_grader
.
grace_period
)
CourseGradingModel
.
update_grace_period_from_json
(
self
.
course_locator
,
test_grader
.
grace_period
,
self
.
user
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
self
.
assertDictEqual
(
test_grader
.
grace_period
,
altered_grader
.
grace_period
,
"Adding in a grace period"
)
self
.
assertDictEqual
(
test_grader
.
grace_period
,
altered_grader
.
grace_period
,
"Adding in a grace period"
)
test_grader
.
grace_period
=
{
'hours'
:
1
,
'minutes'
:
10
,
'seconds'
:
0
}
test_grader
.
grace_period
=
{
'hours'
:
1
,
'minutes'
:
10
,
'seconds'
:
0
}
# Now delete the grace period
# Now delete the grace period
CourseGradingModel
.
delete_grace_period
(
self
.
course_locator
)
CourseGradingModel
.
delete_grace_period
(
self
.
course_locator
,
self
.
user
)
# update_grace_period_from_json doesn't return anything, so query the db for its contents.
# update_grace_period_from_json doesn't return anything, so query the db for its contents.
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
altered_grader
=
CourseGradingModel
.
fetch
(
self
.
course_locator
)
# Once deleted, the grace period should simply be None
# Once deleted, the grace period should simply be None
...
@@ -317,7 +324,7 @@ class CourseGradingTest(CourseTestCase):
...
@@ -317,7 +324,7 @@ class CourseGradingTest(CourseTestCase):
self
.
assertEqual
(
False
,
descriptor
.
graded
)
self
.
assertEqual
(
False
,
descriptor
.
graded
)
# Change the default grader type to Homework, which should also mark the section as graded
# Change the default grader type to Homework, which should also mark the section as graded
CourseGradingModel
.
update_section_grader_type
(
self
.
course
,
'Homework'
)
CourseGradingModel
.
update_section_grader_type
(
self
.
course
,
'Homework'
,
self
.
user
)
descriptor
=
get_modulestore
(
self
.
course
.
location
)
.
get_item
(
self
.
course
.
location
)
descriptor
=
get_modulestore
(
self
.
course
.
location
)
.
get_item
(
self
.
course
.
location
)
section_grader_type
=
CourseGradingModel
.
get_section_grader_type
(
self
.
course_locator
)
section_grader_type
=
CourseGradingModel
.
get_section_grader_type
(
self
.
course_locator
)
...
@@ -326,7 +333,7 @@ class CourseGradingTest(CourseTestCase):
...
@@ -326,7 +333,7 @@ class CourseGradingTest(CourseTestCase):
self
.
assertEqual
(
True
,
descriptor
.
graded
)
self
.
assertEqual
(
True
,
descriptor
.
graded
)
# Change the grader type back to notgraded, which should also unmark the section as graded
# Change the grader type back to notgraded, which should also unmark the section as graded
CourseGradingModel
.
update_section_grader_type
(
self
.
course
,
'notgraded'
)
CourseGradingModel
.
update_section_grader_type
(
self
.
course
,
'notgraded'
,
self
.
user
)
descriptor
=
get_modulestore
(
self
.
course
.
location
)
.
get_item
(
self
.
course
.
location
)
descriptor
=
get_modulestore
(
self
.
course
.
location
)
.
get_item
(
self
.
course
.
location
)
section_grader_type
=
CourseGradingModel
.
get_section_grader_type
(
self
.
course_locator
)
section_grader_type
=
CourseGradingModel
.
get_section_grader_type
(
self
.
course_locator
)
...
@@ -439,19 +446,27 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -439,19 +446,27 @@ class CourseMetadataEditingTest(CourseTestCase):
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
def
test_update_from_json
(
self
):
def
test_update_from_json
(
self
):
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
,
{
test_model
=
CourseMetadata
.
update_from_json
(
"advertised_start"
:
"start A"
,
self
.
course
,
"days_early_for_beta"
:
2
{
})
"advertised_start"
:
"start A"
,
"days_early_for_beta"
:
2
,
},
user
=
self
.
user
)
self
.
update_check
(
test_model
)
self
.
update_check
(
test_model
)
# try fresh fetch to ensure persistence
# try fresh fetch to ensure persistence
fresh
=
modulestore
()
.
get_item
(
self
.
course_location
)
fresh
=
modulestore
()
.
get_item
(
self
.
course_location
)
test_model
=
CourseMetadata
.
fetch
(
fresh
)
test_model
=
CourseMetadata
.
fetch
(
fresh
)
self
.
update_check
(
test_model
)
self
.
update_check
(
test_model
)
# now change some of the existing metadata
# now change some of the existing metadata
test_model
=
CourseMetadata
.
update_from_json
(
fresh
,
{
test_model
=
CourseMetadata
.
update_from_json
(
"advertised_start"
:
"start B"
,
fresh
,
"display_name"
:
"jolly roger"
}
{
"advertised_start"
:
"start B"
,
"display_name"
:
"jolly roger"
,
},
user
=
self
.
user
)
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'jolly roger'
,
"not expected value"
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'jolly roger'
,
"not expected value"
)
...
@@ -468,9 +483,9 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -468,9 +483,9 @@ class CourseMetadataEditingTest(CourseTestCase):
def
test_delete_key
(
self
):
def
test_delete_key
(
self
):
test_model
=
CourseMetadata
.
update_from_json
(
test_model
=
CourseMetadata
.
update_from_json
(
self
.
fullcourse
,
{
self
.
fullcourse
,
"unsetKeys"
:
[
'showanswer'
,
'xqa_key'
]
{
"unsetKeys"
:
[
'showanswer'
,
'xqa_key'
]},
}
user
=
self
.
user
)
)
# ensure no harm
# ensure no harm
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
...
...
cms/djangoapps/contentstore/tests/test_course_updates.py
View file @
16f0d12a
...
@@ -123,7 +123,7 @@ class CourseUpdateTest(CourseTestCase):
...
@@ -123,7 +123,7 @@ class CourseUpdateTest(CourseTestCase):
modulestore
(
'direct'
)
.
create_and_save_xmodule
(
location
)
modulestore
(
'direct'
)
.
create_and_save_xmodule
(
location
)
course_updates
=
modulestore
(
'direct'
)
.
get_item
(
location
)
course_updates
=
modulestore
(
'direct'
)
.
get_item
(
location
)
course_updates
.
data
=
'bad news'
course_updates
.
data
=
'bad news'
modulestore
(
'direct'
)
.
update_item
(
location
,
course_updates
.
data
)
modulestore
(
'direct'
)
.
update_item
(
course_updates
,
self
.
user
.
id
)
init_content
=
'<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
init_content
=
'<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
content
=
init_content
+
'</iframe>'
content
=
init_content
+
'</iframe>'
...
...
cms/djangoapps/contentstore/tests/test_crud.py
View file @
16f0d12a
...
@@ -172,7 +172,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -172,7 +172,7 @@ class TemplateTests(unittest.TestCase):
)
)
first_problem
.
max_attempts
=
3
first_problem
.
max_attempts
=
3
first_problem
.
save
()
# decache the above into the kvs
first_problem
.
save
()
# decache the above into the kvs
updated_problem
=
modulestore
(
'split'
)
.
update_item
(
first_problem
,
'
testbot
'
)
updated_problem
=
modulestore
(
'split'
)
.
update_item
(
first_problem
,
'
**replace_user**
'
)
self
.
assertIsNotNone
(
updated_problem
.
previous_version
)
self
.
assertIsNotNone
(
updated_problem
.
previous_version
)
self
.
assertEqual
(
updated_problem
.
previous_version
,
first_problem
.
update_version
)
self
.
assertEqual
(
updated_problem
.
previous_version
,
first_problem
.
update_version
)
self
.
assertNotEqual
(
updated_problem
.
update_version
,
first_problem
.
update_version
)
self
.
assertNotEqual
(
updated_problem
.
update_version
,
first_problem
.
update_version
)
...
...
cms/djangoapps/contentstore/tests/test_orphan.py
View file @
16f0d12a
...
@@ -4,7 +4,6 @@ Test finding orphans via the view and django config
...
@@ -4,7 +4,6 @@ Test finding orphans via the view and django config
import
json
import
json
from
contentstore.tests.utils
import
CourseTestCase
from
contentstore.tests.utils
import
CourseTestCase
from
xmodule.modulestore.django
import
editable_modulestore
,
loc_mapper
from
xmodule.modulestore.django
import
editable_modulestore
,
loc_mapper
from
django.core.urlresolvers
import
reverse
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
class
TestOrphan
(
CourseTestCase
):
class
TestOrphan
(
CourseTestCase
):
...
@@ -35,7 +34,7 @@ class TestOrphan(CourseTestCase):
...
@@ -35,7 +34,7 @@ class TestOrphan(CourseTestCase):
parent_location
=
self
.
course
.
location
.
replace
(
category
=
parent_category
,
name
=
parent_name
)
parent_location
=
self
.
course
.
location
.
replace
(
category
=
parent_category
,
name
=
parent_name
)
parent
=
editable_modulestore
(
'direct'
)
.
get_item
(
parent_location
)
parent
=
editable_modulestore
(
'direct'
)
.
get_item
(
parent_location
)
parent
.
children
.
append
(
location
.
url
())
parent
.
children
.
append
(
location
.
url
())
editable_modulestore
(
'direct'
)
.
update_
children
(
parent_location
,
parent
.
children
)
editable_modulestore
(
'direct'
)
.
update_
item
(
parent
,
self
.
user
.
id
)
def
test_mongo_orphan
(
self
):
def
test_mongo_orphan
(
self
):
"""
"""
...
...
cms/djangoapps/contentstore/tests/test_textbooks.py
View file @
16f0d12a
...
@@ -58,11 +58,8 @@ class TextbookIndexTestCase(CourseTestCase):
...
@@ -58,11 +58,8 @@ class TextbookIndexTestCase(CourseTestCase):
}
}
]
]
self
.
course
.
pdf_textbooks
=
content
self
.
course
.
pdf_textbooks
=
content
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
self
.
course
.
save
()
store
=
get_modulestore
(
self
.
course
.
location
)
store
=
get_modulestore
(
self
.
course
.
location
)
store
.
update_
metadata
(
self
.
course
.
location
,
own_metadata
(
self
.
course
)
)
store
.
update_
item
(
self
.
course
,
self
.
user
.
id
)
resp
=
self
.
client
.
get
(
resp
=
self
.
client
.
get
(
self
.
url
,
self
.
url
,
...
@@ -200,7 +197,7 @@ class TextbookDetailTestCase(CourseTestCase):
...
@@ -200,7 +197,7 @@ class TextbookDetailTestCase(CourseTestCase):
# MongoKeyValueStore before we update the mongo datastore.
# MongoKeyValueStore before we update the mongo datastore.
self
.
course
.
save
()
self
.
course
.
save
()
self
.
store
=
get_modulestore
(
self
.
course
.
location
)
self
.
store
=
get_modulestore
(
self
.
course
.
location
)
self
.
store
.
update_
metadata
(
self
.
course
.
location
,
own_metadata
(
self
.
course
)
)
self
.
store
.
update_
item
(
self
.
course
,
self
.
user
.
id
)
self
.
url_nonexist
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"20"
)
self
.
url_nonexist
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"20"
)
def
test_get_1
(
self
):
def
test_get_1
(
self
):
...
...
cms/djangoapps/contentstore/tests/test_transcripts.py
View file @
16f0d12a
...
@@ -63,13 +63,13 @@ class Basetranscripts(CourseTestCase):
...
@@ -63,13 +63,13 @@ class Basetranscripts(CourseTestCase):
self
.
item_locator
,
self
.
item_location
=
self
.
_get_locator
(
resp
)
self
.
item_locator
,
self
.
item_location
=
self
.
_get_locator
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
item
=
modulestore
()
.
get_item
(
self
.
item_location
)
# hI10vDNYz4M - valid Youtube ID with transcripts.
# hI10vDNYz4M - valid Youtube ID with transcripts.
# JMD_ifUUfsU, AKqURZnYqpk, DYpADpL7jAY - valid Youtube IDs without transcripts.
# JMD_ifUUfsU, AKqURZnYqpk, DYpADpL7jAY - valid Youtube IDs without transcripts.
data
=
'<video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
self
.
item
.
data
=
'<video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
self
.
item
=
modulestore
()
.
get_item
(
self
.
item_location
)
self
.
item
=
modulestore
()
.
get_item
(
self
.
item_location
)
# Remove all transcripts for current module.
# Remove all transcripts for current module.
self
.
clear_subs_content
()
self
.
clear_subs_content
()
...
@@ -130,14 +130,14 @@ class TestUploadtranscripts(Basetranscripts):
...
@@ -130,14 +130,14 @@ class TestUploadtranscripts(Basetranscripts):
self
.
bad_name_srt_file
.
seek
(
0
)
self
.
bad_name_srt_file
.
seek
(
0
)
def
test_success_video_module_source_subs_uploading
(
self
):
def
test_success_video_module_source_subs_uploading
(
self
):
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="">
<video youtube="">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
)
"""
)
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
link
=
reverse
(
'upload_transcripts'
)
link
=
reverse
(
'upload_transcripts'
)
filename
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
self
.
good_srt_file
.
name
))[
0
]
filename
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
self
.
good_srt_file
.
name
))[
0
]
...
@@ -212,8 +212,9 @@ class TestUploadtranscripts(Basetranscripts):
...
@@ -212,8 +212,9 @@ class TestUploadtranscripts(Basetranscripts):
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
data
=
'<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
item
=
modulestore
()
.
get_item
(
item_location
)
modulestore
()
.
update_item
(
item_location
,
data
)
item
.
data
=
'<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
modulestore
()
.
update_item
(
item
,
self
.
user
.
id
)
# non_video module: testing
# non_video module: testing
...
@@ -232,8 +233,8 @@ class TestUploadtranscripts(Basetranscripts):
...
@@ -232,8 +233,8 @@ class TestUploadtranscripts(Basetranscripts):
self
.
assertEqual
(
json
.
loads
(
resp
.
content
)
.
get
(
'status'
),
'Transcripts are supported only for "video" modules.'
)
self
.
assertEqual
(
json
.
loads
(
resp
.
content
)
.
get
(
'status'
),
'Transcripts are supported only for "video" modules.'
)
def
test_fail_bad_xml
(
self
):
def
test_fail_bad_xml
(
self
):
data
=
'<<<video youtube="0.75:JMD_ifUUfsU,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
self
.
item
.
data
=
'<<<video youtube="0.75:JMD_ifUUfsU,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
link
=
reverse
(
'upload_transcripts'
)
link
=
reverse
(
'upload_transcripts'
)
filename
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
self
.
good_srt_file
.
name
))[
0
]
filename
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
self
.
good_srt_file
.
name
))[
0
]
...
@@ -344,8 +345,8 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -344,8 +345,8 @@ class TestDownloadtranscripts(Basetranscripts):
pass
pass
def
test_success_download_youtube
(
self
):
def
test_success_download_youtube
(
self
):
data
=
'<video youtube="1:JMD_ifUUfsU" />'
self
.
item
.
data
=
'<video youtube="1:JMD_ifUUfsU" />'
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -365,14 +366,14 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -365,14 +366,14 @@ class TestDownloadtranscripts(Basetranscripts):
def
test_success_download_nonyoutube
(
self
):
def
test_success_download_nonyoutube
(
self
):
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="" sub="{}">
<video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
.
format
(
subs_id
))
"""
.
format
(
subs_id
))
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -424,14 +425,15 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -424,14 +425,15 @@ class TestDownloadtranscripts(Basetranscripts):
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
item
=
modulestore
()
.
get_item
(
item_location
)
item
.
data
=
textwrap
.
dedent
(
"""
<videoalpha youtube="" sub="{}">
<videoalpha youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</videoalpha>
</videoalpha>
"""
.
format
(
subs_id
))
"""
.
format
(
subs_id
))
modulestore
()
.
update_item
(
item
_location
,
data
)
modulestore
()
.
update_item
(
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -449,28 +451,28 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -449,28 +451,28 @@ class TestDownloadtranscripts(Basetranscripts):
self
.
assertEqual
(
resp
.
status_code
,
404
)
self
.
assertEqual
(
resp
.
status_code
,
404
)
def
test_fail_nonyoutube_subs_dont_exist
(
self
):
def
test_fail_nonyoutube_subs_dont_exist
(
self
):
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="" sub="UNDEFINED">
<video youtube="" sub="UNDEFINED">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
)
"""
)
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
link
=
reverse
(
'download_transcripts'
)
link
=
reverse
(
'download_transcripts'
)
resp
=
self
.
client
.
get
(
link
,
{
'locator'
:
self
.
item_locator
})
resp
=
self
.
client
.
get
(
link
,
{
'locator'
:
self
.
item_locator
})
self
.
assertEqual
(
resp
.
status_code
,
404
)
self
.
assertEqual
(
resp
.
status_code
,
404
)
def
test_empty_youtube_attr_and_sub_attr
(
self
):
def
test_empty_youtube_attr_and_sub_attr
(
self
):
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="">
<video youtube="">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
)
"""
)
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
link
=
reverse
(
'download_transcripts'
)
link
=
reverse
(
'download_transcripts'
)
resp
=
self
.
client
.
get
(
link
,
{
'locator'
:
self
.
item_locator
})
resp
=
self
.
client
.
get
(
link
,
{
'locator'
:
self
.
item_locator
})
...
@@ -479,14 +481,14 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -479,14 +481,14 @@ class TestDownloadtranscripts(Basetranscripts):
def
test_fail_bad_sjson_subs
(
self
):
def
test_fail_bad_sjson_subs
(
self
):
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="" sub="{}">
<video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
.
format
(
subs_id
))
"""
.
format
(
subs_id
))
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -532,14 +534,14 @@ class TestChecktranscripts(Basetranscripts):
...
@@ -532,14 +534,14 @@ class TestChecktranscripts(Basetranscripts):
def
test_success_download_nonyoutube
(
self
):
def
test_success_download_nonyoutube
(
self
):
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
self
.
item
.
data
=
textwrap
.
dedent
(
"""
<video youtube="" sub="{}">
<video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</video>
</video>
"""
.
format
(
subs_id
))
"""
.
format
(
subs_id
))
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -582,8 +584,8 @@ class TestChecktranscripts(Basetranscripts):
...
@@ -582,8 +584,8 @@ class TestChecktranscripts(Basetranscripts):
transcripts_utils
.
remove_subs_from_store
(
subs_id
,
self
.
item
)
transcripts_utils
.
remove_subs_from_store
(
subs_id
,
self
.
item
)
def
test_check_youtube
(
self
):
def
test_check_youtube
(
self
):
data
=
'<video youtube="1:JMD_ifUUfsU" />'
self
.
item
.
data
=
'<video youtube="1:JMD_ifUUfsU" />'
modulestore
()
.
update_item
(
self
.
item
_location
,
data
)
modulestore
()
.
update_item
(
self
.
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
@@ -674,14 +676,15 @@ class TestChecktranscripts(Basetranscripts):
...
@@ -674,14 +676,15 @@ class TestChecktranscripts(Basetranscripts):
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
item_locator
,
item_location
=
self
.
_get_locator
(
resp
)
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
item
=
modulestore
()
.
get_item
(
item_location
)
item
.
data
=
textwrap
.
dedent
(
"""
<not_video youtube="" sub="{}">
<not_video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
</videoalpha>
</videoalpha>
"""
.
format
(
subs_id
))
"""
.
format
(
subs_id
))
modulestore
()
.
update_item
(
item
_location
,
data
)
modulestore
()
.
update_item
(
item
,
self
.
user
.
id
)
subs
=
{
subs
=
{
'start'
:
[
100
,
200
,
240
],
'start'
:
[
100
,
200
,
240
],
...
...
cms/djangoapps/contentstore/transcripts_utils.py
View file @
16f0d12a
...
@@ -280,16 +280,16 @@ def generate_srt_from_sjson(sjson_subs, speed):
...
@@ -280,16 +280,16 @@ def generate_srt_from_sjson(sjson_subs, speed):
return
output
return
output
def
save_module
(
item
):
def
save_module
(
item
,
user
):
"""
"""
Proceed with additional save operations.
Proceed with additional save operations.
"""
"""
item
.
save
()
item
.
save
()
store
=
get_modulestore
(
Location
(
item
.
id
))
store
=
get_modulestore
(
Location
(
item
.
id
))
store
.
update_
metadata
(
item
.
id
,
own_metadata
(
item
)
)
store
.
update_
item
(
item
,
user
.
id
if
user
else
None
)
def
copy_or_rename_transcript
(
new_name
,
old_name
,
item
,
delete_old
=
False
):
def
copy_or_rename_transcript
(
new_name
,
old_name
,
item
,
delete_old
=
False
,
user
=
None
):
"""
"""
Renames `old_name` transcript file in storage to `new_name`.
Renames `old_name` transcript file in storage to `new_name`.
...
@@ -303,12 +303,12 @@ def copy_or_rename_transcript(new_name, old_name, item, delete_old=False):
...
@@ -303,12 +303,12 @@ def copy_or_rename_transcript(new_name, old_name, item, delete_old=False):
transcripts
=
contentstore
()
.
find
(
content_location
)
.
data
transcripts
=
contentstore
()
.
find
(
content_location
)
.
data
save_subs_to_store
(
json
.
loads
(
transcripts
),
new_name
,
item
)
save_subs_to_store
(
json
.
loads
(
transcripts
),
new_name
,
item
)
item
.
sub
=
new_name
item
.
sub
=
new_name
save_module
(
item
)
save_module
(
item
,
user
)
if
delete_old
:
if
delete_old
:
remove_subs_from_store
(
old_name
,
item
)
remove_subs_from_store
(
old_name
,
item
)
def
manage_video_subtitles_save
(
old_item
,
new_item
):
def
manage_video_subtitles_save
(
old_item
,
new_item
,
user
):
"""
"""
Does some specific things, that can be done only on save.
Does some specific things, that can be done only on save.
...
@@ -340,7 +340,7 @@ def manage_video_subtitles_save(old_item, new_item):
...
@@ -340,7 +340,7 @@ def manage_video_subtitles_save(old_item, new_item):
# copy_or_rename_transcript changes item.sub of module
# copy_or_rename_transcript changes item.sub of module
try
:
try
:
# updates item.sub with `video_id`, if it is successful.
# updates item.sub with `video_id`, if it is successful.
copy_or_rename_transcript
(
video_id
,
sub_name
,
new_item
)
copy_or_rename_transcript
(
video_id
,
sub_name
,
new_item
,
user
=
user
)
except
NotFoundError
:
except
NotFoundError
:
# subtitles file `sub_name` is not presented in the system. Nothing to copy or rename.
# subtitles file `sub_name` is not presented in the system. Nothing to copy or rename.
log
.
debug
(
log
.
debug
(
...
...
cms/djangoapps/contentstore/utils.py
View file @
16f0d12a
...
@@ -222,16 +222,6 @@ def compute_unit_state(unit):
...
@@ -222,16 +222,6 @@ def compute_unit_state(unit):
return
UnitState
.
public
return
UnitState
.
public
def
update_item
(
location
,
value
):
"""
If value is None, delete the db entry. Otherwise, update it using the correct modulestore.
"""
if
value
is
None
:
get_modulestore
(
location
)
.
delete_item
(
location
)
else
:
get_modulestore
(
location
)
.
update_item
(
location
,
value
)
def
add_extra_panel_tab
(
tab_type
,
course
):
def
add_extra_panel_tab
(
tab_type
,
course
):
"""
"""
Used to add the panel tab to a course if it does not exist.
Used to add the panel tab to a course if it does not exist.
...
...
cms/djangoapps/contentstore/views/checklist.py
View file @
16f0d12a
...
@@ -51,8 +51,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
...
@@ -51,8 +51,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
# from the template.
# from the template.
if
not
course_module
.
checklists
:
if
not
course_module
.
checklists
:
course_module
.
checklists
=
CourseDescriptor
.
checklists
.
default
course_module
.
checklists
=
CourseDescriptor
.
checklists
.
default
course_module
.
save
()
modulestore
.
update_item
(
course_module
,
request
.
user
.
id
)
modulestore
.
update_metadata
(
old_location
,
own_metadata
(
course_module
))
expanded_checklists
=
expand_all_action_urls
(
course_module
)
expanded_checklists
=
expand_all_action_urls
(
course_module
)
if
json_request
:
if
json_request
:
...
@@ -81,7 +80,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
...
@@ -81,7 +80,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
# not default
# not default
course_module
.
checklists
=
course_module
.
checklists
course_module
.
checklists
=
course_module
.
checklists
course_module
.
save
()
course_module
.
save
()
modulestore
.
update_
metadata
(
old_location
,
own_metadata
(
course_module
)
)
modulestore
.
update_
item
(
course_module
,
request
.
user
.
id
)
expanded_checklist
=
expand_checklist_action_url
(
course_module
,
persisted_checklist
)
expanded_checklist
=
expand_checklist_action_url
(
course_module
,
persisted_checklist
)
return
JsonResponse
(
expanded_checklist
)
return
JsonResponse
(
expanded_checklist
)
else
:
else
:
...
...
cms/djangoapps/contentstore/views/course.py
View file @
16f0d12a
...
@@ -163,8 +163,7 @@ def course_listing(request):
...
@@ -163,8 +163,7 @@ def course_listing(request):
"""
"""
List all courses available to the logged in user
List all courses available to the logged in user
"""
"""
# there's an index on category which will be used if none of its antecedents are set
courses
=
modulestore
(
'direct'
)
.
get_courses
()
courses
=
modulestore
(
'direct'
)
.
get_items
(
Location
(
None
,
None
,
None
,
'course'
,
None
))
# filter out courses that we don't have access too
# filter out courses that we don't have access too
def
course_filter
(
course
):
def
course_filter
(
course
):
...
@@ -331,7 +330,7 @@ def create_new_course(request):
...
@@ -331,7 +330,7 @@ def create_new_course(request):
definition_data
=
overview_template
.
get
(
'data'
)
definition_data
=
overview_template
.
get
(
'data'
)
)
)
initialize_course_tabs
(
new_course
)
initialize_course_tabs
(
new_course
,
request
.
user
)
new_location
=
loc_mapper
()
.
translate_location
(
new_course
.
location
.
course_id
,
new_course
.
location
,
False
,
True
)
new_location
=
loc_mapper
()
.
translate_location
(
new_course
.
location
.
course_id
,
new_course
.
location
,
False
,
True
)
# can't use auth.add_users here b/c it requires request.user to already have Instructor perms in this course
# can't use auth.add_users here b/c it requires request.user to already have Instructor perms in this course
...
@@ -417,7 +416,7 @@ def course_info_update_handler(request, tag=None, package_id=None, branch=None,
...
@@ -417,7 +416,7 @@ def course_info_update_handler(request, tag=None, package_id=None, branch=None,
return
JsonResponse
(
get_course_updates
(
updates_location
,
provided_id
))
return
JsonResponse
(
get_course_updates
(
updates_location
,
provided_id
))
elif
request
.
method
==
'DELETE'
:
elif
request
.
method
==
'DELETE'
:
try
:
try
:
return
JsonResponse
(
delete_course_update
(
updates_location
,
request
.
json
,
provided_id
))
return
JsonResponse
(
delete_course_update
(
updates_location
,
request
.
json
,
provided_id
,
request
.
user
))
except
:
except
:
return
HttpResponseBadRequest
(
return
HttpResponseBadRequest
(
"Failed to delete"
,
"Failed to delete"
,
...
@@ -426,7 +425,7 @@ def course_info_update_handler(request, tag=None, package_id=None, branch=None,
...
@@ -426,7 +425,7 @@ def course_info_update_handler(request, tag=None, package_id=None, branch=None,
# can be either and sometimes django is rewriting one to the other:
# can be either and sometimes django is rewriting one to the other:
elif
request
.
method
in
(
'POST'
,
'PUT'
):
elif
request
.
method
in
(
'POST'
,
'PUT'
):
try
:
try
:
return
JsonResponse
(
update_course_updates
(
updates_location
,
request
.
json
,
provided_id
))
return
JsonResponse
(
update_course_updates
(
updates_location
,
request
.
json
,
provided_id
,
request
.
user
))
except
:
except
:
return
HttpResponseBadRequest
(
return
HttpResponseBadRequest
(
"Failed to save"
,
"Failed to save"
,
...
@@ -479,7 +478,7 @@ def settings_handler(request, tag=None, package_id=None, branch=None, version_gu
...
@@ -479,7 +478,7 @@ def settings_handler(request, tag=None, package_id=None, branch=None, version_gu
)
)
else
:
# post or put, doesn't matter.
else
:
# post or put, doesn't matter.
return
JsonResponse
(
return
JsonResponse
(
CourseDetails
.
update_from_json
(
locator
,
request
.
json
),
CourseDetails
.
update_from_json
(
locator
,
request
.
json
,
request
.
user
),
encoder
=
CourseSettingsEncoder
encoder
=
CourseSettingsEncoder
)
)
...
@@ -526,15 +525,15 @@ def grading_handler(request, tag=None, package_id=None, branch=None, version_gui
...
@@ -526,15 +525,15 @@ def grading_handler(request, tag=None, package_id=None, branch=None, version_gui
# None implies update the whole model (cutoffs, graceperiod, and graders) not a specific grader
# None implies update the whole model (cutoffs, graceperiod, and graders) not a specific grader
if
grader_index
is
None
:
if
grader_index
is
None
:
return
JsonResponse
(
return
JsonResponse
(
CourseGradingModel
.
update_from_json
(
locator
,
request
.
json
),
CourseGradingModel
.
update_from_json
(
locator
,
request
.
json
,
request
.
user
),
encoder
=
CourseSettingsEncoder
encoder
=
CourseSettingsEncoder
)
)
else
:
else
:
return
JsonResponse
(
return
JsonResponse
(
CourseGradingModel
.
update_grader_from_json
(
locator
,
request
.
json
)
CourseGradingModel
.
update_grader_from_json
(
locator
,
request
.
json
,
request
.
user
)
)
)
elif
request
.
method
==
"DELETE"
and
grader_index
is
not
None
:
elif
request
.
method
==
"DELETE"
and
grader_index
is
not
None
:
CourseGradingModel
.
delete_grader
(
locator
,
grader_index
)
CourseGradingModel
.
delete_grader
(
locator
,
grader_index
,
request
.
user
)
return
JsonResponse
()
return
JsonResponse
()
...
@@ -625,7 +624,8 @@ def advanced_settings_handler(request, package_id=None, branch=None, version_gui
...
@@ -625,7 +624,8 @@ def advanced_settings_handler(request, package_id=None, branch=None, version_gui
return
JsonResponse
(
CourseMetadata
.
update_from_json
(
return
JsonResponse
(
CourseMetadata
.
update_from_json
(
course_module
,
course_module
,
request
.
json
,
request
.
json
,
filter_tabs
=
filter_tabs
filter_tabs
=
filter_tabs
,
user
=
request
.
user
,
))
))
except
(
TypeError
,
ValueError
)
as
err
:
except
(
TypeError
,
ValueError
)
as
err
:
return
HttpResponseBadRequest
(
return
HttpResponseBadRequest
(
...
@@ -743,10 +743,7 @@ def textbooks_list_handler(request, tag=None, package_id=None, branch=None, vers
...
@@ -743,10 +743,7 @@ def textbooks_list_handler(request, tag=None, package_id=None, branch=None, vers
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course
.
tabs
):
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course
.
tabs
):
course
.
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
course
.
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
course
.
pdf_textbooks
=
textbooks
course
.
pdf_textbooks
=
textbooks
store
.
update_metadata
(
store
.
update_item
(
course
,
request
.
user
.
id
)
course
.
location
,
own_metadata
(
course
)
)
return
JsonResponse
(
course
.
pdf_textbooks
)
return
JsonResponse
(
course
.
pdf_textbooks
)
elif
request
.
method
==
'POST'
:
elif
request
.
method
==
'POST'
:
# create a new textbook for the course
# create a new textbook for the course
...
@@ -764,7 +761,7 @@ def textbooks_list_handler(request, tag=None, package_id=None, branch=None, vers
...
@@ -764,7 +761,7 @@ def textbooks_list_handler(request, tag=None, package_id=None, branch=None, vers
tabs
=
course
.
tabs
tabs
=
course
.
tabs
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
course
.
tabs
=
tabs
course
.
tabs
=
tabs
store
.
update_
metadata
(
course
.
location
,
own_metadata
(
course
)
)
store
.
update_
item
(
course
,
request
.
user
.
id
)
resp
=
JsonResponse
(
textbook
,
status
=
201
)
resp
=
JsonResponse
(
textbook
,
status
=
201
)
resp
[
"Location"
]
=
locator
.
url_reverse
(
'textbooks'
,
textbook
[
"id"
])
resp
[
"Location"
]
=
locator
.
url_reverse
(
'textbooks'
,
textbook
[
"id"
])
return
resp
return
resp
...
@@ -815,10 +812,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
...
@@ -815,10 +812,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
course
.
pdf_textbooks
=
new_textbooks
course
.
pdf_textbooks
=
new_textbooks
else
:
else
:
course
.
pdf_textbooks
.
append
(
new_textbook
)
course
.
pdf_textbooks
.
append
(
new_textbook
)
store
.
update_metadata
(
store
.
update_item
(
course
,
request
.
user
.
id
)
course
.
location
,
own_metadata
(
course
)
)
return
JsonResponse
(
new_textbook
,
status
=
201
)
return
JsonResponse
(
new_textbook
,
status
=
201
)
elif
request
.
method
==
'DELETE'
:
elif
request
.
method
==
'DELETE'
:
if
not
textbook
:
if
not
textbook
:
...
@@ -827,10 +821,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
...
@@ -827,10 +821,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
new_textbooks
=
course
.
pdf_textbooks
[
0
:
i
]
new_textbooks
=
course
.
pdf_textbooks
[
0
:
i
]
new_textbooks
.
extend
(
course
.
pdf_textbooks
[
i
+
1
:])
new_textbooks
.
extend
(
course
.
pdf_textbooks
[
i
+
1
:])
course
.
pdf_textbooks
=
new_textbooks
course
.
pdf_textbooks
=
new_textbooks
store
.
update_metadata
(
store
.
update_item
(
course
,
request
.
user
.
id
)
course
.
location
,
own_metadata
(
course
)
)
return
JsonResponse
()
return
JsonResponse
()
...
...
cms/djangoapps/contentstore/views/item.py
View file @
16f0d12a
...
@@ -9,7 +9,6 @@ from functools import partial
...
@@ -9,7 +9,6 @@ from functools import partial
from
static_replace
import
replace_static_urls
from
static_replace
import
replace_static_urls
from
xmodule_modifiers
import
wrap_xblock
from
xmodule_modifiers
import
wrap_xblock
from
django.conf
import
settings
from
django.core.exceptions
import
PermissionDenied
from
django.core.exceptions
import
PermissionDenied
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django.http
import
HttpResponseBadRequest
,
HttpResponse
from
django.http
import
HttpResponseBadRequest
,
HttpResponse
...
@@ -18,9 +17,8 @@ from django.views.decorators.http import require_http_methods
...
@@ -18,9 +17,8 @@ from django.views.decorators.http import require_http_methods
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
xblock.core
import
XBlock
import
xmodule
.x_module
import
xmodule
from
xmodule.modulestore.django
import
modulestore
,
loc_mapper
from
xmodule.modulestore.django
import
modulestore
,
loc_mapper
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
...
@@ -159,7 +157,7 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
...
@@ -159,7 +157,7 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
delete_children
=
str_to_bool
(
request
.
REQUEST
.
get
(
'recurse'
,
'False'
))
delete_children
=
str_to_bool
(
request
.
REQUEST
.
get
(
'recurse'
,
'False'
))
delete_all_versions
=
str_to_bool
(
request
.
REQUEST
.
get
(
'all_versions'
,
'False'
))
delete_all_versions
=
str_to_bool
(
request
.
REQUEST
.
get
(
'all_versions'
,
'False'
))
return
_delete_item_at_location
(
old_location
,
delete_children
,
delete_all_versions
)
return
_delete_item_at_location
(
old_location
,
delete_children
,
delete_all_versions
,
request
.
user
)
else
:
# Since we have a package_id, we are updating an existing xblock.
else
:
# Since we have a package_id, we are updating an existing xblock.
return
_save_item
(
return
_save_item
(
request
,
request
,
...
@@ -184,7 +182,8 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
...
@@ -184,7 +182,8 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
dest_location
=
_duplicate_item
(
dest_location
=
_duplicate_item
(
parent_location
,
parent_location
,
duplicate_source_location
,
duplicate_source_location
,
request
.
json
.
get
(
'display_name'
)
request
.
json
.
get
(
'display_name'
),
request
.
user
,
)
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
BlockUsageLocator
(
parent_locator
),
get_course
=
True
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
BlockUsageLocator
(
parent_locator
),
get_course
=
True
)
dest_locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
dest_locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
...
@@ -232,7 +231,8 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
...
@@ -232,7 +231,8 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
modulestore
()
.
convert_to_draft
(
item_location
)
modulestore
()
.
convert_to_draft
(
item_location
)
if
data
:
if
data
:
store
.
update_item
(
item_location
,
data
)
# TODO Allow any scope.content fields not just "data" (exactly like the get below this)
existing_item
.
data
=
data
else
:
else
:
data
=
existing_item
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
data
=
existing_item
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
...
@@ -242,9 +242,9 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
...
@@ -242,9 +242,9 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
for
child_locator
for
child_locator
in
children
in
children
]
]
store
.
update_children
(
item_location
,
children_ids
)
existing_item
.
children
=
children_ids
#
cdodge:
also commit any metadata which might have been passed along
# also commit any metadata which might have been passed along
if
nullout
is
not
None
or
metadata
is
not
None
:
if
nullout
is
not
None
or
metadata
is
not
None
:
# the postback is not the complete metadata, as there's system metadata which is
# the postback is not the complete metadata, as there's system metadata which is
# not presented to the end-user for editing. So let's use the original (existing_item) and
# not presented to the end-user for editing. So let's use the original (existing_item) and
...
@@ -269,14 +269,11 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
...
@@ -269,14 +269,11 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
return
JsonResponse
({
"error"
:
"Invalid data"
},
400
)
return
JsonResponse
({
"error"
:
"Invalid data"
},
400
)
field
.
write_to
(
existing_item
,
value
)
field
.
write_to
(
existing_item
,
value
)
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
existing_item
.
save
()
# commit to datastore
store
.
update_metadata
(
item_location
,
own_metadata
(
existing_item
))
if
existing_item
.
category
==
'video'
:
if
existing_item
.
category
==
'video'
:
manage_video_subtitles_save
(
existing_item
,
existing_item
)
manage_video_subtitles_save
(
existing_item
,
existing_item
,
request
.
user
)
# commit to datastore
store
.
update_item
(
existing_item
,
request
.
user
.
id
)
result
=
{
result
=
{
'id'
:
unicode
(
usage_loc
),
'id'
:
unicode
(
usage_loc
),
...
@@ -285,7 +282,7 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
...
@@ -285,7 +282,7 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
}
}
if
grader_type
is
not
None
:
if
grader_type
is
not
None
:
result
.
update
(
CourseGradingModel
.
update_section_grader_type
(
existing_item
,
grader_type
))
result
.
update
(
CourseGradingModel
.
update_section_grader_type
(
existing_item
,
grader_type
,
request
.
user
))
# Make public after updating the xblock, in case the caller asked
# Make public after updating the xblock, in case the caller asked
# for both an update and a publish.
# for both an update and a publish.
...
@@ -339,14 +336,15 @@ def _create_item(request):
...
@@ -339,14 +336,15 @@ def _create_item(request):
# TODO replace w/ nicer accessor
# TODO replace w/ nicer accessor
if
not
'detached'
in
parent
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
if
not
'detached'
in
parent
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
get_modulestore
(
parent
.
location
)
.
update_children
(
parent_location
,
parent
.
children
+
[
dest_location
.
url
()])
parent
.
children
.
append
(
dest_location
.
url
())
get_modulestore
(
parent
.
location
)
.
update_item
(
parent
,
request
.
user
.
id
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
parent_locator
,
get_course
=
True
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
parent_locator
,
get_course
=
True
)
locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
return
JsonResponse
({
"locator"
:
unicode
(
locator
)})
return
JsonResponse
({
"locator"
:
unicode
(
locator
)})
def
_duplicate_item
(
parent_location
,
duplicate_source_location
,
display_name
=
None
):
def
_duplicate_item
(
parent_location
,
duplicate_source_location
,
display_name
=
None
,
user
=
None
):
"""
"""
Duplicate an existing xblock as a child of the supplied parent_location.
Duplicate an existing xblock as a child of the supplied parent_location.
"""
"""
...
@@ -373,13 +371,15 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
...
@@ -373,13 +371,15 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
system
=
source_item
.
runtime
,
system
=
source_item
.
runtime
,
)
)
dest_module
=
get_modulestore
(
category
)
.
get_item
(
dest_location
)
# Children are not automatically copied over (and not all xblocks have a 'children' attribute).
# Children are not automatically copied over (and not all xblocks have a 'children' attribute).
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
if
source_item
.
has_children
:
if
source_item
.
has_children
:
copied_
children
=
[]
dest_module
.
children
=
[]
for
child
in
source_item
.
children
:
for
child
in
source_item
.
children
:
copied_children
.
append
(
_duplicate_item
(
dest_location
,
Location
(
child
))
.
url
())
dupe
=
_duplicate_item
(
dest_location
,
Location
(
child
),
user
=
user
)
get_modulestore
(
dest_location
)
.
update_children
(
dest_location
,
copied_children
)
dest_module
.
children
.
append
(
dupe
.
url
())
get_modulestore
(
dest_location
)
.
update_item
(
dest_module
,
user
.
id
if
user
else
None
)
if
not
'detached'
in
source_item
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
if
not
'detached'
in
source_item
.
runtime
.
load_block_type
(
category
)
.
_class_tags
:
parent
=
get_modulestore
(
parent_location
)
.
get_item
(
parent_location
)
parent
=
get_modulestore
(
parent_location
)
.
get_item
(
parent_location
)
...
@@ -390,12 +390,12 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
...
@@ -390,12 +390,12 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
parent
.
children
.
insert
(
source_index
+
1
,
dest_location
.
url
())
parent
.
children
.
insert
(
source_index
+
1
,
dest_location
.
url
())
else
:
else
:
parent
.
children
.
append
(
dest_location
.
url
())
parent
.
children
.
append
(
dest_location
.
url
())
get_modulestore
(
parent_location
)
.
update_
children
(
parent_location
,
parent
.
children
)
get_modulestore
(
parent_location
)
.
update_
item
(
parent
,
user
.
id
if
user
else
None
)
return
dest_location
return
dest_location
def
_delete_item_at_location
(
item_location
,
delete_children
=
False
,
delete_all_versions
=
False
):
def
_delete_item_at_location
(
item_location
,
delete_children
=
False
,
delete_all_versions
=
False
,
user
=
None
):
"""
"""
Deletes the item at with the given Location.
Deletes the item at with the given Location.
...
@@ -406,22 +406,19 @@ def _delete_item_at_location(item_location, delete_children=False, delete_all_ve
...
@@ -406,22 +406,19 @@ def _delete_item_at_location(item_location, delete_children=False, delete_all_ve
item
=
store
.
get_item
(
item_location
)
item
=
store
.
get_item
(
item_location
)
if
delete_children
:
if
delete_children
:
_xmodule_recurse
(
item
,
lambda
i
:
store
.
delete_item
(
i
.
location
,
delete_all_versions
))
_xmodule_recurse
(
item
,
lambda
i
:
store
.
delete_item
(
i
.
location
,
delete_all_versions
=
delete_all_versions
))
else
:
else
:
store
.
delete_item
(
item
.
location
,
delete_all_versions
)
store
.
delete_item
(
item
.
location
,
delete_all_versions
=
delete_all_versions
)
# cdodge: we need to remove our parent's pointer to us so that it is no longer dangling
# cdodge: we need to remove our parent's pointer to us so that it is no longer dangling
if
delete_all_versions
:
if
delete_all_versions
:
parent_locs
=
modulestore
(
'direct'
)
.
get_parent_locations
(
item_location
,
None
)
parent_locs
=
modulestore
(
'direct'
)
.
get_parent_locations
(
item_location
,
None
)
item_url
=
item_location
.
url
()
for
parent_loc
in
parent_locs
:
for
parent_loc
in
parent_locs
:
parent
=
modulestore
(
'direct'
)
.
get_item
(
parent_loc
)
parent
=
modulestore
(
'direct'
)
.
get_item
(
parent_loc
)
item_url
=
item_location
.
url
()
parent
.
children
.
remove
(
item_url
)
if
item_url
in
parent
.
children
:
modulestore
(
'direct'
)
.
update_item
(
parent
,
user
.
id
if
user
else
None
)
children
=
parent
.
children
children
.
remove
(
item_url
)
parent
.
children
=
children
modulestore
(
'direct'
)
.
update_children
(
parent
.
location
,
parent
.
children
)
return
JsonResponse
()
return
JsonResponse
()
...
@@ -452,7 +449,7 @@ def orphan_handler(request, tag=None, package_id=None, branch=None, version_guid
...
@@ -452,7 +449,7 @@ def orphan_handler(request, tag=None, package_id=None, branch=None, version_guid
if
request
.
user
.
is_staff
:
if
request
.
user
.
is_staff
:
items
=
modulestore
()
.
get_orphans
(
old_location
,
'draft'
)
items
=
modulestore
()
.
get_orphans
(
old_location
,
'draft'
)
for
item
in
items
:
for
item
in
items
:
modulestore
(
'draft'
)
.
delete_item
(
item
,
True
)
modulestore
(
'draft'
)
.
delete_item
(
item
,
delete_all_versions
=
True
)
return
JsonResponse
({
'deleted'
:
items
})
return
JsonResponse
({
'deleted'
:
items
})
else
:
else
:
raise
PermissionDenied
()
raise
PermissionDenied
()
...
...
cms/djangoapps/contentstore/views/tabs.py
View file @
16f0d12a
...
@@ -23,7 +23,7 @@ from django.utils.translation import ugettext as _
...
@@ -23,7 +23,7 @@ from django.utils.translation import ugettext as _
__all__
=
[
'tabs_handler'
]
__all__
=
[
'tabs_handler'
]
def
initialize_course_tabs
(
course
):
def
initialize_course_tabs
(
course
,
user
):
"""
"""
set up the default tabs
set up the default tabs
I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
...
@@ -47,7 +47,7 @@ def initialize_course_tabs(course):
...
@@ -47,7 +47,7 @@ def initialize_course_tabs(course):
{
"type"
:
"progress"
,
"name"
:
_
(
"Progress"
)},
{
"type"
:
"progress"
,
"name"
:
_
(
"Progress"
)},
]
]
modulestore
(
'direct'
)
.
update_
metadata
(
course
.
location
.
url
(),
own_metadata
(
course
)
)
modulestore
(
'direct'
)
.
update_
item
(
course
,
user
.
id
)
@expect_json
@expect_json
@login_required
@login_required
...
@@ -123,14 +123,14 @@ def tabs_handler(request, tag=None, package_id=None, branch=None, version_guid=N
...
@@ -123,14 +123,14 @@ def tabs_handler(request, tag=None, package_id=None, branch=None, version_guid=N
# OK, re-assemble the static tabs in the new order
# OK, re-assemble the static tabs in the new order
course_item
.
tabs
=
reordered_tabs
course_item
.
tabs
=
reordered_tabs
modulestore
(
'direct'
)
.
update_
metadata
(
course_item
.
location
,
own_metadata
(
course_item
)
)
modulestore
(
'direct'
)
.
update_
item
(
course_item
,
request
.
user
.
id
)
return
JsonResponse
()
return
JsonResponse
()
else
:
else
:
raise
NotImplementedError
(
'Creating or changing tab content is not supported.'
)
raise
NotImplementedError
(
'Creating or changing tab content is not supported.'
)
elif
request
.
method
==
'GET'
:
# assume html
elif
request
.
method
==
'GET'
:
# assume html
# see tabs have been uninitialized (e.g. supporting courses created before tab support in studio)
# see tabs have been uninitialized (e.g. supporting courses created before tab support in studio)
if
course_item
.
tabs
is
None
or
len
(
course_item
.
tabs
)
==
0
:
if
course_item
.
tabs
is
None
or
len
(
course_item
.
tabs
)
==
0
:
initialize_course_tabs
(
course_item
)
initialize_course_tabs
(
course_item
,
request
.
user
)
# first get all static tabs from the tabs list
# first get all static tabs from the tabs list
# we do this because this is also the order in which items are displayed in the LMS
# we do this because this is also the order in which items are displayed in the LMS
...
@@ -179,7 +179,7 @@ def primitive_delete(course, num):
...
@@ -179,7 +179,7 @@ def primitive_delete(course, num):
# Note for future implementations: if you delete a static_tab, then Chris Dodge
# Note for future implementations: if you delete a static_tab, then Chris Dodge
# points out that there's other stuff to delete beyond this element.
# points out that there's other stuff to delete beyond this element.
# This code happens to not delete static_tab so it doesn't come up.
# This code happens to not delete static_tab so it doesn't come up.
modulestore
(
'direct'
)
.
update_
metadata
(
course
.
location
,
own_metadata
(
course
)
)
modulestore
(
'direct'
)
.
update_
item
(
course
,
'**replace_user**'
)
def
primitive_insert
(
course
,
num
,
tab_type
,
name
):
def
primitive_insert
(
course
,
num
,
tab_type
,
name
):
...
@@ -188,5 +188,5 @@ def primitive_insert(course, num, tab_type, name):
...
@@ -188,5 +188,5 @@ def primitive_insert(course, num, tab_type, name):
new_tab
=
{
u'type'
:
unicode
(
tab_type
),
u'name'
:
unicode
(
name
)}
new_tab
=
{
u'type'
:
unicode
(
tab_type
),
u'name'
:
unicode
(
name
)}
tabs
=
course
.
tabs
tabs
=
course
.
tabs
tabs
.
insert
(
num
,
new_tab
)
tabs
.
insert
(
num
,
new_tab
)
modulestore
(
'direct'
)
.
update_
metadata
(
course
.
location
,
own_metadata
(
course
)
)
modulestore
(
'direct'
)
.
update_
item
(
course
,
'**replace_user**'
)
cms/djangoapps/contentstore/views/transcripts_ajax.py
View file @
16f0d12a
...
@@ -126,17 +126,17 @@ def upload_transcripts(request):
...
@@ -126,17 +126,17 @@ def upload_transcripts(request):
video_name
=
video_dict
[
'video'
]
video_name
=
video_dict
[
'video'
]
# We are creating transcripts for every video source,
# We are creating transcripts for every video source,
# for the case that in future, some of video sources can be deleted.
# for the case that in future, some of video sources can be deleted.
statuses
[
video_name
]
=
copy_or_rename_transcript
(
video_name
,
sub_attr
,
item
)
statuses
[
video_name
]
=
copy_or_rename_transcript
(
video_name
,
sub_attr
,
item
,
user
=
request
.
user
)
try
:
try
:
# updates item.sub with `video_name` if it is successful.
# updates item.sub with `video_name` if it is successful.
copy_or_rename_transcript
(
video_name
,
sub_attr
,
item
)
copy_or_rename_transcript
(
video_name
,
sub_attr
,
item
,
user
=
request
.
user
)
selected_name
=
video_name
# name to write to item.sub field, chosen at random.
selected_name
=
video_name
# name to write to item.sub field, chosen at random.
except
NotFoundError
:
except
NotFoundError
:
# subtitles file `sub_attr` is not presented in the system. Nothing to copy or rename.
# subtitles file `sub_attr` is not presented in the system. Nothing to copy or rename.
return
error_response
(
response
,
"Can't find transcripts in storage for {}"
.
format
(
sub_attr
))
return
error_response
(
response
,
"Can't find transcripts in storage for {}"
.
format
(
sub_attr
))
item
.
sub
=
selected_name
# write one of new subtitles names to item.sub attribute.
item
.
sub
=
selected_name
# write one of new subtitles names to item.sub attribute.
save_module
(
item
)
save_module
(
item
,
request
.
user
)
response
[
'subs'
]
=
item
.
sub
response
[
'subs'
]
=
item
.
sub
response
[
'status'
]
=
'Success'
response
[
'status'
]
=
'Success'
else
:
else
:
...
@@ -389,7 +389,7 @@ def choose_transcripts(request):
...
@@ -389,7 +389,7 @@ def choose_transcripts(request):
if
item
.
sub
!=
html5_id
:
# update sub value
if
item
.
sub
!=
html5_id
:
# update sub value
item
.
sub
=
html5_id
item
.
sub
=
html5_id
save_module
(
item
)
save_module
(
item
,
request
.
user
)
response
=
{
'status'
:
'Success'
,
'subs'
:
item
.
sub
}
response
=
{
'status'
:
'Success'
,
'subs'
:
item
.
sub
}
return
JsonResponse
(
response
)
return
JsonResponse
(
response
)
...
@@ -420,7 +420,7 @@ def replace_transcripts(request):
...
@@ -420,7 +420,7 @@ def replace_transcripts(request):
return
error_response
(
response
,
e
.
message
)
return
error_response
(
response
,
e
.
message
)
item
.
sub
=
youtube_id
item
.
sub
=
youtube_id
save_module
(
item
)
save_module
(
item
,
request
.
user
)
response
=
{
'status'
:
'Success'
,
'subs'
:
item
.
sub
}
response
=
{
'status'
:
'Success'
,
'subs'
:
item
.
sub
}
return
JsonResponse
(
response
)
return
JsonResponse
(
response
)
...
@@ -483,7 +483,7 @@ def rename_transcripts(request):
...
@@ -483,7 +483,7 @@ def rename_transcripts(request):
for
new_name
in
videos
[
'html5'
]
.
keys
():
# copy subtitles for every HTML5 source
for
new_name
in
videos
[
'html5'
]
.
keys
():
# copy subtitles for every HTML5 source
try
:
try
:
# updates item.sub with new_name if it is successful.
# updates item.sub with new_name if it is successful.
copy_or_rename_transcript
(
new_name
,
old_name
,
item
)
copy_or_rename_transcript
(
new_name
,
old_name
,
item
,
user
=
request
.
user
)
except
NotFoundError
:
except
NotFoundError
:
# subtitles file `item.sub` is not presented in the system. Nothing to copy or rename.
# subtitles file `item.sub` is not presented in the system. Nothing to copy or rename.
error_response
(
response
,
"Can't find transcripts in storage for {}"
.
format
(
old_name
))
error_response
(
response
,
"Can't find transcripts in storage for {}"
.
format
(
old_name
))
...
@@ -519,10 +519,10 @@ def save_transcripts(request):
...
@@ -519,10 +519,10 @@ def save_transcripts(request):
for
metadata_key
,
value
in
metadata
.
items
():
for
metadata_key
,
value
in
metadata
.
items
():
setattr
(
item
,
metadata_key
,
value
)
setattr
(
item
,
metadata_key
,
value
)
save_module
(
item
)
# item becomes updated with new values
save_module
(
item
,
request
.
user
)
# item becomes updated with new values
if
new_sub
:
if
new_sub
:
manage_video_subtitles_save
(
None
,
item
)
manage_video_subtitles_save
(
None
,
item
,
request
.
user
)
else
:
else
:
# If `new_sub` is empty, it means that user explicitly does not want to use
# If `new_sub` is empty, it means that user explicitly does not want to use
# transcripts for current video ids and we remove all transcripts from storage.
# transcripts for current video ids and we remove all transcripts from storage.
...
...
cms/djangoapps/models/settings/course_details.py
View file @
16f0d12a
...
@@ -6,10 +6,8 @@ from json.encoder import JSONEncoder
...
@@ -6,10 +6,8 @@ from json.encoder import JSONEncoder
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.inheritance
import
own_metadata
from
contentstore.utils
import
get_modulestore
,
course_image_url
from
contentstore.utils
import
get_modulestore
,
course_image_url
from
models.settings
import
course_grading
from
models.settings
import
course_grading
from
contentstore.utils
import
update_item
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.django
import
loc_mapper
...
@@ -75,7 +73,25 @@ class CourseDetails(object):
...
@@ -75,7 +73,25 @@ class CourseDetails(object):
return
course
return
course
@classmethod
@classmethod
def
update_from_json
(
cls
,
course_locator
,
jsondict
):
def
update_about_item
(
cls
,
course_old_location
,
about_key
,
data
,
course
,
user
):
"""
Update the about item with the new data blob. If data is None, then
delete the about item.
"""
temploc
=
Location
(
course_old_location
)
.
replace
(
category
=
'about'
,
name
=
about_key
)
store
=
get_modulestore
(
temploc
)
if
data
is
None
:
store
.
delete_item
(
temploc
)
else
:
try
:
about_item
=
store
.
get_item
(
temploc
)
except
ItemNotFoundError
:
about_item
=
store
.
create_xmodule
(
temploc
,
system
=
course
.
runtime
)
about_item
.
data
=
data
store
.
update_item
(
about_item
,
user
.
id
)
@classmethod
def
update_from_json
(
cls
,
course_locator
,
jsondict
,
user
):
"""
"""
Decode the json into CourseDetails and save any changed attrs to the db
Decode the json into CourseDetails and save any changed attrs to the db
"""
"""
...
@@ -130,26 +146,15 @@ class CourseDetails(object):
...
@@ -130,26 +146,15 @@ class CourseDetails(object):
dirty
=
True
dirty
=
True
if
dirty
:
if
dirty
:
# Save the data that we've just changed to the underlying
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
# MongoKeyValueStore before we update the mongo datastore.
descriptor
.
save
()
get_modulestore
(
course_old_location
)
.
update_metadata
(
course_old_location
,
own_metadata
(
descriptor
))
# NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
# NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
# to make faster, could compare against db or could have client send over a list of which fields changed.
# to make faster, could compare against db or could have client send over a list of which fields changed.
temploc
=
Location
(
course_old_location
)
.
replace
(
category
=
'about'
,
name
=
'syllabus'
)
for
about_type
in
[
'syllabus'
,
'overview'
,
'effort'
]:
update_item
(
temploc
,
jsondict
[
'syllabus'
]
)
cls
.
update_about_item
(
course_old_location
,
about_type
,
jsondict
[
about_type
],
descriptor
,
user
)
temploc
=
temploc
.
replace
(
name
=
'overview'
)
update_item
(
temploc
,
jsondict
[
'overview'
])
temploc
=
temploc
.
replace
(
name
=
'effort'
)
update_item
(
temploc
,
jsondict
[
'effort'
])
temploc
=
temploc
.
replace
(
name
=
'video'
)
recomposed_video_tag
=
CourseDetails
.
recompose_video_tag
(
jsondict
[
'intro_video'
])
recomposed_video_tag
=
CourseDetails
.
recompose_video_tag
(
jsondict
[
'intro_video'
])
update_item
(
temploc
,
recomposed_video_tag
)
cls
.
update_about_item
(
course_old_location
,
'video'
,
recomposed_video_tag
,
descriptor
,
user
)
# Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
# Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
# it persisted correctly
# it persisted correctly
...
...
cms/djangoapps/models/settings/course_grading.py
View file @
16f0d12a
...
@@ -52,7 +52,7 @@ class CourseGradingModel(object):
...
@@ -52,7 +52,7 @@ class CourseGradingModel(object):
}
}
@staticmethod
@staticmethod
def
update_from_json
(
course_locator
,
jsondict
):
def
update_from_json
(
course_locator
,
jsondict
,
user
):
"""
"""
Decode the json into CourseGradingModel and save any changes. Returns the modified model.
Decode the json into CourseGradingModel and save any changes. Returns the modified model.
Probably not the usual path for updates as it's too coarse grained.
Probably not the usual path for updates as it's too coarse grained.
...
@@ -65,16 +65,14 @@ class CourseGradingModel(object):
...
@@ -65,16 +65,14 @@ class CourseGradingModel(object):
descriptor
.
raw_grader
=
graders_parsed
descriptor
.
raw_grader
=
graders_parsed
descriptor
.
grade_cutoffs
=
jsondict
[
'grade_cutoffs'
]
descriptor
.
grade_cutoffs
=
jsondict
[
'grade_cutoffs'
]
get_modulestore
(
course_old_location
)
.
update_item
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
)
CourseGradingModel
.
update_grace_period_from_json
(
course_locator
,
jsondict
[
'grace_period'
])
CourseGradingModel
.
update_grace_period_from_json
(
course_locator
,
jsondict
[
'grace_period'
]
,
user
)
return
CourseGradingModel
.
fetch
(
course_locator
)
return
CourseGradingModel
.
fetch
(
course_locator
)
@staticmethod
@staticmethod
def
update_grader_from_json
(
course_location
,
grader
):
def
update_grader_from_json
(
course_location
,
grader
,
user
):
"""
"""
Create or update the grader of the given type (string key) for the given course. Returns the modified
Create or update the grader of the given type (string key) for the given course. Returns the modified
grader which is a full model on the client but not on the server (just a dict)
grader which is a full model on the client but not on the server (just a dict)
...
@@ -91,14 +89,12 @@ class CourseGradingModel(object):
...
@@ -91,14 +89,12 @@ class CourseGradingModel(object):
else
:
else
:
descriptor
.
raw_grader
.
append
(
grader
)
descriptor
.
raw_grader
.
append
(
grader
)
get_modulestore
(
course_old_location
)
.
update_item
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
)
return
CourseGradingModel
.
jsonize_grader
(
index
,
descriptor
.
raw_grader
[
index
])
return
CourseGradingModel
.
jsonize_grader
(
index
,
descriptor
.
raw_grader
[
index
])
@staticmethod
@staticmethod
def
update_cutoffs_from_json
(
course_location
,
cutoffs
):
def
update_cutoffs_from_json
(
course_location
,
cutoffs
,
user
):
"""
"""
Create or update the grade cutoffs for the given course. Returns sent in cutoffs (ie., no extra
Create or update the grade cutoffs for the given course. Returns sent in cutoffs (ie., no extra
db fetch).
db fetch).
...
@@ -107,14 +103,12 @@ class CourseGradingModel(object):
...
@@ -107,14 +103,12 @@ class CourseGradingModel(object):
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
.
grade_cutoffs
=
cutoffs
descriptor
.
grade_cutoffs
=
cutoffs
get_modulestore
(
course_old_location
)
.
update_item
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
)
return
cutoffs
return
cutoffs
@staticmethod
@staticmethod
def
update_grace_period_from_json
(
course_location
,
graceperiodjson
):
def
update_grace_period_from_json
(
course_location
,
graceperiodjson
,
user
):
"""
"""
Update the course's default grace period. Incoming dict is {hours: h, minutes: m} possibly as a
Update the course's default grace period. Incoming dict is {hours: h, minutes: m} possibly as a
grace_period entry in an enclosing dict. It is also safe to call this method with a value of
grace_period entry in an enclosing dict. It is also safe to call this method with a value of
...
@@ -132,12 +126,10 @@ class CourseGradingModel(object):
...
@@ -132,12 +126,10 @@ class CourseGradingModel(object):
grace_timedelta
=
timedelta
(
**
graceperiodjson
)
grace_timedelta
=
timedelta
(
**
graceperiodjson
)
descriptor
.
graceperiod
=
grace_timedelta
descriptor
.
graceperiod
=
grace_timedelta
get_modulestore
(
course_old_location
)
.
update_metadata
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
)
@staticmethod
@staticmethod
def
delete_grader
(
course_location
,
index
):
def
delete_grader
(
course_location
,
index
,
user
):
"""
"""
Delete the grader of the given type from the given course.
Delete the grader of the given type from the given course.
"""
"""
...
@@ -150,12 +142,10 @@ class CourseGradingModel(object):
...
@@ -150,12 +142,10 @@ class CourseGradingModel(object):
# force propagation to definition
# force propagation to definition
descriptor
.
raw_grader
=
descriptor
.
raw_grader
descriptor
.
raw_grader
=
descriptor
.
raw_grader
get_modulestore
(
course_old_location
)
.
update_item
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
)
@staticmethod
@staticmethod
def
delete_grace_period
(
course_location
):
def
delete_grace_period
(
course_location
,
user
):
"""
"""
Delete the course's grace period.
Delete the course's grace period.
"""
"""
...
@@ -164,9 +154,7 @@ class CourseGradingModel(object):
...
@@ -164,9 +154,7 @@ class CourseGradingModel(object):
del
descriptor
.
graceperiod
del
descriptor
.
graceperiod
get_modulestore
(
course_old_location
)
.
update_metadata
(
get_modulestore
(
course_old_location
)
.
update_item
(
descriptor
,
user
.
id
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
)
@staticmethod
@staticmethod
def
get_section_grader_type
(
location
):
def
get_section_grader_type
(
location
):
...
@@ -178,7 +166,7 @@ class CourseGradingModel(object):
...
@@ -178,7 +166,7 @@ class CourseGradingModel(object):
}
}
@staticmethod
@staticmethod
def
update_section_grader_type
(
descriptor
,
grader_type
):
def
update_section_grader_type
(
descriptor
,
grader_type
,
user
):
if
grader_type
is
not
None
and
grader_type
!=
u'notgraded'
:
if
grader_type
is
not
None
and
grader_type
!=
u'notgraded'
:
descriptor
.
format
=
grader_type
descriptor
.
format
=
grader_type
descriptor
.
graded
=
True
descriptor
.
graded
=
True
...
@@ -186,9 +174,7 @@ class CourseGradingModel(object):
...
@@ -186,9 +174,7 @@ class CourseGradingModel(object):
del
descriptor
.
format
del
descriptor
.
format
del
descriptor
.
graded
del
descriptor
.
graded
get_modulestore
(
descriptor
.
location
)
.
update_metadata
(
get_modulestore
(
descriptor
.
location
)
.
update_item
(
descriptor
,
user
.
id
)
descriptor
.
location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
)
return
{
'graderType'
:
grader_type
}
return
{
'graderType'
:
grader_type
}
@staticmethod
@staticmethod
...
...
cms/djangoapps/models/settings/course_metadata.py
View file @
16f0d12a
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
contentstore.utils
import
get_modulestore
from
contentstore.utils
import
get_modulestore
from
xmodule.modulestore.inheritance
import
own_metadata
from
cms.lib.xblock.mixin
import
CmsBlockMixin
from
cms.lib.xblock.mixin
import
CmsBlockMixin
...
@@ -48,7 +47,7 @@ class CourseMetadata(object):
...
@@ -48,7 +47,7 @@ class CourseMetadata(object):
return
result
return
result
@classmethod
@classmethod
def
update_from_json
(
cls
,
descriptor
,
jsondict
,
filter_tabs
=
True
):
def
update_from_json
(
cls
,
descriptor
,
jsondict
,
filter_tabs
=
True
,
user
=
None
):
"""
"""
Decode the json into CourseMetadata and save any changed attrs to the db.
Decode the json into CourseMetadata and save any changed attrs to the db.
...
@@ -78,6 +77,6 @@ class CourseMetadata(object):
...
@@ -78,6 +77,6 @@ class CourseMetadata(object):
setattr
(
descriptor
,
key
,
value
)
setattr
(
descriptor
,
key
,
value
)
if
dirty
:
if
dirty
:
get_modulestore
(
descriptor
.
location
)
.
update_
metadata
(
descriptor
.
location
,
own_metadata
(
descriptor
)
)
get_modulestore
(
descriptor
.
location
)
.
update_
item
(
descriptor
,
user
.
id
if
user
else
None
)
return
cls
.
fetch
(
descriptor
)
return
cls
.
fetch
(
descriptor
)
common/djangoapps/external_auth/tests/test_shib.py
View file @
16f0d12a
...
@@ -330,9 +330,7 @@ class ShibSPTest(ModuleStoreTestCase):
...
@@ -330,9 +330,7 @@ class ShibSPTest(ModuleStoreTestCase):
for
domain
in
[
""
,
"shib:https://idp.stanford.edu/"
]:
for
domain
in
[
""
,
"shib:https://idp.stanford.edu/"
]:
# set domains
# set domains
course
.
enrollment_domain
=
domain
course
.
enrollment_domain
=
domain
metadata
=
own_metadata
(
course
)
self
.
store
.
update_item
(
course
,
'**replace_user**'
)
metadata
[
'enrollment_domain'
]
=
domain
self
.
store
.
update_metadata
(
course
.
location
.
url
(),
metadata
)
# setting location to test that GET params get passed through
# setting location to test that GET params get passed through
login_request
=
self
.
request_factory
.
get
(
'/course_specific_login/MITx/999/Robot_Super_Course'
+
login_request
=
self
.
request_factory
.
get
(
'/course_specific_login/MITx/999/Robot_Super_Course'
+
...
@@ -401,15 +399,11 @@ class ShibSPTest(ModuleStoreTestCase):
...
@@ -401,15 +399,11 @@ class ShibSPTest(ModuleStoreTestCase):
# create 2 course, one with limited enrollment one without
# create 2 course, one with limited enrollment one without
shib_course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
shib_course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
shib_course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
shib_course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
metadata
=
own_metadata
(
shib_course
)
self
.
store
.
update_item
(
shib_course
,
'**replace_user**'
)
metadata
[
'enrollment_domain'
]
=
shib_course
.
enrollment_domain
self
.
store
.
update_metadata
(
shib_course
.
location
.
url
(),
metadata
)
open_enroll_course
=
CourseFactory
.
create
(
org
=
'MITx'
,
number
=
'999'
,
display_name
=
'Robot Super Course'
)
open_enroll_course
=
CourseFactory
.
create
(
org
=
'MITx'
,
number
=
'999'
,
display_name
=
'Robot Super Course'
)
open_enroll_course
.
enrollment_domain
=
''
open_enroll_course
.
enrollment_domain
=
''
metadata
=
own_metadata
(
open_enroll_course
)
self
.
store
.
update_item
(
open_enroll_course
,
'**replace_user**'
)
metadata
[
'enrollment_domain'
]
=
open_enroll_course
.
enrollment_domain
self
.
store
.
update_metadata
(
open_enroll_course
.
location
.
url
(),
metadata
)
# create 3 kinds of students, external_auth matching shib_course, external_auth not matching, no external auth
# create 3 kinds of students, external_auth matching shib_course, external_auth not matching, no external auth
shib_student
=
UserFactory
.
create
()
shib_student
=
UserFactory
.
create
()
...
@@ -475,9 +469,7 @@ class ShibSPTest(ModuleStoreTestCase):
...
@@ -475,9 +469,7 @@ class ShibSPTest(ModuleStoreTestCase):
course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
metadata
=
own_metadata
(
course
)
self
.
store
.
update_item
(
course
,
'**replace_user**'
)
metadata
[
'enrollment_domain'
]
=
course
.
enrollment_domain
self
.
store
.
update_metadata
(
course
.
location
.
url
(),
metadata
)
# use django test client for sessions and url processing
# use django test client for sessions and url processing
# no enrollment before trying
# no enrollment before trying
...
...
common/djangoapps/student/tests/test_login.py
View file @
16f0d12a
...
@@ -203,9 +203,7 @@ class ExternalAuthShibTest(ModuleStoreTestCase):
...
@@ -203,9 +203,7 @@ class ExternalAuthShibTest(ModuleStoreTestCase):
self
.
course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'456'
,
display_name
=
'NO SHIB'
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'456'
,
display_name
=
'NO SHIB'
)
self
.
shib_course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
self
.
shib_course
=
CourseFactory
.
create
(
org
=
'Stanford'
,
number
=
'123'
,
display_name
=
'Shib Only'
)
self
.
shib_course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
self
.
shib_course
.
enrollment_domain
=
'shib:https://idp.stanford.edu/'
metadata
=
own_metadata
(
self
.
shib_course
)
self
.
store
.
update_item
(
self
.
shib_course
,
'**replace_user**'
)
metadata
[
'enrollment_domain'
]
=
self
.
shib_course
.
enrollment_domain
self
.
store
.
update_metadata
(
self
.
shib_course
.
location
.
url
(),
metadata
)
self
.
user_w_map
=
UserFactory
.
create
(
email
=
'withmap@stanford.edu'
)
self
.
user_w_map
=
UserFactory
.
create
(
email
=
'withmap@stanford.edu'
)
self
.
extauth
=
ExternalAuthMap
(
external_id
=
'withmap@stanford.edu'
,
self
.
extauth
=
ExternalAuthMap
(
external_id
=
'withmap@stanford.edu'
,
external_email
=
'withmap@stanford.edu'
,
external_email
=
'withmap@stanford.edu'
,
...
...
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
16f0d12a
...
@@ -339,7 +339,7 @@ class ModuleStoreRead(object):
...
@@ -339,7 +339,7 @@ class ModuleStoreRead(object):
pass
pass
@abstractmethod
@abstractmethod
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
):
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
,
qualifiers
=
None
):
"""
"""
Returns a list of XModuleDescriptor instances for the items
Returns a list of XModuleDescriptor instances for the items
that match location. Any element of location that is None is treated
that match location. Any element of location that is None is treated
...
@@ -379,6 +379,15 @@ class ModuleStoreRead(object):
...
@@ -379,6 +379,15 @@ class ModuleStoreRead(object):
pass
pass
@abstractmethod
@abstractmethod
def
get_orphans
(
self
,
course_location
,
branch
):
"""
Get all of the xblocks in the given course which have no parents and are not of types which are
usually orphaned. NOTE: may include xblocks which still have references via xblocks which don't
use children to point to their dependents.
"""
pass
@abstractmethod
def
get_errored_courses
(
self
):
def
get_errored_courses
(
self
):
"""
"""
Return a dictionary of course_dir -> [(msg, exception_str)], for each
Return a dictionary of course_dir -> [(msg, exception_str)], for each
...
@@ -404,44 +413,34 @@ class ModuleStoreWrite(ModuleStoreRead):
...
@@ -404,44 +413,34 @@ class ModuleStoreWrite(ModuleStoreRead):
__metaclass__
=
ABCMeta
__metaclass__
=
ABCMeta
@abstractmethod
@abstractmethod
def
update_item
(
self
,
location
,
data
,
allow_not_found
=
False
):
def
update_item
(
self
,
xblock
,
user_id
=
None
,
allow_not_found
=
False
,
force
=
False
):
"""
"""
Set the data in the item specified by the location to
Update the given xblock's persisted repr. Pass the user's unique id which the persistent store
data
should save with the update if it has that ability.
location: Something that can be passed to Location
:param allow_not_found: whether this method should raise an exception if the given xblock
data: A nested dictionary of problem data
has not been persisted before.
"""
:param force: fork the structure and don't update the course draftVersion if there's a version
pass
conflict (only applicable to version tracking and conflict detecting persistence stores)
@abstractmethod
:raises VersionConflictError: if package_id and version_guid given and the current
def
update_children
(
self
,
location
,
children
):
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
Set the children for the item specified by the location to
children
location: Something that can be passed to Location
children: A list of child item identifiers
"""
"""
pass
pass
@abstractmethod
@abstractmethod
def
update_metadata
(
self
,
location
,
metadata
):
def
delete_item
(
self
,
location
,
user_id
=
None
,
delete_all_versions
=
False
,
delete_children
=
False
,
force
=
False
):
"""
"""
Set the metadata for the item specified by the location to
Delete an item from persistence. Pass the user's unique id which the persistent store
metadata
should save with the update if it has that ability.
location: Something that can be passed to Location
:param delete_all_versions: removes both the draft and published version of this item from
metadata: A nested dictionary of module metadata
the course if using draft and old mongo. Split may or may not implement this.
"""
:param force: fork the structure and don't update the course draftVersion if there's a version
pass
conflict (only applicable to version tracking and conflict detecting persistence stores)
@abstractmethod
:raises VersionConflictError: if package_id and version_guid given and the current
def
delete_item
(
self
,
location
):
version head != version_guid and force is not True. (only applicable to version tracking stores)
"""
Delete an item from this modulestore
location: Something that can be passed to Location
"""
"""
pass
pass
...
...
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
View file @
16f0d12a
...
@@ -163,8 +163,14 @@ class LocMapperStore(object):
...
@@ -163,8 +163,14 @@ class LocMapperStore(object):
else
:
else
:
raise
ItemNotFoundError
(
location
)
raise
ItemNotFoundError
(
location
)
elif
isinstance
(
block_id
,
dict
):
elif
isinstance
(
block_id
,
dict
):
# name is not unique, look through for the right category
# jump_to_id uses a None category.
if
location
.
category
in
block_id
:
if
location
.
category
is
None
:
if
len
(
block_id
)
==
1
:
# unique match (most common case)
block_id
=
block_id
.
values
()[
0
]
else
:
raise
InvalidLocationError
()
elif
location
.
category
in
block_id
:
block_id
=
block_id
[
location
.
category
]
block_id
=
block_id
[
location
.
category
]
elif
add_entry_if_missing
:
elif
add_entry_if_missing
:
block_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
block_id
=
self
.
_add_to_block_map
(
location
,
location_id
,
entry
[
'block_map'
])
...
...
common/lib/xmodule/xmodule/modulestore/locator.py
View file @
16f0d12a
...
@@ -488,6 +488,19 @@ class BlockUsageLocator(CourseLocator):
...
@@ -488,6 +488,19 @@ class BlockUsageLocator(CourseLocator):
raise
ValueError
(
'Could not parse "
%
s" as a package_id'
%
package_id
)
raise
ValueError
(
'Could not parse "
%
s" as a package_id'
%
package_id
)
self
.
_set_value
(
parse
,
'block'
,
self
.
set_block_id
)
self
.
_set_value
(
parse
,
'block'
,
self
.
set_block_id
)
@classmethod
def
make_relative
(
cls
,
course_locator
,
block_id
):
"""
Return a new instance which has the given block_id in the given course
:param course_locator: may be a BlockUsageLocator in the same snapshot
"""
return
BlockUsageLocator
(
package_id
=
course_locator
.
package_id
,
version_guid
=
course_locator
.
version_guid
,
branch
=
course_locator
.
branch
,
block_id
=
block_id
)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
"""
"""
Return a string representing this location.
Return a string representing this location.
...
...
common/lib/xmodule/xmodule/modulestore/mixed.py
View file @
16f0d12a
...
@@ -3,29 +3,43 @@ MixedModuleStore allows for aggregation between multiple modulestores.
...
@@ -3,29 +3,43 @@ MixedModuleStore allows for aggregation between multiple modulestores.
In this way, courses can be served up both - say - XMLModuleStore or MongoModuleStore
In this way, courses can be served up both - say - XMLModuleStore or MongoModuleStore
IMPORTANT: This modulestore only supports READONLY applications, e.g. LMS
"""
"""
from
.
import
ModuleStoreWriteBase
from
.
import
ModuleStoreWriteBase
from
xmodule.modulestore.django
import
create_modulestore_instance
from
xmodule.modulestore.django
import
create_modulestore_instance
,
loc_mapper
import
logging
import
logging
from
xmodule.modulestore
import
Location
from
xblock.fields
import
Reference
,
ReferenceList
,
String
from
xmodule.modulestore.locator
import
CourseLocator
,
Locator
,
BlockUsageLocator
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
,
ItemNotFoundError
from
xmodule.modulestore.parsers
import
ALLOWED_ID_CHARS
import
re
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
class
MixedModuleStore
(
ModuleStoreWriteBase
):
class
MixedModuleStore
(
ModuleStoreWriteBase
):
"""
"""
ModuleStore that can be backed by either XML or Mongo
ModuleStore knows how to route requests to the right persistence ms and how to convert any
references in the xblocks to the type required by the app and the persistence layer.
"""
"""
def
__init__
(
self
,
mappings
,
stores
,
**
kwargs
):
def
__init__
(
self
,
mappings
,
stores
,
reference_type
=
None
,
**
kwargs
):
"""
"""
Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a
Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a
collection of other modulestore configuration informations
collection of other modulestore configuration informations
:param reference_type: either Location or Locator to indicate what type of reference this app
uses.
"""
"""
super
(
MixedModuleStore
,
self
)
.
__init__
(
**
kwargs
)
super
(
MixedModuleStore
,
self
)
.
__init__
(
**
kwargs
)
self
.
modulestores
=
{}
self
.
modulestores
=
{}
self
.
mappings
=
mappings
self
.
mappings
=
mappings
# temporary code for transition period
if
reference_type
is
None
:
log
.
warn
(
"reference_type not specified in MixedModuleStore settings.
%
s"
,
"Will default temporarily to the to-be-deprecated Location."
)
self
.
use_locations
=
(
reference_type
!=
'Locator'
)
if
'default'
not
in
stores
:
if
'default'
not
in
stores
:
raise
Exception
(
'Missing a default modulestore in the MixedModuleStore __init__ method.'
)
raise
Exception
(
'Missing a default modulestore in the MixedModuleStore __init__ method.'
)
...
@@ -45,60 +59,203 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -45,60 +59,203 @@ class MixedModuleStore(ModuleStoreWriteBase):
mapping
=
self
.
mappings
.
get
(
course_id
,
'default'
)
mapping
=
self
.
mappings
.
get
(
course_id
,
'default'
)
return
self
.
modulestores
[
mapping
]
return
self
.
modulestores
[
mapping
]
def
has_item
(
self
,
course_id
,
location
):
def
_locator_to_location
(
self
,
reference
):
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
has_item
(
course_id
,
location
)
"""
Convert the referenced locator to a location casting to and from a string as necessary
"""
stringify
=
isinstance
(
reference
,
basestring
)
if
stringify
:
reference
=
BlockUsageLocator
(
url
=
reference
)
location
=
loc_mapper
()
.
translate_locator_to_location
(
reference
)
return
location
.
url
()
if
stringify
else
location
def
get_item
(
self
,
location
,
depth
=
0
):
def
_location_to_locator
(
self
,
course_id
,
reference
):
"""
"""
This method is explicitly not implemented as we need a course_id to disambiguate
Convert the referenced location to a locator casting to and from a string as necessary
We should be able to fix this when the data-model rearchitecting is done
"""
"""
raise
NotImplementedError
stringify
=
isinstance
(
reference
,
basestring
)
if
stringify
:
reference
=
Location
(
reference
)
locator
=
loc_mapper
()
.
translate_location
(
course_id
,
reference
,
reference
.
revision
==
'draft'
,
True
)
return
unicode
(
locator
)
if
stringify
else
locator
def
get_instance
(
self
,
course_id
,
location
,
depth
=
0
):
def
_incoming_reference_adaptor
(
self
,
store
,
course_id
,
reference
):
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_instance
(
course_id
,
location
,
depth
)
"""
Convert the reference to the type the persistence layer wants
"""
if
issubclass
(
store
.
reference_type
,
Location
if
self
.
use_locations
else
Locator
):
return
reference
if
store
.
reference_type
==
Location
:
return
self
.
_locator_to_location
(
reference
)
return
self
.
_location_to_locator
(
course_id
,
reference
)
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
):
def
_outgoing_reference_adaptor
(
self
,
store
,
course_id
,
reference
):
"""
"""
Returns a list of XModuleDescriptor instances for the items
Convert the reference to the type the application wants
that match location. Any element of location that is None is treated
"""
as a wildcard that matches any value
if
issubclass
(
store
.
reference_type
,
Location
if
self
.
use_locations
else
Locator
):
return
reference
if
store
.
reference_type
==
Location
:
return
self
.
_location_to_locator
(
course_id
,
reference
)
return
self
.
_locator_to_location
(
reference
)
location: Something that can be passed to Location
def
_xblock_adaptor_iterator
(
self
,
adaptor
,
string_converter
,
store
,
course_id
,
xblock
):
"""
Change all reference fields in this xblock to the type expected by the receiving layer
"""
for
field
in
xblock
.
fields
.
itervalues
():
if
field
.
is_set_on
(
xblock
):
if
isinstance
(
field
,
Reference
):
field
.
write_to
(
xblock
,
adaptor
(
store
,
course_id
,
field
.
read_from
(
xblock
))
)
elif
isinstance
(
field
,
ReferenceList
):
field
.
write_to
(
xblock
,
[
adaptor
(
store
,
course_id
,
ref
)
for
ref
in
field
.
read_from
(
xblock
)
]
)
elif
isinstance
(
field
,
String
):
# replace links within the string
string_converter
(
field
,
xblock
)
return
xblock
depth: An argument that some module stores may use to prefetch
def
_incoming_xblock_adaptor
(
self
,
store
,
course_id
,
xblock
):
descendents of the queried modules for more efficient results later
"""
in the request. The depth is counted in the number of calls to
Change all reference fields in this xblock to the type expected by the persistence layer
get_children() to cache. None indicates to cache all descendents
"""
string_converter
=
self
.
_get_string_converter
(
course_id
,
store
.
reference_type
,
xblock
.
location
)
return
self
.
_xblock_adaptor_iterator
(
self
.
_incoming_reference_adaptor
,
string_converter
,
store
,
course_id
,
xblock
)
def
_outgoing_xblock_adaptor
(
self
,
store
,
course_id
,
xblock
):
"""
"""
if
not
course_id
:
Change all reference fields in this xblock to the type expected by the persistence layer
raise
Exception
(
"Must pass in a course_id when calling get_items() with MixedModuleStore"
)
"""
string_converter
=
self
.
_get_string_converter
(
course_id
,
xblock
.
location
.
__class__
,
xblock
.
location
)
return
self
.
_xblock_adaptor_iterator
(
self
.
_outgoing_reference_adaptor
,
string_converter
,
store
,
course_id
,
xblock
)
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_items
(
location
,
course_id
,
depth
)
CONVERT_RE
=
re
.
compile
(
r"/jump_to_id/({}+)"
.
format
(
ALLOWED_ID_CHARS
)
)
def
update_item
(
self
,
location
,
data
,
allow_not_found
=
False
):
def
_get_string_converter
(
self
,
course_id
,
reference_type
,
from_base_addr
):
"""
"""
MixedModuleStore is for read-only (aka LMS)
Return a closure which finds and replaces all embedded links in a string field
with the correct rewritten link for the target type
"""
"""
raise
NotImplementedError
if
self
.
use_locations
and
reference_type
==
Location
:
return
lambda
field
,
xblock
:
None
if
not
self
.
use_locations
and
issubclass
(
reference_type
,
Locator
):
return
lambda
field
,
xblock
:
None
if
isinstance
(
from_base_addr
,
Location
):
def
mapper
(
found_id
):
"""
Convert the found id to BlockUsageLocator block_id
"""
location
=
from_base_addr
.
replace
(
category
=
None
,
name
=
found_id
)
# NOTE without category, it cannot create a new mapping if there's not one already
return
loc_mapper
()
.
translate_location
(
course_id
,
location
)
.
block_id
else
:
def
mapper
(
found_id
):
"""
Convert the found id to Location block_id
"""
locator
=
BlockUsageLocator
.
make_relative
(
from_base_addr
,
found_id
)
return
loc_mapper
()
.
translate_locator_to_location
(
locator
)
.
name
def
update_children
(
self
,
location
,
children
):
def
converter
(
field
,
xblock
):
"""
Find all of the ids in the block and replace them w/ their mapped values
"""
value
=
field
.
read_from
(
xblock
)
self
.
CONVERT_RE
.
sub
(
mapper
,
value
)
field
.
write_to
(
xblock
,
value
)
return
converter
def
has_item
(
self
,
course_id
,
reference
):
"""
"""
MixedModuleStore is for read-only (aka LMS)
Does the course include the xblock who's id is reference?
:param course_id: a course_id or package_id (slashed or dotted)
:param reference: a Location or BlockUsageLocator
"""
"""
raise
NotImplementedError
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
decoded_ref
=
self
.
_incoming_reference_adaptor
(
store
,
course_id
,
reference
)
return
store
.
has_item
(
course_id
,
decoded_ref
)
def
update_metadata
(
self
,
location
,
metadata
):
def
get_item
(
self
,
location
,
depth
=
0
):
"""
"""
MixedModuleStore is for read-only (aka LMS)
This method is explicitly not implemented as we need a course_id to disambiguate
We should be able to fix this when the data-model rearchitecting is done
"""
"""
raise
NotImplementedError
raise
NotImplementedError
def
delete_item
(
self
,
location
):
def
get_instance
(
self
,
course_id
,
location
,
depth
=
0
):
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
decoded_ref
=
self
.
_incoming_reference_adaptor
(
store
,
course_id
,
location
)
xblock
=
store
.
get_instance
(
course_id
,
decoded_ref
,
depth
)
return
self
.
_outgoing_xblock_adaptor
(
store
,
course_id
,
xblock
)
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
,
qualifiers
=
None
):
"""
"""
MixedModuleStore is for read-only (aka LMS)
Returns a list of XModuleDescriptor instances for the items
that match location. Any element of location that is None is treated
as a wildcard that matches any value. NOTE: don't use this to look for courses
as the course_id is required. Use get_courses.
location: either a Location possibly w/ None as wildcards for category or name or
a Locator with at least a package_id and branch but possibly no block_id.
depth: An argument that some module stores may use to prefetch
descendents of the queried modules for more efficient results later
in the request. The depth is counted in the number of calls to
get_children() to cache. None indicates to cache all descendents
"""
"""
raise
NotImplementedError
if
not
(
course_id
or
hasattr
(
location
,
'package_id'
)):
raise
Exception
(
"Must pass in a course_id when calling get_items()"
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
or
getattr
(
location
,
'package_id'
))
# translate won't work w/ missing fields so work around it
if
store
.
reference_type
==
Location
:
if
not
self
.
use_locations
:
if
getattr
(
location
,
'block_id'
,
False
):
location
=
self
.
_incoming_reference_adaptor
(
store
,
course_id
,
location
)
else
:
# get the course's location
location
=
loc_mapper
()
.
translate_locator_to_location
(
location
,
get_course
=
True
)
# now remove the unknowns
location
=
location
.
replace
(
category
=
qualifiers
.
get
(
'category'
,
None
),
name
=
None
)
else
:
if
self
.
use_locations
:
if
not
isinstance
(
location
,
Location
):
location
=
Location
(
location
)
try
:
location
.
ensure_fully_specified
()
location
=
loc_mapper
()
.
translate_location
(
course_id
,
location
,
location
.
revision
==
'published'
,
True
)
except
InsufficientSpecificationError
:
# construct the Locator by hand
if
location
.
category
is
not
None
and
qualifiers
.
get
(
'category'
,
False
):
qualifiers
[
'category'
]
=
location
.
category
location
=
loc_mapper
()
.
translate_location_to_course_locator
(
course_id
,
location
,
location
.
revision
==
'published'
)
xblocks
=
store
.
get_items
(
location
,
course_id
,
depth
,
qualifiers
)
xblocks
=
[
self
.
_outgoing_xblock_adaptor
(
store
,
course_id
,
xblock
)
for
xblock
in
xblocks
]
return
xblocks
def
get_courses
(
self
):
def
get_courses
(
self
):
'''
'''
...
@@ -126,15 +283,50 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -126,15 +283,50 @@ class MixedModuleStore(ModuleStoreWriteBase):
def
get_course
(
self
,
course_id
):
def
get_course
(
self
,
course_id
):
"""
"""
returns the course module associated with the course_id
returns the course module associated with the course_id. If no such course exists,
it returns None
:param course_id: must be either a string course_id or a CourseLocator
"""
"""
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_course
(
course_id
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
.
package_id
if
hasattr
(
course_id
,
'package_id'
)
else
course_id
)
try
:
# translate won't work w/ missing fields so work around it
if
store
.
reference_type
==
Location
:
# takes the course_id: figure out if this is old or new style
if
not
self
.
use_locations
:
if
isinstance
(
course_id
,
basestring
):
course_id
=
CourseLocator
(
package_id
=
course_id
,
branch
=
'published'
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_id
,
get_course
=
True
)
course_id
=
course_location
.
course_id
xblock
=
store
.
get_course
(
course_id
)
else
:
# takes a courseLocator
if
isinstance
(
course_id
,
CourseLocator
):
location
=
course_id
course_id
=
None
# not an old style course_id; so, don't use it further
elif
'/'
in
course_id
:
location
=
loc_mapper
()
.
translate_location_to_course_locator
(
course_id
,
None
,
True
)
else
:
location
=
CourseLocator
(
package_id
=
course_id
,
branch
=
'published'
)
course_id
=
None
# not an old style course_id; so, don't use it further
xblock
=
store
.
get_course
(
location
)
except
ItemNotFoundError
:
return
None
if
xblock
is
not
None
:
return
self
.
_outgoing_xblock_adaptor
(
store
,
course_id
,
xblock
)
else
:
return
None
def
get_parent_locations
(
self
,
location
,
course_id
):
def
get_parent_locations
(
self
,
location
,
course_id
):
"""
"""
returns the parent locations for a given l
co
ation and course_id
returns the parent locations for a given l
oc
ation and course_id
"""
"""
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_parent_locations
(
location
,
course_id
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
decoded_ref
=
self
.
_incoming_reference_adaptor
(
store
,
course_id
,
location
)
parents
=
store
.
get_parent_locations
(
decoded_ref
,
course_id
)
return
[
self
.
_outgoing_reference_adaptor
(
store
,
course_id
,
reference
)
for
reference
in
parents
]
def
get_modulestore_type
(
self
,
course_id
):
def
get_modulestore_type
(
self
,
course_id
):
"""
"""
...
@@ -146,6 +338,17 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -146,6 +338,17 @@ class MixedModuleStore(ModuleStoreWriteBase):
"""
"""
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_modulestore_type
(
course_id
)
return
self
.
_get_modulestore_for_courseid
(
course_id
)
.
get_modulestore_type
(
course_id
)
def
get_orphans
(
self
,
course_location
,
branch
):
"""
Get all of the xblocks in the given course which have no parents and are not of types which are
usually orphaned. NOTE: may include xblocks which still have references via xblocks which don't
use children to point to their dependents.
"""
course_id
=
getattr
(
course_location
,
'course_id'
,
getattr
(
course_location
,
'package_id'
,
None
))
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
decoded_ref
=
self
.
_incoming_reference_adaptor
(
store
,
course_id
,
course_location
)
return
store
.
get_orphans
(
decoded_ref
,
branch
)
def
get_errored_courses
(
self
):
def
get_errored_courses
(
self
):
"""
"""
Return a dictionary of course_dir -> [(msg, exception_str)], for each
Return a dictionary of course_dir -> [(msg, exception_str)], for each
...
@@ -155,3 +358,32 @@ class MixedModuleStore(ModuleStoreWriteBase):
...
@@ -155,3 +358,32 @@ class MixedModuleStore(ModuleStoreWriteBase):
for
store
in
self
.
modulestores
.
values
():
for
store
in
self
.
modulestores
.
values
():
errs
.
update
(
store
.
get_errored_courses
())
errs
.
update
(
store
.
get_errored_courses
())
return
errs
return
errs
def
update_item
(
self
,
xblock
,
user_id
,
allow_not_found
=
False
):
"""
Update the xblock persisted to be the same as the given for all types of fields
(content, children, and metadata) attribute the change to the given user.
"""
if
self
.
use_locations
:
raise
NotImplementedError
locator
=
xblock
.
location
course_id
=
locator
.
package_id
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
# if an xblock, convert its contents to correct addr scheme
xblock
=
self
.
_incoming_xblock_adaptor
(
store
,
course_id
,
xblock
)
xblock
=
store
.
update_item
(
xblock
,
user_id
)
return
self
.
_outgoing_xblock_adaptor
(
store
,
course_id
,
xblock
)
def
delete_item
(
self
,
location
,
**
kwargs
):
"""
Delete the given item from persistence.
"""
if
self
.
use_locations
:
raise
NotImplementedError
store
=
self
.
_get_modulestore_for_courseid
(
location
.
package_id
)
decoded_ref
=
self
.
_incoming_reference_adaptor
(
store
,
location
.
package_id
,
location
)
return
store
.
delete_item
(
decoded_ref
,
**
kwargs
)
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
16f0d12a
...
@@ -38,10 +38,6 @@ from xblock.core import XBlock
...
@@ -38,10 +38,6 @@ from xblock.core import XBlock
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
# TODO (cpennington): This code currently operates under the assumption that
# there is only one revision for each item. Once we start versioning inside the CMS,
# that assumption will have to change
def
get_course_id_no_run
(
location
):
def
get_course_id_no_run
(
location
):
'''
'''
...
@@ -224,6 +220,7 @@ def namedtuple_to_son(namedtuple, prefix=''):
...
@@ -224,6 +220,7 @@ def namedtuple_to_son(namedtuple, prefix=''):
Converts a namedtuple into a SON object with the same key order
Converts a namedtuple into a SON object with the same key order
"""
"""
son
=
SON
()
son
=
SON
()
# pylint: disable=protected-access
for
idx
,
field_name
in
enumerate
(
namedtuple
.
_fields
):
for
idx
,
field_name
in
enumerate
(
namedtuple
.
_fields
):
son
[
prefix
+
field_name
]
=
namedtuple
[
idx
]
son
[
prefix
+
field_name
]
=
namedtuple
[
idx
]
return
son
return
son
...
@@ -258,6 +255,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -258,6 +255,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
"""
A Mongodb backed ModuleStore
A Mongodb backed ModuleStore
"""
"""
reference_type
=
Location
# TODO (cpennington): Enable non-filesystem filestores
# TODO (cpennington): Enable non-filesystem filestores
# pylint: disable=C0103
# pylint: disable=C0103
...
@@ -299,9 +297,11 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -299,9 +297,11 @@ class MongoModuleStore(ModuleStoreWriteBase):
# Force mongo to maintain an index over _id.* that is in the same order
# Force mongo to maintain an index over _id.* that is in the same order
# that is used when querying by a location
# that is used when querying by a location
# pylint: disable=no-member, protected_access
self
.
collection
.
ensure_index
(
self
.
collection
.
ensure_index
(
zip
((
'_id.'
+
field
for
field
in
Location
.
_fields
),
repeat
(
1
)),
zip
((
'_id.'
+
field
for
field
in
Location
.
_fields
),
repeat
(
1
)),
)
)
# pylint: enable=no-member, protected_access
if
default_class
is
not
None
:
if
default_class
is
not
None
:
module_path
,
_
,
class_name
=
default_class
.
rpartition
(
'.'
)
module_path
,
_
,
class_name
=
default_class
.
rpartition
(
'.'
)
...
@@ -363,11 +363,6 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -363,11 +363,6 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
"""
Helper method for computing inherited metadata for a specific location url
Helper method for computing inherited metadata for a specific location url
"""
"""
# check for presence of metadata key. Note that a given module may not yet be fully formed.
# example: update_item -> update_children -> update_metadata sequence on new item create
# if we get called here without update_metadata called first then 'metadata' hasn't been set
# as we're not fully transactional at the DB layer. Same comment applies to below key name
# check
my_metadata
=
results_by_url
[
url
]
.
get
(
'metadata'
,
{})
my_metadata
=
results_by_url
[
url
]
.
get
(
'metadata'
,
{})
# go through all the children and recurse, but only if we have
# go through all the children and recurse, but only if we have
...
@@ -443,6 +438,9 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -443,6 +438,9 @@ class MongoModuleStore(ModuleStoreWriteBase):
del
item
[
'_id'
]
del
item
[
'_id'
]
def
_query_children_for_cache_children
(
self
,
items
):
def
_query_children_for_cache_children
(
self
,
items
):
"""
Generate a pymongo in query for finding the items and return the payloads
"""
# first get non-draft in a round-trip
# first get non-draft in a round-trip
query
=
{
query
=
{
'_id'
:
{
'$in'
:
[
namedtuple_to_son
(
Location
(
item
))
for
item
in
items
]}
'_id'
:
{
'$in'
:
[
namedtuple_to_son
(
Location
(
item
))
for
item
in
items
]}
...
@@ -531,8 +529,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -531,8 +529,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
'''
'''
Returns a list of course descriptors.
Returns a list of course descriptors.
'''
'''
# TODO (vshnayder): Why do I have to specify i4x here?
course_filter
=
Location
(
category
=
"course"
)
course_filter
=
Location
(
"i4x"
,
category
=
"course"
)
return
[
return
[
course
course
for
course
for
course
...
@@ -556,6 +553,16 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -556,6 +553,16 @@ class MongoModuleStore(ModuleStoreWriteBase):
raise
ItemNotFoundError
(
location
)
raise
ItemNotFoundError
(
location
)
return
item
return
item
def
get_course
(
self
,
course_id
):
"""
Get the course with the given courseid (org/course/run)
"""
id_components
=
course_id
.
split
(
'/'
)
try
:
return
self
.
get_item
(
Location
(
'i4x'
,
id_components
[
0
],
id_components
[
1
],
'course'
,
id_components
[
2
]))
except
ItemNotFoundError
:
return
None
def
has_item
(
self
,
course_id
,
location
):
def
has_item
(
self
,
course_id
,
location
):
"""
"""
Returns True if location exists in this ModuleStore.
Returns True if location exists in this ModuleStore.
...
@@ -599,7 +606,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -599,7 +606,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
"""
return
self
.
get_item
(
location
,
depth
=
depth
)
return
self
.
get_item
(
location
,
depth
=
depth
)
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
):
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
,
qualifiers
=
None
):
items
=
self
.
collection
.
find
(
items
=
self
.
collection
.
find
(
location_to_query
(
location
),
location_to_query
(
location
),
sort
=
[(
'revision'
,
pymongo
.
ASCENDING
)],
sort
=
[(
'revision'
,
pymongo
.
ASCENDING
)],
...
@@ -664,13 +671,13 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -664,13 +671,13 @@ class MongoModuleStore(ModuleStoreWriteBase):
# Save any changes to the xmodule to the MongoKeyValueStore
# Save any changes to the xmodule to the MongoKeyValueStore
xmodule
.
save
()
xmodule
.
save
()
self
.
collection
.
save
({
self
.
collection
.
save
({
'_id'
:
xmodule
.
location
.
dict
(
),
'_id'
:
namedtuple_to_son
(
xmodule
.
location
),
'metadata'
:
own_metadata
(
xmodule
),
'metadata'
:
own_metadata
(
xmodule
),
'definition'
:
{
'definition'
:
{
'data'
:
xmodule
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
),
'data'
:
xmodule
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
),
'children'
:
xmodule
.
children
if
xmodule
.
has_children
else
[]
'children'
:
xmodule
.
children
if
xmodule
.
has_children
else
[]
}
}
})
})
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
xmodule
.
location
)
self
.
refresh_cached_metadata_inheritance_tree
(
xmodule
.
location
)
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
xmodule
.
location
),
xmodule
.
location
)
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
xmodule
.
location
),
xmodule
.
location
)
...
@@ -708,7 +715,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -708,7 +715,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
course
.
tabs
=
existing_tabs
course
.
tabs
=
existing_tabs
# Save any changes to the course to the MongoKeyValueStore
# Save any changes to the course to the MongoKeyValueStore
course
.
save
()
course
.
save
()
self
.
update_
metadata
(
course
.
location
,
course
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
)
self
.
update_
item
(
course
,
'**replace_user**'
)
def
fire_updated_modulestore_signal
(
self
,
course_id
,
location
):
def
fire_updated_modulestore_signal
(
self
,
course_id
,
location
):
"""
"""
...
@@ -754,7 +761,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -754,7 +761,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
# See http://www.mongodb.org/display/DOCS/Updating for
# See http://www.mongodb.org/display/DOCS/Updating for
# atomic update syntax
# atomic update syntax
result
=
self
.
collection
.
update
(
result
=
self
.
collection
.
update
(
{
'_id'
:
Location
(
location
)
.
dict
(
)},
{
'_id'
:
namedtuple_to_son
(
Location
(
location
)
)},
{
'$set'
:
update
},
{
'$set'
:
update
},
multi
=
False
,
multi
=
False
,
upsert
=
True
,
upsert
=
True
,
...
@@ -765,73 +772,56 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -765,73 +772,56 @@ class MongoModuleStore(ModuleStoreWriteBase):
if
result
[
'n'
]
==
0
:
if
result
[
'n'
]
==
0
:
raise
ItemNotFoundError
(
location
)
raise
ItemNotFoundError
(
location
)
def
update_item
(
self
,
location
,
data
,
allow_not_found
=
False
):
def
update_item
(
self
,
xblock
,
user
,
allow_not_found
=
False
):
"""
"""
Set the data in the item specified by the location to
Update the persisted version of xblock to reflect its current values.
data
location: Something that can be passed to Location
location: Something that can be passed to Location
data: A nested dictionary of problem data
data: A nested dictionary of problem data
"""
"""
try
:
try
:
self
.
_update_single_item
(
location
,
{
'definition.data'
:
data
})
definition_data
=
xblock
.
get_explicitly_set_fields_by_scope
()
if
len
(
definition_data
)
==
1
and
'data'
in
definition_data
:
definition_data
=
definition_data
[
'data'
]
payload
=
{
'definition.data'
:
definition_data
,
'metadata'
:
own_metadata
(
xblock
),
}
if
xblock
.
has_children
:
# convert all to urls
xblock
.
children
=
[
child
.
url
()
if
isinstance
(
child
,
Location
)
else
child
for
child
in
xblock
.
children
]
payload
.
update
({
'definition.children'
:
xblock
.
children
})
self
.
_update_single_item
(
xblock
.
location
,
payload
)
# for static tabs, their containing course also records their display name
if
xblock
.
category
==
'static_tab'
:
course
=
self
.
_get_course_for_item
(
xblock
.
location
)
# find the course's reference to this tab and update the name.
for
tab
in
course
.
tabs
:
if
tab
.
get
(
'url_slug'
)
==
xblock
.
location
.
name
:
# only update if changed
if
tab
[
'name'
]
!=
xblock
.
display_name
:
tab
[
'name'
]
=
xblock
.
display_name
self
.
update_item
(
course
,
user
)
break
# recompute (and update) the metadata inheritance tree which is cached
# was conditional on children or metadata having changed before dhm made one update to rule them all
self
.
refresh_cached_metadata_inheritance_tree
(
xblock
.
location
)
# fire signal that we've written to DB
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
xblock
.
location
),
xblock
.
location
)
except
ItemNotFoundError
:
except
ItemNotFoundError
:
if
not
allow_not_found
:
if
not
allow_not_found
:
raise
raise
def
update_children
(
self
,
location
,
children
):
# pylint: disable=unused-argument
"""
def
delete_item
(
self
,
location
,
**
kwargs
):
Set the children for the item specified by the location to
children
location: Something that can be passed to Location
children: A list of child item identifiers
"""
# Normalize the children to urls
children
=
[
Location
(
child
)
.
url
()
for
child
in
children
]
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
# fire signal that we've written to DB
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
def
update_metadata
(
self
,
location
,
metadata
):
"""
Set the metadata for the item specified by the location to
metadata
location: Something that can be passed to Location
metadata: A nested dictionary of module metadata
"""
# VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so
# if we add one then we need to also add it to the policy information (i.e. metadata)
# we should remove this once we can break this reference from the course to static tabs
loc
=
Location
(
location
)
if
loc
.
category
==
'static_tab'
:
course
=
self
.
_get_course_for_item
(
loc
)
existing_tabs
=
course
.
tabs
or
[]
for
tab
in
existing_tabs
:
if
tab
.
get
(
'url_slug'
)
==
loc
.
name
:
tab
[
'name'
]
=
metadata
.
get
(
'display_name'
,
tab
.
get
(
'name'
))
break
course
.
tabs
=
existing_tabs
# Save the updates to the course to the MongoKeyValueStore
course
.
save
()
self
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
self
.
_update_single_item
(
location
,
{
'metadata'
:
metadata
})
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
loc
)
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
def
delete_item
(
self
,
location
,
delete_all_versions
=
False
):
"""
"""
Delete an item from this modulestore
Delete an item from this modulestore
location: Something that can be passed to Location
location: Something that can be passed to Location
delete_all_versions: is here because the DraftMongoModuleStore needs it and we need to keep the interface the same. It is unused.
"""
"""
# pylint: enable=unused-argument
# VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so
# VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so
# if we add one then we need to also add it to the policy information (i.e. metadata)
# if we add one then we need to also add it to the policy information (i.e. metadata)
# we should remove this once we can break this reference from the course to static tabs
# we should remove this once we can break this reference from the course to static tabs
...
@@ -840,9 +830,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -840,9 +830,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
course
=
self
.
_get_course_for_item
(
item
.
location
)
course
=
self
.
_get_course_for_item
(
item
.
location
)
existing_tabs
=
course
.
tabs
or
[]
existing_tabs
=
course
.
tabs
or
[]
course
.
tabs
=
[
tab
for
tab
in
existing_tabs
if
tab
.
get
(
'url_slug'
)
!=
location
.
name
]
course
.
tabs
=
[
tab
for
tab
in
existing_tabs
if
tab
.
get
(
'url_slug'
)
!=
location
.
name
]
# Save the updates to the course to the MongoKeyValueStore
self
.
update_item
(
course
,
'**replace_user**'
)
course
.
save
()
self
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
# Must include this to avoid the django debug toolbar (which defines the deprecated "safe=False")
# Must include this to avoid the django debug toolbar (which defines the deprecated "safe=False")
# from overriding our default value set in the init method.
# from overriding our default value set in the init method.
...
@@ -889,7 +877,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -889,7 +877,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
item_locs
-=
all_reachable
item_locs
-=
all_reachable
return
list
(
item_locs
)
return
list
(
item_locs
)
def
_create_new_field_data
(
self
,
category
,
location
,
definition_data
,
metadata
):
def
_create_new_field_data
(
self
,
_category
,
_
location
,
definition_data
,
metadata
):
"""
"""
To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs
To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
View file @
16f0d12a
...
@@ -11,11 +11,9 @@ from datetime import datetime
...
@@ -11,11 +11,9 @@ from datetime import datetime
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
DuplicateItemError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
DuplicateItemError
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.mongo.base
import
location_to_query
,
namedtuple_to_son
,
get_course_id_no_run
,
MongoModuleStore
from
xmodule.modulestore.mongo.base
import
location_to_query
,
namedtuple_to_son
,
get_course_id_no_run
,
MongoModuleStore
import
pymongo
import
pymongo
from
pytz
import
UTC
from
pytz
import
UTC
from
xblock.fields
import
Scope
DRAFT
=
'draft'
DRAFT
=
'draft'
# Things w/ these categories should never be marked as version='draft'
# Things w/ these categories should never be marked as version='draft'
...
@@ -109,7 +107,7 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -109,7 +107,7 @@ class DraftModuleStore(MongoModuleStore):
return
super
(
DraftModuleStore
,
self
)
.
create_xmodule
(
draft_loc
,
definition_data
,
metadata
,
system
)
return
super
(
DraftModuleStore
,
self
)
.
create_xmodule
(
draft_loc
,
definition_data
,
metadata
,
system
)
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
):
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
,
qualifiers
=
None
):
"""
"""
Returns a list of XModuleDescriptor instances for the items
Returns a list of XModuleDescriptor instances for the items
that match location. Any element of location that is None is treated
that match location. Any element of location that is None is treated
...
@@ -146,7 +144,9 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -146,7 +144,9 @@ class DraftModuleStore(MongoModuleStore):
draft_location
=
as_draft
(
source_location
)
draft_location
=
as_draft
(
source_location
)
if
draft_location
.
category
in
DIRECT_ONLY_CATEGORIES
:
if
draft_location
.
category
in
DIRECT_ONLY_CATEGORIES
:
raise
InvalidVersionError
(
source_location
)
raise
InvalidVersionError
(
source_location
)
original
[
'_id'
]
=
draft_location
.
dict
()
if
not
original
:
raise
ItemNotFoundError
original
[
'_id'
]
=
namedtuple_to_son
(
draft_location
)
try
:
try
:
self
.
collection
.
insert
(
original
)
self
.
collection
.
insert
(
original
)
except
pymongo
.
errors
.
DuplicateKeyError
:
except
pymongo
.
errors
.
DuplicateKeyError
:
...
@@ -157,60 +157,27 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -157,60 +157,27 @@ class DraftModuleStore(MongoModuleStore):
return
self
.
_load_items
([
original
])[
0
]
return
self
.
_load_items
([
original
])[
0
]
def
update_item
(
self
,
location
,
data
,
allow_not_found
=
False
):
def
update_item
(
self
,
xblock
,
user
,
allow_not_found
=
False
):
"""
"""
Set the data in the item specified by the location to
Save the current values to persisted version of the xblock
data
location: Something that can be passed to Location
location: Something that can be passed to Location
data: A nested dictionary of problem data
data: A nested dictionary of problem data
"""
"""
draft_loc
=
as_draft
(
location
)
draft_loc
=
as_draft
(
xblock
.
location
)
try
:
try
:
draft_item
=
self
.
get_item
(
location
)
if
not
self
.
has_item
(
None
,
draft_loc
):
if
not
getattr
(
draft_item
,
'is_draft'
,
False
):
self
.
convert_to_draft
(
xblock
.
location
)
self
.
convert_to_draft
(
location
)
except
ItemNotFoundError
:
except
ItemNotFoundError
,
e
:
if
not
allow_not_found
:
if
not
allow_not_found
:
raise
e
raise
return
super
(
DraftModuleStore
,
self
)
.
update_item
(
draft_loc
,
data
)
def
update_children
(
self
,
location
,
children
):
"""
Set the children for the item specified by the location to
children
location: Something that can be passed to Location
children: A list of child item identifiers
"""
draft_loc
=
as_draft
(
location
)
draft_item
=
self
.
get_item
(
location
)
if
not
getattr
(
draft_item
,
'is_draft'
,
False
):
self
.
convert_to_draft
(
location
)
return
super
(
DraftModuleStore
,
self
)
.
update_children
(
draft_loc
,
children
)
def
update_metadata
(
self
,
location
,
metadata
):
"""
Set the metadata for the item specified by the location to
metadata
location: Something that can be passed to Location
metadata: A nested dictionary of module metadata
"""
draft_loc
=
as_draft
(
location
)
draft_item
=
self
.
get_item
(
location
)
if
not
getattr
(
draft_item
,
'is_draft'
,
False
):
self
.
convert_to_draft
(
location
)
if
'is_draft'
in
metadata
:
del
metadata
[
'is_draft'
]
return
super
(
DraftModuleStore
,
self
)
.
update_metadata
(
draft_loc
,
metadata
)
xblock
.
location
=
draft_loc
super
(
DraftModuleStore
,
self
)
.
update_item
(
xblock
,
user
)
# don't allow locations to truly represent themselves as draft outside of this file
xblock
.
location
=
as_published
(
xblock
.
location
)
def
delete_item
(
self
,
location
,
delete_all_versions
=
False
):
def
delete_item
(
self
,
location
,
delete_all_versions
=
False
,
**
kwargs
):
"""
"""
Delete an item from this modulestore
Delete an item from this modulestore
...
@@ -243,7 +210,6 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -243,7 +210,6 @@ class DraftModuleStore(MongoModuleStore):
draft
.
published_date
=
datetime
.
now
(
UTC
)
draft
.
published_date
=
datetime
.
now
(
UTC
)
draft
.
published_by
=
published_by_id
draft
.
published_by
=
published_by_id
super
(
DraftModuleStore
,
self
)
.
update_item
(
location
,
draft
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
))
if
draft
.
has_children
:
if
draft
.
has_children
:
if
original_published
is
not
None
:
if
original_published
is
not
None
:
# see if children were deleted. 2 reasons for children lists to differ:
# see if children were deleted. 2 reasons for children lists to differ:
...
@@ -254,8 +220,7 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -254,8 +220,7 @@ class DraftModuleStore(MongoModuleStore):
rents
=
[
Location
(
mom
)
for
mom
in
self
.
get_parent_locations
(
child
,
None
)]
rents
=
[
Location
(
mom
)
for
mom
in
self
.
get_parent_locations
(
child
,
None
)]
if
(
len
(
rents
)
==
1
and
rents
[
0
]
==
Location
(
location
)):
# the 1 is this original_published
if
(
len
(
rents
)
==
1
and
rents
[
0
]
==
Location
(
location
)):
# the 1 is this original_published
self
.
delete_item
(
child
,
True
)
self
.
delete_item
(
child
,
True
)
super
(
DraftModuleStore
,
self
)
.
update_children
(
location
,
draft
.
children
)
super
(
DraftModuleStore
,
self
)
.
update_item
(
draft
,
'**replace_user**'
)
super
(
DraftModuleStore
,
self
)
.
update_metadata
(
location
,
own_metadata
(
draft
))
self
.
delete_item
(
location
)
self
.
delete_item
(
location
)
def
unpublish
(
self
,
location
):
def
unpublish
(
self
,
location
):
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
16f0d12a
...
@@ -57,7 +57,9 @@ from pytz import UTC
...
@@ -57,7 +57,9 @@ from pytz import UTC
from
xmodule.errortracker
import
null_error_tracker
from
xmodule.errortracker
import
null_error_tracker
from
xmodule.x_module
import
prefer_xmodules
from
xmodule.x_module
import
prefer_xmodules
from
xmodule.modulestore.locator
import
BlockUsageLocator
,
DefinitionLocator
,
CourseLocator
,
VersionTree
,
LocalId
from
xmodule.modulestore.locator
import
(
BlockUsageLocator
,
DefinitionLocator
,
CourseLocator
,
VersionTree
,
LocalId
,
Locator
)
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
,
VersionConflictError
,
DuplicateItemError
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
,
VersionConflictError
,
DuplicateItemError
from
xmodule.modulestore
import
inheritance
,
ModuleStoreWriteBase
,
Location
,
SPLIT_MONGO_MODULESTORE_TYPE
from
xmodule.modulestore
import
inheritance
,
ModuleStoreWriteBase
,
Location
,
SPLIT_MONGO_MODULESTORE_TYPE
...
@@ -98,6 +100,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -98,6 +100,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
A Mongodb backed ModuleStore supporting versions, inheritance,
A Mongodb backed ModuleStore supporting versions, inheritance,
and sharing.
and sharing.
"""
"""
reference_type
=
Locator
def
__init__
(
self
,
doc_store_config
,
fs_root
,
render_template
,
def
__init__
(
self
,
doc_store_config
,
fs_root
,
render_template
,
default_class
=
None
,
default_class
=
None
,
error_tracker
=
null_error_tracker
,
error_tracker
=
null_error_tracker
,
...
@@ -936,7 +939,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -936,7 +939,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self
.
db_connection
.
insert_course_index
(
index_entry
)
self
.
db_connection
.
insert_course_index
(
index_entry
)
return
self
.
get_course
(
CourseLocator
(
package_id
=
new_id
,
branch
=
master_branch
))
return
self
.
get_course
(
CourseLocator
(
package_id
=
new_id
,
branch
=
master_branch
))
def
update_item
(
self
,
descriptor
,
user_id
,
force
=
False
):
def
update_item
(
self
,
descriptor
,
user_id
,
allow_not_found
=
False
,
force
=
False
):
"""
"""
Save the descriptor's fields. it doesn't descend the course dag to save the children.
Save the descriptor's fields. it doesn't descend the course dag to save the children.
Return the new descriptor (updated location).
Return the new descriptor (updated location).
...
@@ -1115,14 +1118,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1115,14 +1118,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
key
not
in
new_keys
or
original_fields
[
key
]
!=
settings
[
key
]:
if
key
not
in
new_keys
or
original_fields
[
key
]
!=
settings
[
key
]:
return
True
return
True
def
update_children
(
self
,
location
,
children
):
'''Deprecated, use update_item.'''
raise
NotImplementedError
(
'use update_item'
)
def
update_metadata
(
self
,
location
,
metadata
):
'''Deprecated, use update_item.'''
raise
NotImplementedError
(
'use update_item'
)
def
xblock_publish
(
self
,
user_id
,
source_course
,
destination_course
,
subtree_list
,
blacklist
):
def
xblock_publish
(
self
,
user_id
,
source_course
,
destination_course
,
subtree_list
,
blacklist
):
"""
"""
Publishes each xblock in subtree_list and those blocks descendants excluding blacklist
Publishes each xblock in subtree_list and those blocks descendants excluding blacklist
...
@@ -1211,7 +1206,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1211,7 +1206,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
"""
self
.
db_connection
.
update_course_index
(
updated_index_entry
)
self
.
db_connection
.
update_course_index
(
updated_index_entry
)
def
delete_item
(
self
,
usage_locator
,
user_id
,
delete_children
=
False
,
force
=
False
):
# TODO impl delete_all_versions
def
delete_item
(
self
,
usage_locator
,
user_id
,
delete_all_versions
=
False
,
delete_children
=
False
,
force
=
False
):
"""
"""
Delete the block or tree rooted at block (if delete_children) and any references w/in the course to the block
Delete the block or tree rooted at block (if delete_children) and any references w/in the course to the block
from a new version of the course structure.
from a new version of the course structure.
...
@@ -1361,6 +1357,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1361,6 +1357,16 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else
:
else
:
return
DefinitionLocator
(
definition
[
'_id'
])
return
DefinitionLocator
(
definition
[
'_id'
])
def
get_modulestore_type
(
self
,
course_id
):
"""
Returns an enumeration-like type reflecting the type of this modulestore
The return can be one of:
"xml" (for XML based courses),
"mongo" for old-style MongoDB backed courses,
"split" for new-style split MongoDB backed courses.
"""
return
SPLIT_MONGO_MODULESTORE_TYPE
def
internal_clean_children
(
self
,
course_locator
):
def
internal_clean_children
(
self
,
course_locator
):
"""
"""
Only intended for rather low level methods to use. Goes through the children attrs of
Only intended for rather low level methods to use. Goes through the children attrs of
...
@@ -1509,16 +1515,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1509,16 +1515,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
del
fields
[
'category'
]
del
fields
[
'category'
]
return
fields
return
fields
def
get_modulestore_type
(
self
,
course_id
):
"""
Returns an enumeration-like type reflecting the type of this modulestore
The return can be one of:
"xml" (for XML based courses),
"mongo" for old-style MongoDB backed courses,
"split" for new-style split MongoDB backed courses.
"""
return
SPLIT_MONGO_MODULESTORE_TYPE
def
_new_structure
(
self
,
user_id
,
root_block_id
,
def
_new_structure
(
self
,
user_id
,
root_block_id
,
root_category
=
None
,
block_fields
=
None
,
definition_id
=
None
):
root_category
=
None
,
block_fields
=
None
,
definition_id
=
None
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/store_utilities.py
View file @
16f0d12a
import
re
import
re
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.inheritance
import
own_metadata
import
logging
import
logging
...
@@ -125,13 +124,10 @@ def _clone_modules(modulestore, modules, source_location, dest_location):
...
@@ -125,13 +124,10 @@ def _clone_modules(modulestore, modules, source_location, dest_location):
print
"Cloning module {0} to {1}...."
.
format
(
original_loc
,
module
.
location
)
print
"Cloning module {0} to {1}...."
.
format
(
original_loc
,
module
.
location
)
# NOTE: usage of the the internal module.xblock_kvs._data does not include any 'default' values for the fields
if
'data'
in
module
.
fields
and
module
.
fields
[
'data'
]
.
is_set_on
(
module
)
and
isinstance
(
module
.
data
,
basestring
):
data
=
module
.
xblock_kvs
.
_data
module
.
data
=
rewrite_nonportable_content_links
(
if
isinstance
(
data
,
basestring
):
source_location
.
course_id
,
dest_location
.
course_id
,
module
.
data
data
=
rewrite_nonportable_content_links
(
)
source_location
.
course_id
,
dest_location
.
course_id
,
data
)
modulestore
.
update_item
(
module
.
location
,
data
)
# repoint children
# repoint children
if
module
.
has_children
:
if
module
.
has_children
:
...
@@ -145,10 +141,9 @@ def _clone_modules(modulestore, modules, source_location, dest_location):
...
@@ -145,10 +141,9 @@ def _clone_modules(modulestore, modules, source_location, dest_location):
)
)
new_children
.
append
(
child_loc
.
url
())
new_children
.
append
(
child_loc
.
url
())
module
store
.
update_children
(
module
.
location
,
new_children
)
module
.
children
=
new_children
# save metadata
modulestore
.
update_item
(
module
,
'**replace_user**'
)
modulestore
.
update_metadata
(
module
.
location
,
own_metadata
(
module
))
def
clone_course
(
modulestore
,
contentstore
,
source_location
,
dest_location
,
delete_original
=
False
):
def
clone_course
(
modulestore
,
contentstore
,
source_location
,
dest_location
,
delete_original
=
False
):
...
...
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
View file @
16f0d12a
...
@@ -33,6 +33,7 @@ def mixed_store_config(data_dir, mappings):
...
@@ -33,6 +33,7 @@ def mixed_store_config(data_dir, mappings):
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'OPTIONS'
:
{
'OPTIONS'
:
{
'mappings'
:
mappings
,
'mappings'
:
mappings
,
'reference_type'
:
'Location'
,
'stores'
:
{
'stores'
:
{
'default'
:
mongo_config
[
'default'
],
'default'
:
mongo_config
[
'default'
],
'xml'
:
xml_config
[
'default'
]
'xml'
:
xml_config
[
'default'
]
...
@@ -196,18 +197,15 @@ class ModuleStoreTestCase(TestCase):
...
@@ -196,18 +197,15 @@ class ModuleStoreTestCase(TestCase):
"""
"""
@staticmethod
@staticmethod
def
update_course
(
course
,
data
):
def
update_course
(
course
):
"""
"""
Updates the version of course in the modulestore
Updates the version of course in the modulestore
with the metadata in 'data' and returns the updated version.
'course' is an instance of CourseDescriptor for which we want
'course' is an instance of CourseDescriptor for which we want
to update metadata.
to update metadata.
'data' is a dictionary with an entry for each CourseField we want to update.
"""
"""
store
=
editable_modulestore
(
'direct'
)
store
=
editable_modulestore
(
'direct'
)
store
.
update_
metadata
(
course
.
location
,
data
)
store
.
update_
item
(
course
,
'**replace_user**'
)
updated_course
=
store
.
get_instance
(
course
.
id
,
course
.
location
)
updated_course
=
store
.
get_instance
(
course
.
id
,
course
.
location
)
return
updated_course
return
updated_course
...
...
common/lib/xmodule/xmodule/modulestore/tests/factories.py
View file @
16f0d12a
import
datetime
from
factory
import
Factory
,
lazy_attribute_sequence
,
lazy_attribute
from
factory
import
Factory
,
lazy_attribute_sequence
,
lazy_attribute
from
factory.containers
import
CyclicDefinitionError
from
factory.containers
import
CyclicDefinitionError
from
uuid
import
uuid4
from
uuid
import
uuid4
from
pytz
import
UTC
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.x_module
import
prefer_xmodules
from
xmodule.x_module
import
prefer_xmodules
...
@@ -127,7 +124,6 @@ class ItemFactory(XModuleFactory):
...
@@ -127,7 +124,6 @@ class ItemFactory(XModuleFactory):
# passed in via **kwargs. However, some of those aren't actual field values,
# passed in via **kwargs. However, some of those aren't actual field values,
# so pop those off for use separately
# so pop those off for use separately
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
# catch any old style users before they get into trouble
# catch any old style users before they get into trouble
assert
'template'
not
in
kwargs
assert
'template'
not
in
kwargs
parent_location
=
Location
(
kwargs
.
pop
(
'parent_location'
,
None
))
parent_location
=
Location
(
kwargs
.
pop
(
'parent_location'
,
None
))
...
@@ -155,7 +151,7 @@ class ItemFactory(XModuleFactory):
...
@@ -155,7 +151,7 @@ class ItemFactory(XModuleFactory):
# replace the display name with an optional parameter passed in from the caller
# replace the display name with an optional parameter passed in from the caller
if
display_name
is
not
None
:
if
display_name
is
not
None
:
metadata
[
'display_name'
]
=
display_name
metadata
[
'display_name'
]
=
display_name
module
=
store
.
create_and_save_xmodule
(
location
,
metadata
=
metadata
,
definition_data
=
data
)
store
.
create_and_save_xmodule
(
location
,
metadata
=
metadata
,
definition_data
=
data
)
module
=
store
.
get_item
(
location
)
module
=
store
.
get_item
(
location
)
...
@@ -165,8 +161,8 @@ class ItemFactory(XModuleFactory):
...
@@ -165,8 +161,8 @@ class ItemFactory(XModuleFactory):
store
.
save_xmodule
(
module
)
store
.
save_xmodule
(
module
)
if
location
.
category
not
in
DETACHED_CATEGORIES
:
if
'detached'
not
in
module
.
_class_tags
:
parent
.
children
.
append
(
location
.
url
())
parent
.
children
.
append
(
location
.
url
())
store
.
update_
children
(
parent_location
,
parent
.
children
)
store
.
update_
item
(
parent
,
'**replace_user**'
)
return
store
.
get_item
(
location
)
return
store
.
get_item
(
location
)
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
View file @
16f0d12a
...
@@ -7,7 +7,7 @@ import unittest
...
@@ -7,7 +7,7 @@ import unittest
import
uuid
import
uuid
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore.loc_mapper_store
import
LocMapperStore
from
xmodule.modulestore.loc_mapper_store
import
LocMapperStore
from
mock
import
Mock
from
mock
import
Mock
...
@@ -114,7 +114,7 @@ class TestLocationMapper(unittest.TestCase):
...
@@ -114,7 +114,7 @@ class TestLocationMapper(unittest.TestCase):
new_style_package_id
=
'{}.geek_dept.{}.baz_run'
.
format
(
org
,
course
)
new_style_package_id
=
'{}.geek_dept.{}.baz_run'
.
format
(
org
,
course
)
block_map
=
{
block_map
=
{
'abc123'
:
{
'problem'
:
'problem2'
},
'abc123'
:
{
'problem'
:
'problem2'
,
'vertical'
:
'vertical2'
},
'def456'
:
{
'problem'
:
'problem4'
},
'def456'
:
{
'problem'
:
'problem4'
},
'ghi789'
:
{
'problem'
:
'problem7'
},
'ghi789'
:
{
'problem'
:
'problem7'
},
}
}
...
@@ -136,6 +136,14 @@ class TestLocationMapper(unittest.TestCase):
...
@@ -136,6 +136,14 @@ class TestLocationMapper(unittest.TestCase):
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'1def23'
),
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'1def23'
),
add_entry_if_missing
=
False
add_entry_if_missing
=
False
)
)
test_no_cat_locn
=
test_problem_locn
.
replace
(
category
=
None
)
with
self
.
assertRaises
(
InvalidLocationError
):
loc_mapper
()
.
translate_location
(
old_style_course_id
,
test_no_cat_locn
,
False
,
False
)
test_no_cat_locn
=
test_no_cat_locn
.
replace
(
name
=
'def456'
)
# only one course matches
self
.
translate_n_check
(
test_no_cat_locn
,
old_style_course_id
,
new_style_package_id
,
'problem4'
,
'published'
)
# add a distractor course (note that abc123 has a different translation in this one)
# add a distractor course (note that abc123 has a different translation in this one)
distractor_block_map
=
{
distractor_block_map
=
{
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_locators.py
View file @
16f0d12a
...
@@ -259,6 +259,24 @@ class LocatorTest(TestCase):
...
@@ -259,6 +259,24 @@ class LocatorTest(TestCase):
testobj
=
BlockUsageLocator
(
package_id
=
package_id
,
branch
=
branch
,
block_id
=
block_id
)
testobj
=
BlockUsageLocator
(
package_id
=
package_id
,
branch
=
branch
,
block_id
=
block_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot handle colon'
,
package_id
=
package_id
,
branch
=
branch
,
block
=
block_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot handle colon'
,
package_id
=
package_id
,
branch
=
branch
,
block
=
block_id
)
def
test_relative
(
self
):
"""
Test making a relative usage locator.
"""
package_id
=
'mit.eecs-1'
branch
=
'foo'
baseobj
=
CourseLocator
(
package_id
=
package_id
,
branch
=
branch
)
block_id
=
'problem:with-colon~2'
testobj
=
BlockUsageLocator
.
make_relative
(
baseobj
,
block_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot make relative to course'
,
package_id
=
package_id
,
branch
=
branch
,
block
=
block_id
)
block_id
=
'completely_different'
testobj
=
BlockUsageLocator
.
make_relative
(
testobj
,
block_id
)
self
.
check_block_locn_fields
(
testobj
,
'Cannot make relative to block usage'
,
package_id
=
package_id
,
branch
=
branch
,
block
=
block_id
)
def
test_repr
(
self
):
def
test_repr
(
self
):
testurn
=
'mit.eecs.6002x/'
+
BRANCH_PREFIX
+
'published/'
+
BLOCK_PREFIX
+
'HW3'
testurn
=
'mit.eecs.6002x/'
+
BRANCH_PREFIX
+
'published/'
+
BLOCK_PREFIX
+
'HW3'
testobj
=
BlockUsageLocator
(
package_id
=
testurn
)
testobj
=
BlockUsageLocator
(
package_id
=
testurn
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
16f0d12a
...
@@ -13,6 +13,8 @@ from xmodule.modulestore.xml_importer import import_from_xml
...
@@ -13,6 +13,8 @@ from xmodule.modulestore.xml_importer import import_from_xml
# Mixed modulestore depends on django, so we'll manually configure some django settings
# Mixed modulestore depends on django, so we'll manually configure some django settings
# before importing the module
# before importing the module
from
django.conf
import
settings
from
django.conf
import
settings
import
unittest
import
copy
if
not
settings
.
configured
:
if
not
settings
.
configured
:
settings
.
configure
()
settings
.
configure
()
...
@@ -37,6 +39,7 @@ OPTIONS = {
...
@@ -37,6 +39,7 @@ OPTIONS = {
XML_COURSEID2
:
'xml'
,
XML_COURSEID2
:
'xml'
,
IMPORT_COURSEID
:
'default'
IMPORT_COURSEID
:
'default'
},
},
'reference_type'
:
'Location'
,
'stores'
:
{
'stores'
:
{
'xml'
:
{
'xml'
:
{
'ENGINE'
:
'xmodule.modulestore.xml.XMLModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.xml.XMLModuleStore'
,
...
@@ -182,6 +185,7 @@ class TestMixedModuleStore(object):
...
@@ -182,6 +185,7 @@ class TestMixedModuleStore(object):
)
)
def
test_get_items
(
self
):
def
test_get_items
(
self
):
# NOTE: use get_course if you just want the course. get_items only allows wildcarding of category and name
modules
=
self
.
store
.
get_items
(
Location
(
'i4x'
,
None
,
None
,
'course'
,
None
),
IMPORT_COURSEID
)
modules
=
self
.
store
.
get_items
(
Location
(
'i4x'
,
None
,
None
,
'course'
,
None
),
IMPORT_COURSEID
)
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
self
.
import_course
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
self
.
import_course
)
...
@@ -190,21 +194,14 @@ class TestMixedModuleStore(object):
...
@@ -190,21 +194,14 @@ class TestMixedModuleStore(object):
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
'toy'
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
'toy'
)
modules
=
self
.
store
.
get_items
(
Location
(
'i4x'
,
None
,
None
,
'course'
,
None
),
XML_COURSEID2
)
modules
=
self
.
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'course'
,
None
),
XML_COURSEID2
)
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
len
(
modules
),
1
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
'simple'
)
assert_equals
(
modules
[
0
]
.
location
.
course
,
'simple'
)
def
test_update_item
(
self
):
def
test_update_item
(
self
):
# FIXME update
with
assert_raises
(
NotImplementedError
):
with
assert_raises
(
NotImplementedError
):
self
.
store
.
update_item
(
self
.
fake_location
,
None
)
self
.
store
.
update_item
(
self
.
fake_location
,
'**replace_user**'
)
def
test_update_children
(
self
):
with
assert_raises
(
NotImplementedError
):
self
.
store
.
update_children
(
self
.
fake_location
,
None
)
def
test_update_metadata
(
self
):
with
assert_raises
(
NotImplementedError
):
self
.
store
.
update_metadata
(
self
.
fake_location
,
None
)
def
test_delete_item
(
self
):
def
test_delete_item
(
self
):
with
assert_raises
(
NotImplementedError
):
with
assert_raises
(
NotImplementedError
):
...
@@ -250,3 +247,25 @@ class TestMixedModuleStore(object):
...
@@ -250,3 +247,25 @@ class TestMixedModuleStore(object):
assert_equals
(
Location
(
parents
[
0
])
.
org
,
'edX'
)
assert_equals
(
Location
(
parents
[
0
])
.
org
,
'edX'
)
assert_equals
(
Location
(
parents
[
0
])
.
course
,
'toy'
)
assert_equals
(
Location
(
parents
[
0
])
.
course
,
'toy'
)
assert_equals
(
Location
(
parents
[
0
])
.
name
,
'2012_Fall'
)
assert_equals
(
Location
(
parents
[
0
])
.
name
,
'2012_Fall'
)
class
TestMixedMSInit
(
unittest
.
TestCase
):
"""
Test initializing w/o a reference_type
"""
def
setUp
(
self
):
unittest
.
TestCase
.
setUp
(
self
)
options
=
copy
.
copy
(
OPTIONS
)
del
options
[
'reference_type'
]
self
.
connection
=
pymongo
.
MongoClient
(
host
=
HOST
,
port
=
PORT
,
tz_aware
=
True
,
)
self
.
store
=
MixedModuleStore
(
**
options
)
def
test_use_locations
(
self
):
"""
Test that use_locations defaulted correctly
"""
self
.
assertTrue
(
self
.
store
.
use_locations
)
common/lib/xmodule/xmodule/modulestore/tests/test_orphan.py
View file @
16f0d12a
...
@@ -82,7 +82,7 @@ class TestOrphan(unittest.TestCase):
...
@@ -82,7 +82,7 @@ class TestOrphan(unittest.TestCase):
parent_location
=
Location
(
'i4x'
,
'test_org'
,
'test_course'
,
parent_category
,
parent_name
)
parent_location
=
Location
(
'i4x'
,
'test_org'
,
'test_course'
,
parent_category
,
parent_name
)
parent
=
self
.
old_mongo
.
get_item
(
parent_location
)
parent
=
self
.
old_mongo
.
get_item
(
parent_location
)
parent
.
children
.
append
(
location
.
url
())
parent
.
children
.
append
(
location
.
url
())
self
.
old_mongo
.
update_
children
(
parent_location
,
parent
.
children
)
self
.
old_mongo
.
update_
item
(
parent
,
self
.
userid
)
# create pointer for split
# create pointer for split
course_or_parent_locator
=
BlockUsageLocator
(
course_or_parent_locator
=
BlockUsageLocator
(
package_id
=
self
.
split_package_id
,
package_id
=
self
.
split_package_id
,
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
View file @
16f0d12a
...
@@ -71,7 +71,7 @@ class TestPublish(unittest.TestCase):
...
@@ -71,7 +71,7 @@ class TestPublish(unittest.TestCase):
mongo
=
self
.
old_mongo
mongo
=
self
.
old_mongo
else
:
else
:
mongo
=
self
.
draft_mongo
mongo
=
self
.
draft_mongo
mongo
.
update_
children
(
parent_location
,
parent
.
children
)
mongo
.
update_
item
(
parent
,
'**replace_user**'
)
def
_create_course
(
self
):
def
_create_course
(
self
):
"""
"""
...
@@ -174,8 +174,8 @@ class TestPublish(unittest.TestCase):
...
@@ -174,8 +174,8 @@ class TestPublish(unittest.TestCase):
draft_vert
.
children
.
remove
(
other_child_loc
.
url
())
draft_vert
.
children
.
remove
(
other_child_loc
.
url
())
other_vert
=
self
.
draft_mongo
.
get_item
(
self
.
course_location
.
replace
(
category
=
'vertical'
,
name
=
'Vert2'
),
0
)
other_vert
=
self
.
draft_mongo
.
get_item
(
self
.
course_location
.
replace
(
category
=
'vertical'
,
name
=
'Vert2'
),
0
)
other_vert
.
children
.
append
(
other_child_loc
.
url
())
other_vert
.
children
.
append
(
other_child_loc
.
url
())
self
.
draft_mongo
.
update_
children
(
draft_vert
.
location
,
draft_vert
.
children
)
self
.
draft_mongo
.
update_
item
(
draft_vert
,
'**replace_user**'
)
self
.
draft_mongo
.
update_
children
(
other_vert
.
location
,
other_vert
.
children
)
self
.
draft_mongo
.
update_
item
(
other_vert
,
'**replace_user**'
)
# publish
# publish
self
.
_xmodule_recurse
(
self
.
_xmodule_recurse
(
draft_vert
,
draft_vert
,
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py
View file @
16f0d12a
...
@@ -102,7 +102,7 @@ class TestMigration(unittest.TestCase):
...
@@ -102,7 +102,7 @@ class TestMigration(unittest.TestCase):
location
=
location
.
replace
(
category
=
'chapter'
,
name
=
uuid
.
uuid4
()
.
hex
)
location
=
location
.
replace
(
category
=
'chapter'
,
name
=
uuid
.
uuid4
()
.
hex
)
chapter2
=
self
.
_create_and_get_item
(
self
.
old_mongo
,
location
,
{},
{
'display_name'
:
'Chapter 2'
},
runtime
)
chapter2
=
self
.
_create_and_get_item
(
self
.
old_mongo
,
location
,
{},
{
'display_name'
:
'Chapter 2'
},
runtime
)
course_root
.
children
.
append
(
chapter2
.
location
.
url
())
course_root
.
children
.
append
(
chapter2
.
location
.
url
())
self
.
old_mongo
.
update_
children
(
course_root
.
location
,
course_root
.
children
)
self
.
old_mongo
.
update_
item
(
course_root
,
'**replace_user**'
)
# vertical in live only
# vertical in live only
location
=
location
.
replace
(
category
=
'vertical'
,
name
=
uuid
.
uuid4
()
.
hex
)
location
=
location
.
replace
(
category
=
'vertical'
,
name
=
uuid
.
uuid4
()
.
hex
)
live_vert
=
self
.
_create_and_get_item
(
self
.
old_mongo
,
location
,
{},
{
'display_name'
:
'Live vertical'
},
runtime
)
live_vert
=
self
.
_create_and_get_item
(
self
.
old_mongo
,
location
,
{},
{
'display_name'
:
'Live vertical'
},
runtime
)
...
@@ -140,7 +140,7 @@ class TestMigration(unittest.TestCase):
...
@@ -140,7 +140,7 @@ class TestMigration(unittest.TestCase):
self
.
create_random_units
(
self
.
old_mongo
,
live_vert
)
self
.
create_random_units
(
self
.
old_mongo
,
live_vert
)
# update the chapter
# update the chapter
self
.
old_mongo
.
update_
children
(
chapter1
.
location
,
chapter1
.
children
)
self
.
old_mongo
.
update_
item
(
chapter1
,
'**replace_user**'
)
# now the other one w/ the conditional
# now the other one w/ the conditional
# first create some show children
# first create some show children
...
@@ -169,7 +169,7 @@ class TestMigration(unittest.TestCase):
...
@@ -169,7 +169,7 @@ class TestMigration(unittest.TestCase):
# add direct children
# add direct children
self
.
create_random_units
(
self
.
old_mongo
,
conditional
)
self
.
create_random_units
(
self
.
old_mongo
,
conditional
)
chapter2
.
children
.
append
(
conditional
.
location
.
url
())
chapter2
.
children
.
append
(
conditional
.
location
.
url
())
self
.
old_mongo
.
update_
children
(
chapter2
.
location
,
chapter2
.
children
)
self
.
old_mongo
.
update_
item
(
chapter2
,
'**replace_user**'
)
# and the ancillary docs (not children)
# and the ancillary docs (not children)
location
=
location
.
replace
(
category
=
'static_tab'
,
name
=
uuid
.
uuid4
()
.
hex
)
location
=
location
.
replace
(
category
=
'static_tab'
,
name
=
uuid
.
uuid4
()
.
hex
)
...
@@ -207,9 +207,9 @@ class TestMigration(unittest.TestCase):
...
@@ -207,9 +207,9 @@ class TestMigration(unittest.TestCase):
cc_store
,
location
,
data
,
{
'display_name'
:
str
(
uuid
.
uuid4
())},
parent
.
runtime
cc_store
,
location
,
data
,
{
'display_name'
:
str
(
uuid
.
uuid4
())},
parent
.
runtime
)
)
cc_parent
.
children
.
append
(
element
.
location
.
url
())
cc_parent
.
children
.
append
(
element
.
location
.
url
())
store
.
update_
children
(
parent
.
location
,
parent
.
children
)
store
.
update_
item
(
parent
,
'**replace_user**'
)
if
cc_store
is
not
None
:
if
cc_store
is
not
None
:
cc_store
.
update_
children
(
cc_parent
.
location
,
cc_parent
.
children
)
cc_store
.
update_
item
(
cc_parent
,
'**replace_user**'
)
def
compare_courses
(
self
,
presplit
,
published
):
def
compare_courses
(
self
,
presplit
,
published
):
# descend via children to do comparison
# descend via children to do comparison
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py
View file @
16f0d12a
...
@@ -748,7 +748,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -748,7 +748,7 @@ class TestItemCrud(SplitModuleTest):
problem
.
max_attempts
=
4
problem
.
max_attempts
=
4
problem
.
save
()
# decache above setting into the kvs
problem
.
save
()
# decache above setting into the kvs
updated_problem
=
modulestore
()
.
update_item
(
problem
,
'
changeMaven
'
)
updated_problem
=
modulestore
()
.
update_item
(
problem
,
'
**replace_user**
'
)
# 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
(
updated_problem
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertEqual
(
updated_problem
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_problem
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertNotEqual
(
updated_problem
.
location
.
version_guid
,
pre_version_guid
)
...
@@ -767,7 +767,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -767,7 +767,7 @@ class TestItemCrud(SplitModuleTest):
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
)
history_info
=
modulestore
()
.
get_course_history_info
(
current_course
.
location
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
pre_version_guid
)
self
.
assertEqual
(
history_info
[
'previous_version'
],
pre_version_guid
)
self
.
assertEqual
(
str
(
history_info
[
'original_version'
]),
self
.
GUID_D3
)
self
.
assertEqual
(
str
(
history_info
[
'original_version'
]),
self
.
GUID_D3
)
self
.
assertEqual
(
history_info
[
'edited_by'
],
"
changeMaven
"
)
self
.
assertEqual
(
history_info
[
'edited_by'
],
"
**replace_user**
"
)
self
.
assertGreaterEqual
(
history_info
[
'edited_on'
],
premod_time
)
self
.
assertGreaterEqual
(
history_info
[
'edited_on'
],
premod_time
)
self
.
assertLessEqual
(
history_info
[
'edited_on'
],
datetime
.
datetime
.
now
(
UTC
))
self
.
assertLessEqual
(
history_info
[
'edited_on'
],
datetime
.
datetime
.
now
(
UTC
))
...
@@ -784,7 +784,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -784,7 +784,7 @@ class TestItemCrud(SplitModuleTest):
self
.
assertGreater
(
len
(
block
.
children
),
0
,
"meaningless test"
)
self
.
assertGreater
(
len
(
block
.
children
),
0
,
"meaningless test"
)
moved_child
=
block
.
children
.
pop
()
moved_child
=
block
.
children
.
pop
()
block
.
save
()
# decache model changes
block
.
save
()
# decache model changes
updated_problem
=
modulestore
()
.
update_item
(
block
,
'
childchanger
'
)
updated_problem
=
modulestore
()
.
update_item
(
block
,
'
**replace_user**
'
)
# 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
(
updated_problem
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertEqual
(
updated_problem
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_problem
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertNotEqual
(
updated_problem
.
location
.
version_guid
,
pre_version_guid
)
...
@@ -794,7 +794,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -794,7 +794,7 @@ class TestItemCrud(SplitModuleTest):
other_block
=
modulestore
()
.
get_item
(
locator
)
other_block
=
modulestore
()
.
get_item
(
locator
)
other_block
.
children
.
append
(
moved_child
)
other_block
.
children
.
append
(
moved_child
)
other_block
.
save
()
# decache model changes
other_block
.
save
()
# decache model changes
other_updated
=
modulestore
()
.
update_item
(
other_block
,
'
childchanger
'
)
other_updated
=
modulestore
()
.
update_item
(
other_block
,
'
**replace_user**
'
)
self
.
assertIn
(
moved_child
,
other_updated
.
children
)
self
.
assertIn
(
moved_child
,
other_updated
.
children
)
def
test_update_definition
(
self
):
def
test_update_definition
(
self
):
...
@@ -808,7 +808,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -808,7 +808,7 @@ class TestItemCrud(SplitModuleTest):
block
.
grading_policy
[
'GRADER'
][
0
][
'min_count'
]
=
13
block
.
grading_policy
[
'GRADER'
][
0
][
'min_count'
]
=
13
block
.
save
()
# decache model changes
block
.
save
()
# decache model changes
updated_block
=
modulestore
()
.
update_item
(
block
,
'
definition_changer
'
)
updated_block
=
modulestore
()
.
update_item
(
block
,
'
**replace_user**
'
)
self
.
assertNotEqual
(
updated_block
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_block
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_block
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertNotEqual
(
updated_block
.
location
.
version_guid
,
pre_version_guid
)
...
@@ -846,7 +846,7 @@ class TestItemCrud(SplitModuleTest):
...
@@ -846,7 +846,7 @@ class TestItemCrud(SplitModuleTest):
block
.
advertised_start
=
"Soon"
block
.
advertised_start
=
"Soon"
block
.
save
()
# decache model changes
block
.
save
()
# decache model changes
updated_block
=
modulestore
()
.
update_item
(
block
,
"
test_update_manifold
"
)
updated_block
=
modulestore
()
.
update_item
(
block
,
"
**replace_user**
"
)
self
.
assertNotEqual
(
updated_block
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_block
.
definition_locator
.
definition_id
,
pre_def_id
)
self
.
assertNotEqual
(
updated_block
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertNotEqual
(
updated_block
.
location
.
version_guid
,
pre_version_guid
)
self
.
assertEqual
(
updated_block
.
grading_policy
[
'GRADER'
][
0
][
'min_count'
],
13
)
self
.
assertEqual
(
updated_block
.
grading_policy
[
'GRADER'
][
0
][
'min_count'
],
13
)
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
16f0d12a
...
@@ -377,6 +377,7 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -377,6 +377,7 @@ class XMLModuleStore(ModuleStoreReadBase):
self
.
default_class
=
class_
self
.
default_class
=
class_
self
.
parent_trackers
=
defaultdict
(
ParentTracker
)
self
.
parent_trackers
=
defaultdict
(
ParentTracker
)
self
.
reference_type
=
Location
# All field data will be stored in an inheriting field data.
# All field data will be stored in an inheriting field data.
self
.
field_data
=
inheriting_field_data
(
kvs
=
DictKeyValueStore
())
self
.
field_data
=
inheriting_field_data
(
kvs
=
DictKeyValueStore
())
...
@@ -644,7 +645,7 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -644,7 +645,7 @@ class XMLModuleStore(ModuleStoreReadBase):
raise
NotImplementedError
(
"XMLModuleStores can't guarantee that definitions"
raise
NotImplementedError
(
"XMLModuleStores can't guarantee that definitions"
" are unique. Use get_instance."
)
" are unique. Use get_instance."
)
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
):
def
get_items
(
self
,
location
,
course_id
=
None
,
depth
=
0
,
qualifiers
=
None
):
items
=
[]
items
=
[]
def
_add_get_items
(
self
,
location
,
modules
):
def
_add_get_items
(
self
,
location
,
modules
):
...
@@ -676,33 +677,22 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -676,33 +677,22 @@ class XMLModuleStore(ModuleStoreReadBase):
"""
"""
return
dict
((
k
,
self
.
errored_courses
[
k
]
.
errors
)
for
k
in
self
.
errored_courses
)
return
dict
((
k
,
self
.
errored_courses
[
k
]
.
errors
)
for
k
in
self
.
errored_courses
)
def
update_item
(
self
,
location
,
data
):
def
get_orphans
(
self
,
course_location
,
_branch
):
"""
"""
Set the data in the item specified by the location to
Get all of the xblocks in the given course which have no parents and are not of types which are
data
usually orphaned. NOTE: may include xblocks which still have references via xblocks which don't
use children to point to their dependents.
location: Something that can be passed to Location
data: A nested dictionary of problem data
"""
"""
raise
NotImplementedError
(
"XMLModuleStores are read-only"
)
# here just to quell the abstractmethod. someone could write the impl if needed
raise
NotImplementedError
def
update_
children
(
self
,
location
,
children
):
def
update_
item
(
self
,
xblock
,
user
,
**
kwargs
):
"""
"""
Set the
children for
the item specified by the location to
Set the
data in
the item specified by the location to
data
data
location: Something that can be passed to Location
location: Something that can be passed to Location
children: A list of child item identifiers
data: A nested dictionary of problem data
"""
raise
NotImplementedError
(
"XMLModuleStores are read-only"
)
def
update_metadata
(
self
,
location
,
metadata
):
"""
Set the metadata for the item specified by the location to
metadata
location: Something that can be passed to Location
metadata: A nested dictionary of module metadata
"""
"""
raise
NotImplementedError
(
"XMLModuleStores are read-only"
)
raise
NotImplementedError
(
"XMLModuleStores are read-only"
)
...
...
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
16f0d12a
...
@@ -4,14 +4,13 @@ import mimetypes
...
@@ -4,14 +4,13 @@ import mimetypes
from
path
import
path
from
path
import
path
import
json
import
json
from
xblock.fields
import
Scope
from
.xml
import
XMLModuleStore
,
ImportSystem
,
ParentTracker
from
.xml
import
XMLModuleStore
,
ImportSystem
,
ParentTracker
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.contentstore.content
import
StaticContent
from
.inheritance
import
own_metadata
from
.inheritance
import
own_metadata
from
xmodule.errortracker
import
make_error_tracker
from
xmodule.errortracker
import
make_error_tracker
from
.store_utilities
import
rewrite_nonportable_content_links
from
.store_utilities
import
rewrite_nonportable_content_links
import
xblock
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -318,43 +317,13 @@ def import_module(
...
@@ -318,43 +317,13 @@ def import_module(
logging
.
debug
(
'processing import of module {}...'
.
format
(
module
.
location
.
url
()))
logging
.
debug
(
'processing import of module {}...'
.
format
(
module
.
location
.
url
()))
content
=
{}
if
do_import_static
and
'data'
in
module
.
fields
and
isinstance
(
module
.
fields
[
'data'
],
xblock
.
fields
.
String
):
for
field
in
module
.
fields
.
values
():
if
field
.
scope
!=
Scope
.
content
:
continue
try
:
content
[
field
.
name
]
=
module
.
_field_data
.
get
(
module
,
field
.
name
)
except
KeyError
:
# Ignore any missing keys in _field_data
pass
module_data
=
{}
if
'data'
in
content
:
module_data
=
content
[
'data'
]
else
:
module_data
=
content
if
isinstance
(
module_data
,
basestring
)
and
do_import_static
:
# we want to convert all 'non-portable' links in the module_data
# we want to convert all 'non-portable' links in the module_data
# (if it is a string) to portable strings (e.g. /static/)
# (if it is a string) to portable strings (e.g. /static/)
module
_
data
=
rewrite_nonportable_content_links
(
module
.
data
=
rewrite_nonportable_content_links
(
source_course_location
.
course_id
,
source_course_location
.
course_id
,
dest_course_location
.
course_id
,
module_data
dest_course_location
.
course_id
,
module
.
data
)
if
allow_not_found
:
store
.
update_item
(
module
.
location
,
module_data
,
allow_not_found
=
allow_not_found
)
)
else
:
store
.
update_item
(
module
.
location
,
module_data
)
if
hasattr
(
module
,
'children'
)
and
module
.
children
!=
[]:
store
.
update_children
(
module
.
location
,
module
.
children
)
# NOTE: It's important to use own_metadata here to avoid writing
# inherited metadata everywhere.
# remove any export/import only xml_attributes
# remove any export/import only xml_attributes
# which are used to wire together draft imports
# which are used to wire together draft imports
if
'parent_sequential_url'
in
getattr
(
module
,
'xml_attributes'
,
[]):
if
'parent_sequential_url'
in
getattr
(
module
,
'xml_attributes'
,
[]):
...
@@ -362,9 +331,8 @@ def import_module(
...
@@ -362,9 +331,8 @@ def import_module(
if
'index_in_children_list'
in
getattr
(
module
,
'xml_attributes'
,
[]):
if
'index_in_children_list'
in
getattr
(
module
,
'xml_attributes'
,
[]):
del
module
.
xml_attributes
[
'index_in_children_list'
]
del
module
.
xml_attributes
[
'index_in_children_list'
]
module
.
save
()
store
.
update_
metadata
(
module
.
location
,
dict
(
own_metadata
(
module
))
)
store
.
update_
item
(
module
,
'**replace_user**'
,
allow_not_found
=
allow_not_found
)
def
import_course_draft
(
def
import_course_draft
(
...
@@ -409,7 +377,7 @@ def import_course_draft(
...
@@ -409,7 +377,7 @@ def import_course_draft(
# First it is necessary to order the draft items by their desired index in the child list
# First it is necessary to order the draft items by their desired index in the child list
# (order os.walk returns them in is not guaranteed).
# (order os.walk returns them in is not guaranteed).
drafts
=
dict
()
drafts
=
dict
()
for
dirname
,
dirnames
,
filenames
in
os
.
walk
(
draft_dir
+
"/vertical"
):
for
dirname
,
_
dirnames
,
filenames
in
os
.
walk
(
draft_dir
+
"/vertical"
):
for
filename
in
filenames
:
for
filename
in
filenames
:
module_path
=
os
.
path
.
join
(
dirname
,
filename
)
module_path
=
os
.
path
.
join
(
dirname
,
filename
)
with
open
(
module_path
,
'r'
)
as
f
:
with
open
(
module_path
,
'r'
)
as
f
:
...
@@ -496,7 +464,7 @@ def import_course_draft(
...
@@ -496,7 +464,7 @@ def import_course_draft(
if
non_draft_location
.
url
()
not
in
sequential
.
children
:
if
non_draft_location
.
url
()
not
in
sequential
.
children
:
sequential
.
children
.
insert
(
index
,
non_draft_location
.
url
())
sequential
.
children
.
insert
(
index
,
non_draft_location
.
url
())
store
.
update_
children
(
sequential
.
location
,
sequential
.
children
)
store
.
update_
item
(
sequential
,
'**replace_user**'
)
import_module
(
import_module
(
module
,
draft_store
,
course_data_path
,
module
,
draft_store
,
course_data_path
,
...
...
lms/djangoapps/courseware/tests/test_submitting_problems.py
View file @
16f0d12a
...
@@ -228,9 +228,9 @@ class TestCourseGrader(TestSubmittingProblems):
...
@@ -228,9 +228,9 @@ class TestCourseGrader(TestSubmittingProblems):
Add a grading policy to the course.
Add a grading policy to the course.
"""
"""
course_data
=
{
'grading_policy'
:
grading_policy
}
self
.
course
.
grading_policy
=
grading_policy
store
=
editable_modulestore
(
'direct'
)
store
=
editable_modulestore
(
'direct'
)
store
.
update_item
(
self
.
course
.
location
,
course_data
)
store
.
update_item
(
self
.
course
,
'**replace_user**'
)
self
.
refresh_course
()
self
.
refresh_course
()
def
get_grade_summary
(
self
):
def
get_grade_summary
(
self
):
...
...
lms/djangoapps/courseware/tests/test_view_authentication.py
View file @
16f0d12a
...
@@ -25,6 +25,7 @@ from courseware.tests.factories import (
...
@@ -25,6 +25,7 @@ from courseware.tests.factories import (
OrgStaffFactory
,
OrgStaffFactory
,
OrgInstructorFactory
,
OrgInstructorFactory
,
)
)
from
xmodule.modulestore.django
import
modulestore
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
...
@@ -84,7 +85,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -84,7 +85,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
urls
.
extend
([
urls
.
extend
([
reverse
(
'book'
,
kwargs
=
{
'course_id'
:
course
.
id
,
reverse
(
'book'
,
kwargs
=
{
'course_id'
:
course
.
id
,
'book_index'
:
index
})
'book_index'
:
index
})
for
index
,
book
in
enumerate
(
course
.
textbooks
)
for
index
in
xrange
(
len
(
course
.
textbooks
)
)
])
])
for
url
in
urls
:
for
url
in
urls
:
check_for_get_code
(
self
,
200
,
url
)
check_for_get_code
(
self
,
200
,
url
)
...
@@ -112,6 +113,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -112,6 +113,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
course
=
CourseFactory
.
create
(
number
=
'999'
,
display_name
=
'Robot_Super_Course'
)
self
.
course
=
CourseFactory
.
create
(
number
=
'999'
,
display_name
=
'Robot_Super_Course'
)
self
.
overview_chapter
=
ItemFactory
.
create
(
display_name
=
'Overview'
)
self
.
overview_chapter
=
ItemFactory
.
create
(
display_name
=
'Overview'
)
self
.
courseware_chapter
=
ItemFactory
.
create
(
display_name
=
'courseware'
)
self
.
courseware_chapter
=
ItemFactory
.
create
(
display_name
=
'courseware'
)
self
.
course
=
modulestore
()
.
get_course
(
self
.
course
.
id
)
self
.
test_course
=
CourseFactory
.
create
(
number
=
'666'
,
display_name
=
'Robot_Sub_Course'
)
self
.
test_course
=
CourseFactory
.
create
(
number
=
'666'
,
display_name
=
'Robot_Sub_Course'
)
self
.
other_org_course
=
CourseFactory
.
create
(
org
=
'Other_Org_Course'
)
self
.
other_org_course
=
CourseFactory
.
create
(
org
=
'Other_Org_Course'
)
...
@@ -126,6 +128,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -126,6 +128,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
parent_location
=
self
.
overview_chapter
.
location
,
parent_location
=
self
.
overview_chapter
.
location
,
display_name
=
'Welcome'
display_name
=
'Welcome'
)
)
self
.
test_course
=
modulestore
()
.
get_course
(
self
.
test_course
.
id
)
self
.
global_staff_user
=
GlobalStaffFactory
()
self
.
global_staff_user
=
GlobalStaffFactory
()
self
.
unenrolled_user
=
UserFactory
(
last_name
=
"Unenrolled"
)
self
.
unenrolled_user
=
UserFactory
(
last_name
=
"Unenrolled"
)
...
@@ -264,10 +267,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -264,10 +267,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
# Make courses start in the future
# Make courses start in the future
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
course_data
=
{
'start'
:
tomorrow
}
self
.
course
.
start
=
tomorrow
test_course_data
=
{
'start'
:
tomorrow
}
self
.
test_course
.
start
=
tomorrow
self
.
course
=
self
.
update_course
(
self
.
course
,
course_data
)
self
.
course
=
self
.
update_course
(
self
.
course
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
,
test_course_data
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
)
self
.
assertFalse
(
self
.
course
.
has_started
())
self
.
assertFalse
(
self
.
course
.
has_started
())
self
.
assertFalse
(
self
.
test_course
.
has_started
())
self
.
assertFalse
(
self
.
test_course
.
has_started
())
...
@@ -289,10 +292,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -289,10 +292,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
course_data
=
{
'start'
:
tomorrow
}
self
.
course
.
start
=
tomorrow
test_course_data
=
{
'start'
:
tomorrow
}
self
.
test_course
.
start
=
tomorrow
self
.
course
=
self
.
update_course
(
self
.
course
,
course_data
)
self
.
course
=
self
.
update_course
(
self
.
course
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
,
test_course_data
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
)
self
.
login
(
self
.
instructor_user
)
self
.
login
(
self
.
instructor_user
)
# Enroll in the classes---can't see courseware otherwise.
# Enroll in the classes---can't see courseware otherwise.
...
@@ -312,10 +315,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -312,10 +315,10 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
now
+
datetime
.
timedelta
(
days
=
1
)
course_data
=
{
'start'
:
tomorrow
}
self
.
course
.
start
=
tomorrow
test_course_data
=
{
'start'
:
tomorrow
}
self
.
test_course
.
start
=
tomorrow
self
.
course
=
self
.
update_course
(
self
.
course
,
course_data
)
self
.
course
=
self
.
update_course
(
self
.
course
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
,
test_course_data
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
)
self
.
login
(
self
.
global_staff_user
)
self
.
login
(
self
.
global_staff_user
)
self
.
enroll
(
self
.
course
,
True
)
self
.
enroll
(
self
.
course
,
True
)
...
@@ -336,13 +339,14 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -336,13 +339,14 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
nextday
=
tomorrow
+
datetime
.
timedelta
(
days
=
1
)
nextday
=
tomorrow
+
datetime
.
timedelta
(
days
=
1
)
yesterday
=
now
-
datetime
.
timedelta
(
days
=
1
)
yesterday
=
now
-
datetime
.
timedelta
(
days
=
1
)
course_data
=
{
'enrollment_start'
:
tomorrow
,
'enrollment_end'
:
nextday
}
test_course_data
=
{
'enrollment_start'
:
yesterday
,
'enrollment_end'
:
tomorrow
}
# self.course's enrollment period hasn't started
# self.course's enrollment period hasn't started
self
.
course
=
self
.
update_course
(
self
.
course
,
course_data
)
self
.
course
.
enrollment_start
=
tomorrow
self
.
course
.
enrollment_end
=
nextday
# test_course course's has
# test_course course's has
self
.
test_course
=
self
.
update_course
(
self
.
test_course
,
test_course_data
)
self
.
test_course
.
enrollment_start
=
yesterday
self
.
test_course
.
enrollment_end
=
tomorrow
self
.
course
=
self
.
update_course
(
self
.
course
)
self
.
test_course
=
self
.
update_course
(
self
.
test_course
)
# First, try with an enrolled student
# First, try with an enrolled student
self
.
login
(
self
.
unenrolled_user
)
self
.
login
(
self
.
unenrolled_user
)
...
...
lms/djangoapps/courseware/views.py
View file @
16f0d12a
...
@@ -232,13 +232,13 @@ def index(request, course_id, chapter=None, section=None,
...
@@ -232,13 +232,13 @@ def index(request, course_id, chapter=None, section=None,
- HTTPresponse
- HTTPresponse
"""
"""
user
=
User
.
objects
.
prefetch_related
(
"groups"
)
.
get
(
id
=
request
.
user
.
id
)
user
=
User
.
objects
.
prefetch_related
(
"groups"
)
.
get
(
id
=
request
.
user
.
id
)
request
.
user
=
user
# keep just one instance of User
request
.
user
=
user
# keep just one instance of User
course
=
get_course_with_access
(
user
,
course_id
,
'load'
,
depth
=
2
)
course
=
get_course_with_access
(
user
,
course_id
,
'load'
,
depth
=
2
)
staff_access
=
has_access
(
user
,
course
,
'staff'
)
staff_access
=
has_access
(
user
,
course
,
'staff'
)
registered
=
registered_for_course
(
course
,
user
)
registered
=
registered_for_course
(
course
,
user
)
if
not
registered
:
if
not
registered
:
# TODO (vshnayder): do course instructors need to be registered to see course?
# TODO (vshnayder): do course instructors need to be registered to see course?
log
.
debug
(
u'User
{0} tried to view course {1} but is not enrolled'
.
format
(
user
,
course
.
location
.
url
()
))
log
.
debug
(
u'User
%
s tried to view course
%
s but is not enrolled'
,
user
,
course
.
location
.
url
(
))
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
masq
=
setup_masquerade
(
request
,
staff_access
)
masq
=
setup_masquerade
(
request
,
staff_access
)
...
...
lms/djangoapps/instructor_task/tests/test_base.py
View file @
16f0d12a
...
@@ -208,7 +208,9 @@ class InstructorTaskModuleTestCase(InstructorTaskCourseTestCase):
...
@@ -208,7 +208,9 @@ class InstructorTaskModuleTestCase(InstructorTaskCourseTestCase):
'num_responses'
:
2
}
'num_responses'
:
2
}
problem_xml
=
factory
.
build_xml
(
**
factory_args
)
problem_xml
=
factory
.
build_xml
(
**
factory_args
)
location
=
InstructorTaskTestCase
.
problem_location
(
problem_url_name
)
location
=
InstructorTaskTestCase
.
problem_location
(
problem_url_name
)
self
.
module_store
.
update_item
(
location
,
problem_xml
)
item
=
self
.
module_store
.
get_instance
(
self
.
course
.
id
,
location
)
item
.
data
=
problem_xml
self
.
module_store
.
update_item
(
item
,
'**replace_user**'
)
def
get_student_module
(
self
,
username
,
descriptor
):
def
get_student_module
(
self
,
username
,
descriptor
):
"""Get StudentModule object for test course, given the `username` and the problem's `descriptor`."""
"""Get StudentModule object for test course, given the `username` and the problem's `descriptor`."""
...
...
lms/djangoapps/instructor_task/tests/test_integration.py
View file @
16f0d12a
...
@@ -288,7 +288,11 @@ class TestRescoringTask(TestIntegrationTask):
...
@@ -288,7 +288,11 @@ class TestRescoringTask(TestIntegrationTask):
"""
%
(
'!='
if
redefine
else
'=='
))
"""
%
(
'!='
if
redefine
else
'=='
))
problem_xml
=
factory
.
build_xml
(
script
=
script
,
cfn
=
"check_func"
,
expect
=
"42"
,
num_responses
=
1
)
problem_xml
=
factory
.
build_xml
(
script
=
script
,
cfn
=
"check_func"
,
expect
=
"42"
,
num_responses
=
1
)
if
redefine
:
if
redefine
:
self
.
module_store
.
update_item
(
InstructorTaskModuleTestCase
.
problem_location
(
problem_url_name
),
problem_xml
)
descriptor
=
self
.
module_store
.
get_instance
(
self
.
course
.
id
,
InstructorTaskModuleTestCase
.
problem_location
(
problem_url_name
)
)
descriptor
.
data
=
problem_xml
self
.
module_store
.
update_item
(
descriptor
,
'**replace_user**'
)
else
:
else
:
# Use "per-student" rerandomization so that check-problem can be called more than once.
# Use "per-student" rerandomization so that check-problem can be called more than once.
# Using "always" means we cannot check a problem twice, but we want to call once to get the
# Using "always" means we cannot check a problem twice, but we want to call once to get the
...
...
lms/envs/acceptance.py
View file @
16f0d12a
...
@@ -44,6 +44,7 @@ MODULESTORE = {
...
@@ -44,6 +44,7 @@ MODULESTORE = {
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'OPTIONS'
:
{
'OPTIONS'
:
{
'mappings'
:
{},
'mappings'
:
{},
'reference_type'
:
'Location'
,
'stores'
:
{
'stores'
:
{
'default'
:
{
'default'
:
{
'ENGINE'
:
'xmodule.modulestore.mongo.MongoModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mongo.MongoModuleStore'
,
...
...
lms/envs/bok_choy.auth.json
View file @
16f0d12a
...
@@ -51,6 +51,7 @@
...
@@ -51,6 +51,7 @@
"ENGINE"
:
"xmodule.modulestore.mixed.MixedModuleStore"
,
"ENGINE"
:
"xmodule.modulestore.mixed.MixedModuleStore"
,
"OPTIONS"
:
{
"OPTIONS"
:
{
"mappings"
:
{},
"mappings"
:
{},
"reference_type"
:
"Location"
,
"stores"
:
{
"stores"
:
{
"default"
:
{
"default"
:
{
"DOC_STORE_CONFIG"
:
{
"DOC_STORE_CONFIG"
:
{
...
...
lms/envs/cms/mixed_dev.py
View file @
16f0d12a
...
@@ -12,6 +12,7 @@ MODULESTORE = {
...
@@ -12,6 +12,7 @@ MODULESTORE = {
'default'
:
{
'default'
:
{
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mixed.MixedModuleStore'
,
'OPTIONS'
:
{
'OPTIONS'
:
{
'reference_type'
:
'Location'
,
'mappings'
:
{
'mappings'
:
{
'MITx/2.01x/2013_Spring'
:
'xml'
'MITx/2.01x/2013_Spring'
:
'xml'
},
},
...
...
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