build!: Switch to openedx-core (renamed from openedx-learning) Instead of installing openedx-learning==0.32.0, we install openedx-core==0.34.1. We update various class names, function names, docstrings, and comments to represent the rename: * We say "openedx-core" when referring to the whole repo or PyPI project * or occasionally "Open edX Core" if we want it to look nice in the docs. * We say "openedx_content" to refer to the Content API within openedx-core, which is actually the thing we have been calling "Learning Core" all along. * In snake-case code, it's `*_openedx_content_*`. * In camel-case code, it's `*OpenedXContent*` For consistency's sake we avoid anything else like oex_core, OeXCore, OpenEdXCore, OexContent, openedx-content, OpenEdxContent, etc. There should be no more references to learning_core, learning-core, Learning Core, Learning-Core, LC, openedx-learning, openedx_learning, etc. BREAKING CHANGE: for openedx-learning/openedx-core developers: You may need to uninstall openedx-learning and re-install openedx-core from your venv. If running tutor, you may need to un-mount openedx-learning, rename the directory to openedx-core, re-mount it, and re-build. The code APIs themselves are fully backwards-compatible. Part of: https://github.com/openedx/openedx-core/issues/470
135 lines
3.7 KiB
Python
135 lines
3.7 KiB
Python
"""
|
|
Value objects
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typing as t
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
from uuid import UUID
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
from opaque_keys.edx.keys import UsageKey
|
|
from opaque_keys.edx.locator import (
|
|
CourseLocator,
|
|
LibraryContainerLocator,
|
|
LibraryLocator,
|
|
LibraryLocatorV2,
|
|
LibraryUsageLocatorV2,
|
|
)
|
|
|
|
from openedx.core.djangoapps.content_libraries.api import ContainerType
|
|
|
|
|
|
class CompositionLevel(Enum):
|
|
"""
|
|
Enumeration of composition levels for legacy content.
|
|
|
|
Defined in increasing order of complexity so that `is_higher_than` works correctly.
|
|
"""
|
|
# Components are individual XBlocks, e.g. Problem
|
|
Component = 'component'
|
|
|
|
# Container types currently supported by Content Libraries
|
|
Unit = ContainerType.Unit.value
|
|
Subsection = ContainerType.Subsection.value
|
|
Section = ContainerType.Section.value
|
|
|
|
@property
|
|
def is_container(self) -> bool:
|
|
return self is not self.Component
|
|
|
|
def is_higher_than(self, other: 'CompositionLevel') -> bool:
|
|
"""
|
|
Is this composition level 'above' (more complex than) the other?
|
|
"""
|
|
levels: list[CompositionLevel] = list(self.__class__)
|
|
return levels.index(self) > levels.index(other)
|
|
|
|
@classmethod
|
|
def supported_choices(cls) -> list[tuple[str, str]]:
|
|
"""
|
|
Returns all supported composition levels as a list of tuples,
|
|
for use in a Django Models ChoiceField.
|
|
"""
|
|
return [
|
|
(composition_level.value, composition_level.name)
|
|
for composition_level in cls
|
|
]
|
|
|
|
|
|
class RepeatHandlingStrategy(Enum):
|
|
"""
|
|
Enumeration of repeat handling strategies for imported content.
|
|
"""
|
|
Skip = 'skip'
|
|
Fork = 'fork'
|
|
Update = 'update'
|
|
|
|
@classmethod
|
|
def supported_choices(cls) -> list[tuple[str, str]]:
|
|
"""
|
|
Returns all supported repeat handling strategies as a list of tuples,
|
|
for use in a Django Models ChoiceField.
|
|
"""
|
|
return [
|
|
(strategy.value, strategy.name)
|
|
for strategy in cls
|
|
]
|
|
|
|
@classmethod
|
|
def default(cls) -> RepeatHandlingStrategy:
|
|
"""
|
|
Returns the default repeat handling strategy.
|
|
"""
|
|
return cls.Skip
|
|
|
|
|
|
SourceContextKey: t.TypeAlias = CourseLocator | LibraryLocator
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ModulestoreMigration:
|
|
"""
|
|
Metadata on a migration of a course or legacy library to a v2 library in openedx_content.
|
|
"""
|
|
pk: int
|
|
source_key: SourceContextKey
|
|
target_key: LibraryLocatorV2
|
|
target_title: str
|
|
target_collection_slug: str | None
|
|
target_collection_title: str | None
|
|
is_failed: bool
|
|
task_uuid: UUID # the UserTask which executed this migration
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ModulestoreBlockMigrationResult:
|
|
"""
|
|
Base class for a modulestore block that was part of an attempted migration to openedx_content.
|
|
"""
|
|
source_key: UsageKey
|
|
is_failed: t.ClassVar[bool]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ModulestoreBlockMigrationSuccess(ModulestoreBlockMigrationResult):
|
|
"""
|
|
Info on a modulestore block which has been successfully migrated to an openedx_content entity
|
|
"""
|
|
target_entity_pk: int
|
|
target_key: LibraryUsageLocatorV2 | LibraryContainerLocator
|
|
target_title: str
|
|
target_version_num: int | None
|
|
is_failed: t.ClassVar[bool] = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ModulestoreBlockMigrationFailure(ModulestoreBlockMigrationResult):
|
|
"""
|
|
Info on a modulestore block which failed to be migrated into openedx_content
|
|
"""
|
|
unsupported_reason: str
|
|
is_failed: t.ClassVar[bool] = True
|