Skip to content

Commit

Permalink
refactor!: move openedx_learning.core -> openedx_learning.apps.authoring
Browse files Browse the repository at this point in the history
All the Django apps that were in openedx_learning.core were specific to
authoring library and course content: publishing, contents, components.
As time goes on, we will likely add other groupings of apps, e.g.
"learner", "personalization", "grading", etc.

Important compatibility notes:

1. This is a breaking change, and edx-platform will need to be updated
   to point to the new locations for apps.
2. This should not affect the table names or migrations,  since all app
   names remain the same, and we use the explicit repo-level "oel_"
   prefix anyway.

feat!: remove placeholder rest_api app

This app was never really functional, and was just put in as a skeleton
to demonstrate a possible approach to adding a REST API to the
openedx_learning package as a distinct app (as opposed to making a
separate API per app). That being said, there's no current use case to
expose a REST API from any of the authoring apps, and it's not clear
whether we'd really want a top level rest_api app or separate rest_api
apps for different groups of apps (e.g. "the REST API for authoring").

So just remove this now to reduce confusion.

feat: create new openedx_learning.api.authoring module

This module will aggregate the public APIs of authoring related apps,
making it easier for consumers to find what they're looking for, and
allowing us to refactor apps if necessary without breaking the API
contracts.
  • Loading branch information
ormsbee committed May 13, 2024
1 parent 8ad5329 commit ca18bec
Show file tree
Hide file tree
Showing 55 changed files with 144 additions and 118 deletions.
12 changes: 6 additions & 6 deletions .importlinter
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@ name = Lib / Core / Contrib Layering
type = layers
layers=
openedx_learning.contrib
openedx_learning.core
openedx_learning.apps
openedx_learning.lib

# This is layering within our Core apps. Every new Core app should be added to
# 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 = Core App Dependency Layering
name = Authoring App Dependency Layering
type = layers
layers=
# The "components" app is responsible for storing versioned Components,
# which is 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.core.components
openedx_learning.apps.authoring.components
# The "contents" app stores the simplest pieces of binary and text data,
# without versioning information. These belong to a single Learning Package.
openedx_learning.core.contents
openedx_learning.apps.authoring.contents
# 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.core.publishing
openedx_learning.apps.authoring.publishing
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Parts
~~~~~

* ``openedx_learning.lib`` is for shared utilities, and may include things like custom field types, plugin registration code, etc.
* ``openedx_learning.core`` contains our Core Django apps, where foundational data structures and APIs will live.
* ``openedx_learning.apps`` contains our Learning Core Django apps, where foundational data structures and APIs will live.
* ``openedx_tagging.core`` contains the core Tagging app, which provides data structures and apis for tagging Open edX objects.

App Dependencies
Expand Down
7 changes: 7 additions & 0 deletions docs/api_reference.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
API Reference
=============

.. automodule:: openedx_learning.api.authoring
:members:
:imported-members:
:show-inheritance:
13 changes: 11 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,17 @@ def on_init(app): # pylint: disable=unused-argument
# If we are, assemble the path manually
bin_path = os.path.abspath(os.path.join(sys.prefix, 'bin'))
apidoc_path = os.path.join(bin_path, apidoc_path)
check_call([apidoc_path, '-o', docs_path, os.path.join(root_path, 'openedx_learning'),
os.path.join(root_path, 'openedx_learning/migrations')])
check_call(
[
apidoc_path,
'-o',
docs_path,
'-H',
"API Reference",
os.path.join(root_path, 'openedx_learning',),
os.path.join(root_path, 'openedx_learning', 'migrations'),
]
)


def setup(app):
Expand Down
9 changes: 4 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
openedx-learning
====================
Open edX Learning Core
======================

An experiment.
The boring, foundational bits of a learning platform that are hard to get right at scale. Currently being developed for content storage.

Contents:

Expand All @@ -17,11 +17,10 @@ Contents:
getting_started
testing
internationalization
modules
changelog
api_reference
decisions


Indices and tables
==================

Expand Down
6 changes: 3 additions & 3 deletions olx_importer/management/commands/load_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
from django.db import transaction

# Model references to remove
from openedx_learning.core.components import api as components_api
from openedx_learning.core.contents import api as contents_api
from openedx_learning.core.publishing import api as publishing_api
from openedx_learning.apps.authoring.components import api as components_api
from openedx_learning.apps.authoring.contents import api as contents_api
from openedx_learning.apps.authoring.publishing import api as publishing_api

SUPPORTED_TYPES = ["problem", "video", "html"]
logger = logging.getLogger(__name__)
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions openedx_learning/api/authoring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
This is the public API for content authoring in Learning Core.
This is the single ``api`` module that code outside of the
``openedx_learning.apps.authoring.*`` package should import from. It will
re-export the public functions from all api.py modules of all authoring apps. It
may also implement its own convenience APIs that wrap calls to multiple app
APIs.
"""
# These wildcard imports are okay because these api modules declare __all__.
from ..apps.authoring.publishing.api import *
from ..apps.authoring.contents.api import *
from ..apps.authoring.components.api import *
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@
from .models import Component, ComponentType, ComponentVersion, ComponentVersionContent


# The public API that will be re-exported by openedx_learning.apps.authoring.api
# is listed in the __all__ entries below. Internal helper functions that are
# private to this module should start with an underscore. If a function does not
# start with an underscore AND it is not in __all__, that function is considered
# to be callable only by other apps in the authoring package.
__all__ = [
"create_component",
"create_component_version",
"create_next_version",
"create_component_and_version",
"get_component",
"get_component_by_key",
"component_exists_by_key",
"get_components",
"create_component_version_content",
]

def get_or_create_component_type(namespace: str, name: str) -> ComponentType:
"""
Get the ID of a ComponentType, and create if missing.
Expand Down Expand Up @@ -308,6 +325,9 @@ def look_up_component_version_content(
Can raise a django.core.exceptions.ObjectDoesNotExist error if there is no
matching ComponentVersionContent.
This API call was only used in our proof-of-concept assets media server, and
I don't know if we wantto make it a part of the public interface.
"""
queries = (
Q(component_version__component__learning_package__key=learning_package_key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class ComponentsConfig(AppConfig):
Configuration for the Components Django application.
"""

name = "openedx_learning.core.components"
verbose_name = "Learning Core: Components"
name = "openedx_learning.apps.authoring.components"
verbose_name = "Learning Core > Authoring > Components"
default_auto_field = "django.db.models.BigAutoField"
label = "oel_components"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

from django.db import models

from ...lib.fields import case_sensitive_char_field, immutable_uuid_field, key_field
from ...lib.managers import WithRelationsManager
from ....lib.fields import case_sensitive_char_field, immutable_uuid_field, key_field
from ....lib.managers import WithRelationsManager
from ..contents.models import Content
from ..publishing.model_mixins import PublishableEntityMixin, PublishableEntityVersionMixin
from ..publishing.models import LearningPackage
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,21 @@
from django.core.files.base import ContentFile
from django.db.transaction import atomic

from ...lib.fields import create_hash_digest
from ....lib.fields import create_hash_digest
from .models import Content, MediaType

# The public API that will be re-exported by openedx_learning.apps.authoring.api
# is listed in the __all__ entries below. Internal helper functions that are
# private to this module should start with an underscore. If a function does not
# start with an underscore AND it is not in __all__, that function is considered
# to be callable only by other apps in the authoring package.
__all__ = [
"get_or_create_media_type",
"get_content",
"get_or_create_text_content",
"get_or_create_file_content",
]


def get_or_create_media_type(mime_type: str) -> MediaType:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ContentsConfig(AppConfig):
Configuration for the Contents Django application.
"""

name = "openedx_learning.core.contents"
verbose_name = "Learning Core: Contents"
name = "openedx_learning.apps.authoring.contents"
verbose_name = "Learning Core > Authoring > Contents"
default_auto_field = "django.db.models.BigAutoField"
label = "oel_contents"
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from django.core.validators import MaxValueValidator
from django.db import models

from ...lib.fields import MultiCollationTextField, case_insensitive_char_field, hash_field, manual_date_time_field
from ...lib.managers import WithRelationsManager
from ....lib.fields import MultiCollationTextField, case_insensitive_char_field, hash_field, manual_date_time_field
from ....lib.managers import WithRelationsManager
from ..publishing.models import LearningPackage


Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@
PublishLogRecord,
)

# The public API that will be re-exported by openedx_learning.apps.authoring.api
# is listed in the __all__ entries below. Internal helper functions that are
# private to this module should start with an underscore. If a function does not
# start with an underscore AND it is not in __all__, that function is considered
# to be callable only by other apps in the authoring package.
__all__ = [
"get_learning_package",
"get_learning_package_by_key",
"create_learning_package",
"update_learning_package",
"learning_package_exists",
"create_publishable_entity",
"create_publishable_entity_version",
"get_publishable_entity",
"get_publishable_entity_by_key",
"get_last_publish",
"get_all_drafts",
"get_entities_with_unpublished_changes",
"get_entities_with_unpublished_deletes",
"publish_all_drafts",
"get_draft_version",
"get_published_version",
"set_draft_version",
"soft_delete_draft",
"reset_drafts_to_published",
"register_content_models",
]

def get_learning_package(learning_package_id: int, /) -> LearningPackage:
"""
Expand Down Expand Up @@ -105,6 +132,13 @@ def update_learning_package(
return lp


def learning_package_exists(key: str) -> bool:
"""
Check whether a LearningPackage with a particular key exists.
"""
return LearningPackage.objects.filter(key=key).exists()


def create_publishable_entity(
learning_package_id: int,
/,
Expand Down Expand Up @@ -167,13 +201,6 @@ def get_publishable_entity_by_key(learning_package_id, /, key) -> PublishableEnt
)


def learning_package_exists(key: str) -> bool:
"""
Check whether a LearningPackage with a particular key exists.
"""
return LearningPackage.objects.filter(key=key).exists()


def get_last_publish(learning_package_id: int, /) -> PublishLog | None:
return PublishLog.objects \
.filter(learning_package_id=learning_package_id) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class PublishingConfig(AppConfig):
Configuration for the publishing Django application.
"""

name = "openedx_learning.core.publishing"
verbose_name = "Learning Core: Publishing"
name = "openedx_learning.apps.authoring.publishing"
verbose_name = "Learning Core > Authoring > Publishing"
default_auto_field = "django.db.models.BigAutoField"
label = "oel_publishing"
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion openedx_learning/contrib/media_server/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import FileResponse, Http404

from openedx_learning.core.components.api import look_up_component_version_content
from openedx_learning.apps.authoring.components.api import look_up_component_version_content


def component_asset(
Expand Down
14 changes: 0 additions & 14 deletions openedx_learning/rest_api/apps.py

This file was deleted.

6 changes: 0 additions & 6 deletions openedx_learning/rest_api/urls.py

This file was deleted.

30 changes: 0 additions & 30 deletions openedx_learning/rest_api/v1/components.py

This file was deleted.

10 changes: 0 additions & 10 deletions openedx_learning/rest_api/v1/urls.py

This file was deleted.

8 changes: 4 additions & 4 deletions projects/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@
"django.contrib.admin",
"django.contrib.admindocs",
# Learning Core Apps
"openedx_learning.core.components.apps.ComponentsConfig",
"openedx_learning.core.contents.apps.ContentsConfig",
"openedx_learning.core.publishing.apps.PublishingConfig",
"openedx_learning.apps.authoring.components.apps.ComponentsConfig",
"openedx_learning.apps.authoring.contents.apps.ContentsConfig",
"openedx_learning.apps.authoring.publishing.apps.PublishingConfig",
# Learning Contrib Apps
"openedx_learning.contrib.media_server.apps.MediaServerConfig",
# Apps that don't belong in this repo in the long term, but are here to make
# testing/iteration easier until the APIs stabilize.
"olx_importer.apps.OLXImporterConfig",
# REST API
"rest_framework",
"openedx_learning.rest_api.apps.RESTAPIConfig",

# django-rules based authorization
'rules.apps.AutodiscoverRulesConfig',
# Tagging Core Apps
Expand Down
1 change: 0 additions & 1 deletion projects/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
path("admin/doc/", include("django.contrib.admindocs.urls")),
path("admin/", admin.site.urls),
path("media_server/", include("openedx_learning.contrib.media_server.urls")),
path("rest_api/", include("openedx_learning.rest_api.urls")),
path("tagging/rest_api/", include("openedx_tagging.core.tagging.urls")),
path('__debug__/', include('debug_toolbar.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Loading

0 comments on commit ca18bec

Please sign in to comment.