[Spec Memo] API-Based Management of edX Course Blocks, Outlines and Settings (MVP)
Problem statement
We seek to empower course owners to maintain their content on any platform, while programmatically co-hosting that content on different OpenEdx instances. This includes the following:
The ability to replicate existing course content from an external source to an existing open edX CMS course run
The ability to efficiently maintain that replicated content as changes to the source occur naturally over time
2U is driving toward an 80/20 solution that may not be able to replicate every distinct edX course run feature possible, but that with bounded effort allows a great many use cases to be accomplished programmatically within the 2023 calendar year.
MVP Definition
From an MVP perspective, it is essential that co-hosting work with:
Hierarchically organized course runs
Course runs with
HTML components (containing static assets like Images)
Video components with subtitles
Problem components to be solved by learner (single answer, multiple choice, open response)
Graded assessments, with rubrics when appropriate
Discussion forums
LTI-consumer components
Course-wide and unit-specific settings that govern content presentation; e.g.,
Course run content may be hidden based on viewer’s role relative to course
Course run content may not yet be released (date driven)
…
This capability must also extend to updates made to course runs, and not be limited to one-time ports. That is, content owners ought to be able to update from the source on-demand, without loss on the Open edX platform of any course run information.
Technical Decisions
At this time, 2U has made three decisions:
Adopt OLX as the basis for how content is communicated to the edX platform.
This is the most complex and consequential of the decisions. Factors that most influenced this decision include:
Desire not to have to support multiple representations
Reduction of work and risk by not having to design an abstraction layer, get buy-in for that layer, and then implement that layer (e.g., a new json-based representation)
Placing the burden of resolving capability gaps or paradigm incompatibilities closest to content owners, as it is content owners who decide what compromises are acceptable
Rely on existing readthedocs documentation to make new studio content management APIs immediately usable without need of new documentation for all remaining XBlock types.
2U will draft guidance on how to set XBlock settings with OLX for all XBlock types that are in the MVP Definition
Enhance existing import/export API and xblock APIs to enable the maintenance of both course runs and components.
This is to enable efficient implementation of both course-wide and fine-grained use cases
Content data transfers
An API exposing content creation capabilities may be written in terms of edX-native constructs, partner-native-constructs, or in terms of a generic capabilities abstraction. We propose to port content using OLX constructs, the edX-native choice.
Our initial approach will be OLX-centric, without abstracting the content (XBlock) types we offer into some generic API. This will place the paradigm-shift burden on our partners, asking them to either “thin publish” units with LTI, or to “thick publish” units by converting them into an OLX representation.
Working with OLX this way entails using the readthedocs documentation to create sample content on studio, exporting that sample content, and then using the generated OLX as a template for the porting work itself. (More on this in the “Self-documenting API” section below).
Self-documenting API
Course run content and settings will be encoded in this API with Open Learning XML (OLX) encoding. We argue here that this decision affords instant breadth in terms of the types of content and settings that the API offers, but the decision admittedly shifts a complexity burden onto content owners.
edX Studio allows you to export a course to a folder of hierarchically related OLX files, and, likewise, to import a course from such a folder. The structure of those files parallels the course structure. Moreover, contents of the OLX files include the names and values of settings that configure the individual units. This means that the following action sequence reveals what settings need to be set to what values to achieve a desired effect in a course unit:
Look up the desired effect in the “Building and Running an edX Course” reference (aka, readthedocs) to learn how to add that to a course in Studio
Add the effect in a demo course
Export the course
Find the OLX file within that course corresponding to the unit with your effect
Read the OLX contents to learn the tag that recruits the appropriate XBlock, as well as the name of the settings and the values you applied through the UI
Use this knowledge to create OLX for new units you wish to programmatically create.
While the above is undoubtedly a bit cumbersome, it does mean that course team, armed with the ReadTheDocs documentation, can leverage edX’s import/export capability to create and configure courses using whatever mix of XBlock types fits the need. For this procedure to be practical it needs to be speedy, something the current export is not; consequently, a partial export extension to exporting capabilities is included in the import/export extensions section below (in the “Read aka export” row). With the partial export extension, it will be possible to limit exporting to just the portion of a course run under exploration.
Alternatively, a simpler and more direct way to discover available OLX tags for a piece of content may exist and is under study. If available and suitable, it will be offered as well.
All that’s needed for a “self-documenting” solution is an API that allows creation and editing of course run content via OLX files, a-la the import/export capability. It is self-documenting in that the API documentation need not document an XBlock type’s idiosyncrasies for the type to be usable via the API.
Import/export extensions
The existing import/export functionality applies at the course run level only: as and end user, you either export an entire course run, or you import (and replace) an entire course run. The course run you import content into must already exist, and prior content is replaced.
The above is too restrictive for 2U goals. First, it’s slow. Even on an initial port, where the entire course is being worked on, there may be some sections that call for tuning. A slow, whole-course import is not nimble enough for that type of tweaking. Second, whole-course imports for maintenance operations would overwrite settings that may have been tuned after the original import, and this is unacceptable.
We propose to augment the whole-course import mechanism to allow CRUD operations acting at a sub-tree scope within a course run.
Create |
|
Read (aka, export) |
|
Update |
|
Delete |
|
edX courses runs are composed of XBlocks. An XBlock is a package that defines how a component is presented to the student, how it behaves, and how it is stored. The above discussion based on OLX representations frames porting work in term of existing XBlock types. While that’s a valid assumption in most cases, new XBlock types may be added as a measure of last resort to resolve capability gaps or paradigm incompatibilities.
Operational “-ilities”
Concurrency | API-driven changes to a course run may need to occur in the context of a locking mechanism: programmatic access to a course run will be limited to one client at a time. Locks will time out after some period of inactivity. 2U acknowledges that lock maintenance is expensive and error prone, other options are also being explored. |
Error Handling |
|
Observability | Analytics will be collected by 2U-internal Observability Tools to identify usage levels by application ID, by API, by XBlock type, and by course run. The analytics is to be rich enough to measure historical API performance and to allow detection of congestion problems. Visibility into ported XBlock types and the settings that apply to them will exist. This is intended to assist with bug reports; also, it will be useful to know whether a bug is a first instance or not, and to have breadcrumbs as to earlier usage. |
Resiliency |
|
Scalability | The design is to be scalable for performance/throughput, but need not be dynamically scalable. That is, static reservation of more resources for programmatic content creation and editing is acceptable. |
Security |
|
Testability |
|
Risks
Capability gaps
A randomly selected course from another CMS may call for capabilities not currently supported in any edX platform XBlock. Porting such a course would either be a lossy process or would require the development of new XBlocks. Our project plan does not currently provide for creation of new XBlocks. How often this will prevent a course from being ported is unknown.
Paradigm-shift incompatibilities
Per the “Solution Alternatives” section below, mapping on-edX course paradigms onto edX course paradigms may sometimes be lossy.
In extreme cases, a capability required to port a given course may be offered by an edX platform XBlock, but its paradigm may differ so much from the source platform’s paradigm as to be effectively unusable. This results in the same risk as with a capability gap.
Inversion of Control
This API will provide more scriptable control to content creators and with it comes more opportunity to do the wrong thing. There is a risk that operators will “shoot themselves in the foot” in a very bad way. The Operational “-ilities” section attempts to mitigate as many of those risks as possible within 2U’s scope of effort.