Feature Plugins for edX Platform


Background

A major challenge with the edX platform today is that developers do not have a documented way to add new features to the web or mobile client. Worse still, there is no way to introduce such changes without modifying the edx-platform code base and then also updating the mobile clients. The primary exception is for features that can be purely implemented as XBlocks as they can be installed into the platform with minimal code changes. See Extending edX Platform for the documented ways in which developers can extend the platform today.

This is also a major roadblock to the community providing new features. If a contributor has a feature that they wish to share, then they have to make a pull request against the edX platform. This needs to be carefully reviewed by edX across all of the dimensions that we are concerned with (i18n, accessibility, performance, security, theming, test coverage etc). This is often too high a hurdle for other organizations to want to take on.

Another challenge is that it is impossible for an operational team running Open edX to add or remove features, because each feature only works with a version of the platform which contains its UI. This requires that a fork of edX be created and the requisite platform changes carefully merged into it so that the set of desired features can all be used at once. 

This document describes the steps required today to introduce new features, and then describes possible approaches to introduce a documented plug-in interface to satisfy a majority of such use cases. The goal of a plug-in interface is that features can be installed into a pre-existing edX platform and will start working with no code changes and minimal configuration. Then, if a feature is no longer needed it can simply be uninstalled and the platform will continue running without code changes. This would allow the Open edX community to develop and share Python packages that provide useful capabilities that edX does not necessarily want to incorporate into the platform. It also helps edX development too, as each developer can have experimental features that they install or remove without ending up with multiple forks of the platform.

Adding Features Today

There are five major ways to introduce a new feature to the edX platform, in approximate priority order:

  1. Create a new XBlock in its own Git repository
  2. Create a new IDA (independently deployable application)
    • If a feature doesn't need to be tightly integrated into Studio or the LMS then it can be built as an independent app
    • It can make use of the edX Platform API to get any platform data that it requires
    • IDAs are a good choice when:
      • They need to scale independently of the edX platform
      • They will be built, deployed and managed by a separate team
      • LMS and Studio should continue unaffected even if the application goes down
    • Consider the following before choosing to make a new IDA:
      • There is more operational complexity for the community to deploy and manage your feature
      • There are performance implications with introducing extra network calls
      • There is more application complexity when working with related data from external APIs, e.g. joins and referential integrity must be implemented in the application layer
      • Most features today need a tighter integration with the edX platform
    • Examples of IDAs:
  3. Create a new Django app in its own Git repository
    • New Django apps can be implemented in a separate repo and pip installed into the platform
    • A Django app in its own repository is a good choice when:
      • it provides new services that will be consumed by other parts of the platform
      • if it requires new UI for LMS or Studio, it provides it via extensions and does not introduce new URLs
        • See Web Fragments for the recommended way to add pluggable user interfaces
    • Consider the following:
      • If the feature has no UI then it may be better suited as an IDA if it doesn't require direct access to platform data models
      • See Managing multiple repo development for descriptions of some of the challenges involved with this approach
    • Examples of Django apps in their own repositories:
  4. Add a new Django app to the edX platform
  5. Update the platform directly
    1. This is the option of last resort
    2. When should you update the platform directly?
      • When the feature you are implementing updates core functionality in fundamental ways
      • When there are no extension points that you can use to build the functionality you need
    3. Consider the following before doing this:
      • Features which update the platform directly require the most scrutiny in code review
      • There is far more risk of unexpected fallout from your changes
      • Consider whether it is possible to introduce a platform extension point and then use it
      • The new code adds to the complexity of the monolith
      • Be sure to consult with edX 
    4. Examples:

However, even for options 2-4, the new capabilities need to be added to hard-coded lists in the platform. For example, there is a list of URLs, a list of tab types, a list of reports, a list of instructor dashboard components etc. This seems simple to update, but it means that the platform has to be changed to receive each new feature.

Use Cases

Here is a quick summary of how several large features were implemented. For more details, see the Appendix: Recent Feature Audit.

1. CCX

PR 6636

CCX introduced some core platform changes along with a new CCX Django app:

A new CCX role was added to the platform

The instructor dashboard was updated to understand this new role

There were pieces that had to be hard-coded into the platform which would more naturally have lived in the Django app:

New configuration settings were added to lms/envs/common.py

New course settings were added to InheritanceMixin in common/lib/xmodule/xmodule/modulestore/inheritance.py

A new "CCX" tab was added to common/lib/xmodule/xmodule/tabs.py

  • The URL for the new tab was added to lms/urls.py
  • Mako templates for the new tab were added to lms/templates
  • CSS for the new tab was added to lms/static/sass
  • The JavaScript Backbone views were added to lms/static/js

Tests were added for the new UI

  • Jasmine tests were added to lms/static/js/spec
  • Jasmine HTML fixtures were added to lms/static/js/fixtures
  • Bok Choy tests were added to common/tests/acceptance

2. Cohorted Courseware

PR 6299

Cohorted Courseware was a platform-wide PR so a substantial portion of the code was inevitably in the platform itself:

A new pluggable user partition scheme was added allowing any Django app to add new group types

  • A cohorted scheme was added to support "Content Groups"

The new "Content Group" concept was added into the pre-existing "Group Configurations" view in Studio

Cohort information was added to the "Profile Information" CSV report

There were pieces that would have been cleaner if they had been implemented as plug-ins:

A new "Edit Visibility" modal was added to every xblock in Studio via a new mixin. 

  • This would have been better as a pluggable tab on the existing editor.

A new "Cohorts" tab was hard-coded into the Instructor Dashboard

  • The URL for the new tab was added to lms/urls.py
  • Mako templates for the new tab were added to lms/templates
  • CSS for the new tab was added to lms/static/sass

Tests were added for the new UI

  • The JavaScript Backbone views were added to lms/static/js
  • Jasmine tests were added to lms/static/js/spec
  • Bok Choy tests were added to common/tests/acceptance

3. ORA 2

https://github.com/edx/edx-ora2

ORA 2 plugs cleanly into the platform as an xblock:

This works seamlessly to introduce a new courseware component

Only very minor changes had to be made to edx-platform

  • Referencing the current ORA 2 release commit hash in requirements/edx/github.txt
  • Adding the xblock to the list of advanced problem types used by Studio

Living outside of the platform had challenges:

There is no way to extend the platform SASS, so instead independent SASS was written which is included in addition. This won't play well with theming.

XBlocks do not support RequireJS 

  • This made it hard to ensure that the block has the dependencies it needs
  • It also required that a custom mechanism be used to modularize the code into smaller files 

Studio authoring view is not truly integrated for xblocks

  • ORA 2 had to fake out the Save and Cancel buttons as Studio can only save changes to xmodules today

The only option for admin views is through the Studio authoring view or through staff-level features added to the courseware block

  • Adding admin views inside an xblock is powerful but constrained by the space provided
    • It could be better to have links from the xblock to a full-page admin view (maybe as a tab on the instructor dashboard)
  • It would be useful to add ORA 2 reports to the "Data Downloads" tab

There was no easy way to write integration tests that get run with the platform release

  • Separate sandbox was provisioned for automated tests but it stopped working and has been disabled

4. Student Notes

PR 6610

Student Notes was built as a mostly independent Django app living in the LMS:

The Django app lives here: https://github.com/edx/edx-platform/tree/master/lms/djangoapps/edxnotes

A new Notes Service was introduced that is independently deployable

There were many aspects that were not able to live in the edxnotes Django app:

New configuration settings were added to lms/envs/common.py

New course settings were added to InheritanceMixin in common/lib/xmodule/xmodule/modulestore/inheritance.py

The core HTML component was updated to render notes inline

The AnnotatorJS library was added as a vendor library to common/static/js/vendor

A new LMS "Notes" tab was added to common/lib/xmodule/xmodule/tabs.py

  • The URL for the new tab was added to lms/urls.py
  • Mako templates for the new tab were added to lms/templates
  • CSS for the new tab was added to lms/static/sass
  • The JavaScript Backbone views were added to lms/static/js

Tests were written for all the new UI

  • Jasmine tests were added to lms/static/js/spec
  • Jasmine HTML fixtures were added to lms/static/js/fixtures
  • Bok Choy tests were added to common/tests/acceptance

5. Proctored Exams

Proctored exams were implemented as a standalone Python library along with some fairly substantial integration into edx-platform

The Python library lives here: https://github.com/edx/edx-proctoring

Some notes:

6. Student Data Tab

Here is an upcoming use case from Braden MacDonald:

We have a custom XBlock used throughout certain Harvard courses, and their instructors want to export all the student data associated with those XBlocks (stored in edx-submissions). So we want to be able to add an instructor-only tab to the course in the LMS, where we can put a new UI for instructors to generate .CSV exports of that data.

Plug-in Architecture

There are a number of changes that were needed to support the ability for a new feature to be implemented purely within its own Django app:

Stevedore extension points should be provided for each of the common UX changes that need to be made

The platform needs to be extended to support pluggable user interfacesDONE

XBlocks should be extended to provide more capabilities

  • Course-scoped fields for configuration settings
  • Ability to add an xblock as a course tab
  • Studio editing tabs for xblocks
    • TNL-850 - Getting issue details... STATUS
    • TNL-851 - Getting issue details... STATUS
  • Admin views for both Studio and LMS
  • Need built-in support for RequireJS or AMD compliant dependency management

The Django asset pipeline needs to be improved DONE

  • Have the pipeline pick up assets from all installed Django apps (to support CDNs, minification etc)
  • RequireJS Optimizer support should be added to the LMS

There are further complications if we want to move the Django app out to its own repo:

How do we write integration tests?

How can SASS build upon the base SASS from edx-platform?

Appendix: Recent Feature Audit

The following is an audit of four recent features (CCX, Cohorted Courseware, ORA 2 and Student Notes) which enumerates all of the ways in which they extend the platform. The hope is that this gives a fairly representative sample of the types of changes that might need to be pluggable.

Here are the various types of UX changes that were seen in the relevant features. Note that the "Mobile Support" column describes whether such a change will automatically be supported by mobile, or whether it will require additional changes to the mobile app.

UX ChangeDescriptionExamplesMobile SupportCohorted CoursewareORA 2Student NotesCCXProctored Exams
Feature FlagAllow the feature to be enabled or disabledENABLED_EDXNOTES flag(tick)(error)(error)(tick)(tick)(tick)
ServiceIntroduce a new service that other parts of the platform can consume

edX Notes Service

edX Submissions Service

(tick) (but clients need to be updated)(error)(tick)(tick)(error)(tick)
REST APIIntroduce new REST APIs that allow new data models to be consumed by other parts of the platformUser API, edX Notes Service API(tick) (but clients need to be updated)(tick)(tick)(tick)(tick)(tick)
Core behavior changeChange how core parts of the platform functionCohorted content, CCX due dates(tick)(tick)(error)(error)(tick)(tick)
New course roleIntroduce a new role for users within a courseCCX coach(error)(error)(error)(error)(tick)(error)
New group typeIntroduce a new kind of grouping of usersCohorts, Content Groups(error)(tick)(error)(error)(error)(error)
New LMS pageAdd a new page to the top level of the LMSLearner profile page(error)
(error)(error)(error)(error)(error)
New Studio pageAdd a new page to the top level of Studio"Group Configurations" pageN/A(tick)(error)(error)(error)(error)
Update Studio course settings viewUpdate the "Settings" view to allow editing of new featuresCourse Licenses(error)(error)(error)(error)(error)(error)
Courseware componentAdd a new component type that an author can add to their course"Peer Assessment" component(tick) (with some caveats)(error)(tick)(error)(error)(error)
LMS tab

There are two different classes of tabs: those that are shown per role, and those that are shown when a feature is enabled.

Note: these are currently shown as tabs in the web version of the LMS, but this concept should be generalized to work for mobile and future non-tab implementations.

"Notes" tab, "CCX" tab(error)(error)(error)(tick)(tick)(error)
Instructor admin componentAdd a new component that instructors can use to administer their course in the LMS"Cohorts" tab(error)
(tick)(error)(error)(tick)(tick)
Instructor reportsAdd a new report that instructors can download"Download Profile Information"(error)
(tick)(error)(error)(error)(error)
Course feature flagAllow the feature to be enabled per course (by author or instructor)"Enable Student Notes", "Enable Cohorts"(error)(tick)(error)(tick)(tick)(tick)
Studio advanced settingAdd a new setting that authors can use to configure aspects of their course"Student Notes Visibility"N/A(error)(error)(tick)(error)(tick)
Studio authoring viewAdd a new view to allow authors to visually configure aspects of an xblock"Edit Visibility", ORA 2 settingsN/A(tick)(tick)(error)(error)(tick)
Studio typeIntroducing a new type requires providing an editor component"Language" pickerN/A(error)(error)(error)(error)(error)

Here are the types of implementation changes that had to be made to introduce the above features:

ChangeDescriptionUpdate RequiredMobile SupportCohorted CoursewareORA 2Student NotesCCXProctored Exams
Configuration SettingsAllow the feature to be enabled or disabledUpdate lms/envs/common.py (or cms)Not picked up by mobile today(tick)(error)(tick)(tick)(tick)
URLsMost features require new LMS and/or Studio URLsUpdate lms/urls.py (or cms)Not picked up by mobile today(tick)(error)(tick)(tick)(tick)
Django modelsAdd new persisted data models, or update existing onesAdd models to your Django app, or update existing ones in the platform(tick)(tick)(tick)(tick)(tick)(tick)
Modulestore logicChanges needed in core modulestore logicUpdate xmodule/modulestore(tick)(tick)(error)(error)(error)(error)
Courseware componentAdd a new component type that an author can add to their course.

Implement an xblock (some caveats that still require xmodule).

Update the Studio configuration to allow the user to create such a component.

(tick) (with some caveats)(error)(tick)(error)(error)(error)
Course-scoped fieldsAdd a new field that can be set per-course.Add fields to xmodule/course_module.py and sometimes xmodule/modulestore/inheritance.py(tick)(tick)(error)(tick)(error)(tick)
User partition schemeProvide a new way to partition users into groups/teams/cohorts etcProvide a plug-in of type openedx.user_partition.scheme(tick)(tick)(error)(error)(error)(error)
ImagesImages need to get into the pipeline to end up served through the CDNAdd new images to lms/static/images (or cms)(tick)(error)(error)(error)(error)(error)
CSSIntroduce new styling for custom componentsAdd new SASS files to lms/static/sass (or cms)(tick) (for web views)(tick)(question) (see ORA section below)(tick)(tick)(tick)
Mako TemplateInclude new HTML templates to be returned when serving a new pageAdd new templates to lms/templates (or cms)(tick)(tick)(error)(tick)(tick)(tick)
Third-party JS librariesAdd new libraries needed by the new JS UI, e.g. AnnotatorJS

Add the files to common/static/js/vendor

Add the new dependencies to RequireJS and the Jasmine tests

(tick)(error)(error)(tick)(error)(error)
JavaScriptAdd new custom JS code to support more dynamic browser interactionsAdd new JS files to lms/static/js (or cms). Add new dependencies to(tick)(tick)(question)(tick)(tick)(error)
Unit TestsAdd new Python or Jasmine code to test your new feature

Add new tests to your Django app

Update paver to know about the new directories to run the tests, for coverage etc

(error)(tick)(tick)(tick)(tick)(tick)
Integration TestsAdd new Bok Choy tests that verify your feature works when running inside the platform

Add new tests to common/test/acceptance

Note: ORA 2 has Bok Choy tests in its own repo, but no easy way to run them

(error)(tick)(question)(tick)(tick)(tick)

See Also

PR 7610: Django extension spike

PR 7937: Add "Teams" tab

Thoughts on LMS front end architecture

How to add a new feature to LMS or Studio

Adding a new course tab