learningequality / learningequality/le-utils

Migrate format_presets.py to spec + code generation

Open
#185 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
7
Forks
36
PR merge metrics
No merged PRs in 30d

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/format_presets.py from the legacy JSON-as-data approach to the modern spec + code generation system. This is the most complex module with 10+ fields per preset and references to content kinds and file formats.

This module references constants from both file_formats and content_kinds, so both must be migrated first.

Context

Currently, le_utils/constants/format_presets.py uses the legacy approach:

  • Loads resources/presetlookup.json at runtime
  • Manual Python constants (VIDEO_HIGH_RES = "high_res_video", etc.)
  • Complex namedtuple with many fields
  • No JavaScript export available
Current Structure

File: le_utils/resources/presetlookup.json

{
  "high_res_video": {
    "readable_name": "High Resolution",
    "multi_language": false,
    "supplementary": false,
    "thumbnail": false,
    "subtitle": false,
    "display": true,
    "order": 1,
    "kind": "video",
    "allowed_formats": ["mp4", "webm"],
    "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
  },
  ...
}

Python module has:

  • Namedtuple: class Preset(namedtuple("Preset", ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"])): pass
  • Manual constants for each preset
  • PRESETLIST with Preset namedtuples
  • choices tuple
Target Spec Format

Create spec/constants-format_presets.json with all preset data:

{
  "namedtuple": {
    "name": "Preset",
    "fields": ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"]
  },
  "constants": {
    "high_res_video": {
      "readable_name": "High Resolution",
      "multi_language": false,
      "supplementary": false,
      "thumbnail": false,
      "subtitle": false,
      "display": true,
      "order": 1,
      "kind_id": "video",
      "allowed_formats": ["mp4", "webm"],
      "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    },
    "low_res_video": {
      "readable_name": "Low Resolution",
      "multi_language": false,
      "supplementary": false,
      "thumbnail": false,
      "subtitle": false,
      "display": true,
      "order": 2,
      "kind_id": "video",
      "allowed_formats": ["mp4", "webm"],
      "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    }
  }
}

Copy all presets from presetlookup.json, changing "kind" to "kind_id" to match the namedtuple field name.

Generated Output Example

Python (le_utils/constants/format_presets.py):

# Generated by scripts/generate_from_specs.py
from collections import namedtuple

class Preset(namedtuple("Preset", ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"])):
    pass

VIDEO_HIGH_RES = "high_res_video"
VIDEO_LOW_RES = "low_res_video"
# ...

choices = (
    (VIDEO_HIGH_RES, "Video High Res"),
    (VIDEO_LOW_RES, "Video Low Res"),
    # ...
)

PRESETLIST = [
    Preset(
        id="high_res_video",
        readable_name="High Resolution",
        multi_language=False,
        supplementary=False,
        thumbnail=False,
        subtitle=False,
        display=True,
        order=1,
        kind_id="video",
        allowed_formats=["mp4", "webm"],
        convertible_formats=["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    ),
    # ...
]

JavaScript (js/FormatPresets.js):

// Generated by scripts/generate_from_specs.py

export default {
    VIDEO_HIGH_RES: "high_res_video",
    VIDEO_LOW_RES: "low_res_video",
    // ...
};

export const PresetsList = [
    {
        id: "high_res_video",
        readable_name: "High Resolution",
        multi_language: false,
        supplementary: false,
        thumbnail: false,
        subtitle: false,
        display: true,
        order: 1,
        kind_id: "video",
        allowed_formats: ["mp4", "webm"],
        convertible_formats: ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    },
    // ...
];

export const PresetsMap = new Map(
    PresetsList.map(preset => [preset.id, preset])
);
Testing Updates

File: tests/test_presets.py

Update to test against spec:

spec_path = os.path.join(os.path.dirname(__file__), "..", "spec", "constants-format_presets.json")
with open(spec_path) as f:
    spec = json.load(f)
    presetlookup = spec["constants"]

# Verify all presets generated correctly
# Verify array fields (allowed_formats, convertible_formats) handled properly
How to Run Tests
pytest tests/test_presets.py -v
pytest tests/ -v
Acceptance Criteria
  • spec/constants-format_presets.json created with all preset data
  • Changed "kind" to "kind_id" in spec to match namedtuple field
  • make build successfully generates Python and JavaScript files
  • Generated le_utils/constants/format_presets.py has:
    • Preset namedtuple with 11 fields
    • Uppercase constants (VIDEO_HIGH_RES, etc.)
    • choices tuple
    • PRESETLIST with Preset namedtuples
    • Array fields (allowed_formats, convertible_formats) as lists
  • Generated js/FormatPresets.js has:
    • Default export with constants
    • PresetsList with full preset data
    • PresetsMap for lookups
    • Array fields properly exported
  • tests/test_presets.py updated to test against spec
  • All tests pass
  • resources/presetlookup.json deleted

Disclosure

🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with le_utils/constants/format_presets.py, resources/presetlookup.json, and the existing spec-based generator; note that file_formats and content_kinds must be migrated first. Create spec/constants-format_presets.json, update tests/test_presets.py, and run pytest tests/test_presets.py -v, pytest tests/ -v, and make build. Done means generated Python and JavaScript outputs, updated tests, and removal of resources/presetlookup.json.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, python
Domain
build-system, tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.