Skip to content

Commit e02cad8

Browse files
committed
feat: add can_stand_alone flag to publishable entity
It allows us to track components that were created independently and components that were created under a container like unit or subsection.
1 parent bf74b09 commit e02cad8

8 files changed

Lines changed: 55 additions & 5 deletions

File tree

openedx_learning/apps/authoring/components/api.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,15 @@ def create_component(
8383
local_key: str,
8484
created: datetime,
8585
created_by: int | None,
86+
can_stand_alone: bool = True,
8687
) -> Component:
8788
"""
8889
Create a new Component (an entity like a Problem or Video)
8990
"""
9091
key = f"{component_type.namespace}:{component_type.name}:{local_key}"
9192
with atomic():
9293
publishable_entity = publishing_api.create_publishable_entity(
93-
learning_package_id, key, created, created_by
94+
learning_package_id, key, created, created_by, can_stand_alone
9495
)
9596
component = Component.objects.create(
9697
publishable_entity=publishable_entity,
@@ -239,13 +240,19 @@ def create_component_and_version( # pylint: disable=too-many-positional-argumen
239240
title: str,
240241
created: datetime,
241242
created_by: int | None = None,
243+
can_stand_alone: bool = True,
242244
) -> tuple[Component, ComponentVersion]:
243245
"""
244246
Create a Component and associated ComponentVersion atomically
245247
"""
246248
with atomic():
247249
component = create_component(
248-
learning_package_id, component_type, local_key, created, created_by
250+
learning_package_id,
251+
component_type,
252+
local_key,
253+
created,
254+
created_by,
255+
can_stand_alone,
249256
)
250257
component_version = create_component_version(
251258
component.pk,

openedx_learning/apps/authoring/publishing/admin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
8585
"learning_package",
8686
"created",
8787
"created_by",
88+
"can_stand_alone",
8889
]
8990
list_filter = ["learning_package"]
9091
search_fields = ["key", "uuid"]
@@ -98,6 +99,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
9899
"created",
99100
"created_by",
100101
"see_also",
102+
"can_stand_alone",
101103
]
102104
readonly_fields = [
103105
"key",
@@ -108,6 +110,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
108110
"created",
109111
"created_by",
110112
"see_also",
113+
"can_stand_alone",
111114
]
112115

113116
def get_queryset(self, request):

openedx_learning/apps/authoring/publishing/api.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def create_publishable_entity(
173173
created: datetime,
174174
# User ID who created this
175175
created_by: int | None,
176+
can_stand_alone: bool = True,
176177
) -> PublishableEntity:
177178
"""
178179
Create a PublishableEntity.
@@ -185,6 +186,7 @@ def create_publishable_entity(
185186
key=key,
186187
created=created,
187188
created_by_id=created_by,
189+
can_stand_alone=can_stand_alone,
188190
)
189191

190192

@@ -583,6 +585,7 @@ def create_container(
583585
key: str,
584586
created: datetime,
585587
created_by: int | None,
588+
can_stand_alone: bool = True,
586589
# The types on the following line are correct, but mypy will complain - https://github.com/python/mypy/issues/3737
587590
container_cls: type[ContainerModel] = Container, # type: ignore[assignment]
588591
) -> ContainerModel:
@@ -603,7 +606,7 @@ def create_container(
603606
assert issubclass(container_cls, Container)
604607
with atomic():
605608
publishable_entity = create_publishable_entity(
606-
learning_package_id, key, created, created_by
609+
learning_package_id, key, created, created_by, can_stand_alone
607610
)
608611
container = container_cls.objects.create(
609612
publishable_entity=publishable_entity,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.19 on 2025-03-17 11:07
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('oel_publishing', '0003_containers'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='publishableentity',
15+
name='can_stand_alone',
16+
field=models.BooleanField(default=True),
17+
),
18+
]

openedx_learning/apps/authoring/publishing/models/publishable_entity.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class PublishableEntity(models.Model):
126126
null=True,
127127
blank=True,
128128
)
129+
can_stand_alone = models.BooleanField(default=True)
129130

130131
class Meta:
131132
constraints = [

openedx_learning/apps/authoring/units/api.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@
3030

3131

3232
def create_unit(
33-
learning_package_id: int, key: str, created: datetime, created_by: int | None
33+
learning_package_id: int,
34+
key: str,
35+
created: datetime,
36+
created_by: int | None,
37+
can_stand_alone: bool = True,
3438
) -> Unit:
3539
"""
3640
[ 🛑 UNSTABLE ] Create a new unit.
@@ -46,6 +50,7 @@ def create_unit(
4650
key,
4751
created,
4852
created_by,
53+
can_stand_alone,
4954
container_cls=Unit,
5055
)
5156

@@ -156,6 +161,7 @@ def create_unit_and_version(
156161
components: list[Component | ComponentVersion] | None = None,
157162
created: datetime,
158163
created_by: int | None = None,
164+
can_stand_alone: bool = True,
159165
) -> tuple[Unit, UnitVersion]:
160166
"""
161167
[ 🛑 UNSTABLE ] Create a new unit and its version.
@@ -168,7 +174,7 @@ def create_unit_and_version(
168174
"""
169175
publishable_entities_pks, entity_version_pks = _pub_entities_for_components(components)
170176
with atomic():
171-
unit = create_unit(learning_package_id, key, created, created_by)
177+
unit = create_unit(learning_package_id, key, created, created_by, can_stand_alone)
172178
unit_version = create_unit_version(
173179
unit,
174180
1,

tests/openedx_learning/apps/authoring/components/test_api.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ def setUpTestData(cls) -> None:
322322
local_key='my_component',
323323
created=cls.now,
324324
created_by=None,
325+
can_stand_alone=False,
325326
)
326327

327328
def test_simple_get(self):
@@ -333,6 +334,16 @@ def test_publishing_entity_key_convention(self):
333334
"""Our mapping convention is {namespace}:{component_type}:{local_key}"""
334335
assert self.problem.key == "xblock.v1:problem:my_component"
335336

337+
def test_stand_alone_flag(self):
338+
"""Check if can_stand_alone flag is set"""
339+
component = components_api.get_component_by_key(
340+
self.learning_package.id,
341+
namespace='xblock.v1',
342+
type_name='html',
343+
local_key='my_component',
344+
)
345+
assert not component.publishable_entity.can_stand_alone
346+
336347
def test_get_by_key(self):
337348
assert self.html == components_api.get_component_by_key(
338349
self.learning_package.id,

tests/openedx_learning/apps/authoring/units/test_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def test_create_empty_unit_and_version(self):
222222
assert unit.versioning.has_unpublished_changes
223223
assert unit.versioning.draft == unit_version
224224
assert unit.versioning.published is None
225+
assert unit.publishable_entity.can_stand_alone
225226

226227
def test_create_next_unit_version_with_two_unpinned_components(self):
227228
"""Test creating a unit version with two unpinned components.

0 commit comments

Comments
 (0)