openedx-learning 0.23.0__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.
- {openedx_learning-0.23.0/openedx_learning.egg-info → openedx_learning-0.25.0}/PKG-INFO +4 -4
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/__init__.py +1 -1
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/api/authoring.py +2 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/api/authoring_models.py +2 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/api.py +7 -11
- openedx_learning-0.25.0/openedx_learning/apps/authoring/publishing/migrations/0008_alter_draftchangelogrecord_options_and_more.py +31 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/api.py +310 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/apps.py +25 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/migrations/0001_initial.py +36 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/sections/models.py +50 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/api.py +309 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/apps.py +25 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/migrations/0001_initial.py +36 -0
- openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/models.py +50 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0/openedx_learning.egg-info}/PKG-INFO +4 -4
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning.egg-info/SOURCES.txt +13 -0
- openedx_learning-0.25.0/openedx_tagging/core/tagging/migrations/__init__.py +0 -0
- openedx_learning-0.25.0/openedx_tagging/core/tagging/rest_api/__init__.py +0 -0
- openedx_learning-0.25.0/openedx_tagging/core/tagging/rest_api/v1/__init__.py +0 -0
- openedx_learning-0.25.0/openedx_tagging/py.typed +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/CHANGELOG.rst +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/LICENSE.txt +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/MANIFEST.in +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/README.rst +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/api/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/admin.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0002_remove_collection_name_collection_created_by_and_more.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0003_collection_entities.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0004_collection_key.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/0005_alter_collection_options_alter_collection_enabled.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/migrations/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/collections/models.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/admin.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/commands/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/management/commands/add_assets_to_component.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0002_alter_componentversioncontent_key.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/0003_remove_componentversioncontent_learner_downloadable.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/migrations/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/components/models.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/admin.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/migrations/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/contents/models.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/admin.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/contextmanagers.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0002_alter_learningpackage_key_and_more.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0003_containers.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0004_publishableentity_can_stand_alone.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0005_alter_entitylistrow_options.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0006_draftchangelog.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/0007_bootstrap_draftchangelog.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/migrations/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/container.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/draft_log.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/entity_list.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/learning_package.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/publish_log.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/publishing/models/publishable_entity.py +0 -0
- {openedx_learning-0.23.0/openedx_learning/apps/authoring/units → openedx_learning-0.25.0/openedx_learning/apps/authoring/sections}/__init__.py +0 -0
- {openedx_learning-0.23.0/openedx_learning/apps/authoring/units → openedx_learning-0.25.0/openedx_learning/apps/authoring/sections}/migrations/__init__.py +0 -0
- {openedx_learning-0.23.0/openedx_learning/contrib → openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections}/__init__.py +0 -0
- {openedx_learning-0.23.0/openedx_learning/lib → openedx_learning-0.25.0/openedx_learning/apps/authoring/subsections/migrations}/__init__.py +0 -0
- {openedx_learning-0.23.0/openedx_tagging/core → openedx_learning-0.25.0/openedx_learning/apps/authoring/units}/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0/openedx_tagging/core/tagging → openedx_learning-0.25.0/openedx_learning/apps/authoring/units/migrations}/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/apps/authoring/units/models.py +0 -0
- {openedx_learning-0.23.0/openedx_tagging/core/tagging/migrations → openedx_learning-0.25.0/openedx_learning/contrib}/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/urls.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/contrib/media_server/views.py +0 -0
- {openedx_learning-0.23.0/openedx_tagging/core/tagging/rest_api → openedx_learning-0.25.0/openedx_learning/lib}/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/admin_utils.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/cache.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/collations.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/fields.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/managers.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/test_utils.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/lib/validators.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/py.typed +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning.egg-info/dependency_links.txt +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning.egg-info/not-zip-safe +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning.egg-info/requires.txt +3 -3
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning.egg-info/top_level.txt +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/__init__.py +0 -0
- {openedx_learning-0.23.0/openedx_tagging/core/tagging/rest_api/v1 → openedx_learning-0.25.0/openedx_tagging/core}/__init__.py +0 -0
- /openedx_learning-0.23.0/openedx_tagging/py.typed → /openedx_learning-0.25.0/openedx_tagging/core/tagging/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/admin.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/apps.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/data.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/actions.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/api.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/exceptions.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/import_plan.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/parsers.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/tasks.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/template.csv +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/import_export/template.json +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0001_initial.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0001_squashed.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0002_auto_20230718_2026.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0003_auto_20230721_1238.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0004_auto_20230723_2001.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0005_language_taxonomy.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0006_alter_objecttag_unique_together.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0006_auto_20230802_1631.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0007_tag_import_task_log_null_fix.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0008_taxonomy_description_not_null.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0009_alter_objecttag_object_id.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0010_cleanups.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0011_remove_required.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0012_language_taxonomy.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0013_tag_parent_blank.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0014_minor_fixes.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0015_taxonomy_export_id.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0016_object_tag_export_id.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0017_alter_tagimporttask_status.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/migrations/0018_objecttag_is_copied.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/__init__.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/base.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/import_export.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/system_defined.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/models/utils.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/paginators.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/urls.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/utils.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/permissions.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/serializers.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/urls.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/views.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rest_api/v1/views_import.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/rules.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_tagging/core/tagging/urls.py +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/requirements/base.in +0 -0
- {openedx_learning-0.23.0 → openedx_learning-0.25.0}/setup.cfg +0 -0
- {openedx_learning-0.23.0 → 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.
|
|
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
|
-
Requires-Dist: djangorestframework<4.0
|
|
25
23
|
Requires-Dist: edx-drf-extensions
|
|
26
|
-
Requires-Dist:
|
|
24
|
+
Requires-Dist: djangorestframework<4.0
|
|
27
25
|
Requires-Dist: celery
|
|
26
|
+
Requires-Dist: Django
|
|
27
|
+
Requires-Dist: rules<4.0
|
|
28
28
|
Dynamic: author
|
|
29
29
|
Dynamic: author-email
|
|
30
30
|
Dynamic: classifier
|
|
@@ -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
|
{openedx_learning-0.23.0 → openedx_learning-0.25.0}/openedx_learning/api/authoring_models.py
RENAMED
|
@@ -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
|
|
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
|
-
)
|
|
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
|
-
)
|
|
1380
|
-
|
|
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(
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Generated by Django 4.2.20 on 2025-04-17 18:22
|
|
2
|
+
import django.db.models.deletion
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('oel_publishing', '0007_bootstrap_draftchangelog'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='draftchangelogrecord',
|
|
15
|
+
options={'verbose_name': 'Draft Change Log Record', 'verbose_name_plural': 'Draft Change Log Records'},
|
|
16
|
+
),
|
|
17
|
+
migrations.AlterModelOptions(
|
|
18
|
+
name='draftsideeffect',
|
|
19
|
+
options={'verbose_name': 'Draft Side Effect', 'verbose_name_plural': 'Draft Side Effects'},
|
|
20
|
+
),
|
|
21
|
+
migrations.AlterField(
|
|
22
|
+
model_name='draftchangelogrecord',
|
|
23
|
+
name='draft_change_log',
|
|
24
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records', to='oel_publishing.draftchangelog'),
|
|
25
|
+
),
|
|
26
|
+
migrations.AlterField(
|
|
27
|
+
model_name='draftsideeffect',
|
|
28
|
+
name='effect',
|
|
29
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='affected_by', to='oel_publishing.draftchangelogrecord'),
|
|
30
|
+
),
|
|
31
|
+
]
|
|
@@ -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'
|