From ce13cd540a035dc3625e907950bdc3775e33218c Mon Sep 17 00:00:00 2001 From: Pooja Kulkarni Date: Tue, 3 Jan 2023 16:43:19 -0500 Subject: [PATCH] refactor: rename descriptor -> block within cms Co-authored-by: Agrendalath --- .../management/commands/edit_course_tabs.py | 2 +- .../contentstore/tests/test_contentstore.py | 18 +-- .../tests/test_course_settings.py | 20 +-- .../contentstore/tests/test_i18n.py | 18 +-- .../contentstore/tests/test_libraries.py | 12 +- .../contentstore/tests/test_utils.py | 2 +- cms/djangoapps/contentstore/tests/utils.py | 4 +- cms/djangoapps/contentstore/utils.py | 12 +- .../contentstore/views/certificates.py | 2 +- .../contentstore/views/component.py | 20 +-- .../contentstore/views/entrance_exam.py | 4 +- cms/djangoapps/contentstore/views/preview.py | 30 ++--- .../contentstore/views/tests/test_block.py | 16 +-- .../contentstore/views/tests/test_preview.py | 45 ++++--- .../views/tests/test_transcripts.py | 2 +- .../contentstore/views/transcripts_ajax.py | 4 +- .../models/settings/course_grading.py | 114 +++++++++--------- .../models/settings/course_metadata.py | 64 +++++----- 18 files changed, 195 insertions(+), 194 deletions(-) diff --git a/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py b/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py index 0381d638d8..529d2f881e 100644 --- a/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py +++ b/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py @@ -25,7 +25,7 @@ def print_course(course): print('num type name') for index, item in enumerate(course.tabs): print(index + 1, '"' + item.get('type') + '"', '"' + item.get('name', '') + '"') - # If a course is bad we will get an error descriptor here, dump it and die instead of + # If a course is bad we will get an error here, dump it and die instead of # just sending up the error that .id doesn't exist. except AttributeError: print(course) diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index be120824a4..f7046cc01a 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1713,17 +1713,17 @@ class MetadataSaveTestCase(ContentStoreTestCase): """ video_data = VideoBlock.parse_video_xml(video_sample_xml) video_data.pop('source') - self.video_descriptor = BlockFactory.create( + self.video_block = BlockFactory.create( parent_location=course.location, category='video', **video_data ) def test_metadata_not_persistence(self): """ - Test that descriptors which set metadata fields in their + Test that blocks which set metadata fields in their constructor are correctly deleted. """ - self.assertIn('html5_sources', own_metadata(self.video_descriptor)) + self.assertIn('html5_sources', own_metadata(self.video_block)) attrs_to_strip = { 'show_captions', 'youtube_id_1_0', @@ -1736,13 +1736,13 @@ class MetadataSaveTestCase(ContentStoreTestCase): 'track' } - location = self.video_descriptor.location + location = self.video_block.location for field_name in attrs_to_strip: - delattr(self.video_descriptor, field_name) + delattr(self.video_block, field_name) - self.assertNotIn('html5_sources', own_metadata(self.video_descriptor)) - self.store.update_item(self.video_descriptor, self.user.id) + self.assertNotIn('html5_sources', own_metadata(self.video_block)) + self.store.update_item(self.video_block, self.user.id) block = self.store.get_item(location) self.assertNotIn('html5_sources', own_metadata(block)) @@ -2047,12 +2047,12 @@ class ContentLicenseTest(ContentStoreTestCase): def test_video_license_export(self): content_store = contentstore() root_dir = path(mkdtemp_clean()) - video_descriptor = BlockFactory.create( + video_block = BlockFactory.create( parent_location=self.course.location, category='video', license="all-rights-reserved" ) export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_license') - fname = f"{video_descriptor.scope_ids.usage_id.block_id}.xml" + fname = f"{video_block.scope_ids.usage_id.block_id}.xml" video_file_path = root_dir / "test_license" / "video" / fname with video_file_path.open() as f: video_xml = etree.parse(f) diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index a135faf067..6a1977f375 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -885,33 +885,33 @@ class CourseGradingTest(CourseTestCase): @mock.patch('cms.djangoapps.contentstore.signals.signals.GRADING_POLICY_CHANGED.send') def test_update_section_grader_type(self, send_signal, tracker, uuid): uuid.return_value = 'mockUUID' - # Get the descriptor and the section_grader_type and assert they are the default values - descriptor = modulestore().get_item(self.course.location) + # Get the block and the section_grader_type and assert they are the default values + block = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual('notgraded', section_grader_type['graderType']) - self.assertEqual(None, descriptor.format) - self.assertEqual(False, descriptor.graded) + self.assertEqual(None, block.format) + self.assertEqual(False, block.graded) # Change the default grader type to Homework, which should also mark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'Homework', self.user) - descriptor = modulestore().get_item(self.course.location) + block = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) grading_policy_1 = self._grading_policy_hash_for_course() self.assertEqual('Homework', section_grader_type['graderType']) - self.assertEqual('Homework', descriptor.format) - self.assertEqual(True, descriptor.graded) + self.assertEqual('Homework', block.format) + self.assertEqual(True, block.graded) # Change the grader type back to notgraded, which should also unmark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'notgraded', self.user) - descriptor = modulestore().get_item(self.course.location) + block = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) grading_policy_2 = self._grading_policy_hash_for_course() self.assertEqual('notgraded', section_grader_type['graderType']) - self.assertEqual(None, descriptor.format) - self.assertEqual(False, descriptor.graded) + self.assertEqual(None, block.format) + self.assertEqual(False, block.graded) # one for each call to update_section_grader_type() send_signal.assert_has_calls([ diff --git a/cms/djangoapps/contentstore/tests/test_i18n.py b/cms/djangoapps/contentstore/tests/test_i18n.py index e38094a2d3..db5746f893 100644 --- a/cms/djangoapps/contentstore/tests/test_i18n.py +++ b/cms/djangoapps/contentstore/tests/test_i18n.py @@ -69,19 +69,19 @@ class TestXBlockI18nService(ModuleStoreTestCase): self.request = mock.Mock() self.course = CourseFactory.create() self.field_data = mock.Mock() - self.descriptor = BlockFactory(category="pure", parent=self.course) + self.block = BlockFactory(category="pure", parent=self.course) _prepare_runtime_for_preview( self.request, - self.descriptor, + self.block, self.field_data, ) self.addCleanup(translation.deactivate) - def get_block_i18n_service(self, descriptor): + def get_block_i18n_service(self, block): """ return the block i18n service. """ - i18n_service = self.descriptor.runtime.service(descriptor, 'i18n') + i18n_service = self.block.runtime.service(block, 'i18n') self.assertIsNotNone(i18n_service) self.assertIsInstance(i18n_service, XBlockI18nService) return i18n_service @@ -113,7 +113,7 @@ class TestXBlockI18nService(ModuleStoreTestCase): self.module.ugettext = self.old_ugettext self.module.gettext = self.old_ugettext - i18n_service = self.get_block_i18n_service(self.descriptor) + i18n_service = self.get_block_i18n_service(self.block) # Activate french, so that if the fr files haven't been loaded, they will be loaded now. with translation.override("fr"): @@ -150,13 +150,13 @@ class TestXBlockI18nService(ModuleStoreTestCase): translation.activate("es") with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir, languages=[get_language()])): - i18n_service = self.get_block_i18n_service(self.descriptor) + i18n_service = self.get_block_i18n_service(self.block) self.assertEqual(i18n_service.ugettext('Hello'), 'es-hello-world') translation.activate("ar") with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir, languages=[get_language()])): - i18n_service = self.get_block_i18n_service(self.descriptor) + i18n_service = self.get_block_i18n_service(self.block) self.assertEqual(get_gettext(i18n_service)('Hello'), 'Hello') self.assertNotEqual(get_gettext(i18n_service)('Hello'), 'fr-hello-world') self.assertNotEqual(get_gettext(i18n_service)('Hello'), 'es-hello-world') @@ -164,14 +164,14 @@ class TestXBlockI18nService(ModuleStoreTestCase): translation.activate("fr") with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir, languages=[get_language()])): - i18n_service = self.get_block_i18n_service(self.descriptor) + i18n_service = self.get_block_i18n_service(self.block) self.assertEqual(i18n_service.ugettext('Hello'), 'fr-hello-world') def test_i18n_service_callable(self): """ Test: i18n service should be callable in studio. """ - self.assertTrue(callable(self.descriptor.runtime._services.get('i18n'))) # pylint: disable=protected-access + self.assertTrue(callable(self.block.runtime._services.get('i18n'))) # pylint: disable=protected-access class InternationalizationTest(ModuleStoreTestCase): diff --git a/cms/djangoapps/contentstore/tests/test_libraries.py b/cms/djangoapps/contentstore/tests/test_libraries.py index 3e850e4794..019f67a594 100644 --- a/cms/djangoapps/contentstore/tests/test_libraries.py +++ b/cms/djangoapps/contentstore/tests/test_libraries.py @@ -114,7 +114,7 @@ class LibraryTestCase(ModuleStoreTestCase): self.assertEqual(response.status_code, status_code_expected) return modulestore().get_item(lib_content_block.location) - def _bind_block(self, descriptor, user=None): + def _bind_block(self, block, user=None): """ Helper to use the CMS's module system so we can access student-specific fields. """ @@ -123,7 +123,7 @@ class LibraryTestCase(ModuleStoreTestCase): if user not in self.session_data: self.session_data[user] = {} request = Mock(user=user, session=self.session_data[user]) - _load_preview_block(request, descriptor) + _load_preview_block(request, block) def _update_block(self, usage_key, metadata): """ @@ -174,13 +174,13 @@ class TestLibraries(LibraryTestCase): lc_block = self._refresh_children(lc_block) # Now, we want to make sure that .children has the total # of potential - # children, and that get_child_descriptors() returns the actual children + # children, and that get_child_blocks() returns the actual children # chosen for a given student. - # In order to be able to call get_child_descriptors(), we must first + # In order to be able to call get_child_blocks(), we must first # call bind_for_student: self._bind_block(lc_block) self.assertEqual(len(lc_block.children), num_to_create) - self.assertEqual(len(lc_block.get_child_descriptors()), num_expected) + self.assertEqual(len(lc_block.get_child_blocks()), num_expected) def test_consistent_children(self): """ @@ -204,7 +204,7 @@ class TestLibraries(LibraryTestCase): """ Fetch the child shown to the current user. """ - children = block.get_child_descriptors() + children = block.get_child_blocks() self.assertEqual(len(children), 1) return children[0] diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py index 5b4f4338b2..4b6dc100db 100644 --- a/cms/djangoapps/contentstore/tests/test_utils.py +++ b/cms/djangoapps/contentstore/tests/test_utils.py @@ -620,7 +620,7 @@ class GetUserPartitionInfoTest(ModuleStoreTestCase): self.assertEqual(partitions[0]["scheme"], "random") def _set_partitions(self, partitions): - """Set the user partitions of the course descriptor. """ + """Set the user partitions of the course block. """ self.course.user_partitions = partitions self.course = self.store.update_item(self.course, ModuleStoreEnum.UserID.test) diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py index b58ec17502..4c2b5b5adf 100644 --- a/cms/djangoapps/contentstore/tests/utils.py +++ b/cms/djangoapps/contentstore/tests/utils.py @@ -191,8 +191,8 @@ class CourseTestCase(ProceduralCourseTestMixin, ModuleStoreTestCase): """ Test getting the editing HTML for each vertical. """ # assert is here to make sure that the course being tested actually has verticals (units) to check. self.assertGreater(len(items), 0, "Course has no verticals (units) to check") - for descriptor in items: - resp = self.client.get_html(get_url('container_handler', descriptor.location)) + for block in items: + resp = self.client.get_html(get_url('container_handler', block.location)) self.assertEqual(resp.status_code, 200) def assertAssetsEqual(self, asset_son, course1_id, course2_id): diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 22a07d3ccd..0c4141b5d2 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -372,7 +372,7 @@ def get_split_group_display_name(xblock, course): Arguments: xblock (XBlock): The courseware component. - course (XBlock): The course descriptor. + course (XBlock): The course block. Returns: group name (String): Group name of the matching group xblock. @@ -399,14 +399,14 @@ def get_user_partition_info(xblock, schemes=None, course=None): schemes (iterable of str): If provided, filter partitions to include only schemes with the provided names. - course (XBlock): The course descriptor. If provided, uses this to look up the user partitions + course (XBlock): The course block. If provided, uses this to look up the user partitions instead of loading the course. This is useful if we're calling this function multiple times for the same course want to minimize queries to the modulestore. Returns: list Example Usage: - >>> get_user_partition_info(block, schemes=["cohort", "verification"]) + >>> get_user_partition_info(xblock, schemes=["cohort", "verification"]) [ { "id": 12345, @@ -509,7 +509,7 @@ def get_visibility_partition_info(xblock, course=None): Arguments: xblock (XBlock): The component being edited. - course (XBlock): The course descriptor. If provided, uses this to look up the user partitions + course (XBlock): The course block. If provided, uses this to look up the user partitions instead of loading the course. This is useful if we're calling this function multiple times for the same course want to minimize queries to the modulestore. @@ -569,8 +569,8 @@ def get_xblock_aside_instance(usage_key): :param usage_key: Usage key of aside xblock """ try: - descriptor = modulestore().get_item(usage_key.usage_key) - for aside in descriptor.runtime.get_asides(descriptor): + xblock = modulestore().get_item(usage_key.usage_key) + for aside in xblock.runtime.get_asides(xblock): if aside.scope_ids.block_type == usage_key.aside_type: return aside except ItemNotFoundError: diff --git a/cms/djangoapps/contentstore/views/certificates.py b/cms/djangoapps/contentstore/views/certificates.py index 6535e0a857..3c43ab1065 100644 --- a/cms/djangoapps/contentstore/views/certificates.py +++ b/cms/djangoapps/contentstore/views/certificates.py @@ -128,7 +128,7 @@ class CertificateValidationError(CertificateException): class CertificateManager: """ The CertificateManager is responsible for storage, retrieval, and manipulation of Certificates - Certificates are not stored in the Django ORM, they are a field/setting on the course descriptor + Certificates are not stored in the Django ORM, they are a field/setting on the course block """ @staticmethod def parse(json_string): diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 7f774ab161..d58f601abc 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -552,18 +552,18 @@ def component_handler(request, usage_key_string, handler, suffix=''): try: if is_xblock_aside(usage_key): - # Get the descriptor for the block being wrapped by the aside (not the aside itself) - descriptor = modulestore().get_item(usage_key.usage_key) - handler_descriptor = get_aside_from_xblock(descriptor, usage_key.aside_type) - asides = [handler_descriptor] + # Get the block being wrapped by the aside (not the aside itself) + block = modulestore().get_item(usage_key.usage_key) + handler_block = get_aside_from_xblock(block, usage_key.aside_type) + asides = [handler_block] else: - descriptor = modulestore().get_item(usage_key) - handler_descriptor = descriptor + block = modulestore().get_item(usage_key) + handler_block = block asides = [] - load_services_for_studio(handler_descriptor.runtime, request.user) - resp = handler_descriptor.handle(handler, req, suffix) + load_services_for_studio(handler_block.runtime, request.user) + resp = handler_block.handle(handler, req, suffix) except NoSuchHandlerError: - log.info("XBlock %s attempted to access missing handler %r", handler_descriptor, handler, exc_info=True) + log.info("XBlock %s attempted to access missing handler %r", handler_block, handler, exc_info=True) raise Http404 # lint-amnesty, pylint: disable=raise-missing-from # unintentional update to handle any side effects of handle call @@ -572,7 +572,7 @@ def component_handler(request, usage_key_string, handler, suffix=''): # TNL 101-62 studio write permission is also checked for editing content. if has_course_author_access(request.user, usage_key.course_key): - modulestore().update_item(descriptor, request.user.id, asides=asides) + modulestore().update_item(block, request.user.id, asides=asides) else: #fail quietly if user is not course author. log.warning( diff --git a/cms/djangoapps/contentstore/views/entrance_exam.py b/cms/djangoapps/contentstore/views/entrance_exam.py index 63917d17f6..ced3ed531b 100644 --- a/cms/djangoapps/contentstore/views/entrance_exam.py +++ b/cms/djangoapps/contentstore/views/entrance_exam.py @@ -176,9 +176,9 @@ def _get_entrance_exam(request, course_key): except InvalidKeyError: return HttpResponse(status=404) try: - exam_descriptor = modulestore().get_item(exam_key) + exam_block = modulestore().get_item(exam_key) return HttpResponse( # lint-amnesty, pylint: disable=http-response-with-content-type-json - dump_js_escaped_json({'locator': str(exam_descriptor.location)}), + dump_js_escaped_json({'locator': str(exam_block.location)}), status=200, content_type='application/json') except ItemNotFoundError: return HttpResponse(status=404) diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 12b00d16e6..1b28e65a4c 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -11,6 +11,7 @@ from django.urls import reverse from django.utils.translation import gettext as _ from django.views.decorators.clickjacking import xframe_options_exempt from opaque_keys.edx.keys import UsageKey +from rest_framework.request import Request from web_fragments.fragment import Fragment from xblock.django.request import django_to_webob_request, webob_to_django_response from xblock.exceptions import NoSuchHandlerError @@ -24,7 +25,7 @@ from xmodule.services import SettingsService, TeamsConfigurationService from xmodule.studio_editable import has_author_view from xmodule.util.sandboxing import SandboxService from xmodule.util.xmodule_django import add_webpack_to_fragment -from xmodule.x_module import AUTHOR_VIEW, PREVIEW_VIEWS, STUDENT_VIEW +from xmodule.x_module import AUTHOR_VIEW, PREVIEW_VIEWS, STUDENT_VIEW, XModuleMixin from cms.djangoapps.xblock_config.models import StudioConfig from cms.djangoapps.contentstore.toggles import individualize_anonymous_user_id, ENABLE_COPY_PASTE_FEATURE from cms.lib.xblock.field_data import CmsFieldData @@ -65,8 +66,8 @@ def preview_handler(request, usage_key_string, handler, suffix=''): """ usage_key = UsageKey.from_string(usage_key_string) - descriptor = modulestore().get_item(usage_key) - instance = _load_preview_block(request, descriptor) + block = modulestore().get_item(usage_key) + instance = _load_preview_block(request, block) # Let the module handle the AJAX req = django_to_webob_request(request) @@ -154,6 +155,7 @@ def _prepare_runtime_for_preview(request, block, field_data): required for rendering block previews. request: The active django request + block: An XBlock field_data: Wrapped field data for previews """ @@ -256,29 +258,29 @@ class StudioPartitionService(PartitionService): return None -def _load_preview_block(request, descriptor): +def _load_preview_block(request: Request, block: XModuleMixin): """ - Return a preview XBlock instantiated from the supplied descriptor. Will use mutable fields + Return a preview XBlock instantiated from the supplied block. Will use mutable fields if XBlock supports an author_view. Otherwise, will use immutable fields and student_view. request: The active django request - descriptor: An XModuleDescriptor + block: An XModuleMixin """ student_data = KvsFieldData(SessionKeyValueStore(request)) - if has_author_view(descriptor): + if has_author_view(block): wrapper = partial(CmsFieldData, student_data=student_data) else: wrapper = partial(LmsFieldData, student_data=student_data) # wrap the _field_data upfront to pass to _prepare_runtime_for_preview - wrapped_field_data = wrapper(descriptor._field_data) # pylint: disable=protected-access - _prepare_runtime_for_preview(request, descriptor, wrapped_field_data) + wrapped_field_data = wrapper(block._field_data) # pylint: disable=protected-access + _prepare_runtime_for_preview(request, block, wrapped_field_data) - descriptor.bind_for_student( + block.bind_for_student( request.user.id, [wrapper] ) - return descriptor + return block def _is_xblock_reorderable(xblock, context): @@ -332,12 +334,12 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False): return frag -def get_preview_fragment(request, descriptor, context): +def get_preview_fragment(request, block, context): """ Returns the HTML returned by the XModule's student_view or author_view (if available), - specified by the descriptor and idx. + specified by the block and idx. """ - block = _load_preview_block(request, descriptor) + block = _load_preview_block(request, block) preview_view = AUTHOR_VIEW if has_author_view(block) else STUDENT_VIEW diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py index 863da2e02f..d2345abe26 100644 --- a/cms/djangoapps/contentstore/views/tests/test_block.py +++ b/cms/djangoapps/contentstore/views/tests/test_block.py @@ -2159,10 +2159,10 @@ class TestComponentHandler(TestCase): self.modulestore = patcher.start() self.addCleanup(patcher.stop) - # component_handler calls modulestore.get_item to get the descriptor of the requested xBlock. + # component_handler calls modulestore.get_item to get the requested xBlock. # Here, we mock the return value of modulestore.get_item so it can be used to mock the handler - # of the xBlock descriptor. - self.descriptor = self.modulestore.return_value.get_item.return_value + # of the xBlock. + self.block = self.modulestore.return_value.get_item.return_value self.usage_key = BlockUsageLocator( CourseLocator('dummy_org', 'dummy_course', 'dummy_run'), 'dummy_category', 'dummy_name' @@ -2173,7 +2173,7 @@ class TestComponentHandler(TestCase): self.request.user = self.user def test_invalid_handler(self): - self.descriptor.handle.side_effect = NoSuchHandlerError + self.block.handle.side_effect = NoSuchHandlerError with self.assertRaises(Http404): component_handler(self.request, self.usage_key_string, 'invalid_handler') @@ -2185,7 +2185,7 @@ class TestComponentHandler(TestCase): self.assertEqual(request.method, method) return Response() - self.descriptor.handle = check_handler + self.block.handle = check_handler # Have to use the right method to create the request to get the HTTP method that we want req_factory_method = getattr(self.request_factory, method.lower()) @@ -2198,7 +2198,7 @@ class TestComponentHandler(TestCase): def create_response(handler, request, suffix): # lint-amnesty, pylint: disable=unused-argument return Response(status_code=status_code) - self.descriptor.handle = create_response + self.block.handle = create_response self.assertEqual(component_handler(self.request, self.usage_key_string, 'dummy_handler').status_code, status_code) @@ -2219,7 +2219,7 @@ class TestComponentHandler(TestCase): self.request.user = UserFactory() mock_handler = 'dummy_handler' - self.descriptor.handle = create_response + self.block.handle = create_response with patch( 'cms.djangoapps.contentstore.views.component.is_xblock_aside', @@ -2253,7 +2253,7 @@ class TestComponentHandler(TestCase): else self.usage_key_string ) - self.descriptor.handle = create_response + self.block.handle = create_response with patch( 'cms.djangoapps.contentstore.views.component.is_xblock_aside', diff --git a/cms/djangoapps/contentstore/views/tests/test_preview.py b/cms/djangoapps/contentstore/views/tests/test_preview.py index c8d1266853..7e407c966d 100644 --- a/cms/djangoapps/contentstore/views/tests/test_preview.py +++ b/cms/djangoapps/contentstore/views/tests/test_preview.py @@ -213,13 +213,13 @@ class StudioXBlockServiceBindingTest(ModuleStoreTestCase): """ Tests that the 'user' and 'i18n' services are provided by the Studio runtime. """ - descriptor = BlockFactory(category="pure", parent=self.course) + block = BlockFactory(category="pure", parent=self.course) _prepare_runtime_for_preview( self.request, - descriptor, + block, self.field_data, ) - service = descriptor.runtime.service(descriptor, expected_service) + service = block.runtime.service(block, expected_service) self.assertIsNotNone(service) @@ -242,32 +242,31 @@ class CmsModuleSystemShimTest(ModuleStoreTestCase): self.request = RequestFactory().get('/dummy-url') self.request.user = self.user self.request.session = {} - self.descriptor = BlockFactory(category="video", parent=course) self.field_data = mock.Mock() self.contentstore = contentstore() - self.descriptor = BlockFactory(category="problem", parent=course) + self.block = BlockFactory(category="problem", parent=course) _prepare_runtime_for_preview( self.request, - block=self.descriptor, + block=self.block, field_data=mock.Mock(), ) self.course = self.store.get_item(course.location) def test_get_user_role(self): - assert self.descriptor.runtime.get_user_role() == 'staff' + assert self.block.runtime.get_user_role() == 'staff' @XBlock.register_temp_plugin(PureXBlock, identifier='pure') def test_render_template(self): - descriptor = BlockFactory(category="pure", parent=self.course) - html = get_preview_fragment(self.request, descriptor, {'element_id': 142}).content + block = BlockFactory(category="pure", parent=self.course) + html = get_preview_fragment(self.request, block, {'element_id': 142}).content assert '
Testing the MakoService
' in html @override_settings(COURSES_WITH_UNSAFE_CODE=[r'course-v1:edX\+LmsModuleShimTest\+2021_Fall']) def test_can_execute_unsafe_code(self): - assert self.descriptor.runtime.can_execute_unsafe_code() + assert self.block.runtime.can_execute_unsafe_code() def test_cannot_execute_unsafe_code(self): - assert not self.descriptor.runtime.can_execute_unsafe_code() + assert not self.block.runtime.can_execute_unsafe_code() @override_settings(PYTHON_LIB_FILENAME=PYTHON_LIB_FILENAME) def test_get_python_lib_zip(self): @@ -277,7 +276,7 @@ class CmsModuleSystemShimTest(ModuleStoreTestCase): source_file=self.PYTHON_LIB_SOURCE_FILE, target_filename=self.PYTHON_LIB_FILENAME, ) - assert self.descriptor.runtime.get_python_lib_zip() == zipfile + assert self.block.runtime.get_python_lib_zip() == zipfile def test_no_get_python_lib_zip(self): zipfile = upload_file_to_course( @@ -286,40 +285,40 @@ class CmsModuleSystemShimTest(ModuleStoreTestCase): source_file=self.PYTHON_LIB_SOURCE_FILE, target_filename=self.PYTHON_LIB_FILENAME, ) - assert self.descriptor.runtime.get_python_lib_zip() is None + assert self.block.runtime.get_python_lib_zip() is None def test_cache(self): - assert hasattr(self.descriptor.runtime.cache, 'get') - assert hasattr(self.descriptor.runtime.cache, 'set') + assert hasattr(self.block.runtime.cache, 'get') + assert hasattr(self.block.runtime.cache, 'set') def test_replace_urls(self): html = '' - assert self.descriptor.runtime.replace_urls(html) == \ + assert self.block.runtime.replace_urls(html) == \ static_replace.replace_static_urls(html, course_id=self.course.id) def test_anonymous_user_id_preview(self): - assert self.descriptor.runtime.anonymous_student_id == 'student' + assert self.block.runtime.anonymous_student_id == 'student' @override_waffle_flag(INDIVIDUALIZE_ANONYMOUS_USER_ID, active=True) def test_anonymous_user_id_individual_per_student(self): """Test anonymous_user_id on a block which uses per-student anonymous IDs""" # Create the runtime with the flag turned on. - descriptor = BlockFactory(category="problem", parent=self.course) + block = BlockFactory(category="problem", parent=self.course) _prepare_runtime_for_preview( self.request, - block=descriptor, + block=block, field_data=mock.Mock(), ) - assert descriptor.runtime.anonymous_student_id == '26262401c528d7c4a6bbeabe0455ec46' + assert block.runtime.anonymous_student_id == '26262401c528d7c4a6bbeabe0455ec46' @override_waffle_flag(INDIVIDUALIZE_ANONYMOUS_USER_ID, active=True) def test_anonymous_user_id_individual_per_course(self): """Test anonymous_user_id on a block which uses per-course anonymous IDs""" # Create the runtime with the flag turned on. - descriptor = BlockFactory(category="lti", parent=self.course) + block = BlockFactory(category="lti", parent=self.course) _prepare_runtime_for_preview( self.request, - block=descriptor, + block=block, field_data=mock.Mock(), ) - assert descriptor.runtime.anonymous_student_id == 'ad503f629b55c531fed2e45aa17a3368' + assert block.runtime.anonymous_student_id == 'ad503f629b55c531fed2e45aa17a3368' diff --git a/cms/djangoapps/contentstore/views/tests/test_transcripts.py b/cms/djangoapps/contentstore/views/tests/test_transcripts.py index 56391560e7..5d18c76cad 100644 --- a/cms/djangoapps/contentstore/views/tests/test_transcripts.py +++ b/cms/djangoapps/contentstore/views/tests/test_transcripts.py @@ -364,7 +364,7 @@ class TestUploadTranscripts(BaseTranscripts): def test_transcript_upload_with_non_existant_edx_video_id(self): """ Test that transcript upload works as expected if `edx_video_id` set on - video descriptor is different from `edx_video_id` received in POST request. + video block is different from `edx_video_id` received in POST request. """ non_existant_edx_video_id = '1111-2222-3333-4444' diff --git a/cms/djangoapps/contentstore/views/transcripts_ajax.py b/cms/djangoapps/contentstore/views/transcripts_ajax.py index 9c20420996..d8a091848f 100644 --- a/cms/djangoapps/contentstore/views/transcripts_ajax.py +++ b/cms/djangoapps/contentstore/views/transcripts_ajax.py @@ -71,7 +71,7 @@ def link_video_to_component(video_component, user): Links a VAL video to the video component. Arguments: - video_component: video descriptor item. + video_component: video block. user: A requesting user. Returns: @@ -134,7 +134,7 @@ def validate_video_block(request, locator): locator: video locator. Returns: - A tuple containing error(or None) and video descriptor(i.e. if validation succeeds). + A tuple containing error(or None) and video block(i.e. if validation succeeds). Raises: PermissionDenied: if requesting user does not have access to author the video component. diff --git a/cms/djangoapps/models/settings/course_grading.py b/cms/djangoapps/models/settings/course_grading.py index 6c72f3b9a6..e6a0e3690c 100644 --- a/cms/djangoapps/models/settings/course_grading.py +++ b/cms/djangoapps/models/settings/course_grading.py @@ -25,21 +25,21 @@ class CourseGradingModel: """ # Within this class, allow access to protected members of client classes. # This comes up when accessing kvs data and caches during kvs saves and modulestore writes. - def __init__(self, course_descriptor): + def __init__(self, course): self.graders = [ - CourseGradingModel.jsonize_grader(i, grader) for i, grader in enumerate(course_descriptor.raw_grader) + CourseGradingModel.jsonize_grader(i, grader) for i, grader in enumerate(course.raw_grader) ] # weights transformed to ints [0..100] - self.grade_cutoffs = course_descriptor.grade_cutoffs - self.grace_period = CourseGradingModel.convert_set_grace_period(course_descriptor) - self.minimum_grade_credit = course_descriptor.minimum_grade_credit + self.grade_cutoffs = course.grade_cutoffs + self.grace_period = CourseGradingModel.convert_set_grace_period(course) + self.minimum_grade_credit = course.minimum_grade_credit @classmethod def fetch(cls, course_key): """ Fetch the course grading policy for the given course from persistence and return a CourseGradingModel. """ - descriptor = modulestore().get_course(course_key) - model = cls(descriptor) + course = modulestore().get_course(course_key) + model = cls(course) return model @staticmethod @@ -48,10 +48,10 @@ class CourseGradingModel: Fetch the course's nth grader Returns an empty dict if there's no such grader. """ - descriptor = modulestore().get_course(course_key) + course = modulestore().get_course(course_key) index = int(index) - if len(descriptor.raw_grader) > index: - return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index]) + if len(course.raw_grader) > index: + return CourseGradingModel.jsonize_grader(index, course.raw_grader[index]) # return empty model else: @@ -69,27 +69,27 @@ class CourseGradingModel: Decode the json into CourseGradingModel and save any changes. Returns the modified model. Probably not the usual path for updates as it's too coarse grained. """ - descriptor = modulestore().get_course(course_key) - previous_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy)) + course = modulestore().get_course(course_key) + previous_grading_policy_hash = str(hash_grading_policy(course.grading_policy)) graders_parsed = [CourseGradingModel.parse_grader(jsonele) for jsonele in jsondict['graders']] fire_signal = CourseGradingModel.must_fire_grading_event_and_signal( course_key, graders_parsed, - descriptor, + course, jsondict ) - descriptor.raw_grader = graders_parsed - descriptor.grade_cutoffs = jsondict['grade_cutoffs'] + course.raw_grader = graders_parsed + course.grade_cutoffs = jsondict['grade_cutoffs'] - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) CourseGradingModel.update_grace_period_from_json(course_key, jsondict['grace_period'], user) CourseGradingModel.update_minimum_grade_credit_from_json(course_key, jsondict['minimum_grade_credit'], user) - descriptor = modulestore().get_course(course_key) - new_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy)) + course = modulestore().get_course(course_key) + new_grading_policy_hash = str(hash_grading_policy(course.grading_policy)) log.info( "Updated course grading policy for course %s from %s to %s. fire_signal = %s", str(course_key), @@ -153,28 +153,28 @@ class CourseGradingModel: Create or update the grader of the given type (string key) for the given course. Returns the modified grader which is a full model on the client but not on the server (just a dict) """ - descriptor = modulestore().get_course(course_key) - previous_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy)) + course = modulestore().get_course(course_key) + previous_grading_policy_hash = str(hash_grading_policy(course.grading_policy)) # parse removes the id; so, grab it before parse - index = int(grader.get('id', len(descriptor.raw_grader))) + index = int(grader.get('id', len(course.raw_grader))) grader = CourseGradingModel.parse_grader(grader) fire_signal = True - if index < len(descriptor.raw_grader): + if index < len(course.raw_grader): fire_signal = CourseGradingModel.must_fire_grading_event_and_signal_single_grader( course_key, grader, - descriptor.raw_grader[index] + course.raw_grader[index] ) - descriptor.raw_grader[index] = grader + course.raw_grader[index] = grader else: - descriptor.raw_grader.append(grader) + course.raw_grader.append(grader) - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) - descriptor = modulestore().get_course(course_key) - new_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy)) + course = modulestore().get_course(course_key) + new_grading_policy_hash = str(hash_grading_policy(course.grading_policy)) log.info( "Updated grader for course %s. Grading policy has changed from %s to %s. fire_signal = %s", str(course_key), @@ -185,7 +185,7 @@ class CourseGradingModel: if fire_signal: _grading_event_and_signal(course_key, user.id) - return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index]) + return CourseGradingModel.jsonize_grader(index, course.raw_grader[index]) @staticmethod def update_cutoffs_from_json(course_key, cutoffs, user): @@ -193,10 +193,10 @@ class CourseGradingModel: Create or update the grade cutoffs for the given course. Returns sent in cutoffs (ie., no extra db fetch). """ - descriptor = modulestore().get_course(course_key) - descriptor.grade_cutoffs = cutoffs + course = modulestore().get_course(course_key) + course.grade_cutoffs = cutoffs - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) _grading_event_and_signal(course_key, user.id) return cutoffs @@ -207,7 +207,7 @@ class CourseGradingModel: grace_period entry in an enclosing dict. It is also safe to call this method with a value of None for graceperiodjson. """ - descriptor = modulestore().get_course(course_key) + course = modulestore().get_course(course_key) # Before a graceperiod has ever been created, it will be None (once it has been # created, it cannot be set back to None). @@ -216,9 +216,9 @@ class CourseGradingModel: graceperiodjson = graceperiodjson['grace_period'] grace_timedelta = timedelta(**graceperiodjson) - descriptor.graceperiod = grace_timedelta + course.graceperiod = grace_timedelta - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) @staticmethod def update_minimum_grade_credit_from_json(course_key, minimum_grade_credit, user): @@ -230,29 +230,29 @@ class CourseGradingModel: user(User): The user object """ - descriptor = modulestore().get_course(course_key) + course = modulestore().get_course(course_key) # 'minimum_grade_credit' cannot be set to None if minimum_grade_credit is not None: minimum_grade_credit = minimum_grade_credit # lint-amnesty, pylint: disable=self-assigning-variable - descriptor.minimum_grade_credit = minimum_grade_credit - modulestore().update_item(descriptor, user.id) + course.minimum_grade_credit = minimum_grade_credit + modulestore().update_item(course, user.id) @staticmethod def delete_grader(course_key, index, user): """ Delete the grader of the given type from the given course. """ - descriptor = modulestore().get_course(course_key) + course = modulestore().get_course(course_key) index = int(index) - if index < len(descriptor.raw_grader): - del descriptor.raw_grader[index] + if index < len(course.raw_grader): + del course.raw_grader[index] # force propagation to definition - descriptor.raw_grader = descriptor.raw_grader + course.raw_grader = course.raw_grader - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) _grading_event_and_signal(course_key, user.id) @staticmethod @@ -260,37 +260,37 @@ class CourseGradingModel: """ Delete the course's grace period. """ - descriptor = modulestore().get_course(course_key) + course = modulestore().get_course(course_key) - del descriptor.graceperiod + del course.graceperiod - modulestore().update_item(descriptor, user.id) + modulestore().update_item(course, user.id) @staticmethod def get_section_grader_type(location): - descriptor = modulestore().get_item(location) + block = modulestore().get_item(location) return { - "graderType": descriptor.format if descriptor.format is not None else 'notgraded', + "graderType": block.format if block.format is not None else 'notgraded', "location": str(location), } @staticmethod - def update_section_grader_type(descriptor, grader_type, user): # lint-amnesty, pylint: disable=missing-function-docstring + def update_section_grader_type(block, grader_type, user): # lint-amnesty, pylint: disable=missing-function-docstring if grader_type is not None and grader_type != 'notgraded': - descriptor.format = grader_type - descriptor.graded = True + block.format = grader_type + block.graded = True else: - del descriptor.format - del descriptor.graded + del block.format + del block.graded - modulestore().update_item(descriptor, user.id) - _grading_event_and_signal(descriptor.location.course_key, user.id) + modulestore().update_item(block, user.id) + _grading_event_and_signal(block.location.course_key, user.id) return {'graderType': grader_type} @staticmethod - def convert_set_grace_period(descriptor): # lint-amnesty, pylint: disable=missing-function-docstring + def convert_set_grace_period(course): # lint-amnesty, pylint: disable=missing-function-docstring # 5 hours 59 minutes 59 seconds => converted to iso format - rawgrace = descriptor.graceperiod + rawgrace = course.graceperiod if rawgrace: hours_from_days = rawgrace.days * 24 seconds = rawgrace.seconds diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index c072a4ae55..f45130d9fa 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -155,14 +155,14 @@ class CourseMetadata: return exclude_list @classmethod - def fetch(cls, descriptor, filter_fields=None): + def fetch(cls, block, filter_fields=None): """ Fetch the key:value editable course details for the given course from persistence and return a CourseMetadata model. """ result = {} - metadata = cls.fetch_all(descriptor, filter_fields=filter_fields) - exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id) + metadata = cls.fetch_all(block, filter_fields=filter_fields) + exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id) for key, value in metadata.items(): if key in exclude_list_of_fields: @@ -171,12 +171,12 @@ class CourseMetadata: return result @classmethod - def fetch_all(cls, descriptor, filter_fields=None): + def fetch_all(cls, block, filter_fields=None): """ Fetches all key:value pairs from persistence and returns a CourseMetadata model. """ result = {} - for field in descriptor.fields.values(): + for field in block.fields.values(): if field.scope != Scope.settings: continue @@ -189,7 +189,7 @@ class CourseMetadata: field_help = field_help.format(**help_args) result[field.name] = { - 'value': field.read_json(descriptor), + 'value': field.read_json(block), 'display_name': _(field.display_name), # lint-amnesty, pylint: disable=translation-of-non-string 'help': field_help, 'deprecated': field.runtime_options.get('deprecated', False), @@ -198,13 +198,13 @@ class CourseMetadata: return result @classmethod - def update_from_json(cls, descriptor, jsondict, user, filter_tabs=True): + def update_from_json(cls, block, jsondict, user, filter_tabs=True): """ Decode the json into CourseMetadata and save any changed attrs to the db. Ensures none of the fields are in the exclude list. """ - exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id) + exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id) # Don't filter on the tab attribute if filter_tabs is False. if not filter_tabs: exclude_list_of_fields.remove("tabs") @@ -218,16 +218,16 @@ class CourseMetadata: continue try: val = model['value'] - if hasattr(descriptor, key) and getattr(descriptor, key) != val: - key_values[key] = descriptor.fields[key].from_json(val) + if hasattr(block, key) and getattr(block, key) != val: + key_values[key] = block.fields[key].from_json(val) except (TypeError, ValueError) as err: raise ValueError(_("Incorrect format for field '{name}'. {detailed_message}").format( # lint-amnesty, pylint: disable=raise-missing-from name=model['display_name'], detailed_message=str(err))) - return cls.update_from_dict(key_values, descriptor, user) + return cls.update_from_dict(key_values, block, user) @classmethod - def validate_and_update_from_json(cls, descriptor, jsondict, user, filter_tabs=True): + def validate_and_update_from_json(cls, block, jsondict, user, filter_tabs=True): """ Validate the values in the json dict (validated by xblock fields from_json method) @@ -240,7 +240,7 @@ class CourseMetadata: errors: list of error objects result: the updated course metadata or None if error """ - exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id) + exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id) if not filter_tabs: exclude_list_of_fields.remove("tabs") @@ -254,8 +254,8 @@ class CourseMetadata: for key, model in filtered_dict.items(): try: val = model['value'] - if hasattr(descriptor, key) and getattr(descriptor, key) != val: - key_values[key] = descriptor.fields[key].from_json(val) + if hasattr(block, key) and getattr(block, key) != val: + key_values[key] = block.fields[key].from_json(val) except (TypeError, ValueError, ValidationError) as err: did_validate = False errors.append({'key': key, 'message': str(err), 'model': model}) @@ -264,7 +264,7 @@ class CourseMetadata: # Because we cannot pass course context to the exception, we need to check if the LTI provider # should actually be available to the course err_message = str(err) - if not exams_ida_enabled(descriptor.id): + if not exams_ida_enabled(block.id): available_providers = get_available_providers() available_providers.remove('lti_external') err_message = str(InvalidProctoringProvider(val, available_providers)) @@ -277,29 +277,29 @@ class CourseMetadata: errors = errors + team_setting_errors did_validate = False - proctoring_errors = cls.validate_proctoring_settings(descriptor, filtered_dict, user) + proctoring_errors = cls.validate_proctoring_settings(block, filtered_dict, user) if proctoring_errors: errors = errors + proctoring_errors did_validate = False # If did validate, go ahead and update the metadata if did_validate: - updated_data = cls.update_from_dict(key_values, descriptor, user, save=False) + updated_data = cls.update_from_dict(key_values, block, user, save=False) return did_validate, errors, updated_data @classmethod - def update_from_dict(cls, key_values, descriptor, user, save=True): + def update_from_dict(cls, key_values, block, user, save=True): """ - Update metadata descriptor from key_values. Saves to modulestore if save is true. + Update metadata from key_values. Saves to modulestore if save is true. """ for key, value in key_values.items(): - setattr(descriptor, key, value) + setattr(block, key, value) if save and key_values: - modulestore().update_item(descriptor, user.id) + modulestore().update_item(block, user.id) - return cls.fetch(descriptor) + return cls.fetch(block) @classmethod def validate_team_settings(cls, settings_dict): @@ -397,7 +397,7 @@ class CourseMetadata: return None @classmethod - def validate_proctoring_settings(cls, descriptor, settings_dict, user): + def validate_proctoring_settings(cls, block, settings_dict, user): """ Verify proctoring settings @@ -412,9 +412,9 @@ class CourseMetadata: if ( not user.is_staff and cls._has_requested_proctoring_provider_changed( - descriptor.proctoring_provider, proctoring_provider_model.get('value') + block.proctoring_provider, proctoring_provider_model.get('value') ) and - datetime.now(pytz.UTC) > descriptor.start + datetime.now(pytz.UTC) > block.start ): message = ( 'The proctoring provider cannot be modified after a course has started.' @@ -426,7 +426,7 @@ class CourseMetadata: # should only be allowed if the exams IDA is enabled for a course available_providers = get_available_providers() updated_provider = settings_dict.get('proctoring_provider', {}).get('value') - if updated_provider == 'lti_external' and not exams_ida_enabled(descriptor.id): + if updated_provider == 'lti_external' and not exams_ida_enabled(block.id): available_providers.remove('lti_external') error = InvalidProctoringProvider('lti_external', available_providers) errors.append({'key': 'proctoring_provider', 'message': str(error), 'model': proctoring_provider_model}) @@ -435,7 +435,7 @@ class CourseMetadata: if enable_proctoring_model: enable_proctoring = enable_proctoring_model.get('value') else: - enable_proctoring = descriptor.enable_proctored_exams + enable_proctoring = block.enable_proctored_exams if enable_proctoring: # Require a valid escalation email if Proctortrack is chosen as the proctoring provider @@ -443,12 +443,12 @@ class CourseMetadata: if escalation_email_model: escalation_email = escalation_email_model.get('value') else: - escalation_email = descriptor.proctoring_escalation_email + escalation_email = block.proctoring_escalation_email if proctoring_provider_model: proctoring_provider = proctoring_provider_model.get('value') else: - proctoring_provider = descriptor.proctoring_provider + proctoring_provider = block.proctoring_provider missing_escalation_email_msg = 'Provider \'{provider}\' requires an exam escalation contact.' if proctoring_provider_model and proctoring_provider == 'proctortrack': @@ -477,7 +477,7 @@ class CourseMetadata: if zendesk_ticket_model: create_zendesk_tickets = zendesk_ticket_model.get('value') else: - create_zendesk_tickets = descriptor.create_zendesk_tickets + create_zendesk_tickets = block.create_zendesk_tickets if ( (proctoring_provider == 'proctortrack' and create_zendesk_tickets) @@ -489,7 +489,7 @@ class CourseMetadata: 'should be updated for this course.'.format( ticket_value=create_zendesk_tickets, provider=proctoring_provider, - course_id=descriptor.id + course_id=block.id ) )