Skip to content
Merged
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
2 changes: 1 addition & 1 deletion openedx_learning/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Open edX Learning ("Learning Core").
"""

__version__ = "0.30.1"
__version__ = "0.30.2"
3 changes: 0 additions & 3 deletions openedx_learning/apps/authoring/backup_restore/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class EntitySerializer(serializers.Serializer): # pylint: disable=abstract-meth
can_stand_alone = serializers.BooleanField(required=True)
key = serializers.CharField(required=True)
created = serializers.DateTimeField(required=True, default_timezone=timezone.utc)
created_by = serializers.CharField(required=True, allow_null=True)


class EntityVersionSerializer(serializers.Serializer): # pylint: disable=abstract-method
Expand All @@ -54,7 +53,6 @@ class EntityVersionSerializer(serializers.Serializer): # pylint: disable=abstra
title = serializers.CharField(required=True)
entity_key = serializers.CharField(required=True)
created = serializers.DateTimeField(required=True, default_timezone=timezone.utc)
created_by = serializers.CharField(required=True, allow_null=True)
version_num = serializers.IntegerField(required=True)


Expand Down Expand Up @@ -160,7 +158,6 @@ class CollectionSerializer(serializers.Serializer): # pylint: disable=abstract-
title = serializers.CharField(required=True)
key = serializers.CharField(required=True)
description = serializers.CharField(required=True, allow_blank=True)
created_by = serializers.IntegerField(required=True, allow_null=True)
entities = serializers.ListField(
child=serializers.CharField(),
required=True,
Expand Down
23 changes: 18 additions & 5 deletions openedx_learning/apps/authoring/backup_restore/zipper.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ class LearningPackageUnzipper:
def __init__(self, zipf: zipfile.ZipFile, key: str | None = None, user: UserType | None = None):
self.zipf = zipf
self.user = user
self.user_id = getattr(self.user, "id", None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for future PRs: You can use the AnonymousUser if you need a user that has no ID but can still be used for things like permissions checks.

self.lp_key = key # If provided, use this key for the restored learning package
self.learning_package_id: int | None = None # Will be set upon restoration
self.utc_now: datetime = datetime.now(timezone.utc)
Expand Down Expand Up @@ -771,7 +772,9 @@ def _save_collections(self, learning_package, collections):
"""Save collections and their entities."""
for valid_collection in collections.get("collections", []):
entities = valid_collection.pop("entities", [])
collection = collections_api.create_collection(learning_package.id, **valid_collection)
collection = collections_api.create_collection(
learning_package.id, created_by=self.user_id, **valid_collection
)
collection = collections_api.add_to_collection(
learning_package_id=learning_package.id,
key=collection.key,
Expand All @@ -782,7 +785,7 @@ def _save_components(self, learning_package, components, component_static_files)
"""Save components and published component versions."""
for valid_component in components.get("components", []):
entity_key = valid_component.pop("key")
component = components_api.create_component(learning_package.id, **valid_component)
component = components_api.create_component(learning_package.id, created_by=self.user_id, **valid_component)
self.components_map_by_key[entity_key] = component

for valid_published in components.get("components_published", []):
Expand All @@ -796,14 +799,15 @@ def _save_components(self, learning_package, components, component_static_files)
self.components_map_by_key[entity_key].publishable_entity.id,
content_to_replace=content_to_replace,
force_version_num=valid_published.pop("version_num", None),
created_by=self.user_id,
**valid_published
)

def _save_units(self, learning_package, containers):
"""Save units and published unit versions."""
for valid_unit in containers.get("unit", []):
entity_key = valid_unit.get("key")
unit = units_api.create_unit(learning_package.id, **valid_unit)
unit = units_api.create_unit(learning_package.id, created_by=self.user_id, **valid_unit)
self.units_map_by_key[entity_key] = unit

for valid_published in containers.get("unit_published", []):
Expand All @@ -816,14 +820,17 @@ def _save_units(self, learning_package, containers):
self.units_map_by_key[entity_key],
force_version_num=valid_published.pop("version_num", None),
components=children,
created_by=self.user_id,
**valid_published
)

def _save_subsections(self, learning_package, containers):
"""Save subsections and published subsection versions."""
for valid_subsection in containers.get("subsection", []):
entity_key = valid_subsection.get("key")
subsection = subsections_api.create_subsection(learning_package.id, **valid_subsection)
subsection = subsections_api.create_subsection(
learning_package.id, created_by=self.user_id, **valid_subsection
)
self.subsections_map_by_key[entity_key] = subsection

for valid_published in containers.get("subsection_published", []):
Expand All @@ -836,14 +843,15 @@ def _save_subsections(self, learning_package, containers):
self.subsections_map_by_key[entity_key],
units=children,
force_version_num=valid_published.pop("version_num", None),
created_by=self.user_id,
**valid_published
)

def _save_sections(self, learning_package, containers):
"""Save sections and published section versions."""
for valid_section in containers.get("section", []):
entity_key = valid_section.get("key")
section = sections_api.create_section(learning_package.id, **valid_section)
section = sections_api.create_section(learning_package.id, created_by=self.user_id, **valid_section)
self.sections_map_by_key[entity_key] = section

for valid_published in containers.get("section_published", []):
Expand All @@ -856,6 +864,7 @@ def _save_sections(self, learning_package, containers):
self.sections_map_by_key[entity_key],
subsections=children,
force_version_num=valid_published.pop("version_num", None),
created_by=self.user_id,
**valid_published
)

Expand All @@ -874,6 +883,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
# Drafts can diverge from published, so we allow ignoring previous content
# Use case: published v1 had files A, B; draft v2 only has file A
ignore_previous_content=True,
created_by=self.user_id,
**valid_draft
)

Expand All @@ -887,6 +897,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
self.units_map_by_key[entity_key],
components=children,
force_version_num=valid_draft.pop("version_num", None),
created_by=self.user_id,
**valid_draft
)

Expand All @@ -900,6 +911,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
self.subsections_map_by_key[entity_key],
units=children,
force_version_num=valid_draft.pop("version_num", None),
created_by=self.user_id,
**valid_draft
)

Expand All @@ -913,6 +925,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
self.sections_map_by_key[entity_key],
subsections=children,
force_version_num=valid_draft.pop("version_num", None),
created_by=self.user_id,
**valid_draft
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class RestoreLearningPackageCommandTest(RestoreTestCase):
@patch("openedx_learning.apps.authoring.backup_restore.api.load_learning_package")
def test_restore_command(self, mock_load_learning_package):
# Mock load_learning_package to return our in-memory zip file
restore_result = LearningPackageUnzipper(self.zip_file, self.user).load()
restore_result = LearningPackageUnzipper(self.zip_file, user=self.user).load()
mock_load_learning_package.return_value = restore_result

out = StringIO()
Expand Down Expand Up @@ -63,20 +63,28 @@ def verify_containers(self, lp):
assert container.key in expected_container_keys
draft_version = publishing_api.get_draft_version(container.publishable_entity.id)
published_version = publishing_api.get_published_version(container.publishable_entity.id)
assert container.created_by is not None
assert container.created_by.username == "lp_user"
if container.key == "unit1-b7eafb":
assert getattr(container, 'unit', None) is not None
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
elif container.key == "subsection1-48afa3":
assert getattr(container, 'subsection', None) is not None
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
elif container.key == "section1-8ca126":
assert getattr(container, 'section', None) is not None
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
else:
assert False, f"Unexpected container key: {container.key}"
Expand All @@ -98,11 +106,15 @@ def verify_components(self, lp):
assert component.key in expected_component_keys
draft_version = publishing_api.get_draft_version(component.publishable_entity.id)
published_version = publishing_api.get_published_version(component.publishable_entity.id)
assert component.created_by is not None
assert component.created_by.username == "lp_user"
if component.key == "xblock.v1:drag-and-drop-v2:4d1b2fac-8b30-42fb-872d-6b10ab580b27":
assert component.component_type.name == "drag-and-drop-v2"
assert component.component_type.namespace == "xblock.v1"
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
# Get the content associated with this component
contents = draft_version.componentversion.contents.all()
Expand All @@ -116,19 +128,27 @@ def verify_components(self, lp):
assert component.component_type.namespace == "xblock.v1"
assert draft_version is not None
assert draft_version.version_num == 5
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is not None
assert published_version.version_num == 4
assert published_version.created_by is not None
assert published_version.created_by.username == "lp_user"
elif component.key == "xblock.v1:openassessment:1ee38208-a585-4455-a27e-4930aa541f53":
assert component.component_type.name == "openassessment"
assert component.component_type.namespace == "xblock.v1"
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
elif component.key == "xblock.v1:problem:256739e8-c2df-4ced-bd10-8156f6cfa90b":
assert component.component_type.name == "problem"
assert component.component_type.namespace == "xblock.v1"
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
elif component.key == "xblock.v1:survey:6681da3f-b056-4c6e-a8f9-040967907471":
assert component.component_type.name == "survey"
Expand All @@ -141,12 +161,18 @@ def verify_components(self, lp):
assert component.component_type.namespace == "xblock.v1"
assert draft_version is not None
assert draft_version.version_num == 3
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is None
elif component.key == "xblock.v1:html:c22b9f97-f1e9-4e8f-87f0-d5a3c26083e2":
assert draft_version is not None
assert draft_version.version_num == 2
assert draft_version.created_by is not None
assert draft_version.created_by.username == "lp_user"
assert published_version is not None
assert published_version.version_num == 2
assert published_version.created_by is not None
assert published_version.created_by.username == "lp_user"
else:
assert False, f"Unexpected component key: {component.key}"

Expand All @@ -158,6 +184,9 @@ def verify_collections(self, lp):
assert collection.title == "Collection test1"
assert collection.key == "collection-test"
assert collection.description == ""
assert collection.created_by is not None
assert collection.created_by.username == "lp_user"

expected_entity_keys = [
"xblock.v1:html:e32d5479-9492-41f6-9222-550a7346bc37",
"xblock.v1:problem:256739e8-c2df-4ced-bd10-8156f6cfa90b",
Expand Down