diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 98f11efff..92fa0e4d3 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -20,11 +20,28 @@ jobs: - name: Install pip run: pip install -r requirements/pip.txt - - name: Build package + - name: Build the package (openedx-core) run: python setup.py sdist bdist_wheel - - name: Publish to PyPI + - name: Publish openedx-core to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_UPLOAD_TOKEN }} + + # TEMPORARY: Build and publish the transitional openedx-learning shell package. + # TODO: Remove after the transition is complete. + # See https://github.com/openedx/openedx-learning/issues/470 + + - name: Build shell package (openedx-learning) + run: | + cd tmp-openedx-learning + python setup.py sdist bdist_wheel + cd .. + + - name: Publish openedx-learning shell to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_UPLOAD_TOKEN }} + packages-dir: tmp-openedx-learning/dist/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index be5c0b25f..000000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,28 +0,0 @@ -Change Log ----------- - -.. - All enhancements and patches to openedx-learning will be documented - in this file. It adheres to the structure of https://keepachangelog.com/ , - but in reStructuredText instead of Markdown (for ease of incorporation into - Sphinx documentation and the PyPI description). - - This project adheres to Semantic Versioning (https://semver.org/). - -.. There should always be an "Unreleased" section for changes pending release. - -Unreleased -~~~~~~~~~~ - -* Removed usage of ``tox-battery`` and added support for ``tox 4.0`` -* Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is - deprecated. See https://github.com/openedx/edx-sphinx-theme/issues/184 for - more details. - -[0.1.0] - 2021-08-08 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Added -_____ - -* First release on PyPI. diff --git a/README.rst b/README.rst index 5d375f9b9..ac8bd8b40 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -Open edX Learning Core (and Tagging) -==================================== +Open edX Core: Foundational Packages for a Teaching & Learning Platform +======================================================================= |pypi-badge| |ci-badge| |codecov-badge| |doc-badge| |pyversions-badge| |license-badge| @@ -7,22 +7,26 @@ Open edX Learning Core (and Tagging) Overview -------- -The ``openedx-learning`` project holds Django apps that represent core learning platform concepts. +*Formerly known as "Learning Core" or "openedx-learning".* + +The ``openedx-core`` project holds Django apps which represent core teaching & learning platform concepts. + +Each app exposes stable, public API of Python functions and Django models. Some apps additionally provides REST APIs. These APIs are suitable for use in ``openedx-platform`` as well as in community-developed Open edX plugins. Motivation ---------- -The short term goal of this project is to create a small, extensible core that is easier to reason about and write extensions for than openedx-platform. The longer term goal is to create a more nimble core learning platform, enabling rapid experimentation and drastic changes to the learner experience that are difficult to implement with Open edX today. +The short term goal of this project is to create a small, extensible core that is easier to reason about and write extensions for than ``openedx-platform``. The longer term goal is to create a more nimble core learning platform, enabling rapid experimentation and drastic changes to the learner experience that are difficult to implement with Open edX today. -Replacing openedx-platform is explicitly *not* a goal of this project, as only a small fraction of the concepts in openedx-platform make sense to carry over here. When these core concepts are extracted and the data migrated, openedx-platform will import apps from this repo and make use of their public in-process APIs. +Replacing ``openedx-platform`` is explicitly *not* a goal of this project, as only a small fraction of the concepts in openedx-platform make sense to carry over here. When these core concepts are extracted and the data migrated, openedx-platform will import apps from this repo and make use of their public in-process APIs. Architecture ------------ -Learning Core Package Dependencies +Open edX Core Package Dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Learning Core code should never import from ``openedx-platform``. +Open edX Core code should never import from ``openedx-platform``. We want to be very strict about dependency management internally as well. Please read the `.importlinter config file <.importlinter>`_ file and the `Python API Conventions ADR `_ for more details. @@ -37,7 +41,6 @@ We have a few different identifier types in the schema, and we try to avoid ``_i * ``key`` is intended to be a case-sensitive, alphanumeric key, which holds some meaning to library clients. This is usually stable, but can be changed, depending on the business logic of the client. The apps in this repo should make no assumptions about it being stable. It can be used as a suffix. Since ``key`` is a reserved name on certain database systems, the database field is ``_key``. * ``num`` is like ``key``, but for use when it's strictly numeric. It can also be used as a suffix. - See Also ~~~~~~~~ @@ -49,66 +52,10 @@ The structure of this repo follows [OEP-0049](https://open-edx-proposals.readthe Code Overview ------------- -The ``src`` folder contains all our Django applications. - -Development Workflow --------------------- - -One Time Setup -~~~~~~~~~~~~~~ -.. code-block:: - - # Clone the repository - git clone git@github.com:ormsbee/openedx-learning.git - cd openedx-learning - - # Set up a virtualenv using virtualenvwrapper with the same name as the repo and activate it - mkvirtualenv -p python3.11 openedx-learning - - -Every time you develop something in this repo -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: - - # Activate the virtualenv - workon openedx-learning - - # Grab the latest code - git checkout master - git pull - - # Install/update the dev requirements - make requirements - - # Run the tests and quality checks (to verify the status before you make any changes) - make validate - - # Make a new branch for your changes - git checkout -b / - - # Using your favorite editor, edit the code to make your change. - vim … - - # Run your new tests - pytest ./path/to/new/tests - - # Run all the tests and quality checks - make validate - - # Commit all your changes - git commit … - git push - - # Open a PR and ask for review. - -Configuring Visual Studio Code ------------------------------- - -If you are using VS Code as your editor, you can enable the Testing bar by copying from the example configuration provided in the ``.vscode`` directory:: - - cd .vscode/ - cp launch.json.example launch.json - cp settings.json.example settings.json +* ``./src/``: All published code. Packages are importable relative to this directory (e.g., ``import openedx_content``). See ``readme.rst`` in each sub-folder. +* ``./tests/``: Unit tests (not published). +* ``./test_utils/``: Internal helpers for unit tests (not published). +* ``./olx_importer/``: Internal utility for importing data for development (not published). License ------- @@ -140,26 +87,26 @@ For more information about these options, see the `Getting Help`_ page. .. _community Slack workspace: https://openedx.slack.com/ .. _Getting Help: https://openedx.org/getting-help -.. |pypi-badge| image:: https://img.shields.io/pypi/v/openedx-learning.svg - :target: https://pypi.python.org/pypi/openedx-learning/ +.. |pypi-badge| image:: https://img.shields.io/pypi/v/openedx-core.svg + :target: https://pypi.python.org/pypi/openedx-core/ :alt: PyPI -.. |ci-badge| image:: https://github.com/openedx/openedx-learning/workflows/Python%20CI/badge.svg?branch=master - :target: https://github.com/openedx/openedx-learning/actions +.. |ci-badge| image:: https://github.com/openedx/openedx-core/workflows/Python%20CI/badge.svg?branch=master + :target: https://github.com/openedx/openedx-core/actions :alt: CI -.. |codecov-badge| image:: https://codecov.io/github/edx/openedx-learning/coverage.svg?branch=master - :target: https://codecov.io/github/edx/openedx-learning?branch=master +.. |codecov-badge| image:: https://codecov.io/github/edx/openedx-core/coverage.svg?branch=master + :target: https://codecov.io/github/edx/openedx-core?branch=master :alt: Codecov -.. |doc-badge| image:: https://readthedocs.org/projects/openedx-learning/badge/?version=latest - :target: https://openedx-learning.readthedocs.io/en/latest/ +.. |doc-badge| image:: https://readthedocs.org/projects/openedx-core/badge/?version=latest + :target: https://openedx-core.readthedocs.io/en/latest/ :alt: Documentation -.. |pyversions-badge| image:: https://img.shields.io/pypi/pyversions/openedx-learning.svg - :target: https://pypi.python.org/pypi/openedx-learning/ +.. |pyversions-badge| image:: https://img.shields.io/pypi/pyversions/openedx-core.svg + :target: https://pypi.python.org/pypi/openedx-core/ :alt: Supported Python versions -.. |license-badge| image:: https://img.shields.io/github/license/edx/openedx-learning.svg - :target: https://github.com/openedx/openedx-learning/blob/master/LICENSE.txt +.. |license-badge| image:: https://img.shields.io/github/license/edx/openedx-core.svg + :target: https://github.com/openedx/openedx-core/blob/master/LICENSE.txt :alt: License diff --git a/catalog-info.yaml b/catalog-info.yaml index d57c3aa29..b9ac69b84 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -1,7 +1,7 @@ apiVersion: backstage.io/v1alpha1 kind: Component metadata: - name: openedx-learning + name: openedx-core spec: type: other lifecycle: unknown diff --git a/docs/Makefile b/docs/Makefile index 0bd821a16..56adb4cd8 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -96,9 +96,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/openedx-learning.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/openedx-core.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/openedx-learning.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/openedx-core.qhc" .PHONY: applehelp applehelp: @@ -115,8 +115,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/openedx-learning" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/openedx-learning" + @echo "# mkdir -p $$HOME/.local/share/devhelp/openedx-core" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/openedx-core" @echo "# devhelp" .PHONY: epub diff --git a/docs/conf.py b/docs/conf.py index 98a61ba92..8153186e7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # pylint: disable=invalid-name """ -openedx-learning documentation build configuration file. +openedx-core documentation build configuration file. This file is execfile()d with the current directory set to its containing dir. @@ -89,10 +89,10 @@ def get_version(*file_paths): top_level_doc = 'index' # General information about the project. -project = 'openedx-learning' -copyright = f'{datetime.now().year}, edX Inc.' # pylint: disable=redefined-builtin -author = 'edX Inc.' -project_title = 'Open edX Learning Core' +project = 'openedx-core' +copyright = f'{datetime.now().year}, Axim Collaborative, Inc.' # pylint: disable=redefined-builtin +author = 'Axim Collaborative, Inc.' +project_title = 'Open edX Core' documentation_title = f"{project_title}" # The version info for the project you're documenting, acts as replacement for @@ -169,7 +169,7 @@ def get_version(*file_paths): # documentation. # html_theme_options = { - "repository_url": "https://github.com/openedx/openedx-learning", + "repository_url": "https://github.com/openedx/openedx-core", "repository_branch": "main", "path_to_docs": "docs/", "home_page_in_toc": True, @@ -206,7 +206,7 @@ def get_version(*file_paths): # The name for this set of Sphinx documents. # " v documentation" by default. # -# html_title = 'openedx-learning v0.1.0' +# html_title = 'openedx-core v0.1.0' # A shorter title for the navigation bar. Default is the same as html_title. # diff --git a/docs/decisions/0001-purpose-of-this-repo.rst b/docs/decisions/0001-purpose-of-this-repo.rst index 0b913886c..d6b412f0c 100644 --- a/docs/decisions/0001-purpose-of-this-repo.rst +++ b/docs/decisions/0001-purpose-of-this-repo.rst @@ -12,7 +12,7 @@ The code that operates on learning content in Open edX primarily resides in edx- Decision -------- -The openedx-learning repository was created to provide a new place for certain core learning concepts, data models, and APIs to be implemented. These concepts will be more granular and composable than the courses we have today. +The openedx-core repository was created to provide a new place for certain core learning concepts, data models, and APIs to be implemented. These concepts will be more granular and composable than the courses we have today. This would have two long term goals: @@ -24,7 +24,7 @@ This repo will first be piloted with the use case of unit composition in service Consequences ------------ -The edx-platform repo will eventually have openedx-learning as a dependency. As functionality is implemented in openedx-learning (e.g. unit composition for content libraries), edx-platform will make use of it. +The edx-platform repo will eventually have openedx-core as a dependency. As functionality is implemented in openedx-core (e.g. unit composition for content libraries), edx-platform will make use of it. Over time, plugin apps should be able to make use of stable APIs in this repo instead of having to call into edx-platform's Modulestore or Block Transformers. This will serve as a third leg of the new in-process extension mechanisms, where openedx-events provides event notification, openedx-filters provides the ability to intercept and modify the workflow of existing views, and this repo will allow content querying capability. diff --git a/docs/decisions/0003-content-extensibility.rst b/docs/decisions/0003-content-extensibility.rst index 17b73b836..db749b4c9 100644 --- a/docs/decisions/0003-content-extensibility.rst +++ b/docs/decisions/0003-content-extensibility.rst @@ -35,7 +35,7 @@ Learning Core data models will be built with extensibility in mind, with the fol #. It will be possible to migrate existing content data over time, as new plugin apps become available. #. All content and versions of content will have UUIDs to allow for stable references across services. -This layering of related models will add complexity to the data model, but we accept that tradeoff to decouple plugin models from the core application and from other plugins. To make this easier to deal with, openedx-learning should provide abstract models for common use cases, and expose those via a ``models_api.py`` module. This will lower the barrier to entry for developers, and allow us to more easily enforce conventions like setting ``primary_key=True`` with our ``OneToOneField`` relationships. +This layering of related models will add complexity to the data model, but we accept that tradeoff to decouple plugin models from the core application and from other plugins. To make this easier to deal with, openedx-core should provide abstract models for common use cases, and expose those via a ``models_api.py`` module. This will lower the barrier to entry for developers, and allow us to more easily enforce conventions like setting ``primary_key=True`` with our ``OneToOneField`` relationships. Rejected Alternatives --------------------- diff --git a/docs/decisions/0007-tagging-app.rst b/docs/decisions/0007-tagging-app.rst index 9993a49bb..ce5c75fc7 100644 --- a/docs/decisions/0007-tagging-app.rst +++ b/docs/decisions/0007-tagging-app.rst @@ -4,7 +4,7 @@ Context ------- -We want the openedx_tagging app to be useful in different Django projects outside of just openedx-learning and edx-platform. +We want the openedx_tagging app to be useful in different Django projects outside of just openedx-core and edx-platform. Decisions diff --git a/docs/decisions/0015-serving-static-assets.rst b/docs/decisions/0015-serving-static-assets.rst index 969b40725..3fd75ff0f 100644 --- a/docs/decisions/0015-serving-static-assets.rst +++ b/docs/decisions/0015-serving-static-assets.rst @@ -8,18 +8,18 @@ Both Studio and the LMS need to serve course team authored static assets as part This ADR is the synthesis of various ideas that were discussed across a handful of pull requests and issues. These links are provided for extra context, but they are not required to understand this ADR: -* `File uploads + Experimental Media Server #31 `_ -* `File Uploads + media_server app #33 `_ -* `Modeling Files and File Dependencies #70 `_ -* `Serving static assets (disorganized thoughts) #108 `_ +* `File uploads + Experimental Media Server #31 `_ +* `File Uploads + media_server app #33 `_ +* `Modeling Files and File Dependencies #70 `_ +* `Serving static assets (disorganized thoughts) #108 `_ Data Storage Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The underlying data models live in the openedx-learning repo. The most relevant models are: +The underlying data models live in the openedx-core repo. The most relevant models are: -* `Content in contents/models.py `_ -* `Component and ComponentVersion in components/models.py `_ +* `Content in contents/models.py `_ +* `Component and ComponentVersion in components/models.py `_ Key takeaways about how this data is stored: @@ -60,7 +60,7 @@ Security Requirements **Assets should enforce more granular permissions at the individual Component level.** An important distinction between ContentStore and v2 Content Library assets is that the latter can be directly associated with a Component. As a long term goal, we should be able to make permissions check on per-Component basis. So if a student does not have permission to view a Component for whatever reason (wrong content group, exam hasn't started, etc.), then they should also not have permission to see static assets associated with that component. - The further implication of this requirement is that *permissions checking must be extensible*. The openedx-learning repo will implement the details of how to serve an asset, but it will not have the necessary models and logic to determine whether it is allowed to. + The further implication of this requirement is that *permissions checking must be extensible*. The openedx-core repo will implement the details of how to serve an asset, but it will not have the necessary models and logic to determine whether it is allowed to. **Assets must be served from an entirely different domain than the LMS and Studio instances.** To reduce our chance of maliciously uploaded JavaScript compromising LMS and Studio users, user-uploaded assets must live on an entirely different domain from LMS and Studio (i.e. not just another subdomain). So if our LMS is located at ``sandbox.openedx.org``, the files should be accessed at a URL like ``assets.sandbox.openedx.io``. diff --git a/docs/decisions/0017-generalized-containers.rst b/docs/decisions/0017-generalized-containers.rst index 71d6abb4f..8b30e7e80 100644 --- a/docs/decisions/0017-generalized-containers.rst +++ b/docs/decisions/0017-generalized-containers.rst @@ -92,4 +92,4 @@ This section defines the rules for pruning container versions, explaining when a - In a top-down approach, start the deletion process with the parent container and work your way down to its children. E.g., when pruning Section V2 > Subsection V1 > Unit V3, the deletion process starts in the greater container working its way down to the smaller. - Pruning a container version will not affect the container's history or the children of other container versions, so containers will not be deleted if they are shared by other containers. -.. _PublishableEntity: https://github.com/openedx/openedx-learning/blob/main/openedx_learning/apps/authoring/publishing/models.py#L100-L184 +.. _PublishableEntity: https://github.com/openedx/openedx-core/blob/main/openedx_learning/apps/authoring/publishing/models.py#L100-L184 diff --git a/docs/index.rst b/docs/index.rst index 9845aeddc..12ecd90e8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,10 +1,10 @@ -.. openedx-learning documentation top level file, created by +.. openedx-core documentation top level file, created by sphinx-quickstart on Sun Aug 08 00:18:04 2021. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Open edX Learning Core -====================== +Open edX Core +============= The boring, foundational bits of a learning platform that are hard to get right at scale. Currently being developed for content storage. diff --git a/docs/make.bat b/docs/make.bat index ef59b0ecc..df6de45b4 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -129,9 +129,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\openedx-learning.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\openedx-core.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\openedx-learning.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\openedx-core.ghc goto end ) diff --git a/docs/public_apis/authoring_api.rst b/docs/public_apis/authoring_api.rst deleted file mode 100644 index b540e90eb..000000000 --- a/docs/public_apis/authoring_api.rst +++ /dev/null @@ -1,7 +0,0 @@ -Authoring API -============= - -.. automodule:: openedx_learning.api.authoring - :members: - :imported-members: - :show-inheritance: diff --git a/docs/public_apis/authoring_models.rst b/docs/public_apis/authoring_models.rst deleted file mode 100644 index 9a2ca135a..000000000 --- a/docs/public_apis/authoring_models.rst +++ /dev/null @@ -1,7 +0,0 @@ -Authoring Models -================ - -.. automodule:: openedx_learning.api.authoring_models - :members: - :imported-members: - :show-inheritance: diff --git a/docs/public_apis/content_api.rst b/docs/public_apis/content_api.rst new file mode 100644 index 000000000..926e2f9a8 --- /dev/null +++ b/docs/public_apis/content_api.rst @@ -0,0 +1,7 @@ +Content API +=========== + +.. automodule:: openedx_content.api + :members: + :imported-members: + :show-inheritance: diff --git a/docs/public_apis/content_models.rst b/docs/public_apis/content_models.rst new file mode 100644 index 000000000..81f3c2eb8 --- /dev/null +++ b/docs/public_apis/content_models.rst @@ -0,0 +1,7 @@ +Content Models +============== + +.. automodule:: openedx_content.models_api + :members: + :imported-members: + :show-inheritance: diff --git a/docs/testing.rst b/docs/testing.rst index 8314ea485..ed0887731 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -3,7 +3,7 @@ Testing ======= -openedx-learning has an assortment of test cases and code quality +openedx-core has an assortment of test cases and code quality checks to catch potential problems during development. To run them all in the version of Python you chose for your virtualenv: diff --git a/mysql_test_settings.py b/mysql_test_settings.py index ee58fe11e..1933fc0b0 100644 --- a/mysql_test_settings.py +++ b/mysql_test_settings.py @@ -1,6 +1,6 @@ """ This is an extension of the default test_settings.py file that uses MySQL for -the backend. While the openedx-learning apps should run fine using SQLite, they +the backend. While the openedx-core apps should run fine using SQLite, they also do some MySQL-specific things around charset/collation settings and row compression. diff --git a/olx_importer/management/commands/load_components.py b/olx_importer/management/commands/load_components.py index 832812b3b..688ba97c6 100644 --- a/olx_importer/management/commands/load_components.py +++ b/olx_importer/management/commands/load_components.py @@ -7,7 +7,7 @@ This script manipulates the data models directly, instead of using stable API calls. This is only because those APIs haven't been created yet, and this is trying to validate basic questions about the data model. This is not how apps -are intended to use openedx-learning in the longer term. +are intended to use openedx-core in the longer term. Open Question: If the data model is extensible, how do we know whether a change has really happened between what's currently stored/published for a particular diff --git a/openedx.yaml b/openedx.yaml deleted file mode 100644 index ca94b6acf..000000000 --- a/openedx.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# This file describes this Open edX repo, as described in OEP-2: -# https://open-edx-proposals.readthedocs.io/en/latest/oep-0002-bp-repo-metadata.html#specification - ---- -nick: openedx-learning -tags: - - tools -oeps: - oep-2: true - oep-3: - state: false - reason: TODO - Implement for this application if appropriate - oep-5: - state: false - reason: TODO - Implement for this application if appropriate - oep-18: true - oep-30: - state: true -# This repo isn't included in Open edX releases because it will -# eventually be a dependency of other repos. diff --git a/projects/dev.py b/projects/dev.py index 9f7488344..58c39eb53 100644 --- a/projects/dev.py +++ b/projects/dev.py @@ -103,7 +103,7 @@ USE_TZ = True -# openedx-learning required configuration +# openedx-core required configuration OPENEDX_LEARNING = { # Custom file storage, though this is better done through Django's # STORAGES setting in Django >= 4.2 diff --git a/setup.py b/setup.py index 99ee848a0..929a19a30 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -Package metadata for openedx-learning. +Package metadata for openedx-core. """ import os import re @@ -62,16 +62,15 @@ def is_requirement(line): sys.exit() README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() -CHANGELOG = open(os.path.join(os.path.dirname(__file__), 'CHANGELOG.rst')).read() setup( - name='openedx-learning', + name='openedx-core', version=VERSION, - description="""Open edX Learning Core and Tagging.""", - long_description=README + '\n\n' + CHANGELOG, + description="Open edX Core: Foundational Packages for a Teaching & Learning Platform""", + long_description=README, author='David Ormsbee', - author_email='dave@tcril.org', - url='https://github.com/openedx/openedx-learning', + author_email='dave@axim.org', + url='https://github.com/openedx/openedx-core', package_dir={"": "src"}, packages=find_packages("src"), include_package_data=True, diff --git a/src/openedx_content/README.rst b/src/openedx_content/README.rst new file mode 100644 index 000000000..30c04699e --- /dev/null +++ b/src/openedx_content/README.rst @@ -0,0 +1,5 @@ +Content App +=========== + +The ``openedx_content`` app provides a stable, efficient, and extensible suite of models +and APIs for representing learning content. \ No newline at end of file diff --git a/src/openedx_content/applets/collections/models.py b/src/openedx_content/applets/collections/models.py index 51ac7e323..2f315b247 100644 --- a/src/openedx_content/applets/collections/models.py +++ b/src/openedx_content/applets/collections/models.py @@ -25,16 +25,16 @@ if any updates have been made, using its version as a key. If a new version exists, the course team has the option of re-copying data from the Library. -ModuleStore based v1 Libraries and Blockstore-based v2 libraries both version +ModuleStore-based v1 Libraries and OeXCore-based v2 libraries both version the entire library in a series of snapshots. This makes it difficult to have very large libraries, which is an explicit goal for Modular Learning. In -Learning Core, we've moved to tracking the versions of individual Components to +Open edX Core, we've moved to tracking the versions of individual Components to address this issue. But that means we no longer have a single version indicator for "has anything here changed"? We *could* have put that version in the ``publishing`` app's PublishLog, but that would make it too broad. We want the ability to eventually collapse many v1 -Libraries into a single Learning Core backed v2 Library. If we tracked the +Libraries into a single OeXCore backed v2 Library. If we tracked the versioning in only a central location, then we'd have many false positives where the version was bumped because something else in the Learning Package changed. So instead, we're creating a new Collection model inside the LearningPackage to diff --git a/src/openedx_content/applets/components/admin.py b/src/openedx_content/applets/components/admin.py index dcc02f9f3..e85149c4a 100644 --- a/src/openedx_content/applets/components/admin.py +++ b/src/openedx_content/applets/components/admin.py @@ -143,7 +143,7 @@ def content_preview(cvc_obj: ComponentVersionContent) -> SafeText: if content_obj.media_type.type == "image": # This base64 encoding looks really goofy and is bad for performance, # but image previews in the admin are extremely useful, and this lets us - # have them without creating a separate view in Learning Core. (Keep in + # have them without creating a separate view in Open edX Core. (Keep in # mind that these assets are private, so they cannot be accessed via the # MEDIA_URL like most Django uploaded assets.) data = content_obj.read_file().read() diff --git a/src/openedx_content/applets/contents/models.py b/src/openedx_content/applets/contents/models.py index 31c565e68..74a89c6fa 100644 --- a/src/openedx_content/applets/contents/models.py +++ b/src/openedx_content/applets/contents/models.py @@ -45,6 +45,7 @@ def get_storage() -> Storage: If there is no value for the OPENEDX_LEARNING setting, we return the default MEDIA storage class. TODO: Should we make it just error instead? """ + # TODO: Document & rename this setting (https://github.com/openedx/openedx-learning/issues/481) config_dict = getattr(settings, 'OPENEDX_LEARNING', {}) if 'MEDIA' in config_dict: diff --git a/src/openedx_content/backcompat/backup_restore/apps.py b/src/openedx_content/backcompat/backup_restore/apps.py index 73ae192ae..90058b3f0 100644 --- a/src/openedx_content/backcompat/backup_restore/apps.py +++ b/src/openedx_content/backcompat/backup_restore/apps.py @@ -7,6 +7,6 @@ class BackupRestoreConfig(AppConfig): name = 'openedx_content.backcompat.backup_restore' - verbose_name = "Learning Core > Authoring > Backup Restore" + verbose_name = "Open edX Core > Authoring > Backup Restore" default_auto_field = 'django.db.models.BigAutoField' label = "oel_backup_restore" diff --git a/src/openedx_content/backcompat/collections/apps.py b/src/openedx_content/backcompat/collections/apps.py index a34325c51..43b878056 100644 --- a/src/openedx_content/backcompat/collections/apps.py +++ b/src/openedx_content/backcompat/collections/apps.py @@ -10,6 +10,5 @@ class CollectionsConfig(AppConfig): """ name = "openedx_content.backcompat.collections" - verbose_name = "Learning Core > Authoring > Collections" default_auto_field = "django.db.models.BigAutoField" label = "oel_collections" diff --git a/src/openedx_content/backcompat/components/apps.py b/src/openedx_content/backcompat/components/apps.py index fbc7cc424..a6f2d3e36 100644 --- a/src/openedx_content/backcompat/components/apps.py +++ b/src/openedx_content/backcompat/components/apps.py @@ -10,6 +10,5 @@ class ComponentsConfig(AppConfig): """ name = "openedx_content.backcompat.components" - verbose_name = "Learning Core > Authoring > Components" default_auto_field = "django.db.models.BigAutoField" label = "oel_components" diff --git a/src/openedx_content/backcompat/contents/apps.py b/src/openedx_content/backcompat/contents/apps.py index aed1cd5c5..214528c35 100644 --- a/src/openedx_content/backcompat/contents/apps.py +++ b/src/openedx_content/backcompat/contents/apps.py @@ -10,6 +10,5 @@ class ContentsConfig(AppConfig): """ name = "openedx_content.backcompat.contents" - verbose_name = "Learning Core > Authoring > Contents" default_auto_field = "django.db.models.BigAutoField" label = "oel_contents" diff --git a/src/openedx_content/backcompat/publishing/apps.py b/src/openedx_content/backcompat/publishing/apps.py index 19de6f241..5a22258e4 100644 --- a/src/openedx_content/backcompat/publishing/apps.py +++ b/src/openedx_content/backcompat/publishing/apps.py @@ -10,6 +10,5 @@ class PublishingConfig(AppConfig): """ name = "openedx_content.backcompat.publishing" - verbose_name = "Learning Core > Authoring > Publishing" default_auto_field = "django.db.models.BigAutoField" label = "oel_publishing" diff --git a/src/openedx_content/backcompat/sections/apps.py b/src/openedx_content/backcompat/sections/apps.py index 897da4606..704c0e16c 100644 --- a/src/openedx_content/backcompat/sections/apps.py +++ b/src/openedx_content/backcompat/sections/apps.py @@ -11,6 +11,5 @@ class SectionsConfig(AppConfig): """ name = "openedx_content.backcompat.sections" - verbose_name = "Learning Core > Authoring > Sections" default_auto_field = "django.db.models.BigAutoField" label = "oel_sections" diff --git a/src/openedx_content/backcompat/subsections/apps.py b/src/openedx_content/backcompat/subsections/apps.py index 2341011a1..4eee6e98f 100644 --- a/src/openedx_content/backcompat/subsections/apps.py +++ b/src/openedx_content/backcompat/subsections/apps.py @@ -11,6 +11,5 @@ class SubsectionsConfig(AppConfig): """ name = "openedx_content.backcompat.subsections" - verbose_name = "Learning Core > Authoring > Subsections" default_auto_field = "django.db.models.BigAutoField" label = "oel_subsections" diff --git a/src/openedx_content/backcompat/units/apps.py b/src/openedx_content/backcompat/units/apps.py index 78d9a741d..f8ab064b3 100644 --- a/src/openedx_content/backcompat/units/apps.py +++ b/src/openedx_content/backcompat/units/apps.py @@ -11,6 +11,5 @@ class UnitsConfig(AppConfig): """ name = "openedx_content.backcompat.units" - verbose_name = "Learning Core > Authoring > Units" default_auto_field = "django.db.models.BigAutoField" label = "oel_units" diff --git a/src/openedx_content/management/commands/add_assets_to_component.py b/src/openedx_content/management/commands/add_assets_to_component.py index d4306210c..aafbb7540 100644 --- a/src/openedx_content/management/commands/add_assets_to_component.py +++ b/src/openedx_content/management/commands/add_assets_to_component.py @@ -19,7 +19,7 @@ class Command(BaseCommand): This does not publish the the Component. Note: This is a quick debug tool meant to stuff some asset data into - Learning Core models for testing. It's not intended as a robust and + Open edX Core Content models for testing. It's not intended as a robust and performant tool for modifying actual production content, and should not be used for that purpose. """ diff --git a/src/openedx_core/__init__.py b/src/openedx_core/__init__.py index 9dda0f5cc..e214e8e9b 100644 --- a/src/openedx_core/__init__.py +++ b/src/openedx_core/__init__.py @@ -6,4 +6,4 @@ """ # The version for the entire repository -__version__ = "0.32.0" +__version__ = "0.33.0" diff --git a/src/openedx_django_lib/README.rst b/src/openedx_django_lib/README.rst new file mode 100644 index 000000000..6af00ad1f --- /dev/null +++ b/src/openedx_django_lib/README.rst @@ -0,0 +1,9 @@ +Generic Django Libraries +======================== + +The ``openedx_django_lib`` folder gives us a place to define Django helper classes and functions +which are useful in ``openedx-core`` and may also be useful for external packages. + +We may eventually merge these into the ``edx-django-utils`` repository, or merge that repository into +this package. For now, though, while we work on Open edX Core v1.0, it is useful to have ``openedx_django_lib`` +co-located with the Core apps that we are developing. \ No newline at end of file diff --git a/src/openedx_django_lib/admin_utils.py b/src/openedx_django_lib/admin_utils.py index ae7602d61..e0378741f 100644 --- a/src/openedx_django_lib/admin_utils.py +++ b/src/openedx_django_lib/admin_utils.py @@ -19,7 +19,7 @@ class ReadOnlyModelAdmin(admin.ModelAdmin): the Django Admin is potentially dangerous. In general, if you're providing Django Admin interfaces for your - openedx-learning related app data models, you should subclass this class + openedx-core related app data models, you should subclass this class instead of subclassing admin.ModelAdmin directly. """ @@ -48,7 +48,7 @@ def one_to_one_related_model_html(model_obj: models.Model) -> SafeText: So instead of creating a circular dependency by having ``publishing`` referencing ``components``, we use Django model introspection to iterate over all models that have a OneToOneField to the passe din``model_obj``. - This allows us to preserve our dependency boundaries within openedx-learning + This allows us to preserve our dependency boundaries within openedx-core and accomodate any third party apps that might further extend these models. This will output a list with one entry for each related field. diff --git a/src/openedx_tagging/readme.rst b/src/openedx_tagging/readme.rst index 5ce22340d..ff4ffac98 100644 --- a/src/openedx_tagging/readme.rst +++ b/src/openedx_tagging/readme.rst @@ -1,7 +1,7 @@ Tagging App -============== +=========== -The ``tagging`` app will enable content authors to tag pieces of content and quickly +The ``openedx_tagging`` app will enable content authors to tag pieces of content and quickly filter for ease of re-use. Motivation @@ -12,15 +12,4 @@ of flexible content structures different from the current implementation and allow adaptive learning in the Open edX platform. This service has been implemented as pluggable django app. Since it is necessary for -it to work independently of the content to which it links to. - -Setup ---------- - -**TODO:** We need to wait the discussion of the `Taxonomy discovery `_. -to build a proper setup for linking different pieces of content. - -The current approach is to save the id of the content through a generic string, -so that the tag can be linked to any type of content, no matter what type of ID have. -For example, with this approach we can link standalone blocks and library blocks, -both on Denver (v3) and Blockstore Content Libraries (v2). +it to work independently of the content to which it links to. \ No newline at end of file diff --git a/test_settings.py b/test_settings.py index b1df13adf..5ce3cf7b5 100644 --- a/test_settings.py +++ b/test_settings.py @@ -83,6 +83,7 @@ def root(*args): # ========================= LEARNING CORE SETTINGS ======================== +# TODO: Document & rename this setting (https://github.com/openedx/openedx-learning/issues/481) OPENEDX_LEARNING = { 'MEDIA': { 'BACKEND': 'django.core.files.storage.InMemoryStorage', diff --git a/tests/__init__.py b/tests/__init__.py index afb65fef5..c661ddd38 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ """ -Tests for Learning Core. +Tests for Open edX Core. """ diff --git a/tests/openedx_content/applets/components/test_assets.py b/tests/openedx_content/applets/components/test_assets.py index 45cfa2ad0..8c24d35c5 100644 --- a/tests/openedx_content/applets/components/test_assets.py +++ b/tests/openedx_content/applets/components/test_assets.py @@ -114,7 +114,7 @@ def test_no_component_version(self): ) assert response.status_code == 404 - # None of the Learning Core headers should be set... + # None of the Open edX Core headers should be set... for header_name in response.headers: assert not header_name.startswith("X-Open-edX") diff --git a/tests/openedx_content/applets/sections/test_api.py b/tests/openedx_content/applets/sections/test_api.py index 435d6e8ec..f31771131 100644 --- a/tests/openedx_content/applets/sections/test_api.py +++ b/tests/openedx_content/applets/sections/test_api.py @@ -5,17 +5,17 @@ import pytest from django.core.exceptions import ValidationError -import openedx_content.api as authoring_api +import openedx_content.api as content_api from openedx_content import models_api as authoring_models from ..subsections.test_api import SubSectionTestCase -Entry = authoring_api.SectionListEntry +Entry = content_api.SectionListEntry # TODO: Turn SubSectionTestCase into SubSectionTestMixin and remove the # test-inherits-tests pylint warning below. -# https://github.com/openedx/openedx-learning/issues/308 +# https://github.com/openedx/openedx-core/issues/308 @ddt.ddt class SectionTestCase(SubSectionTestCase): # pylint: disable=test-inherits-tests """ Test cases for Sections (containers of subsections) """ @@ -35,7 +35,7 @@ def create_subsection(self, *, title: str = "Test Subsection", key: str = "subse authoring_models.Subsection, authoring_models.SubsectionVersion ]: """ Helper method to quickly create a subsection """ - return authoring_api.create_subsection_and_version( + return content_api.create_subsection_and_version( self.learning_package.id, key=key, title=title, @@ -51,7 +51,7 @@ def create_section_with_subsections( key="subsection:key", ) -> authoring_models.Section: """ Helper method to quickly create a section with some subsections """ - section, _section_v1 = authoring_api.create_section_and_version( + section, _section_v1 = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key=key, title=title, @@ -71,7 +71,7 @@ def modify_subsection( """ Helper method to modify a subsection for the purposes of testing subsections/drafts/pinning/publishing/etc. """ - return authoring_api.create_next_subsection_version( + return content_api.create_next_subsection_version( subsection, title=title, created=timestamp or self.now, @@ -82,9 +82,9 @@ def publish_subsection(self, subsection: authoring_models.Subsection): """ Helper method to publish a single subsection. """ - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( entity=subsection.publishable_entity, ), ) @@ -95,7 +95,7 @@ def test_get_section(self): """ section = self.create_section_with_subsections([self.subsection_1, self.subsection_2]) with self.assertNumQueries(1): - result = authoring_api.get_section(section.pk) + result = content_api.get_section(section.pk) assert result == section # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -108,7 +108,7 @@ def test_get_section_version(self): section = self.create_section_with_subsections([]) draft = section.versioning.draft with self.assertNumQueries(1): - result = authoring_api.get_section_version(draft.pk) + result = content_api.get_section_version(draft.pk) assert result == draft def test_get_latest_section_version(self): @@ -118,7 +118,7 @@ def test_get_latest_section_version(self): section = self.create_section_with_subsections([]) draft = section.versioning.draft with self.assertNumQueries(2): - result = authoring_api.get_latest_section_version(section.pk) + result = content_api.get_latest_section_version(section.pk) assert result == draft def test_get_containers(self): @@ -127,7 +127,7 @@ def test_get_containers(self): """ section = self.create_section_with_subsections([]) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) self.assertCountEqual(result, [ self.unit_1.container, self.unit_2.container, @@ -144,10 +144,10 @@ def test_get_containers_deleted(self): Test that get_containers() does not return soft-deleted sections. """ section = self.create_section_with_subsections([]) - authoring_api.soft_delete_draft(section.pk) + content_api.soft_delete_draft(section.pk) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id, include_deleted=True)) + result = list(content_api.get_containers(self.learning_package.id, include_deleted=True)) assert result == [ self.unit_1.container, @@ -158,7 +158,7 @@ def test_get_containers_deleted(self): ] with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) assert result == [ self.unit_1.container, @@ -173,7 +173,7 @@ def test_get_container(self): """ section = self.create_section_with_subsections([self.subsection_1, self.subsection_2]) with self.assertNumQueries(1): - result = authoring_api.get_container(section.pk) + result = content_api.get_container(section.pk) assert result == section.container # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -185,7 +185,7 @@ def test_get_container_by_key(self): """ section = self.create_section_with_subsections([]) with self.assertNumQueries(1): - result = authoring_api.get_container_by_key( + result = content_api.get_container_by_key( self.learning_package.id, key=section.publishable_entity.key, ) @@ -226,7 +226,7 @@ def test_create_section_with_invalid_children(self): exception is raised. """ # Create two sections: - section, section_version = authoring_api.create_section_and_version( + section, section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", @@ -234,7 +234,7 @@ def test_create_section_with_invalid_children(self): created_by=None, ) assert section.versioning.draft == section_version - section2, _s2v1 = authoring_api.create_section_and_version( + section2, _s2v1 = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key2", title="Section 2", @@ -243,7 +243,7 @@ def test_create_section_with_invalid_children(self): ) # Try adding a Section to a Section with pytest.raises(TypeError, match="Section subsections must be either Subsection or SubsectionVersion."): - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Section Containing a Section", subsections=[section2], @@ -252,7 +252,7 @@ def test_create_section_with_invalid_children(self): ) # Check that a new version was not created: section.refresh_from_db() - assert authoring_api.get_section(section.pk).versioning.draft == section_version + assert content_api.get_section(section.pk).versioning.draft == section_version assert section.versioning.draft == section_version def test_adding_external_subsections(self): @@ -260,8 +260,8 @@ def test_adding_external_subsections(self): Test that subsections from another learning package cannot be added to a section. """ - learning_package2 = authoring_api.create_learning_package(key="other-package", title="Other Package") - section, _section_version = authoring_api.create_section_and_version( + learning_package2 = content_api.create_learning_package(key="other-package", title="Other Package") + section, _section_version = content_api.create_section_and_version( learning_package_id=learning_package2.pk, key="section:key", title="Section", @@ -271,7 +271,7 @@ def test_adding_external_subsections(self): assert self.subsection_1.container.publishable_entity.learning_package != learning_package2 # Try adding a a subsection from LP 1 (self.learning_package) to a section from LP 2 with pytest.raises(ValidationError, match="Container entities must be from the same learning package."): - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Section Containing an External Subsection", subsections=[self.subsection_1], @@ -288,7 +288,7 @@ def test_create_empty_section_and_version(self): 3. The section is a draft with unpublished changes. 4. There is no published version of the section. """ - section, section_version = authoring_api.create_section_and_version( + section, section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", @@ -312,14 +312,14 @@ def test_create_next_section_version_with_two_unpinned_subsections(self): 3. The section version is in the section's versions. 4. The subsections are in the draft section version's subsection list and are unpinned. """ - section, _section_version = authoring_api.create_section_and_version( + section, _section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", created=self.now, created_by=None, ) - section_version_v2 = authoring_api.create_next_section_version( + section_version_v2 = content_api.create_next_section_version( section=section, title="Section", subsections=[self.subsection_1, self.subsection_2], @@ -328,26 +328,26 @@ def test_create_next_section_version_with_two_unpinned_subsections(self): ) assert section_version_v2.version_num == 2 assert section_version_v2 in section.versioning.versions.all() - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1.versioning.draft), Entry(self.subsection_2.versioning.draft), ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the section: - authoring_api.get_subsections_in_section(section, published=True) + content_api.get_subsections_in_section(section, published=True) def test_create_next_section_version_with_unpinned_and_pinned_subsections(self): """ Test creating a section version with one unpinned and one pinned 📌 subsection. """ - section, _section_version = authoring_api.create_section_and_version( + section, _section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", created=self.now, created_by=None, ) - section_version_v2 = authoring_api.create_next_section_version( + section_version_v2 = content_api.create_next_section_version( section=section, title="Section", subsections=[ @@ -359,26 +359,26 @@ def test_create_next_section_version_with_unpinned_and_pinned_subsections(self): ) assert section_version_v2.version_num == 2 assert section_version_v2 in section.versioning.versions.all() - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1_v1), Entry(self.subsection_2_v1, pinned=True), # Pinned 📌 to v1 ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the section: - authoring_api.get_subsections_in_section(section, published=True) + content_api.get_subsections_in_section(section, published=True) def test_create_next_section_version_forcing_version_num(self): """ Test creating a section version while forcing the next version number. """ - section, _section_version = authoring_api.create_section_and_version( + section, _section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", created=self.now, created_by=None, ) - section_version_v2 = authoring_api.create_next_section_version( + section_version_v2 = content_api.create_next_section_version( section=section, title="Section", subsections=[self.subsection_1, self.subsection_2], @@ -399,21 +399,21 @@ def test_auto_publish_children(self): title="A draft subsection not in the section", key="subsection:3" ) - assert authoring_api.contains_unpublished_changes(section.pk) + assert content_api.contains_unpublished_changes(section.pk) assert self.subsection_1.versioning.published is None assert self.subsection_2.versioning.published is None # Publish ONLY the section. This should however also auto-publish subsections 1 & 2 since they're children - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter(entity=section.publishable_entity), + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter(entity=section.publishable_entity), ) # Now all changes to the section and to subsection 1 are published: section.refresh_from_db() self.subsection_1.refresh_from_db() assert section.versioning.has_unpublished_changes is False # Shallow check assert self.subsection_1.versioning.has_unpublished_changes is False - assert authoring_api.contains_unpublished_changes(section.pk) is False # Deep check + assert content_api.contains_unpublished_changes(section.pk) is False # Deep check assert self.subsection_1.versioning.published == self.subsection_1_v1 # v1 is now the published version. # But our other subsection that's outside the section is not affected: @@ -439,14 +439,14 @@ def test_no_publish_parent(self): assert section.versioning.published is None with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the section: - authoring_api.get_subsections_in_section(section, published=True) + content_api.get_subsections_in_section(section, published=True) def test_add_subsection_after_publish(self): """ Adding a subsection to a published section will create a new version and show that the section has unpublished changes. """ - section, section_version = authoring_api.create_section_and_version( + section, section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", @@ -457,25 +457,25 @@ def test_add_subsection_after_publish(self): assert section.versioning.published is None assert section.versioning.has_unpublished_changes # Publish the empty section: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) section.refresh_from_db() # Reloading the section is necessary assert section.versioning.has_unpublished_changes is False # Shallow check for the section itself, not children - assert authoring_api.contains_unpublished_changes(section.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(section.pk) is False # Deeper check # Add a published subsection (unpinned): assert self.subsection_1.versioning.has_unpublished_changes is False - section_version_v2 = authoring_api.create_next_section_version( + section_version_v2 = content_api.create_next_section_version( section=section, title=section_version.title, subsections=[self.subsection_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) # Now the section should have unpublished changes: section.refresh_from_db() # Reloading the section is necessary assert section.versioning.has_unpublished_changes # Shallow check - adding a child is a change to the section - assert authoring_api.contains_unpublished_changes(section.pk) # Deeper check + assert content_api.contains_unpublished_changes(section.pk) # Deeper check assert section.versioning.draft == section_version_v2 assert section.versioning.published == section_version @@ -492,11 +492,11 @@ def test_modify_unpinned_subsection_after_publish(self): assert section.versioning.has_unpublished_changes # Publish the section and the subsection: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) section.refresh_from_db() # Reloading the section is necessary if we accessed 'versioning' before publish self.subsection_1.refresh_from_db() assert section.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(section.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(section.pk) is False # Deeper check assert self.subsection_1.versioning.has_unpublished_changes is False # Now modify the subsection by changing its title (it remains a draft): @@ -506,26 +506,26 @@ def test_modify_unpinned_subsection_after_publish(self): section.refresh_from_db() # Reloading the section is necessary, or 'section.versioning' will be outdated self.subsection_1.refresh_from_db() assert section.versioning.has_unpublished_changes is False # Shallow check should be false - section unchanged - assert authoring_api.contains_unpublished_changes(section.pk) # But section DOES contain changes + assert content_api.contains_unpublished_changes(section.pk) # But section DOES contain changes assert self.subsection_1.versioning.has_unpublished_changes # Since the subsection changes haven't been published, they should only appear in the draft section - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(subsection_1_v2), # new version ] - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), # old version ] # But if we publish the subsection, the changes will appear in the published version of the section. self.publish_subsection(self.subsection_1) - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(subsection_1_v2), # new version ] - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(subsection_1_v2), # new version ] - assert authoring_api.contains_unpublished_changes(section.pk) is False # No longer contains unpublished changes + assert content_api.contains_unpublished_changes(section.pk) is False # No longer contains unpublished changes def test_modify_pinned_subsection(self): """ @@ -537,11 +537,11 @@ def test_modify_pinned_subsection(self): section = self.create_section_with_subsections([self.subsection_1_v1]) # Publish the section and the subsection: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) expected_section_contents = [ Entry(self.subsection_1_v1, pinned=True), # pinned 📌 to v1 ] - assert authoring_api.get_subsections_in_section(section, published=True) == expected_section_contents + assert content_api.get_subsections_in_section(section, published=True) == expected_section_contents # Now modify the subsection by changing its title (it remains a draft): self.modify_subsection(self.subsection_1, title="Modified Counting Problem with new title") @@ -550,16 +550,16 @@ def test_modify_pinned_subsection(self): section.refresh_from_db() # Reloading the section is necessary, or 'section.versioning' will be outdated self.subsection_1.refresh_from_db() assert section.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(section.pk) is False # Deep check + assert content_api.contains_unpublished_changes(section.pk) is False # Deep check assert self.subsection_1.versioning.has_unpublished_changes is True # Neither the draft nor the published version of the section is affected - assert authoring_api.get_subsections_in_section(section, published=False) == expected_section_contents - assert authoring_api.get_subsections_in_section(section, published=True) == expected_section_contents + assert content_api.get_subsections_in_section(section, published=False) == expected_section_contents + assert content_api.get_subsections_in_section(section, published=True) == expected_section_contents # Even if we publish the subsection, the section stays pinned to the specified version: self.publish_subsection(self.subsection_1) - assert authoring_api.get_subsections_in_section(section, published=False) == expected_section_contents - assert authoring_api.get_subsections_in_section(section, published=True) == expected_section_contents + assert content_api.get_subsections_in_section(section, published=False) == expected_section_contents + assert content_api.get_subsections_in_section(section, published=True) == expected_section_contents def test_create_two_sections_with_same_subsections(self): """ @@ -577,38 +577,38 @@ def test_create_two_sections_with_same_subsections(self): # Check that the contents are as expected: assert [ - row.subsection_version for row in authoring_api.get_subsections_in_section(section1, published=False) + row.subsection_version for row in content_api.get_subsections_in_section(section1, published=False) ] == [self.subsection_2_v1, self.subsection_2_v1, self.subsection_1_v1,] assert [ - row.subsection_version for row in authoring_api.get_subsections_in_section(section2, published=False) + row.subsection_version for row in content_api.get_subsections_in_section(section2, published=False) ] == [self.subsection_1_v1, self.subsection_2_v1, self.subsection_1_v1,] # Modify subsection 1 subsection_1_v2 = self.modify_subsection(self.subsection_1, title="subsection 1 v2") # Publish changes - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Modify subsection 2 - only in the draft subsection_2_v2 = self.modify_subsection(self.subsection_2, title="subsection 2 DRAFT") # Check that the draft contents are as expected: - assert authoring_api.get_subsections_in_section(section1, published=False) == [ + assert content_api.get_subsections_in_section(section1, published=False) == [ Entry(subsection_2_v2), # v2 in the draft version Entry(self.subsection_2_v1, pinned=True), # pinned 📌 to v1 Entry(subsection_1_v2), # v2 ] - assert authoring_api.get_subsections_in_section(section2, published=False) == [ + assert content_api.get_subsections_in_section(section2, published=False) == [ Entry(self.subsection_1_v1, pinned=True), # pinned 📌 to v1 Entry(subsection_2_v2), # v2 in the draft version Entry(subsection_1_v2), # v2 ] # Check that the published contents are as expected: - assert authoring_api.get_subsections_in_section(section1, published=True) == [ + assert content_api.get_subsections_in_section(section1, published=True) == [ Entry(self.subsection_2_v1), # v1 in the published version Entry(self.subsection_2_v1, pinned=True), # pinned 📌 to v1 Entry(subsection_1_v2), # v2 ] - assert authoring_api.get_subsections_in_section(section2, published=True) == [ + assert content_api.get_subsections_in_section(section2, published=True) == [ Entry(self.subsection_1_v1, pinned=True), # pinned 📌 to v1 Entry(self.subsection_2_v1), # v1 in the published version Entry(subsection_1_v2), # v2 @@ -629,15 +629,15 @@ def test_publishing_shared_subsection(self): ] section1 = self.create_section_with_subsections([s1, s2, s3], title="Section 1", key="section:1") section2 = self.create_section_with_subsections([s2, s4, s5], title="Section 2", key="section:2") - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(section1.pk) is False - assert authoring_api.contains_unpublished_changes(section2.pk) is False + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(section1.pk) is False + assert content_api.contains_unpublished_changes(section2.pk) is False # 2️⃣ Then the author edits S2 inside of Section 1 making S2v2. s2_v2 = self.modify_subsection(s2, title="U2 version 2") # This makes S1, S2 both show up as Sections that CONTAIN unpublished changes, because they share the subsection - assert authoring_api.contains_unpublished_changes(section1.pk) - assert authoring_api.contains_unpublished_changes(section2.pk) + assert content_api.contains_unpublished_changes(section1.pk) + assert content_api.contains_unpublished_changes(section2.pk) # (But the sections themselves are unchanged:) section1.refresh_from_db() section2.refresh_from_db() @@ -648,16 +648,16 @@ def test_publishing_shared_subsection(self): s5_v2 = self.modify_subsection(s5, title="S5 version 2") # 4️⃣ The author then publishes Section 1, and therefore everything in it. - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( # Note: we only publish the section; the publishing API should auto-publish its subsections too. entity_id=section1.publishable_entity.id, ), ) # Result: Section 1 will show the newly published version of U2: - assert authoring_api.get_subsections_in_section(section1, published=True) == [ + assert content_api.get_subsections_in_section(section1, published=True) == [ Entry(s1_v1), Entry(s2_v2), # new published version of U2 Entry(s3_v1), @@ -667,7 +667,7 @@ def test_publishing_shared_subsection(self): # because publishing it anywhere publishes it everywhere. # But publishing U2 and Section 1 does not affect the other subsections in Section 2. # (Publish propagates downward, not upward) - assert authoring_api.get_subsections_in_section(section2, published=True) == [ + assert content_api.get_subsections_in_section(section2, published=True) == [ Entry(s2_v2), # new published version of U2 Entry(s4_v1), # still original version of U4 (it was never modified) Entry(s5_v1), # still original version of U5 (it hasn't been published) @@ -675,18 +675,18 @@ def test_publishing_shared_subsection(self): # Result: Section 2 CONTAINS unpublished changes because of the modified U5. # Section 1 doesn't contain unpub changes. - assert authoring_api.contains_unpublished_changes(section1.pk) is False - assert authoring_api.contains_unpublished_changes(section2.pk) + assert content_api.contains_unpublished_changes(section1.pk) is False + assert content_api.contains_unpublished_changes(section2.pk) # 5️⃣ Publish subsection U5, which should be the only thing unpublished in the learning package self.publish_subsection(s5) # Result: Section 2 shows the new version of C5 and no longer contains unpublished changes: - assert authoring_api.get_subsections_in_section(section2, published=True) == [ + assert content_api.get_subsections_in_section(section2, published=True) == [ Entry(s2_v2), # new published version of U2 Entry(s4_v1), # still original version of U4 (it was never modified) Entry(s5_v2), # new published version of U5 ] - assert authoring_api.contains_unpublished_changes(section2.pk) is False + assert content_api.contains_unpublished_changes(section2.pk) is False def test_query_count_of_contains_unpublished_changes(self): """ @@ -703,15 +703,15 @@ def test_query_count_of_contains_unpublished_changes(self): ) subsections.append(subsection) section = self.create_section_with_subsections(subsections) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) section.refresh_from_db() with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(section.pk) is False + assert content_api.contains_unpublished_changes(section.pk) is False # Modify the most recently created subsection: self.modify_subsection(subsection, title="Modified Subsection") with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(section.pk) is True + assert content_api.contains_unpublished_changes(section.pk) is True def test_metadata_change_doesnt_create_entity_list(self): """ @@ -724,7 +724,7 @@ def test_metadata_change_doesnt_create_entity_list(self): orig_version_num = section.versioning.draft.version_num orig_entity_list_id = section.versioning.draft.entity_list.pk - authoring_api.create_next_section_version(section, title="New Title", created=self.now) + content_api.create_next_section_version(section, title="New Title", created=self.now) section.refresh_from_db() new_version_num = section.versioning.draft.version_num @@ -736,52 +736,52 @@ def test_metadata_change_doesnt_create_entity_list(self): def test_removing_subsection(self): """ Test removing a subsection from a section (but not deleting it) """ section = self.create_section_with_subsections([self.subsection_1, self.subsection_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now remove subsection 2 - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Revised with subsection 2 deleted", subsections=[self.subsection_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the section: - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1_v1), ] section.refresh_from_db() assert section.versioning.has_unpublished_changes # The section itself and its subsection list have change - assert authoring_api.contains_unpublished_changes(section.pk) + assert content_api.contains_unpublished_changes(section.pk) # The published version of the section is not yet affected: - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), Entry(self.subsection_2_v1), ] # But when we publish the new section version with the removal, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # FIXME: Refreshing the section is necessary here because get_entities_in_published_container() accesses # container.versioning.published, and .versioning is cached with the old version. But this seems like # a footgun? We could avoid this if get_entities_in_published_container() took only an ID instead of an object, # but that would involve additional database lookup(s). section.refresh_from_db() - assert authoring_api.contains_unpublished_changes(section.pk) is False - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.contains_unpublished_changes(section.pk) is False + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), ] def test_soft_deleting_subsection(self): """ Test soft deleting a subsection that's in a section (but not removing it) """ section = self.create_section_with_subsections([self.subsection_1, self.subsection_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete subsection 2 - authoring_api.soft_delete_draft(self.subsection_2.pk) + content_api.soft_delete_draft(self.subsection_2.pk) # Now it should not be listed in the section: - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1_v1), # subsection 2 is soft deleted from the draft. # TODO: should we return some kind of placeholder here, to indicate that a subsection is still listed in the @@ -789,72 +789,72 @@ def test_soft_deleting_subsection(self): # or restored if reverted? ] assert section.versioning.has_unpublished_changes is False # The section and its subsection list is not changed - assert authoring_api.contains_unpublished_changes(section.pk) # But it CONTAINS unpublished change (deletion) + assert content_api.contains_unpublished_changes(section.pk) # But it CONTAINS unpublished change (deletion) # The published version of the section is not yet affected: - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), Entry(self.subsection_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(section.pk) is False - assert authoring_api.get_subsections_in_section(section, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(section.pk) is False + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), ] def test_soft_deleting_and_removing_subsection(self): """ Test soft deleting a subsection that's in a section AND removing it """ section = self.create_section_with_subsections([self.subsection_1, self.subsection_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete subsection 2 - authoring_api.soft_delete_draft(self.subsection_2.pk) + content_api.soft_delete_draft(self.subsection_2.pk) # And remove it from the section: - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Revised with subsection 2 deleted", subsections=[self.subsection_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the section: - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1_v1), ] assert section.versioning.has_unpublished_changes is True - assert authoring_api.contains_unpublished_changes(section.pk) + assert content_api.contains_unpublished_changes(section.pk) # The published version of the section is not yet affected: - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), Entry(self.subsection_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(section.pk) is False - assert authoring_api.get_subsections_in_section(section, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(section.pk) is False + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1), ] def test_soft_deleting_pinned_subsection(self): """ Test soft deleting a pinned 📌 subsection that's in a section """ section = self.create_section_with_subsections([self.subsection_1_v1, self.subsection_2_v1]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete subsection 2 - authoring_api.soft_delete_draft(self.subsection_2.pk) + content_api.soft_delete_draft(self.subsection_2.pk) # Now it should still be listed in the section: - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1_v1, pinned=True), Entry(self.subsection_2_v1, pinned=True), ] assert section.versioning.has_unpublished_changes is False # The section and its subsection list is not changed - assert authoring_api.contains_unpublished_changes(section.pk) is False # nor does it contain changes + assert content_api.contains_unpublished_changes(section.pk) is False # nor does it contain changes # The published version of the section is also not affected: - assert authoring_api.get_subsections_in_section(section, published=True) == [ + assert content_api.get_subsections_in_section(section, published=True) == [ Entry(self.subsection_1_v1, pinned=True), Entry(self.subsection_2_v1, pinned=True), ] @@ -870,19 +870,19 @@ def test_soft_delete_section(self): other_section = self.create_section_with_subsections([self.subsection_1], key="other") # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Delete the section: - authoring_api.soft_delete_draft(section_to_delete.publishable_entity_id) + content_api.soft_delete_draft(section_to_delete.publishable_entity_id) section_to_delete.refresh_from_db() # Now draft section is [soft] deleted, but the subsections, published section, and other section is unaffected: assert section_to_delete.versioning.draft is None # Section is soft deleted. assert section_to_delete.versioning.published is not None self.subsection_1.refresh_from_db() assert self.subsection_1.versioning.draft is not None - assert authoring_api.get_subsections_in_section(other_section, published=False) == [Entry(self.subsection_1_v1)] + assert content_api.get_subsections_in_section(other_section, published=False) == [Entry(self.subsection_1_v1)] # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now the section's published version is also deleted, but nothing else is affected. section_to_delete.refresh_from_db() assert section_to_delete.versioning.draft is None # Section is soft deleted. @@ -890,8 +890,8 @@ def test_soft_delete_section(self): self.subsection_1.refresh_from_db() assert self.subsection_1.versioning.draft is not None assert self.subsection_1.versioning.published is not None - assert authoring_api.get_subsections_in_section(other_section, published=False) == [Entry(self.subsection_1_v1)] - assert authoring_api.get_subsections_in_section(other_section, published=True) == [Entry(self.subsection_1_v1)] + assert content_api.get_subsections_in_section(other_section, published=False) == [Entry(self.subsection_1_v1)] + assert content_api.get_subsections_in_section(other_section, published=True) == [Entry(self.subsection_1_v1)] def test_snapshots_of_published_section(self): """ @@ -901,43 +901,43 @@ def test_snapshots_of_published_section(self): # At first the section has one subsection (unpinned): section = self.create_section_with_subsections([self.subsection_1]) self.modify_subsection(self.subsection_1, title="Subsection 1 as of checkpoint 1") - before_publish = authoring_api.get_subsections_in_published_section_as_of(section, 0) + before_publish = content_api.get_subsections_in_published_section_as_of(section, 0) assert before_publish is None # Publish everything, creating Checkpoint 1 - checkpoint_1 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") + checkpoint_1 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") ######################################################################## # Now we update the title of the subsection. self.modify_subsection(self.subsection_1, title="Subsection 1 as of checkpoint 2") # Publish everything, creating Checkpoint 2 - checkpoint_2 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") + checkpoint_2 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") ######################################################################## # Now add a second subsection to the section: self.modify_subsection(self.subsection_1, title="Subsection 1 as of checkpoint 3") self.modify_subsection(self.subsection_2, title="Subsection 2 as of checkpoint 3") - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Section title in checkpoint 3", subsections=[self.subsection_1, self.subsection_2], created=self.now, ) # Publish everything, creating Checkpoint 3 - checkpoint_3 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") + checkpoint_3 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") ######################################################################## # Now add a third subsection to the section, a pinned 📌 version of subsection 1. # This will test pinned versions and also test adding at the beginning rather than the end of the section. - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title="Section title in checkpoint 4", subsections=[self.subsection_1_v1, self.subsection_1, self.subsection_2], created=self.now, ) # Publish everything, creating Checkpoint 4 - checkpoint_4 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") + checkpoint_4 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") ######################################################################## # Modify the drafts, but don't publish: @@ -945,20 +945,20 @@ def test_snapshots_of_published_section(self): self.modify_subsection(self.subsection_2, title="Subsection 2 draft") # Now fetch the snapshots: - as_of_checkpoint_1 = authoring_api.get_subsections_in_published_section_as_of(section, checkpoint_1.pk) + as_of_checkpoint_1 = content_api.get_subsections_in_published_section_as_of(section, checkpoint_1.pk) assert [cv.subsection_version.title for cv in as_of_checkpoint_1] == [ "Subsection 1 as of checkpoint 1", ] - as_of_checkpoint_2 = authoring_api.get_subsections_in_published_section_as_of(section, checkpoint_2.pk) + as_of_checkpoint_2 = content_api.get_subsections_in_published_section_as_of(section, checkpoint_2.pk) assert [cv.subsection_version.title for cv in as_of_checkpoint_2] == [ "Subsection 1 as of checkpoint 2", ] - as_of_checkpoint_3 = authoring_api.get_subsections_in_published_section_as_of(section, checkpoint_3.pk) + as_of_checkpoint_3 = content_api.get_subsections_in_published_section_as_of(section, checkpoint_3.pk) assert [cv.subsection_version.title for cv in as_of_checkpoint_3] == [ "Subsection 1 as of checkpoint 3", "Subsection 2 as of checkpoint 3", ] - as_of_checkpoint_4 = authoring_api.get_subsections_in_published_section_as_of(section, checkpoint_4.pk) + as_of_checkpoint_4 = content_api.get_subsections_in_published_section_as_of(section, checkpoint_4.pk) assert [cv.subsection_version.title for cv in as_of_checkpoint_4] == [ "Subsection (1)", # Pinned. This title is self.subsection_1_v1.title (original v1 title) "Subsection 1 as of checkpoint 3", # we didn't modify these subsections so they're same as in snapshot 3 @@ -994,7 +994,7 @@ def test_sections_containing(self): with self.assertNumQueries(1): result = [ c.section for c in - authoring_api.get_containers_with_entity(self.subsection_1.pk).select_related("section") + content_api.get_containers_with_entity(self.subsection_1.pk).select_related("section") ] assert result == [ section1_1pinned, @@ -1008,7 +1008,7 @@ def test_sections_containing(self): with self.assertNumQueries(1): result2 = [ c.section for c in - authoring_api.get_containers_with_entity( + content_api.get_containers_with_entity( self.subsection_1.pk, ignore_pinned=True ).select_related("section") ] @@ -1025,15 +1025,15 @@ def test_get_subsections_in_section_queries(self): self.subsection_2_v1, ]) with self.assertNumQueries(4): - result = authoring_api.get_subsections_in_section(section, published=False) + result = content_api.get_subsections_in_section(section, published=False) assert result == [ Entry(self.subsection_1.versioning.draft), Entry(self.subsection_2.versioning.draft), Entry(self.subsection_2.versioning.draft, pinned=True), ] - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) with self.assertNumQueries(4): - result = authoring_api.get_subsections_in_section(section, published=True) + result = content_api.get_subsections_in_section(section, published=True) assert result == [ Entry(self.subsection_1.versioning.draft), Entry(self.subsection_2.versioning.draft), @@ -1044,7 +1044,7 @@ def test_add_remove_container_children(self): """ Test adding and removing children subsections from sections. """ - section, section_version = authoring_api.create_section_and_version( + section, section_version = content_api.create_section_and_version( learning_package_id=self.learning_package.id, key="section:key", title="Section", @@ -1052,7 +1052,7 @@ def test_add_remove_container_children(self): created=self.now, created_by=None, ) - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1.versioning.draft), ] subsection_3, _ = self.create_subsection( @@ -1060,36 +1060,36 @@ def test_add_remove_container_children(self): title="Subsection (3)", ) # Add subsection_2 and subsection_3 - section_version_v2 = authoring_api.create_next_section_version( + section_version_v2 = content_api.create_next_section_version( section=section, title=section_version.title, subsections=[self.subsection_2, subsection_3], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) section.refresh_from_db() assert section_version_v2.version_num == 2 assert section_version_v2 in section.versioning.versions.all() # Verify that subsection_2 and subsection_3 is added to end - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_1.versioning.draft), Entry(self.subsection_2.versioning.draft), Entry(subsection_3.versioning.draft), ] # Remove subsection_1 - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title=section_version.title, subsections=[self.subsection_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) section.refresh_from_db() # Verify that subsection_1 is removed - assert authoring_api.get_subsections_in_section(section, published=False) == [ + assert content_api.get_subsections_in_section(section, published=False) == [ Entry(self.subsection_2.versioning.draft), Entry(subsection_3.versioning.draft), ] @@ -1099,34 +1099,34 @@ def test_get_container_children_count(self): Test get_container_children_count() """ section = self.create_section_with_subsections([self.subsection_1]) - assert authoring_api.get_container_children_count(section.container, published=False) == 1 + assert content_api.get_container_children_count(section.container, published=False) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) section_version = section.versioning.draft - authoring_api.create_next_section_version( + content_api.create_next_section_version( section=section, title=section_version.title, subsections=[self.subsection_2], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) section.refresh_from_db() # Should have two subsections in draft version and 1 in published version - assert authoring_api.get_container_children_count(section.container, published=False) == 2 - assert authoring_api.get_container_children_count(section.container, published=True) == 1 + assert content_api.get_container_children_count(section.container, published=False) == 2 + assert content_api.get_container_children_count(section.container, published=True) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) section.refresh_from_db() - assert authoring_api.get_container_children_count(section.container, published=True) == 2 + assert content_api.get_container_children_count(section.container, published=True) == 2 # Soft delete subsection_1 - authoring_api.soft_delete_draft(self.subsection_1.pk) + content_api.soft_delete_draft(self.subsection_1.pk) section.refresh_from_db() # Should contain only 1 child - assert authoring_api.get_container_children_count(section.container, published=False) == 1 - authoring_api.publish_all_drafts(self.learning_package.id) + assert content_api.get_container_children_count(section.container, published=False) == 1 + content_api.publish_all_drafts(self.learning_package.id) section.refresh_from_db() - assert authoring_api.get_container_children_count(section.container, published=True) == 1 + assert content_api.get_container_children_count(section.container, published=True) == 1 # Tests TODO: # Test that I can get a [PublishLog] history of a given section and all its children, including children that aren't diff --git a/tests/openedx_content/applets/subsections/test_api.py b/tests/openedx_content/applets/subsections/test_api.py index 5ea59c205..8d93e1085 100644 --- a/tests/openedx_content/applets/subsections/test_api.py +++ b/tests/openedx_content/applets/subsections/test_api.py @@ -8,17 +8,17 @@ from django.core.exceptions import ValidationError from django.db import IntegrityError -import openedx_content.api as authoring_api +import openedx_content.api as content_api from openedx_content import models_api as authoring_models from ..units.test_api import UnitTestCase -Entry = authoring_api.SubsectionListEntry +Entry = content_api.SubsectionListEntry # TODO: Turn UnitTestCase into UnitTestMixin and remove the # test-inherits-tests pylint warning below. -# https://github.com/openedx/openedx-learning/issues/308 +# https://github.com/openedx/openedx-core/issues/308 @ddt.ddt class SubSectionTestCase(UnitTestCase): # pylint: disable=test-inherits-tests """ Test cases for Subsections (containers of units) """ @@ -38,7 +38,7 @@ def create_unit(self, *, title: str = "Test Unit", key: str = "unit:1") -> tuple authoring_models.Unit, authoring_models.UnitVersion ]: """ Helper method to quickly create a unit """ - return authoring_api.create_unit_and_version( + return content_api.create_unit_and_version( self.learning_package.id, key=key, title=title, @@ -54,7 +54,7 @@ def create_subsection_with_units( key="unit:key", ) -> authoring_models.Subsection: """ Helper method to quickly create a subsection with some units """ - subsection, _subsection_v1 = authoring_api.create_subsection_and_version( + subsection, _subsection_v1 = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key=key, title=title, @@ -74,7 +74,7 @@ def modify_unit( """ Helper method to modify a unit for the purposes of testing units/drafts/pinning/publishing/etc. """ - return authoring_api.create_next_unit_version( + return content_api.create_next_unit_version( unit, title=title, created=timestamp or self.now, @@ -85,9 +85,9 @@ def publish_unit(self, unit: authoring_models.Unit): """ Helper method to publish a single unit. """ - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( entity=unit.publishable_entity, ), ) @@ -98,7 +98,7 @@ def test_get_subsection(self): """ subsection = self.create_subsection_with_units([self.unit_1, self.unit_2]) with self.assertNumQueries(1): - result = authoring_api.get_subsection(subsection.pk) + result = content_api.get_subsection(subsection.pk) assert result == subsection # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -111,7 +111,7 @@ def test_get_subsection_version(self): subsection = self.create_subsection_with_units([]) draft = subsection.versioning.draft with self.assertNumQueries(1): - result = authoring_api.get_subsection_version(draft.pk) + result = content_api.get_subsection_version(draft.pk) assert result == draft def test_get_latest_subsection_version(self): @@ -121,7 +121,7 @@ def test_get_latest_subsection_version(self): subsection = self.create_subsection_with_units([]) draft = subsection.versioning.draft with self.assertNumQueries(2): - result = authoring_api.get_latest_subsection_version(subsection.pk) + result = content_api.get_latest_subsection_version(subsection.pk) assert result == draft def test_get_containers(self): @@ -130,7 +130,7 @@ def test_get_containers(self): """ subsection = self.create_subsection_with_units([]) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) assert result == [self.unit_1.container, self.unit_2.container, subsection.container] # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -141,13 +141,13 @@ def test_get_containers_deleted(self): Test that get_containers() does not return soft-deleted sections. """ subsection = self.create_subsection_with_units([]) - authoring_api.soft_delete_draft(subsection.pk) + content_api.soft_delete_draft(subsection.pk) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id, include_deleted=True)) + result = list(content_api.get_containers(self.learning_package.id, include_deleted=True)) assert result == [self.unit_1.container, self.unit_2.container, subsection.container] with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) assert result == [self.unit_1.container, self.unit_2.container] def test_get_container(self): @@ -156,7 +156,7 @@ def test_get_container(self): """ subsection = self.create_subsection_with_units([self.unit_1, self.unit_2]) with self.assertNumQueries(1): - result = authoring_api.get_container(subsection.pk) + result = content_api.get_container(subsection.pk) assert result == subsection.container # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -168,7 +168,7 @@ def test_get_container_by_key(self): """ subsection = self.create_subsection_with_units([]) with self.assertNumQueries(1): - result = authoring_api.get_container_by_key( + result = content_api.get_container_by_key( self.learning_package.id, key=subsection.publishable_entity.key, ) @@ -209,7 +209,7 @@ def test_create_subsection_with_invalid_children(self): exception is raised. """ # Create two subsections: - subsection, subsection_version = authoring_api.create_subsection_and_version( + subsection, subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", @@ -217,7 +217,7 @@ def test_create_subsection_with_invalid_children(self): created_by=None, ) assert subsection.versioning.draft == subsection_version - subsection2, _s2v1 = authoring_api.create_subsection_and_version( + subsection2, _s2v1 = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key2", title="Subsection 2", @@ -226,7 +226,7 @@ def test_create_subsection_with_invalid_children(self): ) # Try adding a Subsection to a Subsection with pytest.raises(TypeError, match="Subsection units must be either Unit or UnitVersion."): - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Subsection Containing a Subsection", units=[subsection2], @@ -235,7 +235,7 @@ def test_create_subsection_with_invalid_children(self): ) # Check that a new version was not created: subsection.refresh_from_db() - assert authoring_api.get_subsection(subsection.pk).versioning.draft == subsection_version + assert content_api.get_subsection(subsection.pk).versioning.draft == subsection_version assert subsection.versioning.draft == subsection_version def test_adding_external_units(self): @@ -243,8 +243,8 @@ def test_adding_external_units(self): Test that units from another learning package cannot be added to a subsection. """ - learning_package2 = authoring_api.create_learning_package(key="other-package", title="Other Package") - subsection, _subsection_version = authoring_api.create_subsection_and_version( + learning_package2 = content_api.create_learning_package(key="other-package", title="Other Package") + subsection, _subsection_version = content_api.create_subsection_and_version( learning_package_id=learning_package2.pk, key="subsection:key", title="Subsection", @@ -254,7 +254,7 @@ def test_adding_external_units(self): assert self.unit_1.container.publishable_entity.learning_package != learning_package2 # Try adding a a unit from LP 1 (self.learning_package) to a subsection from LP 2 with pytest.raises(ValidationError, match="Container entities must be from the same learning package."): - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Subsection Containing an External Unit", units=[self.unit_1], @@ -268,14 +268,14 @@ def test_adding_mismatched_versions(self, mock_entities_for_units): # pylint: d Test that versioned units must match their entities. """ mock_entities_for_units.return_value = [ - authoring_api.ContainerEntityRow( + content_api.ContainerEntityRow( entity_pk=self.unit_1.pk, version_pk=self.unit_2_v1.pk, ), ] # Try adding a a unit from LP 1 (self.learning_package) to a subsection from LP 2 with pytest.raises(ValidationError, match="Container entity versions must belong to the specified entity"): - authoring_api.create_subsection_and_version( + content_api.create_subsection_and_version( learning_package_id=self.unit_1.container.publishable_entity.learning_package.pk, key="subsection:key", title="Subsection", @@ -308,7 +308,7 @@ def test_create_empty_subsection_and_version(self): 3. The subsection is a draft with unpublished changes. 4. There is no published version of the subsection. """ - subsection, subsection_version = authoring_api.create_subsection_and_version( + subsection, subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", @@ -332,14 +332,14 @@ def test_create_next_subsection_version_with_two_unpinned_units(self): 3. The subsection version is in the subsection's versions. 4. The units are in the draft subsection version's unit list and are unpinned. """ - subsection, _subsection_version = authoring_api.create_subsection_and_version( + subsection, _subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", created=self.now, created_by=None, ) - subsection_version_v2 = authoring_api.create_next_subsection_version( + subsection_version_v2 = content_api.create_next_subsection_version( subsection=subsection, title="Subsection", units=[self.unit_1, self.unit_2], @@ -348,26 +348,26 @@ def test_create_next_subsection_version_with_two_unpinned_units(self): ) assert subsection_version_v2.version_num == 2 assert subsection_version_v2 in subsection.versioning.versions.all() - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1.versioning.draft), Entry(self.unit_2.versioning.draft), ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the subsection: - authoring_api.get_units_in_subsection(subsection, published=True) + content_api.get_units_in_subsection(subsection, published=True) def test_create_next_subsection_version_forcing_version_num(self): """ Test creating a subsection version while forcing the next version number. """ - subsection, _subsection_version = authoring_api.create_subsection_and_version( + subsection, _subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", created=self.now, created_by=None, ) - subsection_version_v2 = authoring_api.create_next_subsection_version( + subsection_version_v2 = content_api.create_next_subsection_version( subsection=subsection, title="Subsection", units=[self.unit_1, self.unit_2], @@ -381,14 +381,14 @@ def test_create_next_subsection_version_with_unpinned_and_pinned_units(self): """ Test creating a subsection version with one unpinned and one pinned 📌 unit. """ - subsection, _subsection_version = authoring_api.create_subsection_and_version( + subsection, _subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", created=self.now, created_by=None, ) - subsection_version_v2 = authoring_api.create_next_subsection_version( + subsection_version_v2 = content_api.create_next_subsection_version( subsection=subsection, title="Subsection", units=[self.unit_1, self.unit_2_v1], # Note the "v1" pinning 📌 the second one to version 1 @@ -397,13 +397,13 @@ def test_create_next_subsection_version_with_unpinned_and_pinned_units(self): ) assert subsection_version_v2.version_num == 2 assert subsection_version_v2 in subsection.versioning.versions.all() - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1_v1), Entry(self.unit_2_v1, pinned=True), # Pinned 📌 to v1 ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the subsection: - authoring_api.get_units_in_subsection(subsection, published=True) + content_api.get_units_in_subsection(subsection, published=True) def test_auto_publish_children(self): """ @@ -414,14 +414,14 @@ def test_auto_publish_children(self): # Also create another unit that's not in the subsection at all: other_unit, _ou_v1 = self.create_unit(title="A draft unit not in the subsection", key="unit:3") - assert authoring_api.contains_unpublished_changes(subsection.pk) + assert content_api.contains_unpublished_changes(subsection.pk) assert self.unit_1.versioning.published is None assert self.unit_2.versioning.published is None # Publish ONLY the subsection. This should however also auto-publish units 1 & 2 since they're children - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( entity=subsection.publishable_entity ), ) @@ -430,7 +430,7 @@ def test_auto_publish_children(self): self.unit_1.refresh_from_db() assert subsection.versioning.has_unpublished_changes is False # Shallow check assert self.unit_1.versioning.has_unpublished_changes is False - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # Deep check + assert content_api.contains_unpublished_changes(subsection.pk) is False # Deep check assert self.unit_1.versioning.published == self.unit_1_v1 # v1 is now the published version. # But our other unit that's outside the subsection is not affected: @@ -456,14 +456,14 @@ def test_no_publish_parent(self): assert subsection.versioning.published is None with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the subsection: - authoring_api.get_units_in_subsection(subsection, published=True) + content_api.get_units_in_subsection(subsection, published=True) def test_add_unit_after_publish(self): """ Adding a unit to a published subsection will create a new version and show that the subsection has unpublished changes. """ - subsection, subsection_version = authoring_api.create_subsection_and_version( + subsection, subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", @@ -474,25 +474,25 @@ def test_add_unit_after_publish(self): assert subsection.versioning.published is None assert subsection.versioning.has_unpublished_changes # Publish the empty subsection: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) subsection.refresh_from_db() # Reloading the subsection is necessary assert subsection.versioning.has_unpublished_changes is False # Shallow check for subsection only, not children - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(subsection.pk) is False # Deeper check # Add a published unit (unpinned): assert self.unit_1.versioning.has_unpublished_changes is False - subsection_version_v2 = authoring_api.create_next_subsection_version( + subsection_version_v2 = content_api.create_next_subsection_version( subsection=subsection, title=subsection_version.title, units=[self.unit_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) # Now the subsection should have unpublished changes: subsection.refresh_from_db() # Reloading the subsection is necessary assert subsection.versioning.has_unpublished_changes # Shallow check: adding a child changes the subsection - assert authoring_api.contains_unpublished_changes(subsection.pk) # Deeper check + assert content_api.contains_unpublished_changes(subsection.pk) # Deeper check assert subsection.versioning.draft == subsection_version_v2 assert subsection.versioning.published == subsection_version @@ -509,11 +509,11 @@ def test_modify_unpinned_unit_after_publish(self): assert subsection.versioning.has_unpublished_changes # Publish the subsection and the unit: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) subsection.refresh_from_db() # Reloading the subsection is necessary if we accessed 'versioning' before publish self.unit_1.refresh_from_db() assert subsection.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(subsection.pk) is False # Deeper check assert self.unit_1.versioning.has_unpublished_changes is False # Now modify the unit by changing its title (it remains a draft): @@ -523,26 +523,26 @@ def test_modify_unpinned_unit_after_publish(self): subsection.refresh_from_db() # Refresh to avoid stale 'versioning' cache self.unit_1.refresh_from_db() assert subsection.versioning.has_unpublished_changes is False # Shallow check: subsection unchanged - assert authoring_api.contains_unpublished_changes(subsection.pk) # But subsection DOES contain changes + assert content_api.contains_unpublished_changes(subsection.pk) # But subsection DOES contain changes assert self.unit_1.versioning.has_unpublished_changes # Since the unit changes haven't been published, they should only appear in the draft subsection - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(unit_1_v2), # new version ] - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), # old version ] # But if we publish the unit, the changes will appear in the published version of the subsection. self.publish_unit(self.unit_1) - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(unit_1_v2), # new version ] - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(unit_1_v2), # new version ] - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # No more unpublished changes + assert content_api.contains_unpublished_changes(subsection.pk) is False # No more unpublished changes def test_modify_pinned_unit(self): """ @@ -554,11 +554,11 @@ def test_modify_pinned_unit(self): subsection = self.create_subsection_with_units([self.unit_1_v1]) # Publish the subsection and the unit: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) expected_subsection_contents = [ Entry(self.unit_1_v1, pinned=True), # pinned 📌 to v1 ] - assert authoring_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents + assert content_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents # Now modify the unit by changing its title (it remains a draft): self.modify_unit(self.unit_1, title="Modified Counting Problem with new title") @@ -567,16 +567,16 @@ def test_modify_pinned_unit(self): subsection.refresh_from_db() # Refresh to avoid stale 'versioning' cache self.unit_1.refresh_from_db() assert subsection.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # Deep check + assert content_api.contains_unpublished_changes(subsection.pk) is False # Deep check assert self.unit_1.versioning.has_unpublished_changes is True # Neither the draft nor the published version of the subsection is affected - assert authoring_api.get_units_in_subsection(subsection, published=False) == expected_subsection_contents - assert authoring_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents + assert content_api.get_units_in_subsection(subsection, published=False) == expected_subsection_contents + assert content_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents # Even if we publish the unit, the subsection stays pinned to the specified version: self.publish_unit(self.unit_1) - assert authoring_api.get_units_in_subsection(subsection, published=False) == expected_subsection_contents - assert authoring_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents + assert content_api.get_units_in_subsection(subsection, published=False) == expected_subsection_contents + assert content_api.get_units_in_subsection(subsection, published=True) == expected_subsection_contents def test_create_two_subsections_with_same_units(self): """ @@ -589,39 +589,39 @@ def test_create_two_subsections_with_same_units(self): subsection2 = self.create_subsection_with_units([self.unit_1_v1, self.unit_2, self.unit_1], key="u2") # Check that the contents are as expected: - assert [row.unit_version for row in authoring_api.get_units_in_subsection(subsection1, published=False)] == [ + assert [row.unit_version for row in content_api.get_units_in_subsection(subsection1, published=False)] == [ self.unit_2_v1, self.unit_2_v1, self.unit_1_v1, ] - assert [row.unit_version for row in authoring_api.get_units_in_subsection(subsection2, published=False)] == [ + assert [row.unit_version for row in content_api.get_units_in_subsection(subsection2, published=False)] == [ self.unit_1_v1, self.unit_2_v1, self.unit_1_v1, ] # Modify unit 1 unit_1_v2 = self.modify_unit(self.unit_1, title="unit 1 v2") # Publish changes - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Modify unit 2 - only in the draft unit_2_v2 = self.modify_unit(self.unit_2, title="unit 2 DRAFT") # Check that the draft contents are as expected: - assert authoring_api.get_units_in_subsection(subsection1, published=False) == [ + assert content_api.get_units_in_subsection(subsection1, published=False) == [ Entry(unit_2_v2), # v2 in the draft version Entry(self.unit_2_v1, pinned=True), # pinned 📌 to v1 Entry(unit_1_v2), # v2 ] - assert authoring_api.get_units_in_subsection(subsection2, published=False) == [ + assert content_api.get_units_in_subsection(subsection2, published=False) == [ Entry(self.unit_1_v1, pinned=True), # pinned 📌 to v1 Entry(unit_2_v2), # v2 in the draft version Entry(unit_1_v2), # v2 ] # Check that the published contents are as expected: - assert authoring_api.get_units_in_subsection(subsection1, published=True) == [ + assert content_api.get_units_in_subsection(subsection1, published=True) == [ Entry(self.unit_2_v1), # v1 in the published version Entry(self.unit_2_v1, pinned=True), # pinned 📌 to v1 Entry(unit_1_v2), # v2 ] - assert authoring_api.get_units_in_subsection(subsection2, published=True) == [ + assert content_api.get_units_in_subsection(subsection2, published=True) == [ Entry(self.unit_1_v1, pinned=True), # pinned 📌 to v1 Entry(self.unit_2_v1), # v1 in the published version Entry(unit_1_v2), # v2 @@ -642,15 +642,15 @@ def test_publishing_shared_unit(self): ] subsection1 = self.create_subsection_with_units([u1, u2, u3], title="Subsection 1", key="subsection:1") subsection2 = self.create_subsection_with_units([u2, u4, u5], title="Subsection 2", key="subsection:2") - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(subsection1.pk) is False - assert authoring_api.contains_unpublished_changes(subsection2.pk) is False + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(subsection1.pk) is False + assert content_api.contains_unpublished_changes(subsection2.pk) is False # 2️⃣ Then the author edits U2 inside of Subsection 1 making U2v2. u2_v2 = self.modify_unit(u2, title="U2 version 2") # Both S1 and S2 now contain unpublished changes since they share the unit. - assert authoring_api.contains_unpublished_changes(subsection1.pk) - assert authoring_api.contains_unpublished_changes(subsection2.pk) + assert content_api.contains_unpublished_changes(subsection1.pk) + assert content_api.contains_unpublished_changes(subsection2.pk) # (But the subsections themselves are unchanged:) subsection1.refresh_from_db() subsection2.refresh_from_db() @@ -661,16 +661,16 @@ def test_publishing_shared_unit(self): u5_v2 = self.modify_unit(u5, title="U5 version 2") # 4️⃣ The author then publishes Subsection 1, and therefore everything in it. - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( # Note: we only publish the subsection; the publishing API should auto-publish its units too. entity_id=subsection1.publishable_entity.id, ), ) # Result: Subsection 1 will show the newly published version of U2: - assert authoring_api.get_units_in_subsection(subsection1, published=True) == [ + assert content_api.get_units_in_subsection(subsection1, published=True) == [ Entry(u1_v1), Entry(u2_v2), # new published version of U2 Entry(u3_v1), @@ -679,25 +679,25 @@ def test_publishing_shared_unit(self): # Result: someone looking at Subsection 2 should see the newly published unit 2, because publishing it anywhere # publishes it everywhere. But publishing U2 and Subsection 1 does not affect the other units in Subsection 2. # (Publish propagates downward, not upward) - assert authoring_api.get_units_in_subsection(subsection2, published=True) == [ + assert content_api.get_units_in_subsection(subsection2, published=True) == [ Entry(u2_v2), # new published version of U2 Entry(u4_v1), # still original version of U4 (it was never modified) Entry(u5_v1), # still original version of U5 (it hasn't been published) ] # Result: Subsection 2 contains unpublished changes due to modified U5; Subsection 1 does not. - assert authoring_api.contains_unpublished_changes(subsection1.pk) is False - assert authoring_api.contains_unpublished_changes(subsection2.pk) + assert content_api.contains_unpublished_changes(subsection1.pk) is False + assert content_api.contains_unpublished_changes(subsection2.pk) # 5️⃣ Publish unit U5, which should be the only thing unpublished in the learning package self.publish_unit(u5) # Result: Subsection 2 shows the new version of C5 and no longer contains unpublished changes: - assert authoring_api.get_units_in_subsection(subsection2, published=True) == [ + assert content_api.get_units_in_subsection(subsection2, published=True) == [ Entry(u2_v2), # new published version of U2 Entry(u4_v1), # still original version of U4 (it was never modified) Entry(u5_v2), # new published version of U5 ] - assert authoring_api.contains_unpublished_changes(subsection2.pk) is False + assert content_api.contains_unpublished_changes(subsection2.pk) is False def test_query_count_of_contains_unpublished_changes(self): """ @@ -714,15 +714,15 @@ def test_query_count_of_contains_unpublished_changes(self): ) units.append(unit) subsection = self.create_subsection_with_units(units) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) subsection.refresh_from_db() with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(subsection.pk) is False + assert content_api.contains_unpublished_changes(subsection.pk) is False # Modify the most recently created unit: self.modify_unit(unit, title="Modified Unit") with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(subsection.pk) is True + assert content_api.contains_unpublished_changes(subsection.pk) is True def test_metadata_change_doesnt_create_entity_list(self): """ @@ -735,7 +735,7 @@ def test_metadata_change_doesnt_create_entity_list(self): orig_version_num = subsection.versioning.draft.version_num orig_entity_list_id = subsection.versioning.draft.entity_list.pk - authoring_api.create_next_subsection_version(subsection, title="New Title", created=self.now) + content_api.create_next_subsection_version(subsection, title="New Title", created=self.now) subsection.refresh_from_db() new_version_num = subsection.versioning.draft.version_num @@ -757,9 +757,9 @@ def test_cannot_add_soft_deleted_unit(self, publish_first): unit, _cv = self.create_unit(title="Deleted unit") if publish_first: # Publish the unit: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now delete it. The draft version is now deleted: - authoring_api.soft_delete_draft(unit.pk) + content_api.soft_delete_draft(unit.pk) # Now try adding that unit to a subsection: with pytest.raises(ValidationError, match="unit is deleted"): self.create_subsection_with_units([unit]) @@ -767,52 +767,52 @@ def test_cannot_add_soft_deleted_unit(self, publish_first): def test_removing_unit(self): """ Test removing a unit from a subsection (but not deleting it) """ subsection = self.create_subsection_with_units([self.unit_1, self.unit_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now remove unit 2 - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Revised with unit 2 deleted", units=[self.unit_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the subsection: - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1_v1), ] subsection.refresh_from_db() assert subsection.versioning.has_unpublished_changes # The subsection itself and its unit list have change - assert authoring_api.contains_unpublished_changes(subsection.pk) + assert content_api.contains_unpublished_changes(subsection.pk) # The published version of the subsection is not yet affected: - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), Entry(self.unit_2_v1), ] # But when we publish the new subsection version with the removal, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # FIXME: Refreshing the subsection is necessary here because get_entities_in_published_container() accesses # container.versioning.published, and .versioning is cached with the old version. But this seems like # a footgun? We could avoid this if get_entities_in_published_container() took only an ID instead of an object, # but that would involve additional database lookup(s). subsection.refresh_from_db() - assert authoring_api.contains_unpublished_changes(subsection.pk) is False - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.contains_unpublished_changes(subsection.pk) is False + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), ] def test_soft_deleting_unit(self): """ Test soft deleting a unit that's in a subsection (but not removing it) """ subsection = self.create_subsection_with_units([self.unit_1, self.unit_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete unit 2 - authoring_api.soft_delete_draft(self.unit_2.pk) + content_api.soft_delete_draft(self.unit_2.pk) # Now it should not be listed in the subsection: - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1_v1), # unit 2 is soft deleted from the draft. # TODO: should we return some kind of placeholder here, to indicate that a unit is still listed in the @@ -820,72 +820,72 @@ def test_soft_deleting_unit(self): # reverted? ] assert subsection.versioning.has_unpublished_changes is False # Subsection and unit list unchanged - assert authoring_api.contains_unpublished_changes(subsection.pk) # It still contains an unpublished deletion + assert content_api.contains_unpublished_changes(subsection.pk) # It still contains an unpublished deletion # The published version of the subsection is not yet affected: - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), Entry(self.unit_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(subsection.pk) is False - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(subsection.pk) is False + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), ] def test_soft_deleting_and_removing_unit(self): """ Test soft deleting a unit that's in a subsection AND removing it """ subsection = self.create_subsection_with_units([self.unit_1, self.unit_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete unit 2 - authoring_api.soft_delete_draft(self.unit_2.pk) + content_api.soft_delete_draft(self.unit_2.pk) # And remove it from the subsection: - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Revised with unit 2 deleted", units=[self.unit_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the subsection: - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1_v1), ] assert subsection.versioning.has_unpublished_changes is True - assert authoring_api.contains_unpublished_changes(subsection.pk) + assert content_api.contains_unpublished_changes(subsection.pk) # The published version of the subsection is not yet affected: - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), Entry(self.unit_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(subsection.pk) is False - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(subsection.pk) is False + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1), ] def test_soft_deleting_pinned_unit(self): """ Test soft deleting a pinned 📌 unit that's in a subsection """ subsection = self.create_subsection_with_units([self.unit_1_v1, self.unit_2_v1]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete unit 2 - authoring_api.soft_delete_draft(self.unit_2.pk) + content_api.soft_delete_draft(self.unit_2.pk) # Now it should still be listed in the subsection: - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1_v1, pinned=True), Entry(self.unit_2_v1, pinned=True), ] assert subsection.versioning.has_unpublished_changes is False # Subsection and unit list unchanged - assert authoring_api.contains_unpublished_changes(subsection.pk) is False # nor does it contain changes + assert content_api.contains_unpublished_changes(subsection.pk) is False # nor does it contain changes # The published version of the subsection is also not affected: - assert authoring_api.get_units_in_subsection(subsection, published=True) == [ + assert content_api.get_units_in_subsection(subsection, published=True) == [ Entry(self.unit_1_v1, pinned=True), Entry(self.unit_2_v1, pinned=True), ] @@ -901,19 +901,19 @@ def test_soft_delete_subsection(self): other_subsection = self.create_subsection_with_units([self.unit_1], key="other") # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Delete the subsection: - authoring_api.soft_delete_draft(subsection_to_delete.publishable_entity_id) + content_api.soft_delete_draft(subsection_to_delete.publishable_entity_id) subsection_to_delete.refresh_from_db() # Now the draft subsection is soft deleted; units, published subsection, and other subsection are unaffected: assert subsection_to_delete.versioning.draft is None # Subsection is soft deleted. assert subsection_to_delete.versioning.published is not None self.unit_1.refresh_from_db() assert self.unit_1.versioning.draft is not None - assert authoring_api.get_units_in_subsection(other_subsection, published=False) == [Entry(self.unit_1_v1)] + assert content_api.get_units_in_subsection(other_subsection, published=False) == [Entry(self.unit_1_v1)] # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now the subsection's published version is also deleted, but nothing else is affected. subsection_to_delete.refresh_from_db() assert subsection_to_delete.versioning.draft is None # Subsection is soft deleted. @@ -921,8 +921,8 @@ def test_soft_delete_subsection(self): self.unit_1.refresh_from_db() assert self.unit_1.versioning.draft is not None assert self.unit_1.versioning.published is not None - assert authoring_api.get_units_in_subsection(other_subsection, published=False) == [Entry(self.unit_1_v1)] - assert authoring_api.get_units_in_subsection(other_subsection, published=True) == [Entry(self.unit_1_v1)] + assert content_api.get_units_in_subsection(other_subsection, published=False) == [Entry(self.unit_1_v1)] + assert content_api.get_units_in_subsection(other_subsection, published=True) == [Entry(self.unit_1_v1)] def test_snapshots_of_published_subsection(self): """ @@ -932,43 +932,43 @@ def test_snapshots_of_published_subsection(self): # At first the subsection has one unit (unpinned): subsection = self.create_subsection_with_units([self.unit_1]) self.modify_unit(self.unit_1, title="Unit 1 as of checkpoint 1") - before_publish = authoring_api.get_units_in_published_subsection_as_of(subsection, 0) + before_publish = content_api.get_units_in_published_subsection_as_of(subsection, 0) assert before_publish is None # Publish everything, creating Checkpoint 1 - checkpoint_1 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") + checkpoint_1 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") ######################################################################## # Now we update the title of the unit. self.modify_unit(self.unit_1, title="Unit 1 as of checkpoint 2") # Publish everything, creating Checkpoint 2 - checkpoint_2 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") + checkpoint_2 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") ######################################################################## # Now add a second unit to the subsection: self.modify_unit(self.unit_1, title="Unit 1 as of checkpoint 3") self.modify_unit(self.unit_2, title="Unit 2 as of checkpoint 3") - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Subsection title in checkpoint 3", units=[self.unit_1, self.unit_2], created=self.now, ) # Publish everything, creating Checkpoint 3 - checkpoint_3 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") + checkpoint_3 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") ######################################################################## # Now add a third unit to the subsection, a pinned 📌 version of unit 1. # This will test pinned versions and also test adding at the beginning rather than the end of the subsection. - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title="Subsection title in checkpoint 4", units=[self.unit_1_v1, self.unit_1, self.unit_2], created=self.now, ) # Publish everything, creating Checkpoint 4 - checkpoint_4 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") + checkpoint_4 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") ######################################################################## # Modify the drafts, but don't publish: @@ -976,20 +976,20 @@ def test_snapshots_of_published_subsection(self): self.modify_unit(self.unit_2, title="Unit 2 draft") # Now fetch the snapshots: - as_of_checkpoint_1 = authoring_api.get_units_in_published_subsection_as_of(subsection, checkpoint_1.pk) + as_of_checkpoint_1 = content_api.get_units_in_published_subsection_as_of(subsection, checkpoint_1.pk) assert [cv.unit_version.title for cv in as_of_checkpoint_1] == [ "Unit 1 as of checkpoint 1", ] - as_of_checkpoint_2 = authoring_api.get_units_in_published_subsection_as_of(subsection, checkpoint_2.pk) + as_of_checkpoint_2 = content_api.get_units_in_published_subsection_as_of(subsection, checkpoint_2.pk) assert [cv.unit_version.title for cv in as_of_checkpoint_2] == [ "Unit 1 as of checkpoint 2", ] - as_of_checkpoint_3 = authoring_api.get_units_in_published_subsection_as_of(subsection, checkpoint_3.pk) + as_of_checkpoint_3 = content_api.get_units_in_published_subsection_as_of(subsection, checkpoint_3.pk) assert [cv.unit_version.title for cv in as_of_checkpoint_3] == [ "Unit 1 as of checkpoint 3", "Unit 2 as of checkpoint 3", ] - as_of_checkpoint_4 = authoring_api.get_units_in_published_subsection_as_of(subsection, checkpoint_4.pk) + as_of_checkpoint_4 = content_api.get_units_in_published_subsection_as_of(subsection, checkpoint_4.pk) assert [cv.unit_version.title for cv in as_of_checkpoint_4] == [ "Unit (1)", # Pinned. This title is self.unit_1_v1.title (original v1 title) "Unit 1 as of checkpoint 3", # we didn't modify these units so they're same as in snapshot 3 @@ -1025,7 +1025,7 @@ def test_subsections_containing(self): with self.assertNumQueries(1): result = [ c.subsection for c in - authoring_api.get_containers_with_entity(self.unit_1.pk).select_related("subsection") + content_api.get_containers_with_entity(self.unit_1.pk).select_related("subsection") ] assert result == [ subsection1_1pinned, @@ -1039,7 +1039,7 @@ def test_subsections_containing(self): with self.assertNumQueries(1): result2 = [ c.subsection for c in - authoring_api.get_containers_with_entity( + content_api.get_containers_with_entity( self.unit_1.pk, ignore_pinned=True ).select_related("subsection") ] @@ -1056,15 +1056,15 @@ def test_get_units_in_subsection_queries(self): self.unit_2_v1, ]) with self.assertNumQueries(4): - result = authoring_api.get_units_in_subsection(subsection, published=False) + result = content_api.get_units_in_subsection(subsection, published=False) assert result == [ Entry(self.unit_1.versioning.draft), Entry(self.unit_2.versioning.draft), Entry(self.unit_2.versioning.draft, pinned=True), ] - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) with self.assertNumQueries(4): - result = authoring_api.get_units_in_subsection(subsection, published=True) + result = content_api.get_units_in_subsection(subsection, published=True) assert result == [ Entry(self.unit_1.versioning.draft), Entry(self.unit_2.versioning.draft), @@ -1075,7 +1075,7 @@ def test_add_remove_container_children(self): """ Test adding and removing children units from subsections. """ - subsection, subsection_version = authoring_api.create_subsection_and_version( + subsection, subsection_version = content_api.create_subsection_and_version( learning_package_id=self.learning_package.id, key="subsection:key", title="Subsection", @@ -1083,7 +1083,7 @@ def test_add_remove_container_children(self): created=self.now, created_by=None, ) - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1.versioning.draft), ] unit_3, _ = self.create_unit( @@ -1091,36 +1091,36 @@ def test_add_remove_container_children(self): title="Unit (3)", ) # Add unit_2 and unit_3 - subsection_version_v2 = authoring_api.create_next_subsection_version( + subsection_version_v2 = content_api.create_next_subsection_version( subsection=subsection, title=subsection_version.title, units=[self.unit_2, unit_3], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) subsection.refresh_from_db() assert subsection_version_v2.version_num == 2 assert subsection_version_v2 in subsection.versioning.versions.all() # Verify that unit_2 and unit_3 is added to end - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_1.versioning.draft), Entry(self.unit_2.versioning.draft), Entry(unit_3.versioning.draft), ] # Remove unit_1 - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title=subsection_version.title, units=[self.unit_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) subsection.refresh_from_db() # Verify that unit_1 is removed - assert authoring_api.get_units_in_subsection(subsection, published=False) == [ + assert content_api.get_units_in_subsection(subsection, published=False) == [ Entry(self.unit_2.versioning.draft), Entry(unit_3.versioning.draft), ] @@ -1130,34 +1130,34 @@ def test_get_container_children_count(self): Test get_container_children_count() """ subsection = self.create_subsection_with_units([self.unit_1]) - assert authoring_api.get_container_children_count(subsection.container, published=False) == 1 + assert content_api.get_container_children_count(subsection.container, published=False) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) subsection_version = subsection.versioning.draft - authoring_api.create_next_subsection_version( + content_api.create_next_subsection_version( subsection=subsection, title=subsection_version.title, units=[self.unit_2], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) subsection.refresh_from_db() # Should have two units in draft version and 1 in published version - assert authoring_api.get_container_children_count(subsection.container, published=False) == 2 - assert authoring_api.get_container_children_count(subsection.container, published=True) == 1 + assert content_api.get_container_children_count(subsection.container, published=False) == 2 + assert content_api.get_container_children_count(subsection.container, published=True) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) subsection.refresh_from_db() - assert authoring_api.get_container_children_count(subsection.container, published=True) == 2 + assert content_api.get_container_children_count(subsection.container, published=True) == 2 # Soft delete unit_1 - authoring_api.soft_delete_draft(self.unit_1.pk) + content_api.soft_delete_draft(self.unit_1.pk) subsection.refresh_from_db() # Should contain only 1 child - assert authoring_api.get_container_children_count(subsection.container, published=False) == 1 - authoring_api.publish_all_drafts(self.learning_package.id) + assert content_api.get_container_children_count(subsection.container, published=False) == 1 + content_api.publish_all_drafts(self.learning_package.id) subsection.refresh_from_db() - assert authoring_api.get_container_children_count(subsection.container, published=True) == 1 + assert content_api.get_container_children_count(subsection.container, published=True) == 1 # Tests TODO: # Test that I can get a [PublishLog] history of a given subsection and all its children, including children diff --git a/tests/openedx_content/applets/units/test_api.py b/tests/openedx_content/applets/units/test_api.py index 560e2e719..aca4fa58c 100644 --- a/tests/openedx_content/applets/units/test_api.py +++ b/tests/openedx_content/applets/units/test_api.py @@ -8,12 +8,12 @@ from django.core.exceptions import ValidationError from django.db import IntegrityError -import openedx_content.api as authoring_api +import openedx_content.api as content_api from openedx_content import models_api as authoring_models from ..components.test_api import ComponentTestCase -Entry = authoring_api.UnitListEntry +Entry = content_api.UnitListEntry @ddt.ddt @@ -35,7 +35,7 @@ def create_component(self, *, title: str = "Test Component", key: str = "compone authoring_models.Component, authoring_models.ComponentVersion ]: """ Helper method to quickly create a component """ - return authoring_api.create_component_and_version( + return content_api.create_component_and_version( self.learning_package.id, component_type=self.problem_type, local_key=key, @@ -52,7 +52,7 @@ def create_unit_with_components( key="unit:key", ) -> authoring_models.Unit: """ Helper method to quickly create a unit with some components """ - unit, _unit_v1 = authoring_api.create_unit_and_version( + unit, _unit_v1 = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key=key, title=title, @@ -72,7 +72,7 @@ def modify_component( """ Helper method to modify a component for the purposes of testing units/drafts/pinning/publishing/etc. """ - return authoring_api.create_next_component_version( + return content_api.create_next_component_version( component.pk, content_to_replace={}, title=title, @@ -86,7 +86,7 @@ def test_get_unit(self): """ unit = self.create_unit_with_components([self.component_1, self.component_2]) with self.assertNumQueries(1): - result = authoring_api.get_unit(unit.pk) + result = content_api.get_unit(unit.pk) assert result == unit # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -99,7 +99,7 @@ def test_get_unit_version(self): unit = self.create_unit_with_components([]) draft = unit.versioning.draft with self.assertNumQueries(1): - result = authoring_api.get_unit_version(draft.pk) + result = content_api.get_unit_version(draft.pk) assert result == draft def test_get_latest_unit_version(self): @@ -109,7 +109,7 @@ def test_get_latest_unit_version(self): unit = self.create_unit_with_components([]) draft = unit.versioning.draft with self.assertNumQueries(2): - result = authoring_api.get_latest_unit_version(unit.pk) + result = content_api.get_latest_unit_version(unit.pk) assert result == draft def test_get_containers(self): @@ -118,7 +118,7 @@ def test_get_containers(self): """ unit = self.create_unit_with_components([]) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) assert result == [unit.container] # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -129,13 +129,13 @@ def test_get_containers_deleted(self): Test that get_containers() does not return soft-deleted units. """ unit = self.create_unit_with_components([]) - authoring_api.soft_delete_draft(unit.pk) + content_api.soft_delete_draft(unit.pk) with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id, include_deleted=True)) + result = list(content_api.get_containers(self.learning_package.id, include_deleted=True)) assert result == [unit.container] with self.assertNumQueries(1): - result = list(authoring_api.get_containers(self.learning_package.id)) + result = list(content_api.get_containers(self.learning_package.id)) assert not result def test_get_container(self): @@ -144,7 +144,7 @@ def test_get_container(self): """ unit = self.create_unit_with_components([self.component_1, self.component_2]) with self.assertNumQueries(1): - result = authoring_api.get_container(unit.pk) + result = content_api.get_container(unit.pk) assert result == unit.container # Versioning data should be pre-loaded via select_related() with self.assertNumQueries(0): @@ -156,7 +156,7 @@ def test_get_container_by_key(self): """ unit = self.create_unit_with_components([]) with self.assertNumQueries(1): - result = authoring_api.get_container_by_key( + result = content_api.get_container_by_key( self.learning_package.id, key=unit.publishable_entity.key, ) @@ -197,7 +197,7 @@ def test_create_unit_with_invalid_children(self): exception is raised. """ # Create two units: - unit, unit_version = authoring_api.create_unit_and_version( + unit, unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", @@ -205,7 +205,7 @@ def test_create_unit_with_invalid_children(self): created_by=None, ) assert unit.versioning.draft == unit_version - unit2, _u2v1 = authoring_api.create_unit_and_version( + unit2, _u2v1 = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key2", title="Unit 2", @@ -214,7 +214,7 @@ def test_create_unit_with_invalid_children(self): ) # Try adding a Unit to a Unit with pytest.raises(TypeError, match="Unit components must be either Component or ComponentVersion."): - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Unit Containing a Unit", components=[unit2], @@ -223,7 +223,7 @@ def test_create_unit_with_invalid_children(self): ) # Check that a new version was not created: unit.refresh_from_db() - assert authoring_api.get_unit(unit.pk).versioning.draft == unit_version + assert content_api.get_unit(unit.pk).versioning.draft == unit_version assert unit.versioning.draft == unit_version def test_adding_external_components(self): @@ -231,8 +231,8 @@ def test_adding_external_components(self): Test that components from another learning package cannot be added to a unit. """ - learning_package2 = authoring_api.create_learning_package(key="other-package", title="Other Package") - unit, _unit_version = authoring_api.create_unit_and_version( + learning_package2 = content_api.create_learning_package(key="other-package", title="Other Package") + unit, _unit_version = content_api.create_unit_and_version( learning_package_id=learning_package2.pk, key="unit:key", title="Unit", @@ -242,7 +242,7 @@ def test_adding_external_components(self): assert self.component_1.learning_package != learning_package2 # Try adding a a component from LP 1 (self.learning_package) to a unit from LP 2 with pytest.raises(ValidationError, match="Container entities must be from the same learning package."): - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Unit Containing an External Component", components=[self.component_1], @@ -256,14 +256,14 @@ def test_adding_mismatched_versions(self, mock_entities_for_components): Test that versioned components must match their entities. """ mock_entities_for_components.return_value = [ - authoring_api.ContainerEntityRow( + content_api.ContainerEntityRow( entity_pk=self.component_1.pk, version_pk=self.component_2_v1.pk, ), ] # Try adding a a component from LP 1 (self.learning_package) to a unit from LP 2 with pytest.raises(ValidationError, match="Container entity versions must belong to the specified entity"): - authoring_api.create_unit_and_version( + content_api.create_unit_and_version( learning_package_id=self.component_1.learning_package.id, key="unit:key", title="Unit", @@ -294,7 +294,7 @@ def test_create_empty_unit_and_version(self): 3. The unit is a draft with unpublished changes. 4. There is no published version of the unit. """ - unit, unit_version = authoring_api.create_unit_and_version( + unit, unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", @@ -318,14 +318,14 @@ def test_create_next_unit_version_with_two_unpinned_components(self): 3. The unit version is in the unit's versions. 4. The components are in the draft unit version's component list and are unpinned. """ - unit, _unit_version = authoring_api.create_unit_and_version( + unit, _unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", created=self.now, created_by=None, ) - unit_version_v2 = authoring_api.create_next_unit_version( + unit_version_v2 = content_api.create_next_unit_version( unit=unit, title="Unit", components=[self.component_1, self.component_2], @@ -334,26 +334,26 @@ def test_create_next_unit_version_with_two_unpinned_components(self): ) assert unit_version_v2.version_num == 2 assert unit_version_v2 in unit.versioning.versions.all() - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1.versioning.draft), Entry(self.component_2.versioning.draft), ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the unit: - authoring_api.get_components_in_unit(unit, published=True) + content_api.get_components_in_unit(unit, published=True) def test_create_next_unit_version_with_unpinned_and_pinned_components(self): """ Test creating a unit version with one unpinned and one pinned 📌 component. """ - unit, _unit_version = authoring_api.create_unit_and_version( + unit, _unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", created=self.now, created_by=None, ) - unit_version_v2 = authoring_api.create_next_unit_version( + unit_version_v2 = content_api.create_next_unit_version( unit=unit, title="Unit", components=[self.component_1, self.component_2_v1], # Note the "v1" pinning 📌 the second one to version 1 @@ -362,26 +362,26 @@ def test_create_next_unit_version_with_unpinned_and_pinned_components(self): ) assert unit_version_v2.version_num == 2 assert unit_version_v2 in unit.versioning.versions.all() - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1_v1), Entry(self.component_2_v1, pinned=True), # Pinned 📌 to v1 ] with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the unit: - authoring_api.get_components_in_unit(unit, published=True) + content_api.get_components_in_unit(unit, published=True) def test_create_next_unit_version_forcing_version_num(self): """ Test creating a unit version with forcing the version number. """ - unit, _unit_version = authoring_api.create_unit_and_version( + unit, _unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", created=self.now, created_by=None, ) - unit_version_v2 = authoring_api.create_next_unit_version( + unit_version_v2 = content_api.create_next_unit_version( unit=unit, title="Unit", components=[self.component_1, self.component_2_v1], # Note the "v1" pinning 📌 the second one to version 1 @@ -400,21 +400,21 @@ def test_auto_publish_children(self): # Also create another component that's not in the unit at all: other_component, _oc_v1 = self.create_component(title="A draft component not in the unit", key="component:3") - assert authoring_api.contains_unpublished_changes(unit.pk) + assert content_api.contains_unpublished_changes(unit.pk) assert self.component_1.versioning.published is None assert self.component_2.versioning.published is None # Publish ONLY the unit. This should however also auto-publish components 1 & 2 since they're children - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter(entity=unit.publishable_entity), + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter(entity=unit.publishable_entity), ) # Now all changes to the unit and to component 1 are published: unit.refresh_from_db() self.component_1.refresh_from_db() assert unit.versioning.has_unpublished_changes is False # Shallow check assert self.component_1.versioning.has_unpublished_changes is False - assert authoring_api.contains_unpublished_changes(unit.pk) is False # Deep check + assert content_api.contains_unpublished_changes(unit.pk) is False # Deep check assert self.component_1.versioning.published == self.component_1_v1 # v1 is now the published version. # But our other component that's outside the unit is not affected: @@ -440,14 +440,14 @@ def test_no_publish_parent(self): assert unit.versioning.published is None with pytest.raises(authoring_models.ContainerVersion.DoesNotExist): # There is no published version of the unit: - authoring_api.get_components_in_unit(unit, published=True) + content_api.get_components_in_unit(unit, published=True) def test_add_component_after_publish(self): """ Adding a component to a published unit will create a new version and show that the unit has unpublished changes. """ - unit, unit_version = authoring_api.create_unit_and_version( + unit, unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", @@ -458,25 +458,25 @@ def test_add_component_after_publish(self): assert unit.versioning.published is None assert unit.versioning.has_unpublished_changes # Publish the empty unit: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) unit.refresh_from_db() # Reloading the unit is necessary assert unit.versioning.has_unpublished_changes is False # Shallow check for just the unit itself, not children - assert authoring_api.contains_unpublished_changes(unit.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(unit.pk) is False # Deeper check # Add a published component (unpinned): assert self.component_1.versioning.has_unpublished_changes is False - unit_version_v2 = authoring_api.create_next_unit_version( + unit_version_v2 = content_api.create_next_unit_version( unit=unit, title=unit_version.title, components=[self.component_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) # Now the unit should have unpublished changes: unit.refresh_from_db() # Reloading the unit is necessary assert unit.versioning.has_unpublished_changes # Shallow check - adding a child is a change to the unit - assert authoring_api.contains_unpublished_changes(unit.pk) # Deeper check + assert content_api.contains_unpublished_changes(unit.pk) # Deeper check assert unit.versioning.draft == unit_version_v2 assert unit.versioning.published == unit_version @@ -493,11 +493,11 @@ def test_modify_unpinned_component_after_publish(self): assert unit.versioning.has_unpublished_changes # Publish the unit and the component: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) unit.refresh_from_db() # Reloading the unit is necessary if we accessed 'versioning' before publish self.component_1.refresh_from_db() assert unit.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(unit.pk) is False # Deeper check + assert content_api.contains_unpublished_changes(unit.pk) is False # Deeper check assert self.component_1.versioning.has_unpublished_changes is False # Now modify the component by changing its title (it remains a draft): @@ -507,26 +507,26 @@ def test_modify_unpinned_component_after_publish(self): unit.refresh_from_db() # Reloading the unit is necessary, or 'unit.versioning' will be outdated self.component_1.refresh_from_db() assert unit.versioning.has_unpublished_changes is False # Shallow check should be false - unit is unchanged - assert authoring_api.contains_unpublished_changes(unit.pk) # But unit DOES contain changes + assert content_api.contains_unpublished_changes(unit.pk) # But unit DOES contain changes assert self.component_1.versioning.has_unpublished_changes # Since the component changes haven't been published, they should only appear in the draft unit - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(component_1_v2), # new version ] - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), # old version ] # But if we publish the component, the changes will appear in the published version of the unit. self.publish_component(self.component_1) - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(component_1_v2), # new version ] - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(component_1_v2), # new version ] - assert authoring_api.contains_unpublished_changes(unit.pk) is False # No longer contains unpublished changes + assert content_api.contains_unpublished_changes(unit.pk) is False # No longer contains unpublished changes def test_modify_pinned_component(self): """ @@ -538,11 +538,11 @@ def test_modify_pinned_component(self): unit = self.create_unit_with_components([self.component_1_v1]) # Publish the unit and the component: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) expected_unit_contents = [ Entry(self.component_1_v1, pinned=True), # pinned 📌 to v1 ] - assert authoring_api.get_components_in_unit(unit, published=True) == expected_unit_contents + assert content_api.get_components_in_unit(unit, published=True) == expected_unit_contents # Now modify the component by changing its title (it remains a draft): self.modify_component(self.component_1, title="Modified Counting Problem with new title") @@ -551,16 +551,16 @@ def test_modify_pinned_component(self): unit.refresh_from_db() # Reloading the unit is necessary, or 'unit.versioning' will be outdated self.component_1.refresh_from_db() assert unit.versioning.has_unpublished_changes is False # Shallow check - assert authoring_api.contains_unpublished_changes(unit.pk) is False # Deep check + assert content_api.contains_unpublished_changes(unit.pk) is False # Deep check assert self.component_1.versioning.has_unpublished_changes is True # Neither the draft nor the published version of the unit is affected - assert authoring_api.get_components_in_unit(unit, published=False) == expected_unit_contents - assert authoring_api.get_components_in_unit(unit, published=True) == expected_unit_contents + assert content_api.get_components_in_unit(unit, published=False) == expected_unit_contents + assert content_api.get_components_in_unit(unit, published=True) == expected_unit_contents # Even if we publish the component, the unit stays pinned to the specified version: self.publish_component(self.component_1) - assert authoring_api.get_components_in_unit(unit, published=False) == expected_unit_contents - assert authoring_api.get_components_in_unit(unit, published=True) == expected_unit_contents + assert content_api.get_components_in_unit(unit, published=False) == expected_unit_contents + assert content_api.get_components_in_unit(unit, published=True) == expected_unit_contents def test_create_two_units_with_same_components(self): """ @@ -573,39 +573,39 @@ def test_create_two_units_with_same_components(self): unit2 = self.create_unit_with_components([self.component_1_v1, self.component_2, self.component_1], key="u2") # Check that the contents are as expected: - assert [row.component_version for row in authoring_api.get_components_in_unit(unit1, published=False)] == [ + assert [row.component_version for row in content_api.get_components_in_unit(unit1, published=False)] == [ self.component_2_v1, self.component_2_v1, self.component_1_v1, ] - assert [row.component_version for row in authoring_api.get_components_in_unit(unit2, published=False)] == [ + assert [row.component_version for row in content_api.get_components_in_unit(unit2, published=False)] == [ self.component_1_v1, self.component_2_v1, self.component_1_v1, ] # Modify component 1 component_1_v2 = self.modify_component(self.component_1, title="component 1 v2") # Publish changes - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Modify component 2 - only in the draft component_2_v2 = self.modify_component(self.component_2, title="component 2 DRAFT") # Check that the draft contents are as expected: - assert authoring_api.get_components_in_unit(unit1, published=False) == [ + assert content_api.get_components_in_unit(unit1, published=False) == [ Entry(component_2_v2), # v2 in the draft version Entry(self.component_2_v1, pinned=True), # pinned 📌 to v1 Entry(component_1_v2), # v2 ] - assert authoring_api.get_components_in_unit(unit2, published=False) == [ + assert content_api.get_components_in_unit(unit2, published=False) == [ Entry(self.component_1_v1, pinned=True), # pinned 📌 to v1 Entry(component_2_v2), # v2 in the draft version Entry(component_1_v2), # v2 ] # Check that the published contents are as expected: - assert authoring_api.get_components_in_unit(unit1, published=True) == [ + assert content_api.get_components_in_unit(unit1, published=True) == [ Entry(self.component_2_v1), # v1 in the published version Entry(self.component_2_v1, pinned=True), # pinned 📌 to v1 Entry(component_1_v2), # v2 ] - assert authoring_api.get_components_in_unit(unit2, published=True) == [ + assert content_api.get_components_in_unit(unit2, published=True) == [ Entry(self.component_1_v1, pinned=True), # pinned 📌 to v1 Entry(self.component_2_v1), # v1 in the published version Entry(component_1_v2), # v2 @@ -626,15 +626,15 @@ def test_publishing_shared_component(self): ] unit1 = self.create_unit_with_components([c1, c2, c3], title="Unit 1", key="unit:1") unit2 = self.create_unit_with_components([c2, c4, c5], title="Unit 2", key="unit:2") - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(unit1.pk) is False - assert authoring_api.contains_unpublished_changes(unit2.pk) is False + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(unit1.pk) is False + assert content_api.contains_unpublished_changes(unit2.pk) is False # 2️⃣ Then the author edits C2 inside of Unit 1 making C2v2. c2_v2 = self.modify_component(c2, title="C2 version 2") # This makes U1 and U2 both show up as Units that CONTAIN unpublished changes, because they share the component. - assert authoring_api.contains_unpublished_changes(unit1.pk) - assert authoring_api.contains_unpublished_changes(unit2.pk) + assert content_api.contains_unpublished_changes(unit1.pk) + assert content_api.contains_unpublished_changes(unit2.pk) # (But the units themselves are unchanged:) unit1.refresh_from_db() unit2.refresh_from_db() @@ -645,16 +645,16 @@ def test_publishing_shared_component(self): c5_v2 = self.modify_component(c5, title="C5 version 2") # 4️⃣ The author then publishes Unit 1, and therefore everything in it. - authoring_api.publish_from_drafts( + content_api.publish_from_drafts( self.learning_package.pk, - draft_qset=authoring_api.get_all_drafts(self.learning_package.pk).filter( + draft_qset=content_api.get_all_drafts(self.learning_package.pk).filter( # Note: we only publish the unit; the publishing API should auto-publish its components too. entity_id=unit1.publishable_entity.id, ), ) # Result: Unit 1 will show the newly published version of C2: - assert authoring_api.get_components_in_unit(unit1, published=True) == [ + assert content_api.get_components_in_unit(unit1, published=True) == [ Entry(c1_v1), Entry(c2_v2), # new published version of C2 Entry(c3_v1), @@ -663,25 +663,25 @@ def test_publishing_shared_component(self): # Result: someone looking at Unit 2 should see the newly published component 2, because publishing it anywhere # publishes it everywhere. But publishing C2 and Unit 1 does not affect the other components in Unit 2. # (Publish propagates downward, not upward) - assert authoring_api.get_components_in_unit(unit2, published=True) == [ + assert content_api.get_components_in_unit(unit2, published=True) == [ Entry(c2_v2), # new published version of C2 Entry(c4_v1), # still original version of C4 (it was never modified) Entry(c5_v1), # still original version of C5 (it hasn't been published) ] # Result: Unit 2 CONTAINS unpublished changes because of the modified C5. Unit 1 doesn't contain unpub changes. - assert authoring_api.contains_unpublished_changes(unit1.pk) is False - assert authoring_api.contains_unpublished_changes(unit2.pk) + assert content_api.contains_unpublished_changes(unit1.pk) is False + assert content_api.contains_unpublished_changes(unit2.pk) # 5️⃣ Publish component C5, which should be the only thing unpublished in the learning package self.publish_component(c5) # Result: Unit 2 shows the new version of C5 and no longer contains unpublished changes: - assert authoring_api.get_components_in_unit(unit2, published=True) == [ + assert content_api.get_components_in_unit(unit2, published=True) == [ Entry(c2_v2), # new published version of C2 Entry(c4_v1), # still original version of C4 (it was never modified) Entry(c5_v2), # new published version of C5 ] - assert authoring_api.contains_unpublished_changes(unit2.pk) is False + assert content_api.contains_unpublished_changes(unit2.pk) is False def test_query_count_of_contains_unpublished_changes(self): """ @@ -698,15 +698,15 @@ def test_query_count_of_contains_unpublished_changes(self): ) components.append(component) unit = self.create_unit_with_components(components) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) unit.refresh_from_db() with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(unit.pk) is False + assert content_api.contains_unpublished_changes(unit.pk) is False # Modify the most recently created component: self.modify_component(component, title="Modified Component") with self.assertNumQueries(1): - assert authoring_api.contains_unpublished_changes(unit.pk) is True + assert content_api.contains_unpublished_changes(unit.pk) is True def test_metadata_change_doesnt_create_entity_list(self): """ @@ -719,7 +719,7 @@ def test_metadata_change_doesnt_create_entity_list(self): orig_version_num = unit.versioning.draft.version_num orig_entity_list_id = unit.versioning.draft.entity_list.pk - authoring_api.create_next_unit_version(unit, title="New Title", created=self.now) + content_api.create_next_unit_version(unit, title="New Title", created=self.now) unit.refresh_from_db() new_version_num = unit.versioning.draft.version_num @@ -741,9 +741,9 @@ def test_cannot_add_soft_deleted_component(self, publish_first): component, _cv = self.create_component(title="Deleted component") if publish_first: # Publish the component: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now delete it. The draft version is now deleted: - authoring_api.soft_delete_draft(component.pk) + content_api.soft_delete_draft(component.pk) # Now try adding that component to a unit: with pytest.raises(ValidationError, match="component is deleted"): self.create_unit_with_components([component]) @@ -751,52 +751,52 @@ def test_cannot_add_soft_deleted_component(self, publish_first): def test_removing_component(self): """ Test removing a component from a unit (but not deleting it) """ unit = self.create_unit_with_components([self.component_1, self.component_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now remove component 2 - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Revised with component 2 deleted", components=[self.component_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the unit: - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1_v1), ] unit.refresh_from_db() assert unit.versioning.has_unpublished_changes # The unit itself and its component list have change - assert authoring_api.contains_unpublished_changes(unit.pk) + assert content_api.contains_unpublished_changes(unit.pk) # The published version of the unit is not yet affected: - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), Entry(self.component_2_v1), ] # But when we publish the new unit version with the removal, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # FIXME: Refreshing the unit is necessary here because get_entities_in_published_container() accesses # container.versioning.published, and .versioning is cached with the old version. But this seems like # a footgun? We could avoid this if get_entities_in_published_container() took only an ID instead of an object, # but that would involve additional database lookup(s). unit.refresh_from_db() - assert authoring_api.contains_unpublished_changes(unit.pk) is False - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.contains_unpublished_changes(unit.pk) is False + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), ] def test_soft_deleting_component(self): """ Test soft deleting a component that's in a unit (but not removing it) """ unit = self.create_unit_with_components([self.component_1, self.component_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete component 2 - authoring_api.soft_delete_draft(self.component_2.pk) + content_api.soft_delete_draft(self.component_2.pk) # Now it should not be listed in the unit: - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1_v1), # component 2 is soft deleted from the draft. # TODO: should we return some kind of placeholder here, to indicate that a component is still listed in the @@ -804,72 +804,72 @@ def test_soft_deleting_component(self): # reverted? ] assert unit.versioning.has_unpublished_changes is False # The unit itself and its component list is not changed - assert authoring_api.contains_unpublished_changes(unit.pk) # But it CONTAINS an unpublished change (a deletion) + assert content_api.contains_unpublished_changes(unit.pk) # But it CONTAINS an unpublished change (a deletion) # The published version of the unit is not yet affected: - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), Entry(self.component_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(unit.pk) is False - assert authoring_api.get_components_in_unit(unit, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(unit.pk) is False + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), ] def test_soft_deleting_and_removing_component(self): """ Test soft deleting a component that's in a unit AND removing it """ unit = self.create_unit_with_components([self.component_1, self.component_2]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete component 2 - authoring_api.soft_delete_draft(self.component_2.pk) + content_api.soft_delete_draft(self.component_2.pk) # And remove it from the unit: - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Revised with component 2 deleted", components=[self.component_2], created=self.now, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) # Now it should not be listed in the unit: - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1_v1), ] assert unit.versioning.has_unpublished_changes is True - assert authoring_api.contains_unpublished_changes(unit.pk) + assert content_api.contains_unpublished_changes(unit.pk) # The published version of the unit is not yet affected: - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), Entry(self.component_2_v1), ] # But when we publish the deletion, the published version is affected: - authoring_api.publish_all_drafts(self.learning_package.id) - assert authoring_api.contains_unpublished_changes(unit.pk) is False - assert authoring_api.get_components_in_unit(unit, published=True) == [ + content_api.publish_all_drafts(self.learning_package.id) + assert content_api.contains_unpublished_changes(unit.pk) is False + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1), ] def test_soft_deleting_pinned_component(self): """ Test soft deleting a pinned 📌 component that's in a unit """ unit = self.create_unit_with_components([self.component_1_v1, self.component_2_v1]) - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now soft delete component 2 - authoring_api.soft_delete_draft(self.component_2.pk) + content_api.soft_delete_draft(self.component_2.pk) # Now it should still be listed in the unit: - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1_v1, pinned=True), Entry(self.component_2_v1, pinned=True), ] assert unit.versioning.has_unpublished_changes is False # The unit itself and its component list is not changed - assert authoring_api.contains_unpublished_changes(unit.pk) is False # nor does it contain changes + assert content_api.contains_unpublished_changes(unit.pk) is False # nor does it contain changes # The published version of the unit is also not affected: - assert authoring_api.get_components_in_unit(unit, published=True) == [ + assert content_api.get_components_in_unit(unit, published=True) == [ Entry(self.component_1_v1, pinned=True), Entry(self.component_2_v1, pinned=True), ] @@ -885,19 +885,19 @@ def test_soft_delete_unit(self): other_unit = self.create_unit_with_components([self.component_1], key="other") # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Delete the unit: - authoring_api.soft_delete_draft(unit_to_delete.publishable_entity_id) + content_api.soft_delete_draft(unit_to_delete.publishable_entity_id) unit_to_delete.refresh_from_db() # Now the draft unit is [soft] deleted, but the components, published unit, and other unit is unaffected: assert unit_to_delete.versioning.draft is None # Unit is soft deleted. assert unit_to_delete.versioning.published is not None self.component_1.refresh_from_db() assert self.component_1.versioning.draft is not None - assert authoring_api.get_components_in_unit(other_unit, published=False) == [Entry(self.component_1_v1)] + assert content_api.get_components_in_unit(other_unit, published=False) == [Entry(self.component_1_v1)] # Publish everything: - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) # Now the unit's published version is also deleted, but nothing else is affected. unit_to_delete.refresh_from_db() assert unit_to_delete.versioning.draft is None # Unit is soft deleted. @@ -905,8 +905,8 @@ def test_soft_delete_unit(self): self.component_1.refresh_from_db() assert self.component_1.versioning.draft is not None assert self.component_1.versioning.published is not None - assert authoring_api.get_components_in_unit(other_unit, published=False) == [Entry(self.component_1_v1)] - assert authoring_api.get_components_in_unit(other_unit, published=True) == [Entry(self.component_1_v1)] + assert content_api.get_components_in_unit(other_unit, published=False) == [Entry(self.component_1_v1)] + assert content_api.get_components_in_unit(other_unit, published=True) == [Entry(self.component_1_v1)] def test_snapshots_of_published_unit(self): """ @@ -916,43 +916,43 @@ def test_snapshots_of_published_unit(self): # At first the unit has one component (unpinned): unit = self.create_unit_with_components([self.component_1]) self.modify_component(self.component_1, title="Component 1 as of checkpoint 1") - before_publish = authoring_api.get_components_in_published_unit_as_of(unit, 0) + before_publish = content_api.get_components_in_published_unit_as_of(unit, 0) assert before_publish is None # Publish everything, creating Checkpoint 1 - checkpoint_1 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") + checkpoint_1 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 1") ######################################################################## # Now we update the title of the component. self.modify_component(self.component_1, title="Component 1 as of checkpoint 2") # Publish everything, creating Checkpoint 2 - checkpoint_2 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") + checkpoint_2 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 2") ######################################################################## # Now add a second component to the unit: self.modify_component(self.component_1, title="Component 1 as of checkpoint 3") self.modify_component(self.component_2, title="Component 2 as of checkpoint 3") - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Unit title in checkpoint 3", components=[self.component_1, self.component_2], created=self.now, ) # Publish everything, creating Checkpoint 3 - checkpoint_3 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") + checkpoint_3 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 3") ######################################################################## # Now add a third component to the unit, a pinned 📌 version of component 1. # This will test pinned versions and also test adding at the beginning rather than the end of the unit. - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title="Unit title in checkpoint 4", components=[self.component_1_v1, self.component_1, self.component_2], created=self.now, ) # Publish everything, creating Checkpoint 4 - checkpoint_4 = authoring_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") + checkpoint_4 = content_api.publish_all_drafts(self.learning_package.id, message="checkpoint 4") ######################################################################## # Modify the drafts, but don't publish: @@ -960,20 +960,20 @@ def test_snapshots_of_published_unit(self): self.modify_component(self.component_2, title="Component 2 draft") # Now fetch the snapshots: - as_of_checkpoint_1 = authoring_api.get_components_in_published_unit_as_of(unit, checkpoint_1.pk) + as_of_checkpoint_1 = content_api.get_components_in_published_unit_as_of(unit, checkpoint_1.pk) assert [cv.component_version.title for cv in as_of_checkpoint_1] == [ "Component 1 as of checkpoint 1", ] - as_of_checkpoint_2 = authoring_api.get_components_in_published_unit_as_of(unit, checkpoint_2.pk) + as_of_checkpoint_2 = content_api.get_components_in_published_unit_as_of(unit, checkpoint_2.pk) assert [cv.component_version.title for cv in as_of_checkpoint_2] == [ "Component 1 as of checkpoint 2", ] - as_of_checkpoint_3 = authoring_api.get_components_in_published_unit_as_of(unit, checkpoint_3.pk) + as_of_checkpoint_3 = content_api.get_components_in_published_unit_as_of(unit, checkpoint_3.pk) assert [cv.component_version.title for cv in as_of_checkpoint_3] == [ "Component 1 as of checkpoint 3", "Component 2 as of checkpoint 3", ] - as_of_checkpoint_4 = authoring_api.get_components_in_published_unit_as_of(unit, checkpoint_4.pk) + as_of_checkpoint_4 = content_api.get_components_in_published_unit_as_of(unit, checkpoint_4.pk) assert [cv.component_version.title for cv in as_of_checkpoint_4] == [ "Querying Counting Problem", # Pinned. This title is self.component_1_v1.title (original v1 title) "Component 1 as of checkpoint 3", # we didn't modify these components so they're same as in snapshot 3 @@ -1014,7 +1014,7 @@ def test_units_containing(self): with self.assertNumQueries(1): result = [ c.unit for c in - authoring_api.get_containers_with_entity(self.component_1.pk).select_related("unit") + content_api.get_containers_with_entity(self.component_1.pk).select_related("unit") ] assert result == [ unit1_1pinned, @@ -1029,7 +1029,7 @@ def test_units_containing(self): with self.assertNumQueries(1): result2 = [ c.unit for c in - authoring_api.get_containers_with_entity(self.component_1.pk, ignore_pinned=True).select_related("unit") + content_api.get_containers_with_entity(self.component_1.pk, ignore_pinned=True).select_related("unit") ] assert result2 == [unit4_unpinned, unit7_several] @@ -1044,15 +1044,15 @@ def test_get_components_in_unit_queries(self): self.component_2_v1, ]) with self.assertNumQueries(3): - result = authoring_api.get_components_in_unit(unit, published=False) + result = content_api.get_components_in_unit(unit, published=False) assert result == [ Entry(self.component_1.versioning.draft), Entry(self.component_2.versioning.draft), Entry(self.component_2.versioning.draft, pinned=True), ] - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) with self.assertNumQueries(3): - result = authoring_api.get_components_in_unit(unit, published=True) + result = content_api.get_components_in_unit(unit, published=True) assert result == [ Entry(self.component_1.versioning.draft), Entry(self.component_2.versioning.draft), @@ -1063,7 +1063,7 @@ def test_add_remove_container_children(self): """ Test adding and removing children components from containers. """ - unit, unit_version = authoring_api.create_unit_and_version( + unit, unit_version = content_api.create_unit_and_version( learning_package_id=self.learning_package.id, key="unit:key", title="Unit", @@ -1071,7 +1071,7 @@ def test_add_remove_container_children(self): created=self.now, created_by=None, ) - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1.versioning.draft), ] component_3, _ = self.create_component( @@ -1079,36 +1079,36 @@ def test_add_remove_container_children(self): title="Querying Counting Problem (3)", ) # Add component_2 and component_3 - unit_version_v2 = authoring_api.create_next_unit_version( + unit_version_v2 = content_api.create_next_unit_version( unit=unit, title=unit_version.title, components=[self.component_2, component_3], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) unit.refresh_from_db() assert unit_version_v2.version_num == 2 assert unit_version_v2 in unit.versioning.versions.all() # Verify that component_2 and component_3 is added to end - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_1.versioning.draft), Entry(self.component_2.versioning.draft), Entry(component_3.versioning.draft), ] # Remove component_1 - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title=unit_version.title, components=[self.component_1], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.REMOVE, + entities_action=content_api.ChildrenEntitiesAction.REMOVE, ) unit.refresh_from_db() # Verify that component_1 is removed - assert authoring_api.get_components_in_unit(unit, published=False) == [ + assert content_api.get_components_in_unit(unit, published=False) == [ Entry(self.component_2.versioning.draft), Entry(component_3.versioning.draft), ] @@ -1118,34 +1118,34 @@ def test_get_container_children_count(self): Test get_container_children_count() """ unit = self.create_unit_with_components([self.component_1]) - assert authoring_api.get_container_children_count(unit.container, published=False) == 1 + assert content_api.get_container_children_count(unit.container, published=False) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) unit_version = unit.versioning.draft - authoring_api.create_next_unit_version( + content_api.create_next_unit_version( unit=unit, title=unit_version.title, components=[self.component_2], created=self.now, created_by=None, - entities_action=authoring_api.ChildrenEntitiesAction.APPEND, + entities_action=content_api.ChildrenEntitiesAction.APPEND, ) unit.refresh_from_db() # Should have two components in draft version and 1 in published version - assert authoring_api.get_container_children_count(unit.container, published=False) == 2 - assert authoring_api.get_container_children_count(unit.container, published=True) == 1 + assert content_api.get_container_children_count(unit.container, published=False) == 2 + assert content_api.get_container_children_count(unit.container, published=True) == 1 # publish - authoring_api.publish_all_drafts(self.learning_package.id) + content_api.publish_all_drafts(self.learning_package.id) unit.refresh_from_db() - assert authoring_api.get_container_children_count(unit.container, published=True) == 2 + assert content_api.get_container_children_count(unit.container, published=True) == 2 # Soft delete component_1 - authoring_api.soft_delete_draft(self.component_1.pk) + content_api.soft_delete_draft(self.component_1.pk) unit.refresh_from_db() # Should contain only 1 child - assert authoring_api.get_container_children_count(unit.container, published=False) == 1 - authoring_api.publish_all_drafts(self.learning_package.id) + assert content_api.get_container_children_count(unit.container, published=False) == 1 + content_api.publish_all_drafts(self.learning_package.id) unit.refresh_from_db() - assert authoring_api.get_container_children_count(unit.container, published=True) == 1 + assert content_api.get_container_children_count(unit.container, published=True) == 1 # Tests TODO: # Test that I can get a [PublishLog] history of a given unit and all its children, including children that aren't diff --git a/tmp-openedx-learning/setup.py b/tmp-openedx-learning/setup.py new file mode 100755 index 000000000..04c545e3d --- /dev/null +++ b/tmp-openedx-learning/setup.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +""" +Package metadata for openedx-core. +""" +import os +import sys + +from setuptools import find_packages, setup + +# @@TODO update this before merging so that it's higher than the previous openedx-learning version, +# but lower than the new openedx-core version. +VERSION = "0.32.0" + +if sys.argv[-1] == 'tag': + print("Tagging the version on github:") + os.system("git tag -a %s -m 'version %s'" % (VERSION, VERSION)) + os.system("git push --tags") + sys.exit() + +setup( + name='openedx-learning', + version=VERSION, + description="""Open edX Learning Core (and Tagging)""", + long_description=""" +**This package has been renamed to [openedx-core](https://pypi.org/project/openedx-core/)!** +""" + author='David Ormsbee', + author_email='dave@axim.org', + url='https://github.com/openedx/openedx-learning', + packages=[], + include_package_data=True, + install_requires=["openedx-core"], + python_requires=">=3.11", + license="AGPL 3.0", + zip_safe=False, + keywords='Python edx', + classifiers=[ + 'Development Status :: 7 - Inactive', + 'Framework :: Django', + 'Framework :: Django :: 4.2', + 'Framework :: Django :: 5.2', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', + 'Natural Language :: English', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + ], +)