-
Notifications
You must be signed in to change notification settings - Fork 34
Description
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
Migrate le_utils/constants/content_kinds.py from the legacy JSON-as-data approach to the modern spec + code generation system. This issue also enhances the generation script to support metadata-driven code generation for the MAPPING dict.
Context
Currently, le_utils/constants/content_kinds.py uses the legacy approach:
- Loads
resources/kindlookup.jsonat runtime - Manual Python constants (
TOPIC = "topic",VIDEO = "video", etc.) - Manual
MAPPINGdict mapping file formats to content kinds (not from JSON!) - No JavaScript export available
Current Structure
File: le_utils/resources/kindlookup.json
(Only has kind names, no mapping data)
{
"topic": {"name": "topic"},
"video": {"name": "video"},
...
}Python module has manual MAPPING:
MAPPING = {
file_formats.MP4: VIDEO,
file_formats.WEBM: VIDEO,
file_formats.MP3: AUDIO,
file_formats.PDF: DOCUMENT,
file_formats.EPUB: DOCUMENT,
file_formats.PERSEUS: EXERCISE,
file_formats.HTML5: HTML5,
file_formats.H5P: H5P,
file_formats.ZIM: ZIM,
file_formats.BLOOMPUB: DOCUMENT,
file_formats.BLOOMD: DOCUMENT,
}Target Spec Format
Create spec/constants-content_kinds.json with associated_formats metadata:
{
"namedtuple": {
"name": "Kind",
"fields": ["id", "name"]
},
"constants": {
"topic": {
"name": "topic"
},
"video": {
"name": "video",
"associated_formats": ["mp4", "webm"]
},
"audio": {
"name": "audio",
"associated_formats": ["mp3"]
},
"exercise": {
"name": "exercise",
"associated_formats": ["perseus"]
},
"document": {
"name": "document",
"associated_formats": ["pdf", "epub", "bloompub", "bloomd"]
},
"html5": {
"name": "html5",
"associated_formats": ["zip"]
},
"slideshow": {
"name": "slideshow"
},
"h5p": {
"name": "h5p",
"associated_formats": ["h5p"]
},
"zim": {
"name": "zim",
"associated_formats": ["zim"]
},
"quiz": {
"name": "quiz"
}
}
}Note: associated_formats is metadata (not a namedtuple field) used to auto-generate the MAPPING dict.
Generation Script Enhancement
Update scripts/generate_from_specs.py:
- Detect metadata fields (fields not in namedtuple definition)
- Generate MAPPING dict from
associated_formats:- Import
file_formatsmodule - Create dict mapping format constants to kind constants
- Python:
MAPPING = {file_formats.MP4: VIDEO, ...} - JavaScript:
export const KindsMapping = { mp4: "video", ... }
- Import
Generated Output Example
Python (le_utils/constants/content_kinds.py):
# Generated by scripts/generate_from_specs.py
from collections import namedtuple
from le_utils.constants import file_formats
class Kind(namedtuple("Kind", ["id", "name"])):
pass
TOPIC = "topic"
VIDEO = "video"
AUDIO = "audio"
# ...
choices = (
(TOPIC, "Topic"),
(VIDEO, "Video"),
# ...
)
KINDLIST = [
Kind(id="topic", name="topic"),
Kind(id="video", name="video"),
# ...
]
# File Format to Content Kind mapping (generated from associated_formats metadata)
MAPPING = {
file_formats.MP4: VIDEO,
file_formats.WEBM: VIDEO,
file_formats.MP3: AUDIO,
file_formats.PDF: DOCUMENT,
file_formats.EPUB: DOCUMENT,
file_formats.PERSEUS: EXERCISE,
file_formats.HTML5: HTML5,
file_formats.H5P: H5P,
file_formats.ZIM: ZIM,
file_formats.BLOOMPUB: DOCUMENT,
file_formats.BLOOMD: DOCUMENT,
}JavaScript (js/ContentKinds.js):
// Generated by scripts/generate_from_specs.py
export default {
TOPIC: "topic",
VIDEO: "video",
AUDIO: "audio",
// ...
};
export const KindsList = [
{ id: "topic", name: "topic" },
{ id: "video", name: "video" },
// ...
];
export const KindsMap = new Map(
KindsList.map(kind => [kind.id, kind])
);
// Format to Kind mapping
export const KindsMapping = {
mp4: "video",
webm: "video",
mp3: "audio",
pdf: "document",
epub: "document",
perseus: "exercise",
zip: "html5",
h5p: "h5p",
zim: "zim",
bloompub: "document",
bloomd: "document",
};Testing Updates
File: tests/test_kinds.py
Update to test against spec:
spec_path = os.path.join(os.path.dirname(__file__), "..", "spec", "constants-content_kinds.json")
with open(spec_path) as f:
spec = json.load(f)
kindlookup = spec["constants"]
# Verify MAPPING is generated correctly from associated_formatsHow to Run Tests
pytest tests/test_kinds.py -v
pytest tests/ -vAcceptance Criteria
-
spec/constants-content_kinds.jsoncreated withassociated_formatsmetadata -
scripts/generate_from_specs.pyenhanced to generate MAPPING from metadata -
make buildsuccessfully generates Python and JavaScript files - Generated
le_utils/constants/content_kinds.pyhas:- Kind namedtuple
- Uppercase constants (TOPIC, VIDEO, etc.)
-
choicestuple -
KINDLISTwith Kind namedtuples - Auto-generated
MAPPINGdict fromassociated_formats
- Generated
js/ContentKinds.jshas:- Default export with constants
-
KindsListwith full data -
KindsMapfor lookups -
KindsMappingfor format→kind lookups
-
tests/test_kinds.pyupdated to test against spec - All tests pass
-
resources/kindlookup.jsondeleted
Disclosure
🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖
