From 9d45f855d6102352c066468b22949e575c69d31d Mon Sep 17 00:00:00 2001 From: Kyle McCormick Date: Tue, 22 Apr 2025 20:13:28 -0400 Subject: [PATCH] feat: Explicit mapping from container_types to OLX tags (#36580) This is needed by several aspects of the Teak Libraries Overhaul (https://github.com/orgs/openedx/projects/66) including: * copy-paste of containers between courses and V2 libraries * syncing of containers in courses from V2 libraries * the import_from_modulestore API --- .../content_libraries/api/containers.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/content_libraries/api/containers.py b/openedx/core/djangoapps/content_libraries/api/containers.py index 3a0a89550c..393755ed1c 100644 --- a/openedx/core/djangoapps/content_libraries/api/containers.py +++ b/openedx/core/djangoapps/content_libraries/api/containers.py @@ -60,7 +60,47 @@ log = logging.getLogger(__name__) class ContainerType(Enum): + """ + The container types supported by content_libraries, and logic to map them to OLX. + """ Unit = "unit" + Subsection = "subsection" + Section = "section" + + @property + def olx_tag(self) -> str: + """ + Canonical XML tag to use when representing this container as OLX. + + For example, Units are encoded as .... + + These tag names are historical. We keep them around for the backwards compatibility of OLX + and for easier interaction with legacy modulestore-powered structural XBlocks + (e.g., copy-paste of Units between courses and V2 libraries). + """ + match self: + case self.Unit: + return "vertical" + case self.Subsection: + return "sequential" + case self.Section: + return "chapter" + raise TypeError(f"unexpected ContainerType: {self!r}") + + @classmethod + def from_source_olx_tag(cls, olx_tag: str) -> 'ContainerType': + """ + Get the ContainerType that this OLX tag maps to. + """ + if olx_tag == "unit": + # There is an alternative implementation to VerticalBlock called UnitBlock whose + # OLX tag is . When converting from OLX, we want to handle both + # and as Unit containers, although the canonical serialization is still . + return cls.Unit + try: + return next(ct for ct in cls if olx_tag == ct.olx_tag) + except StopIteration: + raise ValueError(f"no container_type for XML tag: <{olx_tag}>") from None @dataclass(frozen=True, kw_only=True) @@ -83,7 +123,6 @@ class ContainerMetadata(PublishableItem): container=container, ) container_type = ContainerType(container_key.container_type) - published_by = "" if last_publish_log and last_publish_log.published_by: published_by = last_publish_log.published_by.username @@ -209,7 +248,7 @@ def create_container( created_by=user_id, ) case _: - raise ValueError(f"Invalid container type: {container_type}") + raise NotImplementedError(f"Library support for {container_type} is in progress") LIBRARY_CONTAINER_CREATED.send_event( library_container=LibraryContainerData(