def test_inherited_field(self): kvs = InheritanceKeyValueStore( initial_values={}, inherited_settings={'showanswer': 'inherited'}) model_data = DbModel(kvs) descriptor = self.get_descriptor(model_data) editable_fields = descriptor.editable_metadata_fields self.assert_field_values(editable_fields, 'showanswer', InheritanceMixin.showanswer, explicitly_set=False, value='inherited', default_value='inherited') # Mimic the case where display_name WOULD have been inherited, except we explicitly set it. kvs = InheritanceKeyValueStore( initial_values={'showanswer': 'explicit'}, inherited_settings={'showanswer': 'inheritable value'}) model_data = DbModel(kvs) descriptor = self.get_descriptor(model_data) editable_fields = descriptor.editable_metadata_fields self.assert_field_values(editable_fields, 'showanswer', InheritanceMixin.showanswer, explicitly_set=True, value='explicit', default_value='inheritable value')
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() db_model = DbModel(key_store) runtime = Runtime(Mock(), db_model, [TestMixin]) tester = runtime.construct_xblock_from_class(TestXBlock, ScopeIds('s0', 'TestXBlock', 'd0', 'u0')) assert_false(db_model.has(tester, 'not a field')) for field in tester.fields.values(): new_value = 'new ' + field.name assert_false(db_model.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(db_model.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 test_runtime_render(): key_store = DictKeyValueStore() db_model = DbModel(key_store) runtime = MockRuntimeForQuerying() tester = TestXBlock(runtime, db_model, Mock()) # string we want to update using the handler update_string = u"user state update" # test against the student view frag = runtime.render(tester, 'student_view', [update_string]) assert_equals(frag.body_html(), update_string) assert_equals(tester.preferences, update_string) # test against the fallback view update_string = u"new update" frag = runtime.render(tester, 'test_fallback_view', [update_string]) assert_equals(frag.body_html(), update_string) assert_equals(tester.preferences, update_string) # test block-first update_string = u"penultimate update" frag = tester.render('student_view', [update_string]) assert_equals(frag.body_html(), update_string) assert_equals(tester.preferences, update_string) # test against the no-fallback XBlock update_string = u"ultimate update" tester = TestXBlockNoFallback(Mock(), db_model, Mock()) with assert_raises(NoSuchViewError): runtime.render(tester, 'test_nonexistant_view', [update_string])
def preview_model_data(descriptor): return DbModel( SessionKeyValueStore(request, descriptor._model_data), descriptor.module_class, preview_id, MongoUsage(preview_id, descriptor.location.url()), )
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) url_name = xml_object.get('url_name', xml_object.get('slug')) location = Location('i4x', org, course, 'video', url_name) if is_pointer_tag(xml_object): filepath = cls._format_filepath(xml_object.tag, name_to_pathname(url_name)) xml_data = etree.tostring( cls.load_file(filepath, system.resources_fs, location)) field_data = cls._parse_video_xml(xml_data) field_data['location'] = location kvs = InheritanceKeyValueStore(initial_values=field_data) field_data = DbModel(kvs) video = 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, ) return video
def preview_model_data(descriptor): "Helper method to create a DbModel from a descriptor" return DbModel( SessionKeyValueStore(request, descriptor._model_data), descriptor.module_class, preview_id, MongoUsage(preview_id, descriptor.location.url()), )
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.runtime.module_data) return module else: # load the module and apply the inherited metadata try: category = json_data['location']['category'] class_ = XModuleDescriptor.load_class(category, self.default_class) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in getattr(class_, 'metadata_translations', {}).items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, ) field_data = DbModel(kvs) scope_ids = ScopeIds(None, category, location, location) module = self.construct_xblock_from_class( class_, field_data, scope_ids) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non-draft location non_draft_loc = location.replace(revision=None) # Convert the serialized fields values in self.cached_metadata # to python values metadata_to_inherit = self.cached_metadata.get( non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) # decache any computed pending field settings module.save() return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json(json_data, self, json_data['location'], error_msg=exc_info_to_str( sys.exc_info()))
def xblock_from_json(self, class_, usage_id, json_data, course_entry_override=None): if course_entry_override is None: course_entry_override = self.course_entry # 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 usage_id is None: usage_id = LocalId() block_locator = BlockUsageLocator( version_guid=course_entry_override['_id'], usage_id=usage_id, course_id=course_entry_override.get('course_id'), branch=course_entry_override.get('branch')) kvs = SplitMongoKVS( definition, json_data.get('fields', {}), json_data.get('_inherited_settings'), ) field_data = DbModel(kvs) try: module = self.construct_xblock_from_class( class_, field_data, ScopeIds(None, json_data.get('category'), definition_id, block_locator)) except Exception: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, BlockUsageLocator(version_guid=course_entry_override['_id'], usage_id=usage_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 = self.modulestore.definition_locator( definition) # decache any pending field settings module.save() # If this is an in-memory block, store it in this system if isinstance(block_locator.usage_id, LocalId): self.local_modules[block_locator] = module return module
def create_xblock(usage, student_id=None): """Create an XBlock instance. This will be invoked to create new instances for every request. """ block_cls = XBlock.load_class(usage.block_name) runtime = WorkbenchRuntime(block_cls, student_id, usage) model = DbModel(MEMORY_KVS, block_cls, student_id, usage) block = block_cls(runtime, model) return block
def test_default_fn(): key_store = SerialDefaultKVS() db_model = DbModel(key_store) tester = TestIntegerXblock(Mock(), db_model, Mock()) tester2 = TestIntegerXblock(Mock(), db_model, Mock()) # ensure value is not in tester before any actions assert_false(db_model.has(tester, 'counter')) # ensure value is same over successive calls for same DbModel first_call = tester.counter assert_equals(first_call, 1) assert_equals(first_call, tester.counter) # ensure the value is not saved in the object assert_false(db_model.has(tester, 'counter')) # ensure save does not save the computed default back to the object tester.save() assert_false(db_model.has(tester, 'counter')) # ensure second object gets another value second_call = tester2.counter assert_equals(second_call, 2)
def _create_new_model_data(self, category, location, definition_data, metadata): """ To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs """ kvs = MongoKeyValueStore(definition_data, [], metadata, location, category) class_ = XModuleDescriptor.load_class(category, self.default_class) model_data = DbModel(kvs, class_, None, MongoUsage(None, location)) model_data['category'] = category model_data['location'] = location return model_data
def _load_preview_module(request, descriptor): """ Return a preview XModule instantiated from the supplied descriptor. request: The active django request descriptor: An XModuleDescriptor """ student_data = DbModel(SessionKeyValueStore(request)) descriptor.bind_for_student( _preview_module_system(request, descriptor), LmsFieldData(descriptor._field_data, student_data), # pylint: disable=protected-access ) return descriptor
def _create_new_field_data(self, category, location, definition_data, metadata): """ To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs """ kvs = MongoKeyValueStore( definition_data, [], metadata, ) field_data = DbModel(kvs) return field_data
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.system.module_data) return module else: # load the module and apply the inherited metadata try: class_ = XModuleDescriptor.load_class( json_data['location']['category'], self.default_class ) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in class_.metadata_translations.items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, ) model_data = DbModel(kvs, class_, None, MongoUsage( self.course_id, location)) module = class_(self, location, model_data) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non- # draft location non_draft_loc = location._replace(revision=None) metadata_to_inherit = self.cached_metadata.get( non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, error_msg=exc_info_to_str(sys.exc_info()) )
def xblock_from_json(self, class_, usage_id, json_data, course_entry_override=None): if course_entry_override is None: course_entry_override = self.course_entry # most likely a lazy loader or the id directly definition = json_data.get('definition', {}) block_locator = BlockUsageLocator( version_guid=course_entry_override['_id'], usage_id=usage_id, course_id=course_entry_override.get('course_id'), branch=course_entry_override.get('branch')) kvs = SplitMongoKVS(definition, json_data.get('fields', {}), json_data.get('_inherited_settings'), block_locator, json_data.get('category')) model_data = DbModel( kvs, class_, None, SplitMongoKVSid( # DbModel req's that these support .url() block_locator, self.modulestore.definition_locator(definition))) try: module = class_(self, model_data) except Exception: log.warning("Failed to load descriptor", exc_info=True) if usage_id is None: usage_id = "MISSING" return ErrorDescriptor.from_json( json_data, self, BlockUsageLocator(version_guid=course_entry_override['_id'], usage_id=usage_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 = self.modulestore.definition_locator( definition) # decache any pending field settings module.save() return module
def load_preview_module(request, preview_id, descriptor): """ Return a preview XModule instantiated from the supplied descriptor. request: The active django request preview_id (str): An identifier specifying which preview this module is used for descriptor: An XModuleDescriptor """ student_data = DbModel(SessionKeyValueStore(request)) descriptor.bind_for_student( preview_module_system(request, preview_id, descriptor), LmsFieldData(descriptor._field_data, student_data), # pylint: disable=protected-access ) return descriptor
def test_view_counter_state(): key_store = DictKeyValueStore() db_model = DbModel(key_store) tester = ViewCounter(Mock(), db_model, Mock()) assert_equals(tester.views, 0) # View the XBlock five times for i in xrange(5): generated_html = tester.student_view({}) # Make sure the html fragment we're expecting appears in the body_html assert_in('<span class="views">{0}</span>'.format(i + 1), generated_html.body_html()) assert_equals(tester.views, i + 1)
def test_runtime_handle(): # Test a simple handler and a fallback handler key_store = DictKeyValueStore() db_model = DbModel(key_store) tester = TestXBlock(Mock(), db_model, Mock()) runtime = MockRuntimeForQuerying() # string we want to update using the handler update_string = "user state update" assert_equals(runtime.handle(tester, 'existing_handler', update_string), 'I am the existing test handler') assert_equals(tester.user_state, update_string) # when the handler needs to use the fallback as given name can't be found new_update_string = "new update" assert_equals(runtime.handle(tester, 'test_fallback_handler', new_update_string), 'I have been handled') assert_equals(tester.user_state, new_update_string) # request to use a handler which doesn't have XBlock.handler decoration # should use the fallback new_update_string = "new update" assert_equals(runtime.handle(tester, 'handler_without_correct_decoration', new_update_string), 'gone to fallback') assert_equals(tester.user_state, new_update_string) # handler can't be found & no fallback handler supplied, should throw an exception tester = TestXBlockNoFallback(Mock(), db_model, Mock()) ultimate_string = "ultimate update" with assert_raises(NoSuchHandlerError): runtime.handle(tester, 'test_nonexistant_fallback_handler', ultimate_string) # request to use a handler which doesn't have XBlock.handler decoration # and no fallback should raise NoSuchHandlerError with assert_raises(NoSuchHandlerError): runtime.handle(tester, 'handler_without_correct_decoration', 'handled')
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 xblock_model_data(descriptor): return DbModel( LmsKeyValueStore(descriptor._model_data, model_data_cache), descriptor.module_class, user.id, LmsUsage(descriptor.location, descriptor.location))
def xblock_field_data(descriptor): student_data = DbModel(DjangoKeyValueStore(field_data_cache)) return lms_field_data(descriptor._field_data, student_data)
def __init__(self, student_id=None): super(WorkbenchRuntime, self).__init__(USAGE_STORE, DbModel(WORKBENCH_KVS)) self.student_id = student_id
def get_module_for_descriptor_internal(user, descriptor, field_data_cache, course_id, track_function, xqueue_callback_url_prefix, position=None, wrap_xmodule_display=True, grade_bucket_type=None, static_asset_path=''): """ Actually implement get_module, without requiring a request. See get_module() docstring for further details. """ # Short circuit--if the user shouldn't have access, bail without doing any work if not has_access(user, descriptor, 'load', course_id): return None student_data = DbModel(DjangoKeyValueStore(field_data_cache)) descriptor._field_data = LmsFieldData(descriptor._field_data, student_data) # Setup system context for module instance ajax_url = reverse( 'modx_dispatch', kwargs=dict(course_id=course_id, location=descriptor.location.url(), dispatch=''), ) # Intended use is as {ajax_url}/{dispatch_command}, so get rid of the trailing slash. ajax_url = ajax_url.rstrip('/') def make_xqueue_callback(dispatch='score_update'): # Fully qualified callback URL for external queueing system relative_xqueue_callback_url = reverse( 'xqueue_callback', kwargs=dict(course_id=course_id, userid=str(user.id), mod_id=descriptor.location.url(), dispatch=dispatch), ) return xqueue_callback_url_prefix + relative_xqueue_callback_url # Default queuename is course-specific and is derived from the course that # contains the current module. # TODO: Queuename should be derived from 'course_settings.json' of each course xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course xqueue = { 'interface': xqueue_interface, 'construct_callback': make_xqueue_callback, 'default_queuename': xqueue_default_queuename.replace(' ', '_'), 'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS } # This is a hacky way to pass settings to the combined open ended xmodule # It needs an S3 interface to upload images to S3 # It needs the open ended grading interface in order to get peer grading to be done # this first checks to see if the descriptor is the correct one, and only sends settings if it is # Get descriptor metadata fields indicating needs for various settings needs_open_ended_interface = getattr(descriptor, "needs_open_ended_interface", False) needs_s3_interface = getattr(descriptor, "needs_s3_interface", False) # Initialize interfaces to None open_ended_grading_interface = None s3_interface = None # Create interfaces if needed if needs_open_ended_interface: open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE open_ended_grading_interface[ 'mock_peer_grading'] = settings.MOCK_PEER_GRADING open_ended_grading_interface[ 'mock_staff_grading'] = settings.MOCK_STAFF_GRADING if needs_s3_interface: s3_interface = { 'access_key': getattr(settings, 'AWS_ACCESS_KEY_ID', ''), 'secret_access_key': getattr(settings, 'AWS_SECRET_ACCESS_KEY', ''), 'storage_bucket_name': getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'openended') } def inner_get_module(descriptor): """ Delegate to get_module_for_descriptor_internal() with all values except `descriptor` set. Because it does an access check, it may return None. """ # TODO: fix this so that make_xqueue_callback uses the descriptor passed into # inner_get_module, not the parent's callback. Add it as an argument.... return get_module_for_descriptor_internal( user, descriptor, field_data_cache, course_id, track_function, make_xqueue_callback, position, wrap_xmodule_display, grade_bucket_type, static_asset_path) def publish(event): """A function that allows XModules to publish events. This only supports grade changes right now.""" if event.get('event_name') != 'grade': return # Construct the key for the module key = KeyValueStore.Key(scope=Scope.user_state, user_id=user.id, block_scope_id=descriptor.location, field_name='grade') student_module = field_data_cache.find_or_create(key) # Update the grades student_module.grade = event.get('value') student_module.max_grade = event.get('max_value') # Save all changes to the underlying KeyValueStore student_module.save() # Bin score into range and increment stats score_bucket = get_score_bucket(student_module.grade, student_module.max_grade) org, course_num, run = course_id.split("/") tags = [ "org:{0}".format(org), "course:{0}".format(course_num), "run:{0}".format(run), "score_bucket:{0}".format(score_bucket) ] if grade_bucket_type is not None: tags.append('type:%s' % grade_bucket_type) dog_stats_api.increment("lms.courseware.question_answered", tags=tags) # Build a list of wrapping functions that will be applied in order # to the Fragment content coming out of the xblocks that are about to be rendered. block_wrappers = [] # Wrap the output display in a single div to allow for the XModule # javascript to be bound correctly if wrap_xmodule_display is True: block_wrappers.append(wrap_xblock) # TODO (cpennington): When modules are shared between courses, the static # prefix is going to have to be specific to the module, not the directory # that the xml was loaded from # Rewrite urls beginning in /static to point to course-specific content block_wrappers.append( partial(replace_static_urls, getattr(descriptor, 'data_dir', None), course_id=course_id, static_asset_path=static_asset_path or descriptor.static_asset_path)) # Allow URLs of the form '/course/' refer to the root of multicourse directory # hierarchy of this course block_wrappers.append(partial(replace_course_urls, course_id)) # this will rewrite intra-courseware links (/jump_to_id/<id>). This format # is an improvement over the /course/... format for studio authored courses, # because it is agnostic to course-hierarchy. # NOTE: module_id is empty string here. The 'module_id' will get assigned in the replacement # function, we just need to specify something to get the reverse() to work. block_wrappers.append( partial( replace_jump_to_id_urls, course_id, reverse('jump_to_id', kwargs={ 'course_id': course_id, 'module_id': '' }), )) if settings.MITX_FEATURES.get('DISPLAY_HISTOGRAMS_TO_STAFF'): if has_access(user, descriptor, 'staff', course_id): block_wrappers.append(partial(add_histogram, user)) system = ModuleSystem( track_function=track_function, render_template=render_to_string, static_url=settings.STATIC_URL, ajax_url=ajax_url, xqueue=xqueue, # TODO (cpennington): Figure out how to share info between systems filestore=descriptor.runtime.resources_fs, get_module=inner_get_module, user=user, debug=settings.DEBUG, hostname=settings.SITE_NAME, # TODO (cpennington): This should be removed when all html from # a module is coming through get_html and is therefore covered # by the replace_static_urls code below replace_urls=partial( static_replace.replace_static_urls, data_directory=getattr(descriptor, 'data_dir', None), course_id=course_id, static_asset_path=static_asset_path or descriptor.static_asset_path, ), replace_course_urls=partial(static_replace.replace_course_urls, course_id=course_id), replace_jump_to_id_urls=partial(static_replace.replace_jump_to_id_urls, course_id=course_id, jump_to_id_base_url=reverse( 'jump_to_id', kwargs={ 'course_id': course_id, 'module_id': '' })), node_path=settings.NODE_PATH, publish=publish, anonymous_student_id=unique_id_for_user(user), course_id=course_id, open_ended_grading_interface=open_ended_grading_interface, s3_interface=s3_interface, cache=cache, can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)), # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington) mixins=descriptor.runtime.mixologist._mixins, # pylint: disable=protected-access wrappers=block_wrappers, ) # pass position specified in URL to module through ModuleSystem system.set('position', position) if settings.MITX_FEATURES.get('ENABLE_PSYCHOMETRICS'): system.set( 'psychometrics_handler', # set callback for updating PsychometricsData make_psychometrics_data_update_handler(course_id, user, descriptor.location.url())) system.set('user_is_staff', has_access(user, descriptor.location, 'staff', course_id)) # make an ErrorDescriptor -- assuming that the descriptor's system is ok if has_access(user, descriptor.location, 'staff', course_id): system.error_descriptor_class = ErrorDescriptor else: system.error_descriptor_class = NonStaffErrorDescriptor descriptor.xmodule_runtime = system descriptor.scope_ids = descriptor.scope_ids._replace(user_id=user.id) return descriptor
def preview_field_data(descriptor): "Helper method to create a DbModel from a descriptor" student_data = DbModel(SessionKeyValueStore(request)) return lms_field_data(descriptor._field_data, student_data)