You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
Let a single exercise node containing both raw Perseus questions and QTI questions publish together as one QTI package. Raw Perseus questions are wrapped in a custom QTI interaction that Kolibri's QTI Viewer renders via the Perseus renderer (Kolibri side: #9).
Complexity: High Target branch: unstable
Context
Today a node publishes either Perseus or QTI, never both in one package — the two are mutually exclusive at publish.
Nodes mixing legacy Perseus questions with new QTI questions have no publish path.
Kolibri gates import by renderability; a mixed package also needs the Perseus renderer, so publish declares it via File.included_presets (see contract; honored by Fixes browserify subprocess running for windows. #10).
The Change
Relax the one-generator-per-node selection in publish.py so a node holding both perseus_question and QTI items routes to QTI packaging instead of being forced to one format.
Emit each perseus_question as the Perseus custom interaction defined below, writing its Perseus JSON to a referenced package file via QTIExerciseGenerator.
Add included_presets to the published File mirror (kolibri_content/kolibri_public) and set it per file: its own preset's bit, plus the exercise bit for a qti File embedding ≥1 Perseus custom interaction (see contract).
Contract — Perseus-in-QTI custom interaction
Shared with the Kolibri renderer issue (learningequality/kolibri). Exact attribute validity is confirmed against the QTI 3.0 XSD under #2.
Each raw Perseus question is a qti-custom-interaction (the spec's delivery-engine-specific element — no JS module required) with a data-type="perseus" marker.
The Perseus JSON is a resource file in the package, referenced by data-perseus-path; every file it references (images, and graphie .svg/-data.json) is packaged and declared as a dependency of that resource in the manifest.
The host's Perseus renderer, keyed off data-type="perseus", owns rendering and grading; it grades the attempt and reports the resulting correct/incorrect back through the QTI response (no QTI-native response-processing template).
Kolibri renderer counterpart (Perseus custom-interaction rendering) — created alongside this issue
AI usage
Architecture decided with the maintainer across an iterative session: blanket QTI type with the item XML in raw_data; XSD-authoritative validation across all sources; a legacy→QTI global migration with an API-layer dual-read; ricecooker upload delegating to the AssessmentItem serializer; and a Perseus custom-interaction contract confirmed against the QTI 3.0 specification. Claude mapped the existing publish/validation/ricecooker code, proposed the breakdown, and drafted each issue; the maintainer steered every decision and reviewed throughout.
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
Let a single exercise node containing both raw Perseus questions and QTI questions publish together as one QTI package. Raw Perseus questions are wrapped in a custom QTI interaction that Kolibri's QTI Viewer renders via the Perseus renderer (Kolibri side: #9).
Complexity: High
Target branch: unstable
Context
File.included_presets(see contract; honored by Fixes browserify subprocess running for windows. #10).The Change
publish.pyso a node holding bothperseus_questionandQTIitems routes to QTI packaging instead of being forced to one format.perseus_questionas the Perseus custom interaction defined below, writing its Perseus JSON to a referenced package file viaQTIExerciseGenerator.included_presetsto the publishedFilemirror (kolibri_content/kolibri_public) and set it per file: its own preset's bit, plus theexercisebit for a qtiFileembedding ≥1 Perseus custom interaction (see contract).Contract — Perseus-in-QTI custom interaction
Shared with the Kolibri renderer issue (learningequality/kolibri). Exact attribute validity is confirmed against the QTI 3.0 XSD under #2.
qti-custom-interaction(the spec's delivery-engine-specific element — no JS module required) with adata-type="perseus"marker.data-perseus-path; every file it references (images, and graphie.svg/-data.json) is packaged and declared as a dependency of that resource in the manifest.data-type="perseus", owns rendering and grading; it grades the attempt and reports the resulting correct/incorrect back through the QTI response (no QTI-native response-processing template).Contract —
File.included_presetsbitmaskShared with the Kolibri import-gating issue (learningequality/kolibri).
Filecarriesincluded_presets: a bitmask of the presets whose renderer it needs, including its own preset.Fileembedding ≥1 Perseus custom interaction sets theqtiandexercisebits.QTItype).included_presetsis a subset of the locally available presets.Acceptance Criteria
Filehasincluded_presets=qti | exercise; non-mixed files carry only their own preset bitReferences
AI usage