def test_unique_id_default(): class TestBlock(XBlock): """ Block for testing """ field_a = String(default=UNIQUE_ID, scope=Scope.settings) field_b = String(default=UNIQUE_ID, scope=Scope.user_state) sids = ScopeIds(user_id="bob", block_type="bobs-type", def_id="definition-id", usage_id="usage-id") runtime = TestRuntime(services={'field-data': DictFieldData({})}) block = TestBlock(runtime, DictFieldData({}), sids) unique_a = block.field_a unique_b = block.field_b # Create another instance of the same block. Unique ID defaults should not change. runtime = TestRuntime(services={'field-data': DictFieldData({})}) block = TestBlock(runtime, DictFieldData({}), sids) assert_equals(unique_a, block.field_a) assert_equals(unique_b, block.field_b) # Change the user id. Unique ID default should change for field_b with # user_state scope, but not for field_a with scope=settings. runtime = TestRuntime(services={'field-data': DictFieldData({})}) block = TestBlock(runtime, DictFieldData({}), sids._replace(user_id='alice')) assert_equals(unique_a, block.field_a) assert_not_equals(unique_b, block.field_b) # Change the usage id. Unique ID default for both fields should change. runtime = TestRuntime(services={'field-data': DictFieldData({})}) block = TestBlock(runtime, DictFieldData({}), sids._replace(usage_id='usage-2')) assert_not_equals(unique_a, block.field_a) assert_not_equals(unique_b, block.field_b)
def setUp(self): self.student = '*****@*****.**' self.instructor = '*****@*****.**' self.password = '******' self.location = 'TestLocation' self.create_account('u1', self.student, self.password) self.create_account('u2', self.instructor, self.password) self.activate_user(self.student) self.activate_user(self.instructor) self.course_id = "edX/toy/2012_Fall" self.toy = modulestore().get_course(self.course_id) location = "i4x://edX/toy/peergrading/init" field_data = DictFieldData({ 'data': "<peergrading/>", 'location': location, 'category': 'peergrading' }) self.mock_service = peer_grading_service.MockPeerGradingService() self.system = ModuleSystem( ajax_url=location, track_function=None, get_module=None, render_template=render_to_string, replace_urls=None, xmodule_field_data=lambda d: d._field_data, s3_interface=test_util_open_ended.S3_INTERFACE, open_ended_grading_interface=test_util_open_ended. OPEN_ENDED_GRADING_INTERFACE, mixins=settings.XBLOCK_MIXINS, ) self.descriptor = peer_grading_module.PeerGradingDescriptor( self.system, field_data, ScopeIds(None, None, None, None)) self.peer_module = self.descriptor.xmodule(self.system) self.peer_module.peer_gs = self.mock_service self.logout()
def test_conditional_module_parse_sources(self): dummy_system = Mock() dummy_location = BlockUsageLocator( CourseLocator("edX", "conditional_test", "test_run"), "conditional", "SampleConditional") dummy_scope_ids = ScopeIds(None, None, dummy_location, dummy_location) dummy_field_data = DictFieldData({ 'data': '<conditional/>', 'xml_attributes': { 'sources': 'i4x://HarvardX/ER22x/poll_question/T15_poll;i4x://HarvardX/ER22x/poll_question/T16_poll' }, 'children': None, }) conditional = ConditionalDescriptor( dummy_system, dummy_field_data, dummy_scope_ids, ) self.assertEqual(conditional.parse_sources(conditional.xml_attributes), [ 'i4x://HarvardX/ER22x/poll_question/T15_poll', 'i4x://HarvardX/ER22x/poll_question/T16_poll' ])
def _section_send_email(course_key, access, course): """ Provide data for the corresponding bulk email section """ html_module = HtmlDescriptor( course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))) fragment = course.system.render(html_module, 'studio_view') fragment = wrap_xblock( 'LmsRuntime', html_module, 'studio_view', fragment, None, extra_data={"course-id": course_key.to_deprecated_string()}, usage_id_serializer=lambda usage_id: quote_slashes( usage_id.to_deprecated_string())) email_editor = fragment.content section_data = { 'section_key': 'send_email', 'section_display_name': _('Email'), 'access': access, 'send_email': reverse('send_email', kwargs={'course_id': course_key.to_deprecated_string()}), 'editor': email_editor, 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': course_key.to_deprecated_string()}), 'email_background_tasks_url': reverse('list_background_email_tasks', kwargs={'course_id': course_key.to_deprecated_string()}), } return section_data
def create_block_from_xml(xml_data, system, org=None, course=None, default_class=None): """ Create an XBlock instance from XML data. `xml_data' is a string containing valid xml. `system` is an XMLParsingSystem. `org` and `course` are optional strings that will be used in the generated block's url identifiers. `default_class` is the class to instantiate of the XML indicates a class that can't be loaded. Returns the fully instantiated XBlock. """ node = etree.fromstring(xml_data) raw_class = XBlock.load_class(node.tag, default_class, select=prefer_xmodules) xblock_class = system.mixologist.mix(raw_class) # leave next line commented out - useful for low-level debugging # log.debug('[create_block_from_xml] tag=%s, class=%s' % (node.tag, xblock_class)) url_name = node.get('url_name', node.get('slug')) location = Location('i4x', org, course, node.tag, url_name) scope_ids = ScopeIds(None, location.category, location, location) xblock = xblock_class.parse_xml(node, system, scope_ids) return xblock
def test_children(self): """ Check course/randomize module works fine """ self.assertTrue(self.course.has_children) self.assertEquals(len(self.course.get_children()), 2) def inner_get_module(descriptor): """ Override systems.get_module This method will be called when any call is made to self.system.get_module """ if isinstance(descriptor, BlockUsageLocator): location = descriptor descriptor = self.modulestore.get_item(location, depth=None) descriptor.xmodule_runtime = self.get_dummy_course() descriptor.xmodule_runtime.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access descriptor.xmodule_runtime.get_module = inner_get_module return descriptor self.system.get_module = inner_get_module # Get randomize_descriptor from the course & verify its children randomize_descriptor = inner_get_module(self.course.id.make_usage_key('randomize', 'my_randomize')) self.assertTrue(randomize_descriptor.has_children) self.assertEquals(len(randomize_descriptor.get_children()), 2) # Call RandomizeModule which will select an element from the list of available items randomize_module = RandomizeModule( randomize_descriptor, self.system, scope_ids=ScopeIds(None, None, self.course.id, self.course.id) ) # Verify the selected child self.assertEquals(len(randomize_module.get_child_descriptors()), 1, "No child is chosen") self.assertIn(randomize_module.child.display_name, ['A', 'B'], "Unwanted child selected")
def test_db_model_keys(): # Tests that updates to fields are properly recorded in the KeyValueStore, # and that the keys have been constructed correctly key_store = DictKeyValueStore() field_data = KvsFieldData(key_store) runtime = TestRuntime(Mock(), field_data, [TestMixin]) tester = runtime.construct_xblock_from_class( TestXBlock, ScopeIds('s0', 'TestXBlock', 'd0', 'u0')) assert_false(field_data.has(tester, 'not a field')) for field in tester.fields.values(): new_value = 'new ' + field.name assert_false(field_data.has(tester, field.name)) setattr(tester, field.name, new_value) # Write out the values tester.save() # Make sure everything saved correctly for field in tester.fields.values(): assert_true(field_data.has(tester, field.name)) def get_key_value(scope, user_id, block_scope_id, field_name): """Gets the value, from `key_store`, of a Key with the given values.""" new_key = KeyValueStore.Key(scope, user_id, block_scope_id, field_name) return key_store.db_dict[new_key] # Examine each value in the database and ensure that keys were constructed correctly assert_equals('new content', get_key_value(Scope.content, None, 'd0', 'content')) assert_equals('new settings', get_key_value(Scope.settings, None, 'u0', 'settings')) assert_equals('new user_state', get_key_value(Scope.user_state, 's0', 'u0', 'user_state')) assert_equals( 'new preferences', get_key_value(Scope.preferences, 's0', 'TestXBlock', 'preferences')) assert_equals('new user_info', get_key_value(Scope.user_info, 's0', None, 'user_info')) assert_equals( 'new by_type', get_key_value(Scope(UserScope.NONE, BlockScope.TYPE), None, 'TestXBlock', 'by_type')) assert_equals( 'new for_all', get_key_value(Scope(UserScope.NONE, BlockScope.ALL), None, None, 'for_all')) assert_equals( 'new user_def', get_key_value(Scope(UserScope.ONE, BlockScope.DEFINITION), 's0', 'd0', 'user_def')) assert_equals( 'new agg_global', get_key_value(Scope(UserScope.ALL, BlockScope.ALL), None, None, 'agg_global')) assert_equals( 'new agg_type', get_key_value(Scope(UserScope.ALL, BlockScope.TYPE), None, 'TestXBlock', 'agg_type')) assert_equals( 'new agg_def', get_key_value(Scope(UserScope.ALL, BlockScope.DEFINITION), None, 'd0', 'agg_def')) assert_equals( 'new agg_usage', get_key_value(Scope.user_state_summary, None, 'u0', 'agg_usage')) assert_equals('new mixin_content', get_key_value(Scope.content, None, 'd0', 'mixin_content')) assert_equals('new mixin_settings', get_key_value(Scope.settings, None, 'u0', 'mixin_settings')) assert_equals( 'new mixin_user_state', get_key_value(Scope.user_state, 's0', 'u0', 'mixin_user_state')) assert_equals( 'new mixin_preferences', get_key_value(Scope.preferences, 's0', 'TestXBlock', 'mixin_preferences')) assert_equals( 'new mixin_user_info', get_key_value(Scope.user_info, 's0', None, 'mixin_user_info')) assert_equals( 'new mixin_by_type', get_key_value(Scope(UserScope.NONE, BlockScope.TYPE), None, 'TestXBlock', 'mixin_by_type')) assert_equals( 'new mixin_for_all', get_key_value(Scope(UserScope.NONE, BlockScope.ALL), None, None, 'mixin_for_all')) assert_equals( 'new mixin_user_def', get_key_value(Scope(UserScope.ONE, BlockScope.DEFINITION), 's0', 'd0', 'mixin_user_def')) assert_equals( 'new mixin_agg_global', get_key_value(Scope(UserScope.ALL, BlockScope.ALL), None, None, 'mixin_agg_global')) assert_equals( 'new mixin_agg_type', get_key_value(Scope(UserScope.ALL, BlockScope.TYPE), None, 'TestXBlock', 'mixin_agg_type')) assert_equals( 'new mixin_agg_def', get_key_value(Scope(UserScope.ALL, BlockScope.DEFINITION), None, 'd0', 'mixin_agg_def')) assert_equals( 'new mixin_agg_usage', get_key_value(Scope.user_state_summary, None, 'u0', 'mixin_agg_usage'))
def _make_block(self, block_type=None): """ Creates a test block """ block_type = block_type if block_type else self.TestXBlock runtime_mock = mock.Mock(spec=Runtime) scope_ids = ScopeIds("user_id", block_type.etree_node_tag, "def_id", "usage_id") return block_type(runtime=runtime_mock, field_data=DictFieldData({}), scope_ids=scope_ids)
def _section_send_email(course, access): """ Provide data for the corresponding bulk email section """ course_key = course.id # Monkey-patch applicable_aside_types to return no asides for the duration of this render with patch.object(course.runtime, 'applicable_aside_types', null_applicable_aside_types): # This HtmlDescriptor is only being used to generate a nice text editor. html_module = HtmlDescriptor( course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))) fragment = course.system.render(html_module, 'studio_view') fragment = wrap_xblock( 'LmsRuntime', html_module, 'studio_view', fragment, None, extra_data={"course-id": unicode(course_key)}, usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)), # Generate a new request_token here at random, because this module isn't connected to any other # xblock rendering. request_token=uuid.uuid1().get_hex()) cohorts = [] if is_course_cohorted(course_key): cohorts = get_course_cohorts(course) course_modes = [] if not VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled( course_key): course_modes = CourseMode.modes_for_course(course_key, include_expired=True, only_selectable=False) email_editor = fragment.content section_data = { 'section_key': 'send_email', 'section_display_name': _('Email'), 'access': access, 'send_email': reverse('send_email', kwargs={'course_id': unicode(course_key)}), 'editor': email_editor, 'cohorts': cohorts, 'course_modes': course_modes, 'default_cohort_name': DEFAULT_COHORT_NAME, 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': unicode(course_key)}), 'email_background_tasks_url': reverse('list_background_email_tasks', kwargs={'course_id': unicode(course_key)}), 'email_content_history_url': reverse('list_email_content', kwargs={'course_id': unicode(course_key)}), } return section_data
def xblock_from_json(self, class_, block_id, json_data, course_entry_override=None, **kwargs): if course_entry_override is None: course_entry_override = self.course_entry else: # most recent retrieval is most likely the right one for next caller (see comment above fn) self.course_entry['branch'] = course_entry_override['branch'] self.course_entry['org'] = course_entry_override['org'] self.course_entry['course'] = course_entry_override['course'] self.course_entry['run'] = course_entry_override['run'] # most likely a lazy loader or the id directly definition = json_data.get('definition', {}) definition_id = self.modulestore.definition_locator(definition) # If no usage id is provided, generate an in-memory id if block_id is None: block_id = LocalId() block_locator = BlockUsageLocator( CourseLocator( version_guid=course_entry_override['structure']['_id'], org=course_entry_override.get('org'), course=course_entry_override.get('course'), run=course_entry_override.get('run'), branch=course_entry_override.get('branch'), ), block_type=json_data.get('category'), block_id=block_id, ) converted_fields = self.modulestore.convert_references_to_keys( block_locator.course_key, class_, json_data.get('fields', {}), self.course_entry['structure']['blocks'], ) kvs = SplitMongoKVS(definition, converted_fields, json_data.get('_inherited_settings'), **kwargs) field_data = KvsFieldData(kvs) try: module = self.construct_xblock_from_class( class_, ScopeIds(None, json_data.get('category'), definition_id, block_locator), field_data, ) except Exception: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, BlockUsageLocator(CourseLocator( version_guid=course_entry_override['structure']['_id']), block_type='error', block_id=block_id), error_msg=exc_info_to_str(sys.exc_info())) edit_info = json_data.get('edit_info', {}) module.edited_by = edit_info.get('edited_by') module.edited_on = edit_info.get('edited_on') module.subtree_edited_by = None # TODO - addressed with LMS-11183 module.subtree_edited_on = None # TODO - addressed with LMS-11183 module.published_by = None # TODO - addressed with LMS-11184 module.published_date = None # TODO - addressed with LMS-11184 module.previous_version = edit_info.get('previous_version') module.update_version = edit_info.get('update_version') module.source_version = edit_info.get('source_version', None) module.definition_locator = definition_id # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.block_id, LocalId): self.local_modules[block_locator] = module return module
def xblock_from_json(self, class_, course_key, block_key, block_data, course_entry_override=None, **kwargs): """ Load and return block info. """ if course_entry_override is None: course_entry_override = self.course_entry else: # most recent retrieval is most likely the right one for next caller (see comment above fn) self.course_entry = CourseEnvelope( course_entry_override.course_key, self.course_entry.structure) definition_id = block_data.definition # If no usage id is provided, generate an in-memory id if block_key is None: block_key = BlockKey(block_data.block_type, LocalId()) convert_fields = lambda field: self.modulestore.convert_references_to_keys( course_key, class_, field, self.course_entry.structure['blocks'], ) if definition_id is not None and not block_data.definition_loaded: definition_loader = DefinitionLazyLoader( self.modulestore, course_key, block_key.type, definition_id, convert_fields, ) else: definition_loader = None # If no definition id is provide, generate an in-memory id if definition_id is None: definition_id = LocalId() # Construct the Block Usage Locator: block_locator = course_key.make_usage_key( block_type=block_key.type, block_id=block_key.id, ) converted_fields = convert_fields(block_data.fields) converted_defaults = convert_fields(block_data.defaults) if block_key in self._parent_map: parent_key = self._parent_map[block_key] parent = course_key.make_usage_key(parent_key.type, parent_key.id) else: parent = None try: kvs = SplitMongoKVS(definition_loader, converted_fields, converted_defaults, parent=parent, field_decorator=kwargs.get('field_decorator')) if InheritanceMixin in self.modulestore.xblock_mixins: field_data = inheriting_field_data(kvs) else: field_data = KvsFieldData(kvs) module = self.construct_xblock_from_class( class_, ScopeIds(None, block_key.type, definition_id, block_locator), field_data, for_parent=kwargs.get('for_parent')) except Exception: # pylint: disable=broad-except log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( block_data, self, course_entry_override.course_key.make_usage_key( block_type='error', block_id=block_key.id), error_msg=exc_info_to_str(sys.exc_info())) edit_info = block_data.edit_info module._edited_by = edit_info.edited_by # pylint: disable=protected-access module._edited_on = edit_info.edited_on # pylint: disable=protected-access module.previous_version = edit_info.previous_version module.update_version = edit_info.update_version module.source_version = edit_info.source_version module.definition_locator = DefinitionLocator(block_key.type, definition_id) # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.block_id, LocalId): self.local_modules[block_locator] = module return module
def xblock_from_json(self, class_, course_key, block_id, json_data, inherited_settings, course_entry_override=None, **kwargs): if course_entry_override is None: course_entry_override = self.course_entry else: # most recent retrieval is most likely the right one for next caller (see comment above fn) self.course_entry['branch'] = course_entry_override['branch'] self.course_entry['org'] = course_entry_override['org'] self.course_entry['course'] = course_entry_override['course'] self.course_entry['run'] = course_entry_override['run'] definition_id = json_data.get('definition') block_type = json_data['category'] if block_id is not None: if inherited_settings is None: # see if there's a value in course_entry if (block_type, block_id) in self.course_entry['inherited_settings']: inherited_settings = self.course_entry[ 'inherited_settings'][(block_type, block_id)] elif (block_type, block_id) not in self.course_entry['inherited_settings']: self.course_entry['inherited_settings'][( block_type, block_id)] = inherited_settings if definition_id is not None and not json_data.get( 'definition_loaded', False): definition_loader = DefinitionLazyLoader( self.modulestore, block_type, definition_id, lambda fields: self.modulestore.convert_references_to_keys( course_key, self.load_block_type(block_type), fields, self.course_entry['structure']['blocks'], )) else: definition_loader = None # If no definition id is provide, generate an in-memory id if definition_id is None: definition_id = LocalId() # If no usage id is provided, generate an in-memory id if block_id is None: block_id = LocalId() block_locator = BlockUsageLocator( course_key, block_type=block_type, block_id=block_id, ) converted_fields = self.modulestore.convert_references_to_keys( block_locator.course_key, class_, json_data.get('fields', {}), self.course_entry['structure']['blocks'], ) kvs = SplitMongoKVS(definition_loader, converted_fields, inherited_settings, **kwargs) field_data = KvsFieldData(kvs) try: module = self.construct_xblock_from_class( class_, ScopeIds(None, block_type, definition_id, block_locator), field_data, ) except Exception: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, BlockUsageLocator(CourseLocator( version_guid=course_entry_override['structure']['_id']), block_type='error', block_id=block_id), error_msg=exc_info_to_str(sys.exc_info())) edit_info = json_data.get('edit_info', {}) module._edited_by = edit_info.get('edited_by') module._edited_on = edit_info.get('edited_on') module.previous_version = edit_info.get('previous_version') module.update_version = edit_info.get('update_version') module.source_version = edit_info.get('source_version', None) module.definition_locator = DefinitionLocator(block_type, definition_id) # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.block_id, LocalId): self.local_modules[block_locator] = module return module
def __init__(self): self.runtime = get_test_system() self.student_view = Mock(return_value=Fragment(self.get_html())) self.save = Mock() self.id = 'i4x://this/is/a/fake/id' self.scope_ids = ScopeIds('fake_user_id', 'fake_block_type', 'fake_definition_id', 'fake_usage_id')
def get_block(self, usage_id, for_parent=None, block_type_overrides=None): # pylint: disable=arguments-differ """ Create an XBlock instance in this runtime. Args: usage_key(OpaqueKey): identifier used to find the XBlock class and data. block_type_overrides(dict): optional dict of block types to override in returned block metadata: {'from_block_type': 'to_block_type'} """ def_id = self.id_reader.get_definition_id(usage_id) if def_id is None: raise ValueError(f"Definition not found for usage {usage_id}") if not isinstance(def_id, BundleDefinitionLocator): raise TypeError( "This runtime can only load blocks stored in Blockstore bundles." ) try: block_type = self.id_reader.get_block_type(def_id) if block_type_overrides and block_type in block_type_overrides: block_type = block_type_overrides[block_type] except NoSuchDefinition: raise NoSuchUsage(repr(usage_id)) # lint-amnesty, pylint: disable=raise-missing-from keys = ScopeIds(self.user_id, block_type, def_id, usage_id) if self.system.authored_data_store.has_cached_definition(def_id): return self.construct_xblock(block_type, keys, for_parent=for_parent) else: # We need to load this block's field data from its OLX file in blockstore: xml_node = xml_for_definition(def_id) if xml_node.get("url_name", None): log.warning( "XBlock at %s should not specify an old-style url_name attribute.", def_id.olx_path) block_class = self.mixologist.mix(self.load_block_type(block_type)) if hasattr(block_class, 'parse_xml_new_runtime'): # This is a (former) XModule with messy XML parsing code; let its parse_xml() method continue to work # as it currently does in the old runtime, but let this parse_xml_new_runtime() method parse the XML in # a simpler way that's free of tech debt, if defined. # In particular, XmlParserMixin doesn't play well with this new runtime, so this is mostly about # bypassing that mixin's code. # When a former XModule no longer needs to support the old runtime, its parse_xml_new_runtime method # should be removed and its parse_xml() method should be simplified to just call the super().parse_xml() # plus some minor additional lines of code as needed. block = block_class.parse_xml_new_runtime(xml_node, runtime=self, keys=keys) else: block = block_class.parse_xml(xml_node, runtime=self, keys=keys, id_generator=None) # Update field data with parsed values. We can't call .save() because it will call save_block(), below. block.force_save_fields(block._get_fields_to_save()) # pylint: disable=protected-access self.system.authored_data_store.cache_fields(block) # There is no way to set the parent via parse_xml, so do what # HierarchyMixin would do: if for_parent is not None: block._parent_block = for_parent # pylint: disable=protected-access block._parent_block_id = for_parent.scope_ids.usage_id # pylint: disable=protected-access return block
def setUp(self): self.annotatable = AnnotatableModule( Mock(), get_test_system(), DictFieldData({'data': self.sample_xml}), ScopeIds(None, None, None, None))
def parse_xml(cls, node, runtime, keys, id_generator): # pylint: disable=unused-argument """ Use `node` to construct a new block. Arguments: node (etree.Element): The xml node to parse into an xblock. runtime (:class:`.Runtime`): The runtime to use while parsing. keys (:class:`.ScopeIds`): The keys identifying where this block will store its data. id_generator (:class:`.IdGenerator`): An object that will allow the runtime to generate correct definition and usage ids for children of this block. Returns (XBlock): The newly parsed XBlock """ # VS[compat] -- just have the url_name lookup, once translation is done url_name = node.get('url_name', node.get('slug')) def_id = id_generator.create_definition(node.tag, url_name) usage_id = id_generator.create_usage(def_id) # VS[compat] -- detect new-style each-in-a-file mode if is_pointer_tag(node): # new style: # read the actual definition file--named using url_name.replace(':','/') filepath = cls._format_filepath(node.tag, name_to_pathname(url_name)) definition_xml = cls.load_file(filepath, runtime.resources_fs, def_id) runtime.parse_asides(definition_xml, def_id, usage_id, id_generator) else: filepath = None definition_xml = node dog_stats_api.increment( DEPRECATION_VSCOMPAT_EVENT, tags=["location:xmlparser_util_mixin_parse_xml"]) # Note: removes metadata. definition, children = cls.load_definition(definition_xml, runtime, def_id, id_generator) # VS[compat] -- make Ike's github preview links work in both old and # new file layouts if is_pointer_tag(node): # new style -- contents actually at filepath definition['filename'] = [filepath, filepath] metadata = cls.load_metadata(definition_xml) # move definition metadata into dict dmdata = definition.get('definition_metadata', '') if dmdata: metadata['definition_metadata_raw'] = dmdata try: metadata.update(json.loads(dmdata)) except Exception as err: log.debug('Error in loading metadata %r', dmdata, exc_info=True) metadata['definition_metadata_err'] = str(err) # Set/override any metadata specified by policy cls.apply_policy(metadata, runtime.get_policy(usage_id)) field_data = {} field_data.update(metadata) field_data.update(definition) field_data['children'] = children field_data['xml_attributes']['filename'] = definition.get( 'filename', ['', None]) # for git link kvs = InheritanceKeyValueStore(initial_values=field_data) field_data = KvsFieldData(kvs) return runtime.construct_xblock_from_class( cls, # We're loading a descriptor, so student_id is meaningless ScopeIds(None, node.tag, def_id, usage_id), field_data, )
def setUp(self): self.annotatable = AnnotatableModule(self.descriptor, get_test_system(), self.field_data, ScopeIds(None, None, None, None))
def create(system, source_is_error_module=False, source_visible_to_staff_only=False): """ return a dict of modules: the conditional with a single source and a single child. Keys are 'cond_module', 'source_module', and 'child_module'. if the source_is_error_module flag is set, create a real ErrorModule for the source. """ descriptor_system = get_test_descriptor_system() # construct source descriptor and module: source_location = BlockUsageLocator(CourseLocator("edX", "conditional_test", "test_run", deprecated=True), "problem", "SampleProblem", deprecated=True) if source_is_error_module: # Make an error descriptor and module source_descriptor = NonStaffErrorDescriptor.from_xml( 'some random xml data', system, id_generator=CourseLocationManager(source_location.course_key), error_msg='random error message') else: source_descriptor = Mock(name='source_descriptor') source_descriptor.location = source_location source_descriptor.visible_to_staff_only = source_visible_to_staff_only source_descriptor.runtime = descriptor_system source_descriptor.render = lambda view, context=None: descriptor_system.render( source_descriptor, view, context) # construct other descriptors: child_descriptor = Mock(name='child_descriptor') child_descriptor.visible_to_staff_only = False child_descriptor._xmodule.student_view.return_value = Fragment( content=u'<p>This is a secret</p>') child_descriptor.student_view = child_descriptor._xmodule.student_view child_descriptor.displayable_items.return_value = [child_descriptor] child_descriptor.runtime = descriptor_system child_descriptor.xmodule_runtime = get_test_system() child_descriptor.render = lambda view, context=None: descriptor_system.render( child_descriptor, view, context) child_descriptor.location = source_location.replace(category='html', name='child') def visible_to_nonstaff_users(desc): """ Returns if the object is visible to nonstaff users. """ return not desc.visible_to_staff_only def load_item(usage_id, for_parent=None): # pylint: disable=unused-argument """Test-only implementation of load_item that simply returns static xblocks.""" return { child_descriptor.location: child_descriptor, source_location: source_descriptor }.get(usage_id) descriptor_system.load_item = load_item system.descriptor_runtime = descriptor_system # construct conditional module: cond_location = BlockUsageLocator(CourseLocator("edX", "conditional_test", "test_run", deprecated=True), "conditional", "SampleConditional", deprecated=True) field_data = DictFieldData({ 'data': '<conditional/>', 'conditional_attr': 'attempted', 'conditional_value': 'true', 'xml_attributes': { 'attempted': 'true' }, 'children': [child_descriptor.location], }) cond_descriptor = ConditionalDescriptor( descriptor_system, field_data, ScopeIds(None, None, cond_location, cond_location)) cond_descriptor.xmodule_runtime = system system.get_module = lambda desc: desc if visible_to_nonstaff_users( desc) else None cond_descriptor.get_required_module_descriptors = Mock( return_value=[source_descriptor]) cond_descriptor.required_modules = [ system.get_module(descriptor) for descriptor in cond_descriptor.get_required_module_descriptors() ] # return dict: return { 'cond_module': cond_descriptor, 'source_module': source_descriptor, 'child_module': child_descriptor }
def generate_scope_ids(runtime, block_type): """ helper to generate scope IDs for an XBlock """ def_id = runtime.id_generator.create_definition(block_type) usage_id = runtime.id_generator.create_usage(def_id) return ScopeIds('user', block_type, def_id, usage_id)
def parse_xml(cls, node, runtime, keys, id_generator): # pylint: disable=unused-argument """ Use `node` to construct a new block. Arguments: node (etree.Element): The xml node to parse into an xblock. runtime (:class:`.Runtime`): The runtime to use while parsing. keys (:class:`.ScopeIds`): The keys identifying where this block will store its data. id_generator (:class:`.IdGenerator`): An object that will allow the runtime to generate correct definition and usage ids for children of this block. Returns (XBlock): The newly parsed XBlock """ # VS[compat] -- just have the url_name lookup, once translation is done url_name = cls._get_url_name(node) def_id = id_generator.create_definition(node.tag, url_name) usage_id = id_generator.create_usage(def_id) aside_children = [] # VS[compat] -- detect new-style each-in-a-file mode if is_pointer_tag(node): # new style: # read the actual definition file--named using url_name.replace(':','/') definition_xml, filepath = cls.load_definition_xml( node, runtime, def_id) aside_children = runtime.parse_asides(definition_xml, def_id, usage_id, id_generator) else: filepath = None definition_xml = node # Note: removes metadata. definition, children = cls.load_definition(definition_xml, runtime, def_id, id_generator) # VS[compat] -- make Ike's github preview links work in both old and # new file layouts if is_pointer_tag(node): # new style -- contents actually at filepath definition['filename'] = [filepath, filepath] metadata = cls.load_metadata(definition_xml) # move definition metadata into dict dmdata = definition.get('definition_metadata', '') if dmdata: metadata['definition_metadata_raw'] = dmdata try: metadata.update(json.loads(dmdata)) except Exception as err: # lint-amnesty, pylint: disable=broad-except log.debug('Error in loading metadata %r', dmdata, exc_info=True) metadata['definition_metadata_err'] = str(err) definition_aside_children = definition.pop('aside_children', None) if definition_aside_children: aside_children.extend(definition_aside_children) # Set/override any metadata specified by policy cls.apply_policy(metadata, runtime.get_policy(usage_id)) field_data = {} field_data.update(metadata) field_data.update(definition) field_data['children'] = children field_data['xml_attributes']['filename'] = definition.get( 'filename', ['', None]) # for git link kvs = InheritanceKeyValueStore(initial_values=field_data) field_data = KvsFieldData(kvs) xblock = runtime.construct_xblock_from_class( cls, # We're loading a descriptor, so student_id is meaningless ScopeIds(None, node.tag, def_id, usage_id), field_data, ) if aside_children: asides_tags = [x.tag for x in aside_children] asides = runtime.get_asides(xblock) for asd in asides: if asd.scope_ids.block_type in asides_tags: xblock.add_aside(asd) return xblock
def setUp(self): super(TestPeerGradingService, self).setUp() self.student = '*****@*****.**' self.instructor = '*****@*****.**' self.password = '******' self.create_account('u1', self.student, self.password) self.create_account('u2', self.instructor, self.password) self.activate_user(self.student) self.activate_user(self.instructor) self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall") self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string() self.toy = modulestore().get_course(self.course_id) location = "i4x://edX/toy/peergrading/init" field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category': 'peergrading'}) self.mock_service = peer_grading_service.MockPeerGradingService() self.system = LmsModuleSystem( static_url=settings.STATIC_URL, track_function=None, get_module=None, render_template=render_to_string, replace_urls=None, s3_interface=test_util_open_ended.S3_INTERFACE, open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE, mixins=settings.XBLOCK_MIXINS, error_descriptor_class=ErrorDescriptor, descriptor_runtime=None, ) self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, field_data, ScopeIds(None, None, None, None)) self.descriptor.xmodule_runtime = self.system self.peer_module = self.descriptor self.peer_module.peer_gs = self.mock_service self.logout()
def xblock_from_json(self, class_, block_id, json_data, course_entry_override=None): if course_entry_override is None: course_entry_override = self.course_entry else: # most recent retrieval is most likely the right one for next caller (see comment above fn) self.course_entry['branch'] = course_entry_override['branch'] self.course_entry['package_id'] = course_entry_override['package_id'] # most likely a lazy loader or the id directly definition = json_data.get('definition', {}) definition_id = self.modulestore.definition_locator(definition) # If no usage id is provided, generate an in-memory id if block_id is None: block_id = LocalId() block_locator = BlockUsageLocator( version_guid=course_entry_override['structure']['_id'], block_id=block_id, package_id=course_entry_override.get('package_id'), branch=course_entry_override.get('branch') ) kvs = SplitMongoKVS( definition, json_data.get('fields', {}), json_data.get('_inherited_settings'), ) field_data = KvsFieldData(kvs) try: module = self.construct_xblock_from_class( class_, ScopeIds(None, json_data.get('category'), definition_id, block_locator), field_data, ) except Exception: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, BlockUsageLocator( version_guid=course_entry_override['structure']['_id'], block_id=block_id ), error_msg=exc_info_to_str(sys.exc_info()) ) edit_info = json_data.get('edit_info', {}) module.edited_by = edit_info.get('edited_by') module.edited_on = edit_info.get('edited_on') module.previous_version = edit_info.get('previous_version') module.update_version = edit_info.get('update_version') module.definition_locator = definition_id # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.block_id, LocalId): self.local_modules[block_locator] = module return module
class CombinedOpenEndedModuleTest(unittest.TestCase): """ Unit tests for the combined open ended xmodule """ location = Location( ["i4x", "edX", "open_ended", "combinedopenended", "SampleQuestion"]) definition_template = """ <combinedopenended attempts="10000"> {rubric} {prompt} <task> {task1} </task> <task> {task2} </task> </combinedopenended> """ prompt = "<prompt>This is a question prompt</prompt>" rubric = '''<rubric><rubric> <category> <description>Response Quality</description> <option>The response is not a satisfactory answer to the question. It either fails to address the question or does so in a limited way, with no evidence of higher-order thinking.</option> <option>Second option</option> </category> </rubric></rubric>''' max_score = 1 metadata = {'attempts': '10', 'max_score': max_score} static_data = { 'max_attempts': 20, 'prompt': prompt, 'rubric': rubric, 'max_score': max_score, 'display_name': 'Name', 'accept_file_upload': False, 'close_date': "", 's3_interface': test_util_open_ended.S3_INTERFACE, 'open_ended_grading_interface': test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE, 'skip_basic_checks': False, 'graded': True, } oeparam = etree.XML(''' <openendedparam> <initial_display>Enter essay here.</initial_display> <answer_display>This is the answer.</answer_display> <grader_payload>{"grader_settings" : "ml_grading.conf", "problem_id" : "6.002x/Welcome/OETest"}</grader_payload> </openendedparam> ''') task_xml1 = ''' <selfassessment> <hintprompt> What hint about this problem would you give to someone? </hintprompt> <submitmessage> Save Succcesful. Thanks for participating! </submitmessage> </selfassessment> ''' task_xml2 = ''' <openended min_score_to_attempt="1" max_score_to_attempt="1"> <openendedparam> <initial_display>Enter essay here.</initial_display> <answer_display>This is the answer.</answer_display> <grader_payload>{"grader_settings" : "ml_grading.conf", "problem_id" : "6.002x/Welcome/OETest"}</grader_payload> </openendedparam> </openended>''' definition = { 'prompt': etree.XML(prompt), 'rubric': etree.XML(rubric), 'task_xml': [task_xml1, task_xml2] } full_definition = definition_template.format(prompt=prompt, rubric=rubric, task1=task_xml1, task2=task_xml2) descriptor = Mock(data=full_definition) test_system = get_test_system() test_system.open_ended_grading_interface = None combinedoe_container = CombinedOpenEndedModule( descriptor=descriptor, runtime=test_system, field_data=DictFieldData({ 'data': full_definition, 'weight': '1', }), scope_ids=ScopeIds(None, None, None, None), ) def setUp(self): self.combinedoe = CombinedOpenEndedV1Module( self.test_system, self.location, self.definition, self.descriptor, static_data=self.static_data, metadata=self.metadata, instance_state=self.static_data) def test_get_tag_name(self): """ Test to see if the xml tag name is correct """ name = self.combinedoe.get_tag_name("<t>Tag</t>") self.assertEqual(name, "t") def test_get_last_response(self): """ See if we can parse the last response """ response_dict = self.combinedoe.get_last_response(0) self.assertEqual(response_dict['type'], "selfassessment") self.assertEqual(response_dict['max_score'], self.max_score) self.assertEqual(response_dict['state'], CombinedOpenEndedV1Module.INITIAL) def test_update_task_states(self): """ See if we can update the task states properly """ changed = self.combinedoe.update_task_states() self.assertFalse(changed) current_task = self.combinedoe.current_task current_task.change_state(CombinedOpenEndedV1Module.DONE) changed = self.combinedoe.update_task_states() self.assertTrue(changed) def test_get_max_score(self): """ Try to get the max score of the problem """ self.combinedoe.update_task_states() self.combinedoe.state = "done" self.combinedoe.is_scored = True max_score = self.combinedoe.max_score() self.assertEqual(max_score, 1) def test_container_get_max_score(self): """ See if we can get the max score from the actual xmodule """ # The progress view requires that this function be exposed max_score = self.combinedoe_container.max_score() self.assertEqual(max_score, None) def test_container_get_progress(self): """ See if we can get the progress from the actual xmodule """ progress = self.combinedoe_container.max_score() self.assertEqual(progress, None) def test_get_progress(self): """ Test if we can get the correct progress from the combined open ended class """ self.combinedoe.update_task_states() self.combinedoe.state = "done" self.combinedoe.is_scored = True progress = self.combinedoe.get_progress() self.assertIsInstance(progress, Progress) # progress._a is the score of the xmodule, which is 0 right now. self.assertEqual(progress._a, 0) # progress._b is the max_score (which is 1), divided by the weight (which is 1). self.assertEqual(progress._b, 1) def test_container_weight(self): """ Check the problem weight in the container """ weight = self.combinedoe_container.weight self.assertEqual(weight, 1) def test_container_child_weight(self): """ Test the class to see if it picks up the right weight """ weight = self.combinedoe_container.child_module.weight self.assertEqual(weight, 1) def test_get_score(self): """ See if scoring works """ score_dict = self.combinedoe.get_score() self.assertEqual(score_dict['score'], 0) self.assertEqual(score_dict['total'], 1) def test_alternate_orderings(self): """ Try multiple ordering of definitions to see if the problem renders different steps correctly. """ t1 = self.task_xml1 t2 = self.task_xml2 xml_to_test = [[t1], [t2], [t1, t1], [t1, t2], [t2, t2], [t2, t1], [t1, t2, t1]] for xml in xml_to_test: definition = { 'prompt': etree.XML(self.prompt), 'rubric': etree.XML(self.rubric), 'task_xml': xml } descriptor = Mock(data=definition) combinedoe = CombinedOpenEndedV1Module( self.test_system, self.location, definition, descriptor, static_data=self.static_data, metadata=self.metadata, instance_state=self.static_data) changed = combinedoe.update_task_states() self.assertFalse(changed) combinedoe = CombinedOpenEndedV1Module( self.test_system, self.location, definition, descriptor, static_data=self.static_data, metadata=self.metadata, instance_state={'task_states': TEST_STATE_SA}) combinedoe = CombinedOpenEndedV1Module( self.test_system, self.location, definition, descriptor, static_data=self.static_data, metadata=self.metadata, instance_state={'task_states': TEST_STATE_SA_IN}) def test_get_score_realistic(self): """ Try to parse the correct score from a json instance state """ instance_state = json.loads(MOCK_INSTANCE_STATE) rubric = """ <rubric> <rubric> <category> <description>Response Quality</description> <option>The response is not a satisfactory answer to the question. It either fails to address the question or does so in a limited way, with no evidence of higher-order thinking.</option> <option>The response is a marginal answer to the question. It may contain some elements of a proficient response, but it is inaccurate or incomplete.</option> <option>The response is a proficient answer to the question. It is generally correct, although it may contain minor inaccuracies. There is limited evidence of higher-order thinking.</option> <option>The response is correct, complete, and contains evidence of higher-order thinking.</option> </category> </rubric> </rubric> """ definition = { 'prompt': etree.XML(self.prompt), 'rubric': etree.XML(rubric), 'task_xml': [self.task_xml1, self.task_xml2] } descriptor = Mock(data=definition) combinedoe = CombinedOpenEndedV1Module(self.test_system, self.location, definition, descriptor, static_data=self.static_data, metadata=self.metadata, instance_state=instance_state) score_dict = combinedoe.get_score() self.assertEqual(score_dict['score'], 15.0) self.assertEqual(score_dict['total'], 15.0) def generate_oe_module(self, task_state, task_number, task_xml): """ Return a combined open ended module with the specified parameters """ definition = { 'prompt': etree.XML(self.prompt), 'rubric': etree.XML(self.rubric), 'task_xml': task_xml } descriptor = Mock(data=definition) instance_state = {'task_states': task_state, 'graded': True} if task_number is not None: instance_state.update({'current_task_number': task_number}) combinedoe = CombinedOpenEndedV1Module(self.test_system, self.location, definition, descriptor, static_data=self.static_data, metadata=self.metadata, instance_state=instance_state) return combinedoe def ai_state_reset(self, task_state, task_number=None): """ See if state is properly reset """ combinedoe = self.generate_oe_module(task_state, task_number, [self.task_xml2]) html = combinedoe.get_html() self.assertIsInstance(html, basestring) score = combinedoe.get_score() if combinedoe.is_scored: self.assertEqual(score['score'], 0) else: self.assertEqual(score['score'], None) def ai_state_success(self, task_state, task_number=None, iscore=2, tasks=None): """ See if state stays the same """ if tasks is None: tasks = [self.task_xml1, self.task_xml2] combinedoe = self.generate_oe_module(task_state, task_number, tasks) html = combinedoe.get_html() self.assertIsInstance(html, basestring) score = combinedoe.get_score() self.assertEqual(int(score['score']), iscore) def test_ai_state_reset(self): self.ai_state_reset(TEST_STATE_AI) def test_ai_state2_reset(self): self.ai_state_reset(TEST_STATE_AI2) def test_ai_invalid_state(self): self.ai_state_reset(TEST_STATE_AI2_INVALID) def test_ai_state_rest_task_number(self): self.ai_state_reset(TEST_STATE_AI, task_number=2) self.ai_state_reset(TEST_STATE_AI, task_number=5) self.ai_state_reset(TEST_STATE_AI, task_number=1) self.ai_state_reset(TEST_STATE_AI, task_number=0) def test_ai_state_success(self): self.ai_state_success(TEST_STATE_AI) def test_state_single(self): self.ai_state_success(TEST_STATE_SINGLE, iscore=12) def test_state_pe_single(self): self.ai_state_success(TEST_STATE_PE_SINGLE, iscore=0, tasks=[self.task_xml2])
def scope_ids(self): """ Return the proper scope ids for the peer grading module. """ return ScopeIds(None, None, self.problem_location, self.problem_location)
def xblock_from_json(self, class_, course_key, block_key, json_data, course_entry_override=None, **kwargs): if course_entry_override is None: course_entry_override = self.course_entry else: # most recent retrieval is most likely the right one for next caller (see comment above fn) self.course_entry = CourseEnvelope( course_entry_override.course_key, self.course_entry.structure) definition_id = json_data.get('definition') # If no usage id is provided, generate an in-memory id if block_key is None: block_key = BlockKey(json_data['block_type'], LocalId()) if definition_id is not None and not json_data.get( 'definition_loaded', False): definition_loader = DefinitionLazyLoader( self.modulestore, course_key, block_key.type, definition_id, lambda fields: self.modulestore.convert_references_to_keys( course_key, self.load_block_type(block_key.type), fields, self.course_entry.structure['blocks'], )) else: definition_loader = None # If no definition id is provide, generate an in-memory id if definition_id is None: definition_id = LocalId() block_locator = BlockUsageLocator( course_key, block_type=block_key.type, block_id=block_key.id, ) converted_fields = self.modulestore.convert_references_to_keys( block_locator.course_key, class_, json_data.get('fields', {}), self.course_entry.structure['blocks'], ) if block_key in self._parent_map: parent_key = self._parent_map[block_key] parent = course_key.make_usage_key(parent_key.type, parent_key.id) else: parent = None kvs = SplitMongoKVS(definition_loader, converted_fields, parent=parent, field_decorator=kwargs.get('field_decorator')) if InheritanceMixin in self.modulestore.xblock_mixins: field_data = inheriting_field_data(kvs) else: field_data = KvsFieldData(kvs) try: module = self.construct_xblock_from_class( class_, ScopeIds(None, block_key.type, definition_id, block_locator), field_data, ) except Exception: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, course_entry_override.course_key.make_usage_key( block_type='error', block_id=block_key.id), error_msg=exc_info_to_str(sys.exc_info())) edit_info = json_data.get('edit_info', {}) module._edited_by = edit_info.get('edited_by') module._edited_on = edit_info.get('edited_on') module.previous_version = edit_info.get('previous_version') module.update_version = edit_info.get('update_version') module.source_version = edit_info.get('source_version', None) module.definition_locator = DefinitionLocator(block_key.type, definition_id) # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.block_id, LocalId): self.local_modules[block_locator] = module return module
def create(system, source_is_error_module=False): """ return a dict of modules: the conditional with a single source and a single child. Keys are 'cond_module', 'source_module', and 'child_module'. if the source_is_error_module flag is set, create a real ErrorModule for the source. """ descriptor_system = get_test_descriptor_system() # construct source descriptor and module: source_location = Location("edX", "conditional_test", "test_run", "problem", "SampleProblem", None) if source_is_error_module: # Make an error descriptor and module source_descriptor = NonStaffErrorDescriptor.from_xml( 'some random xml data', system, id_generator=CourseLocationManager(source_location.course_key), error_msg='random error message') else: source_descriptor = Mock(name='source_descriptor') source_descriptor.location = source_location source_descriptor.runtime = descriptor_system source_descriptor.render = lambda view, context=None: descriptor_system.render( source_descriptor, view, context) # construct other descriptors: child_descriptor = Mock(name='child_descriptor') child_descriptor._xmodule.student_view.return_value.content = u'<p>This is a secret</p>' child_descriptor.student_view = child_descriptor._xmodule.student_view child_descriptor.displayable_items.return_value = [child_descriptor] child_descriptor.runtime = descriptor_system child_descriptor.xmodule_runtime = get_test_system() child_descriptor.render = lambda view, context=None: descriptor_system.render( child_descriptor, view, context) child_descriptor.location = source_location.replace(category='html', name='child') descriptor_system.load_item = { child_descriptor.location: child_descriptor, source_location: source_descriptor }.get system.descriptor_runtime = descriptor_system # construct conditional module: cond_location = Location("edX", "conditional_test", "test_run", "conditional", "SampleConditional", None) field_data = DictFieldData({ 'data': '<conditional/>', 'xml_attributes': { 'attempted': 'true' }, 'children': [child_descriptor.location], }) cond_descriptor = ConditionalDescriptor( descriptor_system, field_data, ScopeIds(None, None, cond_location, cond_location)) cond_descriptor.xmodule_runtime = system system.get_module = lambda desc: desc cond_descriptor.get_required_module_descriptors = Mock( return_value=[source_descriptor]) # return dict: return { 'cond_module': cond_descriptor, 'source_module': source_descriptor, 'child_module': child_descriptor }
def from_xml(cls, xml_data, system, id_generator): """ Creates an instance of this descriptor from the supplied xml_data. This may be overridden by subclasses xml_data: A string of xml that will be translated into data and children for this module system: A DescriptorSystem for interacting with external resources """ xml_object = etree.fromstring(xml_data) # VS[compat] -- just have the url_name lookup, once translation is done url_name = xml_object.get('url_name', xml_object.get('slug')) def_id = id_generator.create_definition(xml_object.tag, url_name) usage_id = id_generator.create_usage(def_id) # VS[compat] -- detect new-style each-in-a-file mode if is_pointer_tag(xml_object): # new style: # read the actual definition file--named using url_name.replace(':','/') filepath = cls._format_filepath(xml_object.tag, name_to_pathname(url_name)) definition_xml = cls.load_file(filepath, system.resources_fs, def_id) else: definition_xml = xml_object filepath = None definition, children = cls.load_definition( definition_xml, system, def_id) # note this removes metadata # VS[compat] -- make Ike's github preview links work in both old and # new file layouts if is_pointer_tag(xml_object): # new style -- contents actually at filepath definition['filename'] = [filepath, filepath] metadata = cls.load_metadata(definition_xml) # move definition metadata into dict dmdata = definition.get('definition_metadata', '') if dmdata: metadata['definition_metadata_raw'] = dmdata try: metadata.update(json.loads(dmdata)) except Exception as err: log.debug('Error in loading metadata %r', dmdata, exc_info=True) metadata['definition_metadata_err'] = str(err) # Set/override any metadata specified by policy cls.apply_policy(metadata, system.get_policy(usage_id)) field_data = {} field_data.update(metadata) field_data.update(definition) field_data['children'] = children field_data['xml_attributes']['filename'] = definition.get( 'filename', ['', None]) # for git link kvs = InheritanceKeyValueStore(initial_values=field_data) field_data = KvsFieldData(kvs) return system.construct_xblock_from_class( cls, # We're loading a descriptor, so student_id is meaningless ScopeIds(None, xml_object.tag, def_id, usage_id), field_data, )
def from_xml(cls, xml_data, system, org=None, course=None): """ Creates an instance of this descriptor from the supplied xml_data. This may be overridden by subclasses xml_data: A string of xml that will be translated into data and children for this module system: A DescriptorSystem for interacting with external resources org and course are optional strings that will be used in the generated modules url identifiers """ xml_object = etree.fromstring(xml_data) # VS[compat] -- just have the url_name lookup, once translation is done url_name = xml_object.get('url_name', xml_object.get('slug')) location = Location('i4x', org, course, xml_object.tag, url_name) # VS[compat] -- detect new-style each-in-a-file mode if is_pointer_tag(xml_object): # new style: # read the actual definition file--named using url_name.replace(':','/') filepath = cls._format_filepath(xml_object.tag, name_to_pathname(url_name)) definition_xml = cls.load_file(filepath, system.resources_fs, location) else: definition_xml = xml_object filepath = None definition, children = cls.load_definition( definition_xml, system, location) # note this removes metadata # VS[compat] -- make Ike's github preview links work in both old and # new file layouts if is_pointer_tag(xml_object): # new style -- contents actually at filepath definition['filename'] = [filepath, filepath] metadata = cls.load_metadata(definition_xml) # move definition metadata into dict dmdata = definition.get('definition_metadata', '') if dmdata: metadata['definition_metadata_raw'] = dmdata try: metadata.update(json.loads(dmdata)) except Exception as err: log.debug('Error %s in loading metadata %s' % (err, dmdata)) metadata['definition_metadata_err'] = str(err) # Set/override any metadata specified by policy k = policy_key(location) if k in system.policy: cls.apply_policy(metadata, system.policy[k]) field_data = {} field_data.update(metadata) field_data.update(definition) field_data['children'] = children field_data['xml_attributes']['filename'] = definition.get( 'filename', ['', None]) # for git link field_data['location'] = location field_data['category'] = xml_object.tag kvs = InheritanceKeyValueStore(initial_values=field_data) field_data = DbModel(kvs) return system.construct_xblock_from_class( cls, # We're loading a descriptor, so student_id is meaningless # We also don't have separate notions of definition and usage ids yet, # so we use the location for both ScopeIds(None, location.category, location, location), field_data, )
def test_student_view_data( self, sections, attachments, expected_sections, expected_attachments ): field_data = {"display_name": "Case Study 3"} block = self._construct_xblock_mock( self.block_class, self.keys, field_data=DictFieldData(field_data) ) document_block_keys = ScopeIds( "a_user", "lx_document", "def_id_document", "usage_id_document" ) document_block = DocumentBlock( self.runtime_mock, scope_ids=document_block_keys, field_data=DictFieldData({"display_name": "CS Document", }), ) block.children.append(document_block.scope_ids.block_type) image_block_keys = ScopeIds( "a_user", "lx_image", "def_id_image", "usage_id_image" ) image_block = self._construct_xblock_mock( ImageBlock, image_block_keys, field_data=DictFieldData({"display_name": "CS Image", }), ) block.children.append(image_block.scope_ids.block_type) block.sections = sections block.attachments = attachments def get_block(usage_id): if usage_id == "lx_document": return document_block if usage_id == "lx_image": return image_block self.runtime_mock.get_block.side_effect = get_block data = block.student_view_data(context=None) self.assertDictEqual( data, { "child_blocks": [ { "block_type": "lx_document", "display_name": "CS Document", "usage_id": "lx_document", }, { "block_type": "lx_image", "display_name": "CS Image", "usage_id": "lx_image", }, ], "display_name": "Case Study 3", "sections": expected_sections, "attachments": expected_attachments, }, )
def _load_extra_content(self, system, course_descriptor, category, content_path, course_dir): """ Import fields data content from files """ for filepath in glob.glob(content_path / '*'): if not os.path.isfile(filepath): continue if filepath.endswith('~'): # skip *~ files continue with open(filepath) as f: try: if filepath.find('.json') != -1: # json file with json data content slug, loc, data_content = self._import_field_content( course_descriptor, category, filepath) if data_content is None: continue else: try: # get and update data field in xblock runtime module = system.load_item(loc) for key, value in data_content.items(): setattr(module, key, value) module.save() except ItemNotFoundError: module = None data_content['location'] = loc data_content['category'] = category else: slug = os.path.splitext(os.path.basename(filepath))[0] loc = course_descriptor.scope_ids.usage_id.replace( category=category, name=slug) # html file with html data content html = f.read() try: module = system.load_item(loc) module.data = html module.save() except ItemNotFoundError: module = None data_content = { 'data': html, 'location': loc, 'category': category } if module is None: module = system.construct_xblock( category, # We're loading a descriptor, so student_id is meaningless # We also don't have separate notions of definition and usage ids yet, # so we use the location for both ScopeIds(None, category, loc, loc), DictFieldData(data_content), ) # VS[compat]: # Hack because we need to pull in the 'display_name' for static tabs (because we need to edit them) # lint-amnesty, pylint: disable=line-too-long # from the course policy if category == "static_tab": tab = CourseTabList.get_tab_by_slug( tab_list=course_descriptor.tabs, url_slug=slug) if tab: module.display_name = tab.name module.course_staff_only = tab.course_staff_only module.data_dir = course_dir module.save() self.modules[course_descriptor.id][ module.scope_ids.usage_id] = module except Exception as exc: # pylint: disable=broad-except logging.exception( "Failed to load %s. Skipping... \ Exception: %s", filepath, str(exc)) system.error_tracker("ERROR: " + str(exc))
def test_student_view_data_invalid_attachment(self): field_data = {"display_name": "Case Study 4"} block = self._construct_xblock_mock( self.block_class, self.keys, field_data=DictFieldData(field_data) ) document_block_keys = ScopeIds( "a_user", "lx_document", "def_id_document", "usage_id_document" ) document_block = DocumentBlock( self.runtime_mock, scope_ids=document_block_keys, field_data=DictFieldData({"display_name": "CS Document", }), ) block.children.append(document_block.scope_ids.block_type) image_block_keys = ScopeIds( "a_user", "lx_image", "def_id_image", "usage_id_image" ) image_block = self._construct_xblock_mock( ImageBlock, image_block_keys, field_data=DictFieldData({"display_name": "CS Image", }), ) block.children.append(image_block.scope_ids.block_type) with self.assertRaises(TypeError): block.sections = {"key": "invalid"} with self.assertRaises(TypeError): block.sections = "foo" block.sections = [] with self.assertRaises(TypeError): block.attachments = {"key": "invalid"} with self.assertRaises(TypeError): block.attachments = "foo" block.attachments = ["lx_image"] def get_block(usage_id): if usage_id == "lx_document": return document_block if usage_id == "lx_image": return image_block self.runtime_mock.get_block.side_effect = get_block data = block.student_view_data(context=None) self.assertDictEqual( data, { "child_blocks": [ { "block_type": "lx_document", "display_name": "CS Document", "usage_id": "lx_document", }, { "block_type": "lx_image", "display_name": "CS Image", "usage_id": "lx_image", }, ], "display_name": "Case Study 4", "sections": [], "attachments": ["lx_image"], }, )