refactor: rename ModuleStore runtimes now that XModules are gone (#35523)
* Consolidates and renames the runtime used as a base for all the others:
* Before: `xmodule.x_module:DescriptorSystem` and
`xmodule.mako_block:MakoDescriptorSystem`.
* After: `xmodule.x_module:ModuleStoreRuntime`.
* Co-locates and renames the runtimes for importing course OLX:
* Before: `xmodule.x_module:XMLParsingSystem` and
`xmodule.modulestore.xml:ImportSystem`.
* After: `xmodule.modulestore.xml:XMLParsingModuleStoreRuntime` and
`xmodule.modulestore.xml:XMLImportingModuleStoreRuntime`.
* Note: I would have liked to consolidate these, but it would have
involved nontrivial test refactoring.
* Renames the stub Old Mongo runtime:
* Before: `xmodule.modulestore.mongo.base:CachingDescriptorSystem`.
* After: `xmodule.modulestore.mongo.base:OldModuleStoreRuntime`.
* Renames the Split Mongo runtime, the which is what runs courses in LMS and CMS:
* Before: `xmodule.modulestore.split_mongo.caching_descriptor_system:CachingDescriptorSystem`.
* After: `xmodule.modulestore.split_mongo.runtime:SplitModuleStoreRuntime`.
* Renames some of the dummy runtimes used only in unit tests.
This commit is contained in:
@@ -529,7 +529,7 @@ def _import_xml_node_to_parent(
|
|||||||
node_copied_version = node.attrib.get('copied_from_version', None)
|
node_copied_version = node.attrib.get('copied_from_version', None)
|
||||||
|
|
||||||
# Modulestore's IdGenerator here is SplitMongoIdManager which is assigned
|
# Modulestore's IdGenerator here is SplitMongoIdManager which is assigned
|
||||||
# by CachingDescriptorSystem Runtime and since we need our custom ImportIdGenerator
|
# by SplitModuleStoreRuntime and since we need our custom ImportIdGenerator
|
||||||
# here we are temporaraliy swtiching it.
|
# here we are temporaraliy swtiching it.
|
||||||
original_id_generator = runtime.id_generator
|
original_id_generator = runtime.id_generator
|
||||||
|
|
||||||
@@ -566,7 +566,8 @@ def _import_xml_node_to_parent(
|
|||||||
else:
|
else:
|
||||||
# We have to handle the children ourselves, because there are lots of complex interactions between
|
# We have to handle the children ourselves, because there are lots of complex interactions between
|
||||||
# * the vanilla XBlock parse_xml() method, and its lack of API for "create and save a new XBlock"
|
# * the vanilla XBlock parse_xml() method, and its lack of API for "create and save a new XBlock"
|
||||||
# * the XmlMixin version of parse_xml() which only works with ImportSystem, not modulestore or the v2 runtime
|
# * the XmlMixin version of parse_xml() which only works with XMLImportingModuleStoreRuntime,
|
||||||
|
# not modulestore or the v2 runtime
|
||||||
# * the modulestore APIs for creating and saving a new XBlock, which work but don't support XML parsing.
|
# * the modulestore APIs for creating and saving a new XBlock, which work but don't support XML parsing.
|
||||||
# We can safely assume that if the XBLock class supports children, every child node will be the XML
|
# We can safely assume that if the XBLock class supports children, every child node will be the XML
|
||||||
# serialization of a child block, in order. For blocks that don't support children, their XML content/nodes
|
# serialization of a child block, in order. For blocks that don't support children, their XML content/nodes
|
||||||
|
|||||||
@@ -1855,7 +1855,7 @@ class TestDuplicateItemWithAsides(ItemTest, DuplicateHelper):
|
|||||||
|
|
||||||
@XBlockAside.register_temp_plugin(AsideTest, "test_aside")
|
@XBlockAside.register_temp_plugin(AsideTest, "test_aside")
|
||||||
@patch(
|
@patch(
|
||||||
"xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types",
|
"xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types",
|
||||||
lambda self, block: ["test_aside"],
|
lambda self, block: ["test_aside"],
|
||||||
)
|
)
|
||||||
def test_duplicate_equality_with_asides(self):
|
def test_duplicate_equality_with_asides(self):
|
||||||
@@ -2700,8 +2700,8 @@ class TestEditSplitModule(ItemTest):
|
|||||||
group_id_to_child = split_test.group_id_to_child.copy()
|
group_id_to_child = split_test.group_id_to_child.copy()
|
||||||
self.assertEqual(2, len(group_id_to_child))
|
self.assertEqual(2, len(group_id_to_child))
|
||||||
|
|
||||||
# CachingDescriptorSystem is used in tests.
|
# SplitModuleStoreRuntime is used in tests.
|
||||||
# CachingDescriptorSystem doesn't have user service, that's needed for
|
# SplitModuleStoreRuntime doesn't have user service, that's needed for
|
||||||
# SplitTestBlock. So, in this line of code we add this service manually.
|
# SplitTestBlock. So, in this line of code we add this service manually.
|
||||||
split_test.runtime._services["user"] = DjangoXBlockUserService( # pylint: disable=protected-access
|
split_test.runtime._services["user"] = DjangoXBlockUserService( # pylint: disable=protected-access
|
||||||
self.user
|
self.user
|
||||||
@@ -4449,7 +4449,7 @@ class TestXBlockPublishingInfo(ItemTest):
|
|||||||
|
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types",
|
"xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types",
|
||||||
lambda self, block: ["test_aside"],
|
lambda self, block: ["test_aside"],
|
||||||
)
|
)
|
||||||
class TestUpdateFromSource(ModuleStoreTestCase):
|
class TestUpdateFromSource(ModuleStoreTestCase):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ReplaceURLService(Service):
|
|||||||
A service for replacing static/course/jump-to-id URLs with absolute URLs in XBlocks.
|
A service for replacing static/course/jump-to-id URLs with absolute URLs in XBlocks.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
block: (optional) An XBlock instance. Used when retrieving the service from the DescriptorSystem.
|
block: (optional) An XBlock instance. Used when retrieving the service from the ModuleStoreRuntime.
|
||||||
static_asset_path: (optional) Path for static assets, which overrides data_directory and course_id, if nonempty
|
static_asset_path: (optional) Path for static assets, which overrides data_directory and course_id, if nonempty
|
||||||
static_paths_out: (optional) Array to collect tuples for each static URI found:
|
static_paths_out: (optional) Array to collect tuples for each static URI found:
|
||||||
* the original unmodified static URI
|
* the original unmodified static URI
|
||||||
@@ -39,7 +39,7 @@ class ReplaceURLService(Service):
|
|||||||
self.jump_to_id_base_url = jump_to_id_base_url
|
self.jump_to_id_base_url = jump_to_id_base_url
|
||||||
self.lookup_asset_url = lookup_asset_url
|
self.lookup_asset_url = lookup_asset_url
|
||||||
# This is needed because the `Service` class initialization expects the XBlock passed as an `xblock` keyword
|
# This is needed because the `Service` class initialization expects the XBlock passed as an `xblock` keyword
|
||||||
# argument, but the `service` method from the `DescriptorSystem` passes a `block`.
|
# argument, but the `service` method from the `ModuleStoreRuntime` passes a `block`.
|
||||||
self._xblock = self.xblock() or block
|
self._xblock = self.xblock() or block
|
||||||
|
|
||||||
def replace_urls(self, text, static_replace_only=False):
|
def replace_urls(self, text, static_replace_only=False):
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class TestCourseListing(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
|||||||
self._create_course_with_access_groups(course_key)
|
self._create_course_with_access_groups(course_key)
|
||||||
|
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.SplitMongoKVS', mock.Mock(side_effect=Exception)
|
'xmodule.modulestore.split_mongo.runtime.SplitMongoKVS', mock.Mock(side_effect=Exception)
|
||||||
):
|
):
|
||||||
assert isinstance(modulestore().get_course(course_key), ErrorBlock)
|
assert isinstance(modulestore().get_course(course_key), ErrorBlock)
|
||||||
|
|
||||||
|
|||||||
@@ -242,8 +242,7 @@ To support the Libraries Relaunch in Sumac:
|
|||||||
Video blocks.
|
Video blocks.
|
||||||
|
|
||||||
* We will define method(s) for syncing update on the XBlock runtime so that
|
* We will define method(s) for syncing update on the XBlock runtime so that
|
||||||
they are available in the SplitModuleStore's XBlock Runtime
|
they are available in the SplitModuleStoreRuntime.
|
||||||
(CachingDescriptorSystem).
|
|
||||||
|
|
||||||
* Either in the initial implementation or in a later implementation, it may
|
* Either in the initial implementation or in a later implementation, it may
|
||||||
make sense to declare abstract versions of the syncing method(s) higher up
|
make sense to declare abstract versions of the syncing method(s) higher up
|
||||||
@@ -355,10 +354,10 @@ inheritance hierarchy of CachingDescriptorSystem and SplitModuleStoreRuntime.)
|
|||||||
|
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# xmodule/modulestore/split_mongo/caching_descriptor_system.py
|
# xmodule/modulestore/split_mongo/runtime.py
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
class CachingDescriptorSystem(...):
|
class SplitModuleStoreRuntime(...):
|
||||||
|
|
||||||
def validate_upstream_key(self, usage_key: UsageKey | str) -> UsageKey:
|
def validate_upstream_key(self, usage_key: UsageKey | str) -> UsageKey:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from xmodule.modulestore.django import modulestore
|
|||||||
|
|
||||||
def get_block_side_effect(block_locator, user_known):
|
def get_block_side_effect(block_locator, user_known):
|
||||||
"""
|
"""
|
||||||
Side effect for `CachingDescriptorSystem.get_block`
|
Side effect for `SplitModuleStoreRuntime.get_block`
|
||||||
"""
|
"""
|
||||||
store = modulestore()
|
store = modulestore()
|
||||||
course = store.get_course(block_locator.course_key)
|
course = store.get_course(block_locator.course_key)
|
||||||
@@ -126,8 +126,8 @@ class TestGetCourseBlocks(UserPartitionTestMixin, CourseStructureTestCase):
|
|||||||
|
|
||||||
Access checks are done through the transformers and through Runtime get_block_for_descriptor. Due
|
Access checks are done through the transformers and through Runtime get_block_for_descriptor. Due
|
||||||
to the runtime limitations during the tests, the Runtime access checks are not performed as
|
to the runtime limitations during the tests, the Runtime access checks are not performed as
|
||||||
get_block_for_descriptor is never called and Block is returned by CachingDescriptorSystem.get_block.
|
get_block_for_descriptor is never called and Block is returned by SplitModuleStoreRuntime.get_block.
|
||||||
In this test, we mock the CachingDescriptorSystem.get_block and check block access for known and unknown users.
|
In this test, we mock the SplitModuleStoreRuntime.get_block and check block access for known and unknown users.
|
||||||
For known users, it performs the Runtime access checks through get_block_for_descriptor. For unknown, it
|
For known users, it performs the Runtime access checks through get_block_for_descriptor. For unknown, it
|
||||||
skips the access checks.
|
skips the access checks.
|
||||||
"""
|
"""
|
||||||
@@ -137,7 +137,7 @@ class TestGetCourseBlocks(UserPartitionTestMixin, CourseStructureTestCase):
|
|||||||
add_user_to_cohort(cohort, self.user.username)
|
add_user_to_cohort(cohort, self.user.username)
|
||||||
|
|
||||||
side_effect = get_block_side_effect_for_known_user if user_known else get_block_side_effect_for_unknown_user
|
side_effect = get_block_side_effect_for_known_user if user_known else get_block_side_effect_for_unknown_user
|
||||||
with patch('xmodule.modulestore.split_mongo.split.CachingDescriptorSystem.get_block', side_effect=side_effect):
|
with patch('xmodule.modulestore.split_mongo.split.SplitModuleStoreRuntime.get_block', side_effect=side_effect):
|
||||||
block_structure = get_course_blocks(
|
block_structure = get_course_blocks(
|
||||||
self.user,
|
self.user,
|
||||||
self.course.location,
|
self.course.location,
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class LmsModuleRenderError(Exception):
|
|||||||
def make_track_function(request):
|
def make_track_function(request):
|
||||||
'''
|
'''
|
||||||
Make a tracking function that logs what happened.
|
Make a tracking function that logs what happened.
|
||||||
For use in DescriptorSystem.
|
For use in ModuleStoreRuntime.
|
||||||
'''
|
'''
|
||||||
from common.djangoapps.track import views as track_views
|
from common.djangoapps.track import views as track_views
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
|
|||||||
|
|
||||||
def new_module_runtime(self, runtime=None, **kwargs):
|
def new_module_runtime(self, runtime=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Generate a new DescriptorSystem that is minimally set up for testing
|
Generate a new ModuleStoreRuntime that is minimally set up for testing
|
||||||
"""
|
"""
|
||||||
if runtime:
|
if runtime:
|
||||||
return prepare_block_runtime(runtime, course_id=self.course.id, **kwargs)
|
return prepare_block_runtime(runtime, course_id=self.course.id, **kwargs)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory, Toy
|
|||||||
from xmodule.modulestore.tests.test_asides import AsideTestType # lint-amnesty, pylint: disable=wrong-import-order
|
from xmodule.modulestore.tests.test_asides import AsideTestType # lint-amnesty, pylint: disable=wrong-import-order
|
||||||
from xmodule.services import RebindUserServiceError
|
from xmodule.services import RebindUserServiceError
|
||||||
from xmodule.video_block import VideoBlock # lint-amnesty, pylint: disable=wrong-import-order
|
from xmodule.video_block import VideoBlock # lint-amnesty, pylint: disable=wrong-import-order
|
||||||
from xmodule.x_module import STUDENT_VIEW, DescriptorSystem # lint-amnesty, pylint: disable=wrong-import-order
|
from xmodule.x_module import STUDENT_VIEW, ModuleStoreRuntime # lint-amnesty, pylint: disable=wrong-import-order
|
||||||
from common.djangoapps.course_modes.models import CourseMode # lint-amnesty, pylint: disable=reimported
|
from common.djangoapps.course_modes.models import CourseMode # lint-amnesty, pylint: disable=reimported
|
||||||
from common.djangoapps.student.tests.factories import (
|
from common.djangoapps.student.tests.factories import (
|
||||||
BetaTesterFactory,
|
BetaTesterFactory,
|
||||||
@@ -461,8 +461,11 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
|||||||
@override_settings(FIELD_OVERRIDE_PROVIDERS=(
|
@override_settings(FIELD_OVERRIDE_PROVIDERS=(
|
||||||
'lms.djangoapps.courseware.student_field_overrides.IndividualStudentOverrideProvider',
|
'lms.djangoapps.courseware.student_field_overrides.IndividualStudentOverrideProvider',
|
||||||
))
|
))
|
||||||
@patch('xmodule.modulestore.xml.ImportSystem.applicable_aside_types', lambda self, block: ['test_aside'])
|
@patch(
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
'xmodule.modulestore.xml.XMLImportingModuleStoreRuntime.applicable_aside_types',
|
||||||
|
lambda self, block: ['test_aside']
|
||||||
|
)
|
||||||
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
@ddt.data('regular', 'test_aside')
|
@ddt.data('regular', 'test_aside')
|
||||||
@@ -1920,7 +1923,7 @@ class TestAnonymousStudentId(SharedModuleStoreTestCase, LoginEnrollmentTestCase)
|
|||||||
location=location,
|
location=location,
|
||||||
static_asset_path=None,
|
static_asset_path=None,
|
||||||
_runtime=Mock(
|
_runtime=Mock(
|
||||||
spec=DescriptorSystem,
|
spec=ModuleStoreRuntime,
|
||||||
resources_fs=None,
|
resources_fs=None,
|
||||||
mixologist=Mock(_mixins=(), name='mixologist'),
|
mixologist=Mock(_mixins=(), name='mixologist'),
|
||||||
_services={},
|
_services={},
|
||||||
@@ -1933,7 +1936,7 @@ class TestAnonymousStudentId(SharedModuleStoreTestCase, LoginEnrollmentTestCase)
|
|||||||
fields={},
|
fields={},
|
||||||
days_early_for_beta=None,
|
days_early_for_beta=None,
|
||||||
)
|
)
|
||||||
block.runtime = DescriptorSystem(None, None, None)
|
block.runtime = ModuleStoreRuntime(None, None, None)
|
||||||
# Use the xblock_class's bind_for_student method
|
# Use the xblock_class's bind_for_student method
|
||||||
block.bind_for_student = partial(xblock_class.bind_for_student, block)
|
block.bind_for_student = partial(xblock_class.bind_for_student, block)
|
||||||
|
|
||||||
@@ -2006,9 +2009,9 @@ class TestModuleTrackingContext(SharedModuleStoreTestCase):
|
|||||||
assert problem_display_name == block_info['display_name']
|
assert problem_display_name == block_info['display_name']
|
||||||
|
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
@patch('xmodule.modulestore.mongo.base.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.mongo.base.OldModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types',
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_context_contains_aside_info(self, mock_tracker):
|
def test_context_contains_aside_info(self, mock_tracker):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ from xmodule.modulestore.inheritance import own_metadata
|
|||||||
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE
|
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from xmodule.tests.helpers import override_descriptor_system # pylint: disable=unused-import
|
from xmodule.tests.helpers import override_descriptor_system # pylint: disable=unused-import
|
||||||
from xmodule.tests.test_import import DummySystem
|
from xmodule.tests.test_import import DummyModuleStoreRuntime
|
||||||
from xmodule.tests.test_video import VideoBlockTestBase
|
from xmodule.tests.test_video import VideoBlockTestBase
|
||||||
from xmodule.video_block import VideoBlock, bumper_utils, video_utils
|
from xmodule.video_block import VideoBlock, bumper_utils, video_utils
|
||||||
from xmodule.video_block.transcripts_utils import Transcript, save_to_store, subs_filename
|
from xmodule.video_block.transcripts_utils import Transcript, save_to_store, subs_filename
|
||||||
@@ -1989,7 +1989,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
|||||||
Test that import val data internal works as expected.
|
Test that import val data internal works as expected.
|
||||||
"""
|
"""
|
||||||
create_profile('mobile')
|
create_profile('mobile')
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
edx_video_id = 'test_edx_video_id'
|
edx_video_id = 'test_edx_video_id'
|
||||||
sub_id = '0CzPOIIdUsA'
|
sub_id = '0CzPOIIdUsA'
|
||||||
@@ -2095,7 +2095,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
|||||||
"""
|
"""
|
||||||
xml_data = """<video><video_asset></video_asset></video>"""
|
xml_data = """<video><video_asset></video_asset></video>"""
|
||||||
xml_object = etree.fromstring(xml_data)
|
xml_object = etree.fromstring(xml_data)
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Verify edx_video_id is empty before.
|
# Verify edx_video_id is empty before.
|
||||||
assert self.block.edx_video_id == ''
|
assert self.block.edx_video_id == ''
|
||||||
@@ -2131,7 +2131,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
|||||||
val_transcript_provider=val_transcript_provider
|
val_transcript_provider=val_transcript_provider
|
||||||
)
|
)
|
||||||
xml_object = etree.fromstring(xml_data)
|
xml_object = etree.fromstring(xml_data)
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Create static directory in import file system and place transcript files inside it.
|
# Create static directory in import file system and place transcript files inside it.
|
||||||
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
||||||
@@ -2237,7 +2237,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
|||||||
edx_video_id = 'test_edx_video_id'
|
edx_video_id = 'test_edx_video_id'
|
||||||
language_code = 'en'
|
language_code = 'en'
|
||||||
|
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Create static directory in import file system and place transcript files inside it.
|
# Create static directory in import file system and place transcript files inside it.
|
||||||
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
||||||
@@ -2306,7 +2306,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
|||||||
|
|
||||||
def test_import_val_data_invalid(self):
|
def test_import_val_data_invalid(self):
|
||||||
create_profile('mobile')
|
create_profile('mobile')
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Negative file_size is invalid
|
# Negative file_size is invalid
|
||||||
xml_data = """
|
xml_data = """
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from django.test import TestCase
|
|||||||
from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator
|
from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator
|
||||||
from xblock.fields import ScopeIds
|
from xblock.fields import ScopeIds
|
||||||
|
|
||||||
from xmodule.x_module import DescriptorSystem
|
from xmodule.x_module import ModuleStoreRuntime
|
||||||
from lms.djangoapps.lms_xblock.runtime import handler_url
|
from lms.djangoapps.lms_xblock.runtime import handler_url
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ class TestHandlerUrl(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.block = BlockMock(name='block')
|
self.block = BlockMock(name='block')
|
||||||
self.runtime = DescriptorSystem(
|
self.runtime = ModuleStoreRuntime(
|
||||||
load_item=Mock(name='get_test_descriptor_system.load_item'),
|
load_item=Mock(name='get_test_descriptor_system.load_item'),
|
||||||
resources_fs=Mock(name='get_test_descriptor_system.resources_fs'),
|
resources_fs=Mock(name='get_test_descriptor_system.resources_fs'),
|
||||||
error_tracker=Mock(name='get_test_descriptor_system.error_tracker')
|
error_tracker=Mock(name='get_test_descriptor_system.error_tracker')
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class RuntimeShim:
|
|||||||
"""
|
"""
|
||||||
# We can't parse XML in a vacuum - we need to know the parent block and/or the
|
# We can't parse XML in a vacuum - we need to know the parent block and/or the
|
||||||
# OLX file that holds this XML in order to generate useful definition keys etc.
|
# OLX file that holds this XML in order to generate useful definition keys etc.
|
||||||
# The older ImportSystem runtime could do this because it stored the course_id
|
# The older XMLImportingModuleStoreRuntime runtime could do this because it stored the course_id
|
||||||
# as part of the runtime.
|
# as part of the runtime.
|
||||||
raise NotImplementedError("This newer runtime does not support process_xml()")
|
raise NotImplementedError("This newer runtime does not support process_xml()")
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ class RuntimeShim:
|
|||||||
|
|
||||||
def get_field_provenance(self, xblock, field):
|
def get_field_provenance(self, xblock, field):
|
||||||
"""
|
"""
|
||||||
A Studio-specific method that was implemented on DescriptorSystem.
|
A Studio-specific method that was implemented on ModuleStoreRuntime.
|
||||||
Used by the problem block.
|
Used by the problem block.
|
||||||
|
|
||||||
For the given xblock, return a dict for the field's current state:
|
For the given xblock, return a dict for the field's current state:
|
||||||
|
|||||||
@@ -177,7 +177,10 @@ class TestXBlockAside(SharedModuleStoreTestCase):
|
|||||||
"""test if xblock is not aside"""
|
"""test if xblock is not aside"""
|
||||||
assert is_xblock_aside(self.block.scope_ids.usage_id) is False
|
assert is_xblock_aside(self.block.scope_ids.usage_id) is False
|
||||||
|
|
||||||
@patch('xmodule.modulestore.xml.ImportSystem.applicable_aside_types', lambda self, block: ['test_aside'])
|
@patch(
|
||||||
|
'xmodule.modulestore.xml.XMLImportingModuleStoreRuntime.applicable_aside_types',
|
||||||
|
lambda self, block: ['test_aside'],
|
||||||
|
)
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
def test_get_aside(self):
|
def test_get_aside(self):
|
||||||
"""test get aside success"""
|
"""test get aside success"""
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class LoncapaSystem(object):
|
|||||||
i18n: an object implementing the `gettext.Translations` interface so
|
i18n: an object implementing the `gettext.Translations` interface so
|
||||||
that we can use `.ugettext` to localize strings.
|
that we can use `.ugettext` to localize strings.
|
||||||
|
|
||||||
See :class:`DescriptorSystem` for documentation of other attributes.
|
See :class:`ModuleStoreRuntime` for documentation of other attributes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class ErrorBlock(
|
|||||||
Build a new ErrorBlock using ``system``.
|
Build a new ErrorBlock using ``system``.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
system (:class:`DescriptorSystem`): The :class:`DescriptorSystem` used
|
system (:class:`ModuleStoreRuntime`): The :class:`ModuleStoreRuntime` used
|
||||||
to construct the XBlock that had an error.
|
to construct the XBlock that had an error.
|
||||||
contents (unicode): An encoding of the content of the xblock that had an error.
|
contents (unicode): An encoding of the content of the xblock that had an error.
|
||||||
error_msg (unicode): A message describing the error.
|
error_msg (unicode): A message describing the error.
|
||||||
|
|||||||
@@ -5,30 +5,7 @@ Code to handle mako templating for XModules and XBlocks.
|
|||||||
|
|
||||||
from web_fragments.fragment import Fragment
|
from web_fragments.fragment import Fragment
|
||||||
|
|
||||||
from .x_module import DescriptorSystem, shim_xmodule_js
|
from .x_module import shim_xmodule_js
|
||||||
|
|
||||||
|
|
||||||
class MakoDescriptorSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method
|
|
||||||
"""
|
|
||||||
Descriptor system that renders mako templates.
|
|
||||||
"""
|
|
||||||
def __init__(self, render_template, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
self.render_template = render_template
|
|
||||||
|
|
||||||
# Add the MakoService to the runtime services.
|
|
||||||
# If it already exists, do not attempt to reinitialize it; otherwise, this could override the `namespace_prefix`
|
|
||||||
# of the `MakoService`, breaking template rendering in Studio.
|
|
||||||
#
|
|
||||||
# This is not needed by most XBlocks, because the MakoService is added to their runtimes.
|
|
||||||
# However, there are a few cases where the MakoService is not added to the XBlock's
|
|
||||||
# runtime. Specifically:
|
|
||||||
# * in the Instructor Dashboard bulk emails tab, when rendering the HtmlBlock for its WYSIWYG editor.
|
|
||||||
# * during testing, when fetching factory-created blocks.
|
|
||||||
if 'mako' not in self._services:
|
|
||||||
from common.djangoapps.edxmako.services import MakoService
|
|
||||||
self._services['mako'] = MakoService()
|
|
||||||
|
|
||||||
|
|
||||||
class MakoTemplateBlockBase:
|
class MakoTemplateBlockBase:
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ class EditInfo:
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.from_storable(kwargs)
|
self.from_storable(kwargs)
|
||||||
|
|
||||||
# For details, see caching_descriptor_system.py get_subtree_edited_by/on.
|
# For details, see runtime.py get_subtree_edited_by/on.
|
||||||
self._subtree_edited_on = kwargs.get('_subtree_edited_on', None)
|
self._subtree_edited_on = kwargs.get('_subtree_edited_on', None)
|
||||||
self._subtree_edited_by = kwargs.get('_subtree_edited_by', None)
|
self._subtree_edited_by = kwargs.get('_subtree_edited_by', None)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ from xmodule.course_block import CourseSummary
|
|||||||
from xmodule.error_block import ErrorBlock
|
from xmodule.error_block import ErrorBlock
|
||||||
from xmodule.errortracker import exc_info_to_str, null_error_tracker
|
from xmodule.errortracker import exc_info_to_str, null_error_tracker
|
||||||
from xmodule.exceptions import HeartbeatFailure
|
from xmodule.exceptions import HeartbeatFailure
|
||||||
from xmodule.mako_block import MakoDescriptorSystem
|
|
||||||
from xmodule.modulestore import BulkOperationsMixin, ModuleStoreEnum, ModuleStoreWriteBase
|
from xmodule.modulestore import BulkOperationsMixin, ModuleStoreEnum, ModuleStoreWriteBase
|
||||||
from xmodule.modulestore.draft_and_published import DIRECT_ONLY_CATEGORIES, ModuleStoreDraftAndPublished
|
from xmodule.modulestore.draft_and_published import DIRECT_ONLY_CATEGORIES, ModuleStoreDraftAndPublished
|
||||||
from xmodule.modulestore.edit_info import EditInfoRuntimeMixin
|
from xmodule.modulestore.edit_info import EditInfoRuntimeMixin
|
||||||
@@ -46,6 +45,7 @@ from xmodule.modulestore.xml import CourseLocationManager
|
|||||||
from xmodule.mongo_utils import connect_to_mongodb, create_collection_index
|
from xmodule.mongo_utils import connect_to_mongodb, create_collection_index
|
||||||
from xmodule.partitions.partitions_service import PartitionService
|
from xmodule.partitions.partitions_service import PartitionService
|
||||||
from xmodule.services import SettingsService
|
from xmodule.services import SettingsService
|
||||||
|
from xmodule.x_module import ModuleStoreRuntime
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -146,19 +146,19 @@ class MongoKeyValueStore(InheritanceKeyValueStore):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # lint-amnesty, pylint: disable=abstract-method
|
class OldModuleStoreRuntime(ModuleStoreRuntime, EditInfoRuntimeMixin): # pylint: disable=abstract-method
|
||||||
"""
|
"""
|
||||||
A system that has a cache of block json that it will use to load blocks
|
A system that has a cache of block json that it will use to load blocks
|
||||||
from, with a backup of calling to the underlying modulestore for more data
|
from, with a backup of calling to the underlying modulestore for more data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This CachingDescriptorSystem runtime sets block._field_data on each block via construct_xblock_from_class(),
|
# This OldModuleStoreRuntime sets block._field_data on each block via construct_xblock_from_class(),
|
||||||
# rather than the newer approach of providing a "field-data" service via runtime.service(). As a result, during
|
# rather than the newer approach of providing a "field-data" service via runtime.service(). As a result, during
|
||||||
# bind_for_student() we can't just set ._bound_field_data; we must overwrite block._field_data.
|
# bind_for_student() we can't just set ._bound_field_data; we must overwrite block._field_data.
|
||||||
uses_deprecated_field_data = True
|
uses_deprecated_field_data = True
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "CachingDescriptorSystem{!r}".format((
|
return "{}{!r}".format(self.__class__.__name__, (
|
||||||
self.modulestore,
|
self.modulestore,
|
||||||
str(self.course_id),
|
str(self.course_id),
|
||||||
[str(key) for key in self.module_data.keys()],
|
[str(key) for key in self.module_data.keys()],
|
||||||
@@ -177,12 +177,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li
|
|||||||
default_class: The default_class to use when loading an
|
default_class: The default_class to use when loading an
|
||||||
XModuleDescriptor from the module_data
|
XModuleDescriptor from the module_data
|
||||||
|
|
||||||
resources_fs: a filesystem, as per MakoDescriptorSystem
|
resources_fs: a filesystem, as per ModuleStoreRuntime
|
||||||
|
|
||||||
error_tracker: a function that logs errors for later display to users
|
error_tracker: a function that logs errors for later display to users
|
||||||
|
|
||||||
render_template: a function for rendering templates, as per
|
render_template: a function for rendering templates, as per
|
||||||
MakoDescriptorSystem
|
ModuleStoreRuntime
|
||||||
"""
|
"""
|
||||||
id_manager = CourseLocationManager(course_key)
|
id_manager = CourseLocationManager(course_key)
|
||||||
kwargs.setdefault('id_reader', id_manager)
|
kwargs.setdefault('id_reader', id_manager)
|
||||||
@@ -660,7 +660,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
|
|||||||
data_dir (optional): The directory name to use as the root data directory for this XModule
|
data_dir (optional): The directory name to use as the root data directory for this XModule
|
||||||
data_cache (dict): A dictionary mapping from UsageKeys to xblock field data
|
data_cache (dict): A dictionary mapping from UsageKeys to xblock field data
|
||||||
(this is the xblock data loaded from the database)
|
(this is the xblock data loaded from the database)
|
||||||
using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
|
using_descriptor_system (OldModuleStoreRuntime): The existing runtime
|
||||||
to add data to, and to load the XBlocks from.
|
to add data to, and to load the XBlocks from.
|
||||||
for_parent (:class:`XBlock`): The parent of the XBlock being loaded.
|
for_parent (:class:`XBlock`): The parent of the XBlock being loaded.
|
||||||
"""
|
"""
|
||||||
@@ -687,7 +687,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
|
|||||||
|
|
||||||
services["partitions"] = PartitionService(course_key)
|
services["partitions"] = PartitionService(course_key)
|
||||||
|
|
||||||
system = CachingDescriptorSystem(
|
system = OldModuleStoreRuntime(
|
||||||
modulestore=self,
|
modulestore=self,
|
||||||
course_key=course_key,
|
course_key=course_key,
|
||||||
module_data=data_cache,
|
module_data=data_cache,
|
||||||
@@ -922,7 +922,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
|
|||||||
descendents of the queried blocks for more efficient results later
|
descendents of the queried blocks for more efficient results later
|
||||||
in the request. The depth is counted in the number of
|
in the request. The depth is counted in the number of
|
||||||
calls to get_children() to cache. None indicates to cache all descendents.
|
calls to get_children() to cache. None indicates to cache all descendents.
|
||||||
using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
|
using_descriptor_system (SplitModuleStoreRuntime): The existing SplitModuleStoreRuntime
|
||||||
to add data to, and to load the XBlocks from.
|
to add data to, and to load the XBlocks from.
|
||||||
"""
|
"""
|
||||||
item = self._find_one(usage_key)
|
item = self._find_one(usage_key)
|
||||||
@@ -994,7 +994,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
|
|||||||
For this modulestore, ``name`` is a commonly provided key (Location based stores)
|
For this modulestore, ``name`` is a commonly provided key (Location based stores)
|
||||||
This modulestore does not allow searching dates by comparison or edited_by, previous_version,
|
This modulestore does not allow searching dates by comparison or edited_by, previous_version,
|
||||||
update_version info.
|
update_version info.
|
||||||
using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
|
using_descriptor_system (SplitModuleStoreRuntime): The existing SplitModuleStoreRuntime
|
||||||
to add data to, and to load the XBlocks from.
|
to add data to, and to load the XBlocks from.
|
||||||
"""
|
"""
|
||||||
qualifiers = qualifiers.copy() if qualifiers else {} # copy the qualifiers (destructively manipulated here)
|
qualifiers = qualifiers.copy() if qualifiers else {} # copy the qualifiers (destructively manipulated here)
|
||||||
@@ -1104,7 +1104,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
|
|||||||
|
|
||||||
services["partitions"] = PartitionService(course_key)
|
services["partitions"] = PartitionService(course_key)
|
||||||
|
|
||||||
runtime = CachingDescriptorSystem(
|
runtime = OldModuleStoreRuntime(
|
||||||
modulestore=self,
|
modulestore=self,
|
||||||
module_data={},
|
module_data={},
|
||||||
course_key=course_key,
|
course_key=course_key,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class DraftModuleStore(MongoModuleStore):
|
|||||||
Note: If the item is in DIRECT_ONLY_CATEGORIES, then returns only the PUBLISHED
|
Note: If the item is in DIRECT_ONLY_CATEGORIES, then returns only the PUBLISHED
|
||||||
version regardless of the revision.
|
version regardless of the revision.
|
||||||
|
|
||||||
using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
|
using_descriptor_system (ModuleStoreRuntime): The existing runtime
|
||||||
to add data to, and to load the XBlocks from.
|
to add data to, and to load the XBlocks from.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ from xmodule.x_module import AsideKeyGenerator, OpaqueKeyReader
|
|||||||
class SplitMongoIdManager(OpaqueKeyReader, AsideKeyGenerator): # pylint: disable=abstract-method
|
class SplitMongoIdManager(OpaqueKeyReader, AsideKeyGenerator): # pylint: disable=abstract-method
|
||||||
"""
|
"""
|
||||||
An IdManager that knows how to retrieve the DefinitionLocator, given
|
An IdManager that knows how to retrieve the DefinitionLocator, given
|
||||||
a usage_id and a :class:`.CachingDescriptorSystem`.
|
a usage_id and a :class:`.SplitModuleStoreRuntime`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, caching_descriptor_system):
|
def __init__(self, runtime):
|
||||||
self._cds = caching_descriptor_system
|
self._runtime = runtime
|
||||||
|
|
||||||
def get_definition_id(self, usage_id):
|
def get_definition_id(self, usage_id):
|
||||||
if isinstance(usage_id.block_id, LocalId):
|
if isinstance(usage_id.block_id, LocalId):
|
||||||
# a LocalId indicates that this block hasn't been persisted yet, and is instead stored
|
# a LocalId indicates that this block hasn't been persisted yet, and is instead stored
|
||||||
# in-memory in the local_modules dictionary.
|
# in-memory in the local_modules dictionary.
|
||||||
return self._cds.local_modules[usage_id].scope_ids.def_id
|
return self._runtime.local_modules[usage_id].scope_ids.def_id
|
||||||
else:
|
else:
|
||||||
block_key = BlockKey.from_usage_key(usage_id)
|
block_key = BlockKey.from_usage_key(usage_id)
|
||||||
module_data = self._cds.get_module_data(block_key, usage_id.course_key)
|
module_data = self._runtime.get_module_data(block_key, usage_id.course_key)
|
||||||
|
|
||||||
if module_data.definition is not None:
|
if module_data.definition is not None:
|
||||||
return DefinitionLocator(usage_id.block_type, module_data.definition)
|
return DefinitionLocator(usage_id.block_type, module_data.definition)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from xblock.runtime import KeyValueStore, KvsFieldData
|
|||||||
from xmodule.error_block import ErrorBlock
|
from xmodule.error_block import ErrorBlock
|
||||||
from xmodule.errortracker import exc_info_to_str
|
from xmodule.errortracker import exc_info_to_str
|
||||||
from xmodule.library_tools import LegacyLibraryToolsService
|
from xmodule.library_tools import LegacyLibraryToolsService
|
||||||
from xmodule.mako_block import MakoDescriptorSystem
|
|
||||||
from xmodule.modulestore import BlockData
|
from xmodule.modulestore import BlockData
|
||||||
from xmodule.modulestore.edit_info import EditInfoRuntimeMixin
|
from xmodule.modulestore.edit_info import EditInfoRuntimeMixin
|
||||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||||
@@ -24,12 +23,12 @@ from xmodule.modulestore.split_mongo.definition_lazy_loader import DefinitionLaz
|
|||||||
from xmodule.modulestore.split_mongo.id_manager import SplitMongoIdManager
|
from xmodule.modulestore.split_mongo.id_manager import SplitMongoIdManager
|
||||||
from xmodule.modulestore.split_mongo.split_mongo_kvs import SplitMongoKVS
|
from xmodule.modulestore.split_mongo.split_mongo_kvs import SplitMongoKVS
|
||||||
from xmodule.util.misc import get_library_or_course_attribute
|
from xmodule.util.misc import get_library_or_course_attribute
|
||||||
from xmodule.x_module import XModuleMixin
|
from xmodule.x_module import XModuleMixin, ModuleStoreRuntime
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # lint-amnesty, pylint: disable=abstract-method
|
class SplitModuleStoreRuntime(ModuleStoreRuntime, EditInfoRuntimeMixin): # pylint: disable=abstract-method
|
||||||
"""
|
"""
|
||||||
A system that has a cache of a course version's json that it will use to load blocks
|
A system that has a cache of a course version's json that it will use to load blocks
|
||||||
from, with a backup of calling to the underlying modulestore for more data.
|
from, with a backup of calling to the underlying modulestore for more data.
|
||||||
@@ -106,7 +106,7 @@ from xmodule.util.misc import get_library_or_course_attribute
|
|||||||
from xmodule.util.keys import BlockKey, derive_key
|
from xmodule.util.keys import BlockKey, derive_key
|
||||||
|
|
||||||
from ..exceptions import ItemNotFoundError
|
from ..exceptions import ItemNotFoundError
|
||||||
from .caching_descriptor_system import CachingDescriptorSystem
|
from .runtime import SplitModuleStoreRuntime
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -715,7 +715,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
|
|||||||
per course per fetch operations are done.
|
per course per fetch operations are done.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
system: a CachingDescriptorSystem
|
system: a SplitModuleStoreRuntime
|
||||||
base_block_ids: list of BlockIds to fetch
|
base_block_ids: list of BlockIds to fetch
|
||||||
course_key: the destination course providing the context
|
course_key: the destination course providing the context
|
||||||
depth: how deep below these to prefetch
|
depth: how deep below these to prefetch
|
||||||
@@ -3290,7 +3290,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
|
|||||||
if not isinstance(course_entry.course_key, LibraryLocator):
|
if not isinstance(course_entry.course_key, LibraryLocator):
|
||||||
services["partitions"] = PartitionService(course_entry.course_key)
|
services["partitions"] = PartitionService(course_entry.course_key)
|
||||||
|
|
||||||
return CachingDescriptorSystem(
|
return SplitModuleStoreRuntime(
|
||||||
modulestore=self,
|
modulestore=self,
|
||||||
course_entry=course_entry,
|
course_entry=course_entry,
|
||||||
module_data={},
|
module_data={},
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class TestAsidesXmlStore(TestCase):
|
|||||||
Test Asides sourced from xml store
|
Test Asides sourced from xml store
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@patch('xmodule.modulestore.xml.ImportSystem.applicable_aside_types', lambda self, block: ['test_aside'])
|
@patch(
|
||||||
|
'xmodule.modulestore.xml.XMLImportingModuleStoreRuntime.applicable_aside_types',
|
||||||
|
lambda self, block: ['test_aside']
|
||||||
|
)
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
def test_xml_aside(self):
|
def test_xml_aside(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -3349,7 +3349,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_aside_crud(self, default_store):
|
def test_aside_crud(self, default_store):
|
||||||
"""
|
"""
|
||||||
@@ -3423,7 +3423,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_export_course_with_asides(self, default_store):
|
def test_export_course_with_asides(self, default_store):
|
||||||
if default_store == ModuleStoreEnum.Type.mongo:
|
if default_store == ModuleStoreEnum.Type.mongo:
|
||||||
@@ -3510,7 +3510,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_export_course_after_creating_new_items_with_asides(self, default_store): # pylint: disable=too-many-statements
|
def test_export_course_after_creating_new_items_with_asides(self, default_store): # pylint: disable=too-many-statements
|
||||||
if default_store == ModuleStoreEnum.Type.mongo:
|
if default_store == ModuleStoreEnum.Type.mongo:
|
||||||
@@ -3645,7 +3645,7 @@ class TestAsidesWithMixedModuleStore(CommonMixedModuleStoreSetup):
|
|||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
||||||
@XBlockAside.register_temp_plugin(AsideBar, 'test_aside2')
|
@XBlockAside.register_temp_plugin(AsideBar, 'test_aside2')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside1', 'test_aside2'])
|
lambda self, block: ['test_aside1', 'test_aside2'])
|
||||||
def test_get_and_update_asides(self, default_store):
|
def test_get_and_update_asides(self, default_store):
|
||||||
"""
|
"""
|
||||||
@@ -3709,7 +3709,7 @@ class TestAsidesWithMixedModuleStore(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside1'])
|
lambda self, block: ['test_aside1'])
|
||||||
def test_clone_course_with_asides(self, default_store):
|
def test_clone_course_with_asides(self, default_store):
|
||||||
"""
|
"""
|
||||||
@@ -3757,7 +3757,7 @@ class TestAsidesWithMixedModuleStore(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data(ModuleStoreEnum.Type.split)
|
@ddt.data(ModuleStoreEnum.Type.split)
|
||||||
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside1'])
|
lambda self, block: ['test_aside1'])
|
||||||
def test_delete_item_with_asides(self, default_store):
|
def test_delete_item_with_asides(self, default_store):
|
||||||
"""
|
"""
|
||||||
@@ -3806,7 +3806,7 @@ class TestAsidesWithMixedModuleStore(CommonMixedModuleStoreSetup):
|
|||||||
|
|
||||||
@ddt.data((ModuleStoreEnum.Type.split, 1, 0))
|
@ddt.data((ModuleStoreEnum.Type.split, 1, 0))
|
||||||
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
@XBlockAside.register_temp_plugin(AsideFoo, 'test_aside1')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside1'])
|
lambda self, block: ['test_aside1'])
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_published_and_unpublish_item_with_asides(self, default_store, max_find, max_send):
|
def test_published_and_unpublish_item_with_asides(self, default_store, max_find, max_send):
|
||||||
|
|||||||
@@ -415,14 +415,14 @@ class TestSplitDirectOnlyCategorySemantics(DirectOnlyCategorySemantics):
|
|||||||
|
|
||||||
@ddt.data(*TESTABLE_BLOCK_TYPES)
|
@ddt.data(*TESTABLE_BLOCK_TYPES)
|
||||||
@XBlockAside.register_temp_plugin(AsideTest, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTest, 'test_aside')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_create_with_asides(self, block_type):
|
def test_create_with_asides(self, block_type):
|
||||||
self._do_create(block_type, with_asides=True)
|
self._do_create(block_type, with_asides=True)
|
||||||
|
|
||||||
@ddt.data(*TESTABLE_BLOCK_TYPES)
|
@ddt.data(*TESTABLE_BLOCK_TYPES)
|
||||||
@XBlockAside.register_temp_plugin(AsideTest, 'test_aside')
|
@XBlockAside.register_temp_plugin(AsideTest, 'test_aside')
|
||||||
@patch('xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.applicable_aside_types',
|
@patch('xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.applicable_aside_types',
|
||||||
lambda self, block: ['test_aside'])
|
lambda self, block: ['test_aside'])
|
||||||
def test_update_asides(self, block_type):
|
def test_update_asides(self, block_type):
|
||||||
block_usage_key = self._do_create(block_type, with_asides=True)
|
block_usage_key = self._do_create(block_type, with_asides=True)
|
||||||
|
|||||||
@@ -15,24 +15,29 @@ from importlib import import_module
|
|||||||
|
|
||||||
from fs.osfs import OSFS
|
from fs.osfs import OSFS
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from opaque_keys.edx.keys import CourseKey
|
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator
|
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator
|
||||||
from path import Path as path
|
from path import Path as path
|
||||||
|
from xblock.core import XBlockAside
|
||||||
from xblock.field_data import DictFieldData
|
from xblock.field_data import DictFieldData
|
||||||
from xblock.fields import ScopeIds
|
from xblock.fields import (
|
||||||
|
Reference,
|
||||||
|
ReferenceList,
|
||||||
|
ReferenceValueDict,
|
||||||
|
ScopeIds,
|
||||||
|
)
|
||||||
from xblock.runtime import DictKeyValueStore
|
from xblock.runtime import DictKeyValueStore
|
||||||
|
|
||||||
from common.djangoapps.util.monitoring import monitor_import_failure
|
from common.djangoapps.util.monitoring import monitor_import_failure
|
||||||
from xmodule.error_block import ErrorBlock
|
from xmodule.error_block import ErrorBlock
|
||||||
from xmodule.errortracker import exc_info_to_str, make_error_tracker
|
from xmodule.errortracker import exc_info_to_str, make_error_tracker
|
||||||
from xmodule.mako_block import MakoDescriptorSystem
|
|
||||||
from xmodule.modulestore import COURSE_ROOT, LIBRARY_ROOT, ModuleStoreEnum, ModuleStoreReadBase
|
from xmodule.modulestore import COURSE_ROOT, LIBRARY_ROOT, ModuleStoreEnum, ModuleStoreReadBase
|
||||||
from xmodule.modulestore.xml_exporter import DEFAULT_CONTENT_FIELDS
|
from xmodule.modulestore.xml_exporter import DEFAULT_CONTENT_FIELDS
|
||||||
from xmodule.tabs import CourseTabList
|
from xmodule.tabs import CourseTabList
|
||||||
from xmodule.x_module import ( # lint-amnesty, pylint: disable=unused-import
|
from xmodule.x_module import (
|
||||||
AsideKeyGenerator,
|
AsideKeyGenerator,
|
||||||
OpaqueKeyReader,
|
OpaqueKeyReader,
|
||||||
XMLParsingSystem,
|
ModuleStoreRuntime,
|
||||||
policy_key
|
policy_key
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,12 +51,129 @@ etree.set_default_parser(edx_xml_parser)
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
|
class XMLParsingModuleStoreRuntime(ModuleStoreRuntime):
|
||||||
|
"""
|
||||||
|
ModuleStoreRuntime with some tweaks for XML processing.
|
||||||
|
"""
|
||||||
|
def __init__(self, process_xml, **kwargs):
|
||||||
|
"""
|
||||||
|
process_xml: Takes an xml string, and returns a XModuleDescriptor
|
||||||
|
created from that xml
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.process_xml = process_xml
|
||||||
|
|
||||||
|
def _usage_id_from_node(self, node, parent_id):
|
||||||
|
"""Create a new usage id from an XML dom node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node (lxml.etree.Element): The DOM node to interpret.
|
||||||
|
parent_id: The usage ID of the parent block
|
||||||
|
Returns:
|
||||||
|
UsageKey: the usage key for the new xblock
|
||||||
|
"""
|
||||||
|
return self.xblock_from_node(node, parent_id, self.id_generator).scope_ids.usage_id
|
||||||
|
|
||||||
|
def xblock_from_node(self, node, parent_id, id_generator=None):
|
||||||
|
"""
|
||||||
|
Create an XBlock instance from XML data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id_generator (IdGenerator): An :class:`~xblock.runtime.IdGenerator` that
|
||||||
|
will be used to construct the usage_id and definition_id for the block.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
XBlock: The fully instantiated :class:`~xblock.core.XBlock`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
id_generator = id_generator or self.id_generator
|
||||||
|
# leave next line commented out - useful for low-level debugging
|
||||||
|
# log.debug('[_usage_id_from_node] tag=%s, class=%s' % (node.tag, xblock_class))
|
||||||
|
|
||||||
|
block_type = node.tag
|
||||||
|
# remove xblock-family from elements
|
||||||
|
node.attrib.pop('xblock-family', None)
|
||||||
|
|
||||||
|
url_name = node.get('url_name') # difference from XBlock.runtime
|
||||||
|
def_id = id_generator.create_definition(block_type, url_name)
|
||||||
|
usage_id = id_generator.create_usage(def_id)
|
||||||
|
|
||||||
|
keys = ScopeIds(None, block_type, def_id, usage_id)
|
||||||
|
block_class = self.mixologist.mix(self.load_block_type(block_type))
|
||||||
|
|
||||||
|
aside_children = self.parse_asides(node, def_id, usage_id, id_generator)
|
||||||
|
asides_tags = [x.tag for x in aside_children]
|
||||||
|
|
||||||
|
block = block_class.parse_xml(node, self, keys)
|
||||||
|
self._convert_reference_fields_to_keys(block) # difference from XBlock.runtime
|
||||||
|
block.parent = parent_id
|
||||||
|
block.save()
|
||||||
|
|
||||||
|
asides = self.get_asides(block)
|
||||||
|
for asd in asides:
|
||||||
|
if asd.scope_ids.block_type in asides_tags:
|
||||||
|
block.add_aside(asd)
|
||||||
|
|
||||||
|
return block
|
||||||
|
|
||||||
|
def parse_asides(self, node, def_id, usage_id, id_generator):
|
||||||
|
"""pull the asides out of the xml payload and instantiate them"""
|
||||||
|
aside_children = []
|
||||||
|
for child in node.iterchildren():
|
||||||
|
# get xblock-family from node
|
||||||
|
xblock_family = child.attrib.pop('xblock-family', None)
|
||||||
|
if xblock_family:
|
||||||
|
xblock_family = self._family_id_to_superclass(xblock_family)
|
||||||
|
if issubclass(xblock_family, XBlockAside):
|
||||||
|
aside_children.append(child)
|
||||||
|
# now process them & remove them from the xml payload
|
||||||
|
for child in aside_children:
|
||||||
|
self._aside_from_xml(child, def_id, usage_id)
|
||||||
|
node.remove(child)
|
||||||
|
return aside_children
|
||||||
|
|
||||||
|
def _make_usage_key(self, course_key, value):
|
||||||
|
"""
|
||||||
|
Makes value into a UsageKey inside the specified course.
|
||||||
|
If value is already a UsageKey, returns that.
|
||||||
|
"""
|
||||||
|
if isinstance(value, UsageKey):
|
||||||
|
return value
|
||||||
|
usage_key = UsageKey.from_string(value)
|
||||||
|
return usage_key.map_into_course(course_key)
|
||||||
|
|
||||||
|
def _convert_reference_fields_to_keys(self, xblock):
|
||||||
|
"""
|
||||||
|
Find all fields of type reference and convert the payload into UsageKeys
|
||||||
|
"""
|
||||||
|
course_key = xblock.scope_ids.usage_id.course_key
|
||||||
|
|
||||||
|
for field in xblock.fields.values():
|
||||||
|
if field.is_set_on(xblock):
|
||||||
|
field_value = getattr(xblock, field.name)
|
||||||
|
if field_value is None:
|
||||||
|
continue
|
||||||
|
elif isinstance(field, Reference):
|
||||||
|
setattr(xblock, field.name, self._make_usage_key(course_key, field_value))
|
||||||
|
elif isinstance(field, ReferenceList):
|
||||||
|
setattr(xblock, field.name, [self._make_usage_key(course_key, ele) for ele in field_value])
|
||||||
|
elif isinstance(field, ReferenceValueDict):
|
||||||
|
for key, subvalue in field_value.items():
|
||||||
|
assert isinstance(subvalue, str)
|
||||||
|
field_value[key] = self._make_usage_key(course_key, subvalue)
|
||||||
|
setattr(xblock, field.name, field_value)
|
||||||
|
|
||||||
|
|
||||||
|
class XMLImportingModuleStoreRuntime(XMLParsingModuleStoreRuntime): # pylint: disable=abstract-method
|
||||||
|
"""
|
||||||
|
A runtime for importing OLX into ModuleStore.
|
||||||
|
"""
|
||||||
def __init__(self, xmlstore, course_id, course_dir, # lint-amnesty, pylint: disable=too-many-statements
|
def __init__(self, xmlstore, course_id, course_dir, # lint-amnesty, pylint: disable=too-many-statements
|
||||||
error_tracker,
|
error_tracker,
|
||||||
load_error_blocks=True, target_course_id=None, **kwargs):
|
load_error_blocks=True, target_course_id=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
A class that handles loading from xml. Does some munging to ensure that
|
A class that handles loading from xml to ModuleStore. Does some munging to ensure that
|
||||||
all elements have unique slugs.
|
all elements have unique slugs.
|
||||||
|
|
||||||
xmlstore: the XMLModuleStore to store the loaded blocks in
|
xmlstore: the XMLModuleStore to store the loaded blocks in
|
||||||
@@ -257,7 +379,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
|||||||
)
|
)
|
||||||
return super().construct_xblock_from_class(cls, scope_ids, field_data, *args, **kwargs)
|
return super().construct_xblock_from_class(cls, scope_ids, field_data, *args, **kwargs)
|
||||||
|
|
||||||
# id_generator is ignored, because each ImportSystem is already local to
|
# id_generator is ignored, because each XMLImportingModuleStoreRuntime is already local to
|
||||||
# a course, and has it's own id_generator already in place
|
# a course, and has it's own id_generator already in place
|
||||||
def add_node_as_child(self, block, node): # lint-amnesty, pylint: disable=signature-differs
|
def add_node_as_child(self, block, node): # lint-amnesty, pylint: disable=signature-differs
|
||||||
child_block = self.process_xml(etree.tostring(node))
|
child_block = self.process_xml(etree.tostring(node))
|
||||||
@@ -532,7 +654,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
|||||||
if self.user_service:
|
if self.user_service:
|
||||||
services['user'] = self.user_service
|
services['user'] = self.user_service
|
||||||
|
|
||||||
system = ImportSystem(
|
system = XMLImportingModuleStoreRuntime(
|
||||||
xmlstore=self,
|
xmlstore=self,
|
||||||
course_id=course_id,
|
course_id=course_id,
|
||||||
course_dir=course_dir,
|
course_dir=course_dir,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ from xmodule.modulestore.django import ASSET_IGNORE_REGEX
|
|||||||
from xmodule.modulestore.exceptions import DuplicateCourseError
|
from xmodule.modulestore.exceptions import DuplicateCourseError
|
||||||
from xmodule.modulestore.mongo.base import MongoRevisionKey
|
from xmodule.modulestore.mongo.base import MongoRevisionKey
|
||||||
from xmodule.modulestore.store_utilities import draft_node_constructor, get_draft_subtree_roots
|
from xmodule.modulestore.store_utilities import draft_node_constructor, get_draft_subtree_roots
|
||||||
from xmodule.modulestore.xml import ImportSystem, LibraryXMLModuleStore, XMLModuleStore
|
from xmodule.modulestore.xml import XMLImportingModuleStoreRuntime, LibraryXMLModuleStore, XMLModuleStore
|
||||||
from xmodule.tabs import CourseTabList
|
from xmodule.tabs import CourseTabList
|
||||||
from xmodule.util.misc import escape_invalid_characters
|
from xmodule.util.misc import escape_invalid_characters
|
||||||
from xmodule.x_module import XModuleMixin
|
from xmodule.x_module import XModuleMixin
|
||||||
@@ -993,8 +993,8 @@ def _import_course_draft(
|
|||||||
# create a new 'System' object which will manage the importing
|
# create a new 'System' object which will manage the importing
|
||||||
errorlog = make_error_tracker()
|
errorlog = make_error_tracker()
|
||||||
|
|
||||||
# The course_dir as passed to ImportSystem is expected to just be relative, not
|
# The course_dir as passed to XMLImportingModuleStoreRuntime is expected to just be relative, not
|
||||||
# the complete path including data_dir. ImportSystem will concatenate the two together.
|
# the complete path including data_dir. XMLImportingModuleStoreRuntime will concatenate the two together.
|
||||||
data_dir = xml_module_store.data_dir
|
data_dir = xml_module_store.data_dir
|
||||||
# Whether or not data_dir ends with a "/" differs in production vs. test.
|
# Whether or not data_dir ends with a "/" differs in production vs. test.
|
||||||
if not data_dir.endswith("/"):
|
if not data_dir.endswith("/"):
|
||||||
@@ -1002,7 +1002,7 @@ def _import_course_draft(
|
|||||||
# Remove absolute path, leaving relative <course_name>/drafts.
|
# Remove absolute path, leaving relative <course_name>/drafts.
|
||||||
draft_course_dir = draft_dir.replace(data_dir, '', 1)
|
draft_course_dir = draft_dir.replace(data_dir, '', 1)
|
||||||
|
|
||||||
system = ImportSystem(
|
system = XMLImportingModuleStoreRuntime(
|
||||||
xmlstore=xml_module_store,
|
xmlstore=xml_module_store,
|
||||||
course_id=source_course_id,
|
course_id=source_course_id,
|
||||||
course_dir=draft_course_dir,
|
course_dir=draft_course_dir,
|
||||||
|
|||||||
@@ -24,14 +24,13 @@ from xblock.fields import Reference, ReferenceList, ReferenceValueDict, ScopeIds
|
|||||||
from xmodule.capa.xqueue_interface import XQueueService
|
from xmodule.capa.xqueue_interface import XQueueService
|
||||||
from xmodule.assetstore import AssetMetadata
|
from xmodule.assetstore import AssetMetadata
|
||||||
from xmodule.contentstore.django import contentstore
|
from xmodule.contentstore.django import contentstore
|
||||||
from xmodule.mako_block import MakoDescriptorSystem
|
|
||||||
from xmodule.modulestore import ModuleStoreEnum
|
from xmodule.modulestore import ModuleStoreEnum
|
||||||
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished
|
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished
|
||||||
from xmodule.modulestore.inheritance import InheritanceMixin
|
from xmodule.modulestore.inheritance import InheritanceMixin
|
||||||
from xmodule.modulestore.xml import CourseLocationManager
|
from xmodule.modulestore.xml import CourseLocationManager
|
||||||
from xmodule.tests.helpers import StubReplaceURLService, mock_render_template, StubMakoService, StubUserService
|
from xmodule.tests.helpers import StubReplaceURLService, mock_render_template, StubMakoService, StubUserService
|
||||||
from xmodule.util.sandboxing import SandboxService
|
from xmodule.util.sandboxing import SandboxService
|
||||||
from xmodule.x_module import DoNothingCache, XModuleMixin
|
from xmodule.x_module import DoNothingCache, XModuleMixin, ModuleStoreRuntime
|
||||||
from openedx.core.lib.cache_utils import CacheService
|
from openedx.core.lib.cache_utils import CacheService
|
||||||
|
|
||||||
|
|
||||||
@@ -64,12 +63,12 @@ def get_asides(block):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def resources_fs():
|
def resources_fs():
|
||||||
return Mock(name='TestDescriptorSystem.resources_fs', root_path='.')
|
return Mock(name='TestModuleStoreRuntime.resources_fs', root_path='.')
|
||||||
|
|
||||||
|
|
||||||
class TestDescriptorSystem(MakoDescriptorSystem): # pylint: disable=abstract-method
|
class TestModuleStoreRuntime(ModuleStoreRuntime): # pylint: disable=abstract-method
|
||||||
"""
|
"""
|
||||||
DescriptorSystem for testing
|
ModuleStore-based XBlock Runtime for testing
|
||||||
"""
|
"""
|
||||||
def handler_url(self, block, handler, suffix='', query='', thirdparty=False): # lint-amnesty, pylint: disable=arguments-differ
|
def handler_url(self, block, handler, suffix='', query='', thirdparty=False): # lint-amnesty, pylint: disable=arguments-differ
|
||||||
return '{usage_id}/{handler}{suffix}?{query}'.format(
|
return '{usage_id}/{handler}{suffix}?{query}'.format(
|
||||||
@@ -90,7 +89,7 @@ class TestDescriptorSystem(MakoDescriptorSystem): # pylint: disable=abstract-me
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def resources_fs(self): # lint-amnesty, pylint: disable=method-hidden
|
def resources_fs(self): # lint-amnesty, pylint: disable=method-hidden
|
||||||
return Mock(name='TestDescriptorSystem.resources_fs', root_path='.')
|
return Mock(name='TestModuleStoreRuntime.resources_fs', root_path='.')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
@@ -117,7 +116,7 @@ def get_test_system(
|
|||||||
add_get_block_overrides=False
|
add_get_block_overrides=False
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Construct a test DescriptorSystem instance.
|
Construct a test ModuleStoreRuntime instance.
|
||||||
|
|
||||||
By default, the descriptor system's render_template() method simply returns the repr of the
|
By default, the descriptor system's render_template() method simply returns the repr of the
|
||||||
context it is passed. You can override this by passing in a different render_template argument.
|
context it is passed. You can override this by passing in a different render_template argument.
|
||||||
@@ -239,11 +238,11 @@ def prepare_block_runtime(
|
|||||||
|
|
||||||
def get_test_descriptor_system(render_template=None, **kwargs):
|
def get_test_descriptor_system(render_template=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Construct a test DescriptorSystem instance.
|
Construct a test ModuleStoreRuntime instance.
|
||||||
"""
|
"""
|
||||||
field_data = DictFieldData({})
|
field_data = DictFieldData({})
|
||||||
|
|
||||||
descriptor_system = TestDescriptorSystem(
|
descriptor_system = TestModuleStoreRuntime(
|
||||||
load_item=Mock(name='get_test_descriptor_system.load_item'),
|
load_item=Mock(name='get_test_descriptor_system.load_item'),
|
||||||
resources_fs=Mock(name='get_test_descriptor_system.resources_fs'),
|
resources_fs=Mock(name='get_test_descriptor_system.resources_fs'),
|
||||||
error_tracker=Mock(name='get_test_descriptor_system.error_tracker'),
|
error_tracker=Mock(name='get_test_descriptor_system.error_tracker'),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import pprint
|
|||||||
import pytest
|
import pytest
|
||||||
from path import Path as path
|
from path import Path as path
|
||||||
from xblock.reference.user_service import UserService, XBlockUser
|
from xblock.reference.user_service import UserService, XBlockUser
|
||||||
from xmodule.x_module import DescriptorSystem
|
from xmodule.x_module import ModuleStoreRuntime
|
||||||
|
|
||||||
|
|
||||||
def directories_equal(directory1, directory2):
|
def directories_equal(directory1, directory2):
|
||||||
@@ -132,7 +132,7 @@ class StubReplaceURLService:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def override_descriptor_system(monkeypatch):
|
def override_descriptor_system(monkeypatch):
|
||||||
"""
|
"""
|
||||||
Fixture to override get_block method of DescriptorSystem
|
Fixture to override get_block method of ModuleStoreRuntime
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_block(self, usage_id, for_parent=None):
|
def get_block(self, usage_id, for_parent=None):
|
||||||
@@ -140,4 +140,4 @@ def override_descriptor_system(monkeypatch):
|
|||||||
block = self.load_item(usage_id, for_parent=for_parent)
|
block = self.load_item(usage_id, for_parent=for_parent)
|
||||||
return block
|
return block
|
||||||
|
|
||||||
monkeypatch.setattr(DescriptorSystem, "get_block", get_block)
|
monkeypatch.setattr(ModuleStoreRuntime, "get_block", get_block)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from xblock.fields import ScopeIds
|
|||||||
|
|
||||||
from xmodule.conditional_block import ConditionalBlock
|
from xmodule.conditional_block import ConditionalBlock
|
||||||
from xmodule.error_block import ErrorBlock
|
from xmodule.error_block import ErrorBlock
|
||||||
from xmodule.modulestore.xml import CourseLocationManager, ImportSystem, XMLModuleStore
|
from xmodule.modulestore.xml import CourseLocationManager, XMLImportingModuleStoreRuntime, XMLModuleStore
|
||||||
from xmodule.tests import DATA_DIR, get_test_system, prepare_block_runtime
|
from xmodule.tests import DATA_DIR, get_test_system, prepare_block_runtime
|
||||||
from xmodule.tests.xml import XModuleXmlImportTest
|
from xmodule.tests.xml import XModuleXmlImportTest
|
||||||
from xmodule.tests.xml import factories as xml
|
from xmodule.tests.xml import factories as xml
|
||||||
@@ -26,7 +26,10 @@ ORG = 'test_org'
|
|||||||
COURSE = 'conditional' # name of directory with course data
|
COURSE = 'conditional' # name of directory with course data
|
||||||
|
|
||||||
|
|
||||||
class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
|
class DummyModuleStoreRuntime(XMLImportingModuleStoreRuntime): # pylint: disable=abstract-method
|
||||||
|
"""
|
||||||
|
Minimal modulestore runtime for tests
|
||||||
|
"""
|
||||||
|
|
||||||
@patch('xmodule.modulestore.xml.OSFS', lambda directory: MemoryFS())
|
@patch('xmodule.modulestore.xml.OSFS', lambda directory: MemoryFS())
|
||||||
def __init__(self, load_error_blocks):
|
def __init__(self, load_error_blocks):
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from openedx.core.lib.teams_config import TeamsConfig, DEFAULT_COURSE_RUN_MAX_TE
|
|||||||
import xmodule.course_block
|
import xmodule.course_block
|
||||||
from xmodule.course_metadata_utils import DEFAULT_START_DATE
|
from xmodule.course_metadata_utils import DEFAULT_START_DATE
|
||||||
from xmodule.data import CertificatesDisplayBehaviors
|
from xmodule.data import CertificatesDisplayBehaviors
|
||||||
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
|
from xmodule.modulestore.xml import XMLImportingModuleStoreRuntime, XMLModuleStore
|
||||||
from xmodule.modulestore.exceptions import InvalidProctoringProvider
|
from xmodule.modulestore.exceptions import InvalidProctoringProvider
|
||||||
|
|
||||||
ORG = 'test_org'
|
ORG = 'test_org'
|
||||||
@@ -52,7 +52,10 @@ class CourseFieldsTestCase(unittest.TestCase): # lint-amnesty, pylint: disable=
|
|||||||
assert xmodule.course_block.CourseFields.enrollment_start.default == expected
|
assert xmodule.course_block.CourseFields.enrollment_start.default == expected
|
||||||
|
|
||||||
|
|
||||||
class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
|
class DummyModuleStoreRuntime(XMLImportingModuleStoreRuntime): # pylint: disable=abstract-method
|
||||||
|
"""
|
||||||
|
Minimal modulestore runtime for tests.
|
||||||
|
"""
|
||||||
@patch('xmodule.modulestore.xml.OSFS', lambda dir: MemoryFS())
|
@patch('xmodule.modulestore.xml.OSFS', lambda dir: MemoryFS())
|
||||||
def __init__(self, load_error_blocks, course_id=None):
|
def __init__(self, load_error_blocks, course_id=None):
|
||||||
|
|
||||||
@@ -83,7 +86,7 @@ def get_dummy_course(
|
|||||||
):
|
):
|
||||||
"""Get a dummy course"""
|
"""Get a dummy course"""
|
||||||
|
|
||||||
system = DummySystem(load_error_blocks=True)
|
system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
def to_attrb(n, v):
|
def to_attrb(n, v):
|
||||||
return '' if v is None else f'{n}="{v}"'.lower()
|
return '' if v is None else f'{n}="{v}"'.lower()
|
||||||
@@ -126,7 +129,7 @@ class HasEndedMayCertifyTestCase(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
system = DummySystem(load_error_blocks=True) # lint-amnesty, pylint: disable=unused-variable
|
system = DummyModuleStoreRuntime(load_error_blocks=True) # lint-amnesty, pylint: disable=unused-variable
|
||||||
|
|
||||||
past_end = (datetime.now() - timedelta(days=12)).strftime("%Y-%m-%dT%H:%M:00")
|
past_end = (datetime.now() - timedelta(days=12)).strftime("%Y-%m-%dT%H:%M:00")
|
||||||
future_end = (datetime.now() + timedelta(days=12)).strftime("%Y-%m-%dT%H:%M:00")
|
future_end = (datetime.now() + timedelta(days=12)).strftime("%Y-%m-%dT%H:%M:00")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from xblock.runtime import DictKeyValueStore, KvsFieldData
|
|||||||
|
|
||||||
from xmodule.fields import Date
|
from xmodule.fields import Date
|
||||||
from xmodule.modulestore.inheritance import InheritanceMixin, compute_inherited_metadata
|
from xmodule.modulestore.inheritance import InheritanceMixin, compute_inherited_metadata
|
||||||
from xmodule.modulestore.xml import ImportSystem, LibraryXMLModuleStore, XMLModuleStore
|
from xmodule.modulestore.xml import XMLImportingModuleStoreRuntime, LibraryXMLModuleStore, XMLModuleStore
|
||||||
from xmodule.tests import DATA_DIR
|
from xmodule.tests import DATA_DIR
|
||||||
from xmodule.x_module import XModuleMixin
|
from xmodule.x_module import XModuleMixin
|
||||||
from xmodule.xml_block import is_pointer_tag
|
from xmodule.xml_block import is_pointer_tag
|
||||||
@@ -28,7 +28,10 @@ COURSE = 'test_course'
|
|||||||
RUN = 'test_run'
|
RUN = 'test_run'
|
||||||
|
|
||||||
|
|
||||||
class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
|
class DummyModuleStoreRuntime(XMLImportingModuleStoreRuntime): # pylint: disable=abstract-method, missing-class-docstring
|
||||||
|
"""
|
||||||
|
Minimal modulestore runtime for tests
|
||||||
|
"""
|
||||||
|
|
||||||
@patch('xmodule.modulestore.xml.OSFS', lambda dir: OSFS(mkdtemp()))
|
@patch('xmodule.modulestore.xml.OSFS', lambda dir: OSFS(mkdtemp()))
|
||||||
def __init__(self, load_error_blocks, library=False):
|
def __init__(self, load_error_blocks, library=False):
|
||||||
@@ -58,7 +61,7 @@ class BaseCourseTestCase(TestCase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_system(load_error_blocks=True, library=False):
|
def get_system(load_error_blocks=True, library=False):
|
||||||
'''Get a dummy system'''
|
'''Get a dummy system'''
|
||||||
return DummySystem(load_error_blocks, library=library)
|
return DummyModuleStoreRuntime(load_error_blocks, library=library)
|
||||||
|
|
||||||
def get_course(self, name):
|
def get_course(self, name):
|
||||||
"""Get a test course by directory name. If there's more than one, error."""
|
"""Get a test course by directory name. If there's more than one, error."""
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from xmodule.capa_block import ProblemBlock
|
|||||||
from common.djangoapps.student.tests.factories import UserFactory
|
from common.djangoapps.student.tests.factories import UserFactory
|
||||||
|
|
||||||
from ..item_bank_block import ItemBankBlock
|
from ..item_bank_block import ItemBankBlock
|
||||||
from .test_course_block import DummySystem as TestImportSystem
|
from .test_course_block import DummyModuleStoreRuntime
|
||||||
|
|
||||||
dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-name
|
dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-name
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ class TestItemBankForCms(ItemBankTestBase):
|
|||||||
olx_element = etree.fromstring(actual_olx_export)
|
olx_element = etree.fromstring(actual_olx_export)
|
||||||
|
|
||||||
# Re-import the OLX.
|
# Re-import the OLX.
|
||||||
runtime = TestImportSystem(load_error_blocks=True, course_id=self.item_bank.context_key)
|
runtime = DummyModuleStoreRuntime(load_error_blocks=True, course_id=self.item_bank.context_key)
|
||||||
runtime.resources_fs = export_fs
|
runtime.resources_fs = export_fs
|
||||||
imported_item_bank = ItemBankBlock.parse_xml(olx_element, runtime, None)
|
imported_item_bank = ItemBankBlock.parse_xml(olx_element, runtime, None)
|
||||||
|
|
||||||
@@ -168,11 +168,11 @@ class TestItemBankForCms(ItemBankTestBase):
|
|||||||
assert self.item_bank.validate()
|
assert self.item_bank.validate()
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.render',
|
'xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.render',
|
||||||
VanillaRuntime.render,
|
VanillaRuntime.render,
|
||||||
)
|
)
|
||||||
@patch('xmodule.capa_block.ProblemBlock.author_view', dummy_render, create=True)
|
@patch('xmodule.capa_block.ProblemBlock.author_view', dummy_render, create=True)
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types', lambda self, block: [])
|
||||||
def test_preview_view(self):
|
def test_preview_view(self):
|
||||||
""" Test preview view rendering """
|
""" Test preview view rendering """
|
||||||
self._bind_course_block(self.item_bank)
|
self._bind_course_block(self.item_bank)
|
||||||
@@ -183,11 +183,11 @@ class TestItemBankForCms(ItemBankTestBase):
|
|||||||
assert '<p>Hello world from problem 3</p>' in rendered.content
|
assert '<p>Hello world from problem 3</p>' in rendered.content
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.render',
|
'xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.render',
|
||||||
VanillaRuntime.render,
|
VanillaRuntime.render,
|
||||||
)
|
)
|
||||||
@patch('xmodule.capa_block.ProblemBlock.author_view', dummy_render, create=True)
|
@patch('xmodule.capa_block.ProblemBlock.author_view', dummy_render, create=True)
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types', lambda self, block: [])
|
||||||
def test_author_view(self):
|
def test_author_view(self):
|
||||||
""" Test author view rendering """
|
""" Test author view rendering """
|
||||||
self._bind_course_block(self.item_bank)
|
self._bind_course_block(self.item_bank)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from xmodule.tests import prepare_block_runtime
|
|||||||
from xmodule.validation import StudioValidationMessage
|
from xmodule.validation import StudioValidationMessage
|
||||||
from xmodule.x_module import AUTHOR_VIEW
|
from xmodule.x_module import AUTHOR_VIEW
|
||||||
|
|
||||||
from .test_course_block import DummySystem as TestImportSystem
|
from .test_course_block import DummyModuleStoreRuntime
|
||||||
|
|
||||||
dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-name
|
dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-name
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ class TestLibraryContentExportImport(LegacyLibraryContentTest):
|
|||||||
self.lc_block.runtime.export_fs = self.export_fs # pylint: disable=protected-access
|
self.lc_block.runtime.export_fs = self.export_fs # pylint: disable=protected-access
|
||||||
|
|
||||||
# Prepare runtime for the import.
|
# Prepare runtime for the import.
|
||||||
self.runtime = TestImportSystem(load_error_blocks=True, course_id=self.lc_block.location.course_key)
|
self.runtime = DummyModuleStoreRuntime(load_error_blocks=True, course_id=self.lc_block.location.course_key)
|
||||||
self.runtime.resources_fs = self.export_fs
|
self.runtime.resources_fs = self.export_fs
|
||||||
self.id_generator = Mock()
|
self.id_generator = Mock()
|
||||||
|
|
||||||
@@ -521,10 +521,10 @@ class TestLegacyLibraryContentBlockWithSearchIndex(LegacyLibraryContentBlockTest
|
|||||||
|
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.render', VanillaRuntime.render
|
'xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.render', VanillaRuntime.render
|
||||||
)
|
)
|
||||||
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types', lambda self, block: [])
|
||||||
class TestLibraryContentRender(LegacyLibraryContentTest):
|
class TestLibraryContentRender(LegacyLibraryContentTest):
|
||||||
"""
|
"""
|
||||||
Rendering unit tests for LegacyLibraryContentBlock
|
Rendering unit tests for LegacyLibraryContentBlock
|
||||||
@@ -732,10 +732,10 @@ class TestLibraryContentAnalytics(LegacyLibraryContentTest):
|
|||||||
|
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.render', VanillaRuntime.render
|
'xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.render', VanillaRuntime.render
|
||||||
)
|
)
|
||||||
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types', lambda self, block: [])
|
||||||
class TestMigratedLibraryContentRender(LegacyLibraryContentTest):
|
class TestMigratedLibraryContentRender(LegacyLibraryContentTest):
|
||||||
"""
|
"""
|
||||||
Rendering unit tests for LegacyLibraryContentBlock
|
Rendering unit tests for LegacyLibraryContentBlock
|
||||||
@@ -825,7 +825,7 @@ class TestMigratedLibraryContentRender(LegacyLibraryContentTest):
|
|||||||
assert exported_olx == expected_olx_export
|
assert exported_olx == expected_olx_export
|
||||||
|
|
||||||
# Now import it.
|
# Now import it.
|
||||||
runtime = TestImportSystem(load_error_blocks=True, course_id=self.lc_block.location.course_key)
|
runtime = DummyModuleStoreRuntime(load_error_blocks=True, course_id=self.lc_block.location.course_key)
|
||||||
runtime.resources_fs = export_fs
|
runtime.resources_fs = export_fs
|
||||||
olx_element = etree.fromstring(exported_olx)
|
olx_element = etree.fromstring(exported_olx)
|
||||||
imported_lc_block = LegacyLibraryContentBlock.parse_xml(olx_element, runtime, None)
|
imported_lc_block = LegacyLibraryContentBlock.parse_xml(olx_element, runtime, None)
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-
|
|||||||
|
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
'xmodule.modulestore.split_mongo.caching_descriptor_system.CachingDescriptorSystem.render', VanillaRuntime.render
|
'xmodule.modulestore.split_mongo.runtime.SplitModuleStoreRuntime.render', VanillaRuntime.render
|
||||||
)
|
)
|
||||||
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
|
||||||
@patch('xmodule.html_block.HtmlBlock.has_author_view', True, create=True)
|
@patch('xmodule.html_block.HtmlBlock.has_author_view', True, create=True)
|
||||||
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
|
@patch('xmodule.x_module.ModuleStoreRuntime.applicable_aside_types', lambda self, block: [])
|
||||||
class TestLibraryRoot(MixedSplitTestCase):
|
class TestLibraryRoot(MixedSplitTestCase):
|
||||||
"""
|
"""
|
||||||
Basic unit tests for LibraryRoot (library_root_xblock.py)
|
Basic unit tests for LibraryRoot (library_root_xblock.py)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from django.test import TestCase
|
|||||||
from openedx.core.lib.safe_lxml import etree
|
from openedx.core.lib.safe_lxml import etree
|
||||||
from xmodule import poll_block
|
from xmodule import poll_block
|
||||||
from . import get_test_system
|
from . import get_test_system
|
||||||
from .test_import import DummySystem
|
from .test_import import DummyModuleStoreRuntime
|
||||||
|
|
||||||
|
|
||||||
class _PollBlockTestBase(TestCase):
|
class _PollBlockTestBase(TestCase):
|
||||||
@@ -67,7 +67,7 @@ class _PollBlockTestBase(TestCase):
|
|||||||
Make sure that poll_block will export fine if its xml contains
|
Make sure that poll_block will export fine if its xml contains
|
||||||
unescaped characters.
|
unescaped characters.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
module_system.id_generator.target_course_id = self.xblock.course_id
|
module_system.id_generator.target_course_id = self.xblock.course_id
|
||||||
sample_poll_xml = '''
|
sample_poll_xml = '''
|
||||||
<poll_question display_name="Poll Question">
|
<poll_question display_name="Poll Question">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from xmodule.modulestore.tests.utils import MixedSplitTestCase
|
|||||||
from xmodule.randomize_block import RandomizeBlock
|
from xmodule.randomize_block import RandomizeBlock
|
||||||
from xmodule.tests import prepare_block_runtime
|
from xmodule.tests import prepare_block_runtime
|
||||||
|
|
||||||
from .test_course_block import DummySystem as TestImportSystem
|
from .test_course_block import DummyModuleStoreRuntime
|
||||||
|
|
||||||
|
|
||||||
class RandomizeBlockTest(MixedSplitTestCase):
|
class RandomizeBlockTest(MixedSplitTestCase):
|
||||||
@@ -78,7 +78,7 @@ class RandomizeBlockTest(MixedSplitTestCase):
|
|||||||
# And compare.
|
# And compare.
|
||||||
assert exported_olx == expected_olx
|
assert exported_olx == expected_olx
|
||||||
|
|
||||||
runtime = TestImportSystem(load_error_blocks=True, course_id=randomize_block.location.course_key)
|
runtime = DummyModuleStoreRuntime(load_error_blocks=True, course_id=randomize_block.location.course_key)
|
||||||
runtime.resources_fs = export_fs
|
runtime.resources_fs = export_fs
|
||||||
|
|
||||||
# Now import it.
|
# Now import it.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from xmodule.split_test_block import (
|
|||||||
user_partition_values,
|
user_partition_values,
|
||||||
)
|
)
|
||||||
from xmodule.tests import prepare_block_runtime
|
from xmodule.tests import prepare_block_runtime
|
||||||
from xmodule.tests.test_course_block import DummySystem as TestImportSystem
|
from xmodule.tests.test_course_block import DummyModuleStoreRuntime
|
||||||
from xmodule.tests.xml import XModuleXmlImportTest
|
from xmodule.tests.xml import XModuleXmlImportTest
|
||||||
from xmodule.tests.xml import factories as xml
|
from xmodule.tests.xml import factories as xml
|
||||||
from xmodule.validation import StudioValidationMessage
|
from xmodule.validation import StudioValidationMessage
|
||||||
@@ -581,7 +581,7 @@ class SplitTestBlockExportImportTest(MixedSplitTestCase):
|
|||||||
# And compare.
|
# And compare.
|
||||||
assert exported_olx == expected_olx
|
assert exported_olx == expected_olx
|
||||||
|
|
||||||
runtime = TestImportSystem(load_error_blocks=True, course_id=split_test_block.location.course_key)
|
runtime = DummyModuleStoreRuntime(load_error_blocks=True, course_id=split_test_block.location.course_key)
|
||||||
runtime.resources_fs = export_fs
|
runtime.resources_fs = export_fs
|
||||||
|
|
||||||
# Now import it.
|
# Now import it.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from xmodule.video_block.transcripts_utils import save_to_store
|
|||||||
from xblock.core import XBlockAside
|
from xblock.core import XBlockAside
|
||||||
from xmodule.modulestore.tests.test_asides import AsideTestType
|
from xmodule.modulestore.tests.test_asides import AsideTestType
|
||||||
|
|
||||||
from .test_import import DummySystem
|
from .test_import import DummyModuleStoreRuntime
|
||||||
|
|
||||||
SRT_FILEDATA = '''
|
SRT_FILEDATA = '''
|
||||||
0
|
0
|
||||||
@@ -283,7 +283,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def test_parse_xml(self):
|
def test_parse_xml(self):
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||||
@@ -324,7 +324,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
@ddt.data(True, False)
|
@ddt.data(True, False)
|
||||||
def test_parse_xml_with_asides(self, video_xml_has_aside, mock_is_pointer_tag, mock_load_file):
|
def test_parse_xml_with_asides(self, video_xml_has_aside, mock_is_pointer_tag, mock_load_file):
|
||||||
"""Test that `parse_xml` parses asides from the video xml"""
|
"""Test that `parse_xml` parses asides from the video xml"""
|
||||||
runtime = DummySystem(load_error_blocks=True)
|
runtime = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
if video_xml_has_aside:
|
if video_xml_has_aside:
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video url_name="a16643fa63234fef8f6ebbc1902e2253">
|
<video url_name="a16643fa63234fef8f6ebbc1902e2253">
|
||||||
@@ -358,7 +358,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
Test that if handout link is course_asset then it will contain targeted course_id in handout link.
|
Test that if handout link is course_asset then it will contain targeted course_id in handout link.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
course_id = CourseKey.from_string(course_id_string)
|
course_id = CourseKey.from_string(course_id_string)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
@@ -401,7 +401,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
Ensure that attributes have the right values if they aren't
|
Ensure that attributes have the right values if they aren't
|
||||||
explicitly set in XML.
|
explicitly set in XML.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
|
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
|
||||||
@@ -432,7 +432,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
Ensure that attributes have the right values if they aren't
|
Ensure that attributes have the right values if they aren't
|
||||||
explicitly set in XML.
|
explicitly set in XML.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
|
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
|
||||||
@@ -463,7 +463,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
Make sure settings are correct if none are explicitly set in XML.
|
Make sure settings are correct if none are explicitly set in XML.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '<video></video>'
|
xml_data = '<video></video>'
|
||||||
xml_object = etree.fromstring(xml_data)
|
xml_object = etree.fromstring(xml_data)
|
||||||
output = VideoBlock.parse_xml(xml_object, module_system, None)
|
output = VideoBlock.parse_xml(xml_object, module_system, None)
|
||||||
@@ -489,7 +489,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
Make sure we can handle the double-quoted string format (which was used for exporting for
|
Make sure we can handle the double-quoted string format (which was used for exporting for
|
||||||
a few weeks).
|
a few weeks).
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name=""display_name""
|
<video display_name=""display_name""
|
||||||
html5_sources="["source_1", "source_2"]"
|
html5_sources="["source_1", "source_2"]"
|
||||||
@@ -524,7 +524,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def test_parse_xml_double_quote_concatenated_youtube(self):
|
def test_parse_xml_double_quote_concatenated_youtube(self):
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = '''
|
xml_data = '''
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:"p2Q6BrNhdh8",1.25:"1EeWXzPdhSA"">
|
youtube="1.0:"p2Q6BrNhdh8",1.25:"1EeWXzPdhSA"">
|
||||||
@@ -552,7 +552,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
Test backwards compatibility with VideoBlock's XML format.
|
Test backwards compatibility with VideoBlock's XML format.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = """
|
xml_data = """
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||||
@@ -584,7 +584,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
Ensure that Video is able to read VideoBlock's model data.
|
Ensure that Video is able to read VideoBlock's model data.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = """
|
xml_data = """
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||||
@@ -615,7 +615,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
Ensure that Video is able to read VideoBlock's model data.
|
Ensure that Video is able to read VideoBlock's model data.
|
||||||
"""
|
"""
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
xml_data = """
|
xml_data = """
|
||||||
<video display_name="Test Video"
|
<video display_name="Test Video"
|
||||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||||
@@ -660,7 +660,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
|
|
||||||
edx_video_id = 'test_edx_video_id'
|
edx_video_id = 'test_edx_video_id'
|
||||||
mock_val_api.import_from_xml = Mock(wraps=mock_val_import)
|
mock_val_api.import_from_xml = Mock(wraps=mock_val_import)
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Create static directory in import file system and place transcript files inside it.
|
# Create static directory in import file system and place transcript files inside it.
|
||||||
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
|
||||||
@@ -691,7 +691,7 @@ class VideoBlockImportTestCase(TestCase):
|
|||||||
def test_import_val_data_invalid(self, mock_val_api):
|
def test_import_val_data_invalid(self, mock_val_api):
|
||||||
mock_val_api.ValCannotCreateError = _MockValCannotCreateError
|
mock_val_api.ValCannotCreateError = _MockValCannotCreateError
|
||||||
mock_val_api.import_from_xml = Mock(side_effect=mock_val_api.ValCannotCreateError)
|
mock_val_api.import_from_xml = Mock(side_effect=mock_val_api.ValCannotCreateError)
|
||||||
module_system = DummySystem(load_error_blocks=True)
|
module_system = DummyModuleStoreRuntime(load_error_blocks=True)
|
||||||
|
|
||||||
# Negative duration is invalid
|
# Negative duration is invalid
|
||||||
xml_data = """
|
xml_data = """
|
||||||
|
|||||||
@@ -11,14 +11,13 @@ from lxml import etree
|
|||||||
from opaque_keys.edx.keys import CourseKey
|
from opaque_keys.edx.keys import CourseKey
|
||||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||||
|
|
||||||
from xmodule.mako_block import MakoDescriptorSystem
|
from xmodule.modulestore.xml import XMLParsingModuleStoreRuntime, CourseLocationManager
|
||||||
from xmodule.modulestore.xml import CourseLocationManager
|
from xmodule.x_module import policy_key
|
||||||
from xmodule.x_module import XMLParsingSystem, policy_key
|
|
||||||
|
|
||||||
|
|
||||||
class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable=abstract-method
|
class InMemoryModuleStoreRuntime(XMLParsingModuleStoreRuntime): # pylint: disable=abstract-method
|
||||||
"""
|
"""
|
||||||
The simplest possible XMLParsingSystem
|
The simplest possible ModuleStoreRuntime
|
||||||
"""
|
"""
|
||||||
def __init__(self, xml_import_data):
|
def __init__(self, xml_import_data):
|
||||||
self.course_id = CourseKey.from_string(xml_import_data.course_id)
|
self.course_id = CourseKey.from_string(xml_import_data.course_id)
|
||||||
@@ -63,5 +62,5 @@ class XModuleXmlImportTest(TestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def process_xml(cls, xml_import_data):
|
def process_xml(cls, xml_import_data):
|
||||||
"""Use the `xml_import_data` to import an :class:`XBlock` from XML."""
|
"""Use the `xml_import_data` to import an :class:`XBlock` from XML."""
|
||||||
system = InMemorySystem(xml_import_data)
|
system = InMemoryModuleStoreRuntime(xml_import_data)
|
||||||
return system.process_xml(xml_import_data.xml_string)
|
return system.process_xml(xml_import_data.xml_string)
|
||||||
|
|||||||
@@ -17,17 +17,13 @@ from opaque_keys.edx.keys import UsageKey
|
|||||||
from web_fragments.fragment import Fragment
|
from web_fragments.fragment import Fragment
|
||||||
from webob import Response
|
from webob import Response
|
||||||
from webob.multidict import MultiDict
|
from webob.multidict import MultiDict
|
||||||
from xblock.core import XBlock, XBlockAside
|
from xblock.core import XBlock
|
||||||
from xblock.fields import (
|
from xblock.fields import (
|
||||||
Dict,
|
Dict,
|
||||||
Float,
|
Float,
|
||||||
Integer,
|
Integer,
|
||||||
List,
|
List,
|
||||||
Reference,
|
|
||||||
ReferenceList,
|
|
||||||
ReferenceValueDict,
|
|
||||||
Scope,
|
Scope,
|
||||||
ScopeIds,
|
|
||||||
String,
|
String,
|
||||||
UserScope
|
UserScope
|
||||||
)
|
)
|
||||||
@@ -267,17 +263,17 @@ class XModuleMixin(XModuleFields, XBlock):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._asides = []
|
self._asides = []
|
||||||
# Initialization data used by CachingDescriptorSystem to defer FieldData initialization
|
# Initialization data used by SplitModuleStoreRuntime to defer FieldData initialization
|
||||||
self._cds_init_args = kwargs.pop("cds_init_args", None)
|
self._cds_init_args = kwargs.pop("cds_init_args", None)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_cds_init_args(self):
|
def get_cds_init_args(self):
|
||||||
""" Get initialization data used by CachingDescriptorSystem to defer FieldData initialization """
|
""" Get initialization data used by SplitModuleStoreRuntime to defer FieldData initialization """
|
||||||
if self._cds_init_args is None:
|
if self._cds_init_args is None:
|
||||||
raise KeyError("cds_init_args was not provided for this XBlock")
|
raise KeyError("cds_init_args was not provided for this XBlock")
|
||||||
if self._cds_init_args is False:
|
if self._cds_init_args is False:
|
||||||
raise RuntimeError("Tried to get CachingDescriptorSystem cds_init_args twice for the same XBlock.")
|
raise RuntimeError("Tried to get SplitModuleStoreRuntime cds_init_args twice for the same XBlock.")
|
||||||
args = self._cds_init_args
|
args = self._cds_init_args
|
||||||
# Free the memory and set this False to flag any double-access bugs. This only needs to be read once.
|
# Free the memory and set this False to flag any double-access bugs. This only needs to be read once.
|
||||||
self._cds_init_args = False
|
self._cds_init_args = False
|
||||||
@@ -617,8 +613,8 @@ class XModuleMixin(XModuleFields, XBlock):
|
|||||||
wrapped_field_data = wrapper(wrapped_field_data)
|
wrapped_field_data = wrapper(wrapped_field_data)
|
||||||
self._bound_field_data = wrapped_field_data
|
self._bound_field_data = wrapped_field_data
|
||||||
if getattr(self.runtime, "uses_deprecated_field_data", False):
|
if getattr(self.runtime, "uses_deprecated_field_data", False):
|
||||||
# This approach is deprecated but old mongo's CachingDescriptorSystem still requires it.
|
# This approach is deprecated but OldModuleStoreRuntime still requires it.
|
||||||
# For Split mongo's CachingDescriptor system, don't set ._field_data this way.
|
# For SplitModuleStoreRuntime, don't set ._field_data this way.
|
||||||
self._field_data = wrapped_field_data
|
self._field_data = wrapped_field_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -919,7 +915,7 @@ class ResourceTemplates:
|
|||||||
return cls._load_template(abs_path, template_id)
|
return cls._load_template(abs_path, template_id)
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableFragmentWrapper:
|
class _ConfigurableFragmentWrapper:
|
||||||
"""
|
"""
|
||||||
Runtime mixin that allows for composition of many `wrap_xblock` wrappers
|
Runtime mixin that allows for composition of many `wrap_xblock` wrappers
|
||||||
"""
|
"""
|
||||||
@@ -985,9 +981,9 @@ def block_global_local_resource_url(block, uri):
|
|||||||
raise NotImplementedError("Applications must monkey-patch this function before using local_resource_url for studio_view") # lint-amnesty, pylint: disable=line-too-long
|
raise NotImplementedError("Applications must monkey-patch this function before using local_resource_url for studio_view") # lint-amnesty, pylint: disable=line-too-long
|
||||||
|
|
||||||
|
|
||||||
class MetricsMixin:
|
class _MetricsMixin:
|
||||||
"""
|
"""
|
||||||
Mixin for adding metric logging for render and handle methods in the DescriptorSystem.
|
Mixin for adding metric logging for render and handle methods in the ModuleStoreRuntime.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def render(self, block, view_name, context=None): # lint-amnesty, pylint: disable=missing-function-docstring
|
def render(self, block, view_name, context=None): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||||
@@ -1022,7 +1018,7 @@ class MetricsMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ModuleSystemShim:
|
class _ModuleSystemShim:
|
||||||
"""
|
"""
|
||||||
This shim provides the properties formerly available from ModuleSystem which are now being provided by services.
|
This shim provides the properties formerly available from ModuleSystem which are now being provided by services.
|
||||||
|
|
||||||
@@ -1346,12 +1342,19 @@ class ModuleSystemShim:
|
|||||||
self._deprecated_course_id = course_id
|
self._deprecated_course_id = course_id
|
||||||
|
|
||||||
|
|
||||||
class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemShim, Runtime):
|
class ModuleStoreRuntime(_MetricsMixin, _ConfigurableFragmentWrapper, _ModuleSystemShim, Runtime):
|
||||||
"""
|
"""
|
||||||
Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s
|
Base class for :class:`Runtime`s to be used with :class:`XBlock`s loaded from ModuleStore.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, load_item, resources_fs, error_tracker, get_policy=None, disabled_xblock_types=lambda: [], **kwargs
|
self,
|
||||||
|
load_item,
|
||||||
|
resources_fs,
|
||||||
|
error_tracker,
|
||||||
|
get_policy=None,
|
||||||
|
render_template=None,
|
||||||
|
disabled_xblock_types=lambda: [],
|
||||||
|
**kwargs
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
load_item: Takes a Location and returns an XModuleDescriptor
|
load_item: Takes a Location and returns an XModuleDescriptor
|
||||||
@@ -1385,6 +1388,19 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
|
|||||||
self.get_policy = get_policy
|
self.get_policy = get_policy
|
||||||
else:
|
else:
|
||||||
self.get_policy = lambda u: {}
|
self.get_policy = lambda u: {}
|
||||||
|
if render_template:
|
||||||
|
self.render_template = render_template
|
||||||
|
# Add the MakoService to the runtime services. If it already exists, do not attempt to reinitialize it;
|
||||||
|
# otherwise, this could override the `namespace_prefix` of the `MakoService`, breaking template rendering in
|
||||||
|
# Studio.
|
||||||
|
#
|
||||||
|
# This is not needed by most XBlocks, because the MakoService is added to their runtimes. However, there are
|
||||||
|
# a few cases where the MakoService is not added to the XBlock's runtime. Specifically: * in the Instructor
|
||||||
|
# Dashboard bulk emails tab, when rendering the HtmlBlock for its WYSIWYG editor. * during testing, when
|
||||||
|
# fetching factory-created blocks.
|
||||||
|
if 'mako' not in self._services:
|
||||||
|
from common.djangoapps.edxmako.services import MakoService
|
||||||
|
self._services['mako'] = MakoService()
|
||||||
|
|
||||||
self.disabled_xblock_types = disabled_xblock_types
|
self.disabled_xblock_types = disabled_xblock_types
|
||||||
|
|
||||||
@@ -1439,7 +1455,7 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
|
def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
|
||||||
# When the Modulestore instantiates DescriptorSystems, we will reference a
|
# When the Modulestore instantiates ModuleStoreRuntime, we will reference a
|
||||||
# global function that the application can override, unless a specific function is
|
# global function that the application can override, unless a specific function is
|
||||||
# defined for LMS/CMS through the handler_url_override property.
|
# defined for LMS/CMS through the handler_url_override property.
|
||||||
if getattr(self, 'handler_url_override', None):
|
if getattr(self, 'handler_url_override', None):
|
||||||
@@ -1450,8 +1466,8 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
|
|||||||
"""
|
"""
|
||||||
See :meth:`xblock.runtime.Runtime:local_resource_url` for documentation.
|
See :meth:`xblock.runtime.Runtime:local_resource_url` for documentation.
|
||||||
"""
|
"""
|
||||||
# Currently, Modulestore is responsible for instantiating DescriptorSystems
|
# Currently, Modulestore is responsible for instantiating ModuleStoreRuntime
|
||||||
# This means that LMS/CMS don't have a way to define a subclass of DescriptorSystem
|
# This means that LMS/CMS don't have a way to define a subclass of ModuleStoreRuntime
|
||||||
# that implements the correct local_resource_url. So, for now, instead, we will reference a
|
# that implements the correct local_resource_url. So, for now, instead, we will reference a
|
||||||
# global function that the application can override.
|
# global function that the application can override.
|
||||||
return block_global_local_resource_url(block, uri)
|
return block_global_local_resource_url(block, uri)
|
||||||
@@ -1522,120 +1538,6 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
|
|||||||
return super().layout_asides(block, context, frag, view_name, aside_frag_fns)
|
return super().layout_asides(block, context, frag, view_name, aside_frag_fns)
|
||||||
|
|
||||||
|
|
||||||
class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
|
|
||||||
def __init__(self, process_xml, **kwargs):
|
|
||||||
"""
|
|
||||||
process_xml: Takes an xml string, and returns a XModuleDescriptor
|
|
||||||
created from that xml
|
|
||||||
"""
|
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.process_xml = process_xml
|
|
||||||
|
|
||||||
def _usage_id_from_node(self, node, parent_id):
|
|
||||||
"""Create a new usage id from an XML dom node.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
node (lxml.etree.Element): The DOM node to interpret.
|
|
||||||
parent_id: The usage ID of the parent block
|
|
||||||
Returns:
|
|
||||||
UsageKey: the usage key for the new xblock
|
|
||||||
"""
|
|
||||||
return self.xblock_from_node(node, parent_id, self.id_generator).scope_ids.usage_id
|
|
||||||
|
|
||||||
def xblock_from_node(self, node, parent_id, id_generator=None):
|
|
||||||
"""
|
|
||||||
Create an XBlock instance from XML data.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
xml_data (string): A string containing valid xml.
|
|
||||||
system (XMLParsingSystem): The :class:`.XMLParsingSystem` used to connect the block
|
|
||||||
to the outside world.
|
|
||||||
id_generator (IdGenerator): An :class:`~xblock.runtime.IdGenerator` that
|
|
||||||
will be used to construct the usage_id and definition_id for the block.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
XBlock: The fully instantiated :class:`~xblock.core.XBlock`.
|
|
||||||
|
|
||||||
"""
|
|
||||||
id_generator = id_generator or self.id_generator
|
|
||||||
# leave next line commented out - useful for low-level debugging
|
|
||||||
# log.debug('[_usage_id_from_node] tag=%s, class=%s' % (node.tag, xblock_class))
|
|
||||||
|
|
||||||
block_type = node.tag
|
|
||||||
# remove xblock-family from elements
|
|
||||||
node.attrib.pop('xblock-family', None)
|
|
||||||
|
|
||||||
url_name = node.get('url_name') # difference from XBlock.runtime
|
|
||||||
def_id = id_generator.create_definition(block_type, url_name)
|
|
||||||
usage_id = id_generator.create_usage(def_id)
|
|
||||||
|
|
||||||
keys = ScopeIds(None, block_type, def_id, usage_id)
|
|
||||||
block_class = self.mixologist.mix(self.load_block_type(block_type))
|
|
||||||
|
|
||||||
aside_children = self.parse_asides(node, def_id, usage_id, id_generator)
|
|
||||||
asides_tags = [x.tag for x in aside_children]
|
|
||||||
|
|
||||||
block = block_class.parse_xml(node, self, keys)
|
|
||||||
self._convert_reference_fields_to_keys(block) # difference from XBlock.runtime
|
|
||||||
block.parent = parent_id
|
|
||||||
block.save()
|
|
||||||
|
|
||||||
asides = self.get_asides(block)
|
|
||||||
for asd in asides:
|
|
||||||
if asd.scope_ids.block_type in asides_tags:
|
|
||||||
block.add_aside(asd)
|
|
||||||
|
|
||||||
return block
|
|
||||||
|
|
||||||
def parse_asides(self, node, def_id, usage_id, id_generator):
|
|
||||||
"""pull the asides out of the xml payload and instantiate them"""
|
|
||||||
aside_children = []
|
|
||||||
for child in node.iterchildren():
|
|
||||||
# get xblock-family from node
|
|
||||||
xblock_family = child.attrib.pop('xblock-family', None)
|
|
||||||
if xblock_family:
|
|
||||||
xblock_family = self._family_id_to_superclass(xblock_family)
|
|
||||||
if issubclass(xblock_family, XBlockAside):
|
|
||||||
aside_children.append(child)
|
|
||||||
# now process them & remove them from the xml payload
|
|
||||||
for child in aside_children:
|
|
||||||
self._aside_from_xml(child, def_id, usage_id)
|
|
||||||
node.remove(child)
|
|
||||||
return aside_children
|
|
||||||
|
|
||||||
def _make_usage_key(self, course_key, value):
|
|
||||||
"""
|
|
||||||
Makes value into a UsageKey inside the specified course.
|
|
||||||
If value is already a UsageKey, returns that.
|
|
||||||
"""
|
|
||||||
if isinstance(value, UsageKey):
|
|
||||||
return value
|
|
||||||
usage_key = UsageKey.from_string(value)
|
|
||||||
return usage_key.map_into_course(course_key)
|
|
||||||
|
|
||||||
def _convert_reference_fields_to_keys(self, xblock):
|
|
||||||
"""
|
|
||||||
Find all fields of type reference and convert the payload into UsageKeys
|
|
||||||
"""
|
|
||||||
course_key = xblock.scope_ids.usage_id.course_key
|
|
||||||
|
|
||||||
for field in xblock.fields.values():
|
|
||||||
if field.is_set_on(xblock):
|
|
||||||
field_value = getattr(xblock, field.name)
|
|
||||||
if field_value is None:
|
|
||||||
continue
|
|
||||||
elif isinstance(field, Reference):
|
|
||||||
setattr(xblock, field.name, self._make_usage_key(course_key, field_value))
|
|
||||||
elif isinstance(field, ReferenceList):
|
|
||||||
setattr(xblock, field.name, [self._make_usage_key(course_key, ele) for ele in field_value])
|
|
||||||
elif isinstance(field, ReferenceValueDict):
|
|
||||||
for key, subvalue in field_value.items():
|
|
||||||
assert isinstance(subvalue, str)
|
|
||||||
field_value[key] = self._make_usage_key(course_key, subvalue)
|
|
||||||
setattr(xblock, field.name, field_value)
|
|
||||||
|
|
||||||
|
|
||||||
class DoNothingCache:
|
class DoNothingCache:
|
||||||
"""A duck-compatible object to use in ModuleSystemShim when there's no cache."""
|
"""A duck-compatible object to use in ModuleSystemShim when there's no cache."""
|
||||||
def get(self, _key):
|
def get(self, _key):
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ class XmlMixin:
|
|||||||
Returns (XBlock): The newly parsed XBlock
|
Returns (XBlock): The newly parsed XBlock
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from xmodule.modulestore.xml import ImportSystem # done here to avoid circular import
|
from xmodule.modulestore.xml import XMLImportingModuleStoreRuntime # done here to avoid circular import
|
||||||
|
|
||||||
if keys is None:
|
if keys is None:
|
||||||
# Passing keys=None is against the XBlock API but some platform tests do it.
|
# Passing keys=None is against the XBlock API but some platform tests do it.
|
||||||
@@ -361,9 +361,10 @@ class XmlMixin:
|
|||||||
if "filename" in field_data:
|
if "filename" in field_data:
|
||||||
del field_data["filename"] # filename should only be in xml_attributes.
|
del field_data["filename"] # filename should only be in xml_attributes.
|
||||||
|
|
||||||
if isinstance(runtime, ImportSystem):
|
if isinstance(runtime, XMLImportingModuleStoreRuntime):
|
||||||
# we shouldn't be instantiating our own field data instance here, but there are complex inter-depenencies
|
# we shouldn't be instantiating our own field data instance here, but there are complex
|
||||||
# between this mixin and ImportSystem that currently seem to require it for proper metadata inheritance.
|
# inter-depenencies between this mixin and XMLImportingModuleStoreRuntime that currently
|
||||||
|
# seem to require it for proper metadata inheritance.
|
||||||
kvs = InheritanceKeyValueStore(initial_values=field_data)
|
kvs = InheritanceKeyValueStore(initial_values=field_data)
|
||||||
field_data = KvsFieldData(kvs)
|
field_data = KvsFieldData(kvs)
|
||||||
xblock = runtime.construct_xblock_from_class(cls, keys, field_data)
|
xblock = runtime.construct_xblock_from_class(cls, keys, field_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user