Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ venv/
!.vscode/settings.json.example

# Media files (for uploads)
media/
/media/

# Media files generated during test runs
test_media/
/test_media/
2 changes: 1 addition & 1 deletion .importlinter
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ layers=

# The "contents" app stores the simplest pieces of binary and text data,
# without versioning information. These belong to a single Learning Package.
openedx_learning.apps.authoring.contents
openedx_learning.apps.authoring.media

# The "collections" app stores arbitrary groupings of PublishableEntities.
# Its only dependency should be the publishing app.
Expand Down
6 changes: 3 additions & 3 deletions olx_importer/management/commands/load_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

# Model references to remove
from openedx_learning.apps.authoring.components import api as components_api
from openedx_learning.apps.authoring.contents import api as contents_api
from openedx_learning.apps.authoring.media import api as contents_api
from openedx_learning.apps.authoring.publishing import api as publishing_api

SUPPORTED_TYPES = ["problem", "video", "html"]
Expand Down Expand Up @@ -116,7 +116,7 @@ def create_content(self, static_local_path, now, component_version):
logger.warning(f' Static reference not found: "{real_path}"')
return # Might as well bail if we can't find the file.

content = contents_api.get_or_create_file_content(
content = contents_api.get_or_create_file_media(
self.learning_package.id,
data=data_bytes,
mime_type=mime_type,
Expand Down Expand Up @@ -165,7 +165,7 @@ def import_block_type(self, block_type_name, now): # , publish_log_entry):

# Create the Content entry for the raw data...
text = xml_file_path.read_text('utf-8')
text_content, _created = contents_api.get_or_create_text_content(
text_content, _created = contents_api.get_or_create_text_media(
self.learning_package.id,
text=text,
mime_type=f"application/vnd.openedx.xblock.v1.{block_type_name}+xml",
Expand Down
2 changes: 1 addition & 1 deletion openedx_learning/api/authoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ..apps.authoring.backup_restore.api import *
from ..apps.authoring.collections.api import *
from ..apps.authoring.components.api import *
from ..apps.authoring.contents.api import *
from ..apps.authoring.media.api import *
from ..apps.authoring.publishing.api import *
from ..apps.authoring.sections.api import *
from ..apps.authoring.subsections.api import *
Expand Down
2 changes: 1 addition & 1 deletion openedx_learning/api/authoring_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# pylint: disable=wildcard-import
from ..apps.authoring.collections.models import *
from ..apps.authoring.components.models import *
from ..apps.authoring.contents.models import *
from ..apps.authoring.media.models import *
from ..apps.authoring.publishing.models import *
from ..apps.authoring.sections.models import *
from ..apps.authoring.subsections.models import *
Expand Down
20 changes: 10 additions & 10 deletions openedx_learning/apps/authoring/backup_restore/zipper.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
Collection,
ComponentType,
ComponentVersion,
ComponentVersionContent,
Content,
ComponentVersionMedia,
Media,
LearningPackage,
PublishableEntity,
PublishableEntityVersion,
Expand All @@ -47,7 +47,7 @@
)
from openedx_learning.apps.authoring.collections import api as collections_api
from openedx_learning.apps.authoring.components import api as components_api
from openedx_learning.apps.authoring.contents import api as contents_api
from openedx_learning.apps.authoring.media import api as contents_api
from openedx_learning.apps.authoring.publishing import api as publishing_api
from openedx_learning.apps.authoring.sections import api as sections_api
from openedx_learning.apps.authoring.subsections import api as subsections_api
Expand Down Expand Up @@ -190,13 +190,13 @@ def get_publishable_entities(self) -> QuerySet[PublishableEntity]:
# especially with large libraries (up to 100K items),
# which is too large for this type of prefetch.
Prefetch(
"draft__version__componentversion__componentversioncontent_set",
queryset=ComponentVersionContent.objects.select_related("content"),
"draft__version__componentversion__componentversionmedia_set",
queryset=ComponentVersionMedia.objects.select_related("media"),
to_attr="prefetched_contents",
),
Prefetch(
"published__version__componentversion__componentversioncontent_set",
queryset=ComponentVersionContent.objects.select_related("content"),
"published__version__componentversion__componentversionmedia_set",
queryset=ComponentVersionMedia.objects.select_related("media"),
to_attr="prefetched_contents",
),
)
Expand Down Expand Up @@ -373,11 +373,11 @@ def create_zip(self, path: str) -> None:

# Get content data associated with this version
contents: QuerySet[
ComponentVersionContent
ComponentVersionMedia
] = component_version.prefetched_contents # type: ignore[attr-defined]

for component_version_content in contents:
content: Content = component_version_content.content
content: Media = component_version_content.media

# Important: The component_version_content.key contains implicitly
# the file name and the file extension
Expand Down Expand Up @@ -984,7 +984,7 @@ def _resolve_static_files(
# storing the value as a content instance
if not self.learning_package_id:
raise ValueError("learning_package_id must be set before resolving static files.")
text_content = contents_api.get_or_create_text_content(
text_content = contents_api.get_or_create_text_media(
self.learning_package_id,
contents_api.get_or_create_media_type(f"application/vnd.openedx.xblock.v1.{block_type}+xml").id,
text=content_bytes.decode("utf-8"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from openedx_learning.lib.admin_utils import ReadOnlyModelAdmin

from .models import Component, ComponentVersion, ComponentVersionContent
from .models import Component, ComponentVersion, ComponentVersionMedia


class ComponentVersionInline(admin.TabularInline):
Expand Down Expand Up @@ -54,7 +54,7 @@ class ContentInline(admin.TabularInline):
"""
Django admin configuration for Content
"""
model = ComponentVersion.contents.through
model = ComponentVersion.media.through

def get_queryset(self, request):
queryset = super().get_queryset(request)
Expand Down Expand Up @@ -134,11 +134,11 @@ def format_text_for_admin_display(text: str) -> SafeText:
)


def content_preview(cvc_obj: ComponentVersionContent) -> SafeText:
def content_preview(cvc_obj: ComponentVersionMedia) -> SafeText:
"""
Get the HTML to display a preview of the given ComponentVersionContent
"""
content_obj = cvc_obj.content
content_obj = cvc_obj.media

if content_obj.media_type.type == "image":
# This base64 encoding looks really goofy and is bad for performance,
Expand Down
34 changes: 17 additions & 17 deletions openedx_learning/apps/authoring/components/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
from django.db.transaction import atomic
from django.http.response import HttpResponse, HttpResponseNotFound

from ..contents import api as contents_api
from ..media import api as contents_api
from ..publishing import api as publishing_api
from .models import Component, ComponentType, ComponentVersion, ComponentVersionContent
from .models import Component, ComponentType, ComponentVersion, ComponentVersionMedia

# The public API that will be re-exported by openedx_learning.apps.authoring.api
# is listed in the __all__ entries below. Internal helper functions that are
Expand Down Expand Up @@ -256,7 +256,7 @@ def create_next_component_version(
# RFC 2046: https://datatracker.ietf.org/doc/html/rfc2046
media_type_str = media_type_str or "application/octet-stream"
media_type = contents_api.get_or_create_media_type(media_type_str)
content = contents_api.get_or_create_file_content(
content = contents_api.get_or_create_file_media(
component.learning_package.id,
media_type.id,
data=file_content,
Expand All @@ -265,8 +265,8 @@ def create_next_component_version(
content_pk = content.pk
else:
content_pk = content_pk_or_bytes
ComponentVersionContent.objects.create(
content_id=content_pk,
ComponentVersionMedia.objects.create(
media_id=content_pk,
component_version=component_version,
key=key,
)
Expand All @@ -276,12 +276,12 @@ def create_next_component_version(

# Now copy any old associations that existed, as long as they aren't
# in conflict with the new stuff or marked for deletion.
last_version_content_mapping = ComponentVersionContent.objects \
last_version_content_mapping = ComponentVersionMedia.objects \
.filter(component_version=last_version)
for cvrc in last_version_content_mapping:
if cvrc.key not in content_to_replace:
ComponentVersionContent.objects.create(
content_id=cvrc.content_id,
ComponentVersionMedia.objects.create(
media_id=cvrc.media_id,
component_version=component_version,
key=cvrc.key,
)
Expand Down Expand Up @@ -454,7 +454,7 @@ def look_up_component_version_content(
component_key: str,
version_num: int,
key: Path,
) -> ComponentVersionContent:
) -> ComponentVersionMedia:
"""
Look up ComponentVersionContent by human readable keys.

Expand All @@ -470,7 +470,7 @@ def look_up_component_version_content(
& Q(component_version__publishable_entity_version__version_num=version_num)
& Q(key=key)
)
return ComponentVersionContent.objects \
return ComponentVersionMedia.objects \
.select_related(
"content",
"content__media_type",
Expand All @@ -485,7 +485,7 @@ def create_component_version_content(
content_id: int,
/,
key: str,
) -> ComponentVersionContent:
) -> ComponentVersionMedia:
"""
Add a Content to the given ComponentVersion

Expand All @@ -503,9 +503,9 @@ def create_component_version_content(
)
key = key.lstrip('/')

cvrc, _created = ComponentVersionContent.objects.get_or_create(
cvrc, _created = ComponentVersionMedia.objects.get_or_create(
component_version_id=component_version_id,
content_id=content_id,
media_id=content_id,
key=key,
)
return cvrc
Expand Down Expand Up @@ -621,8 +621,8 @@ def _error_header(error: AssetError) -> dict[str, str]:

# Check: Does the ComponentVersion have the requested asset (Content)?
try:
cv_content = component_version.componentversioncontent_set.get(key=asset_path)
except ComponentVersionContent.DoesNotExist:
cv_content = component_version.componentversionmedia_set.get(key=asset_path)
except ComponentVersionMedia.DoesNotExist:
logger.error(f"ComponentVersion {component_version_uuid} has no asset {asset_path}")
info_headers.update(
_error_header(AssetError.ASSET_PATH_NOT_FOUND_FOR_COMPONENT_VERSION)
Expand All @@ -634,7 +634,7 @@ def _error_header(error: AssetError) -> dict[str, str]:
# anyway, but we're explicitly not doing so because streaming large text
# fields from the database is less scalable, and we don't want to encourage
# that usage pattern.
content = cv_content.content
content = cv_content.media
if not content.has_file:
logger.error(
f"ComponentVersion {component_version_uuid} has asset {asset_path}, "
Expand All @@ -647,7 +647,7 @@ def _error_header(error: AssetError) -> dict[str, str]:

# At this point, we know that there is valid Content that we want to send.
# This adds Content-level headers, like the hash/etag and content type.
info_headers.update(contents_api.get_content_info_headers(content))
info_headers.update(contents_api.get_media_info_headers(content))

# Recompute redirect headers (reminder: this should never be cached).
redirect_headers = contents_api.get_redirect_headers(content.path, public)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ def handle(self, *args, **options):
f"Created v{next_version.version_num} of "
f"{next_version.component.key} ({next_version.uuid}):"
)
for cvc in next_version.componentversioncontent_set.all():
for cvc in next_version.componentversionmedia_set.all():
self.stdout.write(f"- {cvc.key} ({cvc.uuid})")
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.9 on 2025-12-23 19:15

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('oel_components', '0004_remove_componentversioncontent_uuid'),
('oel_contents', '0003_rename_index'),
]

operations = [
migrations.RenameModel(
old_name='ComponentVersionContent',
new_name='ComponentVersionMedia',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 5.2.9 on 2025-12-23 19:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('oel_components', '0005_rename_componentversioncontent_componentversionmedia'),
('oel_contents', '0003_rename_index'),
]

operations = [
migrations.RemoveConstraint(
model_name='componentversionmedia',
name='oel_cvcontent_uniq_cv_key',
),
migrations.RemoveIndex(
model_name='componentversionmedia',
name='oel_cvcontent_c_cv',
),
migrations.RemoveIndex(
model_name='componentversionmedia',
name='oel_cvcontent_cv_d',
),
migrations.RenameField(
model_name='componentversion',
old_name='contents',
new_name='media',
),
migrations.RenameField(
model_name='componentversionmedia',
old_name='content',
new_name='media',
),
migrations.AddIndex(
model_name='componentversionmedia',
index=models.Index(fields=['media', 'component_version'], name='oel_cvmediat_c_cv'),
),
migrations.AddIndex(
model_name='componentversionmedia',
index=models.Index(fields=['component_version', 'media'], name='oel_cvmedia_cv_d'),
),
migrations.AddConstraint(
model_name='componentversionmedia',
constraint=models.UniqueConstraint(fields=('component_version', 'key'), name='oel_cvmedia_uniq_cv_key'),
),
]
Loading
Loading