openedx-learning 0.19.0__py2.py3-none-any.whl → 0.19.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,4 +2,4 @@
2
2
  Open edX Learning ("Learning Core").
3
3
  """
4
4
 
5
- __version__ = "0.19.0"
5
+ __version__ = "0.19.1"
@@ -83,6 +83,8 @@ def create_component(
83
83
  local_key: str,
84
84
  created: datetime,
85
85
  created_by: int | None,
86
+ *,
87
+ can_stand_alone: bool = True,
86
88
  ) -> Component:
87
89
  """
88
90
  Create a new Component (an entity like a Problem or Video)
@@ -90,7 +92,11 @@ def create_component(
90
92
  key = f"{component_type.namespace}:{component_type.name}:{local_key}"
91
93
  with atomic():
92
94
  publishable_entity = publishing_api.create_publishable_entity(
93
- learning_package_id, key, created, created_by
95
+ learning_package_id,
96
+ key,
97
+ created,
98
+ created_by,
99
+ can_stand_alone=can_stand_alone
94
100
  )
95
101
  component = Component.objects.create(
96
102
  publishable_entity=publishable_entity,
@@ -239,13 +245,20 @@ def create_component_and_version( # pylint: disable=too-many-positional-argumen
239
245
  title: str,
240
246
  created: datetime,
241
247
  created_by: int | None = None,
248
+ *,
249
+ can_stand_alone: bool = True,
242
250
  ) -> tuple[Component, ComponentVersion]:
243
251
  """
244
252
  Create a Component and associated ComponentVersion atomically
245
253
  """
246
254
  with atomic():
247
255
  component = create_component(
248
- learning_package_id, component_type, local_key, created, created_by
256
+ learning_package_id,
257
+ component_type,
258
+ local_key,
259
+ created,
260
+ created_by,
261
+ can_stand_alone=can_stand_alone,
249
262
  )
250
263
  component_version = create_component_version(
251
264
  component.pk,
@@ -85,6 +85,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
85
85
  "learning_package",
86
86
  "created",
87
87
  "created_by",
88
+ "can_stand_alone",
88
89
  ]
89
90
  list_filter = ["learning_package"]
90
91
  search_fields = ["key", "uuid"]
@@ -98,6 +99,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
98
99
  "created",
99
100
  "created_by",
100
101
  "see_also",
102
+ "can_stand_alone",
101
103
  ]
102
104
  readonly_fields = [
103
105
  "key",
@@ -108,6 +110,7 @@ class PublishableEntityAdmin(ReadOnlyModelAdmin):
108
110
  "created",
109
111
  "created_by",
110
112
  "see_also",
113
+ "can_stand_alone",
111
114
  ]
112
115
 
113
116
  def get_queryset(self, request):
@@ -71,6 +71,8 @@ __all__ = [
71
71
  "create_container_version",
72
72
  "create_next_container_version",
73
73
  "get_container",
74
+ "get_container_by_key",
75
+ "get_containers",
74
76
  "ContainerEntityListEntry",
75
77
  "get_entities_in_container",
76
78
  "contains_unpublished_changes",
@@ -173,6 +175,8 @@ def create_publishable_entity(
173
175
  created: datetime,
174
176
  # User ID who created this
175
177
  created_by: int | None,
178
+ *,
179
+ can_stand_alone: bool = True,
176
180
  ) -> PublishableEntity:
177
181
  """
178
182
  Create a PublishableEntity.
@@ -185,6 +189,7 @@ def create_publishable_entity(
185
189
  key=key,
186
190
  created=created,
187
191
  created_by_id=created_by,
192
+ can_stand_alone=can_stand_alone,
188
193
  )
189
194
 
190
195
 
@@ -583,6 +588,8 @@ def create_container(
583
588
  key: str,
584
589
  created: datetime,
585
590
  created_by: int | None,
591
+ *,
592
+ can_stand_alone: bool = True,
586
593
  # The types on the following line are correct, but mypy will complain - https://github.com/python/mypy/issues/3737
587
594
  container_cls: type[ContainerModel] = Container, # type: ignore[assignment]
588
595
  ) -> ContainerModel:
@@ -595,6 +602,7 @@ def create_container(
595
602
  key: The key of the container.
596
603
  created: The date and time the container was created.
597
604
  created_by: The ID of the user who created the container
605
+ can_stand_alone: Set to False when created as part of containers
598
606
  container_cls: The subclass of Container to use, if applicable
599
607
 
600
608
  Returns:
@@ -603,7 +611,11 @@ def create_container(
603
611
  assert issubclass(container_cls, Container)
604
612
  with atomic():
605
613
  publishable_entity = create_publishable_entity(
606
- learning_package_id, key, created, created_by
614
+ learning_package_id,
615
+ key,
616
+ created,
617
+ created_by,
618
+ can_stand_alone=can_stand_alone,
607
619
  )
608
620
  container = container_cls.objects.create(
609
621
  publishable_entity=publishable_entity,
@@ -837,6 +849,43 @@ def get_container(pk: int) -> Container:
837
849
  return Container.objects.get(pk=pk)
838
850
 
839
851
 
852
+ def get_container_by_key(learning_package_id: int, /, key: str) -> Container:
853
+ """
854
+ [ 🛑 UNSTABLE ]
855
+ Get a container by its learning package and primary key.
856
+
857
+ Args:
858
+ learning_package_id: The ID of the learning package that contains the container.
859
+ key: The primary key of the container.
860
+
861
+ Returns:
862
+ The container with the given primary key.
863
+ """
864
+ return Container.objects.get(
865
+ publishable_entity__learning_package_id=learning_package_id,
866
+ publishable_entity__key=key,
867
+ )
868
+
869
+
870
+ def get_containers(
871
+ learning_package_id: int,
872
+ container_cls: type[ContainerModel] = Container, # type: ignore[assignment]
873
+ ) -> QuerySet[ContainerModel]:
874
+ """
875
+ [ 🛑 UNSTABLE ]
876
+ Get all containers in the given learning package.
877
+
878
+ Args:
879
+ learning_package_id: The primary key of the learning package
880
+ container_cls: The subclass of Container to use, if applicable
881
+
882
+ Returns:
883
+ A queryset containing the container associated with the given learning package.
884
+ """
885
+ assert issubclass(container_cls, Container)
886
+ return container_cls.objects.filter(publishable_entity__learning_package=learning_package_id)
887
+
888
+
840
889
  @dataclass(frozen=True)
841
890
  class ContainerEntityListEntry:
842
891
  """
@@ -0,0 +1,21 @@
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(
17
+ default=True,
18
+ help_text="Set to True when created independently, False when created as part of a container.",
19
+ ),
20
+ ),
21
+ ]
@@ -9,6 +9,7 @@ from django.conf import settings
9
9
  from django.core.exceptions import ImproperlyConfigured
10
10
  from django.core.validators import MinValueValidator
11
11
  from django.db import models
12
+ from django.utils.translation import gettext as _
12
13
 
13
14
  from openedx_learning.lib.fields import (
14
15
  case_insensitive_char_field,
@@ -126,6 +127,10 @@ class PublishableEntity(models.Model):
126
127
  null=True,
127
128
  blank=True,
128
129
  )
130
+ can_stand_alone = models.BooleanField(
131
+ default=True,
132
+ help_text=_("Set to True when created independently, False when created as part of a container."),
133
+ )
129
134
 
130
135
  class Meta:
131
136
  constraints = [
@@ -30,7 +30,12 @@ __all__ = [
30
30
 
31
31
 
32
32
  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
+ *,
38
+ can_stand_alone: bool = True,
34
39
  ) -> Unit:
35
40
  """
36
41
  [ 🛑 UNSTABLE ] Create a new unit.
@@ -40,12 +45,14 @@ def create_unit(
40
45
  key: The key.
41
46
  created: The creation date.
42
47
  created_by: The user who created the unit.
48
+ can_stand_alone: Set to False when created as part of containers
43
49
  """
44
50
  return publishing_api.create_container(
45
51
  learning_package_id,
46
52
  key,
47
53
  created,
48
54
  created_by,
55
+ can_stand_alone=can_stand_alone,
49
56
  container_cls=Unit,
50
57
  )
51
58
 
@@ -156,6 +163,7 @@ def create_unit_and_version(
156
163
  components: list[Component | ComponentVersion] | None = None,
157
164
  created: datetime,
158
165
  created_by: int | None = None,
166
+ can_stand_alone: bool = True,
159
167
  ) -> tuple[Unit, UnitVersion]:
160
168
  """
161
169
  [ 🛑 UNSTABLE ] Create a new unit and its version.
@@ -165,10 +173,17 @@ def create_unit_and_version(
165
173
  key: The key.
166
174
  created: The creation date.
167
175
  created_by: The user who created the unit.
176
+ can_stand_alone: Set to False when created as part of containers
168
177
  """
169
178
  publishable_entities_pks, entity_version_pks = _pub_entities_for_components(components)
170
179
  with atomic():
171
- unit = create_unit(learning_package_id, key, created, created_by)
180
+ unit = create_unit(
181
+ learning_package_id,
182
+ key,
183
+ created,
184
+ created_by,
185
+ can_stand_alone=can_stand_alone,
186
+ )
172
187
  unit_version = create_unit_version(
173
188
  unit,
174
189
  1,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: openedx-learning
3
- Version: 0.19.0
3
+ Version: 0.19.1
4
4
  Summary: Open edX Learning Core and Tagging.
5
5
  Home-page: https://github.com/openedx/openedx-learning
6
6
  Author: David Ormsbee
@@ -18,11 +18,11 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Requires-Python: >=3.11
20
20
  License-File: LICENSE.txt
21
- Requires-Dist: attrs
21
+ Requires-Dist: edx-drf-extensions
22
22
  Requires-Dist: Django<5.0
23
23
  Requires-Dist: rules<4.0
24
- Requires-Dist: edx-drf-extensions
25
24
  Requires-Dist: celery
25
+ Requires-Dist: attrs
26
26
  Requires-Dist: djangorestframework<4.0
27
27
  Dynamic: author
28
28
  Dynamic: author-email
@@ -1,4 +1,4 @@
1
- openedx_learning/__init__.py,sha256=de9JrtQMO5tVm1WwyxT5eY8XnVN7AdgqRkaQee-ZUGk,69
1
+ openedx_learning/__init__.py,sha256=px6MoC3pWrrXjiOIJXCYYjh1zVYJmN1_36ERKhfnEOQ,69
2
2
  openedx_learning/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  openedx_learning/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  openedx_learning/api/authoring.py,sha256=AuEegFTT3tAer_3zSxcxqLq3Yd_eilk-Hxt4ESYearA,970
@@ -18,7 +18,7 @@ openedx_learning/apps/authoring/collections/migrations/0005_alter_collection_opt
18
18
  openedx_learning/apps/authoring/collections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  openedx_learning/apps/authoring/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  openedx_learning/apps/authoring/components/admin.py,sha256=zfEpuBEySMYpUZzygaE2MDoI8SH-2H3xIL20YCSCMLo,4582
21
- openedx_learning/apps/authoring/components/api.py,sha256=ygSb5G8RzsIQzXqyJiqNwuGJp9r388ayuW3UuJNYHh8,24997
21
+ openedx_learning/apps/authoring/components/api.py,sha256=PmCtJiyw0Vy4PGSRt9JCStJQv9OJ4FchtOyiY1sfJ-s,25254
22
22
  openedx_learning/apps/authoring/components/apps.py,sha256=Rcydv_FH-rVvuWIFqUezBNOh8DtrZHyozZM2yqX2JKE,778
23
23
  openedx_learning/apps/authoring/components/models.py,sha256=ttZzVnMZTa14-qudrLb4CFuCanEQJT8cuC_iVPH8XTA,10887
24
24
  openedx_learning/apps/authoring/components/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -36,12 +36,13 @@ openedx_learning/apps/authoring/contents/models.py,sha256=8CYrHK7CZ4U3F7Den4ZV_a
36
36
  openedx_learning/apps/authoring/contents/migrations/0001_initial.py,sha256=FtOTmIGX2KHpjw-PHbfRjxkFEomI5CEDhNKCZ7IpFeE,3060
37
37
  openedx_learning/apps/authoring/contents/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  openedx_learning/apps/authoring/publishing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- openedx_learning/apps/authoring/publishing/admin.py,sha256=F-0QlVQmuovLIF258XK_vKJdOnn7lLa_0A5veE72TKc,4830
40
- openedx_learning/apps/authoring/publishing/api.py,sha256=JAm3a3ItiQYe9agqGHHNoTZ6LYhhMzw3JIZ-BVH9BMM,35911
39
+ openedx_learning/apps/authoring/publishing/admin.py,sha256=yPtqznDzlWHj0xpJ_FGFRviyftgPmuql5EoqKLeZQFo,4911
40
+ openedx_learning/apps/authoring/publishing/api.py,sha256=OregsRNQev41Nm1Uq6B_zoezJgV-EVsYaf8_iJ3sbxI,37430
41
41
  openedx_learning/apps/authoring/publishing/apps.py,sha256=v9PTe3YoICaYT9wfu268ZkVAlnZFvxi-DqYdbRi25bY,750
42
42
  openedx_learning/apps/authoring/publishing/migrations/0001_initial.py,sha256=wvekNV19YRSdxRmQaFnLSn_nCsQlHIucPDVMmgKf_OE,9272
43
43
  openedx_learning/apps/authoring/publishing/migrations/0002_alter_learningpackage_key_and_more.py,sha256=toI7qJhNukk6hirKfFx9EpqTpzF2O2Yq1VpFJusDn2M,806
44
44
  openedx_learning/apps/authoring/publishing/migrations/0003_containers.py,sha256=k5P1LFkjQT-42yyHE2f57dAMQKafLuJCTRaCBMKWcjc,2519
45
+ openedx_learning/apps/authoring/publishing/migrations/0004_publishableentity_can_stand_alone.py,sha256=RapDZKcjTp5QGgob9DUaf-zl_pQgg-5nz8PZWoXMlVA,549
45
46
  openedx_learning/apps/authoring/publishing/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
47
  openedx_learning/apps/authoring/publishing/models/__init__.py,sha256=Ou6Ks3qxEhel9uHLpzi77jlkb_IHc9_WIfqxOZJuqaE,1059
47
48
  openedx_learning/apps/authoring/publishing/models/container.py,sha256=GCUD3WTlgvgZSQOKcoOsfhNAfc5pz3Wbs9ClE9mhtB0,2594
@@ -49,9 +50,9 @@ openedx_learning/apps/authoring/publishing/models/draft_published.py,sha256=dDZf
49
50
  openedx_learning/apps/authoring/publishing/models/entity_list.py,sha256=EWpkUHUqJr5xKGJCRQ494xugdULxMXxYviFOot9ypUU,3009
50
51
  openedx_learning/apps/authoring/publishing/models/learning_package.py,sha256=1fuNLHD6k0qGuL0jXYGf4-TA5WczgxJrXUdAIM_JNBI,2688
51
52
  openedx_learning/apps/authoring/publishing/models/publish_log.py,sha256=-uMj1X8umqRM259ucOqNt6POeKDpY5RLdA8XnWXt7Go,4109
52
- openedx_learning/apps/authoring/publishing/models/publishable_entity.py,sha256=tErsaWW_Bh2B4ce_6kdx6mKmGDDFaLKW6W6Ambl0BFk,25191
53
+ openedx_learning/apps/authoring/publishing/models/publishable_entity.py,sha256=-6Fde_sYqaq1AUC4sw4Rf1X9Hh4jun2Py7Xhsl4VWuY,25419
53
54
  openedx_learning/apps/authoring/units/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
- openedx_learning/apps/authoring/units/api.py,sha256=1sFdNchAZ1aNLEN5imxJWo5D000ACjyoZNfrMzrlilM,9578
55
+ openedx_learning/apps/authoring/units/api.py,sha256=WjeM9E_5yH-zawlCzBY0BwCywRVeNxoojq4IDN9gOds,9957
55
56
  openedx_learning/apps/authoring/units/apps.py,sha256=cIzphjDw5sjIZ3NLE911N7IMUa8JQSXMReNl03uI7jg,701
56
57
  openedx_learning/apps/authoring/units/models.py,sha256=eTOwFWC9coQLf0ovx08Mj7zi8mPAWCw9QOznybajRk0,1418
57
58
  openedx_learning/apps/authoring/units/migrations/0001_initial.py,sha256=qM_0JGffxECVgXzncHXfgSE-g8u3L3a14R0M1Bnj_Ys,1129
@@ -124,8 +125,8 @@ openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=0HQD_Jrf6-YpocYfz
124
125
  openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
125
126
  openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=Hf92cy-tE767DE9FgsZcPKiCYrf5ihfETz8qGKBnuiU,36278
126
127
  openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
127
- openedx_learning-0.19.0.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
128
- openedx_learning-0.19.0.dist-info/METADATA,sha256=xvtsGiNCb627JQXOZqEi7Ws6bPM6TwMlBxKfkBn7zz0,8975
129
- openedx_learning-0.19.0.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
130
- openedx_learning-0.19.0.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
131
- openedx_learning-0.19.0.dist-info/RECORD,,
128
+ openedx_learning-0.19.1.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
129
+ openedx_learning-0.19.1.dist-info/METADATA,sha256=Jm1GoiVhtSJfYV6nlRXB7H5XPy8XVvGTivFNF7j0f0w,8975
130
+ openedx_learning-0.19.1.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
131
+ openedx_learning-0.19.1.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
132
+ openedx_learning-0.19.1.dist-info/RECORD,,