Beispiel #1
0
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)
Beispiel #2
0
    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'
                      ])
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
    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")
Beispiel #7
0
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'))
Beispiel #8
0
 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
Beispiel #12
0
    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
Beispiel #15
0
 def setUp(self):
     self.annotatable = AnnotatableModule(
         Mock(), get_test_system(), DictFieldData({'data':
                                                   self.sample_xml}),
         ScopeIds(None, None, None, None))
Beispiel #16
0
    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))
Beispiel #18
0
    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)
Beispiel #20
0
    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
Beispiel #21
0
    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
Beispiel #23
0
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])
Beispiel #24
0
 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
        }
Beispiel #27
0
    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,
        )
Beispiel #28
0
    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,
        )
Beispiel #29
0
    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,
            },
        )
Beispiel #30
0
    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))
Beispiel #31
0
    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"],
            },
        )