Skip to content

Commit 1f10aec

Browse files
refactor: pin versions in frozen list each time a new version is created
1 parent 3780e49 commit 1f10aec

2 files changed

Lines changed: 109 additions & 55 deletions

File tree

openedx_learning/apps/authoring/containers/api.py

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -70,34 +70,42 @@ def create_entity_list() -> EntityList:
7070
return EntityList.objects.create()
7171

7272

73-
def create_entity_list_row(
73+
def create_entity_list_rows(
7474
entity_list: EntityList,
75-
entity_pk: int,
76-
order_num: int,
77-
draft_version_pk: int | None,
78-
published_version_pk: int | None,
75+
entity_pks: list[int],
76+
draft_version_pks: list[int | None],
77+
published_version_pks: list[int | None],
7978
) -> EntityListRow:
8079
"""
81-
Create a new entity list row. This is a row in an entity list that references
82-
publishable entities.
80+
Create new entity list rows for an entity list.
8381
8482
Args:
85-
entity_list: The entity list that the entity list row belongs to.
86-
entity: The ID of the publishable entity that the entity list row references.
87-
order_num: The order_num of the entity list row in the entity list.
88-
draft_version_pk: The ID of the draft version of the entity (PublishableEntityVersion) that the entity list row references.
89-
published_version_pk: The ID of the published version of the entity (PublishableEntityVersion) that the entity list row references
83+
entity_list: The entity list to create the rows for.
84+
entity_pks: The IDs of the publishable entities that the entity list rows reference.
85+
draft_version_pks: The IDs of the draft versions of the entities (PublishableEntityVersion) that the entity list rows reference.
86+
published_version_pks: The IDs of the published versions of the entities (PublishableEntityVersion) that the entity list rows reference.
9087
9188
Returns:
92-
The newly created entity list row.
89+
The newly created entity list rows.
9390
"""
94-
return EntityListRow.objects.create(
95-
entity_list=entity_list,
96-
entity_id=entity_pk,
97-
order_num=order_num,
98-
draft_version_id=draft_version_pk,
99-
published_version_id=published_version_pk,
100-
)
91+
order_nums = range(len(entity_pks))
92+
with atomic():
93+
entity_list_rows = EntityListRow.objects.bulk_create(
94+
[
95+
EntityListRow(
96+
entity_list=entity_list,
97+
entity_id=entity_pk,
98+
order_num=order_num,
99+
draft_version_id=draft_version_pk,
100+
published_version_id=published_version_pk,
101+
)
102+
for entity_pk, order_num, draft_version_pk, published_version_pk in zip(
103+
entity_pks, order_nums, draft_version_pks, published_version_pks
104+
)
105+
]
106+
)
107+
108+
return entity_list_rows
101109

102110

103111
def create_entity_list_with_rows(
@@ -109,26 +117,55 @@ def create_entity_list_with_rows(
109117
Create a new entity list with rows.
110118
111119
Args:
112-
entity_pks: The IDs of the publishable entities that the entity list rows reference.
120+
entity_pks: The IDs of the publishable entities that the entity list rows
121+
reference.
113122
order_nums: The order_nums of the entity list rows in the entity list.
114-
draft_version_pks: The IDs of the draft versions of the entities (PublishableEntityVersion) that the entity list rows reference.
115-
published_version_pks: The IDs of the published versions of the entities (PublishableEntityVersion) that the entity list rows reference.
123+
draft_version_pks: The IDs of the draft versions of the entities
124+
(PublishableEntityVersion) that the entity list rows reference.
125+
published_version_pks: The IDs of the published versions of the entities
126+
(PublishableEntityVersion) that the entity list rows reference.
116127
117128
Returns:
118129
The newly created entity list.
119130
"""
120131
entity_list = create_entity_list()
121-
order_nums = range(len(entity_pks))
122-
for entity_pk, order_num, draft_version_pk, published_version_pk in zip(
123-
entity_pks, order_nums, draft_version_pks, published_version_pks
124-
):
125-
create_entity_list_row(
126-
entity_list=entity_list,
127-
entity_pk=entity_pk,
128-
order_num=order_num,
129-
draft_version_pk=draft_version_pk,
130-
published_version_pk=published_version_pk,
132+
create_entity_list_rows(
133+
entity_list=entity_list,
134+
entity_pks=entity_pks,
135+
draft_version_pks=draft_version_pks,
136+
published_version_pks=published_version_pks,
137+
)
138+
return entity_list
139+
140+
141+
def copy_rows_to_new_entity_list(
142+
rows: QuerySet[EntityListRow],
143+
) -> EntityList:
144+
"""
145+
Copy rows from an existing entity list to a new entity list.
146+
147+
Args:
148+
entity_list: The entity list to copy the rows to.
149+
rows: The rows to copy to the new entity list.
150+
151+
Returns:
152+
The newly created entity list.
153+
"""
154+
entity_list = create_entity_list()
155+
with atomic():
156+
entity_list_rows = EntityListRow.objects.bulk_create(
157+
[
158+
EntityListRow(
159+
entity_list=entity_list,
160+
entity_id=row.entity.id,
161+
order_num=row.order_num,
162+
draft_version_id=row.entity.draft.version.pk,
163+
published_version_id=row.entity.published.version.pk,
164+
)
165+
for row in rows
166+
]
131167
)
168+
132169
return entity_list
133170

134171

@@ -137,6 +174,8 @@ def create_container_version(
137174
version_num: int,
138175
title: str,
139176
publishable_entities_pk: list[int],
177+
draft_version_pks: list[int | None],
178+
published_version_pks: list[int | None],
140179
entity: PublishableEntity,
141180
created: datetime,
142181
created_by: int | None,
@@ -164,20 +203,17 @@ def create_container_version(
164203
created=created,
165204
created_by=created_by,
166205
)
167-
# This implementation assumes:
168-
# 1. We are creating the first version of the container, so the defined list is the same as the initial list.
169-
# 2. The frozen list is empty because this is the first version.
170-
# 3. Published and draft versions are always the latest for all members.
171206
entity_list = create_entity_list_with_rows(
172207
entity_pks=publishable_entities_pk,
173-
draft_version_pks=[None] * len(publishable_entities_pk),
174-
published_version_pks=[None] * len(publishable_entities_pk),
208+
draft_version_pks=draft_version_pks,
209+
published_version_pks=published_version_pks,
175210
)
176211
container_version = ContainerEntityVersion.objects.create(
177212
publishable_entity_version=publishable_entity_version,
178213
container_id=container_pk,
179214
defined_list=entity_list,
180215
initial_list=entity_list,
216+
# Would frozen_list be always None the 1st time a container is created?
181217
frozen_list=None,
182218
)
183219
return container_version
@@ -187,6 +223,8 @@ def create_next_container_version(
187223
container_pk: int,
188224
title: str,
189225
publishable_entities_pk: list[int],
226+
draft_version_pks: list[int | None],
227+
published_version_pks: list[int | None],
190228
entity: PublishableEntity,
191229
created: datetime,
192230
created_by: int | None,
@@ -216,23 +254,26 @@ def create_next_container_version(
216254
created=created,
217255
created_by=created_by,
218256
)
219-
# This implementation assumes:
220-
# 1. The changes provoking a new version are the addition, removal of members or reordering.
257+
# 1. The changes provoking a new version are always the addition, removal of members or reordering. How can we detect changes only in the metadata?
221258
# 2. Published and draft versions are always the latest for all members.
222259
# 3. When creating a new version, a new user-defined entity list is created to preserve the latest state as the previous user-defined list.
223-
# TODO: instead consider copying the previous user-defined list as the frozen list, and add/remove to the previous user-defined list.
224-
# If it's a reordering, the previous user-defined list is copied as the frozen and a new user-defined list is created with the new order.
260+
# 4. When creating a new version, a new frozen entity list is created copying the state of the user-defined list of the previous version.
261+
# 5. While copying the versions into the new frozen list, versions are pinned in new rows for published/draft versions.
225262
new_user_defined_list = create_entity_list_with_rows(
226263
entity_pks=publishable_entities_pk,
227-
draft_version_pks=[None] * len(publishable_entities_pk),
228-
published_version_pks=[None] * len(publishable_entities_pk),
264+
draft_version_pks=draft_version_pks,
265+
published_version_pks=published_version_pks,
229266
)
267+
new_frozen_list = copy_rows_to_new_entity_list(
268+
rows=get_defined_list_rows_for_container_version(last_version)
269+
)
270+
230271
container_version = ContainerEntityVersion.objects.create(
231272
publishable_entity_version=publishable_entity_version,
232273
container_id=container_pk,
233274
defined_list=new_user_defined_list,
234275
initial_list=last_version.initial_list,
235-
frozen_list=last_version.defined_list,
276+
frozen_list=new_frozen_list,
236277
)
237278
return container_version
238279

@@ -244,6 +285,8 @@ def create_container_and_version(
244285
created_by: int | None,
245286
title: str,
246287
publishable_entities_pk: list[int],
288+
draft_version_pks: list[int | None],
289+
published_version_pks: list[int | None],
247290
) -> ContainerEntityVersion:
248291
"""
249292
Create a new container and its first version.
@@ -267,6 +310,8 @@ def create_container_and_version(
267310
version_num=1,
268311
title=title,
269312
publishable_entities_pk=publishable_entities_pk,
313+
draft_version_pks=draft_version_pks,
314+
published_version_pks=published_version_pks,
270315
entity=container.publishable_entity,
271316
created=created,
272317
created_by=created_by,
@@ -283,13 +328,12 @@ def get_container(pk: int) -> ContainerEntity:
283328
284329
Returns:
285330
The container with the given primary key.
286-
287-
TODO: should this use with_publishing_relations as in components?
288331
"""
332+
# TODO: should this use with_publishing_relations as in components?
289333
return ContainerEntity.objects.get(pk=pk)
290334

291335

292-
def get_defined_list_for_container_version(
336+
def get_defined_list_rows_for_container_version(
293337
container_version: ContainerEntityVersion,
294338
) -> QuerySet[EntityListRow]:
295339
"""
@@ -304,7 +348,7 @@ def get_defined_list_for_container_version(
304348
return container_version.defined_list.entitylistrow_set.all()
305349

306350

307-
def get_initial_list_for_container_version(
351+
def get_initial_list_rows_for_container_version(
308352
container_version: ContainerEntityVersion,
309353
) -> QuerySet[EntityListRow]:
310354
"""
@@ -319,7 +363,7 @@ def get_initial_list_for_container_version(
319363
return container_version.initial_list.entitylistrow_set.all()
320364

321365

322-
def get_frozen_list_for_container_version(
366+
def get_frozen_list_rows_for_container_version(
323367
container_version: ContainerEntityVersion,
324368
) -> QuerySet[EntityListRow]:
325369
"""
@@ -331,4 +375,6 @@ def get_frozen_list_for_container_version(
331375
Returns:
332376
The frozen members of the container version.
333377
"""
378+
if container_version.frozen_list is None:
379+
return QuerySet[EntityListRow]()
334380
return container_version.frozen_list.entitylistrow_set.all()

openedx_learning/apps/authoring/units/api.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ def create_unit_version(
5454
unit: Unit,
5555
version_num: int,
5656
title: str,
57-
publishable_entities_pk: list[int],
57+
publishable_entities_pks: list[int],
58+
draft_version_pks: list[int | None],
59+
published_version_pks: list[int | None],
5860
created: datetime,
5961
created_by: int | None,
6062
) -> Unit:
@@ -74,7 +76,9 @@ def create_unit_version(
7476
unit.container_entity.pk,
7577
version_num,
7678
title,
77-
publishable_entities_pk,
79+
publishable_entities_pks,
80+
draft_version_pks,
81+
published_version_pks,
7882
unit.container_entity.publishable_entity,
7983
created,
8084
created_by,
@@ -90,7 +94,9 @@ def create_unit_version(
9094
def create_next_unit_version(
9195
unit: Unit,
9296
title: str,
93-
publishable_entities_pk: list[int],
97+
publishable_entities_pks: list[int],
98+
draft_version_pks: list[int | None],
99+
published_version_pks: list[int | None],
94100
created: datetime,
95101
created_by: int | None,
96102
) -> Unit:
@@ -110,7 +116,9 @@ def create_next_unit_version(
110116
container_entity_version = container_api.create_next_container_version(
111117
unit.container_entity.pk,
112118
title,
113-
publishable_entities_pk,
119+
publishable_entities_pks,
120+
draft_version_pks,
121+
published_version_pks,
114122
unit.container_entity.publishable_entity,
115123
created,
116124
created_by,

0 commit comments

Comments
 (0)