Repository navigation
[QTI] Restoring a published channel loses or breaks its QTI exercises #6211
Description
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
restore_channel rebuilds an exercise's questions only from its Perseus archive. An exercise published as a QTI package alone restores with no questions. A dual-published exercise restores with its QTI item identifiers as assessment_ids, and publishing the restored channel then fails on them.
Complexity: Medium
Target branch: unstable
Context
- Restore reads only
preset='exercise'files:studio/contentcuration/contentcuration/utils/import_tools.py
Lines 456 to 461 in 1beec6e
sql_command = ( "SELECT checksum, extension " "preset FROM {table} WHERE contentnode_id='{id}' AND preset='exercise';".format( table=FILE_TABLE, id=contentnode.node_id ) ) - Restored
assessment_ids are the idsexercise.jsonlists:studio/contentcuration/contentcuration/utils/import_tools.py
Lines 516 to 524 in 1beec6e
for index, assessment_id in enumerate(data["all_assessment_items"]): with open("{}.json".format(assessment_id), "rb") as fobj: assessment_item = generate_assessment_item( assessment_id, index, data["assessment_mapping"][assessment_id], json.load(fobj), download_url=download_url, ) - Publishing decodes every legacy item's
assessment_idas hex:def hex_to_qti_id(hex_string): """ Encode a 32 digit hex to a 22 character base64 encoded id and a K prefix. """ bytes_data = bytes.fromhex(hex_string) return f"K{base64.urlsafe_b64encode(bytes_data).decode('ascii').rstrip('=')}" - On
1beec6ecb, a dual-published node with one QTI editor choice question and one legacyinput_question, restored from its Perseus archive, then published:restored assessment_ids: item_1, <32-char hex> ValueError: non-hexadecimal number found in fromhex() arg at position 0
The Change
- Restore should recreate the questions of every published exercise, whichever archives it was published with.
How to Get There
- Publish a channel holding an exercise with one QTI editor question and one legacy question.
- Run
manage.py restore_channel <channel_id> --download-url <studio url>. - Publish the restored channel.
Acceptance Criteria
- An exercise published as a QTI package only restores with all its questions.
- An exercise mixing QTI editor and legacy questions restores with all its questions.
- Publishing a channel restored from either case succeeds.
-
import_toolstests cover both cases.
AI usage
Claude Code found this during an automated self-review of the #6205 branch, reproduced the publish failure with a scratch test, and drafted this issue.

Activity
rtibblesbot commentedon Sep 30, 2026
Plan:
restore_channelrecreates every question of an exercise published with a QTI package, alone or alongside a Perseus archive, and the restored channel publishes.perseus_questionAssumptions — the issue did not settle these; say so if one is wrong.
ASSESSMENT_TESTmanifest resources are ignored; wrong → items from externally authored packages with tests would be dropped (Studio never emits them).EXERCISE_IMAGEpreset; wrong → republish omits them fromperseus/images/(base.py:153-155filters on that preset).@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?