Skip to content

Commit 5f22b59

Browse files
authored
fix: add missing author value when restoring entities (#448)
1 parent 4fefc53 commit 5f22b59

File tree

4 files changed

+49
-10
lines changed

4 files changed

+49
-10
lines changed

openedx_learning/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Open edX Learning ("Learning Core").
33
"""
44

5-
__version__ = "0.30.1"
5+
__version__ = "0.30.2"

openedx_learning/apps/authoring/backup_restore/serializers.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class EntitySerializer(serializers.Serializer): # pylint: disable=abstract-meth
4444
can_stand_alone = serializers.BooleanField(required=True)
4545
key = serializers.CharField(required=True)
4646
created = serializers.DateTimeField(required=True, default_timezone=timezone.utc)
47-
created_by = serializers.CharField(required=True, allow_null=True)
4847

4948

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

6058

@@ -160,7 +158,6 @@ class CollectionSerializer(serializers.Serializer): # pylint: disable=abstract-
160158
title = serializers.CharField(required=True)
161159
key = serializers.CharField(required=True)
162160
description = serializers.CharField(required=True, allow_blank=True)
163-
created_by = serializers.IntegerField(required=True, allow_null=True)
164161
entities = serializers.ListField(
165162
child=serializers.CharField(),
166163
required=True,

openedx_learning/apps/authoring/backup_restore/zipper.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ class LearningPackageUnzipper:
508508
def __init__(self, zipf: zipfile.ZipFile, key: str | None = None, user: UserType | None = None):
509509
self.zipf = zipf
510510
self.user = user
511+
self.user_id = getattr(self.user, "id", None)
511512
self.lp_key = key # If provided, use this key for the restored learning package
512513
self.learning_package_id: int | None = None # Will be set upon restoration
513514
self.utc_now: datetime = datetime.now(timezone.utc)
@@ -771,7 +772,9 @@ def _save_collections(self, learning_package, collections):
771772
"""Save collections and their entities."""
772773
for valid_collection in collections.get("collections", []):
773774
entities = valid_collection.pop("entities", [])
774-
collection = collections_api.create_collection(learning_package.id, **valid_collection)
775+
collection = collections_api.create_collection(
776+
learning_package.id, created_by=self.user_id, **valid_collection
777+
)
775778
collection = collections_api.add_to_collection(
776779
learning_package_id=learning_package.id,
777780
key=collection.key,
@@ -782,7 +785,7 @@ def _save_components(self, learning_package, components, component_static_files)
782785
"""Save components and published component versions."""
783786
for valid_component in components.get("components", []):
784787
entity_key = valid_component.pop("key")
785-
component = components_api.create_component(learning_package.id, **valid_component)
788+
component = components_api.create_component(learning_package.id, created_by=self.user_id, **valid_component)
786789
self.components_map_by_key[entity_key] = component
787790

788791
for valid_published in components.get("components_published", []):
@@ -796,14 +799,15 @@ def _save_components(self, learning_package, components, component_static_files)
796799
self.components_map_by_key[entity_key].publishable_entity.id,
797800
content_to_replace=content_to_replace,
798801
force_version_num=valid_published.pop("version_num", None),
802+
created_by=self.user_id,
799803
**valid_published
800804
)
801805

802806
def _save_units(self, learning_package, containers):
803807
"""Save units and published unit versions."""
804808
for valid_unit in containers.get("unit", []):
805809
entity_key = valid_unit.get("key")
806-
unit = units_api.create_unit(learning_package.id, **valid_unit)
810+
unit = units_api.create_unit(learning_package.id, created_by=self.user_id, **valid_unit)
807811
self.units_map_by_key[entity_key] = unit
808812

809813
for valid_published in containers.get("unit_published", []):
@@ -816,14 +820,17 @@ def _save_units(self, learning_package, containers):
816820
self.units_map_by_key[entity_key],
817821
force_version_num=valid_published.pop("version_num", None),
818822
components=children,
823+
created_by=self.user_id,
819824
**valid_published
820825
)
821826

822827
def _save_subsections(self, learning_package, containers):
823828
"""Save subsections and published subsection versions."""
824829
for valid_subsection in containers.get("subsection", []):
825830
entity_key = valid_subsection.get("key")
826-
subsection = subsections_api.create_subsection(learning_package.id, **valid_subsection)
831+
subsection = subsections_api.create_subsection(
832+
learning_package.id, created_by=self.user_id, **valid_subsection
833+
)
827834
self.subsections_map_by_key[entity_key] = subsection
828835

829836
for valid_published in containers.get("subsection_published", []):
@@ -836,14 +843,15 @@ def _save_subsections(self, learning_package, containers):
836843
self.subsections_map_by_key[entity_key],
837844
units=children,
838845
force_version_num=valid_published.pop("version_num", None),
846+
created_by=self.user_id,
839847
**valid_published
840848
)
841849

842850
def _save_sections(self, learning_package, containers):
843851
"""Save sections and published section versions."""
844852
for valid_section in containers.get("section", []):
845853
entity_key = valid_section.get("key")
846-
section = sections_api.create_section(learning_package.id, **valid_section)
854+
section = sections_api.create_section(learning_package.id, created_by=self.user_id, **valid_section)
847855
self.sections_map_by_key[entity_key] = section
848856

849857
for valid_published in containers.get("section_published", []):
@@ -856,6 +864,7 @@ def _save_sections(self, learning_package, containers):
856864
self.sections_map_by_key[entity_key],
857865
subsections=children,
858866
force_version_num=valid_published.pop("version_num", None),
867+
created_by=self.user_id,
859868
**valid_published
860869
)
861870

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

@@ -887,6 +897,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
887897
self.units_map_by_key[entity_key],
888898
components=children,
889899
force_version_num=valid_draft.pop("version_num", None),
900+
created_by=self.user_id,
890901
**valid_draft
891902
)
892903

@@ -900,6 +911,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
900911
self.subsections_map_by_key[entity_key],
901912
units=children,
902913
force_version_num=valid_draft.pop("version_num", None),
914+
created_by=self.user_id,
903915
**valid_draft
904916
)
905917

@@ -913,6 +925,7 @@ def _save_draft_versions(self, components, containers, component_static_files):
913925
self.sections_map_by_key[entity_key],
914926
subsections=children,
915927
force_version_num=valid_draft.pop("version_num", None),
928+
created_by=self.user_id,
916929
**valid_draft
917930
)
918931

tests/openedx_learning/apps/authoring/backup_restore/test_restore.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class RestoreLearningPackageCommandTest(RestoreTestCase):
3434
@patch("openedx_learning.apps.authoring.backup_restore.api.load_learning_package")
3535
def test_restore_command(self, mock_load_learning_package):
3636
# Mock load_learning_package to return our in-memory zip file
37-
restore_result = LearningPackageUnzipper(self.zip_file, self.user).load()
37+
restore_result = LearningPackageUnzipper(self.zip_file, user=self.user).load()
3838
mock_load_learning_package.return_value = restore_result
3939

4040
out = StringIO()
@@ -63,20 +63,28 @@ def verify_containers(self, lp):
6363
assert container.key in expected_container_keys
6464
draft_version = publishing_api.get_draft_version(container.publishable_entity.id)
6565
published_version = publishing_api.get_published_version(container.publishable_entity.id)
66+
assert container.created_by is not None
67+
assert container.created_by.username == "lp_user"
6668
if container.key == "unit1-b7eafb":
6769
assert getattr(container, 'unit', None) is not None
6870
assert draft_version is not None
6971
assert draft_version.version_num == 2
72+
assert draft_version.created_by is not None
73+
assert draft_version.created_by.username == "lp_user"
7074
assert published_version is None
7175
elif container.key == "subsection1-48afa3":
7276
assert getattr(container, 'subsection', None) is not None
7377
assert draft_version is not None
7478
assert draft_version.version_num == 2
79+
assert draft_version.created_by is not None
80+
assert draft_version.created_by.username == "lp_user"
7581
assert published_version is None
7682
elif container.key == "section1-8ca126":
7783
assert getattr(container, 'section', None) is not None
7884
assert draft_version is not None
7985
assert draft_version.version_num == 2
86+
assert draft_version.created_by is not None
87+
assert draft_version.created_by.username == "lp_user"
8088
assert published_version is None
8189
else:
8290
assert False, f"Unexpected container key: {container.key}"
@@ -98,11 +106,15 @@ def verify_components(self, lp):
98106
assert component.key in expected_component_keys
99107
draft_version = publishing_api.get_draft_version(component.publishable_entity.id)
100108
published_version = publishing_api.get_published_version(component.publishable_entity.id)
109+
assert component.created_by is not None
110+
assert component.created_by.username == "lp_user"
101111
if component.key == "xblock.v1:drag-and-drop-v2:4d1b2fac-8b30-42fb-872d-6b10ab580b27":
102112
assert component.component_type.name == "drag-and-drop-v2"
103113
assert component.component_type.namespace == "xblock.v1"
104114
assert draft_version is not None
105115
assert draft_version.version_num == 2
116+
assert draft_version.created_by is not None
117+
assert draft_version.created_by.username == "lp_user"
106118
assert published_version is None
107119
# Get the content associated with this component
108120
contents = draft_version.componentversion.contents.all()
@@ -116,19 +128,27 @@ def verify_components(self, lp):
116128
assert component.component_type.namespace == "xblock.v1"
117129
assert draft_version is not None
118130
assert draft_version.version_num == 5
131+
assert draft_version.created_by is not None
132+
assert draft_version.created_by.username == "lp_user"
119133
assert published_version is not None
120134
assert published_version.version_num == 4
135+
assert published_version.created_by is not None
136+
assert published_version.created_by.username == "lp_user"
121137
elif component.key == "xblock.v1:openassessment:1ee38208-a585-4455-a27e-4930aa541f53":
122138
assert component.component_type.name == "openassessment"
123139
assert component.component_type.namespace == "xblock.v1"
124140
assert draft_version is not None
125141
assert draft_version.version_num == 2
142+
assert draft_version.created_by is not None
143+
assert draft_version.created_by.username == "lp_user"
126144
assert published_version is None
127145
elif component.key == "xblock.v1:problem:256739e8-c2df-4ced-bd10-8156f6cfa90b":
128146
assert component.component_type.name == "problem"
129147
assert component.component_type.namespace == "xblock.v1"
130148
assert draft_version is not None
131149
assert draft_version.version_num == 2
150+
assert draft_version.created_by is not None
151+
assert draft_version.created_by.username == "lp_user"
132152
assert published_version is None
133153
elif component.key == "xblock.v1:survey:6681da3f-b056-4c6e-a8f9-040967907471":
134154
assert component.component_type.name == "survey"
@@ -141,12 +161,18 @@ def verify_components(self, lp):
141161
assert component.component_type.namespace == "xblock.v1"
142162
assert draft_version is not None
143163
assert draft_version.version_num == 3
164+
assert draft_version.created_by is not None
165+
assert draft_version.created_by.username == "lp_user"
144166
assert published_version is None
145167
elif component.key == "xblock.v1:html:c22b9f97-f1e9-4e8f-87f0-d5a3c26083e2":
146168
assert draft_version is not None
147169
assert draft_version.version_num == 2
170+
assert draft_version.created_by is not None
171+
assert draft_version.created_by.username == "lp_user"
148172
assert published_version is not None
149173
assert published_version.version_num == 2
174+
assert published_version.created_by is not None
175+
assert published_version.created_by.username == "lp_user"
150176
else:
151177
assert False, f"Unexpected component key: {component.key}"
152178

@@ -158,6 +184,9 @@ def verify_collections(self, lp):
158184
assert collection.title == "Collection test1"
159185
assert collection.key == "collection-test"
160186
assert collection.description == ""
187+
assert collection.created_by is not None
188+
assert collection.created_by.username == "lp_user"
189+
161190
expected_entity_keys = [
162191
"xblock.v1:html:e32d5479-9492-41f6-9222-550a7346bc37",
163192
"xblock.v1:problem:256739e8-c2df-4ced-bd10-8156f6cfa90b",

0 commit comments

Comments
 (0)