chore: Switch to new openedx-learning import paths (#38004)

Upgrades openedx-learning from 0.31.0 to 0.32.0,
incorporating a major openedx-learning Python API
restructuring: ca0b3eb
This commit is contained in:
Kyle McCormick
2026-02-13 15:39:05 -05:00
committed by GitHub
parent d847d222b2
commit a55c1ddabf
65 changed files with 291 additions and 286 deletions

View File

@@ -3,8 +3,8 @@
import django.db.migrations.operations.special
import django.db.models.deletion
import opaque_keys.edx.django.models
import openedx_learning.lib.fields
import openedx_learning.lib.validators
import openedx_django_lib.fields
import openedx_django_lib.validators
import uuid
from django.conf import settings
from django.db import migrations, models
@@ -107,8 +107,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('context_key', opaque_keys.edx.django.models.CourseKeyField(help_text='Linking status for course context key', max_length=255, unique=True)),
('status', models.CharField(choices=[('pending', 'Pending'), ('processing', 'Processing'), ('failed', 'Failed'), ('completed', 'Completed')], help_text='Status of links in given learning context/course.', max_length=20)),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
],
options={
'verbose_name': 'Learning Context Links status',
@@ -121,13 +121,13 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')),
('upstream_usage_key', opaque_keys.edx.django.models.UsageKeyField(help_text='Upstream block usage key, this value cannot be null and useful to track upstream library blocks that do not exist yet', max_length=255)),
('upstream_context_key', openedx_learning.lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('upstream_context_key', openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('downstream_usage_key', opaque_keys.edx.django.models.UsageKeyField(max_length=255, unique=True)),
('downstream_context_key', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)),
('version_synced', models.IntegerField()),
('version_declined', models.IntegerField(blank=True, null=True)),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('upstream_block', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='links', to='openedx_content.component')),
],
options={
@@ -140,13 +140,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')),
('upstream_context_key', openedx_learning.lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('upstream_context_key', openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('downstream_usage_key', opaque_keys.edx.django.models.UsageKeyField(max_length=255, unique=True)),
('downstream_context_key', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)),
('version_synced', models.IntegerField()),
('version_declined', models.IntegerField(blank=True, null=True)),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('upstream_container_key', opaque_keys.edx.django.models.ContainerKeyField(help_text='Upstream block key (e.g. lct:...), this value cannot be null and is useful to track upstream library blocks that do not exist yet or were deleted.', max_length=255)),
('upstream_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='links', to='openedx_content.container')),
],

View File

@@ -4,8 +4,8 @@ import uuid
import django.db.models.deletion
import opaque_keys.edx.django.models
import openedx_learning.lib.fields
import openedx_learning.lib.validators
import openedx_django_lib.fields
import openedx_django_lib.validators
from django.db import migrations, models
@@ -39,8 +39,8 @@ class Migration(migrations.Migration):
max_length=20,
),
),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
],
options={
'verbose_name': 'Learning Context Links status',
@@ -61,7 +61,7 @@ class Migration(migrations.Migration):
),
(
'upstream_context_key',
openedx_learning.lib.fields.MultiCollationCharField(
openedx_django_lib.fields.MultiCollationCharField(
db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'},
db_index=True,
help_text='Upstream context key i.e., learning_package/library key',
@@ -72,8 +72,8 @@ class Migration(migrations.Migration):
('downstream_context_key', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)),
('version_synced', models.IntegerField()),
('version_declined', models.IntegerField(blank=True, null=True)),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
(
'upstream_block',
models.ForeignKey(

View File

@@ -3,8 +3,8 @@ import uuid
import django.db.models.deletion
import opaque_keys.edx.django.models
import openedx_learning.lib.fields
import openedx_learning.lib.validators
import openedx_django_lib.fields
import openedx_django_lib.validators
from django.db import migrations, models
@@ -40,13 +40,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')),
('upstream_context_key', openedx_learning.lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('upstream_context_key', openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, db_index=True, help_text='Upstream context key i.e., learning_package/library key', max_length=500)),
('downstream_usage_key', opaque_keys.edx.django.models.UsageKeyField(max_length=255, unique=True)),
('downstream_context_key', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)),
('version_synced', models.IntegerField()),
('version_declined', models.IntegerField(blank=True, null=True)),
('created', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_learning.lib.validators.validate_utc_datetime])),
('created', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('updated', models.DateTimeField(validators=[openedx_django_lib.validators.validate_utc_datetime])),
('upstream_container_key', opaque_keys.edx.django.models.ContainerKeyField(help_text='Upstream block key (e.g. lct:...), this value cannot be null and is useful to track upstream library blocks that do not exist yet or were deleted.', max_length=255)),
('upstream_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='links', to='oel_publishing.container')),
],

View File

@@ -15,9 +15,9 @@ from django.utils.translation import gettext_lazy as _
from opaque_keys.edx.django.models import ContainerKeyField, CourseKeyField, UsageKeyField
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryContainerLocator
from openedx_learning.api.authoring import get_published_version
from openedx_learning.api.authoring_models import Component, Container
from openedx_learning.lib.fields import (
from openedx_content.api import get_published_version
from openedx_content.models_api import Component, Container
from openedx_django_lib.fields import (
immutable_uuid_field,
key_field,
manual_date_time_field,

View File

@@ -32,13 +32,13 @@ class APIHeartBeatView(DeveloperErrorViewMixin, APIView):
**Response Values**
If the request is successful, an HTTP 200 "OK" response is returned.
The HTTP 200 response contains a single dict with the "authoring_api_enabled" value "True".
The HTTP 200 response contains a single dict with the "content_api_enabled" value "True".
**Example Response**
```json
{
"authoring_api_enabled": "True"
"content_api_enabled": "True"
}
```
"""

View File

@@ -10,7 +10,7 @@ from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from organizations.tests.factories import OrganizationFactory
from rest_framework import status
@@ -272,7 +272,7 @@ class HomePageLibrariesViewTest(LibraryTestCase):
self.url = reverse("cms.djangoapps.contentstore:v1:libraries")
# Create a collection to migrate this library to
collection_key = "test-collection"
authoring_api.create_collection(
content_api.create_collection(
learning_package_id=learning_package.id,
key=collection_key,
title="Test Collection",

View File

@@ -13,7 +13,7 @@ from openedx_events.content_authoring.signals import (
XBLOCK_UPDATED,
)
from openedx_events.tests.utils import OpenEdxEventsTestMixin
from openedx_tagging.core.tagging.models import Tag
from openedx_tagging.models import Tag
from organizations.models import Organization
from xmodule.modulestore.django import contentstore, modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, upload_file_to_course, ImmediateOnCommitMixin

View File

@@ -11,8 +11,8 @@ from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.locator import (
LibraryLocatorV2, LibraryUsageLocatorV2, LibraryContainerLocator
)
from openedx_learning.api.authoring import get_draft_version, get_all_drafts
from openedx_learning.api.authoring_models import (
from openedx_content.api import get_draft_version, get_all_drafts
from openedx_content.models_api import (
PublishableEntityVersion, PublishableEntity, DraftChangeLogRecord
)
from xblock.plugin import PluginMissingError

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from celery.result import AsyncResult
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_learning.api.authoring import get_collection
from openedx_content.api import get_collection
from openedx.core.types.user import AuthUser
from openedx.core.djangoapps.content_libraries.api import get_library

View File

@@ -11,7 +11,7 @@ from opaque_keys.edx.django.models import (
LearningContextKeyField,
UsageKeyField,
)
from openedx_learning.api.authoring_models import (
from openedx_content.models_api import (
Collection,
DraftChangeLog,
DraftChangeLogRecord,

View File

@@ -5,7 +5,7 @@ Serializers for the Course to Library Import API.
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import LearningContextKey
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_learning.api.authoring_models import Collection
from openedx_content.models_api import Collection
from rest_framework import serializers
from user_tasks.models import UserTaskStatus
from user_tasks.serializers import StatusSerializer

View File

@@ -30,8 +30,8 @@ from opaque_keys.edx.locator import (
LibraryLocatorV2,
LibraryUsageLocatorV2,
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import (
from openedx_content import api as content_api
from openedx_content.models_api import (
Collection,
Component,
ComponentType,
@@ -295,8 +295,8 @@ def _import_assets(migration: models.ModulestoreMigration) -> dict[str, int]:
continue
filename = os.path.basename(old_path)
media_type_str = mimetypes.guess_type(filename)[0] or "application/octet-stream"
media_type = authoring_api.get_or_create_media_type(media_type_str)
content_by_filename[filename] = authoring_api.get_or_create_file_content(
media_type = content_api.get_or_create_media_type(media_type_str)
content_by_filename[filename] = content_api.get_or_create_file_content(
migration.target_id,
media_type.id,
data=file_data,
@@ -335,7 +335,7 @@ def _import_structure(
tuple[Any, _MigratedNode]:
A tuple containing:
- The first element (`change_log`): the bulk draft change log generated by
`authoring_api.bulk_draft_changes_for`, containing all the imported changes.
`content_api.bulk_draft_changes_for`, containing all the imported changes.
- The second element (`root_migrated_node`): a `_MigratedNode` object that
represents the mapping between the legacy root node and its newly created
Learning Core equivalent.
@@ -345,12 +345,12 @@ def _import_structure(
used_component_keys=set(
LibraryUsageLocatorV2(target_library.key, block_type, block_id) # type: ignore[abstract]
for block_type, block_id
in authoring_api.get_components(migration.target.pk).values_list(
in content_api.get_components(migration.target.pk).values_list(
"component_type__name", "local_key"
)
),
used_container_slugs=set(
authoring_api.get_containers(
content_api.get_containers(
migration.target.pk
).values_list("publishable_entity__key", flat=True)
),
@@ -369,7 +369,7 @@ def _import_structure(
created_by=status.user_id,
created_at=datetime.now(timezone.utc),
)
with authoring_api.bulk_draft_changes_for(migration.target.id) as change_log:
with content_api.bulk_draft_changes_for(migration.target.id) as change_log:
root_migrated_node = _migrate_node(
context=migration_context,
source_node=root_node,
@@ -407,7 +407,7 @@ def _populate_collection(user_id: int, migration: models.ModulestoreMigration) -
).values_list('target_id', flat=True)
)
if block_target_pks:
authoring_api.add_to_collection(
content_api.add_to_collection(
learning_package_id=migration.target.pk,
key=migration.target_collection.key,
entities_qset=PublishableEntity.objects.filter(id__in=block_target_pks),
@@ -693,7 +693,7 @@ def bulk_migrate_from_modulestore(
if source_data.previous_migration:
if previous_collection_slug := source_data.previous_migration.target_collection_slug:
try:
existing_collection_to_use = authoring_api.get_collection(
existing_collection_to_use = content_api.get_collection(
target_package.id, previous_collection_slug
)
except Collection.DoesNotExist:
@@ -886,11 +886,11 @@ def _migrate_container(
version_num=container.draft_version_num,
), None
container_publishable_entity_version = authoring_api.create_next_container_version(
container_publishable_entity_version = content_api.create_next_container_version(
container.container_pk,
title=title,
entity_rows=[
authoring_api.ContainerEntityRow(entity_pk=child.entity_id, version_pk=None)
content_api.ContainerEntityRow(entity_pk=child.entity_id, version_pk=None)
for child in children
],
created=context.created_at,
@@ -924,7 +924,7 @@ def _migrate_component(
(We assume that the destination is a library rather than some other future kind of learning
package, but let's keep than an internal assumption.)
"""
component_type = authoring_api.get_or_create_component_type("xblock.v1", source_key.block_type)
component_type = content_api.get_or_create_component_type("xblock.v1", source_key.block_type)
target_key = _get_distinct_target_usage_key(
context,
@@ -934,7 +934,7 @@ def _migrate_component(
)
try:
component = authoring_api.get_components(context.target_package_id).get(
component = content_api.get_components(context.target_package_id).get(
component_type=component_type,
local_key=target_key.block_id,
)
@@ -954,7 +954,7 @@ def _migrate_component(
except PluginMissingError as e:
log.error(f"Block type not supported in {context.target_library_key}: {e}")
return None, f"Invalid block type: {e}"
component = authoring_api.create_component(
component = content_api.create_component(
context.target_package_id,
component_type=component_type,
local_key=target_key.block_id,
@@ -974,7 +974,7 @@ def _migrate_component(
if filename_no_ext not in olx:
continue
new_path = f"static/{filename}"
authoring_api.create_component_version_content(
content_api.create_component_version_content(
component_version.pk, content_pk, key=new_path
)

View File

@@ -5,7 +5,7 @@ Test cases for the modulestore migrator API.
import pytest
from unittest.mock import patch
from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2, CourseLocator
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from organizations.tests.factories import OrganizationFactory
from cms.djangoapps.modulestore_migrator import api
@@ -224,7 +224,7 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
user = UserFactory()
collection_key = "test-collection"
authoring_api.create_collection(
content_api.create_collection(
learning_package_id=self.learning_package.id,
key=collection_key,
title="Test Collection",
@@ -479,19 +479,19 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
# Lib 1 has Collection A and Collection B
# Lib 2 has Collection C
authoring_api.create_collection(
content_api.create_collection(
learning_package_id=self.learning_package.id,
key="test-collection-1a",
title="Test Collection A in Lib 1",
created_by=user.id,
)
authoring_api.create_collection(
content_api.create_collection(
learning_package_id=self.learning_package.id,
key="test-collection-1b",
title="Test Collection B in Lib 1",
created_by=user.id,
)
authoring_api.create_collection(
content_api.create_collection(
learning_package_id=self.learning_package_2.id,
key="test-collection-2c",
title="Test Collection C in Lib 2",

View File

@@ -9,8 +9,8 @@ from django.utils import timezone
from lxml import etree
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Collection, PublishableEntityVersion
from openedx_content import api as content_api
from openedx_content.models_api import Collection, PublishableEntityVersion
from organizations.tests.factories import OrganizationFactory
from user_tasks.models import UserTaskArtifact
from user_tasks.tasks import UserTaskStatus
@@ -404,8 +404,8 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
source_key = self.course.id.make_usage_key("problem", "test_problem_with_image")
olx = '<problem display_name="Test Problem"><p>See image: test_image.png</p></problem>'
media_type = authoring_api.get_or_create_media_type("image/png")
test_content = authoring_api.get_or_create_file_content(
media_type = content_api.get_or_create_media_type("image/png")
test_content = content_api.get_or_create_file_content(
self.learning_package.id,
media_type.id,
data=b"fake_image_data",
@@ -637,14 +637,14 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
)
olx = '<problem display_name="Test Problem"><p>See image: referenced.png</p></problem>'
media_type = authoring_api.get_or_create_media_type("image/png")
referenced_content = authoring_api.get_or_create_file_content(
media_type = content_api.get_or_create_media_type("image/png")
referenced_content = content_api.get_or_create_file_content(
self.learning_package.id,
media_type.id,
data=b"referenced_image_data",
created=timezone.now(),
)
unreferenced_content = authoring_api.get_or_create_file_content(
unreferenced_content = content_api.get_or_create_file_content(
self.learning_package.id,
media_type.id,
data=b"unreferenced_image_data",
@@ -711,32 +711,32 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
"""
source_key = self.course.id.make_usage_key("vertical", "test_vertical")
child_component_1 = authoring_api.create_component(
child_component_1 = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "problem"
),
local_key="child_problem_1",
created=timezone.now(),
created_by=self.user.id,
)
child_version_1 = authoring_api.create_next_component_version(
child_version_1 = content_api.create_next_component_version(
child_component_1.pk,
content_to_replace={},
created=timezone.now(),
created_by=self.user.id,
)
child_component_2 = authoring_api.create_component(
child_component_2 = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "html"
),
local_key="child_html_1",
created=timezone.now(),
created_by=self.user.id,
)
child_version_2 = authoring_api.create_next_component_version(
child_version_2 = content_api.create_next_component_version(
child_component_2.pk,
content_to_replace={},
created=timezone.now(),
@@ -801,7 +801,7 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
container_version = result.containerversion
self.assertEqual(container_version.title, f"Test {block_type.title()}")
# The container is published
self.assertFalse(authoring_api.contains_unpublished_changes(container_version.container.pk))
self.assertFalse(content_api.contains_unpublished_changes(container_version.container.pk))
def test_migrate_container_same_title(self):
"""
@@ -899,16 +899,16 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
context = self._make_migration_context(repeat_handling_strategy=RepeatHandlingStrategy.Skip)
children = []
for i in range(3):
child_component = authoring_api.create_component(
child_component = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "problem"
),
local_key=f"child_problem_{i}",
created=timezone.now(),
created_by=self.user.id,
)
child_version = authoring_api.create_next_component_version(
child_version = content_api.create_next_component_version(
child_component.pk,
content_to_replace={},
created=timezone.now(),
@@ -939,48 +939,48 @@ class TestMigrateFromModulestore(ModuleStoreTestCase):
"""
source_key = self.course.id.make_usage_key("vertical", "mixed_vertical")
problem_component = authoring_api.create_component(
problem_component = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "problem"
),
local_key="mixed_problem",
created=timezone.now(),
created_by=self.user.id,
)
problem_version = authoring_api.create_next_component_version(
problem_version = content_api.create_next_component_version(
problem_component.pk,
content_to_replace={},
created=timezone.now(),
created_by=self.user.id,
)
html_component = authoring_api.create_component(
html_component = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "html"
),
local_key="mixed_html",
created=timezone.now(),
created_by=self.user.id,
)
html_version = authoring_api.create_next_component_version(
html_version = content_api.create_next_component_version(
html_component.pk,
content_to_replace={},
created=timezone.now(),
created_by=self.user.id,
)
video_component = authoring_api.create_component(
video_component = content_api.create_component(
self.learning_package.id,
component_type=authoring_api.get_or_create_component_type(
component_type=content_api.get_or_create_component_type(
"xblock.v1", "video"
),
local_key="mixed_video",
created=timezone.now(),
created_by=self.user.id,
)
video_version = authoring_api.create_next_component_version(
video_version = content_api.create_next_component_version(
video_component.pk,
content_to_replace={},
created=timezone.now(),

View File

@@ -45,7 +45,7 @@ from corsheaders.defaults import default_headers as corsheaders_default_headers
from datetime import timedelta
from django.utils.translation import gettext_lazy as _
from openedx_learning.api.django import openedx_learning_apps_to_install
from openedx_content.settings_api import openedx_content_backcompat_apps_to_install
from openedx.envs.common import * # pylint: disable=wildcard-import
@@ -849,7 +849,7 @@ INSTALLED_APPS = [
'drf_yasg',
# Tagging
'openedx_tagging.core.tagging.apps.TaggingConfig',
'openedx_tagging',
'openedx.core.djangoapps.content_tagging',
# Search
@@ -898,7 +898,9 @@ INSTALLED_APPS = [
'openedx_events',
*openedx_learning_apps_to_install(),
# Core apps that power libraries
"openedx_content",
*openedx_content_backcompat_apps_to_install(),
]
### Apps only installed in some instances

View File

@@ -818,4 +818,3 @@ ZENDESK_GROUP_ID_MAPPING:
ZENDESK_OAUTH_ACCESS_TOKEN: test_zendesk_access
ZENDESK_URL: https://zendesk.com
ZENDESK_USER: daemon@example.com

View File

@@ -60,7 +60,7 @@ from enterprise.constants import (
PROVISIONING_PENDING_ENTERPRISE_CUSTOMER_ADMIN_ROLE,
DEFAULT_ENTERPRISE_ENROLLMENT_INTENTIONS_ROLE,
)
from openedx_learning.api.django import openedx_learning_apps_to_install
from openedx_content.settings_api import openedx_content_backcompat_apps_to_install
from openedx.core.lib.derived import Derived
from openedx.envs.common import * # pylint: disable=wildcard-import
@@ -1950,7 +1950,7 @@ INSTALLED_APPS = [
'lms.djangoapps.course_goals.apps.CourseGoalsConfig',
# Tagging
'openedx_tagging.core.tagging.apps.TaggingConfig',
'openedx_tagging',
'openedx.core.djangoapps.content_tagging',
# Features
@@ -2020,8 +2020,9 @@ INSTALLED_APPS = [
'openedx_events',
# Learning Core apps that power libraries
*openedx_learning_apps_to_install(),
# Core apps that power libraries
"openedx_content",
*openedx_content_backcompat_apps_to_install(),
]
# Add LMS specific optional apps

View File

@@ -25,7 +25,7 @@ from opaque_keys.edx.locator import (
LibraryContainerLocator,
LibraryLocatorV2,
)
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from rest_framework.request import Request
from common.djangoapps.student.role_helpers import get_course_roles
@@ -556,7 +556,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None, incremental=Fa
# To reduce memory usage on large instances, split up the Collections into pages of 100 collections:
library = lib_api.get_library(lib_key)
collections = authoring_api.get_collections(library.learning_package_id, enabled=True)
collections = content_api.get_collections(library.learning_package_id, enabled=True)
num_collections = collections.count()
num_collections_done = 0
status_cb(f"{num_collections_done}/{num_collections}. Now indexing collections in library {lib_key}")
@@ -572,7 +572,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None, incremental=Fa
status_cb(f"{num_collections_done}/{num_collections} collections indexed for library {lib_key}")
# Similarly, batch process Containers (units, sections, etc) in pages of 100
containers = authoring_api.get_containers(library.learning_package_id)
containers = content_api.get_containers(library.learning_package_id)
num_containers = containers.count()
num_containers_done = 0
status_cb(f"{num_containers_done}/{num_containers}. Now indexing containers in library {lib_key}")
@@ -791,7 +791,7 @@ def update_library_components_collections(
"""
library_key = collection_key.lib_key
library = lib_api.get_library(library_key)
components = authoring_api.get_collection_components(
components = content_api.get_collection_components(
library.learning_package_id,
collection_key.collection_id,
)
@@ -827,7 +827,7 @@ def update_library_containers_collections(
"""
library_key = collection_key.lib_key
library = lib_api.get_library(library_key)
containers = authoring_api.get_collection_containers(
containers = content_api.get_collection_containers(
library.learning_package_id,
collection_key.collection_id,
)

View File

@@ -10,8 +10,8 @@ from django.core.exceptions import ObjectDoesNotExist
from django.utils.text import slugify
from opaque_keys.edx.keys import ContainerKey, LearningContextKey, UsageKey, OpaqueKey
from opaque_keys.edx.locator import LibraryCollectionLocator, LibraryContainerLocator
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Collection
from openedx_content import api as content_api
from openedx_content.models_api import Collection
from rest_framework.exceptions import NotFound
from openedx.core.djangoapps.content.search.models import SearchAccess
@@ -446,7 +446,7 @@ def searchable_doc_collections(object_id: OpaqueKey) -> dict:
try:
if isinstance(object_id, UsageKey):
component = lib_api.get_component_from_usage_key(object_id)
collections = authoring_api.get_entity_collections(
collections = content_api.get_entity_collections(
component.learning_package_id,
component.key,
).values('key', 'title')
@@ -543,11 +543,11 @@ def searchable_doc_for_collection(
if collection:
assert collection.key == collection_key.collection_id
draft_num_children = authoring_api.filter_publishable_entities(
draft_num_children = content_api.filter_publishable_entities(
collection.entities,
has_draft=True,
).count()
published_num_children = authoring_api.filter_publishable_entities(
published_num_children = content_api.filter_publishable_entities(
collection.entities,
has_published=True,
).count()

View File

@@ -15,7 +15,7 @@ import pytest
from django.test import override_settings
from freezegun import freeze_time
from meilisearch.errors import MeilisearchApiError
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from organizations.tests.factories import OrganizationFactory
from common.djangoapps.student.tests.factories import UserFactory
@@ -189,9 +189,9 @@ class TestSearchApi(ModuleStoreTestCase):
tagging_api.add_tag_to_taxonomy(self.taxonomyB, "four")
# Create a collection:
self.learning_package = authoring_api.get_learning_package_by_key(self.library.key)
self.learning_package = content_api.get_learning_package_by_key(self.library.key)
with freeze_time(self.created_date):
self.collection = authoring_api.create_collection(
self.collection = content_api.create_collection(
learning_package_id=self.learning_package.id,
key="MYCOL",
title="my_collection",
@@ -926,7 +926,7 @@ class TestSearchApi(ModuleStoreTestCase):
mock_meilisearch.return_value.index.reset_mock()
# Soft-delete the collection
authoring_api.delete_collection(
content_api.delete_collection(
self.collection.learning_package_id,
self.collection.key,
)
@@ -961,7 +961,7 @@ class TestSearchApi(ModuleStoreTestCase):
# Restore the collection
restored_date = datetime(2023, 8, 9, 10, 11, 12, tzinfo=timezone.utc)
with freeze_time(restored_date):
authoring_api.restore_collection(
content_api.restore_collection(
self.collection.learning_package_id,
self.collection.key,
)
@@ -983,7 +983,7 @@ class TestSearchApi(ModuleStoreTestCase):
mock_meilisearch.return_value.index.reset_mock()
# Hard-delete the collection
authoring_api.delete_collection(
content_api.delete_collection(
self.collection.learning_package_id,
self.collection.key,
hard_delete=True,

View File

@@ -7,7 +7,7 @@ from datetime import datetime, timezone
from freezegun import freeze_time
from opaque_keys.edx.locator import LibraryCollectionLocator, LibraryContainerLocator
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from organizations.models import Organization
from openedx.core.djangoapps.content_libraries import api as library_api
@@ -660,7 +660,7 @@ class StudioDocumentsTest(SharedModuleStoreTestCase):
self.container.container_key,
[block_2.usage_key],
user_id=None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
doc = searchable_doc_for_container(self.container.container_key)

View File

@@ -36,8 +36,8 @@ from openedx_events.content_authoring.signals import (
LIBRARY_COLLECTION_UPDATED,
LIBRARY_CONTAINER_UPDATED
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import (
from openedx_content import api as content_api
from openedx_content.models_api import (
Component, ComponentVersion, LearningPackage, MediaType,
Container, Collection
)
@@ -115,7 +115,7 @@ def get_library_components(
lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
learning_package = lib.learning_package
assert learning_package is not None
components = authoring_api.get_components(
components = content_api.get_components(
learning_package.id,
draft=True,
namespace='xblock.v1',
@@ -133,7 +133,7 @@ def get_library_containers(library_key: LibraryLocatorV2) -> QuerySet[Container]
lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
learning_package = lib.learning_package
assert learning_package is not None
containers: QuerySet[Container] = authoring_api.get_containers(
containers: QuerySet[Container] = content_api.get_containers(
learning_package.id
)
@@ -147,7 +147,7 @@ def get_library_collections(library_key: LibraryLocatorV2) -> QuerySet[Collectio
lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
learning_package = lib.learning_package
assert learning_package is not None
collections = authoring_api.get_collections(
collections = content_api.get_collections(
learning_package.id
)
return collections
@@ -177,7 +177,7 @@ def get_library_block(usage_key: LibraryUsageLocatorV2, include_collections=Fals
raise ContentLibraryBlockNotFound(usage_key)
if include_collections:
associated_collections = authoring_api.get_entity_collections(
associated_collections = content_api.get_entity_collections(
component.learning_package_id,
component.key,
).values('key', 'title')
@@ -238,13 +238,13 @@ def set_library_block_olx(usage_key: LibraryUsageLocatorV2, new_olx_str: str) ->
now = datetime.now(tz=timezone.utc)
with transaction.atomic():
new_content = authoring_api.get_or_create_text_content(
new_content = content_api.get_or_create_text_content(
component.learning_package_id,
get_or_create_olx_media_type(usage_key.block_type).id,
text=new_olx_str,
created=now,
)
new_component_version = authoring_api.create_next_component_version(
new_component_version = content_api.create_next_component_version(
component.pk,
title=new_title,
content_to_replace={
@@ -295,7 +295,7 @@ def validate_can_add_block_to_library(
# If adding a component would take us over our max, return an error.
assert content_library.learning_package_id is not None
component_count = authoring_api.get_all_drafts(content_library.learning_package_id).count()
component_count = content_api.get_all_drafts(content_library.learning_package_id).count()
if component_count + 1 > settings.MAX_BLOCKS_PER_CONTENT_LIBRARY:
raise BlockLimitReachedError(
_("Library cannot have more than {} Components.").format(
@@ -419,10 +419,10 @@ def _import_staged_block(
with transaction.atomic(savepoint=False):
# First create the Component, but do not initialize it to anything (i.e.
# no ComponentVersion).
component_type = authoring_api.get_or_create_component_type(
component_type = content_api.get_or_create_component_type(
"xblock.v1", usage_key.block_type
)
component = authoring_api.create_component(
component = content_api.create_component(
learning_package.id,
component_type=component_type,
local_key=usage_key.block_id,
@@ -480,14 +480,14 @@ def _import_staged_block(
if not media_type_str:
media_type_str = "application/octet-stream"
media_type = authoring_api.get_or_create_media_type(media_type_str)
content = authoring_api.get_or_create_file_content(
media_type = content_api.get_or_create_media_type(media_type_str)
content = content_api.get_or_create_file_content(
learning_package.id,
media_type.id,
data=file_data,
created=now,
)
authoring_api.create_component_version_content(
content_api.create_component_version_content(
component_version.pk,
content.id,
key=filename,
@@ -690,7 +690,7 @@ def get_or_create_olx_media_type(block_type: str) -> MediaType:
Learning Core stores all Content with a Media Type (a.k.a. MIME type). For
OLX, we use the "application/vnd.*" convention, per RFC 6838.
"""
return authoring_api.get_or_create_media_type(
return content_api.get_or_create_media_type(
f"application/vnd.openedx.xblock.v1.{block_type}+xml"
)
@@ -704,10 +704,10 @@ def delete_library_block(
"""
component = get_component_from_usage_key(usage_key)
library_key = usage_key.context_key
affected_collections = authoring_api.get_entity_collections(component.learning_package_id, component.key)
affected_collections = content_api.get_entity_collections(component.learning_package_id, component.key)
affected_containers = get_containers_contains_item(usage_key)
authoring_api.soft_delete_draft(component.pk, deleted_by=user_id)
content_api.soft_delete_draft(component.pk, deleted_by=user_id)
# .. event_implemented_name: LIBRARY_BLOCK_DELETED
# .. event_type: org.openedx.content_authoring.library_block.deleted.v1
@@ -756,10 +756,10 @@ def restore_library_block(usage_key: LibraryUsageLocatorV2, user_id: int | None
"""
component = get_component_from_usage_key(usage_key)
library_key = usage_key.context_key
affected_collections = authoring_api.get_entity_collections(component.learning_package_id, component.key)
affected_collections = content_api.get_entity_collections(component.learning_package_id, component.key)
# Set draft version back to the latest available component version id.
authoring_api.set_draft_version(
content_api.set_draft_version(
component.pk,
component.versioning.latest.pk,
set_by=user_id,
@@ -895,7 +895,7 @@ def add_library_block_static_asset_file(
component = get_component_from_usage_key(usage_key)
with transaction.atomic():
component_version = authoring_api.create_next_component_version(
component_version = content_api.create_next_component_version(
component.pk,
content_to_replace={file_path: file_content},
created=datetime.now(tz=timezone.utc),
@@ -943,7 +943,7 @@ def delete_library_block_static_asset_file(usage_key, file_path, user=None):
now = datetime.now(tz=timezone.utc)
with transaction.atomic():
component_version = authoring_api.create_next_component_version(
component_version = content_api.create_next_component_version(
component.pk,
content_to_replace={file_path: None},
created=now,
@@ -971,9 +971,9 @@ def publish_component_changes(usage_key: LibraryUsageLocatorV2, user_id: int):
learning_package = content_library.learning_package
assert learning_package
# The core publishing API is based on draft objects, so find the draft that corresponds to this component:
drafts_to_publish = authoring_api.get_all_drafts(learning_package.id).filter(entity__key=component.key)
drafts_to_publish = content_api.get_all_drafts(learning_package.id).filter(entity__key=component.key)
# Publish the component and update anything that needs to be updated (e.g. search index):
publish_log = authoring_api.publish_from_drafts(
publish_log = content_api.publish_from_drafts(
learning_package.id, draft_qset=drafts_to_publish, published_by=user_id,
)
# Since this is a single component, it should be safe to process synchronously and in-process:
@@ -1026,10 +1026,10 @@ def _create_component_for_block(
assert learning_package is not None # mostly for type checker
with transaction.atomic():
component_type = authoring_api.get_or_create_component_type(
component_type = content_api.get_or_create_component_type(
"xblock.v1", usage_key.block_type
)
component, component_version = authoring_api.create_component_and_version(
component, component_version = content_api.create_component_and_version(
learning_package.id,
component_type=component_type,
local_key=usage_key.block_id,
@@ -1038,13 +1038,13 @@ def _create_component_for_block(
created_by=user_id,
can_stand_alone=can_stand_alone,
)
content = authoring_api.get_or_create_text_content(
content = content_api.get_or_create_text_content(
learning_package.id,
get_or_create_olx_media_type(usage_key.block_type).id,
text=xml_text,
created=now,
)
authoring_api.create_component_version_content(
content_api.create_component_version_content(
component_version.pk,
content.id,
key="block.xml",

View File

@@ -8,8 +8,8 @@ from opaque_keys.edx.keys import BlockTypeKey, UsageKeyV2
from opaque_keys.edx.locator import LibraryCollectionLocator, LibraryContainerLocator, LibraryLocatorV2
from openedx_events.content_authoring.data import LibraryCollectionData
from openedx_events.content_authoring.signals import LIBRARY_COLLECTION_UPDATED
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Collection, Component, PublishableEntity
from openedx_content import api as content_api
from openedx_content.models_api import Collection, Component, PublishableEntity
from ..models import ContentLibrary
from .exceptions import (
@@ -52,7 +52,7 @@ def create_library_collection(
assert content_library.library_key == library_key
try:
collection = authoring_api.create_collection(
collection = content_api.create_collection(
learning_package_id=content_library.learning_package_id,
key=collection_key,
title=title,
@@ -84,7 +84,7 @@ def update_library_collection(
assert content_library.library_key == library_key
try:
collection = authoring_api.update_collection(
collection = content_api.update_collection(
learning_package_id=content_library.learning_package_id,
key=collection_key,
title=title,
@@ -132,7 +132,7 @@ def update_library_collection_items(
for opaque_key in opaque_keys:
if isinstance(opaque_key, LibraryContainerLocator):
try:
container = authoring_api.get_container_by_key(
container = content_api.get_container_by_key(
content_library.learning_package_id,
key=opaque_key.container_id,
)
@@ -144,7 +144,7 @@ def update_library_collection_items(
# Parse the block_family from the key to use as namespace.
block_type = BlockTypeKey.from_string(str(opaque_key))
try:
component = authoring_api.get_component_by_key(
component = content_api.get_component_by_key(
content_library.learning_package_id,
namespace=block_type.block_family,
type_name=opaque_key.block_type,
@@ -163,13 +163,13 @@ def update_library_collection_items(
)
if remove:
collection = authoring_api.remove_from_collection(
collection = content_api.remove_from_collection(
content_library.learning_package_id,
collection_key,
entities_qset,
)
else:
collection = authoring_api.add_to_collection(
collection = content_api.add_to_collection(
content_library.learning_package_id,
collection_key,
entities_qset,
@@ -207,17 +207,17 @@ def set_library_item_collections(
assert content_library.learning_package_id
assert content_library.library_key == library_key
publishable_entity = authoring_api.get_publishable_entity_by_key(
publishable_entity = content_api.get_publishable_entity_by_key(
content_library.learning_package_id,
key=entity_key,
)
# Note: Component.key matches its PublishableEntity.key
collection_qs = authoring_api.get_collections(content_library.learning_package_id).filter(
collection_qs = content_api.get_collections(content_library.learning_package_id).filter(
key__in=collection_keys
)
affected_collections = authoring_api.set_collections(
affected_collections = content_api.set_collections(
publishable_entity,
collection_qs,
created_by=created_by,
@@ -263,7 +263,7 @@ def get_library_collection_from_locator(
content_library = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
assert content_library.learning_package_id is not None # shouldn't happen but it's technically possible.
try:
return authoring_api.get_collection(
return content_api.get_collection(
content_library.learning_package_id,
collection_key,
)

View File

@@ -8,8 +8,8 @@ from enum import Enum
from django.db.models import QuerySet
from opaque_keys.edx.locator import LibraryContainerLocator, LibraryLocatorV2, LibraryUsageLocatorV2
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import (
from openedx_content import api as content_api
from openedx_content.models_api import (
Component,
Container,
ContainerVersion,
@@ -149,7 +149,7 @@ class ContainerMetadata(PublishableItem):
published_by=published_by,
last_draft_created=last_draft_created,
last_draft_created_by=last_draft_created_by,
has_unpublished_changes=authoring_api.contains_unpublished_changes(container.pk),
has_unpublished_changes=content_api.contains_unpublished_changes(container.pk),
tags_count=tags.get(str(container_key), 0),
collections=associated_collections or [],
)
@@ -295,7 +295,7 @@ def _get_containers_with_entities(
"""
qs = Container.objects.none()
for member in members:
qs = qs.union(authoring_api.get_containers_with_entity(
qs = qs.union(content_api.get_containers_with_entity(
member.entity.pk,
ignore_pinned=ignore_pinned,
))
@@ -320,7 +320,7 @@ def _get_containers_children(
for member in members:
container = member.container
assert container
for entry in authoring_api.get_entities_in_container(
for entry in content_api.get_entities_in_container(
container,
published=published,
):
@@ -372,7 +372,7 @@ class ContainerHierarchyMember:
container=entity,
),
display_name=entity.versioning.draft.title,
has_unpublished_changes=authoring_api.contains_unpublished_changes(entity.pk),
has_unpublished_changes=content_api.contains_unpublished_changes(entity.pk),
container=entity,
component=None,
)
@@ -425,7 +425,7 @@ def get_container_from_key(container_key: LibraryContainerLocator, include_delet
content_library = ContentLibrary.objects.get_by_key(container_key.lib_key)
learning_package = content_library.learning_package
assert learning_package is not None
container = authoring_api.get_container_by_key(
container = content_api.get_container_by_key(
learning_package.id,
key=container_key.container_id,
)

View File

@@ -22,8 +22,8 @@ from openedx_events.content_authoring.signals import (
LIBRARY_CONTAINER_DELETED,
LIBRARY_CONTAINER_UPDATED,
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Container, ContainerVersion, Component
from openedx_content import api as content_api
from openedx_content.models_api import Container, ContainerVersion, Component
from openedx.core.djangoapps.content_libraries.api.collections import library_collection_locator
from openedx.core.djangoapps.xblock.api import get_component_from_usage_key
@@ -76,7 +76,7 @@ def get_container(
"""
container = get_container_from_key(container_key)
if include_collections:
associated_collections = authoring_api.get_entity_collections(
associated_collections = content_api.get_entity_collections(
container.publishable_entity.learning_package_id,
container_key.container_id,
).values('key', 'title')
@@ -126,7 +126,7 @@ def create_container(
# Then try creating the actual container:
match container_type:
case ContainerType.Unit:
container, _initial_version = authoring_api.create_unit_and_version(
container, _initial_version = content_api.create_unit_and_version(
content_library.learning_package_id,
key=slug,
title=title,
@@ -134,7 +134,7 @@ def create_container(
created_by=user_id,
)
case ContainerType.Subsection:
container, _initial_version = authoring_api.create_subsection_and_version(
container, _initial_version = content_api.create_subsection_and_version(
content_library.learning_package_id,
key=slug,
title=title,
@@ -142,7 +142,7 @@ def create_container(
created_by=user_id,
)
case ContainerType.Section:
container, _initial_version = authoring_api.create_section_and_version(
container, _initial_version = content_api.create_section_and_version(
content_library.learning_package_id,
key=slug,
title=title,
@@ -188,7 +188,7 @@ def update_container(
match container_type:
case ContainerType.Unit:
version = authoring_api.create_next_unit_version(
version = content_api.create_next_unit_version(
container.unit,
title=display_name,
created=created,
@@ -198,7 +198,7 @@ def update_container(
# Components have usage_key instead of container_key
child_key_name = 'usage_key'
case ContainerType.Subsection:
version = authoring_api.create_next_subsection_version(
version = content_api.create_next_subsection_version(
container.subsection,
title=display_name,
created=created,
@@ -206,7 +206,7 @@ def update_container(
)
affected_containers = get_containers_contains_item(container_key)
case ContainerType.Section:
version = authoring_api.create_next_section_version(
version = content_api.create_next_section_version(
container.section,
title=display_name,
created=created,
@@ -265,7 +265,7 @@ def delete_container(
container = get_container_from_key(container_key)
# Fetch related collections and containers before soft-delete
affected_collections = authoring_api.get_entity_collections(
affected_collections = content_api.get_entity_collections(
container.publishable_entity.learning_package_id,
container.key,
)
@@ -275,7 +275,7 @@ def delete_container(
container_key,
published=False,
)
authoring_api.soft_delete_draft(container.pk)
content_api.soft_delete_draft(container.pk)
# .. event_implemented_name: LIBRARY_CONTAINER_DELETED
# .. event_type: org.openedx.content_authoring.content_library.container.deleted.v1
@@ -337,12 +337,12 @@ def restore_container(container_key: LibraryContainerLocator) -> None:
library_key = container_key.lib_key
container = get_container_from_key(container_key, include_deleted=True)
affected_collections = authoring_api.get_entity_collections(
affected_collections = content_api.get_entity_collections(
container.publishable_entity.learning_package_id,
container.key,
)
authoring_api.set_draft_version(container.pk, container.versioning.latest.pk)
content_api.set_draft_version(container.pk, container.versioning.latest.pk)
# Fetch related containers after restore
affected_containers = get_containers_contains_item(container_key)
# Get children containers or components to update their index data
@@ -430,25 +430,25 @@ def get_container_children(
match container_type:
case ContainerType.Unit:
child_components = authoring_api.get_components_in_unit(container.unit, published=published)
child_components = content_api.get_components_in_unit(container.unit, published=published)
return [LibraryXBlockMetadata.from_component(
container_key.lib_key,
entry.component
) for entry in child_components]
case ContainerType.Subsection:
child_units = authoring_api.get_units_in_subsection(container.subsection, published=published)
child_units = content_api.get_units_in_subsection(container.subsection, published=published)
return [ContainerMetadata.from_container(
container_key.lib_key,
entry.unit
) for entry in child_units]
case ContainerType.Section:
child_subsections = authoring_api.get_subsections_in_section(container.section, published=published)
child_subsections = content_api.get_subsections_in_section(container.section, published=published)
return [ContainerMetadata.from_container(
container_key.lib_key,
entry.subsection,
) for entry in child_subsections]
case _:
child_entities = authoring_api.get_entities_in_container(container, published=published)
child_entities = content_api.get_entities_in_container(container, published=published)
return [ContainerMetadata.from_container(
container_key.lib_key,
entry.entity
@@ -463,14 +463,14 @@ def get_container_children_count(
[ 🛑 UNSTABLE ] Get the count of entities contained in the given container (e.g. the components/xblocks in a unit)
"""
container = get_container_from_key(container_key)
return authoring_api.get_container_children_count(container, published=published)
return content_api.get_container_children_count(container, published=published)
def update_container_children(
container_key: LibraryContainerLocator,
children_ids: list[LibraryUsageLocatorV2] | list[LibraryContainerLocator],
user_id: int | None,
entities_action: authoring_api.ChildrenEntitiesAction = authoring_api.ChildrenEntitiesAction.REPLACE,
entities_action: content_api.ChildrenEntitiesAction = content_api.ChildrenEntitiesAction.REPLACE,
):
"""
[ 🛑 UNSTABLE ] Adds children components or containers to given container.
@@ -483,7 +483,7 @@ def update_container_children(
match container_type:
case ContainerType.Unit:
components = [get_component_from_usage_key(key) for key in children_ids] # type: ignore[arg-type]
new_version = authoring_api.create_next_unit_version(
new_version = content_api.create_next_unit_version(
container.unit,
components=components, # type: ignore[arg-type]
created=created,
@@ -502,7 +502,7 @@ def update_container_children(
)
case ContainerType.Subsection:
units = [get_container_from_key(key).unit for key in children_ids] # type: ignore[arg-type]
new_version = authoring_api.create_next_subsection_version(
new_version = content_api.create_next_subsection_version(
container.subsection,
units=units, # type: ignore[arg-type]
created=created,
@@ -521,7 +521,7 @@ def update_container_children(
)
case ContainerType.Section:
subsections = [get_container_from_key(key).subsection for key in children_ids] # type: ignore[arg-type]
new_version = authoring_api.create_next_section_version(
new_version = content_api.create_next_section_version(
container.section,
subsections=subsections, # type: ignore[arg-type]
created=created,
@@ -566,7 +566,7 @@ def get_containers_contains_item(
elif isinstance(key, LibraryContainerLocator):
item = get_container_from_key(key)
containers = authoring_api.get_containers_with_entity(
containers = content_api.get_containers_with_entity(
item.publishable_entity.pk,
)
return [
@@ -590,9 +590,9 @@ def publish_container_changes(
learning_package = content_library.learning_package
assert learning_package
# The core publishing API is based on draft objects, so find the draft that corresponds to this container:
drafts_to_publish = authoring_api.get_all_drafts(learning_package.id).filter(entity__pk=container.pk)
drafts_to_publish = content_api.get_all_drafts(learning_package.id).filter(entity__pk=container.pk)
# Publish the container, which will also auto-publish any unpublished child components:
publish_log = authoring_api.publish_from_drafts(
publish_log = content_api.publish_from_drafts(
learning_package.id,
draft_qset=drafts_to_publish,
published_by=user_id,

View File

@@ -3,7 +3,7 @@ Exceptions that can be thrown by the Content Libraries API.
"""
from django.db import IntegrityError
from openedx_learning.api.authoring_models import Collection, Container
from openedx_content.models_api import Collection, Container
from xblock.exceptions import XBlockNotFoundError
from ..models import ContentLibrary

View File

@@ -63,8 +63,8 @@ from openedx_events.content_authoring.signals import (
CONTENT_LIBRARY_DELETED,
CONTENT_LIBRARY_UPDATED,
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Component, LearningPackage
from openedx_content import api as content_api
from openedx_content.models_api import Component, LearningPackage
from organizations.models import Organization
from user_tasks.models import UserTaskArtifact, UserTaskStatus
from xblock.core import XBlock
@@ -364,9 +364,9 @@ def get_library(library_key: LibraryLocatorV2) -> ContentLibraryMetadata:
ref = ContentLibrary.objects.get_by_key(library_key)
learning_package = ref.learning_package
assert learning_package is not None # Shouldn't happen - this is just for the type checker
num_blocks = authoring_api.get_all_drafts(learning_package.id).count()
last_publish_log = authoring_api.get_last_publish(learning_package.id)
last_draft_log = authoring_api.get_entities_with_unpublished_changes(learning_package.id) \
num_blocks = content_api.get_all_drafts(learning_package.id).count()
last_publish_log = content_api.get_last_publish(learning_package.id)
last_draft_log = content_api.get_entities_with_unpublished_changes(learning_package.id) \
.order_by('-created').first()
last_draft_created = last_draft_log.created if last_draft_log else None
last_draft_created_by = last_draft_log.created_by.username if last_draft_log and last_draft_log.created_by else ""
@@ -377,8 +377,9 @@ def get_library(library_key: LibraryLocatorV2) -> ContentLibraryMetadata:
# with how Blockstore staged changes, but Learning Core works differently,
# and has_unpublished_changes should be sufficient.
# Ref: https://github.com/openedx/edx-platform/issues/34283
has_unpublished_deletes = authoring_api.get_entities_with_unpublished_deletes(learning_package.id) \
.exists()
has_unpublished_deletes = (
content_api.get_entities_with_unpublished_deletes(learning_package.id).exists()
)
published_by = ""
if last_publish_log and last_publish_log.published_by:
@@ -450,7 +451,7 @@ def create_library(
if learning_package:
# A temporary LearningPackage was passed in, so update its key to match the library,
# and also update its title/description in case they differ.
authoring_api.update_learning_package(
content_api.update_learning_package(
learning_package.id,
key=str(ref.library_key),
title=title,
@@ -458,7 +459,7 @@ def create_library(
)
else:
# We have to generate a new LearningPackage for this library.
learning_package = authoring_api.create_learning_package(
learning_package = content_api.create_learning_package(
key=str(ref.library_key),
title=title,
description=description,
@@ -485,7 +486,7 @@ def create_library(
allow_public_learning=ref.allow_public_learning,
allow_public_read=ref.allow_public_read,
license=library_license,
learning_package_id=ref.learning_package.pk,
learning_package_id=ref.learning_package.pk, # type: ignore[union-attr]
)
@@ -625,7 +626,7 @@ def update_library(
content_lib.save()
if learning_pkg_changed:
authoring_api.update_learning_package(
content_api.update_learning_package(
learning_package_id,
title=title,
description=description,
@@ -717,7 +718,7 @@ def publish_changes(library_key: LibraryLocatorV2, user_id: int | None = None):
"""
learning_package = ContentLibrary.objects.get_by_key(library_key).learning_package
assert learning_package is not None # shouldn't happen but it's technically possible.
publish_log = authoring_api.publish_all_drafts(learning_package.id, published_by=user_id)
publish_log = content_api.publish_all_drafts(learning_package.id, published_by=user_id)
# Update the search index (and anything else) for the affected blocks
# This is mostly synchronous but may complete some work asynchronously if there are a lot of changes.
@@ -735,8 +736,8 @@ def revert_changes(library_key: LibraryLocatorV2, user_id: int | None = None) ->
"""
learning_package = ContentLibrary.objects.get_by_key(library_key).learning_package
assert learning_package is not None # shouldn't happen but it's technically possible.
with authoring_api.bulk_draft_changes_for(learning_package.id) as draft_change_log:
authoring_api.reset_drafts_to_published(learning_package.id, reset_by=user_id)
with content_api.bulk_draft_changes_for(learning_package.id) as draft_change_log:
content_api.reset_drafts_to_published(learning_package.id, reset_by=user_id)
# Call the event handlers as needed.
tasks.wait_for_post_revert_events(draft_change_log, library_key)

View File

@@ -10,7 +10,7 @@ from openedx_events.content_authoring.data import LibraryBlockData, LibraryConta
from openedx_events.content_authoring.signals import LIBRARY_BLOCK_UPDATED, LIBRARY_CONTAINER_UPDATED
from opaque_keys.edx.keys import UsageKeyV2
from opaque_keys.edx.locator import LibraryUsageLocatorV2, LibraryLocatorV2
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from openedx.core.djangoapps.content_libraries import api, permissions
from openedx.core.djangoapps.content_libraries.models import ContentLibrary
@@ -96,7 +96,7 @@ class LibraryContextImpl(LearningContext):
if learning_package is None:
return False
return authoring_api.component_exists_by_key(
return content_api.component_exists_by_key(
learning_package.id,
namespace='xblock.v1',
type_name=usage_key.block_type,

View File

@@ -57,7 +57,7 @@ from opaque_keys.edx.django.models import UsageKeyField
from openedx.core.djangoapps.content_libraries.constants import (
LICENSE_OPTIONS, ALL_RIGHTS_RESERVED,
)
from openedx_learning.api.authoring_models import LearningPackage
from openedx_content.models_api import LearningPackage
from organizations.models import Organization # lint-amnesty, pylint: disable=wrong-import-order
from .apps import ContentLibrariesConfig

View File

@@ -10,7 +10,7 @@ from django.utils.decorators import method_decorator
from drf_yasg.utils import swagger_auto_schema
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
from openedx_authz.constants import permissions as authz_permissions
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from rest_framework import status
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.generics import GenericAPIView
@@ -392,7 +392,7 @@ def get_component_version_asset(request, component_version_uuid, asset_path):
eventually).
"""
try:
component_version = authoring_api.get_component_version_by_uuid(
component_version = content_api.get_component_version_by_uuid(
component_version_uuid
)
except ObjectDoesNotExist as exc:
@@ -411,7 +411,7 @@ def get_component_version_asset(request, component_version_uuid, asset_path):
# this response in conjunction with a media reverse proxy (Caddy or Nginx),
# but in the short term we're just going to remove the redirect and stream
# the content directly.
redirect_response = authoring_api.get_redirect_response_for_component_asset(
redirect_response = content_api.get_redirect_response_for_component_asset(
component_version_uuid,
asset_path,
public=False,

View File

@@ -14,8 +14,8 @@ from rest_framework.status import HTTP_204_NO_CONTENT
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_authz.constants import permissions as authz_permissions
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Collection
from openedx_content import api as content_api
from openedx_content.models_api import Collection
from .. import api, permissions
from ..models import ContentLibrary
@@ -72,7 +72,7 @@ class LibraryCollectionsView(ModelViewSet):
"""
content_library = self.get_content_library()
assert content_library.learning_package_id
return authoring_api.get_collections(content_library.learning_package_id)
return content_api.get_collections(content_library.learning_package_id)
def get_object(self) -> Collection:
"""
@@ -183,7 +183,7 @@ class LibraryCollectionsView(ModelViewSet):
)
collection = super().get_object()
assert collection.learning_package_id
authoring_api.delete_collection(
content_api.delete_collection(
collection.learning_package_id,
collection.key,
hard_delete=False,
@@ -204,7 +204,7 @@ class LibraryCollectionsView(ModelViewSet):
)
assert content_library.learning_package_id
collection_key = kwargs["key"]
authoring_api.restore_collection(
content_api.restore_collection(
content_library.learning_package_id,
collection_key,
)

View File

@@ -13,7 +13,7 @@ from drf_yasg import openapi
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryContainerLocator
from openedx_authz.constants import permissions as authz_permissions
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_200_OK
@@ -200,7 +200,7 @@ class LibraryContainerChildrenView(GenericAPIView):
self,
request,
container_key: LibraryContainerLocator,
action: authoring_api.ChildrenEntitiesAction,
action: content_api.ChildrenEntitiesAction,
):
"""
Helper function to update children in container.
@@ -237,7 +237,7 @@ class LibraryContainerChildrenView(GenericAPIView):
return self._update_component_children(
request,
container_key,
action=authoring_api.ChildrenEntitiesAction.APPEND,
action=content_api.ChildrenEntitiesAction.APPEND,
)
@convert_exceptions
@@ -256,7 +256,7 @@ class LibraryContainerChildrenView(GenericAPIView):
return self._update_component_children(
request,
container_key,
action=authoring_api.ChildrenEntitiesAction.REMOVE,
action=content_api.ChildrenEntitiesAction.REMOVE,
)
@convert_exceptions
@@ -275,7 +275,7 @@ class LibraryContainerChildrenView(GenericAPIView):
return self._update_component_children(
request,
container_key,
action=authoring_api.ChildrenEntitiesAction.REPLACE,
action=content_api.ChildrenEntitiesAction.REPLACE,
)

View File

@@ -8,7 +8,7 @@ import logging
from django.core.validators import validate_unicode_slug
from opaque_keys import InvalidKeyError, OpaqueKey
from opaque_keys.edx.locator import LibraryContainerLocator, LibraryUsageLocatorV2
from openedx_learning.api.authoring_models import Collection, LearningPackage
from openedx_content.models_api import Collection, LearningPackage
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from user_tasks.models import UserTaskStatus

View File

@@ -16,8 +16,8 @@ from openedx_events.content_authoring.signals import (
LIBRARY_COLLECTION_DELETED,
LIBRARY_COLLECTION_UPDATED
)
from openedx_learning.api.authoring import get_components, get_containers
from openedx_learning.api.authoring_models import Collection, CollectionPublishableEntity, PublishableEntity
from openedx_content.api import get_components, get_containers
from openedx_content.models_api import Collection, CollectionPublishableEntity, PublishableEntity
from lms.djangoapps.grades.api import signals as grades_signals

View File

@@ -57,9 +57,9 @@ from openedx_events.content_authoring.signals import (
LIBRARY_CONTAINER_PUBLISHED,
LIBRARY_CONTAINER_UPDATED
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring import create_zip_file as create_lib_zip_file
from openedx_learning.api.authoring_models import DraftChangeLog, PublishLog
from openedx_content import api as content_api
from openedx_content.api import create_zip_file as create_lib_zip_file
from openedx_content.models_api import DraftChangeLog, PublishLog
from path import Path
from user_tasks.models import UserTaskArtifact
from user_tasks.tasks import UserTask, UserTaskStatus
@@ -244,7 +244,7 @@ def send_events_after_revert(draft_change_log_id: int, library_key_str: str) ->
)
# If any collections contain this entity, their item count may need to be updated, e.g. if this was a
# newly created component in the collection and is now deleted, or this was deleted and is now re-added.
for parent_collection in authoring_api.get_entity_collections(
for parent_collection in content_api.get_entity_collections(
record.entity.learning_package_id, record.entity.key,
):
collection_key = api.library_collection_locator(
@@ -640,7 +640,7 @@ class LibraryRestoreTask(UserTask):
TASK_LOGGER.info('Restoring learning package from temporary file %s', tmp_file.name)
result = authoring_api.load_learning_package(tmp_file.name, user=user)
result = content_api.load_learning_package(tmp_file.name, user=user)
# If there was an error during the load, fail the task with the error log
if result.get("status") == "error":

View File

@@ -29,7 +29,7 @@ from openedx_events.content_authoring.signals import (
LIBRARY_CONTAINER_UPDATED,
)
from openedx_authz.api.users import get_user_role_assignments_in_scope
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from common.djangoapps.student.tests.factories import UserFactory
from .. import api
@@ -411,7 +411,7 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
LIBRARY_COLLECTION_DELETED.connect(event_receiver)
assert self.lib1.learning_package_id is not None
authoring_api.delete_collection(
content_api.delete_collection(
self.lib1.learning_package_id,
self.col1.key,
hard_delete=True,
@@ -549,8 +549,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
)
assert self.lib2.learning_package_id is not None
assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col2.key).entities.all()) == 1
assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col3.key).entities.all()) == 1
assert len(content_api.get_collection(self.lib2.learning_package_id, self.col2.key).entities.all()) == 1
assert len(content_api.get_collection(self.lib2.learning_package_id, self.col3.key).entities.all()) == 1
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
@@ -1059,7 +1059,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.unit2.container_key,
[LibraryUsageLocatorV2.from_string(html_block_1["id"])],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
event_reciver = mock.Mock()
@@ -1068,7 +1068,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.unit2.container_key,
[LibraryUsageLocatorV2.from_string(html_block_1["id"])],
None,
entities_action=authoring_api.ChildrenEntitiesAction.REMOVE,
entities_action=content_api.ChildrenEntitiesAction.REMOVE,
)
assert event_reciver.call_count == 1
@@ -1091,7 +1091,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.subsection2.container_key,
[unit4.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
event_reciver = mock.Mock()
@@ -1100,7 +1100,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.subsection2.container_key,
[unit4.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.REMOVE,
entities_action=content_api.ChildrenEntitiesAction.REMOVE,
)
assert event_reciver.call_count == 1
@@ -1129,7 +1129,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.section2.container_key,
[subsection3.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
event_reciver = mock.Mock()
@@ -1138,7 +1138,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.section2.container_key,
[subsection3.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.REMOVE,
entities_action=content_api.ChildrenEntitiesAction.REMOVE,
)
assert event_reciver.call_count == 1
@@ -1171,7 +1171,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
LibraryUsageLocatorV2.from_string(html_block_2["id"])
],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
assert event_reciver.call_count == 2
@@ -1209,7 +1209,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.subsection2.container_key,
[unit4.container_key, unit5.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
assert event_reciver.call_count == 2
self.assertDictContainsEntries(
@@ -1257,7 +1257,7 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self.section2.container_key,
[subsection3.container_key, subsection4.container_key],
None,
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
entities_action=content_api.ChildrenEntitiesAction.APPEND,
)
assert event_reciver.call_count == 2
self.assertDictContainsEntries(

View File

@@ -23,7 +23,7 @@ from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2, Lib
from organizations.models import Organization
from rest_framework.test import APITestCase
from rest_framework import status
from openedx_learning.api.authoring_models import LearningPackage
from openedx_content.models_api import LearningPackage
from user_tasks.models import UserTaskStatus, UserTaskArtifact
from common.djangoapps.student.tests.factories import UserFactory
@@ -1179,7 +1179,7 @@ class LibraryRestoreViewTestCase(ContentLibrariesRestApiTest):
with self.as_user(self.admin_user):
with patch(
"openedx.core.djangoapps.content_libraries.tasks.authoring_api.load_learning_package",
"openedx.core.djangoapps.content_libraries.tasks.content_api.load_learning_package",
return_value=error_result
):
response = self._start_library_restore_task(self.uploaded_zip_file)
@@ -1202,7 +1202,7 @@ class LibraryRestoreViewTestCase(ContentLibrariesRestApiTest):
"""
with self.as_user(self.admin_user):
with patch(
"openedx.core.djangoapps.content_libraries.tasks.authoring_api.load_learning_package",
"openedx.core.djangoapps.content_libraries.tasks.content_api.load_learning_package",
side_effect=Exception("Uncaught exception during processing.")
):
response = self._start_library_restore_task(self.uploaded_zip_file)

View File

@@ -5,7 +5,7 @@ Tests Library Collections REST API views
from __future__ import annotations
import ddt
from openedx_learning.api.authoring_models import Collection
from openedx_content.models_api import Collection
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx.core.djangolib.testing.utils import skip_unless_cms

View File

@@ -1,7 +1,7 @@
# Generated by Django 3.2.23 on 2023-12-07 20:10
from django.db import migrations
import openedx_learning.lib.fields
import openedx_django_lib.fields
class Migration(migrations.Migration):
@@ -14,11 +14,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='stagedcontent',
name='display_name',
field=openedx_learning.lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_unicode_ci', 'sqlite': 'NOCASE'}, max_length=768),
field=openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_unicode_ci', 'sqlite': 'NOCASE'}, max_length=768),
),
migrations.AlterField(
model_name='stagedcontent',
name='olx',
field=openedx_learning.lib.fields.MultiCollationTextField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}),
field=openedx_django_lib.fields.MultiCollationTextField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}),
),
]

View File

@@ -11,7 +11,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import ContainerKey, LearningContextKey, UsageKey
from openedx_learning.lib.fields import MultiCollationTextField, case_insensitive_char_field
from openedx_django_lib.fields import MultiCollationTextField, case_insensitive_char_field
from openedx.core.djangoapps.content.course_overviews.api import get_course_overview_or_none

View File

@@ -9,12 +9,12 @@ import csv
from typing import Iterator
from opaque_keys.edx.keys import CourseKey, CollectionKey, ContainerKey, UsageKey
import openedx_tagging.core.tagging.api as oel_tagging
import openedx_tagging.api as oel_tagging
from django.db.models import Exists, OuterRef, Q, QuerySet
from django.utils.timezone import now
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_tagging.core.tagging.models import ObjectTag, Taxonomy
from openedx_tagging.core.tagging.models.utils import TAGS_CSV_SEPARATOR
from openedx_tagging.models import ObjectTag, Taxonomy
from openedx_tagging.models.utils import TAGS_CSV_SEPARATOR
from organizations.models import Organization
from .helpers.objecttag_export_helpers import build_object_tree_with_objecttags, iterate_with_level
from openedx_events.content_authoring.data import ContentObjectData, ContentObjectChangedData

View File

@@ -3,7 +3,7 @@ Functions to validate the access in content tagging actions
"""
from openedx_tagging.core.tagging import rules as oel_tagging_rules
from openedx_tagging import rules as oel_tagging_rules
def has_view_object_tags_access(user, object_id):

View File

@@ -6,7 +6,7 @@ from __future__ import annotations
from django.db import models
from django.db.models import Q, QuerySet
from django.utils.translation import gettext as _
from openedx_tagging.core.tagging.models import Taxonomy
from openedx_tagging.models import Taxonomy
from organizations.models import Organization

View File

@@ -5,7 +5,7 @@ API Filters for content tagging org
from django.db.models import Exists, OuterRef, Q
from rest_framework.filters import BaseFilterBackend
import openedx_tagging.core.tagging.rules as oel_tagging
import openedx_tagging.rules as oel_tagging
from ...rules import get_admin_orgs, get_user_orgs
from ...models import TaxonomyOrg

View File

@@ -6,7 +6,7 @@ from __future__ import annotations
from rest_framework import serializers, fields
from openedx_tagging.core.tagging.rest_api.v1.serializers import (
from openedx_tagging.rest_api.v1.serializers import (
ObjectTagMinimalSerializer,
TaxonomyListQueryParamsSerializer,
TaxonomySerializer,

View File

@@ -15,9 +15,9 @@ from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from edx_django_utils.cache import RequestCache
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryCollectionLocator, LibraryContainerLocator
from openedx_tagging.core.tagging.models import Tag, Taxonomy
from openedx_tagging.core.tagging.models.system_defined import SystemDefinedTaxonomy
from openedx_tagging.core.tagging.rest_api.v1.serializers import TaxonomySerializer
from openedx_tagging.models import Tag, Taxonomy
from openedx_tagging.models.system_defined import SystemDefinedTaxonomy
from openedx_tagging.rest_api.v1.serializers import TaxonomySerializer
from organizations.models import Organization
from rest_framework import status
from rest_framework.test import APITestCase

View File

@@ -3,9 +3,9 @@ Taxonomies API v1 URLs.
"""
from django.urls.conf import include, path
from openedx_tagging.core.tagging.rest_api.v1 import views as oel_tagging_views
from openedx_tagging.core.tagging.rest_api.v1 import views_import as oel_tagging_views_import
from openedx_tagging.core.tagging.rest_api.v1.views import ObjectTagCountsView
from openedx_tagging.rest_api.v1 import views as oel_tagging_views
from openedx_tagging.rest_api.v1 import views_import as oel_tagging_views_import
from openedx_tagging.rest_api.v1.views import ObjectTagCountsView
from rest_framework.routers import DefaultRouter
from . import views

View File

@@ -5,8 +5,8 @@ from __future__ import annotations
from django.db.models import Count
from django.http import StreamingHttpResponse
from openedx_tagging.core.tagging import rules as oel_tagging_rules
from openedx_tagging.core.tagging.rest_api.v1.views import ObjectTagView, TaxonomyView
from openedx_tagging import rules as oel_tagging_rules
from openedx_tagging.rest_api.v1.views import ObjectTagView, TaxonomyView
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied, ValidationError

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from typing import Union
import django.contrib.auth.models
import openedx_tagging.core.tagging.rules as oel_tagging
import openedx_tagging.rules as oel_tagging
import rules
from organizations.models import Organization

View File

@@ -12,7 +12,7 @@ from django.contrib.auth import get_user_model
from edx_django_utils.monitoring import set_code_owner_attribute
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryUsageLocatorV2
from openedx_tagging.core.tagging.models import Taxonomy
from openedx_tagging.models import Taxonomy
from xmodule.modulestore.django import modulestore

View File

@@ -7,7 +7,7 @@ from django.test.testcases import TestCase
from fs.osfs import OSFS
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryCollectionLocator, LibraryContainerLocator
from openedx_tagging.core.tagging.models import ObjectTag
from openedx_tagging.models import ObjectTag
from organizations.models import Organization
from .test_objecttag_export_helpers import TestGetAllObjectTagsMixin, TaggedCourseMixin

View File

@@ -4,7 +4,7 @@ Test the objecttag_export_helpers module
import time
from unittest.mock import patch
from openedx_tagging.core.tagging.models import ObjectTag
from openedx_tagging.models import ObjectTag
from organizations.models import Organization
from openedx.core.djangoapps.content_libraries import api as library_api

View File

@@ -4,11 +4,11 @@ import ddt
from django.contrib.auth import get_user_model
from django.test import TestCase
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from openedx_tagging.core.tagging.models import (
from openedx_tagging.models import (
Tag,
UserSystemDefinedTaxonomy,
)
from openedx_tagging.core.tagging.rules import ObjectTagPermissionItem
from openedx_tagging.rules import ObjectTagPermissionItem
from common.djangoapps.student.auth import add_users, update_org_role
from common.djangoapps.student.roles import CourseStaffRole, OrgStaffRole

View File

@@ -8,7 +8,7 @@ from unittest.mock import patch
from django.test import override_settings, LiveServerTestCase
from django.http import HttpRequest
from edx_toggles.toggles.testutils import override_waffle_flag
from openedx_tagging.core.tagging.models import Tag, Taxonomy, ObjectTag
from openedx_tagging.models import Tag, Taxonomy, ObjectTag
from organizations.models import Organization
from common.djangoapps.student.tests.factories import UserFactory
@@ -41,9 +41,9 @@ class LanguageTaxonomyTestMixin:
running migrations. So data created by our migrations is not present.
In particular, the Language Taxonomy is not present. So this mixin will
create the taxonomy, simulating the effect of the following migrations:
1. openedx_tagging.core.tagging.migrations.0012_language_taxonomy
1. openedx_tagging.migrations.0012_language_taxonomy
2. content_tagging.migrations.0007_system_defined_org_2
3. openedx_tagging.core.tagging.migrations.0015_taxonomy_export_id
3. openedx_tagging.migrations.0015_taxonomy_export_id
"""
super().setUp()
Taxonomy.objects.get_or_create(id=-1, defaults={
@@ -54,7 +54,7 @@ class LanguageTaxonomyTestMixin:
"allow_free_text": False,
"visible_to_authors": True,
"export_id": "-1_languages",
"_taxonomy_class": "openedx_tagging.core.tagging.models.system_defined.LanguageTaxonomy",
"_taxonomy_class": "openedx_tagging.models.system_defined.LanguageTaxonomy",
})
TaxonomyOrg.objects.get_or_create(taxonomy_id=-1, defaults={"org": None})

View File

@@ -7,7 +7,7 @@ from typing import Dict, List, Union
from opaque_keys.edx.keys import CourseKey, UsageKey, CollectionKey, ContainerKey
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_tagging.core.tagging.models import Taxonomy
from openedx_tagging.models import Taxonomy
ContentKey = Union[LibraryLocatorV2, CourseKey, UsageKey, CollectionKey, ContainerKey]
ContextKey = Union[LibraryLocatorV2, CourseKey]

View File

@@ -7,7 +7,7 @@ from edx_django_utils.cache import RequestCache
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, CollectionKey, ContainerKey, UsageKey
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_tagging.core.tagging.models import Taxonomy
from openedx_tagging.models import Taxonomy
from organizations.models import Organization
from .models import TaxonomyOrg

View File

@@ -16,8 +16,8 @@ import threading
from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.utils.translation import gettext as _
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Component, ComponentVersion
from openedx_content import api as content_api
from openedx_content.models_api import Component, ComponentVersion
from opaque_keys.edx.keys import UsageKeyV2
from opaque_keys.edx.locator import LibraryUsageLocatorV2
from rest_framework.exceptions import NotFound
@@ -202,10 +202,10 @@ def get_component_from_usage_key(usage_key: UsageKeyV2) -> Component:
This is a lower-level function that will return a Component even if there is
no current draft version of that Component (because it's been soft-deleted).
"""
learning_package = authoring_api.get_learning_package_by_key(
learning_package = content_api.get_learning_package_by_key(
str(usage_key.context_key)
)
return authoring_api.get_component_by_key(
return content_api.get_component_by_key(
learning_package.id,
namespace='xblock.v1',
type_name=usage_key.block_type,

View File

@@ -12,7 +12,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db.transaction import atomic
from django.urls import reverse
from openedx_learning.api import authoring as authoring_api
from openedx_content import api as content_api
from lxml import etree
@@ -295,16 +295,16 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
usage_key = block.scope_ids.usage_id
with atomic():
component = self._get_component_from_usage_key(usage_key)
block_media_type = authoring_api.get_or_create_media_type(
block_media_type = content_api.get_or_create_media_type(
f"application/vnd.openedx.xblock.v1.{usage_key.block_type}+xml"
)
content = authoring_api.get_or_create_text_content(
content = content_api.get_or_create_text_content(
component.learning_package_id,
block_media_type.id,
text=serialized.olx_str,
created=now,
)
authoring_api.create_next_version(
content_api.create_next_component_version(
component.pk,
title=block.display_name,
content_to_replace={
@@ -329,9 +329,9 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
TODO: This is the third place where we're implementing this. Figure out
where the definitive place should be and have everything else call that.
"""
learning_package = authoring_api.get_learning_package_by_key(str(usage_key.lib_key))
learning_package = content_api.get_learning_package_by_key(str(usage_key.lib_key))
try:
component = authoring_api.get_component_by_key(
component = content_api.get_component_by_key(
learning_package.id,
namespace='xblock.v1',
type_name=usage_key.block_type,

View File

@@ -8,7 +8,7 @@ from xmodule.modulestore.django import contentstore, modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase, upload_file_to_course
from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory, ToyCourseFactory, LibraryFactory
from xmodule.util.sandboxing import DEFAULT_PYTHON_LIB_FILENAME
from openedx_tagging.core.tagging.models import Tag
from openedx_tagging.models import Tag
from openedx.core.djangoapps.content_tagging.models import TaxonomyOrg
from openedx.core.djangoapps.content_tagging import api as tagging_api

View File

@@ -63,7 +63,7 @@ numpy<2.0.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
openedx-learning==0.31.0
openedx-learning==0.32.0
# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
@@ -146,4 +146,3 @@ pact-python<3.0.0
# building requirements with Python 3.12
# https://github.com/openedx/edx-platform/issues/37880
sphinx-autoapi<3.6.1

View File

@@ -832,7 +832,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/kernel.in
openedx-learning==0.31.0
openedx-learning==0.32.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/kernel.in

View File

@@ -1399,7 +1399,7 @@ openedx-forum==0.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-learning==0.31.0
openedx-learning==0.32.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/doc.txt

View File

@@ -1009,7 +1009,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/base.txt
openedx-learning==0.31.0
openedx-learning==0.32.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt

View File

@@ -1059,7 +1059,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/base.txt
openedx-learning==0.31.0
openedx-learning==0.32.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt

View File

@@ -60,7 +60,7 @@ root_packages =
lms
cms
openedx
openedx_learning
openedx_content
include_external_packages = True
contract_types =
# Our custom contract which checks that we're only importing from 'api.py'
@@ -193,14 +193,17 @@ allowed_modules =
tests
[importlinter:contract:3]
name = Do not import apps from openedx-learning (only import from openedx_learning.api.* and openedx_learning.lib.*).
name = Do not directly import internals of openedx_content (only import from openedx_content.api).
type = forbidden
source_modules =
cms
lms
openedx
common
xmodule
forbidden_modules =
openedx_learning.apps
openedx_content.applets
openedx_content.backcompat
allow_indirect_imports = True
[importlinter:contract:4]