Repository navigation
[QTI] Publish mixed Perseus + QTI exercises as one QTI package #6006
Copy link
Copy link
Labels
Description
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: learningequality/kolibri#14892).
Complexity: High
Target branch: unstable
Context
- Today a node publishes either Perseus or QTI, never both.
publish.py:370-374picksQTIExerciseGeneratorwhen any question is free-response andPerseusExerciseGeneratorotherwise, one generator per node. - Nodes mixing legacy Perseus with new QTI questions therefore have no publish path.
- The mixed package needs the Perseus renderer too, so its qti
Filesets theexercisebit in theincluded_presetsbitmask (Publish a renderer-preset bitmask (File.included_presets) into the content database #6004).
The Change
- Relax the one-generator-per-node selection in
publish.py(370-374) so a node whose questions include both raw Perseus and QTI items routes toQTIExerciseGenerator. - Have
QTIExerciseGeneratoremit each raw Perseus question as the custom interaction defined below. - Set the mixed qti
File'sincluded_presetstoqti | exercisewhen it embeds ≥1 Perseus custom interaction.
Contract — Perseus-in-QTI custom interaction
Shared with the Kolibri renderer issue. Exact attribute validity is confirmed against the QTI 3.0 XSD under #6005.
- Each raw Perseus question is a
qti-custom-interaction(the spec's delivery-engine-specific element — no JS module required) with adata-type="perseus"marker. - The Perseus JSON is a resource file in the package, referenced by
data-perseus-path. - Every file that JSON references (images, 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 reports the resulting correct/incorrect back through the QTI response, with no QTI-native response-processing template.
<qti-custom-interaction
response-identifier="RESPONSE"
data-type="perseus"
data-perseus-path="perseus/{assessment_id}.json"/>Acceptance Criteria
- A mixed node yields one QTI package whose manifest lists both native QTI items and Perseus custom interactions
- Each Perseus custom interaction references its Perseus JSON and assets as package files (see Contract)
- The package validates against the QTI 3.0 XSD ([QTI] QTI 3.0 schema XML validation utilities #6005)
- The mixed qti
Filehasincluded_presets=qti | exercise - Native QTI questions in the same node are unaffected
- Tests cover a mixed node and assert both question kinds appear in the package
References
- Publish a renderer-preset bitmask (
File.included_presets) into the content database #6004 —File.included_presetsbitmask column and base population (blocks this issue) - Allow Kolibri to render QTI assessments kolibri#11428 — Kolibri QTI rendering
- [QTI] Render embedded Perseus questions in the QTI viewer kolibri#14892 — Kolibri renderer counterpart (Perseus custom-interaction rendering)
AI usage
Claude mapped the existing publish and validation code, proposed the breakdown, and drafted this issue; the maintainer steered every decision and reviewed throughout.
Reactions are currently unavailable
Activity
File.included_presets) into the content database #6004rtibblesbot commentedon Jul 16, 2026
Triage: blocking prerequisite not on the target branch
This issue targets
unstable, and AC #4 ("mixed qtiFilehasincluded_presets=qti | exercise") depends on#6004.#6004is closed via #6040, but that PR merged intohotfixes, notunstable:File.included_presets(model +kolibri_content/kolibri_publicmigrations + publish population) exists onorigin/hotfixes.origin/unstable— a branch offunstabletoday cannot setincluded_presets.The rest of the spec is implementable as written: the routing change (
publish.pymixed node →QTIExerciseGenerator), theqti-custom-interaction data-type="perseus"contract, and asset packaging are clear, and the QTI 3.0 XSD (#6005) plus the ingest-side custom-interaction parsing are already present onunstable.Need a decision before implementation:
hotfixes(or just the feat: export included_presets renderable bitmask for content-schema import gating #6040 changes) intounstablefirst, then implement here; orhotfixes; orincluded_presetscolumn onunstable(risks conflicting with thehotfixesversion on the next integration).Which path?
rtibblesbot commentedon Jul 16, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.
rtibbles commentedon Jul 17, 2026
hotfixeshas been merged intounstablenow, so this should be unblocked.rtibblesbot commentedon Jul 17, 2026
Plan: Let one exercise node holding both raw Perseus questions and native QTI questions publish as a single QTI package, emitting each raw Perseus question as a
qti-custom-interaction data-type="perseus"that Kolibri's Perseus renderer owns.CustomInteractionmodel + item builderQTIExerciseGeneratorembedsperseus_questionitemspublish.pyincluded_presets = qti | exerciseon the mixed QTI file@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
rtibblesbot commentedon Jul 17, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.
rtibblesbot commentedon Jul 18, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.
rtibblesbot commentedon Jul 18, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.
rtibblesbot commentedon Jul 18, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.