-
Notifications
You must be signed in to change notification settings - Fork 18
feat!: Make openedx_content and openedx_tagging flat, top-level APIs
#478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,13 +17,13 @@ jobs: | |
| - name: setup python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: '3.11' | ||
| python-version: '3.12' | ||
|
|
||
| - name: Install pip | ||
| run: pip install -r requirements/pip.txt | ||
|
|
||
| - name: Install Dependencies | ||
| run: pip install -r requirements/ci.txt | ||
| run: pip install -e . -r requirements/quality.txt | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor requirements rejigger:
|
||
|
|
||
| - name: Analyze imports | ||
| run: lint-imports | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,59 +1,64 @@ | ||
| # openedx_learning is intended to be a library of apps used across multiple | ||
| # This repo is intended to be a library of apps used across multiple | ||
| # projects, and we want to ensure certain dependency relationships. Please | ||
| # think through any changes you make to this file carefully, and don't just | ||
| # casually modify these linting rules to "fix the build". | ||
|
|
||
| [importlinter] | ||
| root_package = openedx_learning | ||
|
|
||
| # This is the most basic layering for openedx_learning. | ||
| # | ||
| # The "lib" package is meant for low level utilities, field definitions, and the | ||
| # like, so it's at the bottom layer. | ||
| # | ||
| # The "core" apps are meant to be the heart of our system, with foundational | ||
| # data models and plugin interfaces. It can rely on "lib" utilities. | ||
| # | ||
| # The "contrib" apps are meant to be apps that could easily be created outside | ||
| # of openedx_learning in a separate repository, but are bundled here because | ||
| # we think they'll be generally useful. These apps may call into "core" or "lib" | ||
| # apps, but not the other way around. The "core" apps should *never* import from | ||
| # "contrib". | ||
| [importlinter:contract:openedx_learning_layering] | ||
| name = Lib / Core / Contrib Layering | ||
| root_packages = | ||
| openedx_content | ||
| openedx_tagging | ||
| openedx_django_lib | ||
| openedx_core | ||
|
|
||
| # This is layering between our top-level src folders (mostly Django apps). | ||
| # This should be updated as new Django apps are added. | ||
| # It's possible this might need to grow into multiple rules. | ||
| [importlinter:contract:src_layering] | ||
| name = "top-level source folders are layered correctly" | ||
| type = layers | ||
| layers= | ||
| openedx_learning.contrib | ||
| openedx_learning.apps | ||
| openedx_learning.lib | ||
|
|
||
| # This is layering within our Authoring apps. Every new app should be added to | ||
| # this list when it it created. | ||
| [importlinter:contract:core_apps_layering] | ||
| name = Authoring App Dependency Layering | ||
| layers = | ||
| # Content is currently the highest-level thing in this repo. | ||
| # Over time, we may add apps "above" or "below" this. | ||
| openedx_content | ||
|
|
||
| # Tagging is very simple & fundamental. Should probably not depend on any other Django apps. | ||
| openedx_tagging | ||
|
|
||
| # Django utilities. Should not dependend on any of the real apps (above). | ||
| openedx_django_lib | ||
|
|
||
| # This just an empty shell package, to expose the __version__ number. | ||
| # Should not depend on anything. | ||
| openedx_core | ||
|
|
||
| # This is "applet" layering, within our Content djangoapp. | ||
| # Every new applet should be added to this list when it it created. | ||
| [importlinter:contract:content_applet_layering] | ||
| name = "openedx_content's internal applets are layered correctly" | ||
| type = layers | ||
| layers= | ||
| # The public authoring API is at the top–none of the apps should call to it. | ||
| openedx_learning.api.authoring | ||
| # The public API is at the top. None of the internal applets should call to it. | ||
| openedx_content.api | ||
|
|
||
| # The "backup_restore" app handle the new export and import mechanism. | ||
| openedx_learning.apps.openedx_content.applets.backup_restore | ||
| # The "backup_restore" applet handles the new export and import mechanism. | ||
| openedx_content.applets.backup_restore | ||
|
|
||
| # The "components" app is responsible for storing versioned Components, | ||
| # which is Open edX Studio terminology maps to things like individual | ||
| # The "components" applet is responsible for storing versioned Components, | ||
| # which in Open edX Studio terminology maps to things like individual | ||
| # Problems, Videos, and blocks of HTML text. This is also the type we would | ||
| # associate with a single "leaf" XBlock–one that is not a container type and | ||
| # has no child elements. | ||
| openedx_learning.apps.openedx_content.applets.components | ||
| openedx_content.applets.components | ||
|
|
||
| # The "contents" app stores the simplest pieces of binary and text data, | ||
| # The "contents" applet stores the simplest pieces of binary and text data, | ||
| # without versioning information. These belong to a single Learning Package. | ||
| openedx_learning.apps.openedx_content.applets.contents | ||
| openedx_content.applets.contents | ||
|
|
||
| # The "collections" app stores arbitrary groupings of PublishableEntities. | ||
| # The "collections" applet stores arbitrary groupings of PublishableEntities. | ||
| # Its only dependency should be the publishing app. | ||
| openedx_learning.apps.openedx_content.applets.collections | ||
| openedx_content.applets.collections | ||
|
|
||
| # The lowest layer is "publishing", which holds the basic primitives needed | ||
| # to create Learning Packages and manage the draft and publish states for | ||
| # various types of content. | ||
| openedx_learning.apps.openedx_content.applets.publishing | ||
| openedx_content.applets.publishing |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,21 +80,19 @@ selfcheck: ## check that the Makefile is well-formed | |
| @echo "The Makefile is well-formed." | ||
|
|
||
| ## Localization targets | ||
| # TODO: Need to audit these, and then actually tell openedx-translations | ||
| # to use them. https://github.com/openedx/openedx-learning/issues/483 | ||
|
|
||
| extract_translations: ## extract strings to be translated, outputting .mo files | ||
| rm -rf docs/_build | ||
| cd openedx_learning && ../manage.py makemessages -l en -v1 -d django | ||
| cd openedx_learning && ../manage.py makemessages -l en -v1 -d djangojs | ||
| cd openedx_tagging && ../manage.py makemessages -l en -v1 -d django | ||
| cd openedx_tagging && ../manage.py makemessages -l en -v1 -d djangojs | ||
| ./manage.py makemessages -l en -v1 -d django | ||
| ./manage.py makemessages -l en -v1 -d djangojs | ||
|
Comment on lines
+88
to
+89
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NOTE: I need to confirm that this works. Will do before merging, or will make a follow-up ticket if it isn't critical to this PR.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was actually broken because manage.py wasn't marked executable. I pushed a commit to fix that, but also pushed a commit to generally enable translations on this repo, which they currently aren't. #483 |
||
|
|
||
| compile_translations: ## compile translation files, outputting .po files for each supported language | ||
| cd openedx_learning && ../manage.py compilemessages | ||
| cd openedx_tagging && ../manage.py compilemessages | ||
| ./manage.py compilemessages | ||
|
|
||
| detect_changed_source_translations: | ||
| cd openedx_learning && i18n_tool changed | ||
| cd openedx_tagging && i18n_tool changed | ||
| cd src && i18n_tool changed | ||
|
|
||
| pull_translations: ## pull translations from Transifex | ||
| tx pull -a -f -t --mode reviewed | ||
|
|
@@ -103,8 +101,7 @@ push_translations: ## push source translation files (.po) from Transifex | |
| tx push -s | ||
|
|
||
| dummy_translations: ## generate dummy translation (.po) files | ||
| cd openedx_learning && i18n_tool dummy | ||
| cd openedx_tagging && i18n_tool dummy | ||
| cd src && i18n_tool dummy | ||
|
|
||
| build_dummy_translations: extract_translations dummy_translations compile_translations ## generate and compile dummy translation files | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,9 +28,7 @@ | |
| from django.db import transaction | ||
|
|
||
| # Model references to remove | ||
| from openedx_learning.apps.openedx_content.applets.components import api as components_api | ||
| from openedx_learning.apps.openedx_content.applets.contents import api as contents_api | ||
| from openedx_learning.apps.openedx_content.applets.publishing import api as publishing_api | ||
| from openedx_content import api as content_api | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure where |
||
|
|
||
| SUPPORTED_TYPES = ["problem", "video", "html"] | ||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -81,20 +79,20 @@ def load_course_data(self, learning_package_key): | |
| now = datetime.now(timezone.utc) | ||
| title = self.get_course_title() | ||
|
|
||
| if publishing_api.learning_package_exists(learning_package_key): | ||
| if content_api.learning_package_exists(learning_package_key): | ||
| raise CommandError( | ||
| f"{learning_package_key} already exists. " | ||
| "This command currently only supports initial import." | ||
| ) | ||
|
|
||
| with transaction.atomic(): | ||
| self.learning_package = publishing_api.create_learning_package( | ||
| self.learning_package = content_api.create_learning_package( | ||
| learning_package_key, title, created=now, | ||
| ) | ||
| for block_type in SUPPORTED_TYPES: | ||
| self.import_block_type(block_type, now) #, publish_log_entry) | ||
|
|
||
| publishing_api.publish_all_drafts( | ||
| content_api.publish_all_drafts( | ||
| self.learning_package.id, | ||
| message="Initial Import from load_components script" | ||
| ) | ||
|
|
@@ -116,13 +114,13 @@ def create_content(self, static_local_path, now, component_version): | |
| logger.warning(f' Static reference not found: "{real_path}"') | ||
| return # Might as well bail if we can't find the file. | ||
|
|
||
| content = contents_api.get_or_create_file_content( | ||
| content = content_api.get_or_create_file_content( | ||
| self.learning_package.id, | ||
| data=data_bytes, | ||
| mime_type=mime_type, | ||
| created=now, | ||
| ) | ||
| components_api.create_component_version_content( | ||
| content_api.create_component_version_content( | ||
| component_version, | ||
| content.id, | ||
| key=key, | ||
|
|
@@ -138,7 +136,7 @@ def import_block_type(self, block_type_name, now): # , publish_log_entry): | |
| # outside of tag declarations as well. | ||
| static_files_regex = re.compile(r"""['"]\/static\/(.+?)["'\?]""") | ||
| block_data_path = self.course_data_path / block_type_name | ||
| block_type = components_api.get_or_create_component_type("xblock.v1", block_type_name) | ||
| block_type = content_api.get_or_create_component_type("xblock.v1", block_type_name) | ||
|
|
||
| for xml_file_path in block_data_path.glob("*.xml"): | ||
| components_found += 1 | ||
|
|
@@ -154,7 +152,7 @@ def import_block_type(self, block_type_name, now): # , publish_log_entry): | |
| continue | ||
|
|
||
| display_name = block_root.attrib.get("display_name", "") | ||
| _component, component_version = components_api.create_component_and_version( | ||
| _component, component_version = content_api.create_component_and_version( | ||
| self.learning_package.id, | ||
| component_type=block_type, | ||
| local_key=local_key, | ||
|
|
@@ -165,14 +163,14 @@ def import_block_type(self, block_type_name, now): # , publish_log_entry): | |
|
|
||
| # Create the Content entry for the raw data... | ||
| text = xml_file_path.read_text('utf-8') | ||
| text_content, _created = contents_api.get_or_create_text_content( | ||
| text_content, _created = content_api.get_or_create_text_content( | ||
| self.learning_package.id, | ||
| text=text, | ||
| mime_type=f"application/vnd.openedx.xblock.v1.{block_type_name}+xml", | ||
| created=now, | ||
| ) | ||
| # Add the OLX source text to the ComponentVersion | ||
| components_api.create_component_version_content( | ||
| content_api.create_component_version_content( | ||
| component_version, | ||
| text_content.pk, | ||
| key="block.xml", | ||
|
|
||
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not critical to this PR, but this brings import-linter's python version up to date with unit tests.