Skip to content

Commit 37e269e

Browse files
committed
feat: add uai content customization plugin
1 parent f9f015e commit 37e269e

20 files changed

Lines changed: 1547 additions & 4 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Changelog
2+
=========
3+
4+
[0.1.0] - 2026-05-15
5+
---------------------
6+
7+
Added
8+
~~~~~
9+
- Initial release.
10+
- Management command ``generate_uai_courses`` to generate industry- and
11+
length-specific UAI course variants from two CSV files using Open edX
12+
modulestore APIs.
13+
- CSV utilities for parsing customized video metadata and Open edX video
14+
asset exports, and for mapping video file names to Open edX video IDs.
15+
- Modulestore helper functions for course, section, subsection, unit, and
16+
video block creation.
17+
- ``--dry-run`` flag for safe inspection without writing to the modulestore.
18+
- Full test suite for CSV utilities and the management command.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Copyright (C) 2026 MIT Open Learning
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
* Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
OL Open edX UAI Content Customization
2+
======================================
3+
4+
An Open edX CMS plugin that automates the generation of industry- and
5+
length-specific UAI course variants using direct Open edX modulestore APIs.
6+
7+
Version Compatibility
8+
---------------------
9+
10+
Supports Open edX releases from **Sumac** and onwards.
11+
12+
Installing The Plugin
13+
---------------------
14+
15+
For detailed installation instructions, please refer to the
16+
`plugin installation guide <../../docs#installation-guide>`_.
17+
18+
Installation required in:
19+
20+
* CMS
21+
22+
Overview
23+
--------
24+
25+
Original UAI courses are transformed into multiple custom courses per industry
26+
and length combination:
27+
28+
+--------------+------+-------------+--------+
29+
| Industry | Code | Length code | Length |
30+
+==============+======+=============+========+
31+
| Healthcare | HC | S | Short |
32+
+--------------+------+-------------+--------+
33+
| Healthcare | HC | F | Full |
34+
+--------------+------+-------------+--------+
35+
| Finance | F | S | Short |
36+
+--------------+------+-------------+--------+
37+
| Finance | F | F | Full |
38+
+--------------+------+-------------+--------+
39+
| Energy | E | S | Short |
40+
+--------------+------+-------------+--------+
41+
| Energy | E | F | Full |
42+
+--------------+------+-------------+--------+
43+
| Original || S | Short |
44+
+--------------+------+-------------+--------+
45+
| Original || F | Full |
46+
+--------------+------+-------------+--------+
47+
48+
Course Key Format
49+
~~~~~~~~~~~~~~~~~
50+
51+
.. code-block:: text
52+
53+
course-v1:ORG+NUMBER.<DURATION>[.<INDUSTRY>]+RUN
54+
55+
For the **Original** industry, no industry code is appended:
56+
57+
.. code-block:: text
58+
59+
course-v1:UAI_SOURCE+UAI.3.S+1T2026 ← Original, Short
60+
course-v1:UAI_SOURCE+UAI.3.F+1T2026 ← Original, Full
61+
course-v1:UAI_SOURCE+UAI.3.S.HC+1T2026 ← Healthcare, Short
62+
63+
Course Structure
64+
~~~~~~~~~~~~~~~~
65+
66+
Each generated course has the following structure::
67+
68+
Course (<display name>)
69+
└── Lectures (section)
70+
└── <Video Title> (subsection)
71+
└── <Video Title> (unit)
72+
└── <Video Title> (video block with edX video ID)
73+
74+
Usage
75+
-----
76+
77+
Prerequisites
78+
~~~~~~~~~~~~~
79+
80+
You will need two CSV files:
81+
82+
1. **Customized video metadata CSV** — produced by the video customization
83+
workflow. Required columns:
84+
85+
- ``Course Key`` — the original Open edX course key (e.g.
86+
``course-v1:UAI_SOURCE+UAI.2+1T2026``)
87+
- ``Industry`` — one of: ``Healthcare``, ``Finance``, ``Energy``,
88+
``Original industry``
89+
- ``Duration (Minutes)`` — a numeric value (≤30 = Short) or the literal
90+
``long`` (= Full)
91+
- ``Video File Name`` — file name matching the Name column in the assets CSV
92+
- ``Video Title (Lecture Title)`` — display name for the subsection/unit/video
93+
- ``Module Name`` — used to build the course display name
94+
95+
2. **Open edX video asset CSV** — exported from Studio / OVS after uploading
96+
the customized videos. Required columns:
97+
98+
- ``Name`` — video file name (matches ``Video File Name`` above)
99+
- ``Video ID`` — the Open edX UUID for the video
100+
101+
Running the Command
102+
~~~~~~~~~~~~~~~~~~~
103+
104+
Run the management command from inside the CMS container (e.g. Tutor dev
105+
shell):
106+
107+
.. code-block:: bash
108+
109+
python manage.py generate_uai_courses \
110+
--customized-csv /path/to/customized.csv \
111+
--video-assets-csv /path/to/video_assets.csv \
112+
[--username studio_worker] \
113+
[--dry-run]
114+
115+
Options
116+
~~~~~~~
117+
118+
``--customized-csv``
119+
Path to the customized video metadata CSV file. **Required.**
120+
121+
``--video-assets-csv``
122+
Path to the Open edX video asset CSV file. **Required.**
123+
124+
``--username``
125+
Username of the platform user under whose authority the courses are
126+
created. Defaults to ``studio_worker``.
127+
128+
``--dry-run``
129+
Print what would be created without writing anything to the modulestore.
130+
Use this to verify CSV mapping before committing.
131+
132+
Development
133+
-----------
134+
135+
.. code-block:: bash
136+
137+
# Install dependencies
138+
uv sync --dev
139+
140+
# Run tests (requires Open edX environment — see AGENTS.md)
141+
./run_edx_integration_tests.sh --plugin ol_openedx_uai_content_customization --skip-build

src/ol_openedx_uai_content_customization/ol_openedx_uai_content_customization/__init__.py

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""App configuration for ol-openedx-uai-content-customization plugin."""
2+
3+
from django.apps import AppConfig
4+
from edx_django_utils.plugins import PluginSettings
5+
from openedx.core.djangoapps.plugins.constants import ProjectType, SettingsType
6+
7+
8+
class OLOpenEdxUaiContentCustomizationConfig(AppConfig):
9+
"""App configuration for the ol-openedx-uai-content-customization plugin."""
10+
11+
name = "ol_openedx_uai_content_customization"
12+
verbose_name = "OL Open edX UAI Content Customization"
13+
14+
plugin_app = {
15+
PluginSettings.CONFIG: {
16+
ProjectType.CMS: {
17+
SettingsType.PRODUCTION: {
18+
PluginSettings.RELATIVE_PATH: "settings.production"
19+
},
20+
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: "settings.common"},
21+
},
22+
},
23+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Constants for ol-openedx-uai-content-customization plugin."""
2+
3+
# Industry short codes used in course key generation.
4+
# "Original industry" has no code — only a length code is appended.
5+
INDUSTRY_CODES = {
6+
"Healthcare": "HC",
7+
"Finance": "F",
8+
"Energy": "E",
9+
"Original industry": "",
10+
}
11+
12+
# Duration label → short code used in course key generation.
13+
# Numeric minutes (e.g. "10") are treated as Short; "long" as Full.
14+
DURATION_CODE_SHORT = "S"
15+
DURATION_CODE_FULL = "F"
16+
17+
DURATION_CODES = {
18+
"short": DURATION_CODE_SHORT,
19+
"long": DURATION_CODE_FULL,
20+
}
21+
22+
# Duration threshold: any numeric value at or below this (in minutes) maps to
23+
# "Short" (code "S"). Values above it map to "Full" (code "F").
24+
# The spec defines short as ≤10 min; 30 allows headroom for slightly longer
25+
# short-form variants without requiring a CSV format change.
26+
SHORT_DURATION_THRESHOLD = 30
27+
28+
# Display name for the top-level section added to every generated course
29+
LECTURES_SECTION_DISPLAY_NAME = "Lectures"
30+
31+
# CSV column names — customized video metadata CSV
32+
CSV_COL_COURSE_KEY = "Course Key"
33+
CSV_COL_INDUSTRY = "Industry"
34+
CSV_COL_DURATION = "duration_minutes"
35+
CSV_COL_VIDEO_FILE = "Video File Name"
36+
CSV_COL_VIDEO_TITLE = "Video Title (Lecture Title)"
37+
CSV_COL_MODULE_NAME = "Module Name"
38+
39+
# CSV column names — Open edX video asset CSV
40+
CSV_COL_ASSET_NAME = "Name"
41+
CSV_COL_ASSET_VIDEO_ID = "Video ID"
42+
43+
# Required columns for each CSV — used to give early, clear error messages
44+
REQUIRED_CUSTOMIZED_CSV_COLS = [
45+
CSV_COL_COURSE_KEY,
46+
CSV_COL_INDUSTRY,
47+
CSV_COL_DURATION,
48+
CSV_COL_VIDEO_FILE,
49+
CSV_COL_VIDEO_TITLE,
50+
CSV_COL_MODULE_NAME,
51+
]
52+
53+
REQUIRED_ASSET_CSV_COLS = [
54+
CSV_COL_ASSET_NAME,
55+
CSV_COL_ASSET_VIDEO_ID,
56+
]

0 commit comments

Comments
 (0)