diff --git a/cms/djangoapps/modulestore_migrator/data.py b/cms/djangoapps/modulestore_migrator/data.py index 3112a0f57f57..22d444873e1e 100644 --- a/cms/djangoapps/modulestore_migrator/data.py +++ b/cms/djangoapps/modulestore_migrator/data.py @@ -21,6 +21,7 @@ class CompositionLevel(Enum): Unit = ContainerType.Unit.value Subsection = ContainerType.Subsection.value Section = ContainerType.Section.value + OutlineRoot = ContainerType.OutlineRoot.value @property def is_container(self) -> bool: diff --git a/cms/djangoapps/modulestore_migrator/tasks.py b/cms/djangoapps/modulestore_migrator/tasks.py index e8577ead6526..ab2d418d5aa9 100644 --- a/cms/djangoapps/modulestore_migrator/tasks.py +++ b/cms/djangoapps/modulestore_migrator/tasks.py @@ -358,7 +358,7 @@ def _migrate_node( container_type = ContainerType.from_source_olx_tag(source_node.tag) except ValueError: container_type = None - if source_node.tag in {"course", "library"}: + if source_node.tag == "library": should_migrate_node = False should_migrate_children = True else: @@ -473,7 +473,7 @@ def _migrate_container( ], created=created_at, created_by=created_by, - container_version_cls=ContainerVersion, + container_version_cls=container_type.container_model_classes[1], ).publishable_entity_version diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py index 389f6d21161f..d7cdd26cbafa 100644 --- a/openedx/core/djangoapps/content/search/api.py +++ b/openedx/core/djangoapps/content/search/api.py @@ -502,6 +502,8 @@ def index_container_batch(batch, num_done, library_key) -> int: doc.update(searchable_doc_containers(container_key, "subsections")) case lib_api.ContainerType.Subsection: doc.update(searchable_doc_containers(container_key, "sections")) + case lib_api.ContainerType.Section: + doc.update(searchable_doc_containers(container_key, "outline_roots")) docs.append(doc) except Exception as err: # pylint: disable=broad-except status_cb(f"Error indexing container {container.key}: {err}") diff --git a/openedx/core/djangoapps/content/search/documents.py b/openedx/core/djangoapps/content/search/documents.py index aaabb0b92a51..88ec128ddc0c 100644 --- a/openedx/core/djangoapps/content/search/documents.py +++ b/openedx/core/djangoapps/content/search/documents.py @@ -630,7 +630,7 @@ def get_child_keys(children) -> list[str]: str(child.usage_key) for child in children ] - case lib_api.ContainerType.Subsection | lib_api.ContainerType.Section: + case lib_api.ContainerType.Subsection | lib_api.ContainerType.Section | lib_api.ContainerType.OutlineRoot: return [ str(child.container_key) for child in children diff --git a/openedx/core/djangoapps/content_libraries/api/containers.py b/openedx/core/djangoapps/content_libraries/api/containers.py index dc13d9d43107..8d9b9592be91 100644 --- a/openedx/core/djangoapps/content_libraries/api/containers.py +++ b/openedx/core/djangoapps/content_libraries/api/containers.py @@ -64,6 +64,39 @@ class ContainerType(Enum): Unit = "unit" Subsection = "subsection" Section = "section" + OutlineRoot = "outline_root" + + @property + def container_model_classes(self) -> tuple[type[Container], type[ContainerVersion]]: + """ + Get the container, containerversion subclasses associated with this type. + + @@TODO Is this what we want, a hard mapping between container_types and Container classes? + * If so, then expand on this pattern, so that all ContainerType logic is contained within + this class, and get rid of the match-case statements that are all over the content_libraries + app. + * If not, then figure out what to do instead. + """ + from openedx_learning.api.authoring_models import ( + Unit, + UnitVersion, + Subsection, + SubsectionVersion, + Section, + SectionVersion, + OutlineRoot, + OutlineRootVersion, + ) + match self: + case self.Unit: + return (Unit, UnitVersion) + case self.Subsection: + return (Subsection, SubsectionVersion) + case self.Section: + return (Section, SectionVersion) + case self.OutlineRoot: + return (OutlineRoot, OutlineRootVersion) + raise TypeError(f"unexpected ContainerType: {self!r}") @property def olx_tag(self) -> str: @@ -83,6 +116,8 @@ def olx_tag(self) -> str: return "sequential" case self.Section: return "chapter" + case self.OutlineRoot: + return "course" raise TypeError(f"unexpected ContainerType: {self!r}") @classmethod @@ -165,6 +200,8 @@ def library_container_locator( container_type = ContainerType.Subsection elif hasattr(container, 'section'): container_type = ContainerType.Section + elif hasattr(container, 'outlineroot'): + container_type = ContainerType.OutlineRoot assert container_type is not None @@ -277,6 +314,14 @@ def create_container( created=created, created_by=user_id, ) + case ContainerType.OutlineRoot: + container, _initial_version = authoring_api.create_outline_root_and_version( + content_library.learning_package_id, + key=slug, + title=title, + created=created, + created_by=user_id, + ) case _: raise NotImplementedError(f"Library does not support {container_type} yet") @@ -330,8 +375,15 @@ def update_container( created=created, created_by=user_id, ) - - # The `affected_containers` are not obtained, because the sections are + affected_containers = get_containers_contains_item(container_key) + case ContainerType.OutlineRoot: + version = authoring_api.create_next_outline_root_version( + container.outlineroot, + title=display_name, + created=created, + created_by=user_id, + ) + # The `affected_containers` are not obtained, because the outline_roots are # not contained in any container. case _: raise NotImplementedError(f"Library does not support {container_type} yet") @@ -546,6 +598,23 @@ def update_container_children( entities_action=entities_action, ) + for key in children_ids: + CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event( + content_object=ContentObjectChangedData( + object_id=str(key), + changes=["sections"], + ), + ) + case ContainerType.OutlineRoot: + subsections = [_get_container_from_key(key).outlineroot for key in children_ids] # type: ignore[arg-type] + new_version = authoring_api.create_next_outline_root_version( + container.outlineroot, + sections=sections, # type: ignore[arg-type] + created=created, + created_by=user_id, + entities_action=entities_action, + ) + for key in children_ids: CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event( content_object=ContentObjectChangedData(