
❌ 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.json at runtime
- Manual Python constants (
TOPIC = "topic", VIDEO = "video", etc.)
- Manual
MAPPING dict 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_formats module
- Create dict mapping format constants to kind constants
- Python:
MAPPING = {file_formats.MP4: VIDEO, ...}
- JavaScript:
export const KindsMapping = { mp4: "video", ... }
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_formats
How to Run Tests
pytest tests/test_kinds.py -v
pytest tests/ -v
Acceptance Criteria
Disclosure
🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖
❌ 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.pyfrom 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 theMAPPINGdict.Context
Currently,
le_utils/constants/content_kinds.pyuses the legacy approach:resources/kindlookup.jsonat runtimeTOPIC = "topic",VIDEO = "video", etc.)MAPPINGdict mapping file formats to content kinds (not from JSON!)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:Target Spec Format
Create
spec/constants-content_kinds.jsonwithassociated_formatsmetadata:{ "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_formatsis metadata (not a namedtuple field) used to auto-generate the MAPPING dict.Generation Script Enhancement
Update
scripts/generate_from_specs.py:associated_formats:file_formatsmoduleMAPPING = {file_formats.MP4: VIDEO, ...}export const KindsMapping = { mp4: "video", ... }Generated Output Example
Python (
le_utils/constants/content_kinds.py):JavaScript (
js/ContentKinds.js):Testing Updates
File:
tests/test_kinds.pyUpdate to test against spec:
How to Run Tests
Acceptance Criteria
spec/constants-content_kinds.jsoncreated withassociated_formatsmetadatascripts/generate_from_specs.pyenhanced to generate MAPPING from metadatamake buildsuccessfully generates Python and JavaScript filesle_utils/constants/content_kinds.pyhas:choicestupleKINDLISTwith Kind namedtuplesMAPPINGdict fromassociated_formatsjs/ContentKinds.jshas:KindsListwith full dataKindsMapfor lookupsKindsMappingfor format→kind lookupstests/test_kinds.pyupdated to test against specresources/kindlookup.jsondeletedDisclosure
🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖