MixedModulestore wraps most getters, update_item, delete_item
with code to translate between addressing schemes based on app and persistence layer addressing scheme specification. STUD-1206
This commit is contained in:
@@ -102,7 +102,7 @@ def update_course_updates(location, update, passed_id=None):
|
||||
|
||||
# update db record
|
||||
course_updates.data = html.tostring(course_html_parsed)
|
||||
modulestore('direct').update_item(location, course_updates.data)
|
||||
modulestore('direct').update_item(course_updates, 'course_info_model')
|
||||
|
||||
return {
|
||||
"id": idx,
|
||||
@@ -158,7 +158,7 @@ def delete_course_update(location, update, passed_id):
|
||||
# update db record
|
||||
course_updates.data = html.tostring(course_html_parsed)
|
||||
store = modulestore('direct')
|
||||
store.update_item(location, course_updates.data)
|
||||
store.update_item(course_updates, 'course_info_model')
|
||||
|
||||
return get_course_updates(location, None)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class ChecklistTestCase(CourseTestCase):
|
||||
# Save the changed `checklists` to the underlying KeyValueStore before updating the modulestore
|
||||
self.course.save()
|
||||
modulestore = get_modulestore(self.course.location)
|
||||
modulestore.update_metadata(self.course.location, own_metadata(self.course))
|
||||
modulestore.update_item(self.course, self.user.pk)
|
||||
self.assertEqual(self.get_persisted_checklists(), None)
|
||||
response = self.client.get(self.checklists_url)
|
||||
self.assertEqual(payload, response.content)
|
||||
|
||||
@@ -47,8 +47,6 @@ from xmodule.exceptions import NotFoundError
|
||||
|
||||
from django_comment_common.utils import are_permissions_roles_seeded
|
||||
from xmodule.exceptions import InvalidVersionError
|
||||
import datetime
|
||||
from pytz import UTC
|
||||
from uuid import uuid4
|
||||
from pymongo import MongoClient
|
||||
from student.models import CourseEnrollment
|
||||
@@ -126,11 +124,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
|
||||
course.advanced_modules = component_types
|
||||
|
||||
# Save the data that we've just changed to the underlying
|
||||
# MongoKeyValueStore before we update the mongo datastore.
|
||||
course.save()
|
||||
|
||||
store.update_metadata(course.location, own_metadata(course))
|
||||
store.update_item(course, self.user.username)
|
||||
|
||||
# just pick one vertical
|
||||
descriptor = store.get_items(Location('i4x', 'edX', 'simple', 'vertical', None, None))[0]
|
||||
@@ -269,7 +263,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertIn('graceperiod', own_metadata(html_module))
|
||||
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.username)
|
||||
|
||||
# read back to make sure it reads as 'own-metadata'
|
||||
html_module = draft_store.get_item(Location('i4x', 'edX', 'simple', 'html', 'test_html', None))
|
||||
@@ -385,8 +379,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(course.tabs, expected_tabs)
|
||||
|
||||
item.display_name = 'Updated'
|
||||
item.save()
|
||||
module_store.update_metadata(item.location, own_metadata(item))
|
||||
module_store.update_item(item, self.user.username)
|
||||
|
||||
course = module_store.get_item(course_location)
|
||||
|
||||
@@ -834,9 +827,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
html_module = module_store.get_instance(source_location.course_id, html_module_location)
|
||||
|
||||
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))
|
||||
module_store.update_item(html_module_location, new_data)
|
||||
module_store.update_item(html_module, None)
|
||||
|
||||
html_module = module_store.get_instance(source_location.course_id, html_module_location)
|
||||
self.assertEqual(new_data, html_module.data)
|
||||
@@ -858,22 +851,18 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
draft_store = modulestore('draft')
|
||||
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')
|
||||
# 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)
|
||||
direct_store.create_and_save_xmodule(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')
|
||||
|
||||
# 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)})
|
||||
with self.assertRaises(InvalidVersionError):
|
||||
draft_store.update_item(chapter, 'user')
|
||||
|
||||
self.assertRaises(InvalidVersionError, draft_store.unpublish, location)
|
||||
|
||||
@@ -992,8 +981,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
sequential = module_store.get_item(Location(['i4x', 'edX', 'toy',
|
||||
'sequential', 'vertical_sequential', None]))
|
||||
private_location_no_draft = private_vertical.location.replace(revision=None)
|
||||
module_store.update_children(sequential.location, sequential.children +
|
||||
[private_location_no_draft.url()])
|
||||
sequential.children.append(private_location_no_draft.url())
|
||||
module_store.update_item(sequential, 'user')
|
||||
|
||||
# read back the sequential, to make sure we have a pointer to
|
||||
sequential = module_store.get_item(Location(['i4x', 'edX', 'toy',
|
||||
@@ -1285,31 +1274,6 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertFalse(Location(['i4x', 'edX', 'toy', 'vertical', 'vertical_test', None])
|
||||
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):
|
||||
module_store = modulestore('direct')
|
||||
content_store = contentstore()
|
||||
@@ -1319,16 +1283,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
import_from_xml(module_store, 'common/test/data/', ['toy'])
|
||||
location = CourseDescriptor.id_to_location('edX/toy/2012_Fall')
|
||||
|
||||
# Add a sequence
|
||||
|
||||
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())
|
||||
|
||||
@@ -1343,7 +1298,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
module_store, root_dir, ['test_export_no_content_store'],
|
||||
draft_store=None,
|
||||
static_content_store=None,
|
||||
target_location_namespace=course.location
|
||||
target_location_namespace=location
|
||||
)
|
||||
|
||||
# Verify reimported course
|
||||
@@ -1810,7 +1765,8 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
# crate a new module and add it as a child to a vertical
|
||||
module_store.create_and_save_xmodule(new_component_location)
|
||||
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, 'user')
|
||||
|
||||
# flush the cache
|
||||
module_store.refresh_cached_metadata_inheritance_tree(new_component_location)
|
||||
@@ -1827,8 +1783,7 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
# now let's define an override at the leaf node level
|
||||
#
|
||||
new_module.graceperiod = timedelta(1)
|
||||
new_module.save()
|
||||
module_store.update_metadata(new_module.location, own_metadata(new_module))
|
||||
module_store.update_item(new_module, self.user.username)
|
||||
|
||||
# flush the cache and refetch
|
||||
module_store.refresh_cached_metadata_inheritance_tree(new_component_location)
|
||||
@@ -1942,10 +1897,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
|
||||
delattr(self.video_descriptor, field_name)
|
||||
|
||||
self.assertNotIn('html5_sources', own_metadata(self.video_descriptor))
|
||||
get_modulestore(location).update_metadata(
|
||||
location,
|
||||
own_metadata(self.video_descriptor)
|
||||
)
|
||||
get_modulestore(location).update_item(self.video_descriptor, 'testuser')
|
||||
module = get_modulestore(location).get_item(location)
|
||||
|
||||
self.assertNotIn('html5_sources', own_metadata(module))
|
||||
@@ -2001,7 +1953,7 @@ def _course_factory_create_course():
|
||||
Creates a course via the CourseFactory and returns the locator for it.
|
||||
"""
|
||||
course = CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
|
||||
return loc_mapper().translate_location(course.location.course_id, course.location, True, True)
|
||||
return loc_mapper().translate_location(course.id, course.location, False, True)
|
||||
|
||||
|
||||
def _get_course_id(test_course_data):
|
||||
|
||||
@@ -123,7 +123,7 @@ class CourseUpdateTest(CourseTestCase):
|
||||
modulestore('direct').create_and_save_xmodule(location)
|
||||
course_updates = modulestore('direct').get_item(location)
|
||||
course_updates.data = 'bad news'
|
||||
modulestore('direct').update_item(location, course_updates.data)
|
||||
modulestore('direct').update_item(course_updates, 'test_course_updates')
|
||||
|
||||
init_content = '<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
|
||||
content = init_content + '</iframe>'
|
||||
|
||||
@@ -4,7 +4,6 @@ Test finding orphans via the view and django config
|
||||
import json
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from xmodule.modulestore.django import editable_modulestore, loc_mapper
|
||||
from django.core.urlresolvers import reverse
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
class TestOrphan(CourseTestCase):
|
||||
@@ -35,7 +34,7 @@ class TestOrphan(CourseTestCase):
|
||||
parent_location = self.course.location.replace(category=parent_category, name=parent_name)
|
||||
parent = editable_modulestore('direct').get_item(parent_location)
|
||||
parent.children.append(location.url())
|
||||
editable_modulestore('direct').update_children(parent_location, parent.children)
|
||||
editable_modulestore('direct').update_item(parent, self.user.pk)
|
||||
|
||||
def test_mongo_orphan(self):
|
||||
"""
|
||||
|
||||
@@ -58,11 +58,8 @@ class TextbookIndexTestCase(CourseTestCase):
|
||||
}
|
||||
]
|
||||
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.update_metadata(self.course.location, own_metadata(self.course))
|
||||
store.update_item(self.course, self.user.pk)
|
||||
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
@@ -200,7 +197,7 @@ class TextbookDetailTestCase(CourseTestCase):
|
||||
# MongoKeyValueStore before we update the mongo datastore.
|
||||
self.course.save()
|
||||
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.pk)
|
||||
self.url_nonexist = self.course_locator.url_reverse("textbooks", "20")
|
||||
|
||||
def test_get_1(self):
|
||||
|
||||
@@ -63,13 +63,13 @@ class Basetranscripts(CourseTestCase):
|
||||
self.item_locator, self.item_location = self._get_locator(resp)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
self.item = modulestore().get_item(self.item_location)
|
||||
# hI10vDNYz4M - valid Youtube ID with 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" />'
|
||||
modulestore().update_item(self.item_location, data)
|
||||
self.item.data = '<video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
self.item = modulestore().get_item(self.item_location)
|
||||
|
||||
# Remove all transcripts for current module.
|
||||
self.clear_subs_content()
|
||||
|
||||
@@ -130,14 +130,14 @@ class TestUploadtranscripts(Basetranscripts):
|
||||
self.bad_name_srt_file.seek(0)
|
||||
|
||||
def test_success_video_module_source_subs_uploading(self):
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""")
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
link = reverse('upload_transcripts')
|
||||
filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
|
||||
@@ -212,8 +212,9 @@ class TestUploadtranscripts(Basetranscripts):
|
||||
}
|
||||
resp = self.client.ajax_post('/xblock', data)
|
||||
item_locator, item_location = self._get_locator(resp)
|
||||
data = '<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
|
||||
modulestore().update_item(item_location, data)
|
||||
item = modulestore().get_item(item_location)
|
||||
item.data = '<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
|
||||
modulestore().update_item(item, 'test_transcripts')
|
||||
|
||||
# non_video module: testing
|
||||
|
||||
@@ -232,8 +233,8 @@ class TestUploadtranscripts(Basetranscripts):
|
||||
self.assertEqual(json.loads(resp.content).get('status'), 'Transcripts are supported only for "video" modules.')
|
||||
|
||||
def test_fail_bad_xml(self):
|
||||
data = '<<<video youtube="0.75:JMD_ifUUfsU,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
|
||||
modulestore().update_item(self.item_location, data)
|
||||
self.item.data = '<<<video youtube="0.75:JMD_ifUUfsU,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" />'
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
link = reverse('upload_transcripts')
|
||||
filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
|
||||
@@ -344,8 +345,8 @@ class TestDownloadtranscripts(Basetranscripts):
|
||||
pass
|
||||
|
||||
def test_success_download_youtube(self):
|
||||
data = '<video youtube="1:JMD_ifUUfsU" />'
|
||||
modulestore().update_item(self.item_location, data)
|
||||
self.item.data = '<video youtube="1:JMD_ifUUfsU" />'
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -365,14 +366,14 @@ class TestDownloadtranscripts(Basetranscripts):
|
||||
|
||||
def test_success_download_nonyoutube(self):
|
||||
subs_id = str(uuid4())
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""".format(subs_id))
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -424,14 +425,15 @@ class TestDownloadtranscripts(Basetranscripts):
|
||||
resp = self.client.ajax_post('/xblock', data)
|
||||
item_locator, item_location = self._get_locator(resp)
|
||||
subs_id = str(uuid4())
|
||||
data = textwrap.dedent("""
|
||||
item = modulestore().get_item(item_location)
|
||||
item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</videoalpha>
|
||||
""".format(subs_id))
|
||||
modulestore().update_item(item_location, data)
|
||||
modulestore().update_item(item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -449,28 +451,28 @@ class TestDownloadtranscripts(Basetranscripts):
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
def test_fail_nonyoutube_subs_dont_exist(self):
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""")
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
link = reverse('download_transcripts')
|
||||
resp = self.client.get(link, {'locator': self.item_locator})
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
def test_empty_youtube_attr_and_sub_attr(self):
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""")
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
link = reverse('download_transcripts')
|
||||
resp = self.client.get(link, {'locator': self.item_locator})
|
||||
@@ -479,14 +481,14 @@ class TestDownloadtranscripts(Basetranscripts):
|
||||
|
||||
def test_fail_bad_sjson_subs(self):
|
||||
subs_id = str(uuid4())
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""".format(subs_id))
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -532,14 +534,14 @@ class TestChecktranscripts(Basetranscripts):
|
||||
|
||||
def test_success_download_nonyoutube(self):
|
||||
subs_id = str(uuid4())
|
||||
data = textwrap.dedent("""
|
||||
self.item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</video>
|
||||
""".format(subs_id))
|
||||
modulestore().update_item(self.item_location, data)
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -582,8 +584,8 @@ class TestChecktranscripts(Basetranscripts):
|
||||
transcripts_utils.remove_subs_from_store(subs_id, self.item)
|
||||
|
||||
def test_check_youtube(self):
|
||||
data = '<video youtube="1:JMD_ifUUfsU" />'
|
||||
modulestore().update_item(self.item_location, data)
|
||||
self.item.data = '<video youtube="1:JMD_ifUUfsU" />'
|
||||
modulestore().update_item(self.item, 'test_transcripts')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
@@ -674,14 +676,15 @@ class TestChecktranscripts(Basetranscripts):
|
||||
resp = self.client.ajax_post('/xblock', data)
|
||||
item_locator, item_location = self._get_locator(resp)
|
||||
subs_id = str(uuid4())
|
||||
data = textwrap.dedent("""
|
||||
item = modulestore().get_item(item_location)
|
||||
item.data = textwrap.dedent("""
|
||||
<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.webm"/>
|
||||
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.ogv"/>
|
||||
</videoalpha>
|
||||
""".format(subs_id))
|
||||
modulestore().update_item(item_location, data)
|
||||
modulestore().update_item(item, 'test_transcript')
|
||||
|
||||
subs = {
|
||||
'start': [100, 200, 240],
|
||||
|
||||
@@ -286,7 +286,7 @@ def save_module(item):
|
||||
"""
|
||||
item.save()
|
||||
store = get_modulestore(Location(item.id))
|
||||
store.update_metadata(item.id, own_metadata(item))
|
||||
store.update_item(item, 'save_module')
|
||||
|
||||
|
||||
def copy_or_rename_transcript(new_name, old_name, item, delete_old=False):
|
||||
|
||||
@@ -222,16 +222,6 @@ def compute_unit_state(unit):
|
||||
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):
|
||||
"""
|
||||
Used to add the panel tab to a course if it does not exist.
|
||||
|
||||
@@ -51,8 +51,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
|
||||
# from the template.
|
||||
if not course_module.checklists:
|
||||
course_module.checklists = CourseDescriptor.checklists.default
|
||||
course_module.save()
|
||||
modulestore.update_metadata(old_location, own_metadata(course_module))
|
||||
modulestore.update_item(course_module, request.user.username)
|
||||
|
||||
expanded_checklists = expand_all_action_urls(course_module)
|
||||
if json_request:
|
||||
@@ -81,7 +80,7 @@ def checklists_handler(request, tag=None, package_id=None, branch=None, version_
|
||||
# not default
|
||||
course_module.checklists = course_module.checklists
|
||||
course_module.save()
|
||||
modulestore.update_metadata(old_location, own_metadata(course_module))
|
||||
modulestore.update_item(course_module, request.user.username)
|
||||
expanded_checklist = expand_checklist_action_url(course_module, persisted_checklist)
|
||||
return JsonResponse(expanded_checklist)
|
||||
else:
|
||||
|
||||
@@ -163,8 +163,7 @@ def course_listing(request):
|
||||
"""
|
||||
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_items(Location(None, None, None, 'course', None))
|
||||
courses = modulestore('direct').get_courses()
|
||||
|
||||
# filter out courses that we don't have access too
|
||||
def course_filter(course):
|
||||
@@ -743,10 +742,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):
|
||||
course.tabs.append({"type": "pdf_textbooks"})
|
||||
course.pdf_textbooks = textbooks
|
||||
store.update_metadata(
|
||||
course.location,
|
||||
own_metadata(course)
|
||||
)
|
||||
store.update_item(course, request.user.username)
|
||||
return JsonResponse(course.pdf_textbooks)
|
||||
elif request.method == 'POST':
|
||||
# create a new textbook for the course
|
||||
@@ -764,7 +760,7 @@ def textbooks_list_handler(request, tag=None, package_id=None, branch=None, vers
|
||||
tabs = course.tabs
|
||||
tabs.append({"type": "pdf_textbooks"})
|
||||
course.tabs = tabs
|
||||
store.update_metadata(course.location, own_metadata(course))
|
||||
store.update_item(course, request.user.username)
|
||||
resp = JsonResponse(textbook, status=201)
|
||||
resp["Location"] = locator.url_reverse('textbooks', textbook["id"])
|
||||
return resp
|
||||
@@ -815,10 +811,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
|
||||
course.pdf_textbooks = new_textbooks
|
||||
else:
|
||||
course.pdf_textbooks.append(new_textbook)
|
||||
store.update_metadata(
|
||||
course.location,
|
||||
own_metadata(course)
|
||||
)
|
||||
store.update_item(course, request.user.username)
|
||||
return JsonResponse(new_textbook, status=201)
|
||||
elif request.method == 'DELETE':
|
||||
if not textbook:
|
||||
@@ -827,10 +820,7 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
|
||||
new_textbooks = course.pdf_textbooks[0:i]
|
||||
new_textbooks.extend(course.pdf_textbooks[i + 1:])
|
||||
course.pdf_textbooks = new_textbooks
|
||||
store.update_metadata(
|
||||
course.location,
|
||||
own_metadata(course)
|
||||
)
|
||||
store.update_item(course, request.user.username)
|
||||
return JsonResponse()
|
||||
|
||||
|
||||
|
||||
@@ -232,7 +232,8 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
|
||||
modulestore().convert_to_draft(item_location)
|
||||
|
||||
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:
|
||||
data = existing_item.get_explicitly_set_fields_by_scope(Scope.content)
|
||||
|
||||
@@ -242,9 +243,9 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
|
||||
for child_locator
|
||||
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:
|
||||
# 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
|
||||
@@ -269,15 +270,12 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
|
||||
return JsonResponse({"error": "Invalid data"}, 400)
|
||||
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':
|
||||
manage_video_subtitles_save(existing_item, existing_item)
|
||||
|
||||
# commit to datastore
|
||||
store.update_item(existing_item, request.user)
|
||||
|
||||
result = {
|
||||
'id': unicode(usage_loc),
|
||||
'data': data,
|
||||
@@ -339,7 +337,8 @@ def _create_item(request):
|
||||
|
||||
# TODO replace w/ nicer accessor
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -373,13 +372,14 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
|
||||
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).
|
||||
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
|
||||
if source_item.has_children:
|
||||
copied_children = []
|
||||
dest_module.children = []
|
||||
for child in source_item.children:
|
||||
copied_children.append(_duplicate_item(dest_location, Location(child)).url())
|
||||
get_modulestore(dest_location).update_children(dest_location, copied_children)
|
||||
dest_module.children.append(_duplicate_item(dest_location, Location(child)).url())
|
||||
get_modulestore(dest_location).update_item(dest_module, 'user')
|
||||
|
||||
if not 'detached' in source_item.runtime.load_block_type(category)._class_tags:
|
||||
parent = get_modulestore(parent_location).get_item(parent_location)
|
||||
@@ -390,7 +390,7 @@ def _duplicate_item(parent_location, duplicate_source_location, display_name=Non
|
||||
parent.children.insert(source_index + 1, dest_location.url())
|
||||
else:
|
||||
parent.children.append(dest_location.url())
|
||||
get_modulestore(parent_location).update_children(parent_location, parent.children)
|
||||
get_modulestore(parent_location).update_item(parent, 'user')
|
||||
|
||||
return dest_location
|
||||
|
||||
@@ -406,22 +406,19 @@ def _delete_item_at_location(item_location, delete_children=False, delete_all_ve
|
||||
item = store.get_item(item_location)
|
||||
|
||||
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:
|
||||
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
|
||||
if delete_all_versions:
|
||||
parent_locs = modulestore('direct').get_parent_locations(item_location, None)
|
||||
|
||||
item_url = item_location.url()
|
||||
for parent_loc in parent_locs:
|
||||
parent = modulestore('direct').get_item(parent_loc)
|
||||
item_url = item_location.url()
|
||||
if item_url in parent.children:
|
||||
children = parent.children
|
||||
children.remove(item_url)
|
||||
parent.children = children
|
||||
modulestore('direct').update_children(parent.location, parent.children)
|
||||
parent.children.remove(item_url)
|
||||
modulestore('direct').update_item(parent, 'user')
|
||||
|
||||
return JsonResponse()
|
||||
|
||||
@@ -452,7 +449,7 @@ def orphan_handler(request, tag=None, package_id=None, branch=None, version_guid
|
||||
if request.user.is_staff:
|
||||
items = modulestore().get_orphans(old_location, 'draft')
|
||||
for item in items:
|
||||
modulestore('draft').delete_item(item, True)
|
||||
modulestore('draft').delete_item(item, delete_all_versions=True)
|
||||
return JsonResponse({'deleted': items})
|
||||
else:
|
||||
raise PermissionDenied()
|
||||
|
||||
@@ -47,7 +47,7 @@ def initialize_course_tabs(course):
|
||||
{"type": "progress", "name": _("Progress")},
|
||||
]
|
||||
|
||||
modulestore('direct').update_metadata(course.location.url(), own_metadata(course))
|
||||
modulestore('direct').update_item(course, 'system')
|
||||
|
||||
@expect_json
|
||||
@login_required
|
||||
@@ -123,7 +123,7 @@ def tabs_handler(request, tag=None, package_id=None, branch=None, version_guid=N
|
||||
|
||||
# OK, re-assemble the static tabs in the new order
|
||||
course_item.tabs = reordered_tabs
|
||||
modulestore('direct').update_metadata(course_item.location, own_metadata(course_item))
|
||||
modulestore('direct').update_item(course_item, request.user.username)
|
||||
return JsonResponse()
|
||||
else:
|
||||
raise NotImplementedError('Creating or changing tab content is not supported.')
|
||||
@@ -179,7 +179,7 @@ def primitive_delete(course, num):
|
||||
# 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.
|
||||
# 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, 'primitive delete')
|
||||
|
||||
|
||||
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)}
|
||||
tabs = course.tabs
|
||||
tabs.insert(num, new_tab)
|
||||
modulestore('direct').update_metadata(course.location, own_metadata(course))
|
||||
modulestore('direct').update_item(course, 'primitive insert')
|
||||
|
||||
|
||||
@@ -6,10 +6,8 @@ from json.encoder import JSONEncoder
|
||||
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.inheritance import own_metadata
|
||||
from contentstore.utils import get_modulestore, course_image_url
|
||||
from models.settings import course_grading
|
||||
from contentstore.utils import update_item
|
||||
from xmodule.fields import Date
|
||||
from xmodule.modulestore.django import loc_mapper
|
||||
|
||||
@@ -74,6 +72,24 @@ class CourseDetails(object):
|
||||
|
||||
return course
|
||||
|
||||
@classmethod
|
||||
def update_about_item(cls, course_old_location, about_key, data, course):
|
||||
"""
|
||||
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, 'update_about_item')
|
||||
|
||||
@classmethod
|
||||
def update_from_json(cls, course_locator, jsondict):
|
||||
"""
|
||||
@@ -130,26 +146,15 @@ class CourseDetails(object):
|
||||
dirty = True
|
||||
|
||||
if dirty:
|
||||
# Save the data that we've just changed to the underlying
|
||||
# MongoKeyValueStore before we update the mongo datastore.
|
||||
descriptor.save()
|
||||
|
||||
get_modulestore(course_old_location).update_metadata(course_old_location, own_metadata(descriptor))
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'course_details')
|
||||
|
||||
# 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.
|
||||
temploc = Location(course_old_location).replace(category='about', name='syllabus')
|
||||
update_item(temploc, jsondict['syllabus'])
|
||||
for about_type in ['syllabus', 'overview', 'effort']:
|
||||
cls.update_about_item(course_old_location, about_type, jsondict[about_type], descriptor)
|
||||
|
||||
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'])
|
||||
update_item(temploc, recomposed_video_tag)
|
||||
cls.update_about_item(course_old_location, 'video', recomposed_video_tag, descriptor)
|
||||
|
||||
# Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
|
||||
# it persisted correctly
|
||||
|
||||
@@ -65,9 +65,7 @@ class CourseGradingModel(object):
|
||||
descriptor.raw_grader = graders_parsed
|
||||
descriptor.grade_cutoffs = jsondict['grade_cutoffs']
|
||||
|
||||
get_modulestore(course_old_location).update_item(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.content)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'course_grading')
|
||||
|
||||
CourseGradingModel.update_grace_period_from_json(course_locator, jsondict['grace_period'])
|
||||
|
||||
@@ -91,9 +89,7 @@ class CourseGradingModel(object):
|
||||
else:
|
||||
descriptor.raw_grader.append(grader)
|
||||
|
||||
get_modulestore(course_old_location).update_item(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.content)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'course_grading')
|
||||
|
||||
return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index])
|
||||
|
||||
@@ -107,9 +103,7 @@ class CourseGradingModel(object):
|
||||
descriptor = get_modulestore(course_old_location).get_item(course_old_location)
|
||||
descriptor.grade_cutoffs = cutoffs
|
||||
|
||||
get_modulestore(course_old_location).update_item(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.content)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'course_grading')
|
||||
|
||||
return cutoffs
|
||||
|
||||
@@ -132,9 +126,7 @@ class CourseGradingModel(object):
|
||||
grace_timedelta = timedelta(**graceperiodjson)
|
||||
descriptor.graceperiod = grace_timedelta
|
||||
|
||||
get_modulestore(course_old_location).update_metadata(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.settings)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'update_grace_period')
|
||||
|
||||
@staticmethod
|
||||
def delete_grader(course_location, index):
|
||||
@@ -150,9 +142,7 @@ class CourseGradingModel(object):
|
||||
# force propagation to definition
|
||||
descriptor.raw_grader = descriptor.raw_grader
|
||||
|
||||
get_modulestore(course_old_location).update_item(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.content)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'delete_grader')
|
||||
|
||||
@staticmethod
|
||||
def delete_grace_period(course_location):
|
||||
@@ -164,9 +154,7 @@ class CourseGradingModel(object):
|
||||
|
||||
del descriptor.graceperiod
|
||||
|
||||
get_modulestore(course_old_location).update_metadata(
|
||||
course_old_location, descriptor.get_explicitly_set_fields_by_scope(Scope.settings)
|
||||
)
|
||||
get_modulestore(course_old_location).update_item(descriptor, 'delete_grace_period')
|
||||
|
||||
@staticmethod
|
||||
def get_section_grader_type(location):
|
||||
@@ -186,9 +174,7 @@ class CourseGradingModel(object):
|
||||
del descriptor.format
|
||||
del descriptor.graded
|
||||
|
||||
get_modulestore(descriptor.location).update_metadata(
|
||||
descriptor.location, descriptor.get_explicitly_set_fields_by_scope(Scope.settings)
|
||||
)
|
||||
get_modulestore(descriptor.location).update_item(descriptor, 'update_grader')
|
||||
return {'graderType': grader_type}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from xblock.fields import Scope
|
||||
|
||||
from contentstore.utils import get_modulestore
|
||||
from xmodule.modulestore.inheritance import own_metadata
|
||||
from cms.lib.xblock.mixin import CmsBlockMixin
|
||||
|
||||
|
||||
@@ -78,6 +77,6 @@ class CourseMetadata(object):
|
||||
setattr(descriptor, key, value)
|
||||
|
||||
if dirty:
|
||||
get_modulestore(descriptor.location).update_metadata(descriptor.location, own_metadata(descriptor))
|
||||
get_modulestore(descriptor.location).update_item(descriptor, 'update_settings')
|
||||
|
||||
return cls.fetch(descriptor)
|
||||
|
||||
Reference in New Issue
Block a user