openedx-learning 0.23.1__tar.gz → 0.25.0__tar.gz

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.
Files changed (158) hide show
  1. {openedx_learning-0.23.1/openedx_learning.egg-info → openedx_learning-0.25.0}/PKG-INFO +3 -3
  2. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/__init__.py +1 -1
  3. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/api/authoring.py +2 -0
  4. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/api/authoring_models.py +2 -0
  5. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/api.py +7 -11
  6. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0008_alter_draftchangelogrecord_options_and_more.py +1 -2
  7. openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/api.py +310 -0
  8. openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/apps.py +25 -0
  9. openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/migrations/0001_initial.py +36 -0
  10. openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/models.py +50 -0
  11. openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/api.py +309 -0
  12. openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/apps.py +25 -0
  13. openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/migrations/0001_initial.py +36 -0
  14. openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/models.py +50 -0
  15. {openedx_learning-0.23.1 → openedx_learning-0.25.0/openedx_learning.egg-info}/PKG-INFO +3 -3
  16. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning.egg-info/SOURCES.txt +12 -0
  17. openedx_learning-0.25.0/openedx_tagging/core/tagging/migrations/__init__.py +0 -0
  18. openedx_learning-0.25.0/openedx_tagging/core/tagging/rest_api/__init__.py +0 -0
  19. openedx_learning-0.25.0/openedx_tagging/core/tagging/rest_api/v1/__init__.py +0 -0
  20. openedx_learning-0.25.0/openedx_tagging/py.typed +0 -0
  21. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/CHANGELOG.rst +0 -0
  22. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/LICENSE.txt +0 -0
  23. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/MANIFEST.in +0 -0
  24. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/README.rst +0 -0
  25. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/api/__init__.py +0 -0
  26. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/__init__.py +0 -0
  27. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/__init__.py +0 -0
  28. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/__init__.py +0 -0
  29. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/admin.py +0 -0
  30. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/api.py +0 -0
  31. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/apps.py +0 -0
  32. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0001_initial.py +0 -0
  33. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0002_remove_collection_name_collection_created_by_and_more.py +0 -0
  34. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0003_collection_entities.py +0 -0
  35. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0004_collection_key.py +0 -0
  36. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0005_alter_collection_options_alter_collection_enabled.py +0 -0
  37. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/__init__.py +0 -0
  38. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/models.py +0 -0
  39. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/__init__.py +0 -0
  40. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/admin.py +0 -0
  41. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/api.py +0 -0
  42. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/apps.py +0 -0
  43. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/__init__.py +0 -0
  44. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/commands/__init__.py +0 -0
  45. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/commands/add_assets_to_component.py +0 -0
  46. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0001_initial.py +0 -0
  47. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0002_alter_componentversioncontent_key.py +0 -0
  48. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0003_remove_componentversioncontent_learner_downloadable.py +0 -0
  49. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/__init__.py +0 -0
  50. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/models.py +0 -0
  51. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/__init__.py +0 -0
  52. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/admin.py +0 -0
  53. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/api.py +0 -0
  54. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/apps.py +0 -0
  55. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/migrations/0001_initial.py +0 -0
  56. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/migrations/__init__.py +0 -0
  57. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/models.py +0 -0
  58. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/__init__.py +0 -0
  59. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/admin.py +0 -0
  60. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/apps.py +0 -0
  61. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/contextmanagers.py +0 -0
  62. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0001_initial.py +0 -0
  63. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0002_alter_learningpackage_key_and_more.py +0 -0
  64. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0003_containers.py +0 -0
  65. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0004_publishableentity_can_stand_alone.py +0 -0
  66. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0005_alter_entitylistrow_options.py +0 -0
  67. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0006_draftchangelog.py +0 -0
  68. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0007_bootstrap_draftchangelog.py +0 -0
  69. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/__init__.py +0 -0
  70. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/__init__.py +0 -0
  71. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/container.py +0 -0
  72. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/draft_log.py +0 -0
  73. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/entity_list.py +0 -0
  74. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/learning_package.py +0 -0
  75. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/publish_log.py +0 -0
  76. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/publishable_entity.py +0 -0
  77. {openedx_learning-0.23.1/openedx_learning/apps/authoring/units → openedx_learning-0.25.0/openedx_learning/apps/authoring/sections}/__init__.py +0 -0
  78. {openedx_learning-0.23.1/openedx_learning/apps/authoring/units → openedx_learning-0.25.0/openedx_learning/apps/authoring/sections}/migrations/__init__.py +0 -0
  79. {openedx_learning-0.23.1/openedx_learning/contrib → openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections}/__init__.py +0 -0
  80. {openedx_learning-0.23.1/openedx_learning/lib → openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/migrations}/__init__.py +0 -0
  81. {openedx_learning-0.23.1/openedx_tagging/core → openedx_learning-0.25.0/openedx_learning/apps/authoring/units}/__init__.py +0 -0
  82. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/api.py +0 -0
  83. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/apps.py +0 -0
  84. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/migrations/0001_initial.py +0 -0
  85. {openedx_learning-0.23.1/openedx_tagging/core/tagging → openedx_learning-0.25.0/openedx_learning/apps/authoring/units/migrations}/__init__.py +0 -0
  86. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/models.py +0 -0
  87. {openedx_learning-0.23.1/openedx_tagging/core/tagging/migrations → openedx_learning-0.25.0/openedx_learning/contrib}/__init__.py +0 -0
  88. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/__init__.py +0 -0
  89. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/apps.py +0 -0
  90. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/urls.py +0 -0
  91. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/views.py +0 -0
  92. {openedx_learning-0.23.1/openedx_tagging/core/tagging/rest_api → openedx_learning-0.25.0/openedx_learning/lib}/__init__.py +0 -0
  93. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/admin_utils.py +0 -0
  94. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/cache.py +0 -0
  95. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/collations.py +0 -0
  96. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/fields.py +0 -0
  97. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/managers.py +0 -0
  98. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/test_utils.py +0 -0
  99. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/lib/validators.py +0 -0
  100. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning/py.typed +0 -0
  101. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning.egg-info/dependency_links.txt +0 -0
  102. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning.egg-info/not-zip-safe +0 -0
  103. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning.egg-info/requires.txt +2 -2
  104. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_learning.egg-info/top_level.txt +0 -0
  105. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/__init__.py +0 -0
  106. {openedx_learning-0.23.1/openedx_tagging/core/tagging/rest_api/v1 → openedx_learning-0.25.0/openedx_tagging/core}/__init__.py +0 -0
  107. /openedx_learning-0.23.1/openedx_tagging/py.typed → /openedx_learning-0.25.0/openedx_tagging/core/tagging/__init__.py +0 -0
  108. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/admin.py +0 -0
  109. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/api.py +0 -0
  110. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/apps.py +0 -0
  111. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/data.py +0 -0
  112. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/__init__.py +0 -0
  113. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/actions.py +0 -0
  114. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/api.py +0 -0
  115. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/exceptions.py +0 -0
  116. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/import_plan.py +0 -0
  117. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/parsers.py +0 -0
  118. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/tasks.py +0 -0
  119. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/template.csv +0 -0
  120. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/template.json +0 -0
  121. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0001_initial.py +0 -0
  122. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0001_squashed.py +0 -0
  123. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0002_auto_20230718_2026.py +0 -0
  124. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0003_auto_20230721_1238.py +0 -0
  125. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0004_auto_20230723_2001.py +0 -0
  126. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0005_language_taxonomy.py +0 -0
  127. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0006_alter_objecttag_unique_together.py +0 -0
  128. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0006_auto_20230802_1631.py +0 -0
  129. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0007_tag_import_task_log_null_fix.py +0 -0
  130. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0008_taxonomy_description_not_null.py +0 -0
  131. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0009_alter_objecttag_object_id.py +0 -0
  132. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0010_cleanups.py +0 -0
  133. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0011_remove_required.py +0 -0
  134. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0012_language_taxonomy.py +0 -0
  135. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0013_tag_parent_blank.py +0 -0
  136. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0014_minor_fixes.py +0 -0
  137. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0015_taxonomy_export_id.py +0 -0
  138. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0016_object_tag_export_id.py +0 -0
  139. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0017_alter_tagimporttask_status.py +0 -0
  140. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0018_objecttag_is_copied.py +0 -0
  141. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/__init__.py +0 -0
  142. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/base.py +0 -0
  143. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/import_export.py +0 -0
  144. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/system_defined.py +0 -0
  145. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/utils.py +0 -0
  146. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/paginators.py +0 -0
  147. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/urls.py +0 -0
  148. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/utils.py +0 -0
  149. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/permissions.py +0 -0
  150. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/serializers.py +0 -0
  151. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/urls.py +0 -0
  152. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/views.py +0 -0
  153. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/views_import.py +0 -0
  154. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rules.py +0 -0
  155. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/urls.py +0 -0
  156. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/requirements/base.in +0 -0
  157. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/setup.cfg +0 -0
  158. {openedx_learning-0.23.1 → openedx_learning-0.25.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openedx-learning
3
- Version: 0.23.1
3
+ Version: 0.25.0
4
4
  Summary: Open edX Learning Core and Tagging.
5
5
  Home-page: https://github.com/openedx/openedx-learning
6
6
  Author: David Ormsbee
@@ -20,11 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Requires-Python: >=3.11
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: attrs
23
- Requires-Dist: Django
24
23
  Requires-Dist: edx-drf-extensions
24
+ Requires-Dist: djangorestframework<4.0
25
25
  Requires-Dist: celery
26
+ Requires-Dist: Django
26
27
  Requires-Dist: rules<4.0
27
- Requires-Dist: djangorestframework<4.0
28
28
  Dynamic: author
29
29
  Dynamic: author-email
30
30
  Dynamic: classifier
@@ -2,4 +2,4 @@
2
2
  Open edX Learning ("Learning Core").
3
3
  """
4
4
 
5
- __version__ = "0.23.1"
5
+ __version__ = "0.25.0"
@@ -13,6 +13,8 @@ from ..apps.authoring.collections.api import *
13
13
  from ..apps.authoring.components.api import *
14
14
  from ..apps.authoring.contents.api import *
15
15
  from ..apps.authoring.publishing.api import *
16
+ from ..apps.authoring.sections.api import *
17
+ from ..apps.authoring.subsections.api import *
16
18
  from ..apps.authoring.units.api import *
17
19
 
18
20
  # This was renamed after the authoring API refactoring pushed this and other
@@ -11,4 +11,6 @@ from ..apps.authoring.collections.models import *
11
11
  from ..apps.authoring.components.models import *
12
12
  from ..apps.authoring.contents.models import *
13
13
  from ..apps.authoring.publishing.models import *
14
+ from ..apps.authoring.sections.models import *
15
+ from ..apps.authoring.subsections.models import *
14
16
  from ..apps.authoring.units.models import *
@@ -1322,7 +1322,10 @@ def contains_unpublished_changes(container_id: int) -> bool:
1322
1322
  return True
1323
1323
 
1324
1324
  # We only care about children that are un-pinned, since published changes to pinned children don't matter
1325
- entity_list = container.versioning.draft.entity_list
1325
+ entity_list = getattr(container.versioning.draft, "entity_list", None)
1326
+ if entity_list is None:
1327
+ # This container has been soft-deleted, so it has no children.
1328
+ return False
1326
1329
 
1327
1330
  # This is a naive and inefficient implementation but should be correct.
1328
1331
  # TODO: Once we have expanded the containers system to support multiple levels (not just Units and Components but
@@ -1367,23 +1370,16 @@ def get_containers_with_entity(
1367
1370
  ignore_pinned: if true, ignore any pinned references to the entity.
1368
1371
  """
1369
1372
  if ignore_pinned:
1370
- # TODO: Do we need to run distinct() on this? Will fix in https://github.com/openedx/openedx-learning/issues/303
1371
1373
  qs = Container.objects.filter(
1372
1374
  # Note: these two conditions must be in the same filter() call, or the query won't be correct.
1373
1375
  publishable_entity__draft__version__containerversion__entity_list__entitylistrow__entity_id=publishable_entity_pk, # pylint: disable=line-too-long # noqa: E501
1374
1376
  publishable_entity__draft__version__containerversion__entity_list__entitylistrow__entity_version_id=None, # pylint: disable=line-too-long # noqa: E501
1375
- ).order_by("pk") # Ordering is mostly for consistent test cases.
1377
+ )
1376
1378
  else:
1377
1379
  qs = Container.objects.filter(
1378
1380
  publishable_entity__draft__version__containerversion__entity_list__entitylistrow__entity_id=publishable_entity_pk, # pylint: disable=line-too-long # noqa: E501
1379
- ).order_by("pk") # Ordering is mostly for consistent test cases.
1380
- # Could alternately do this query in two steps. Not sure which is more efficient; depends on how the DB plans it.
1381
- # # Find all the EntityLists that contain the given entity:
1382
- # lists = EntityList.objects.filter(entitylistrow__entity_id=publishable_entity_pk).values_list('pk', flat=True)
1383
- # qs = Container.objects.filter(
1384
- # publishable_entity__draft__version__containerversion__entity_list__in=lists
1385
- # )
1386
- return qs
1381
+ )
1382
+ return qs.order_by("pk").distinct() # Ordering is mostly for consistent test cases.
1387
1383
 
1388
1384
 
1389
1385
  def get_container_children_count(
@@ -1,7 +1,6 @@
1
1
  # Generated by Django 4.2.20 on 2025-04-17 18:22
2
-
3
- from django.db import migrations, models
4
2
  import django.db.models.deletion
3
+ from django.db import migrations, models
5
4
 
6
5
 
7
6
  class Migration(migrations.Migration):
@@ -0,0 +1,310 @@
1
+ """Sections API.
2
+
3
+ This module provides functions to manage sections.
4
+ """
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+
8
+ from django.db.transaction import atomic
9
+
10
+ from openedx_learning.apps.authoring.subsections.models import Subsection, SubsectionVersion
11
+
12
+ from ..publishing import api as publishing_api
13
+ from .models import Section, SectionVersion
14
+
15
+ # 🛑 UNSTABLE: All APIs related to containers are unstable until we've figured
16
+ # out our approach to dynamic content (randomized, A/B tests, etc.)
17
+ __all__ = [
18
+ "create_section",
19
+ "create_section_version",
20
+ "create_next_section_version",
21
+ "create_section_and_version",
22
+ "get_section",
23
+ "get_section_version",
24
+ "get_latest_section_version",
25
+ "SectionListEntry",
26
+ "get_subsections_in_section",
27
+ "get_subsections_in_section",
28
+ "get_subsections_in_published_section_as_of",
29
+ ]
30
+
31
+
32
+ def create_section(
33
+ learning_package_id: int,
34
+ key: str,
35
+ created: datetime,
36
+ created_by: int | None,
37
+ *,
38
+ can_stand_alone: bool = True,
39
+ ) -> Section:
40
+ """
41
+ [ 🛑 UNSTABLE ] Create a new section.
42
+
43
+ Args:
44
+ learning_package_id: The learning package ID.
45
+ key: The key.
46
+ created: The creation date.
47
+ created_by: The user who created the section.
48
+ can_stand_alone: Set to False when created as part of containers
49
+ """
50
+ return publishing_api.create_container(
51
+ learning_package_id,
52
+ key,
53
+ created,
54
+ created_by,
55
+ can_stand_alone=can_stand_alone,
56
+ container_cls=Section,
57
+ )
58
+
59
+
60
+ def create_section_version(
61
+ section: Section,
62
+ version_num: int,
63
+ *,
64
+ title: str,
65
+ entity_rows: list[publishing_api.ContainerEntityRow],
66
+ created: datetime,
67
+ created_by: int | None = None,
68
+ ) -> SectionVersion:
69
+ """
70
+ [ 🛑 UNSTABLE ] Create a new section version.
71
+
72
+ This is a very low-level API, likely only needed for import/export. In
73
+ general, you will use `create_section_and_version()` and
74
+ `create_next_section_version()` instead.
75
+
76
+ Args:
77
+ section_pk: The section ID.
78
+ version_num: The version number.
79
+ title: The title.
80
+ entity_rows: child entities/versions
81
+ created: The creation date.
82
+ created_by: The user who created the section.
83
+ """
84
+ return publishing_api.create_container_version(
85
+ section.pk,
86
+ version_num,
87
+ title=title,
88
+ entity_rows=entity_rows,
89
+ created=created,
90
+ created_by=created_by,
91
+ container_version_cls=SectionVersion,
92
+ )
93
+
94
+
95
+ def _pub_entities_for_subsections(
96
+ subsections: list[Subsection | SubsectionVersion] | None,
97
+ ) -> list[publishing_api.ContainerEntityRow] | None:
98
+ """
99
+ Helper method: given a list of Subsection | SubsectionVersion, return the
100
+ lists of publishable_entities_pks and entity_version_pks needed for the
101
+ base container APIs.
102
+
103
+ SubsectionVersion is passed when we want to pin a specific version, otherwise
104
+ Subsection is used for unpinned.
105
+ """
106
+ if subsections is None:
107
+ # When these are None, that means don't change the entities in the list.
108
+ return None
109
+ for u in subsections:
110
+ if not isinstance(u, (Subsection, SubsectionVersion)):
111
+ raise TypeError("Section subsections must be either Subsection or SubsectionVersion.")
112
+ return [
113
+ (
114
+ publishing_api.ContainerEntityRow(
115
+ entity_pk=s.container.publishable_entity_id,
116
+ version_pk=None,
117
+ ) if isinstance(s, Subsection)
118
+ else publishing_api.ContainerEntityRow(
119
+ entity_pk=s.subsection.container.publishable_entity_id,
120
+ version_pk=s.container_version.publishable_entity_version_id,
121
+ )
122
+ )
123
+ for s in subsections
124
+ ]
125
+
126
+
127
+ def create_next_section_version(
128
+ section: Section,
129
+ *,
130
+ title: str | None = None,
131
+ subsections: list[Subsection | SubsectionVersion] | None = None,
132
+ created: datetime,
133
+ created_by: int | None = None,
134
+ entities_action: publishing_api.ChildrenEntitiesAction = publishing_api.ChildrenEntitiesAction.REPLACE,
135
+ ) -> SectionVersion:
136
+ """
137
+ [ 🛑 UNSTABLE ] Create the next section version.
138
+
139
+ Args:
140
+ section_pk: The section ID.
141
+ title: The title. Leave as None to keep the current title.
142
+ subsections: The subsections, as a list of Subsections (unpinned) and/or SubsectionVersions (pinned).
143
+ Passing None will leave the existing subsections unchanged.
144
+ created: The creation date.
145
+ created_by: The user who created the section.
146
+ """
147
+ entity_rows = _pub_entities_for_subsections(subsections)
148
+ section_version = publishing_api.create_next_container_version(
149
+ section.pk,
150
+ title=title,
151
+ entity_rows=entity_rows,
152
+ created=created,
153
+ created_by=created_by,
154
+ container_version_cls=SectionVersion,
155
+ entities_action=entities_action,
156
+ )
157
+ return section_version
158
+
159
+
160
+ def create_section_and_version(
161
+ learning_package_id: int,
162
+ key: str,
163
+ *,
164
+ title: str,
165
+ subsections: list[Subsection | SubsectionVersion] | None = None,
166
+ created: datetime,
167
+ created_by: int | None = None,
168
+ can_stand_alone: bool = True,
169
+ ) -> tuple[Section, SectionVersion]:
170
+ """
171
+ [ 🛑 UNSTABLE ] Create a new section and its version.
172
+
173
+ Args:
174
+ learning_package_id: The learning package ID.
175
+ key: The key.
176
+ created: The creation date.
177
+ created_by: The user who created the section.
178
+ can_stand_alone: Set to False when created as part of containers
179
+ """
180
+ entity_rows = _pub_entities_for_subsections(subsections)
181
+ with atomic():
182
+ section = create_section(
183
+ learning_package_id,
184
+ key,
185
+ created,
186
+ created_by,
187
+ can_stand_alone=can_stand_alone,
188
+ )
189
+ section_version = create_section_version(
190
+ section,
191
+ 1,
192
+ title=title,
193
+ entity_rows=entity_rows or [],
194
+ created=created,
195
+ created_by=created_by,
196
+ )
197
+ return section, section_version
198
+
199
+
200
+ def get_section(section_pk: int) -> Section:
201
+ """
202
+ [ 🛑 UNSTABLE ] Get a section.
203
+
204
+ Args:
205
+ section_pk: The section ID.
206
+ """
207
+ return Section.objects.get(pk=section_pk)
208
+
209
+
210
+ def get_section_version(section_version_pk: int) -> SectionVersion:
211
+ """
212
+ [ 🛑 UNSTABLE ] Get a section version.
213
+
214
+ Args:
215
+ section_version_pk: The section version ID.
216
+ """
217
+ return SectionVersion.objects.get(pk=section_version_pk)
218
+
219
+
220
+ def get_latest_section_version(section_pk: int) -> SectionVersion:
221
+ """
222
+ [ 🛑 UNSTABLE ] Get the latest section version.
223
+
224
+ Args:
225
+ section_pk: The section ID.
226
+ """
227
+ return Section.objects.get(pk=section_pk).versioning.latest
228
+
229
+
230
+ @dataclass(frozen=True)
231
+ class SectionListEntry:
232
+ """
233
+ [ 🛑 UNSTABLE ]
234
+ Data about a single entity in a container, e.g. a subsection in a section.
235
+ """
236
+ subsection_version: SubsectionVersion
237
+ pinned: bool = False
238
+
239
+ @property
240
+ def subsection(self):
241
+ return self.subsection_version.subsection
242
+
243
+
244
+ def get_subsections_in_section(
245
+ section: Section,
246
+ *,
247
+ published: bool,
248
+ ) -> list[SectionListEntry]:
249
+ """
250
+ [ 🛑 UNSTABLE ]
251
+ Get the list of entities and their versions in the draft or published
252
+ version of the given Section.
253
+
254
+ Args:
255
+ section: The Section, e.g. returned by `get_section()`
256
+ published: `True` if we want the published version of the section, or
257
+ `False` for the draft version.
258
+ """
259
+ assert isinstance(section, Section)
260
+ subsections = []
261
+ for entry in publishing_api.get_entities_in_container(section, published=published):
262
+ # Convert from generic PublishableEntityVersion to SubsectionVersion:
263
+ subsection_version = entry.entity_version.containerversion.subsectionversion
264
+ assert isinstance(subsection_version, SubsectionVersion)
265
+ subsections.append(SectionListEntry(subsection_version=subsection_version, pinned=entry.pinned))
266
+ return subsections
267
+
268
+
269
+ def get_subsections_in_published_section_as_of(
270
+ section: Section,
271
+ publish_log_id: int,
272
+ ) -> list[SectionListEntry] | None:
273
+ """
274
+ [ 🛑 UNSTABLE ]
275
+ Get the list of entities and their versions in the published version of the
276
+ given container as of the given PublishLog version (which is essentially a
277
+ version for the entire learning package).
278
+
279
+ TODO: This API should be updated to also return the SectionVersion so we can
280
+ see the section title and any other metadata from that point in time.
281
+ TODO: accept a publish log UUID, not just int ID?
282
+ TODO: move the implementation to be a generic 'containers' implementation
283
+ that this sections function merely wraps.
284
+ TODO: optimize, perhaps by having the publishlog store a record of all
285
+ ancestors of every modified PublishableEntity in the publish.
286
+ """
287
+ assert isinstance(section, Section)
288
+ section_pub_entity_version = publishing_api.get_published_version_as_of(
289
+ section.publishable_entity_id, publish_log_id
290
+ )
291
+ if section_pub_entity_version is None:
292
+ return None # This section was not published as of the given PublishLog ID.
293
+ container_version = section_pub_entity_version.containerversion
294
+
295
+ entity_list = []
296
+ rows = container_version.entity_list.entitylistrow_set.order_by("order_num")
297
+ for row in rows:
298
+ if row.entity_version is not None:
299
+ subsection_version = row.entity_version.containerversion.subsectionversion
300
+ assert isinstance(subsection_version, SubsectionVersion)
301
+ entity_list.append(SectionListEntry(subsection_version=subsection_version, pinned=True))
302
+ else:
303
+ # Unpinned subsection - figure out what its latest published version was.
304
+ # This is not optimized. It could be done in one query per section rather than one query per subsection.
305
+ pub_entity_version = publishing_api.get_published_version_as_of(row.entity_id, publish_log_id)
306
+ if pub_entity_version:
307
+ entity_list.append(SectionListEntry(
308
+ subsection_version=pub_entity_version.containerversion.subsectionversion, pinned=False
309
+ ))
310
+ return entity_list
@@ -0,0 +1,25 @@
1
+ """
2
+ Subsection Django application initialization.
3
+ """
4
+
5
+ from django.apps import AppConfig
6
+
7
+
8
+ class SectionsConfig(AppConfig):
9
+ """
10
+ Configuration for the subsections Django application.
11
+ """
12
+
13
+ name = "openedx_learning.apps.authoring.sections"
14
+ verbose_name = "Learning Core > Authoring > Sections"
15
+ default_auto_field = "django.db.models.BigAutoField"
16
+ label = "oel_sections"
17
+
18
+ def ready(self):
19
+ """
20
+ Register Section and SectionVersion.
21
+ """
22
+ from ..publishing.api import register_content_models # pylint: disable=import-outside-toplevel
23
+ from .models import Section, SectionVersion # pylint: disable=import-outside-toplevel
24
+
25
+ register_content_models(Section, SectionVersion)
@@ -0,0 +1,36 @@
1
+ # Generated by Django 4.2.20 on 2025-04-11 12:53
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ('oel_publishing', '0005_alter_entitylistrow_options'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.CreateModel(
17
+ name='Section',
18
+ fields=[
19
+ ('container', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='oel_publishing.container')),
20
+ ],
21
+ options={
22
+ 'abstract': False,
23
+ },
24
+ bases=('oel_publishing.container',),
25
+ ),
26
+ migrations.CreateModel(
27
+ name='SectionVersion',
28
+ fields=[
29
+ ('container_version', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='oel_publishing.containerversion')),
30
+ ],
31
+ options={
32
+ 'abstract': False,
33
+ },
34
+ bases=('oel_publishing.containerversion',),
35
+ ),
36
+ ]
@@ -0,0 +1,50 @@
1
+ """
2
+ Models that implement sections
3
+ """
4
+ from django.db import models
5
+
6
+ from ..publishing.models import Container, ContainerVersion
7
+
8
+ __all__ = [
9
+ "Section",
10
+ "SectionVersion",
11
+ ]
12
+
13
+
14
+ class Section(Container):
15
+ """
16
+ A Section is type of Container that holds Units.
17
+
18
+ Via Container and its PublishableEntityMixin, Sections are also publishable
19
+ entities and can be added to other containers.
20
+ """
21
+ container = models.OneToOneField(
22
+ Container,
23
+ on_delete=models.CASCADE,
24
+ parent_link=True,
25
+ primary_key=True,
26
+ )
27
+
28
+
29
+ class SectionVersion(ContainerVersion):
30
+ """
31
+ A SectionVersion is a specific version of a Section.
32
+
33
+ Via ContainerVersion and its EntityList, it defines the list of Units
34
+ in this version of the Section.
35
+ """
36
+ container_version = models.OneToOneField(
37
+ ContainerVersion,
38
+ on_delete=models.CASCADE,
39
+ parent_link=True,
40
+ primary_key=True,
41
+ )
42
+
43
+ @property
44
+ def section(self):
45
+ """ Convenience accessor to the Section this version is associated with """
46
+ return self.container_version.container.section # pylint: disable=no-member
47
+
48
+ # Note: the 'publishable_entity_version' field is inherited and will appear on this model, but does not exist
49
+ # in the underlying database table. It only exists in the ContainerVersion table.
50
+ # You can verify this by running 'python manage.py sqlmigrate oel_sections 0001_initial'