openedx-learning 0.27.0__py2.py3-none-any.whl → 0.27.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.27.0"
5
+ __version__ = "0.27.1"
@@ -9,6 +9,7 @@ APIs.
9
9
  """
10
10
  # These wildcard imports are okay because these api modules declare __all__.
11
11
  # pylint: disable=wildcard-import
12
+ from ..apps.authoring.backup_restore.api import *
12
13
  from ..apps.authoring.collections.api import *
13
14
  from ..apps.authoring.components.api import *
14
15
  from ..apps.authoring.contents.api import *
@@ -1,14 +1,9 @@
1
1
  """
2
2
  Backup Restore API
3
3
  """
4
- import zipfile
5
-
4
+ from openedx_learning.apps.authoring.backup_restore.zipper import LearningPackageZipper
6
5
  from openedx_learning.apps.authoring.publishing.api import get_learning_package_by_key
7
6
 
8
- from .toml import TOMLLearningPackageFile
9
-
10
- TOML_PACKAGE_NAME = "package.toml"
11
-
12
7
 
13
8
  def create_zip_file(lp_key: str, path: str) -> None:
14
9
  """
@@ -17,9 +12,4 @@ def create_zip_file(lp_key: str, path: str) -> None:
17
12
  Can throw a NotFoundError at get_learning_package_by_key
18
13
  """
19
14
  learning_package = get_learning_package_by_key(lp_key)
20
- toml_file = TOMLLearningPackageFile(learning_package)
21
- toml_file.create()
22
- toml_content: str = toml_file.get()
23
- with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as zipf:
24
- # Add the TOML string as a file in the ZIP
25
- zipf.writestr(TOML_PACKAGE_NAME, toml_content)
15
+ LearningPackageZipper(learning_package).create_zip(path)
@@ -33,7 +33,6 @@ class Command(BaseCommand):
33
33
  self.stdout.write(self.style.SUCCESS(message))
34
34
  except LearningPackage.DoesNotExist as exc:
35
35
  message = f"Learning package with key {lp_key} not found"
36
- logger.exception(message)
37
36
  raise CommandError(message) from exc
38
37
  except Exception as e:
39
38
  message = f"Failed to export learning package '{lp_key}': {e}"
@@ -1,72 +1,75 @@
1
1
  """
2
- Utilities for backup and restore app
2
+ TOML serialization for learning packages and publishable entities.
3
3
  """
4
4
 
5
5
  from datetime import datetime
6
- from typing import Any, Dict
7
6
 
8
- from tomlkit import comment, document, dumps, nl, table
9
- from tomlkit.items import Table
7
+ import tomlkit
10
8
 
9
+ from openedx_learning.apps.authoring.publishing.models import PublishableEntity, PublishableEntityVersion
11
10
  from openedx_learning.apps.authoring.publishing.models.learning_package import LearningPackage
12
11
 
13
12
 
14
- class TOMLLearningPackageFile():
15
- """
16
- Class to create a .toml representation of a LearningPackage instance.
17
-
18
- This class builds a structured TOML document using `tomlkit` with metadata and fields
19
- extracted from a `LearningPackage` object. The output can later be saved to a file or used elsewhere.
20
- """
21
-
22
- def __init__(self, learning_package: LearningPackage):
23
- self.doc = document()
24
- self.learning_package = learning_package
25
-
26
- def _create_header(self) -> None:
27
- """
28
- Adds a comment with the current datetime to indicate when the export occurred.
29
- This helps with traceability and file versioning.
30
- """
31
- self.doc.add(comment(f"Datetime of the export: {datetime.now()}"))
32
- self.doc.add(nl())
33
-
34
- def _create_table(self, params: Dict[str, Any]) -> Table:
35
- """
36
- Builds a TOML table section from a dictionary of key-value pairs.
37
-
38
- Args:
39
- params (Dict[str, Any]): A dictionary containing keys and values to include in the TOML table.
40
-
41
- Returns:
42
- Table: A TOML table populated with the provided keys and values.
43
- """
44
- section = table()
45
- for key, value in params.items():
46
- section.add(key, value)
47
- return section
48
-
49
- def create(self) -> None:
50
- """
51
- Populates the TOML document with a header and a table containing
52
- metadata from the LearningPackage instance.
53
-
54
- This method must be called before calling `get()`, otherwise the document will be empty.
55
- """
56
- self._create_header()
57
- section = self._create_table({
58
- "title": self.learning_package.title,
59
- "key": self.learning_package.key,
60
- "description": self.learning_package.description,
61
- "created": self.learning_package.created,
62
- "updated": self.learning_package.updated
63
- })
64
- self.doc.add("learning_package", section)
65
-
66
- def get(self) -> str:
67
- """
68
- Returns:
69
- str: The string representation of the generated TOML document.
70
- Ensure `create()` has been called beforehand to get meaningful output.
71
- """
72
- return dumps(self.doc)
13
+ def toml_learning_package(learning_package: LearningPackage) -> str:
14
+ """Create a TOML representation of the learning package."""
15
+ doc = tomlkit.document()
16
+ doc.add(tomlkit.comment(f"Datetime of the export: {datetime.now()}"))
17
+ section = tomlkit.table()
18
+ section.add("title", learning_package.title)
19
+ section.add("key", learning_package.key)
20
+ section.add("description", learning_package.description)
21
+ section.add("created", learning_package.created)
22
+ section.add("updated", learning_package.updated)
23
+ doc.add("learning_package", section)
24
+ return tomlkit.dumps(doc)
25
+
26
+
27
+ def toml_publishable_entity(entity: PublishableEntity) -> str:
28
+ """Create a TOML representation of a publishable entity."""
29
+
30
+ current_draft_version = getattr(entity, "draft", None)
31
+ current_published_version = getattr(entity, "published", None)
32
+
33
+ doc = tomlkit.document()
34
+ entity_table = tomlkit.table()
35
+ entity_table.add("uuid", str(entity.uuid))
36
+ entity_table.add("can_stand_alone", entity.can_stand_alone)
37
+
38
+ if current_draft_version:
39
+ draft_table = tomlkit.table()
40
+ draft_table.add("version_num", current_draft_version.version.version_num)
41
+ entity_table.add("draft", draft_table)
42
+
43
+ published_table = tomlkit.table()
44
+ if current_published_version:
45
+ published_table.add("version_num", current_published_version.version.version_num)
46
+ else:
47
+ published_table.add(tomlkit.comment("unpublished: no published_version_num"))
48
+ entity_table.add("published", published_table)
49
+
50
+ doc.add("entity", entity_table)
51
+ doc.add(tomlkit.nl())
52
+ doc.add(tomlkit.comment("### Versions"))
53
+
54
+ for entity_version in entity.versions.all():
55
+ version = tomlkit.aot()
56
+ version_table = toml_publishable_entity_version(entity_version)
57
+ version.append(version_table)
58
+ doc.add("version", version)
59
+
60
+ return tomlkit.dumps(doc)
61
+
62
+
63
+ def toml_publishable_entity_version(version: PublishableEntityVersion) -> tomlkit.items.Table:
64
+ """Create a TOML representation of a publishable entity version."""
65
+ version_table = tomlkit.table()
66
+ version_table.add("title", version.title)
67
+ version_table.add("uuid", str(version.uuid))
68
+ version_table.add("version_num", version.version_num)
69
+ container_table = tomlkit.table()
70
+ container_table.add("children", [])
71
+ unit_table = tomlkit.table()
72
+ unit_table.add("graded", True)
73
+ container_table.add("unit", unit_table)
74
+ version_table.add("container", container_table)
75
+ return version_table # For use in AoT
@@ -0,0 +1,53 @@
1
+ """
2
+ This module provides functionality to create a zip file containing the learning package data,
3
+ including a TOML representation of the learning package and its entities.
4
+ """
5
+ import zipfile
6
+ from pathlib import Path
7
+
8
+ from openedx_learning.apps.authoring.backup_restore.toml import toml_learning_package, toml_publishable_entity
9
+ from openedx_learning.apps.authoring.publishing import api as publishing_api
10
+ from openedx_learning.apps.authoring.publishing.models.learning_package import LearningPackage
11
+
12
+ TOML_PACKAGE_NAME = "package.toml"
13
+
14
+
15
+ class LearningPackageZipper:
16
+ """
17
+ A class to handle the zipping of learning content for backup and restore.
18
+ """
19
+
20
+ def __init__(self, learning_package: LearningPackage):
21
+ self.learning_package = learning_package
22
+
23
+ def create_zip(self, path: str) -> None:
24
+ """
25
+ Creates a zip file containing the learning package data.
26
+ Args:
27
+ path (str): The path where the zip file will be created.
28
+ Raises:
29
+ Exception: If the learning package cannot be found or if the zip creation fails.
30
+ """
31
+ package_toml_content: str = toml_learning_package(self.learning_package)
32
+
33
+ with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as zipf:
34
+ # Add the package.toml string
35
+ zipf.writestr(TOML_PACKAGE_NAME, package_toml_content)
36
+
37
+ # Add the entities directory
38
+ entities_folder = Path("entities")
39
+ zip_info = zipfile.ZipInfo(str(entities_folder) + "/") # Ensure trailing slash
40
+ zipf.writestr(zip_info, "") # Add explicit empty directory entry
41
+
42
+ # Add the collections directory
43
+ collections_folder = Path("collections")
44
+ collections_info = zipfile.ZipInfo(str(collections_folder) + "/") # Ensure trailing slash
45
+ zipf.writestr(collections_info, "") # Add explicit empty directory
46
+
47
+ # Add each entity's TOML file
48
+ for entity in publishing_api.get_entities(self.learning_package.pk):
49
+ # Create a TOML representation of the entity
50
+ entity_toml_content: str = toml_publishable_entity(entity)
51
+ entity_toml_filename = f"{entity.key}.toml"
52
+ entity_toml_path = entities_folder / entity_toml_filename
53
+ zipf.writestr(str(entity_toml_path), entity_toml_content)
@@ -18,7 +18,7 @@ class ComponentsConfig(AppConfig):
18
18
  """
19
19
  Register Component and ComponentVersion.
20
20
  """
21
- from ..publishing.api import register_content_models # pylint: disable=import-outside-toplevel
21
+ from ..publishing.api import register_publishable_models # pylint: disable=import-outside-toplevel
22
22
  from .models import Component, ComponentVersion # pylint: disable=import-outside-toplevel
23
23
 
24
- register_content_models(Component, ComponentVersion)
24
+ register_publishable_models(Component, ComponentVersion)
@@ -60,6 +60,7 @@ __all__ = [
60
60
  "get_publishable_entity_by_key",
61
61
  "get_last_publish",
62
62
  "get_all_drafts",
63
+ "get_entities",
63
64
  "get_entities_with_unpublished_changes",
64
65
  "get_entities_with_unpublished_deletes",
65
66
  "publish_all_drafts",
@@ -69,7 +70,7 @@ __all__ = [
69
70
  "set_draft_version",
70
71
  "soft_delete_draft",
71
72
  "reset_drafts_to_published",
72
- "register_content_models",
73
+ "register_publishable_models",
73
74
  "filter_publishable_entities",
74
75
  # 🛑 UNSTABLE: All APIs related to containers are unstable until we've figured
75
76
  # out our approach to dynamic content (randomized, A/B tests, etc.)
@@ -261,6 +262,13 @@ def get_all_drafts(learning_package_id: int, /) -> QuerySet[Draft]:
261
262
  )
262
263
 
263
264
 
265
+ def get_entities(learning_package_id: int, /) -> QuerySet[PublishableEntity]:
266
+ """
267
+ Get all entities in a learning package.
268
+ """
269
+ return PublishableEntity.objects.filter(learning_package_id=learning_package_id)
270
+
271
+
264
272
  def get_entities_with_unpublished_changes(
265
273
  learning_package_id: int,
266
274
  /,
@@ -789,7 +797,7 @@ def reset_drafts_to_published(
789
797
  set_draft_version(draft, published_version_id)
790
798
 
791
799
 
792
- def register_content_models(
800
+ def register_publishable_models(
793
801
  content_model_cls: type[PublishableEntityMixin],
794
802
  content_version_model_cls: type[PublishableEntityVersionMixin],
795
803
  ) -> PublishableContentModelRegistry:
@@ -805,10 +813,10 @@ def register_content_models(
805
813
  method. For example, in the components app, this looks like:
806
814
 
807
815
  def ready(self):
808
- from ..publishing.api import register_content_models
816
+ from ..publishing.api import register_publishable_models
809
817
  from .models import Component, ComponentVersion
810
818
 
811
- register_content_models(Component, ComponentVersion)
819
+ register_publishable_models(Component, ComponentVersion)
812
820
 
813
821
  There may be a more clever way to introspect this information from the model
814
822
  metadata, but this is simple and explicit.
@@ -1275,6 +1283,7 @@ def get_entities_in_container(
1275
1283
  container: Container,
1276
1284
  *,
1277
1285
  published: bool,
1286
+ select_related_version: str | None = None,
1278
1287
  ) -> list[ContainerEntityListEntry]:
1279
1288
  """
1280
1289
  [ 🛑 UNSTABLE ]
@@ -1285,14 +1294,35 @@ def get_entities_in_container(
1285
1294
  container: The Container, e.g. returned by `get_container()`
1286
1295
  published: `True` if we want the published version of the container, or
1287
1296
  `False` for the draft version.
1297
+ select_related_version: An optional optimization; specify a relationship
1298
+ on ContainerVersion, like `componentversion` or `containerversion__x`
1299
+ to preload via select_related.
1288
1300
  """
1289
1301
  assert isinstance(container, Container)
1290
- container_version = container.versioning.published if published else container.versioning.draft
1302
+ if published:
1303
+ # Very minor optimization: reload the container with related 1:1 entities
1304
+ container = Container.objects.select_related(
1305
+ "publishable_entity__published__version__containerversion__entity_list").get(pk=container.pk)
1306
+ container_version = container.versioning.published
1307
+ select_related = ["entity__published__version"]
1308
+ if select_related_version:
1309
+ select_related.append(f"entity__published__version__{select_related_version}")
1310
+ else:
1311
+ # Very minor optimization: reload the container with related 1:1 entities
1312
+ container = Container.objects.select_related(
1313
+ "publishable_entity__draft__version__containerversion__entity_list").get(pk=container.pk)
1314
+ container_version = container.versioning.draft
1315
+ select_related = ["entity__draft__version"]
1316
+ if select_related_version:
1317
+ select_related.append(f"entity__draft__version__{select_related_version}")
1291
1318
  if container_version is None:
1292
1319
  raise ContainerVersion.DoesNotExist # This container has not been published yet, or has been deleted.
1293
1320
  assert isinstance(container_version, ContainerVersion)
1294
- entity_list = []
1295
- for row in container_version.entity_list.entitylistrow_set.order_by("order_num"):
1321
+ entity_list: list[ContainerEntityListEntry] = []
1322
+ for row in container_version.entity_list.entitylistrow_set.select_related(
1323
+ "entity_version",
1324
+ *select_related,
1325
+ ).order_by("order_num"):
1296
1326
  entity_version = row.entity_version # This will be set if pinned
1297
1327
  if not entity_version: # If this entity is "unpinned", use the latest published/draft version:
1298
1328
  entity_version = row.entity.published.version if published else row.entity.draft.version
@@ -1385,7 +1415,10 @@ def get_containers_with_entity(
1385
1415
  qs = Container.objects.filter(
1386
1416
  publishable_entity__draft__version__containerversion__entity_list__entitylistrow__entity_id=publishable_entity_pk, # pylint: disable=line-too-long # noqa: E501
1387
1417
  )
1388
- return qs.order_by("pk").distinct() # Ordering is mostly for consistent test cases.
1418
+ return qs.select_related(
1419
+ "publishable_entity__draft__version__containerversion",
1420
+ "publishable_entity__published__version__containerversion",
1421
+ ).order_by("pk").distinct() # Ordering is mostly for consistent test cases.
1389
1422
 
1390
1423
 
1391
1424
  def get_container_children_count(
@@ -19,7 +19,7 @@ class PublishingConfig(AppConfig):
19
19
  """
20
20
  Register Container and ContainerVersion.
21
21
  """
22
- from .api import register_content_models # pylint: disable=import-outside-toplevel
22
+ from .api import register_publishable_models # pylint: disable=import-outside-toplevel
23
23
  from .models import Container, ContainerVersion # pylint: disable=import-outside-toplevel
24
24
 
25
- register_content_models(Container, ContainerVersion)
25
+ register_publishable_models(Container, ContainerVersion)
@@ -272,7 +272,7 @@ class PublishableEntityMixin(models.Model):
272
272
  Please see docstring for PublishableEntity for more details.
273
273
 
274
274
  If you use this class, you *MUST* also use PublishableEntityVersionMixin and
275
- the publishing app's api.register_content_models (see its docstring for
275
+ the publishing app's api.register_publishable_models (see its docstring for
276
276
  details).
277
277
  """
278
278
  # select these related entities by default for all queries
@@ -294,6 +294,10 @@ class PublishableEntityMixin(models.Model):
294
294
  def uuid(self) -> str:
295
295
  return self.publishable_entity.uuid
296
296
 
297
+ @property
298
+ def can_stand_alone(self) -> bool:
299
+ return self.publishable_entity.can_stand_alone
300
+
297
301
  @property
298
302
  def key(self) -> str:
299
303
  return self.publishable_entity.key
@@ -551,7 +555,7 @@ class PublishableEntityVersionMixin(models.Model):
551
555
  Please see docstring for PublishableEntityVersion for more details.
552
556
 
553
557
  If you use this class, you *MUST* also use PublishableEntityMixin and the
554
- publishing app's api.register_content_models (see its docstring for
558
+ publishing app's api.register_publishable_models (see its docstring for
555
559
  details).
556
560
  """
557
561
 
@@ -609,7 +613,7 @@ class PublishableContentModelRegistry:
609
613
  Register what content model maps to what content version model.
610
614
 
611
615
  If you want to call this from another app, please use the
612
- ``register_content_models`` function in this app's ``api`` module
616
+ ``register_publishable_models`` function in this app's ``api`` module
613
617
  instead.
614
618
  """
615
619
  if not issubclass(content_model_cls, PublishableEntityMixin):
@@ -257,7 +257,12 @@ def get_subsections_in_section(
257
257
  """
258
258
  assert isinstance(section, Section)
259
259
  subsections = []
260
- for entry in publishing_api.get_entities_in_container(section, published=published):
260
+ entries = publishing_api.get_entities_in_container(
261
+ section,
262
+ published=published,
263
+ select_related_version="containerversion__subsectionversion",
264
+ )
265
+ for entry in entries:
261
266
  # Convert from generic PublishableEntityVersion to SubsectionVersion:
262
267
  subsection_version = entry.entity_version.containerversion.subsectionversion
263
268
  assert isinstance(subsection_version, SubsectionVersion)
@@ -19,7 +19,7 @@ class SectionsConfig(AppConfig):
19
19
  """
20
20
  Register Section and SectionVersion.
21
21
  """
22
- from ..publishing.api import register_content_models # pylint: disable=import-outside-toplevel
22
+ from ..publishing.api import register_publishable_models # pylint: disable=import-outside-toplevel
23
23
  from .models import Section, SectionVersion # pylint: disable=import-outside-toplevel
24
24
 
25
- register_content_models(Section, SectionVersion)
25
+ register_publishable_models(Section, SectionVersion)
@@ -256,7 +256,12 @@ def get_units_in_subsection(
256
256
  """
257
257
  assert isinstance(subsection, Subsection)
258
258
  units = []
259
- for entry in publishing_api.get_entities_in_container(subsection, published=published):
259
+ entries = publishing_api.get_entities_in_container(
260
+ subsection,
261
+ published=published,
262
+ select_related_version="containerversion__unitversion",
263
+ )
264
+ for entry in entries:
260
265
  # Convert from generic PublishableEntityVersion to UnitVersion:
261
266
  unit_version = entry.entity_version.containerversion.unitversion
262
267
  assert isinstance(unit_version, UnitVersion)
@@ -19,7 +19,7 @@ class SubsectionsConfig(AppConfig):
19
19
  """
20
20
  Register Subsection and SubsectionVersion.
21
21
  """
22
- from ..publishing.api import register_content_models # pylint: disable=import-outside-toplevel
22
+ from ..publishing.api import register_publishable_models # pylint: disable=import-outside-toplevel
23
23
  from .models import Subsection, SubsectionVersion # pylint: disable=import-outside-toplevel
24
24
 
25
- register_content_models(Subsection, SubsectionVersion)
25
+ register_publishable_models(Subsection, SubsectionVersion)
@@ -257,7 +257,12 @@ def get_components_in_unit(
257
257
  """
258
258
  assert isinstance(unit, Unit)
259
259
  components = []
260
- for entry in publishing_api.get_entities_in_container(unit, published=published):
260
+ entries = publishing_api.get_entities_in_container(
261
+ unit,
262
+ published=published,
263
+ select_related_version="componentversion",
264
+ )
265
+ for entry in entries:
261
266
  # Convert from generic PublishableEntityVersion to ComponentVersion:
262
267
  component_version = entry.entity_version.componentversion
263
268
  assert isinstance(component_version, ComponentVersion)
@@ -19,7 +19,7 @@ class UnitsConfig(AppConfig):
19
19
  """
20
20
  Register Unit and UnitVersion.
21
21
  """
22
- from ..publishing.api import register_content_models # pylint: disable=import-outside-toplevel
22
+ from ..publishing.api import register_publishable_models # pylint: disable=import-outside-toplevel
23
23
  from .models import Unit, UnitVersion # pylint: disable=import-outside-toplevel
24
24
 
25
- register_content_models(Unit, UnitVersion)
25
+ register_publishable_models(Unit, UnitVersion)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openedx-learning
3
- Version: 0.27.0
3
+ Version: 0.27.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: Django
23
- Requires-Dist: rules<4.0
24
- Requires-Dist: celery
25
22
  Requires-Dist: djangorestframework<4.0
26
- Requires-Dist: attrs
27
23
  Requires-Dist: tomlkit
24
+ Requires-Dist: celery
25
+ Requires-Dist: Django
26
+ Requires-Dist: attrs
28
27
  Requires-Dist: edx-drf-extensions
28
+ Requires-Dist: rules<4.0
29
29
  Dynamic: author
30
30
  Dynamic: author-email
31
31
  Dynamic: classifier
@@ -1,19 +1,20 @@
1
- openedx_learning/__init__.py,sha256=SyGXwpGB_cwgn1A-xCULm9ZZQwV783LkH-64qx6sehg,69
1
+ openedx_learning/__init__.py,sha256=yNCuSKMLv6YJnAu09cqc4GpR5uVIDaJP2-5z9RAWJeM,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
- openedx_learning/api/authoring.py,sha256=1sh-hUH3pJLVIQHzzjlqWb_9uxf9y3-hanLpU4mRvXc,1061
4
+ openedx_learning/api/authoring.py,sha256=EDWTY_JDKtjD9nFrrijzWuVccs3LZeDLEdzTUNanR4I,1111
5
5
  openedx_learning/api/authoring_models.py,sha256=lA500-C7LBlVzeaEVqmCiQMAPPmeoDMi8wS0TbzgdGw,778
6
6
  openedx_learning/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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=T5hYOuelkK2-UnJNDObk2DnkoKPxKdc8LJ6MwtwaXRI,760
10
+ openedx_learning/apps/authoring/backup_restore/api.py,sha256=zEns3crvfFEFFh7MmwzSqW0WuGmZaSgdmujzl0PnfvU,508
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/toml.py,sha256=zjaeVJqZJhtITVaL2Pmopkg3bhgbSM8-t4J3IUeNHts,2496
13
+ openedx_learning/apps/authoring/backup_restore/toml.py,sha256=KRlOSln1wNB6U7qXXf6Cr9_GZrLUXLwH-x9Yxd9lmBk,2874
14
+ openedx_learning/apps/authoring/backup_restore/zipper.py,sha256=6hX8qQFHS3HCAUs5JsxHuLUWOOa_6Y8c93WcAZKDE4A,2379
14
15
  openedx_learning/apps/authoring/backup_restore/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  openedx_learning/apps/authoring/backup_restore/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- openedx_learning/apps/authoring/backup_restore/management/commands/lp_dump.py,sha256=NiZ-fEDGgh9Q0IUjpjOIvm9BtzwnVp6LK6IY-OA_o98,1734
17
+ openedx_learning/apps/authoring/backup_restore/management/commands/lp_dump.py,sha256=TkbyBf9Jsa7yoXiGEduO0ZqKTYO7vWGHbqr5NbEclRs,1696
17
18
  openedx_learning/apps/authoring/backup_restore/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  openedx_learning/apps/authoring/collections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
20
  openedx_learning/apps/authoring/collections/admin.py,sha256=f0hySjDMfIphVDEGkCSMIUHoEiqHRA7EE2NiO7lvL4g,1156
@@ -29,7 +30,7 @@ openedx_learning/apps/authoring/collections/migrations/__init__.py,sha256=47DEQp
29
30
  openedx_learning/apps/authoring/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
31
  openedx_learning/apps/authoring/components/admin.py,sha256=zfEpuBEySMYpUZzygaE2MDoI8SH-2H3xIL20YCSCMLo,4582
31
32
  openedx_learning/apps/authoring/components/api.py,sha256=nJZcGXN5gOz8EYX3XHCiDILowNRcm__RxTfTNt5rZZw,22780
32
- openedx_learning/apps/authoring/components/apps.py,sha256=Rcydv_FH-rVvuWIFqUezBNOh8DtrZHyozZM2yqX2JKE,778
33
+ openedx_learning/apps/authoring/components/apps.py,sha256=hi1SF2Z8Ex0hgE82wJK5Z_vYYfbcRhtaUW1zWZCdJYI,786
33
34
  openedx_learning/apps/authoring/components/models.py,sha256=ttZzVnMZTa14-qudrLb4CFuCanEQJT8cuC_iVPH8XTA,10887
34
35
  openedx_learning/apps/authoring/components/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
36
  openedx_learning/apps/authoring/components/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -47,8 +48,8 @@ openedx_learning/apps/authoring/contents/migrations/0001_initial.py,sha256=FtOTm
47
48
  openedx_learning/apps/authoring/contents/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
49
  openedx_learning/apps/authoring/publishing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
50
  openedx_learning/apps/authoring/publishing/admin.py,sha256=nvAAl3Xswqqq3WyaI1NT7pLCcu1o-ynciJZOlc-9L24,16244
50
- openedx_learning/apps/authoring/publishing/api.py,sha256=BQ5Q0JsSRBmb8ZRKzUlEOOV6QxNUjYkkqCjxmnX9O48,55565
51
- openedx_learning/apps/authoring/publishing/apps.py,sha256=v9PTe3YoICaYT9wfu268ZkVAlnZFvxi-DqYdbRi25bY,750
51
+ openedx_learning/apps/authoring/publishing/api.py,sha256=rLwS_Ma1Yp5bryR5-gTllIot6AjFB5g-7UFUxZtJsPU,57208
52
+ openedx_learning/apps/authoring/publishing/apps.py,sha256=PXYIx-TwN7a8dDudodX80Z7hNV9bWzrMZnpDET8lCGE,758
52
53
  openedx_learning/apps/authoring/publishing/contextmanagers.py,sha256=AH5zhr0Tz_gUG9--dfr_oZAu8DMy94n6mnOJuPbWkeU,6723
53
54
  openedx_learning/apps/authoring/publishing/migrations/0001_initial.py,sha256=wvekNV19YRSdxRmQaFnLSn_nCsQlHIucPDVMmgKf_OE,9272
54
55
  openedx_learning/apps/authoring/publishing/migrations/0002_alter_learningpackage_key_and_more.py,sha256=toI7qJhNukk6hirKfFx9EpqTpzF2O2Yq1VpFJusDn2M,806
@@ -65,25 +66,25 @@ openedx_learning/apps/authoring/publishing/models/draft_log.py,sha256=7hpbtnnc3y
65
66
  openedx_learning/apps/authoring/publishing/models/entity_list.py,sha256=8MyJqDdC8dqJY-N9UAu-WS-ZeFOYuRNMKloSY9wMH1w,3042
66
67
  openedx_learning/apps/authoring/publishing/models/learning_package.py,sha256=1fuNLHD6k0qGuL0jXYGf4-TA5WczgxJrXUdAIM_JNBI,2688
67
68
  openedx_learning/apps/authoring/publishing/models/publish_log.py,sha256=QD7Fb00yUMWM4HHae_m60JW-TDX8upbQdWCEIdhB0yE,5615
68
- openedx_learning/apps/authoring/publishing/models/publishable_entity.py,sha256=ncks-eNn5h-b1FjWPMAYtnuiMSvFYHwsGeNWGJA8GBE,25773
69
+ openedx_learning/apps/authoring/publishing/models/publishable_entity.py,sha256=ErzsvCcYbvjnMvsPqErOgSob9Vpaa7nmykNPTSQkZk8,25894
69
70
  openedx_learning/apps/authoring/sections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
71
  openedx_learning/apps/authoring/sections/admin.py,sha256=OQOTtXYM-Zj8BBb1wNBkOxgkF2Pv3JdUrZ45VOEmThM,1757
71
- openedx_learning/apps/authoring/sections/api.py,sha256=qfT29Vgcc-1d3-BTiKY_a5M9H7oFBXVD8KqWe-1OqM0,10323
72
- openedx_learning/apps/authoring/sections/apps.py,sha256=h2-1csh-f3tLkauxXvn1xvi_P-l-_oe819XYO6BIYm4,738
72
+ openedx_learning/apps/authoring/sections/api.py,sha256=MFAHxDqUqbXygY_L37Lo4uXrju0m4Kf9ozk4yZnDs6Y,10438
73
+ openedx_learning/apps/authoring/sections/apps.py,sha256=vbLhC3WIKmG1MD0mvxX01IjoF6xPiJoutJar-h_bH7Y,746
73
74
  openedx_learning/apps/authoring/sections/models.py,sha256=2GK-dDMJwNRw_9gNFho8iKcDV-iYz_zBzqGMDmQ_jbc,1450
74
75
  openedx_learning/apps/authoring/sections/migrations/0001_initial.py,sha256=iW5AFhC26mfZNWEVNu8cTsr32Ca4htL4CUanHoXfaeY,1152
75
76
  openedx_learning/apps/authoring/sections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
77
  openedx_learning/apps/authoring/subsections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
78
  openedx_learning/apps/authoring/subsections/admin.py,sha256=vPfOeTzh10aRhtZjXAzYsbwfw4Hc5yuySbpjAvtDH98,1795
78
- openedx_learning/apps/authoring/subsections/api.py,sha256=7heZHREJy4t0L8b6mOLtpi0JxLjqsJ7sHQFZc8z07a4,10229
79
- openedx_learning/apps/authoring/subsections/apps.py,sha256=WueCaPOE-7x3cu-6rA9FdeKzipCZSNIhvqpAbxTysOg,773
79
+ openedx_learning/apps/authoring/subsections/api.py,sha256=cJHPZ2JbAL3aXa1rhV6R5_oIP_VmWWDCi9L-2qjifwo,10338
80
+ openedx_learning/apps/authoring/subsections/apps.py,sha256=awpHVtg6bwIF1sEMeVcaGMwvrVzckfEHOFA9eFt5900,781
80
81
  openedx_learning/apps/authoring/subsections/models.py,sha256=1uhdpS9Eg6keSqkzQaE8-XSVLAQlmi0llIIU2V7Nl44,1492
81
82
  openedx_learning/apps/authoring/subsections/migrations/0001_initial.py,sha256=7kEHIC-EwG2KvlW4hg5tnl45--dW4Yv5gqV5SDqNYNo,1158
82
83
  openedx_learning/apps/authoring/subsections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
84
  openedx_learning/apps/authoring/units/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
85
  openedx_learning/apps/authoring/units/admin.py,sha256=chp-bTfufBiQ3uycVF1DBEFSPvwXaROJnyyY8AaH_yw,1717
85
- openedx_learning/apps/authoring/units/api.py,sha256=Bk6CndTU3sO0SvAphEl5UWKGBkWD_TE9p12BXBxxvLQ,9767
86
- openedx_learning/apps/authoring/units/apps.py,sha256=cIzphjDw5sjIZ3NLE911N7IMUa8JQSXMReNl03uI7jg,701
86
+ openedx_learning/apps/authoring/units/api.py,sha256=vmNdXwI4n-ksDu_nD3b-U0ZyQa0mGaJ3cuaj882HU2s,9863
87
+ openedx_learning/apps/authoring/units/apps.py,sha256=AlKOUoC5zPrRrEedLvGzMf31ujWyhcaaoCNS4LI-u50,709
87
88
  openedx_learning/apps/authoring/units/models.py,sha256=eTOwFWC9coQLf0ovx08Mj7zi8mPAWCw9QOznybajRk0,1418
88
89
  openedx_learning/apps/authoring/units/migrations/0001_initial.py,sha256=qM_0JGffxECVgXzncHXfgSE-g8u3L3a14R0M1Bnj_Ys,1129
89
90
  openedx_learning/apps/authoring/units/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -100,7 +101,7 @@ openedx_learning/lib/fields.py,sha256=eiGoXMPhRuq25EH2qf6BAODshAQE3DBVdIYAMIUAXW
100
101
  openedx_learning/lib/managers.py,sha256=-Q3gxalSqyPZ9Im4DTROW5tF8wVTZLlmfTe62_xmowY,1643
101
102
  openedx_learning/lib/test_utils.py,sha256=g3KLuepIZbaDBCsaj9711YuqyUx7LD4gXDcfNC-mWdc,527
102
103
  openedx_learning/lib/validators.py,sha256=iqEdEAvFV2tC7Ecssx69kjecpdU8nE87AlDJYrqrsnc,404
103
- openedx_learning-0.27.0.dist-info/licenses/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
104
+ openedx_learning-0.27.1.dist-info/licenses/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
104
105
  openedx_tagging/__init__.py,sha256=V9N8M7f9LYlAbA_DdPUsHzTnWjYRXKGa5qHw9P1JnNI,30
105
106
  openedx_tagging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
107
  openedx_tagging/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -156,7 +157,7 @@ openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=0HQD_Jrf6-YpocYfz
156
157
  openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
157
158
  openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=Hf92cy-tE767DE9FgsZcPKiCYrf5ihfETz8qGKBnuiU,36278
158
159
  openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
159
- openedx_learning-0.27.0.dist-info/METADATA,sha256=aj1eSVrsZiti6xVUZKZONiQbNPv-5tD8AbAkiOKbgvk,9055
160
- openedx_learning-0.27.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
161
- openedx_learning-0.27.0.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
162
- openedx_learning-0.27.0.dist-info/RECORD,,
160
+ openedx_learning-0.27.1.dist-info/METADATA,sha256=0otM32S0-1Xf6yX0mjj-P2xAOiU_k5VhIQtVpL1WMos,9055
161
+ openedx_learning-0.27.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
162
+ openedx_learning-0.27.1.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
163
+ openedx_learning-0.27.1.dist-info/RECORD,,