Convert LTIModule into LTIBlock. (#25713)
This commit is contained in:
@@ -18,7 +18,6 @@ XMODULES = [
|
||||
"videosequence = xmodule.seq_module:SequenceDescriptor",
|
||||
"custom_tag_template = xmodule.raw_module:RawDescriptor",
|
||||
"raw = xmodule.raw_module:RawDescriptor",
|
||||
"lti = xmodule.lti_module:LTIDescriptor",
|
||||
]
|
||||
XBLOCKS = [
|
||||
"about = xmodule.html_module:AboutBlock",
|
||||
@@ -31,6 +30,7 @@ XBLOCKS = [
|
||||
"library = xmodule.library_root_xblock:LibraryRoot",
|
||||
"library_content = xmodule.library_content_module:LibraryContentBlock",
|
||||
"library_sourced = xmodule.library_sourced_block:LibrarySourcedBlock",
|
||||
"lti = xmodule.lti_module:LTIBlock",
|
||||
"nonstaff_error = xmodule.error_module:NonStaffErrorBlock",
|
||||
"problem = xmodule.capa_module:ProblemBlock",
|
||||
"randomize = xmodule.randomize_module:RandomizeBlock",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
A mixin class for LTI 2.0 functionality. This is really just done to refactor the code to
|
||||
keep the LTIModule class from getting too big
|
||||
keep the LTIBlock class from getting too big
|
||||
"""
|
||||
|
||||
|
||||
@@ -26,13 +26,12 @@ LTI_2_0_JSON_CONTENT_TYPE = 'application/vnd.ims.lis.v2.result+json'
|
||||
|
||||
|
||||
class LTIError(Exception):
|
||||
"""Error class for LTIModule and LTI20ModuleMixin"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
"""Error class for LTIBlock and LTI20BlockMixin"""
|
||||
|
||||
|
||||
class LTI20ModuleMixin(object):
|
||||
class LTI20BlockMixin(object):
|
||||
"""
|
||||
This class MUST be mixed into LTIModule. It does not do anything on its own. It's just factored
|
||||
This class MUST be mixed into LTIBlock. It does not do anything on its own. It's just factored
|
||||
out for modularity.
|
||||
"""
|
||||
|
||||
|
||||
@@ -71,14 +71,27 @@ from pkg_resources import resource_string
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from webob import Response
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import List, Scope, String, XBlock
|
||||
from xblock.fields import Boolean, Float
|
||||
from xmodule.mako_module import MakoTemplateBlockBase
|
||||
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from xmodule.editing_module import MetadataOnlyEditingDescriptor
|
||||
from xmodule.lti_2_util import LTI20ModuleMixin, LTIError
|
||||
from xmodule.raw_module import EmptyDataRawDescriptor
|
||||
from xmodule.x_module import XModule, module_attr
|
||||
from xmodule.editing_module import EditingMixin
|
||||
|
||||
from xmodule.lti_2_util import LTI20BlockMixin, LTIError
|
||||
from xmodule.raw_module import EmptyDataRawMixin
|
||||
from xmodule.util.xmodule_django import add_webpack_to_fragment
|
||||
from xmodule.xml_module import XmlMixin
|
||||
from xmodule.x_module import (
|
||||
HTMLSnippet,
|
||||
ResourceTemplates,
|
||||
shim_xmodule_js,
|
||||
XModuleDescriptorToXBlockMixin,
|
||||
XModuleMixin,
|
||||
XModuleToXBlockMixin,
|
||||
)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -256,7 +269,20 @@ class LTIFields(object):
|
||||
)
|
||||
|
||||
|
||||
class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
|
||||
@XBlock.needs("i18n")
|
||||
class LTIBlock(
|
||||
LTIFields,
|
||||
LTI20BlockMixin,
|
||||
EmptyDataRawMixin,
|
||||
XmlMixin,
|
||||
EditingMixin,
|
||||
MakoTemplateBlockBase,
|
||||
XModuleDescriptorToXBlockMixin,
|
||||
XModuleToXBlockMixin,
|
||||
HTMLSnippet,
|
||||
ResourceTemplates,
|
||||
XModuleMixin,
|
||||
): # pylint: disable=abstract-method
|
||||
"""
|
||||
THIS MODULE IS DEPRECATED IN FAVOR OF https://github.com/edx/xblock-lti-consumer
|
||||
|
||||
@@ -338,14 +364,50 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
|
||||
|
||||
Otherwise error message from LTI provider is generated.
|
||||
"""
|
||||
resources_dir = None
|
||||
uses_xmodule_styles_setup = True
|
||||
|
||||
js = {
|
||||
preview_view_js = {
|
||||
'js': [
|
||||
resource_string(__name__, 'js/src/lti/lti.js')
|
||||
]
|
||||
],
|
||||
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
|
||||
}
|
||||
css = {'scss': [resource_string(__name__, 'css/lti/lti.scss')]}
|
||||
js_module_name = 'LTI'
|
||||
preview_view_css = {
|
||||
'scss': [
|
||||
resource_string(__name__, 'css/lti/lti.scss')
|
||||
],
|
||||
}
|
||||
|
||||
mako_template = 'widgets/metadata-only-edit.html'
|
||||
|
||||
studio_js_module_name = 'MetadataOnlyEditingDescriptor'
|
||||
studio_view_js = {
|
||||
'js': [
|
||||
resource_string(__name__, 'js/src/raw/edit/metadata-only.js')
|
||||
],
|
||||
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
|
||||
}
|
||||
studio_view_css = {
|
||||
'scss': [],
|
||||
}
|
||||
|
||||
def studio_view(self, _context):
|
||||
"""
|
||||
Return the studio view.
|
||||
"""
|
||||
context = MakoTemplateBlockBase.get_context(self)
|
||||
# Add our specific template information (the raw data body)
|
||||
context.update({'data': self.data})
|
||||
fragment = Fragment(
|
||||
self.system.render_template(self.mako_template, context)
|
||||
)
|
||||
add_webpack_to_fragment(fragment, 'LTIBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
return fragment
|
||||
|
||||
def max_score(self):
|
||||
return self.weight if self.has_score else None
|
||||
|
||||
def get_input_fields(self): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
# LTI provides a list of default parameters that might be passed as
|
||||
@@ -449,11 +511,15 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
|
||||
'accept_grades_past_due': self.accept_grades_past_due,
|
||||
}
|
||||
|
||||
def get_html(self):
|
||||
def student_view(self, _context):
|
||||
"""
|
||||
Renders parameters to template.
|
||||
Return the student view.
|
||||
"""
|
||||
return self.system.render_template('lti.html', self.get_context())
|
||||
fragment = Fragment()
|
||||
fragment.add_content(self.system.render_template('lti.html', self.get_context()))
|
||||
add_webpack_to_fragment(fragment, 'LTIBlockPreview')
|
||||
shim_xmodule_js(fragment, 'LTI')
|
||||
return fragment
|
||||
|
||||
@XBlock.handler
|
||||
def preview_handler(self, _, __):
|
||||
@@ -533,7 +599,7 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
|
||||
"""
|
||||
Return course by course id.
|
||||
"""
|
||||
return self.descriptor.runtime.modulestore.get_course(self.course_id)
|
||||
return self.runtime.modulestore.get_course(self.course_id)
|
||||
|
||||
@property
|
||||
def context_id(self):
|
||||
@@ -907,20 +973,3 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
||||
else:
|
||||
close_date = due_date
|
||||
return close_date is not None and datetime.datetime.now(UTC) > close_date
|
||||
|
||||
|
||||
class LTIDescriptor(LTIFields, MetadataOnlyEditingDescriptor, EmptyDataRawDescriptor):
|
||||
"""
|
||||
Descriptor for LTI Xmodule.
|
||||
"""
|
||||
|
||||
def max_score(self):
|
||||
return self.weight if self.has_score else None
|
||||
|
||||
module_class = LTIModule
|
||||
resources_dir = None
|
||||
grade_handler = module_attr('grade_handler')
|
||||
preview_handler = module_attr('preview_handler')
|
||||
lti_2_0_result_rest_handler = module_attr('lti_2_0_result_rest_handler')
|
||||
clear_user_module_score = module_attr('clear_user_module_score')
|
||||
get_outcome_service_url = module_attr('get_outcome_service_url')
|
||||
|
||||
@@ -25,6 +25,7 @@ from xmodule.capa_module import ProblemBlock
|
||||
from xmodule.conditional_module import ConditionalBlock
|
||||
from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock
|
||||
from xmodule.library_content_module import LibraryContentBlock
|
||||
from xmodule.lti_module import LTIBlock
|
||||
from xmodule.word_cloud_module import WordCloudBlock
|
||||
from xmodule.x_module import XModuleDescriptor, HTMLSnippet
|
||||
|
||||
@@ -73,6 +74,7 @@ XBLOCK_CLASSES = [
|
||||
CourseInfoBlock,
|
||||
HtmlBlock,
|
||||
LibraryContentBlock,
|
||||
LTIBlock,
|
||||
ProblemBlock,
|
||||
StaticTabBlock,
|
||||
VideoBlock,
|
||||
|
||||
@@ -4,27 +4,30 @@
|
||||
|
||||
import datetime
|
||||
import textwrap
|
||||
import unittest
|
||||
|
||||
from mock import Mock
|
||||
from pytz import UTC
|
||||
from xblock.field_data import DictFieldData
|
||||
|
||||
from xmodule.lti_2_util import LTIError
|
||||
from xmodule.lti_module import LTIDescriptor
|
||||
from xmodule.lti_module import LTIBlock
|
||||
|
||||
from . import LogicTest
|
||||
from . import get_test_system
|
||||
|
||||
|
||||
class LTI20RESTResultServiceTest(LogicTest):
|
||||
class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""Logic tests for LTI module. LTI2.0 REST ResultService"""
|
||||
descriptor_class = LTIDescriptor
|
||||
|
||||
def setUp(self):
|
||||
super(LTI20RESTResultServiceTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
self.system = get_test_system()
|
||||
self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'}
|
||||
self.system.get_real_user = Mock()
|
||||
self.system.publish = Mock()
|
||||
self.system.rebind_noauth_module_to_user = Mock()
|
||||
self.user_id = self.xmodule.runtime.anonymous_student_id
|
||||
|
||||
self.xmodule = LTIBlock(self.system, DictFieldData({}), Mock())
|
||||
self.lti_id = self.xmodule.lti_id
|
||||
self.xmodule.due = None
|
||||
self.xmodule.graceperiod = None
|
||||
@@ -35,8 +38,8 @@ class LTI20RESTResultServiceTest(LogicTest):
|
||||
mocked_course = Mock(name='mocked_course', lti_passports=['lti_id:test_client:test_secret'])
|
||||
modulestore = Mock(name='modulestore')
|
||||
modulestore.get_course.return_value = mocked_course
|
||||
runtime = Mock(name='runtime', modulestore=modulestore)
|
||||
self.xmodule.descriptor.runtime = runtime
|
||||
runtime = Mock(name='runtime', modulestore=modulestore, anonymous_student_id='student')
|
||||
self.xmodule.runtime = runtime
|
||||
self.xmodule.lti_id = "lti_id"
|
||||
|
||||
test_cases = ( # (before sanitize, after sanitize)
|
||||
|
||||
@@ -4,28 +4,31 @@
|
||||
|
||||
import datetime
|
||||
import textwrap
|
||||
import unittest
|
||||
from copy import copy
|
||||
|
||||
import six
|
||||
from lxml import etree
|
||||
from mock import Mock, PropertyMock, patch
|
||||
from opaque_keys.edx.locator import BlockUsageLocator
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from webob.request import Request
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
from xmodule.fields import Timedelta
|
||||
from xmodule.lti_2_util import LTIError
|
||||
from xmodule.lti_module import LTIDescriptor
|
||||
from xmodule.lti_module import LTIBlock
|
||||
|
||||
from . import LogicTest
|
||||
from . import get_test_system
|
||||
|
||||
|
||||
class LTIModuleTest(LogicTest):
|
||||
class LTIBlockTest(unittest.TestCase):
|
||||
"""Logic tests for LTI module."""
|
||||
descriptor_class = LTIDescriptor
|
||||
|
||||
def setUp(self):
|
||||
super(LTIModuleTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'}
|
||||
self.request_body_xml_template = textwrap.dedent(u"""
|
||||
<?xml version = "1.0" encoding = "UTF-8"?>
|
||||
@@ -53,11 +56,17 @@ class LTIModuleTest(LogicTest):
|
||||
</imsx_POXBody>
|
||||
</imsx_POXEnvelopeRequest>
|
||||
""")
|
||||
self.system = get_test_system()
|
||||
self.system.get_real_user = Mock()
|
||||
self.system.publish = Mock()
|
||||
self.system.rebind_noauth_module_to_user = Mock()
|
||||
self.user_id = self.system.anonymous_student_id
|
||||
|
||||
self.user_id = self.xmodule.runtime.anonymous_student_id
|
||||
self.xmodule = LTIBlock(
|
||||
self.system,
|
||||
DictFieldData({}),
|
||||
ScopeIds(None, None, None, BlockUsageLocator(self.system.course_id, 'lti', 'name'))
|
||||
)
|
||||
self.lti_id = self.xmodule.lti_id
|
||||
self.unquoted_resource_link_id = u'{}-i4x-2-3-lti-31de800015cf4afb973356dbe81496df'.format(
|
||||
self.xmodule.runtime.hostname
|
||||
@@ -75,7 +84,6 @@ class LTIModuleTest(LogicTest):
|
||||
|
||||
self.xmodule.due = None
|
||||
self.xmodule.graceperiod = None
|
||||
self.xmodule.descriptor = self.system.construct_xblock_from_class(self.descriptor_class, self.xmodule.scope_ids)
|
||||
|
||||
def get_request_body(self, params=None):
|
||||
"""Fetches the body of a request specified by params"""
|
||||
@@ -111,7 +119,7 @@ class LTIModuleTest(LogicTest):
|
||||
}
|
||||
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIModule.get_client_key_secret',
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
return_value=('test_client_key', u'test_client_secret')
|
||||
)
|
||||
def test_authorization_header_not_present(self, _get_key_secret):
|
||||
@@ -135,7 +143,7 @@ class LTIModuleTest(LogicTest):
|
||||
self.assertDictEqual(expected_response, real_response)
|
||||
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIModule.get_client_key_secret',
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
return_value=('test_client_key', u'test_client_secret')
|
||||
)
|
||||
def test_authorization_header_empty(self, _get_key_secret):
|
||||
@@ -300,7 +308,7 @@ class LTIModuleTest(LogicTest):
|
||||
self.assertEqual(real_outcome_service_url, mock_url_prefix + test_service_name)
|
||||
|
||||
def test_resource_link_id(self):
|
||||
with patch('xmodule.lti_module.LTIModule.location', new_callable=PropertyMock):
|
||||
with patch('xmodule.lti_module.LTIBlock.location', new_callable=PropertyMock):
|
||||
self.xmodule.location.html_id = lambda: 'i4x-2-3-lti-31de800015cf4afb973356dbe81496df'
|
||||
expected_resource_link_id = text_type(six.moves.urllib.parse.quote(self.unquoted_resource_link_id))
|
||||
real_resource_link_id = self.xmodule.get_resource_link_id()
|
||||
@@ -324,7 +332,7 @@ class LTIModuleTest(LogicTest):
|
||||
modulestore = Mock()
|
||||
modulestore.get_course.return_value = mocked_course
|
||||
runtime = Mock(modulestore=modulestore)
|
||||
self.xmodule.descriptor.runtime = runtime
|
||||
self.xmodule.runtime = runtime
|
||||
self.xmodule.lti_id = "lti_id"
|
||||
key, secret = self.xmodule.get_client_key_secret()
|
||||
expected = ('test_client', 'test_secret')
|
||||
@@ -342,7 +350,7 @@ class LTIModuleTest(LogicTest):
|
||||
modulestore = Mock()
|
||||
modulestore.get_course.return_value = mocked_course
|
||||
runtime = Mock(modulestore=modulestore)
|
||||
self.xmodule.descriptor.runtime = runtime
|
||||
self.xmodule.runtime = runtime
|
||||
# set another lti_id
|
||||
self.xmodule.lti_id = "another_lti_id"
|
||||
key_secret = self.xmodule.get_client_key_secret()
|
||||
@@ -360,14 +368,14 @@ class LTIModuleTest(LogicTest):
|
||||
modulestore = Mock()
|
||||
modulestore.get_course.return_value = mocked_course
|
||||
runtime = Mock(modulestore=modulestore)
|
||||
self.xmodule.descriptor.runtime = runtime
|
||||
self.xmodule.runtime = runtime
|
||||
self.xmodule.lti_id = 'lti_id'
|
||||
with self.assertRaises(LTIError):
|
||||
self.xmodule.get_client_key_secret()
|
||||
|
||||
@patch('xmodule.lti_module.signature.verify_hmac_sha1', Mock(return_value=True))
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIModule.get_client_key_secret',
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=('test_client_key', u'test_client_secret'))
|
||||
)
|
||||
def test_successful_verify_oauth_body_sign(self):
|
||||
@@ -376,8 +384,8 @@ class LTIModuleTest(LogicTest):
|
||||
"""
|
||||
self.xmodule.verify_oauth_body_sign(self.get_signed_grade_mock_request())
|
||||
|
||||
@patch('xmodule.lti_module.LTIModule.get_outcome_service_url', Mock(return_value=u'https://testurl/'))
|
||||
@patch('xmodule.lti_module.LTIModule.get_client_key_secret',
|
||||
@patch('xmodule.lti_module.LTIBlock.get_outcome_service_url', Mock(return_value=u'https://testurl/'))
|
||||
@patch('xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=(u'__consumer_key__', u'__lti_secret__')))
|
||||
def test_failed_verify_oauth_body_sign_proxy_mangle_url(self):
|
||||
"""
|
||||
@@ -449,7 +457,7 @@ class LTIModuleTest(LogicTest):
|
||||
|
||||
@patch('xmodule.lti_module.signature.verify_hmac_sha1', Mock(return_value=False))
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIModule.get_client_key_secret',
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=('test_client_key', u'test_client_secret'))
|
||||
)
|
||||
def test_failed_verify_oauth_body_sign(self):
|
||||
|
||||
@@ -96,7 +96,6 @@ from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.error_module import ErrorBlock, NonStaffErrorBlock
|
||||
from xmodule.exceptions import NotFoundError, ProcessingError
|
||||
from xmodule.lti_module import LTIModule
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.util.sandboxing import can_execute_unsafe_code, get_python_lib_zip
|
||||
@@ -641,7 +640,7 @@ def get_module_system_for_user(
|
||||
field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course_id,
|
||||
real_user,
|
||||
module.descriptor,
|
||||
module,
|
||||
asides=XBlockAsidesConfig.possible_asides(),
|
||||
)
|
||||
student_data_real_user = KvsFieldData(DjangoKeyValueStore(field_data_cache_real_user))
|
||||
@@ -649,7 +648,7 @@ def get_module_system_for_user(
|
||||
(inner_system, inner_student_data) = get_module_system_for_user(
|
||||
user=real_user,
|
||||
student_data=student_data_real_user, # These have implicit user bindings, rest of args considered not to
|
||||
descriptor=module.descriptor,
|
||||
descriptor=module,
|
||||
course_id=course_id,
|
||||
track_function=track_function,
|
||||
xqueue_callback_url_prefix=xqueue_callback_url_prefix,
|
||||
@@ -663,7 +662,7 @@ def get_module_system_for_user(
|
||||
will_recheck_access=will_recheck_access,
|
||||
)
|
||||
|
||||
module.descriptor.bind_for_student(
|
||||
module.bind_for_student(
|
||||
inner_system,
|
||||
real_user.id,
|
||||
[
|
||||
@@ -673,10 +672,9 @@ def get_module_system_for_user(
|
||||
],
|
||||
)
|
||||
|
||||
module.descriptor.scope_ids = (
|
||||
module.descriptor.scope_ids._replace(user_id=real_user.id)
|
||||
module.scope_ids = (
|
||||
module.scope_ids._replace(user_id=real_user.id)
|
||||
)
|
||||
module.scope_ids = module.descriptor.scope_ids # this is needed b/c NamedTuples are immutable
|
||||
# now bind the module to the new ModuleSystem instance and vice-versa
|
||||
module.runtime = inner_system
|
||||
inner_system.xmodule_instance = module
|
||||
@@ -757,9 +755,7 @@ def get_module_system_for_user(
|
||||
# As we have the time to manually test more modules, we can add to the list
|
||||
# of modules that get the per-course anonymized id.
|
||||
is_pure_xblock = isinstance(descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor)
|
||||
module_class = getattr(descriptor, 'module_class', None)
|
||||
is_lti_module = not is_pure_xblock and issubclass(module_class, LTIModule)
|
||||
if (is_pure_xblock and not getattr(descriptor, 'requires_per_student_anonymous_id', False)) or is_lti_module:
|
||||
if (is_pure_xblock and not getattr(descriptor, 'requires_per_student_anonymous_id', False)):
|
||||
anonymous_student_id = anonymous_id_for_user(user, course_id)
|
||||
else:
|
||||
anonymous_student_id = anonymous_id_for_user(user, None)
|
||||
|
||||
@@ -126,7 +126,7 @@ class TestLTI(BaseTestXmodule):
|
||||
self.assertEqual(generated_content.decode('utf-8'), expected_content)
|
||||
|
||||
|
||||
class TestLTIModuleListing(SharedModuleStoreTestCase):
|
||||
class TestLTIBlockListing(SharedModuleStoreTestCase):
|
||||
"""
|
||||
a test for the rest endpoint that lists LTI modules in a course
|
||||
"""
|
||||
@@ -136,7 +136,7 @@ class TestLTIModuleListing(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestLTIModuleListing, cls).setUpClass()
|
||||
super(TestLTIBlockListing, cls).setUpClass()
|
||||
cls.course = CourseFactory.create(display_name=cls.COURSE_NAME, number=cls.COURSE_SLUG)
|
||||
cls.chapter1 = ItemFactory.create(
|
||||
parent_location=cls.course.location,
|
||||
|
||||
@@ -78,7 +78,7 @@ from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVer
|
||||
from common.djangoapps.xblock_django.models import XBlockConfiguration
|
||||
from xmodule.capa_module import ProblemBlock
|
||||
from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock
|
||||
from xmodule.lti_module import LTIDescriptor
|
||||
from xmodule.lti_module import LTIBlock
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
@@ -1917,7 +1917,9 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
self.assertTrue(mock_grade_histogram.called)
|
||||
|
||||
|
||||
PER_COURSE_ANONYMIZED_DESCRIPTORS = (LTIDescriptor, )
|
||||
PER_COURSE_ANONYMIZED_XBLOCKS = (
|
||||
LTIBlock,
|
||||
)
|
||||
PER_STUDENT_ANONYMIZED_XBLOCKS = [
|
||||
AboutBlock,
|
||||
CourseInfoBlock,
|
||||
@@ -1930,7 +1932,6 @@ PER_STUDENT_ANONYMIZED_XBLOCKS = [
|
||||
# The "set" here is to work around the bug that load_classes returns duplicates for multiply-declared classes.
|
||||
PER_STUDENT_ANONYMIZED_DESCRIPTORS = sorted(set([
|
||||
class_ for (name, class_) in XModuleDescriptor.load_classes()
|
||||
if not issubclass(class_, PER_COURSE_ANONYMIZED_DESCRIPTORS)
|
||||
] + PER_STUDENT_ANONYMIZED_XBLOCKS), key=str)
|
||||
|
||||
|
||||
@@ -1999,20 +2000,20 @@ class TestAnonymousStudentId(SharedModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
self._get_anonymous_id(CourseKey.from_string(course_id), descriptor_class)
|
||||
)
|
||||
|
||||
@ddt.data(*PER_COURSE_ANONYMIZED_DESCRIPTORS)
|
||||
def test_per_course_anonymized_id(self, descriptor_class):
|
||||
@ddt.data(*PER_COURSE_ANONYMIZED_XBLOCKS)
|
||||
def test_per_course_anonymized_id(self, xblock_class):
|
||||
self.assertEqual(
|
||||
# This value is set by observation, so that later changes to the student
|
||||
# id computation don't break old data
|
||||
'0c706d119cad686d28067412b9178454',
|
||||
self._get_anonymous_id(CourseKey.from_string('MITx/6.00x/2012_Fall'), descriptor_class)
|
||||
self._get_anonymous_id(CourseKey.from_string('MITx/6.00x/2012_Fall'), xblock_class)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
# This value is set by observation, so that later changes to the student
|
||||
# id computation don't break old data
|
||||
'e9969c28c12c8efa6e987d6dbeedeb0b',
|
||||
self._get_anonymous_id(CourseKey.from_string('MITx/6.00x/2013_Spring'), descriptor_class)
|
||||
self._get_anonymous_id(CourseKey.from_string('MITx/6.00x/2013_Spring'), xblock_class)
|
||||
)
|
||||
|
||||
|
||||
@@ -2288,11 +2289,11 @@ class TestRebindModule(TestSubmittingProblems):
|
||||
module = self.get_module_for_user(self.anon_user)
|
||||
user2 = UserFactory()
|
||||
user2.id = 2
|
||||
module.system.rebind_noauth_module_to_user(module._xmodule, user2) # pylint: disable=protected-access
|
||||
module.system.rebind_noauth_module_to_user(module, user2)
|
||||
self.assertTrue(module)
|
||||
self.assertEqual(module.system.anonymous_student_id, anonymous_id_for_user(user2, self.course.id))
|
||||
self.assertEqual(module.scope_ids.user_id, user2.id)
|
||||
self.assertEqual(module._xmodule.scope_ids.user_id, user2.id) # pylint: disable=protected-access
|
||||
self.assertEqual(module.scope_ids.user_id, user2.id)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
|
||||
Reference in New Issue
Block a user