openedx-learning 0.28.0__py2.py3-none-any.whl → 0.29.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.
- openedx_learning/__init__.py +1 -1
- openedx_learning/apps/authoring/backup_restore/api.py +19 -4
- openedx_learning/apps/authoring/backup_restore/management/commands/lp_dump.py +22 -4
- openedx_learning/apps/authoring/backup_restore/management/commands/lp_load.py +57 -0
- openedx_learning/apps/authoring/backup_restore/serializers.py +168 -0
- openedx_learning/apps/authoring/backup_restore/toml.py +202 -24
- openedx_learning/apps/authoring/backup_restore/zipper.py +874 -54
- openedx_learning/apps/authoring/components/api.py +55 -0
- openedx_learning/apps/authoring/components/migrations/0004_remove_componentversioncontent_uuid.py +17 -0
- openedx_learning/apps/authoring/components/models.py +1 -3
- openedx_learning/apps/authoring/publishing/api.py +36 -4
- openedx_learning/apps/authoring/sections/api.py +17 -0
- openedx_learning/apps/authoring/subsections/api.py +17 -0
- openedx_learning/apps/authoring/units/api.py +17 -0
- {openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/METADATA +13 -4
- {openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/RECORD +21 -18
- openedx_tagging/core/tagging/models/base.py +7 -5
- openedx_tagging/core/tagging/models/utils.py +1 -1
- {openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/WHEEL +0 -0
- {openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/licenses/LICENSE.txt +0 -0
- {openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/top_level.txt +0 -0
|
@@ -34,6 +34,7 @@ from .models import Component, ComponentType, ComponentVersion, ComponentVersion
|
|
|
34
34
|
# to be callable only by other apps in the authoring package.
|
|
35
35
|
__all__ = [
|
|
36
36
|
"get_or_create_component_type",
|
|
37
|
+
"get_or_create_component_type_by_entity_key",
|
|
37
38
|
"create_component",
|
|
38
39
|
"create_component_version",
|
|
39
40
|
"create_next_component_version",
|
|
@@ -73,6 +74,27 @@ def get_or_create_component_type(namespace: str, name: str) -> ComponentType:
|
|
|
73
74
|
return component_type
|
|
74
75
|
|
|
75
76
|
|
|
77
|
+
def get_or_create_component_type_by_entity_key(entity_key: str) -> tuple[ComponentType, str]:
|
|
78
|
+
"""
|
|
79
|
+
Get or create a ComponentType based on a full entity key string.
|
|
80
|
+
|
|
81
|
+
The entity key is expected to be in the format
|
|
82
|
+
``"{namespace}:{type_name}:{local_key}"``. This function will parse out the
|
|
83
|
+
``namespace`` and ``type_name`` parts and use those to get or create the
|
|
84
|
+
ComponentType.
|
|
85
|
+
|
|
86
|
+
Raises ValueError if the entity_key is not in the expected format.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
namespace, type_name, local_key = entity_key.split(':', 2)
|
|
90
|
+
except ValueError as exc:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Invalid entity_key format: {entity_key!r}. "
|
|
93
|
+
"Expected format: '{namespace}:{type_name}:{local_key}'"
|
|
94
|
+
) from exc
|
|
95
|
+
return get_or_create_component_type(namespace, type_name), local_key
|
|
96
|
+
|
|
97
|
+
|
|
76
98
|
def create_component(
|
|
77
99
|
learning_package_id: int,
|
|
78
100
|
/,
|
|
@@ -137,10 +159,28 @@ def create_next_component_version(
|
|
|
137
159
|
created: datetime,
|
|
138
160
|
title: str | None = None,
|
|
139
161
|
created_by: int | None = None,
|
|
162
|
+
*,
|
|
163
|
+
force_version_num: int | None = None,
|
|
164
|
+
ignore_previous_content: bool = False,
|
|
140
165
|
) -> ComponentVersion:
|
|
141
166
|
"""
|
|
142
167
|
Create a new ComponentVersion based on the most recent version.
|
|
143
168
|
|
|
169
|
+
Args:
|
|
170
|
+
component_pk (int): The primary key of the Component to version.
|
|
171
|
+
content_to_replace (dict): Mapping of file keys to Content IDs,
|
|
172
|
+
None (for deletion), or bytes (for new file content).
|
|
173
|
+
created (datetime): The creation timestamp for the new version.
|
|
174
|
+
title (str, optional): Title for the new version. If None, uses the previous version's title.
|
|
175
|
+
created_by (int, optional): User ID of the creator.
|
|
176
|
+
force_version_num (int, optional): If provided, overrides the automatic version number increment and sets
|
|
177
|
+
this version's number explicitly. Use this if you need to restore or import a version with a specific
|
|
178
|
+
version number, such as during data migration or when synchronizing with external systems.
|
|
179
|
+
ignore_previous_content (bool): If True, do not copy over content from the previous version.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
ComponentVersion: The newly created ComponentVersion instance.
|
|
183
|
+
|
|
144
184
|
A very common pattern for making a new ComponentVersion is going to be "make
|
|
145
185
|
it just like the last version, except changing these one or two things".
|
|
146
186
|
Before calling this, you should create any new contents via the contents
|
|
@@ -161,6 +201,14 @@ def create_next_component_version(
|
|
|
161
201
|
convenient to remove paths (e.g. due to deprecation) without having to
|
|
162
202
|
always check for its existence first.
|
|
163
203
|
|
|
204
|
+
Why use force_version_num?
|
|
205
|
+
Normally, the version number is incremented automatically from the latest version. If you need to set a specific
|
|
206
|
+
version number (for example, when restoring from backup, importing legacy data, or synchronizing with another
|
|
207
|
+
system), use force_version_num to override the default behavior.
|
|
208
|
+
|
|
209
|
+
Why not use create_component_version?
|
|
210
|
+
The main reason is that we want to reuse the logic to create a static file component from a dictionary.
|
|
211
|
+
|
|
164
212
|
TODO: Have to add learning_downloadable info to this when it comes time to
|
|
165
213
|
support static asset download.
|
|
166
214
|
"""
|
|
@@ -180,6 +228,9 @@ def create_next_component_version(
|
|
|
180
228
|
if title is None:
|
|
181
229
|
title = last_version.title
|
|
182
230
|
|
|
231
|
+
if force_version_num is not None:
|
|
232
|
+
next_version_num = force_version_num
|
|
233
|
+
|
|
183
234
|
with atomic():
|
|
184
235
|
publishable_entity_version = publishing_api.create_publishable_entity_version(
|
|
185
236
|
component_pk,
|
|
@@ -219,6 +270,10 @@ def create_next_component_version(
|
|
|
219
270
|
component_version=component_version,
|
|
220
271
|
key=key,
|
|
221
272
|
)
|
|
273
|
+
|
|
274
|
+
if ignore_previous_content:
|
|
275
|
+
return component_version
|
|
276
|
+
|
|
222
277
|
# Now copy any old associations that existed, as long as they aren't
|
|
223
278
|
# in conflict with the new stuff or marked for deletion.
|
|
224
279
|
last_version_content_mapping = ComponentVersionContent.objects \
|
openedx_learning/apps/authoring/components/migrations/0004_remove_componentversioncontent_uuid.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-10-02 14:00
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('oel_components', '0003_remove_componentversioncontent_learner_downloadable'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name='componentversioncontent',
|
|
15
|
+
name='uuid',
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -21,7 +21,7 @@ from typing import ClassVar
|
|
|
21
21
|
|
|
22
22
|
from django.db import models
|
|
23
23
|
|
|
24
|
-
from ....lib.fields import case_sensitive_char_field,
|
|
24
|
+
from ....lib.fields import case_sensitive_char_field, key_field
|
|
25
25
|
from ....lib.managers import WithRelationsManager
|
|
26
26
|
from ..contents.models import Content
|
|
27
27
|
from ..publishing.models import LearningPackage, PublishableEntityMixin, PublishableEntityVersionMixin
|
|
@@ -240,8 +240,6 @@ class ComponentVersionContent(models.Model):
|
|
|
240
240
|
component_version = models.ForeignKey(ComponentVersion, on_delete=models.CASCADE)
|
|
241
241
|
content = models.ForeignKey(Content, on_delete=models.RESTRICT)
|
|
242
242
|
|
|
243
|
-
uuid = immutable_uuid_field()
|
|
244
|
-
|
|
245
243
|
# "key" is a reserved word for MySQL, so we're temporarily using the column
|
|
246
244
|
# name of "_key" to avoid breaking downstream tooling. A possible
|
|
247
245
|
# alternative name for this would be "path", since it's most often used as
|
|
@@ -89,6 +89,7 @@ __all__ = [
|
|
|
89
89
|
"get_containers_with_entity",
|
|
90
90
|
"get_container_children_count",
|
|
91
91
|
"bulk_draft_changes_for",
|
|
92
|
+
"get_container_children_entities_keys",
|
|
92
93
|
]
|
|
93
94
|
|
|
94
95
|
|
|
@@ -1141,6 +1142,7 @@ def create_next_container_version(
|
|
|
1141
1142
|
created_by: int | None,
|
|
1142
1143
|
container_version_cls: type[ContainerVersionModel] = ContainerVersion, # type: ignore[assignment]
|
|
1143
1144
|
entities_action: ChildrenEntitiesAction = ChildrenEntitiesAction.REPLACE,
|
|
1145
|
+
force_version_num: int | None = None,
|
|
1144
1146
|
) -> ContainerVersionModel:
|
|
1145
1147
|
"""
|
|
1146
1148
|
[ 🛑 UNSTABLE ]
|
|
@@ -1161,26 +1163,40 @@ def create_next_container_version(
|
|
|
1161
1163
|
created: The date and time the container version was created.
|
|
1162
1164
|
created_by: The ID of the user who created the container version.
|
|
1163
1165
|
container_version_cls: The subclass of ContainerVersion to use, if applicable.
|
|
1166
|
+
force_version_num (int, optional): If provided, overrides the automatic version number increment and sets
|
|
1167
|
+
this version's number explicitly. Use this if you need to restore or import a version with a specific
|
|
1168
|
+
version number, such as during data migration or when synchronizing with external systems.
|
|
1164
1169
|
|
|
1165
1170
|
Returns:
|
|
1166
1171
|
The newly created container version.
|
|
1172
|
+
|
|
1173
|
+
Why use force_version_num?
|
|
1174
|
+
Normally, the version number is incremented automatically from the latest version.
|
|
1175
|
+
If you need to set a specific version number (for example, when restoring from backup,
|
|
1176
|
+
importing legacy data, or synchronizing with another system),
|
|
1177
|
+
use force_version_num to override the default behavior.
|
|
1167
1178
|
"""
|
|
1168
1179
|
assert issubclass(container_version_cls, ContainerVersion)
|
|
1169
1180
|
with atomic():
|
|
1170
1181
|
container = Container.objects.select_related("publishable_entity").get(pk=container_pk)
|
|
1171
1182
|
entity = container.publishable_entity
|
|
1172
1183
|
last_version = container.versioning.latest
|
|
1173
|
-
|
|
1174
|
-
|
|
1184
|
+
if last_version is None:
|
|
1185
|
+
next_version_num = 1
|
|
1186
|
+
else:
|
|
1187
|
+
next_version_num = last_version.version_num + 1
|
|
1188
|
+
|
|
1189
|
+
if force_version_num is not None:
|
|
1190
|
+
next_version_num = force_version_num
|
|
1175
1191
|
|
|
1176
|
-
if entity_rows is None:
|
|
1192
|
+
if entity_rows is None and last_version is not None:
|
|
1177
1193
|
# We're only changing metadata. Keep the same entity list.
|
|
1178
1194
|
next_entity_list = last_version.entity_list
|
|
1179
1195
|
else:
|
|
1180
1196
|
next_entity_list = create_next_entity_list(
|
|
1181
1197
|
entity.learning_package_id,
|
|
1182
1198
|
last_version,
|
|
1183
|
-
entity_rows,
|
|
1199
|
+
entity_rows if entity_rows is not None else [],
|
|
1184
1200
|
entities_action
|
|
1185
1201
|
)
|
|
1186
1202
|
|
|
@@ -1468,6 +1484,22 @@ def get_container_children_count(
|
|
|
1468
1484
|
return container_version.entity_list.entitylistrow_set.filter(**filter_deleted).count()
|
|
1469
1485
|
|
|
1470
1486
|
|
|
1487
|
+
def get_container_children_entities_keys(container_version: ContainerVersion) -> list[str]:
|
|
1488
|
+
"""
|
|
1489
|
+
Fetch the list of entity keys for all entities in the given container version.
|
|
1490
|
+
|
|
1491
|
+
Args:
|
|
1492
|
+
container_version: The ContainerVersion to fetch the entity keys for.
|
|
1493
|
+
Returns:
|
|
1494
|
+
A list of entity keys for all entities in the container version, ordered by entity key.
|
|
1495
|
+
"""
|
|
1496
|
+
return list(
|
|
1497
|
+
container_version.entity_list.entitylistrow_set
|
|
1498
|
+
.values_list("entity__key", flat=True)
|
|
1499
|
+
.order_by("order_num")
|
|
1500
|
+
)
|
|
1501
|
+
|
|
1502
|
+
|
|
1471
1503
|
def bulk_draft_changes_for(
|
|
1472
1504
|
learning_package_id: int,
|
|
1473
1505
|
changed_by: int | None = None,
|
|
@@ -131,6 +131,7 @@ def create_next_section_version(
|
|
|
131
131
|
created: datetime,
|
|
132
132
|
created_by: int | None = None,
|
|
133
133
|
entities_action: publishing_api.ChildrenEntitiesAction = publishing_api.ChildrenEntitiesAction.REPLACE,
|
|
134
|
+
force_version_num: int | None = None,
|
|
134
135
|
) -> SectionVersion:
|
|
135
136
|
"""
|
|
136
137
|
[ 🛑 UNSTABLE ] Create the next section version.
|
|
@@ -142,6 +143,21 @@ def create_next_section_version(
|
|
|
142
143
|
Passing None will leave the existing subsections unchanged.
|
|
143
144
|
created: The creation date.
|
|
144
145
|
created_by: The user who created the section.
|
|
146
|
+
force_version_num (int, optional): If provided, overrides the automatic version number increment and sets
|
|
147
|
+
this version's number explicitly. Use this if you need to restore or import a version with a specific
|
|
148
|
+
version number, such as during data migration or when synchronizing with external systems.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The newly created SectionVersion.
|
|
152
|
+
|
|
153
|
+
Why use force_version_num?
|
|
154
|
+
Normally, the version number is incremented automatically from the latest version.
|
|
155
|
+
If you need to set a specific version number (for example, when restoring from backup,
|
|
156
|
+
importing legacy data, or synchronizing with another system),
|
|
157
|
+
use force_version_num to override the default behavior.
|
|
158
|
+
|
|
159
|
+
Why not use create_component_version?
|
|
160
|
+
The main reason is that we want to reuse the logic for adding entities to this container.
|
|
145
161
|
"""
|
|
146
162
|
entity_rows = _pub_entities_for_subsections(subsections)
|
|
147
163
|
section_version = publishing_api.create_next_container_version(
|
|
@@ -152,6 +168,7 @@ def create_next_section_version(
|
|
|
152
168
|
created_by=created_by,
|
|
153
169
|
container_version_cls=SectionVersion,
|
|
154
170
|
entities_action=entities_action,
|
|
171
|
+
force_version_num=force_version_num,
|
|
155
172
|
)
|
|
156
173
|
return section_version
|
|
157
174
|
|
|
@@ -130,6 +130,7 @@ def create_next_subsection_version(
|
|
|
130
130
|
created: datetime,
|
|
131
131
|
created_by: int | None = None,
|
|
132
132
|
entities_action: publishing_api.ChildrenEntitiesAction = publishing_api.ChildrenEntitiesAction.REPLACE,
|
|
133
|
+
force_version_num: int | None = None,
|
|
133
134
|
) -> SubsectionVersion:
|
|
134
135
|
"""
|
|
135
136
|
[ 🛑 UNSTABLE ] Create the next subsection version.
|
|
@@ -141,6 +142,21 @@ def create_next_subsection_version(
|
|
|
141
142
|
will leave the existing units unchanged.
|
|
142
143
|
created: The creation date.
|
|
143
144
|
created_by: The user who created the subsection.
|
|
145
|
+
force_version_num (int, optional): If provided, overrides the automatic version number increment and sets
|
|
146
|
+
this version's number explicitly. Use this if you need to restore or import a version with a specific
|
|
147
|
+
version number, such as during data migration or when synchronizing with external systems.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
The newly created subsection version.
|
|
151
|
+
|
|
152
|
+
Why use force_version_num?
|
|
153
|
+
Normally, the version number is incremented automatically from the latest version.
|
|
154
|
+
If you need to set a specific version number (for example, when restoring from backup,
|
|
155
|
+
importing legacy data, or synchronizing with another system),
|
|
156
|
+
use force_version_num to override the default behavior.
|
|
157
|
+
|
|
158
|
+
Why not use create_component_version?
|
|
159
|
+
The main reason is that we want to reuse the logic for adding entities to this container.
|
|
144
160
|
"""
|
|
145
161
|
entity_rows = _pub_entities_for_units(units)
|
|
146
162
|
subsection_version = publishing_api.create_next_container_version(
|
|
@@ -151,6 +167,7 @@ def create_next_subsection_version(
|
|
|
151
167
|
created_by=created_by,
|
|
152
168
|
container_version_cls=SubsectionVersion,
|
|
153
169
|
entities_action=entities_action,
|
|
170
|
+
force_version_num=force_version_num,
|
|
154
171
|
)
|
|
155
172
|
return subsection_version
|
|
156
173
|
|
|
@@ -79,6 +79,21 @@ def create_unit_version(
|
|
|
79
79
|
entity_rows: child entities/versions
|
|
80
80
|
created: The creation date.
|
|
81
81
|
created_by: The user who created the unit.
|
|
82
|
+
force_version_num (int, optional): If provided, overrides the automatic version number increment and sets
|
|
83
|
+
this version's number explicitly. Use this if you need to restore or import a version with a specific
|
|
84
|
+
version number, such as during data migration or when synchronizing with external systems.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
UnitVersion: The newly created UnitVersion instance.
|
|
88
|
+
|
|
89
|
+
Why use force_version_num?
|
|
90
|
+
Normally, the version number is incremented automatically from the latest version.
|
|
91
|
+
If you need to set a specific version number (for example, when restoring from backup,
|
|
92
|
+
importing legacy data, or synchronizing with another system),
|
|
93
|
+
use force_version_num to override the default behavior.
|
|
94
|
+
|
|
95
|
+
Why not use create_component_version?
|
|
96
|
+
The main reason is that we want to reuse the logic for adding entities to this container.
|
|
82
97
|
"""
|
|
83
98
|
return publishing_api.create_container_version(
|
|
84
99
|
unit.pk,
|
|
@@ -131,6 +146,7 @@ def create_next_unit_version(
|
|
|
131
146
|
created: datetime,
|
|
132
147
|
created_by: int | None = None,
|
|
133
148
|
entities_action: publishing_api.ChildrenEntitiesAction = publishing_api.ChildrenEntitiesAction.REPLACE,
|
|
149
|
+
force_version_num: int | None = None,
|
|
134
150
|
) -> UnitVersion:
|
|
135
151
|
"""
|
|
136
152
|
[ 🛑 UNSTABLE ] Create the next unit version.
|
|
@@ -152,6 +168,7 @@ def create_next_unit_version(
|
|
|
152
168
|
created_by=created_by,
|
|
153
169
|
container_version_cls=UnitVersion,
|
|
154
170
|
entities_action=entities_action,
|
|
171
|
+
force_version_num=force_version_num,
|
|
155
172
|
)
|
|
156
173
|
return unit_version
|
|
157
174
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openedx-learning
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.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
|
|
@@ -19,13 +19,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
License-File: LICENSE.txt
|
|
22
|
-
Requires-Dist: celery
|
|
23
22
|
Requires-Dist: rules<4.0
|
|
24
|
-
Requires-Dist: Django
|
|
25
23
|
Requires-Dist: edx-drf-extensions
|
|
24
|
+
Requires-Dist: Django
|
|
26
25
|
Requires-Dist: tomlkit
|
|
27
|
-
Requires-Dist: attrs
|
|
28
26
|
Requires-Dist: djangorestframework<4.0
|
|
27
|
+
Requires-Dist: attrs
|
|
28
|
+
Requires-Dist: celery
|
|
29
29
|
Dynamic: author
|
|
30
30
|
Dynamic: author-email
|
|
31
31
|
Dynamic: classifier
|
|
@@ -148,6 +148,15 @@ Every time you develop something in this repo
|
|
|
148
148
|
|
|
149
149
|
# Open a PR and ask for review.
|
|
150
150
|
|
|
151
|
+
Configuring Visual Studio Code
|
|
152
|
+
------------------------------
|
|
153
|
+
|
|
154
|
+
If you are using VS Code as your editor, you can enable the Testing bar by copying from the example configuration provided in the ``.vscode`` directory::
|
|
155
|
+
|
|
156
|
+
cd .vscode/
|
|
157
|
+
cp launch.json.example launch.json
|
|
158
|
+
cp settings.json.example settings.json
|
|
159
|
+
|
|
151
160
|
License
|
|
152
161
|
-------
|
|
153
162
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
openedx_learning/__init__.py,sha256=
|
|
1
|
+
openedx_learning/__init__.py,sha256=Z6oFsZrI2xFIXJ8CCTDE4l0MUEFWwTjOpo6C06gHz0U,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=EDWTY_JDKtjD9nFrrijzWuVccs3LZeDLEdzTUNanR4I,1111
|
|
@@ -7,14 +7,16 @@ openedx_learning/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
7
7
|
openedx_learning/apps/authoring/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
openedx_learning/apps/authoring/backup_restore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
openedx_learning/apps/authoring/backup_restore/admin.py,sha256=OnEixkOuysPRr-F6C_CMwPkiXawkqgSEF46n3yiUK0o,59
|
|
10
|
-
openedx_learning/apps/authoring/backup_restore/api.py,sha256=
|
|
10
|
+
openedx_learning/apps/authoring/backup_restore/api.py,sha256=TlQmb9VADDj8UlNjOczlwOfY8JVFfGyZ3UN5nluL9Tw,1243
|
|
11
11
|
openedx_learning/apps/authoring/backup_restore/apps.py,sha256=UnExBA7jhd3qI30_87JMvzVhS_k82t89qDVKSMpvg_A,340
|
|
12
12
|
openedx_learning/apps/authoring/backup_restore/models.py,sha256=jlr0ppxW0IOW3HPHoJNChHvDrYVnKMb5_3uC2itxqQk,45
|
|
13
|
-
openedx_learning/apps/authoring/backup_restore/
|
|
14
|
-
openedx_learning/apps/authoring/backup_restore/
|
|
13
|
+
openedx_learning/apps/authoring/backup_restore/serializers.py,sha256=LBWrlWmA0Aok-XSfwiGBqNTU-1ig4hnEX_AxFaAjaoY,6487
|
|
14
|
+
openedx_learning/apps/authoring/backup_restore/toml.py,sha256=4QFKDoFXsEF3xr1gvRXKQien-4_GezH6Gn1BdYQF8yU,8286
|
|
15
|
+
openedx_learning/apps/authoring/backup_restore/zipper.py,sha256=S3pxdsKmt-KeTaJlJBzZg0qKOmUOvhkSKP12N6dGguU,47171
|
|
15
16
|
openedx_learning/apps/authoring/backup_restore/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
17
|
openedx_learning/apps/authoring/backup_restore/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
openedx_learning/apps/authoring/backup_restore/management/commands/lp_dump.py,sha256=
|
|
18
|
+
openedx_learning/apps/authoring/backup_restore/management/commands/lp_dump.py,sha256=6-8D1rnDajTAIJu1HZLayY87hTfMnkaumYgSmLWpiGE,2302
|
|
19
|
+
openedx_learning/apps/authoring/backup_restore/management/commands/lp_load.py,sha256=z-glaYCulllcptqFbX5cb0UL_b9NslaaHK-0kOmPLa4,2205
|
|
18
20
|
openedx_learning/apps/authoring/backup_restore/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
21
|
openedx_learning/apps/authoring/collections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
22
|
openedx_learning/apps/authoring/collections/admin.py,sha256=f0hySjDMfIphVDEGkCSMIUHoEiqHRA7EE2NiO7lvL4g,1156
|
|
@@ -29,15 +31,16 @@ openedx_learning/apps/authoring/collections/migrations/0005_alter_collection_opt
|
|
|
29
31
|
openedx_learning/apps/authoring/collections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
32
|
openedx_learning/apps/authoring/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
33
|
openedx_learning/apps/authoring/components/admin.py,sha256=zfEpuBEySMYpUZzygaE2MDoI8SH-2H3xIL20YCSCMLo,4582
|
|
32
|
-
openedx_learning/apps/authoring/components/api.py,sha256=
|
|
34
|
+
openedx_learning/apps/authoring/components/api.py,sha256=fnc7PaxaQFsTkJHfQMsxu62V4wHGXt4I5fjJHT9xDsU,25386
|
|
33
35
|
openedx_learning/apps/authoring/components/apps.py,sha256=hi1SF2Z8Ex0hgE82wJK5Z_vYYfbcRhtaUW1zWZCdJYI,786
|
|
34
|
-
openedx_learning/apps/authoring/components/models.py,sha256=
|
|
36
|
+
openedx_learning/apps/authoring/components/models.py,sha256=j0AXjCDSBqqBlhP_06aGEuLFiJkhsolA0FdA41NbCmg,10830
|
|
35
37
|
openedx_learning/apps/authoring/components/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
38
|
openedx_learning/apps/authoring/components/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
39
|
openedx_learning/apps/authoring/components/management/commands/add_assets_to_component.py,sha256=0dJ77NZZoNzYheOdFPXtJrjdL_Z-pCNg3l1rbEGnMCY,3175
|
|
38
40
|
openedx_learning/apps/authoring/components/migrations/0001_initial.py,sha256=446LkJSFeK8J_-l-bxakZ_BVx_CiJIllGcBYqWcEenA,4664
|
|
39
41
|
openedx_learning/apps/authoring/components/migrations/0002_alter_componentversioncontent_key.py,sha256=98724dtucRjJCRyLt5p45qXYb2d6-ouVGp7PB6zTG6E,539
|
|
40
42
|
openedx_learning/apps/authoring/components/migrations/0003_remove_componentversioncontent_learner_downloadable.py,sha256=hDAkKdBvKULepML9pVMqkZg31nAyCeszQHJsFJ4qGws,382
|
|
43
|
+
openedx_learning/apps/authoring/components/migrations/0004_remove_componentversioncontent_uuid.py,sha256=hmCUt9VkiisGPckXjVY_qDNNkyj3r6KMK-MpasgOf6w,384
|
|
41
44
|
openedx_learning/apps/authoring/components/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
45
|
openedx_learning/apps/authoring/contents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
46
|
openedx_learning/apps/authoring/contents/admin.py,sha256=9Njd_lje1emcd168KBWUTGf0mVJ6K-dMYMcqHNjRU4k,1761
|
|
@@ -48,7 +51,7 @@ openedx_learning/apps/authoring/contents/migrations/0001_initial.py,sha256=FtOTm
|
|
|
48
51
|
openedx_learning/apps/authoring/contents/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
52
|
openedx_learning/apps/authoring/publishing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
53
|
openedx_learning/apps/authoring/publishing/admin.py,sha256=nvAAl3Xswqqq3WyaI1NT7pLCcu1o-ynciJZOlc-9L24,16244
|
|
51
|
-
openedx_learning/apps/authoring/publishing/api.py,sha256=
|
|
54
|
+
openedx_learning/apps/authoring/publishing/api.py,sha256=zTG88gVIWfw-oCxC1SLl64OXbA_V_Cd3KDwAAoJWcZM,59679
|
|
52
55
|
openedx_learning/apps/authoring/publishing/apps.py,sha256=PXYIx-TwN7a8dDudodX80Z7hNV9bWzrMZnpDET8lCGE,758
|
|
53
56
|
openedx_learning/apps/authoring/publishing/contextmanagers.py,sha256=AH5zhr0Tz_gUG9--dfr_oZAu8DMy94n6mnOJuPbWkeU,6723
|
|
54
57
|
openedx_learning/apps/authoring/publishing/migrations/0001_initial.py,sha256=wvekNV19YRSdxRmQaFnLSn_nCsQlHIucPDVMmgKf_OE,9272
|
|
@@ -69,21 +72,21 @@ openedx_learning/apps/authoring/publishing/models/publish_log.py,sha256=QD7Fb00y
|
|
|
69
72
|
openedx_learning/apps/authoring/publishing/models/publishable_entity.py,sha256=ErzsvCcYbvjnMvsPqErOgSob9Vpaa7nmykNPTSQkZk8,25894
|
|
70
73
|
openedx_learning/apps/authoring/sections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
74
|
openedx_learning/apps/authoring/sections/admin.py,sha256=OQOTtXYM-Zj8BBb1wNBkOxgkF2Pv3JdUrZ45VOEmThM,1757
|
|
72
|
-
openedx_learning/apps/authoring/sections/api.py,sha256=
|
|
75
|
+
openedx_learning/apps/authoring/sections/api.py,sha256=DXFHCjR8hauJK0Cmwxtk-PQLQpoVPpna-USOYNeRenI,11405
|
|
73
76
|
openedx_learning/apps/authoring/sections/apps.py,sha256=vbLhC3WIKmG1MD0mvxX01IjoF6xPiJoutJar-h_bH7Y,746
|
|
74
77
|
openedx_learning/apps/authoring/sections/models.py,sha256=2GK-dDMJwNRw_9gNFho8iKcDV-iYz_zBzqGMDmQ_jbc,1450
|
|
75
78
|
openedx_learning/apps/authoring/sections/migrations/0001_initial.py,sha256=iW5AFhC26mfZNWEVNu8cTsr32Ca4htL4CUanHoXfaeY,1152
|
|
76
79
|
openedx_learning/apps/authoring/sections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
80
|
openedx_learning/apps/authoring/subsections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
81
|
openedx_learning/apps/authoring/subsections/admin.py,sha256=vPfOeTzh10aRhtZjXAzYsbwfw4Hc5yuySbpjAvtDH98,1795
|
|
79
|
-
openedx_learning/apps/authoring/subsections/api.py,sha256=
|
|
82
|
+
openedx_learning/apps/authoring/subsections/api.py,sha256=aoGLjSbf9cUcAFbPvENThKhbJvZlAuzYP-P6Q0G5duw,11309
|
|
80
83
|
openedx_learning/apps/authoring/subsections/apps.py,sha256=awpHVtg6bwIF1sEMeVcaGMwvrVzckfEHOFA9eFt5900,781
|
|
81
84
|
openedx_learning/apps/authoring/subsections/models.py,sha256=1uhdpS9Eg6keSqkzQaE8-XSVLAQlmi0llIIU2V7Nl44,1492
|
|
82
85
|
openedx_learning/apps/authoring/subsections/migrations/0001_initial.py,sha256=7kEHIC-EwG2KvlW4hg5tnl45--dW4Yv5gqV5SDqNYNo,1158
|
|
83
86
|
openedx_learning/apps/authoring/subsections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
84
87
|
openedx_learning/apps/authoring/units/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
88
|
openedx_learning/apps/authoring/units/admin.py,sha256=chp-bTfufBiQ3uycVF1DBEFSPvwXaROJnyyY8AaH_yw,1717
|
|
86
|
-
openedx_learning/apps/authoring/units/api.py,sha256=
|
|
89
|
+
openedx_learning/apps/authoring/units/api.py,sha256=ERJhwkpcIKNOZogZ2R3-T8mv3ud9oQNgloQRv-7ld-k,10849
|
|
87
90
|
openedx_learning/apps/authoring/units/apps.py,sha256=AlKOUoC5zPrRrEedLvGzMf31ujWyhcaaoCNS4LI-u50,709
|
|
88
91
|
openedx_learning/apps/authoring/units/models.py,sha256=eTOwFWC9coQLf0ovx08Mj7zi8mPAWCw9QOznybajRk0,1418
|
|
89
92
|
openedx_learning/apps/authoring/units/migrations/0001_initial.py,sha256=qM_0JGffxECVgXzncHXfgSE-g8u3L3a14R0M1Bnj_Ys,1129
|
|
@@ -101,7 +104,7 @@ openedx_learning/lib/fields.py,sha256=eiGoXMPhRuq25EH2qf6BAODshAQE3DBVdIYAMIUAXW
|
|
|
101
104
|
openedx_learning/lib/managers.py,sha256=-Q3gxalSqyPZ9Im4DTROW5tF8wVTZLlmfTe62_xmowY,1643
|
|
102
105
|
openedx_learning/lib/test_utils.py,sha256=g3KLuepIZbaDBCsaj9711YuqyUx7LD4gXDcfNC-mWdc,527
|
|
103
106
|
openedx_learning/lib/validators.py,sha256=iqEdEAvFV2tC7Ecssx69kjecpdU8nE87AlDJYrqrsnc,404
|
|
104
|
-
openedx_learning-0.
|
|
107
|
+
openedx_learning-0.29.1.dist-info/licenses/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
|
|
105
108
|
openedx_tagging/__init__.py,sha256=V9N8M7f9LYlAbA_DdPUsHzTnWjYRXKGa5qHw9P1JnNI,30
|
|
106
109
|
openedx_tagging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
110
|
openedx_tagging/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -143,10 +146,10 @@ openedx_tagging/core/tagging/migrations/0017_alter_tagimporttask_status.py,sha25
|
|
|
143
146
|
openedx_tagging/core/tagging/migrations/0018_objecttag_is_copied.py,sha256=zmr4b65T0vX6fYc8MpvSmQnYkAiNMpx3RKEd5tudsl8,517
|
|
144
147
|
openedx_tagging/core/tagging/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
145
148
|
openedx_tagging/core/tagging/models/__init__.py,sha256=yYdOnthuc7EUdfEULtZgqRwn5Y4bbYQmJCjVZqR5GTM,236
|
|
146
|
-
openedx_tagging/core/tagging/models/base.py,sha256=
|
|
149
|
+
openedx_tagging/core/tagging/models/base.py,sha256=V2Qyp4L2QhTs6lZwB05_H54W6yWvyIEHhTcAGJAq-BY,39845
|
|
147
150
|
openedx_tagging/core/tagging/models/import_export.py,sha256=Aj0pleh0nh2LNS6zmdB1P4bpdgUMmvmobTkqBerORAI,4570
|
|
148
151
|
openedx_tagging/core/tagging/models/system_defined.py,sha256=_6LfvUZGEltvQMtm2OXy6TOLh3C8GnVTqtZDSAZW6K4,9062
|
|
149
|
-
openedx_tagging/core/tagging/models/utils.py,sha256=
|
|
152
|
+
openedx_tagging/core/tagging/models/utils.py,sha256=VhNL2cUTbCuxhsEsLXEDcgdV2tK2amUwVdWxmeZDr2w,2925
|
|
150
153
|
openedx_tagging/core/tagging/rest_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
151
154
|
openedx_tagging/core/tagging/rest_api/paginators.py,sha256=BUIAg3taihHx7uAjpTZAGK1xSZzZY9G0aib4OKv5c0k,2651
|
|
152
155
|
openedx_tagging/core/tagging/rest_api/urls.py,sha256=egXaRQv1EAgF04ThgVZBQuvLK1LimuyUKKBD2Hbqb10,148
|
|
@@ -157,7 +160,7 @@ openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=0HQD_Jrf6-YpocYfz
|
|
|
157
160
|
openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
|
|
158
161
|
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=Hf92cy-tE767DE9FgsZcPKiCYrf5ihfETz8qGKBnuiU,36278
|
|
159
162
|
openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
|
|
160
|
-
openedx_learning-0.
|
|
161
|
-
openedx_learning-0.
|
|
162
|
-
openedx_learning-0.
|
|
163
|
-
openedx_learning-0.
|
|
163
|
+
openedx_learning-0.29.1.dist-info/METADATA,sha256=T9KZOwk1jrSCv47Qy-9TsC4lFxjVcKg3oNAX0K2Vz7w,9372
|
|
164
|
+
openedx_learning-0.29.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
|
165
|
+
openedx_learning-0.29.1.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
|
|
166
|
+
openedx_learning-0.29.1.dist-info/RECORD,,
|
|
@@ -465,7 +465,7 @@ class Taxonomy(models.Model):
|
|
|
465
465
|
if include_counts:
|
|
466
466
|
return qs.annotate(usage_count=models.Count("value"))
|
|
467
467
|
else:
|
|
468
|
-
return qs.distinct()
|
|
468
|
+
return qs.distinct() # type: ignore[return-value]
|
|
469
469
|
|
|
470
470
|
def _get_filtered_tags_one_level(
|
|
471
471
|
self,
|
|
@@ -486,12 +486,14 @@ class Taxonomy(models.Model):
|
|
|
486
486
|
# Use parent_tag.value not parent_tag_value because they may differ in case
|
|
487
487
|
qs = qs.annotate(parent_value=Value(parent_tag.value))
|
|
488
488
|
else:
|
|
489
|
-
qs = self.tag_set.filter(parent=None).annotate(depth=Value(0))
|
|
489
|
+
qs = self.tag_set.filter(parent=None).annotate(depth=Value(0)) # type: ignore[no-redef]
|
|
490
490
|
qs = qs.annotate(parent_value=Value(None, output_field=models.CharField()))
|
|
491
|
-
qs = qs.annotate(child_count=models.Count("children", distinct=True))
|
|
491
|
+
qs = qs.annotate(child_count=models.Count("children", distinct=True)) # type: ignore[no-redef]
|
|
492
492
|
qs = qs.annotate(grandchild_count=models.Count("children__children", distinct=True))
|
|
493
493
|
qs = qs.annotate(great_grandchild_count=models.Count("children__children__children"))
|
|
494
|
-
qs = qs.annotate(
|
|
494
|
+
qs = qs.annotate(
|
|
495
|
+
descendant_count=F("child_count") + F("grandchild_count") + F("great_grandchild_count")
|
|
496
|
+
) # type: ignore[no-redef]
|
|
495
497
|
# Filter by search term:
|
|
496
498
|
if search_term:
|
|
497
499
|
qs = qs.filter(value__icontains=search_term)
|
|
@@ -597,7 +599,7 @@ class Taxonomy(models.Model):
|
|
|
597
599
|
count=models.Func(F('id'), function='Count')
|
|
598
600
|
)
|
|
599
601
|
qs = qs.annotate(usage_count=models.Subquery(obj_tags.values('count')))
|
|
600
|
-
return qs
|
|
602
|
+
return qs # type: ignore[return-value]
|
|
601
603
|
|
|
602
604
|
def add_tag(
|
|
603
605
|
self,
|
|
@@ -59,7 +59,7 @@ class StringAgg(Aggregate, Combinable):
|
|
|
59
59
|
# Check the database backend (PostgreSQL, MySQL, or SQLite)
|
|
60
60
|
if 'postgresql' in db_connection.vendor.lower():
|
|
61
61
|
self.function = 'STRING_AGG'
|
|
62
|
-
self.template =
|
|
62
|
+
self.template = "%(function)s(%(distinct)s%(expressions)s::TEXT, '%(delimiter)s')"
|
|
63
63
|
extra.update({
|
|
64
64
|
"delimiter": self.delimiter,
|
|
65
65
|
"output_field": TextField(),
|
|
File without changes
|
{openedx_learning-0.28.0.dist-info → openedx_learning-0.29.1.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|