diff --git a/cms/djangoapps/contentstore/core/course_optimizer_provider.py b/cms/djangoapps/contentstore/core/course_optimizer_provider.py index 134329992cfe..96b3614be084 100644 --- a/cms/djangoapps/contentstore/core/course_optimizer_provider.py +++ b/cms/djangoapps/contentstore/core/course_optimizer_provider.py @@ -249,7 +249,7 @@ def _update_node_tree_and_dictionary(block, link, link_state, node_tree, diction xblock_id, { 'display_name': xblock.display_name, - 'category': getattr(xblock, 'category', ''), + 'category': getattr(getattr(xblock, 'scope_ids', None), 'block_type', ''), } ) # Sets new current node and creates the node if it doesn't exist @@ -258,7 +258,7 @@ def _update_node_tree_and_dictionary(block, link, link_state, node_tree, diction # Add block-level details for the last xblock in the path (URL and broken/locked links) updated_dictionary[xblock_id].setdefault( 'url', - f'/course/{block.course_id}/editor/{block.category}/{block.location}' + f'/course/{block.course_id}/editor/{block.scope_ids.block_type}/{block.location}' ) # The link_state == True condition is maintained for backward compatibility. diff --git a/cms/djangoapps/contentstore/courseware_index.py b/cms/djangoapps/contentstore/courseware_index.py index b7b74992035a..ef0f8c6c4377 100644 --- a/cms/djangoapps/contentstore/courseware_index.py +++ b/cms/djangoapps/contentstore/courseware_index.py @@ -185,7 +185,7 @@ def prepare_item_index(item, skip_index=False, groups_usage_info=None): item_content_groups = None - if item.category == "split_test": # lint-amnesty, pylint: disable=too-many-nested-blocks + if item.scope_ids.block_type == "split_test": # lint-amnesty, pylint: disable=too-many-nested-blocks split_partition = item.get_selected_partition() for split_test_child in item.get_children(): if split_partition: diff --git a/cms/djangoapps/contentstore/helpers.py b/cms/djangoapps/contentstore/helpers.py index 9bfab1f1f385..687117f1346d 100644 --- a/cms/djangoapps/contentstore/helpers.py +++ b/cms/djangoapps/contentstore/helpers.py @@ -82,10 +82,10 @@ def is_unit(xblock, parent_xblock=None): Returns true if the specified xblock is a vertical that is treated as a unit. A unit is a vertical that is a direct child of a sequential (aka a subsection). """ - if xblock.category == 'vertical': + if xblock.scope_ids.block_type == 'vertical': if parent_xblock is None: parent_xblock = get_parent_xblock(xblock) - parent_category = parent_xblock.category if parent_xblock else None + parent_category = parent_xblock.scope_ids.block_type if parent_xblock else None return parent_category == 'sequential' return False @@ -94,7 +94,7 @@ def is_library_content(xblock): """ Returns true if the specified xblock is library content. """ - return xblock.category == 'library_content' + return xblock.scope_ids.block_type == 'library_content' def get_parent_if_split_test(xblock): @@ -102,7 +102,7 @@ def get_parent_if_split_test(xblock): Returns the parent of the specified xblock if it is a split test, otherwise returns None. """ parent_xblock = get_parent_xblock(xblock) - if parent_xblock and parent_xblock.category == 'split_test': + if parent_xblock and parent_xblock.scope_ids.block_type == 'split_test': return parent_xblock @@ -117,7 +117,7 @@ def xblock_has_own_studio_page(xblock, parent_xblock=None): - a direct child of a unit 3. XBlocks that support children """ - category = xblock.category + category = xblock.scope_ids.block_type if is_unit(xblock, parent_xblock): return True @@ -151,7 +151,7 @@ def xblock_studio_url(xblock, parent_xblock=None, find_parent=False): return None else: return None - category = xblock.category + category = xblock.scope_ids.block_type if category == 'course': return reverse_course_url('course_handler', xblock.location.course_key) elif category in ('chapter', 'sequential'): @@ -204,8 +204,8 @@ def xblock_type_display_name(xblock, default_display_name=None): :return: """ - if hasattr(xblock, 'category'): - category = xblock.category + if hasattr(xblock, 'scope_ids'): + category = xblock.scope_ids.block_type if category == 'vertical' and not is_unit(xblock): return _('Vertical') else: @@ -238,7 +238,7 @@ def xblock_primary_child_category(xblock): """ Returns the primary child category for the specified xblock, or None if there is not a primary category. """ - category = xblock.category + category = xblock.scope_ids.block_type if category == 'course': return 'chapter' elif category == 'chapter': diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index 983471e1ebef..90619aa6db12 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -1256,7 +1256,7 @@ def _scan_course_for_links(course_key): for block in blocks: # Excluding 'drag-and-drop-v2' as it contains data of object type instead of string, causing errors, # and it doesn't contain user-facing links to scan. - if block.category == 'drag-and-drop-v2': + if block.scope_ids.block_type == 'drag-and-drop-v2': continue block_id = str(block.location) block_info = get_block_info(block) diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index f40fad42152c..d635bba5f5e4 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -634,7 +634,7 @@ def find_release_date_source(xblock): """ # Stop searching at the section level - if xblock.category == 'chapter': + if xblock.scope_ids.block_type == 'chapter': return xblock parent_location = modulestore().get_parent_location(xblock.location, @@ -661,7 +661,7 @@ def find_staff_lock_source(xblock): return xblock # Stop searching at the section level - if xblock.category == 'chapter': + if xblock.scope_ids.block_type == 'chapter': return None parent_location = modulestore().get_parent_location(xblock.location, @@ -1276,13 +1276,13 @@ def gather_block_attributes(source_item, display_name=None, is_child=False): duplicate_metadata[field.name] = field.read_from(source_item) if is_child: - display_name = display_name or source_item.display_name or source_item.category + display_name = display_name or source_item.display_name or source_item.scope_ids.block_type if display_name is not None: duplicate_metadata['display_name'] = display_name else: if source_item.display_name is None: - duplicate_metadata['display_name'] = _("Duplicate of {0}").format(source_item.category) + duplicate_metadata['display_name'] = _("Duplicate of {0}").format(source_item.scope_ids.block_type) else: duplicate_metadata['display_name'] = _("Duplicate of '{0}'").format(source_item.display_name) diff --git a/cms/djangoapps/contentstore/views/block.py b/cms/djangoapps/contentstore/views/block.py index b57042085df2..829a00cd4e5e 100644 --- a/cms/djangoapps/contentstore/views/block.py +++ b/cms/djangoapps/contentstore/views/block.py @@ -363,8 +363,7 @@ def xblock_outline_handler(request, usage_key_string): root_xblock, include_child_info=True, course_outline=True, - include_children_predicate=lambda xblock: not xblock.category - == "vertical", + include_children_predicate=lambda xblock: xblock.scope_ids.block_type != "vertical", ) ) else: diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 115aea1a83aa..4fd091fa2b1a 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -365,7 +365,7 @@ def _course_outline_json(request, course_block): Returns a JSON representation of the course block and recursively all of its children. """ is_concise = request.GET.get('format') == 'concise' - include_children_predicate = lambda xblock: not xblock.category == 'vertical' + include_children_predicate = lambda xblock: xblock.scope_ids.block_type != 'vertical' if is_concise: include_children_predicate = lambda xblock: xblock.has_children return create_xblock_info( diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 6600430a7a89..8137fee4bf93 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -163,7 +163,7 @@ def _prepare_runtime_for_preview(request, block): """ course_id = block.location.course_key - display_name_only = (block.category == 'static_tab') + display_name_only = (block.scope_ids.block_type == 'static_tab') wrappers = [ # This wrapper wraps the block in the template specified above @@ -337,7 +337,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False): can_move = False if upstream_link.error_message is None and upstream_link.upstream_ref: - can_edit = xblock.category in editable_library_components + can_edit = xblock.scope_ids.block_type in editable_library_components # Is this a course or a library? is_course = xblock.context_key.is_course diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py index 31f5244e2f55..2ddde030a2d4 100644 --- a/cms/djangoapps/contentstore/views/tests/test_block.py +++ b/cms/djangoapps/contentstore/views/tests/test_block.py @@ -528,7 +528,7 @@ def assert_xblock_info(xblock, xblock_info): """ self.assertEqual(str(xblock.location), xblock_info["id"]) self.assertEqual(xblock.display_name, xblock_info["display_name"]) - self.assertEqual(xblock.category, xblock_info["category"]) + self.assertEqual(xblock.scope_ids.block_type, xblock_info["category"]) for usage_key in ( problem_usage_key, @@ -727,7 +727,7 @@ def _verify_duplicate_display_name( """ if is_child: if original_item.display_name is None: - return duplicated_item.display_name == original_item.category + return duplicated_item.display_name == original_item.scope_ids.block_type return duplicated_item.display_name == original_item.display_name if original_item.display_name is not None: return ( @@ -737,7 +737,7 @@ def _verify_duplicate_display_name( ) ) return duplicated_item.display_name == "Duplicate of {display_name}".format( - display_name=original_item.category + display_name=original_item.scope_ids.block_type ) def _duplicate_item(self, parent_usage_key, source_usage_key, display_name=None): @@ -2469,8 +2469,8 @@ def test_create_groups(self): self.assertEqual(2, len(split_test.children)) vertical_0 = self.get_item_from_modulestore(split_test.children[0]) vertical_1 = self.get_item_from_modulestore(split_test.children[1]) - self.assertEqual("vertical", vertical_0.category) - self.assertEqual("vertical", vertical_1.category) + self.assertEqual("vertical", vertical_0.scope_ids.block_type) + self.assertEqual("vertical", vertical_1.scope_ids.block_type) self.assertEqual( "Group ID " + str(MINIMUM_UNUSED_PARTITION_ID + 1), vertical_0.display_name ) @@ -3836,7 +3836,7 @@ def validate_component_xblock_info(self, xblock_info, original_block): """ Validate that the xblock info is correct for the test component. """ - self.assertEqual(xblock_info["category"], original_block.category) + self.assertEqual(xblock_info["category"], original_block.scope_ids.block_type) self.assertEqual(xblock_info["id"], str(original_block.location)) self.assertEqual(xblock_info["display_name"], original_block.display_name) self.assertIsNone(xblock_info.get("has_changes", None)) diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index 58c425c601a3..4ae2dc23a4b3 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -115,7 +115,7 @@ def test_course_outline_initial_state(self): course_structure = create_xblock_info( course_block, include_child_info=True, - include_children_predicate=lambda xblock: not xblock.category == 'vertical' + include_children_predicate=lambda xblock: xblock.scope_ids.block_type != 'vertical' ) # Verify that None is returned for a non-existent locator diff --git a/cms/djangoapps/contentstore/views/transcripts_ajax.py b/cms/djangoapps/contentstore/views/transcripts_ajax.py index 7d4eccf7000b..70d35b885a82 100644 --- a/cms/djangoapps/contentstore/views/transcripts_ajax.py +++ b/cms/djangoapps/contentstore/views/transcripts_ajax.py @@ -191,7 +191,7 @@ def validate_video_block(request, locator): error, item = None, None try: item = _get_item(request, {'locator': locator}) - if item.category != 'video': + if item.scope_ids.block_type != 'video': error = _('Transcripts are supported only for "video" blocks.') except (InvalidKeyError, ItemNotFoundError): @@ -523,7 +523,7 @@ def _validate_transcripts_data(request): except (InvalidKeyError, ItemNotFoundError): raise TranscriptsRequestValidationException(_("Can't find item by locator.")) # lint-amnesty, pylint: disable=raise-missing-from - if item.category != 'video': + if item.scope_ids.block_type != 'video': raise TranscriptsRequestValidationException(_('Transcripts are supported only for "video" blocks.')) # parse data form request.GET.['data']['video'] to useful format diff --git a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py index 9a30807b60a3..98d16392c531 100644 --- a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py +++ b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py @@ -579,7 +579,7 @@ def _save_xblock( ) # Save gating info - if xblock.category == "sequential" and course.enable_subsection_gating: + if xblock.scope_ids.block_type == "sequential" and course.enable_subsection_gating: if is_prereq is not None: if is_prereq: gating_api.add_prerequisite( @@ -601,7 +601,7 @@ def _save_xblock( # If publish is set to 'republish' and this item is not in direct only categories and has previously been # published, then this item should be republished. This is used by staff locking to ensure that changing the # draft value of the staff lock will also update the published version, but only at the unit level. - if publish == "republish" and xblock.category not in DIRECT_ONLY_CATEGORIES: + if publish == "republish" and xblock.scope_ids.block_type not in DIRECT_ONLY_CATEGORIES: if modulestore().has_published_version(xblock): publish = "make_public" @@ -610,7 +610,7 @@ def _save_xblock( modulestore().publish(xblock.location, user.id) # If summary_configuration_enabled is not None, use AIAsideSummary to update it. - if xblock.category == "vertical" and summary_configuration_enabled is not None: + if xblock.scope_ids.block_type == "vertical" and summary_configuration_enabled is not None: AiAsideSummaryConfig(course.id).set_summary_settings(xblock.location, { 'enabled': summary_configuration_enabled }) @@ -883,8 +883,8 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non source_item = store.get_item(source_usage_key) source_parent = source_item.get_parent() target_parent = store.get_item(target_parent_usage_key) - source_type = source_item.category - target_parent_type = target_parent.category + source_type = source_item.scope_ids.block_type + target_parent_type = target_parent.scope_ids.block_type error = None # Store actual/initial index of the source item. This would be sent back with response, @@ -1102,7 +1102,7 @@ def _get_gating_info(course, xblock): dict: Gating information """ info = {} - if xblock.category == "sequential" and course.enable_subsection_gating: + if xblock.scope_ids.block_type == "sequential" and course.enable_subsection_gating: if not hasattr(course, "gating_prerequisites"): # Cache gating prerequisites on course block so that we are not # hitting the database for every xblock in the course @@ -1211,7 +1211,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements release_date = _get_release_date(xblock, user) - if xblock.category != "course" and not is_concise: + if xblock.scope_ids.block_type != "course" and not is_concise: visibility_state = _compute_visibility_state( xblock, child_info, is_xblock_unit and has_changes, is_self_paced(course) ) @@ -1240,7 +1240,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements explanatory_message = None # is_entrance_exam is inherited metadata. - if xblock.category == "chapter" and getattr(xblock, "is_entrance_exam", None): + if xblock.scope_ids.block_type == "chapter" and getattr(xblock, "is_entrance_exam", None): # Entrance exam section should not be deletable, draggable and not have 'New Subsection' button. xblock_actions["deletable"] = xblock_actions["childAddable"] = xblock_actions[ "draggable" @@ -1263,7 +1263,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements xblock_info = { "id": str(xblock.location), "display_name": xblock.display_name_with_default, - "category": xblock.category, + "category": xblock.scope_ids.block_type, "has_children": xblock.has_children, } @@ -1278,7 +1278,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements } ) - if xblock.category == "course": + if xblock.scope_ids.block_type == "course": discussions_config = DiscussionsConfiguration.get(course.id) show_unit_level_discussions_toggle = ( discussions_config.enabled @@ -1336,20 +1336,20 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements } ) - if xblock.category == "sequential": + if xblock.scope_ids.block_type == "sequential": xblock_info.update( { "hide_after_due": xblock.hide_after_due, } ) - elif xblock.category in ("chapter", "course"): - if xblock.category == "chapter": + elif xblock.scope_ids.block_type in ("chapter", "course"): + if xblock.scope_ids.block_type == "chapter": xblock_info.update( { "highlights": xblock.highlights, } ) - elif xblock.category == "course": + elif xblock.scope_ids.block_type == "course": xblock_info.update( { "highlights_enabled_for_messaging": course.highlights_enabled_for_messaging, @@ -1370,7 +1370,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements # update xblock_info with special exam information if the feature flag is enabled if settings.FEATURES.get("ENABLE_SPECIAL_EXAMS"): - if xblock.category == "course": + if xblock.scope_ids.block_type == "course": xblock_info.update( { "enable_proctored_exams": xblock.enable_proctored_exams, @@ -1378,7 +1378,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements "enable_timed_exams": xblock.enable_timed_exams, } ) - elif xblock.category == "sequential": + elif xblock.scope_ids.block_type == "sequential": rules_url = settings.PROCTORING_SETTINGS.get("LINK_URLS", {}).get( "online_proctoring_rules", "" ) @@ -1441,7 +1441,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements # if xblock is a Unit we add the discussion_enabled option xblock_info["discussion_enabled"] = xblock.discussion_enabled - if xblock.category == "sequential": + if xblock.scope_ids.block_type == "sequential": # Entrance exam subsection should be hidden. in_entrance_exam is # inherited metadata, all children will have it. if getattr(xblock, "in_entrance_exam", False): diff --git a/lms/djangoapps/ccx/views.py b/lms/djangoapps/ccx/views.py index 7c6a75aaf6d4..1e280db6a7ae 100644 --- a/lms/djangoapps/ccx/views.py +++ b/lms/djangoapps/ccx/views.py @@ -415,7 +415,7 @@ def visit(node, depth=1): visited = { 'location': str(child.location), 'display_name': child.display_name, - 'category': child.category, + 'category': child.scope_ids.block_type, 'start': start, 'due': due, 'hidden': hidden, @@ -424,7 +424,7 @@ def visit(node, depth=1): visited = { 'location': str(child.location), 'display_name': child.display_name, - 'category': child.category, + 'category': child.scope_ids.block_type, 'start': start, 'hidden': hidden, } diff --git a/lms/djangoapps/course_api/blocks/tests/test_views.py b/lms/djangoapps/course_api/blocks/tests/test_views.py index f577762ad70f..909a7b372ed4 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_views.py +++ b/lms/djangoapps/course_api/blocks/tests/test_views.py @@ -51,7 +51,7 @@ def setUpClass(cls): str(item.location) for item in cls.store.get_items(cls.course_key) # remove all orphaned items in the course, except for the root 'course' block - if cls.store.get_parent_location(item.location) or item.category == 'course' + if cls.store.get_parent_location(item.location) or item.scope_ids.block_type == 'course' } def setUp(self): @@ -421,7 +421,7 @@ def setUp(self): item.location for item in self.store.get_items(self.course_key) # remove all orphaned items in the course, except for the root 'course' block - if self.store.get_parent_location(item.location) or item.category == 'course' + if self.store.get_parent_location(item.location) or item.scope_ids.block_type == 'course' } def test_no_course_id(self): @@ -441,7 +441,7 @@ def test_non_existent_course_anonymous(self): def test_completion_one_unit(self): for item in self.store.get_items(self.course_key): - if item.category == 'html': + if item.scope_ids.block_type == 'html': block_usage_key = item.location break @@ -568,7 +568,7 @@ def setUpClass(cls): str(item.location) for item in cls.store.get_items(cls.course_key) # remove all orphaned items in the course, except for the root 'course' block - if cls.store.get_parent_location(item.location) or item.category == 'course' + if cls.store.get_parent_location(item.location) or item.scope_ids.block_type == 'course' } def setUp(self): diff --git a/lms/djangoapps/course_api/blocks/transformers/block_counts.py b/lms/djangoapps/course_api/blocks/transformers/block_counts.py index b2eebbcbb3ed..7fd42ab9fa98 100644 --- a/lms/djangoapps/course_api/blocks/transformers/block_counts.py +++ b/lms/djangoapps/course_api/blocks/transformers/block_counts.py @@ -28,7 +28,7 @@ def collect(cls, block_structure): transform method. """ # collect basic xblock fields - block_structure.request_xblock_fields('category') + # Note: block type is available via xblock.scope_ids.block_type (not an XBlock Field). def transform(self, usage_info, block_structure): """ @@ -49,6 +49,6 @@ def transform(self, usage_info, block_structure): block_type, ( descendants_type_count + - (1 if (block_structure.get_xblock_field(block_key, 'category') == block_type) else 0) + (1 if (block_structure.get_xblock(block_key).scope_ids.block_type == block_type) else 0) ) ) diff --git a/lms/djangoapps/course_api/blocks/transformers/student_view.py b/lms/djangoapps/course_api/blocks/transformers/student_view.py index b677bd66f5b6..c37ddc66acef 100644 --- a/lms/djangoapps/course_api/blocks/transformers/student_view.py +++ b/lms/djangoapps/course_api/blocks/transformers/student_view.py @@ -28,8 +28,6 @@ def collect(cls, block_structure): Collect student_view_multi_device and student_view_data values for each block """ # collect basic xblock fields - block_structure.request_xblock_fields('category') - for block_key in block_structure.topological_traversal(): block = block_structure.get_xblock(block_key) @@ -73,5 +71,5 @@ def transform(self, usage_info, block_structure): Mutates block_structure based on the given usage_info. """ for block_key in block_structure.post_order_traversal(): - if block_structure.get_xblock_field(block_key, 'category') not in self.requested_student_view_data: + if block_structure.get_xblock(block_key).scope_ids.block_type not in self.requested_student_view_data: block_structure.remove_transformer_block_field(block_key, self, self.STUDENT_VIEW_DATA) diff --git a/lms/djangoapps/course_blocks/transformers/library_content.py b/lms/djangoapps/course_blocks/transformers/library_content.py index e7f2258483bc..b51481836244 100644 --- a/lms/djangoapps/course_blocks/transformers/library_content.py +++ b/lms/djangoapps/course_blocks/transformers/library_content.py @@ -48,7 +48,6 @@ def collect(cls, block_structure): transformer's transform method. """ block_structure.request_xblock_fields('max_count') - block_structure.request_xblock_fields('category') store = modulestore() # needed for analytics purposes diff --git a/lms/djangoapps/courseware/self_paced_overrides.py b/lms/djangoapps/courseware/self_paced_overrides.py index 8eddd0e529dd..4faf85562145 100644 --- a/lms/djangoapps/courseware/self_paced_overrides.py +++ b/lms/djangoapps/courseware/self_paced_overrides.py @@ -17,7 +17,7 @@ def get(self, block, name, default): if name == 'due': return None # Remove release dates for course content - if name == 'start' and block.category != 'course': + if name == 'start' and block.scope_ids.block_type != 'course': return None return default diff --git a/lms/djangoapps/courseware/tests/test_lti_integration.py b/lms/djangoapps/courseware/tests/test_lti_integration.py index bdaf1a13549b..4df5348aa1af 100644 --- a/lms/djangoapps/courseware/tests/test_lti_integration.py +++ b/lms/djangoapps/courseware/tests/test_lti_integration.py @@ -90,7 +90,7 @@ def setUp(self): self.expected_context = { 'display_name': self.block.display_name, 'input_fields': self.correct_headers, - 'element_class': self.block.category, + 'element_class': self.block.scope_ids.block_type, 'element_id': self.block.location.html_id(), 'launch_url': 'http://www.example.com', # default value 'open_in_a_new_page': True, diff --git a/lms/djangoapps/edxnotes/helpers.py b/lms/djangoapps/edxnotes/helpers.py index 5e68ae4b2003..aeb8bf16fd25 100644 --- a/lms/djangoapps/edxnotes/helpers.py +++ b/lms/djangoapps/edxnotes/helpers.py @@ -251,15 +251,15 @@ def get_block_context(course, block): 'location': str(block.location), 'display_name': Text(block.display_name_with_default), } - if block.category == 'chapter' and block.get_parent(): + if block.scope_ids.block_type == 'chapter' and block.get_parent(): # course is a locator w/o branch and version # so for uniformity we replace it with one that has them course = block.get_parent() block_dict['index'] = get_index(block_dict['location'], course.children) - elif block.category == 'vertical': + elif block.scope_ids.block_type == 'vertical': # Use the MFE-aware URL generator instead of always using the legacy URL format block_dict['url'] = get_courseware_url(block.location) - if block.category in ('chapter', 'sequential'): + if block.scope_ids.block_type in ('chapter', 'sequential'): block_dict['children'] = [str(child) for child in block.children] return block_dict diff --git a/lms/djangoapps/grades/rest_api/v1/views.py b/lms/djangoapps/grades/rest_api/v1/views.py index b6835fd61ec4..1e76052d16bd 100644 --- a/lms/djangoapps/grades/rest_api/v1/views.py +++ b/lms/djangoapps/grades/rest_api/v1/views.py @@ -471,7 +471,7 @@ def get_problem_blocks(course): for subsection in section.get_children(): for vertical in subsection.get_children(): for block in vertical.get_children(): - if block.category == 'problem' and getattr(block, 'has_score', False): + if block.scope_ids.block_type == 'problem' and getattr(block, 'has_score', False): blocks.append(block) return blocks diff --git a/lms/lib/utils.py b/lms/lib/utils.py index 932873378247..3abd665b94ce 100644 --- a/lms/lib/utils.py +++ b/lms/lib/utils.py @@ -25,7 +25,7 @@ def get_parent_unit(xblock): grandparent = parent.get_parent() if grandparent is None: return None - if parent.category == "vertical" and grandparent.category == "sequential": + if parent.scope_ids.block_type == "vertical" and grandparent.scope_ids.block_type == "sequential": return parent xblock = parent diff --git a/openedx/core/djangoapps/content_tagging/helpers/objecttag_export_helpers.py b/openedx/core/djangoapps/content_tagging/helpers/objecttag_export_helpers.py index cb7865e136c6..e5d78c16e53b 100644 --- a/openedx/core/djangoapps/content_tagging/helpers/objecttag_export_helpers.py +++ b/openedx/core/djangoapps/content_tagging/helpers/objecttag_export_helpers.py @@ -58,7 +58,7 @@ def _get_course_tagged_object_and_children( tagged_course = TaggedContent( display_name=course.display_name_with_default, block_id=course_id, - category=course.category, + category=course.scope_ids.block_type, object_tags=object_tag_cache.get(course_id, {}), children=None, ) @@ -107,7 +107,7 @@ def _get_xblock_tagged_object_and_children( tagged_block = TaggedContent( display_name=block.display_name_with_default, block_id=block_id, - category=block.category, + category=block.scope_ids.block_type, object_tags=object_tag_cache.get(block_id, {}), children=None, ) diff --git a/openedx/core/djangoapps/course_date_signals/handlers.py b/openedx/core/djangoapps/course_date_signals/handlers.py index 6f3f4ed9a713..4a22d62d5980 100644 --- a/openedx/core/djangoapps/course_date_signals/handlers.py +++ b/openedx/core/djangoapps/course_date_signals/handlers.py @@ -67,7 +67,7 @@ def _gather_graded_items(root, due): # lint-amnesty, pylint: disable=missing-fu # Open response assessments (ORA) contain their own set of due dates # and we do not want to potentially conflict with due dates that are set from Studio. # So here we do not assign a due date to items that are ORA. - if next_item.category != 'openassessment': + if next_item.scope_ids.block_type != 'openassessment': collected_items.append(( next_item.location, {'due': due if _has_assignment_blocks(next_item) else None} @@ -76,7 +76,7 @@ def _gather_graded_items(root, due): # lint-amnesty, pylint: disable=missing-fu # especially if we find ourselves needing more exceptions. has_non_ora_scored_content = ( has_non_ora_scored_content or - (next_item.has_score and next_item.category != 'openassessment') + (next_item.has_score and next_item.scope_ids.block_type != 'openassessment') ) items.extend(next_item.get_children()) @@ -96,11 +96,11 @@ def _get_custom_pacing_children(subsection, num_weeks): section_date_items = [] while items: next_item = items.pop() - is_problem = next_item.category not in {'sequential', 'vertical'} + is_problem = next_item.scope_ids.block_type not in {'sequential', 'vertical'} if is_problem: has_content = True # Open response assessment problems have their own due dates - if next_item.category != 'openassessment': + if next_item.scope_ids.block_type != 'openassessment': section_date_items.append((next_item.location, {'due': timedelta(weeks=num_weeks)})) items.extend(next_item.get_children()) if is_problem: diff --git a/openedx/features/content_type_gating/tests/test_access.py b/openedx/features/content_type_gating/tests/test_access.py index 3e3dae129801..1c2505b99126 100644 --- a/openedx/features/content_type_gating/tests/test_access.py +++ b/openedx/features/content_type_gating/tests/test_access.py @@ -142,7 +142,7 @@ def _assert_block_is_gated(store, block, is_gated, user, course, request_factory assert 'student_view_url' in course_api_block else: assert 'authorization_denial_reason' not in course_api_block - if block.category == 'html': + if block.scope_ids.block_type == 'html': assert 'student_view_data' in course_api_block diff --git a/openedx/features/course_duration_limits/access.py b/openedx/features/course_duration_limits/access.py index 8f73ae2266ea..49516bdaa52c 100644 --- a/openedx/features/course_duration_limits/access.py +++ b/openedx/features/course_duration_limits/access.py @@ -245,7 +245,7 @@ def course_expiration_wrapper(user, block, view, frag, context): # pylint: disa if context.get('is_mobile_app'): return frag - if block.category != 'vertical': + if block.scope_ids.block_type != 'vertical': return frag course_expiration_fragment = generate_course_expired_fragment_from_key( diff --git a/openedx/features/discounts/utils.py b/openedx/features/discounts/utils.py index 00b60f6cadfd..2166e4d88fcf 100644 --- a/openedx/features/discounts/utils.py +++ b/openedx/features/discounts/utils.py @@ -28,7 +28,7 @@ def offer_banner_wrapper(user, block, view, frag, context): # pylint: disable=W A wrapper that prepends the First Purchase Discount banner if the user hasn't upgraded yet. """ - if block.category != 'vertical': + if block.scope_ids.block_type != 'vertical': return frag offer_banner_fragment = None diff --git a/openedx/features/effort_estimation/block_transformers.py b/openedx/features/effort_estimation/block_transformers.py index c3e6bce1f80e..94d7577ca259 100644 --- a/openedx/features/effort_estimation/block_transformers.py +++ b/openedx/features/effort_estimation/block_transformers.py @@ -67,7 +67,6 @@ def collect(cls, block_structure): Pooling leaf estimates higher up the tree (e.g. in verticals, then sequentials, then chapters) is done by transform() below at run time, because which blocks each user sees can be different. """ - block_structure.request_xblock_fields('category') block_structure.request_xblock_fields('global_speed', 'only_on_web') # video fields collection_cache = {} # collection methods can stuff some temporary data here @@ -81,8 +80,8 @@ def collect(cls, block_structure): for block_key in block_structure.topological_traversal(): xblock = block_structure.get_xblock(block_key) - if xblock.category in collections: - collections[xblock.category](block_structure, block_key, xblock, collection_cache) + if xblock.scope_ids.block_type in collections: + collections[xblock.scope_ids.block_type](block_structure, block_key, xblock, collection_cache) except cls.MissingEstimationData: # Some bit of required data is missing. Likely some duration info is missing from the video pipeline. diff --git a/openedx/features/personalized_learner_schedules/call_to_action.py b/openedx/features/personalized_learner_schedules/call_to_action.py index 634c425b38bd..1ec667d51623 100644 --- a/openedx/features/personalized_learner_schedules/call_to_action.py +++ b/openedx/features/personalized_learner_schedules/call_to_action.py @@ -167,7 +167,7 @@ def _make_deadlines_cta(cls, course_key, xblock, category, is_learning_mfe=False }, 'research_event_data': { 'block_id': str(xblock.location), - 'location': f'{xblock.category}-view', + 'location': f'{xblock.scope_ids.block_type}-view', }, } diff --git a/xmodule/capa_block.py b/xmodule/capa_block.py index 55c593f4772f..ffea9318fff5 100644 --- a/xmodule/capa_block.py +++ b/xmodule/capa_block.py @@ -685,7 +685,7 @@ def generate_report_data(self, user_state_iterator, limit_responses=None): "Answer ID": "98e6a8e915904d5389821a94e48babcf_10_1" }) """ - if self.category != "problem": + if self.scope_ids.block_type != "problem": raise NotImplementedError() if limit_responses == 0: diff --git a/xmodule/html_block.py b/xmodule/html_block.py index 27a2b51e4a31..51cdd587a9ba 100644 --- a/xmodule/html_block.py +++ b/xmodule/html_block.py @@ -323,7 +323,7 @@ def definition_to_xml(self, resource_fs): # Write html to file, return an empty tag pathname = name_to_pathname(self.url_name) filepath = '{category}/{pathname}.html'.format( - category=self.category, + category=self.scope_ids.block_type, pathname=pathname ) diff --git a/xmodule/library_content_block.py b/xmodule/library_content_block.py index 6f583d8beff2..6ef812a63113 100644 --- a/xmodule/library_content_block.py +++ b/xmodule/library_content_block.py @@ -179,7 +179,7 @@ def author_view(self, context): context['is_loading'] = is_updating # The following JS is used to make the "Update now" button work on the unit page and the container view: - if root_xblock and 'library' in root_xblock.category: + if root_xblock and 'library' in root_xblock.scope_ids.block_type: if root_xblock.source_library_id and len(root_xblock.children) > 0: fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_content_edit.js')) else: diff --git a/xmodule/lti_block.py b/xmodule/lti_block.py index 2984cb09d88a..c73c8b6291b7 100644 --- a/xmodule/lti_block.py +++ b/xmodule/lti_block.py @@ -508,7 +508,7 @@ def get_context(self): # These parameters do not participate in OAuth signing. 'launch_url': self.launch_url.strip(), 'element_id': self.location.html_id(), - 'element_class': self.category, + 'element_class': self.scope_ids.block_type, 'open_in_a_new_page': self.open_in_a_new_page, 'display_name': self.display_name, 'form_url': self.runtime.handler_url(self, 'preview_handler').rstrip('/?'), diff --git a/xmodule/modulestore/split_mongo/split.py b/xmodule/modulestore/split_mongo/split.py index aeacac25333e..adf3bf5507a7 100644 --- a/xmodule/modulestore/split_mongo/split.py +++ b/xmodule/modulestore/split_mongo/split.py @@ -2160,12 +2160,12 @@ def persist_xblock_dag(self, xblock, user_id, force=False): def _persist_subdag(self, course_key, xblock, user_id, structure_blocks, new_id): # lint-amnesty, pylint: disable=missing-function-docstring # persist the definition if persisted != passed partitioned_fields = self.partition_xblock_fields_by_scope(xblock) - new_def_data = self._serialize_fields(xblock.category, partitioned_fields[Scope.content]) + new_def_data = self._serialize_fields(xblock.scope_ids.block_type, partitioned_fields[Scope.content]) is_updated = False current_definition_locator = getattr(xblock, "definition_locator", xblock.scope_ids.def_id) if current_definition_locator is None or isinstance(current_definition_locator.definition_id, LocalId): xblock.definition_locator = self.create_definition_from_data( - course_key, new_def_data, xblock.category, user_id + course_key, new_def_data, xblock.scope_ids.block_type, user_id ) is_updated = True elif new_def_data: @@ -2200,7 +2200,7 @@ def _persist_subdag(self, course_key, xblock, user_id, structure_blocks, new_id) is_updated = is_updated or structure_blocks[block_key].fields['children'] != children block_fields = partitioned_fields[Scope.settings] - block_fields = self._serialize_fields(xblock.category, block_fields) + block_fields = self._serialize_fields(xblock.scope_ids.block_type, block_fields) if not is_new and not is_updated: is_updated = self._compare_settings(block_fields, structure_blocks[block_key].fields) if children: @@ -2210,7 +2210,7 @@ def _persist_subdag(self, course_key, xblock, user_id, structure_blocks, new_id) if is_new: block_info = self._new_block( user_id, - xblock.category, + xblock.scope_ids.block_type, block_fields, xblock.definition_locator.definition_id, new_id, diff --git a/xmodule/modulestore/tests/test_split_copy_from_template.py b/xmodule/modulestore/tests/test_split_copy_from_template.py index 3fc0bcd85a72..153c35686242 100644 --- a/xmodule/modulestore/tests/test_split_copy_from_template.py +++ b/xmodule/modulestore/tests/test_split_copy_from_template.py @@ -154,7 +154,7 @@ def test_copy_from_template_auto_publish(self): while block_keys: key = block_keys.pop() block = self.store.get_item(key) - new_blocks[block.category] = block + new_blocks[block.scope_ids.block_type] = block block_keys.update(set(getattr(block, "children", []))) # Check that auto-publish blocks with no children are indeed published: diff --git a/xmodule/modulestore/tests/test_split_modulestore.py b/xmodule/modulestore/tests/test_split_modulestore.py index 09278ff5e8f0..986b6dc8751a 100644 --- a/xmodule/modulestore/tests/test_split_modulestore.py +++ b/xmodule/modulestore/tests/test_split_modulestore.py @@ -571,7 +571,7 @@ def test_get_courses(self): # check metadata -- NOTE no promised order course = self.findByIdInResult(courses, "head12345") assert course.location.org == 'testx' - assert course.category == 'course', 'wrong category' + assert course.scope_ids.block_type == 'course', 'wrong category' assert len(course.tabs) == 5, 'wrong number of tabs' assert course.display_name == 'The Ancient Greek Hero', 'wrong display name' assert course.advertised_start == 'Fall 2013', 'advertised_start' @@ -625,7 +625,7 @@ def _verify_published_course(courses_published): assert course is not None, 'published courses' assert course.location.course_key.org == 'testx' assert course.location.course_key.course == 'wonderful' - assert course.category == 'course', 'wrong category' + assert course.scope_ids.block_type == 'course', 'wrong category' assert len(course.tabs) == 3, 'wrong number of tabs' assert course.display_name == 'The most wonderful course', course.display_name assert course.advertised_start is None @@ -655,7 +655,7 @@ def test_get_course(self): course = modulestore().get_course(locator) assert course.location.course_key.org is None assert course.location.version_guid == head_course.previous_version - assert course.category == 'course' + assert course.scope_ids.block_type == 'course' assert len(course.tabs) == 5 assert course.display_name == 'The Ancient Greek Hero' assert course.graceperiod == datetime.timedelta(hours=2) @@ -671,7 +671,7 @@ def test_get_course(self): assert course.location.course_key.org == 'testx' assert course.location.course_key.course == 'GreekHero' assert course.location.course_key.run == 'run' - assert course.category == 'course' + assert course.scope_ids.block_type == 'course' assert len(course.tabs) == 5 assert course.display_name == 'The Ancient Greek Hero' assert course.advertised_start == 'Fall 2013' @@ -742,11 +742,11 @@ def test_persist_dag(self): persisted_course = modulestore().persist_xblock_dag(test_course, TEST_OTHER_USER_ID) assert len(persisted_course.children) == 1 persisted_chapter = persisted_course.get_children()[0] - assert persisted_chapter.category == 'chapter' + assert persisted_chapter.scope_ids.block_type == 'chapter' assert persisted_chapter.display_name == 'chapter n' assert len(persisted_chapter.children) == 1 persisted_problem = persisted_chapter.get_children()[0] - assert persisted_problem.category == 'problem' + assert persisted_problem.scope_ids.block_type == 'problem' assert persisted_problem.data == test_def_content # update it persisted_problem.display_name = 'altered problem' @@ -981,7 +981,7 @@ def test_get_non_root(self): block = modulestore().get_item(locator) assert block.location.org == 'testx' assert block.location.course == 'GreekHero' - assert block.category == 'chapter' + assert block.scope_ids.block_type == 'chapter' assert block.display_name == 'Hercules' assert block.edited_by == TEST_ASSISTANT_USER_ID @@ -1105,7 +1105,7 @@ def test_get_children(self): "chapter1", "chap", "chapter2", "chapter3" ] for child in children: - assert child.category == 'chapter' + assert child.scope_ids.block_type == 'chapter' assert child.location.block_id in expected_ids expected_ids.remove(child.location.block_id) assert len(expected_ids) == 0 @@ -1170,7 +1170,7 @@ def test_create_minimal_item(self): assert history_info['original_version'] == premod_history['original_version'] assert history_info['edited_by'] == 'user123' # check block's info: category, definition_locator, and display_name - assert new_block.category == 'sequential' + assert new_block.scope_ids.block_type == 'sequential' assert new_block.definition_locator is not None assert new_block.display_name == 'new sequential' # check that block does not exist in previous version @@ -1635,7 +1635,7 @@ def test_simple_creation(self): assert structure_info['edited_by'] == TEST_USER_ID # check the returned course object assert isinstance(new_course, CourseBlock) - assert new_course.category == 'course' + assert new_course.scope_ids.block_type == 'course' assert not new_course.show_calculator assert new_course.allow_anonymous assert len(new_course.children) == 0 @@ -1749,7 +1749,7 @@ def test_create_with_root(self): root_block_id='top', root_category='chapter' ) assert new_course.location.block_id == 'top' - assert new_course.category == 'chapter' + assert new_course.scope_ids.block_type == 'chapter' # look at db to verify db_structure = modulestore().db_connection.get_structure( new_course.location.as_object_id(new_course.location.version_guid) @@ -1994,7 +1994,7 @@ def _check_course(self, source_course_loc, dest_course_loc, expected_blocks, une source = modulestore().get_item(source_course_loc.make_usage_key(expected.type, expected.id)) pub_copy = modulestore().get_item(dest_course_loc.make_usage_key(expected.type, expected.id)) # everything except previous_version & children should be the same - assert source.category == pub_copy.category + assert source.scope_ids.block_type == pub_copy.scope_ids.block_type assert source.update_version == pub_copy.source_version,\ f"Versions don't match for {expected}: {source.update_version} != {pub_copy.update_version}" assert self.user_id == pub_copy.edited_by,\ diff --git a/xmodule/raw_block.py b/xmodule/raw_block.py index 6ee1852a9a5a..8937fddf3a38 100644 --- a/xmodule/raw_block.py +++ b/xmodule/raw_block.py @@ -103,4 +103,4 @@ def definition_to_xml(self, resource_fs): # pylint: disable=unused-argument """Return an XML Element from stored data, or an empty element if data is empty.""" if self.data: return etree.fromstring(self.data) - return etree.Element(self.category) + return etree.Element(self.scope_ids.block_type) diff --git a/xmodule/split_test_block.py b/xmodule/split_test_block.py index f259ef40e7bb..2b25a2146e9f 100644 --- a/xmodule/split_test_block.py +++ b/xmodule/split_test_block.py @@ -360,7 +360,7 @@ def studio_render_children(self, fragment, children, context): for active_child_block in children: active_child = self.runtime.get_block_for_descriptor(active_child_block) rendered_child = active_child.render(StudioEditableBlock.get_preview_view_name(active_child), context) - if active_child.category == 'vertical': + if active_child.scope_ids.block_type == 'vertical': group_name, group_id = self.get_data_for_vertical(active_child) if group_name: rendered_child.content = rendered_child.content.replace( diff --git a/xmodule/x_module.py b/xmodule/x_module.py index 9eb407a105c0..91dc9aaeee55 100644 --- a/xmodule/x_module.py +++ b/xmodule/x_module.py @@ -320,15 +320,6 @@ def course_id(self): """ return self.context_key - @property - def category(self): - """ - Return the block type, formerly known as "category". - - Preferred forms for new code: `self.usage_key.block_type` or `self.scope_ids.blocks_type` - """ - return self.scope_ids.block_type - @property def location(self): """ @@ -1551,7 +1542,7 @@ def resource_url(self, resource): def add_block_as_child_node(self, block, node): """Append the block’s XML to the given parent XML node.""" - child = etree.SubElement(node, block.category) + child = etree.SubElement(node, block.scope_ids.block_type) child.set("url_name", block.url_name) block.add_xml_to_node(child) diff --git a/xmodule/xml_block.py b/xmodule/xml_block.py index dac8fd93d37b..da62f84f696c 100644 --- a/xmodule/xml_block.py +++ b/xmodule/xml_block.py @@ -471,12 +471,12 @@ def add_xml_to_node(self, node): aside.add_xml_to_node(aside_node) xml_object.append(aside_node) - not_to_clean_fields = self.metadata_to_not_to_clean.get(self.category, ()) + not_to_clean_fields = self.metadata_to_not_to_clean.get(self.scope_ids.block_type, ()) self.clean_metadata_from_xml(xml_object, excluded_fields=not_to_clean_fields) # Set the tag on both nodes so we get the file path right. - xml_object.tag = self.category - node.tag = self.category + xml_object.tag = self.scope_ids.block_type + node.tag = self.scope_ids.block_type # Add the non-inherited metadata for attr in sorted(own_metadata(self)): @@ -507,8 +507,8 @@ def add_xml_to_node(self, node): url_path = name_to_pathname(self.url_name) # if folder is course then create file with name {course_run}.xml filepath = self._format_filepath( - self.category, - self.location.run if self.category == "course" else url_path, + self.scope_ids.block_type, + self.location.run if self.scope_ids.block_type == "course" else url_path, ) self.runtime.export_fs.makedirs(os.path.dirname(filepath), recreate=True) with self.runtime.export_fs.open(filepath, "wb") as fileobj: @@ -527,7 +527,7 @@ def add_xml_to_node(self, node): node.set("url_name", self.url_name) # Special case for course pointers: - if self.category == "course": + if self.scope_ids.block_type == "course": # add org and course attributes on the pointer tag node.set("org", self.location.org) node.set("course", self.location.course)