Skip to content

List a shared answer once in the Match and Associate response pools - #6330

Merged
AlexVelezLl merged 5 commits into
learningequality:unstablefrom
rtibblesbot:issue-6324-32b178
Oct 26, 2026
Merged

AlexVelezLl merged 5 commits into
learningequality:unstablefrom
rtibblesbot:issue-6324-32b178

Conversation

@rtibblesbot

@rtibblesbot rtibblesbot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Match and Associate pools list a shared answer once, as the saved XML and Kolibri do; blanks and Match distractors keep a chip each.
  • richTextComparisonKey returns '' for blank content and hosts hasRichTextContent; utils/richText.js and QTISanitizer are gone.

References

Fixes #6324. First seen: #6168.

Reviewer guidance

Suggested review: Light-touch code review, then manual QA.

  • User-visible files changed: contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/EditableChipList/index.vue, contentcuration/contentcuration/frontend/shared/views/QTIEditor/composables/useInlineChoiceInteraction.js, contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/associate/Editor.vue, contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/associate/parse.js, contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/associate/validation.js….
  • Open a saved Match question with two rows sharing one answer, and an Associate question that reuses an option, in view mode with "show answers" on and off; check each shared option shows as one chip, and blank rows or options still show one chip each.

Manual QA. QA team: Match and Associate response pools now list a shared answer once. Blank errors changed in all question types.

Use a new exercise. Tick Show answers.

  1. Match. Give "Row A" and "Row B" the Math formula x^2. Give "Row C" an image and "Row D" only spaces. Add the distractor x^2. Row D shows "Answer cannot be blank". The distractor shows "Distractors cannot repeat another item". The pool shows x^2 once, green.
  2. Associate. Give two pairs x^2 and a third an image. Add that image as a distractor. In a fourth pair, type only spaces in one item. The distractor shows the repeat error. The pool shows x^2 once and two blank chips.
  3. Other types. Requires: an exercise with hints. In Multiple Choice, Ordering and Text entry, add a space-only and an image-only answer. Add an empty hint and an image hint. Only space-only answers show an error.
  4. Reopen. Fix all errors and reload. Each pair keeps its answer. The empty hint is gone; the image hint stays.

Look out for:

  • In item 2, change one pair. The pool order changes. Is this acceptable?

QA cannot open Gap Match questions in the editor yet.

Evidence

Match: shared formula, image and space-only answers

Step Screenshot
Row D blank error; distractor x^2 repeat error; formula and image rows pass Row D blank error; distractor x^2 repeat error; formula and image rows pass
Pool: x^2 once in green, image green, blank chip, distractor not green Pool: x^2 once in green, image green, blank chip, distractor not green
After fixing and reload: x^2, image, "Four" green; "Wrong" not After fixing and reload: x^2, image, Four green; Wrong not

Associate: reused formula, blank pair, image distractor

Step Screenshot
Pair 3 image repeat error; Pair 4 blank error Pair 3 image repeat error; Pair 4 blank error
Pool: two separate blank chips, x^2 once Pool: two separate blank chips, x^2 once
s2-associate-reopen.webm

Multiple Choice, Ordering, Text entry and hints

Step Screenshot
Space-only choice flagged; image-only choice passes Space-only choice flagged; image-only choice passes
After reload: empty hint gone, image hint kept After reload: empty hint gone, image hint kept
More captures (32)
Step Screenshot
Match fixed, before reload Match fixed, before reload
Associate after reload, edit mode: pairs kept Associate after reload, edit mode: pairs kept
Multiple Choice: blank and image-only hints added Multiple Choice: blank and image-only hints added
Ordering: space-only item flagged; image-only item passes Ordering: space-only item flagged; image-only item passes
Ordering after reload: image hint kept Ordering after reload: image hint kept
Second question after reload: image hint kept Second question after reload: image hint kept
Text entry: space-only question and answer flagged Text entry: space-only question and answer flagged
Earlier round — Match: one green "Europe" chip France and Bulgaria both → Europe: one green "Europe" chip
Earlier round — Bulgaria → Asia: "Europe" and "Asia", both green Bulgaria changed to Asia: pool splits into "Europe" and "Asia", both green
Earlier round — bold and plain "Europe" fold to one green chip; distractor and blanks grey Bold and plain "Europe" answers fold to one green chip; distractor "Europe" and two blank chips stay grey
Earlier round — same image in two rows: one green image chip Same image in two rows: one green image chip
Earlier round — Associate: Mammal once, five green chips Mammal in two pairs: listed once, all five chips green
Earlier round — empty Pair 4: two blank chips, not green Empty Pair 4: two separate blank chips, not green
Earlier round — Pair 2 → Reptile: Mammal once, Reptile added Pair 2 changed to Reptile, distractor Mammal: Mammal once, Reptile added
Match shared answer, answers hidden Match shared answer, answers hidden
Match shared answer after reopen Match shared answer after reopen
Pool before edit: one Europe Pool before edit: one Europe
Editing row 2 answer to Asia Editing row 2 answer to Asia
Split pool, answers hidden Split pool, answers hidden
Edit mode: both rows Europe again Edit mode: both rows Europe again
Refolded to one Europe, answers shown Refolded to one Europe, answers shown
After reload, edit mode: both rows Europe After reload, edit mode: both rows Europe
Edit mode: bold, padded, blank and distractor answers Edit mode: bold, padded, blank and distractor answers
One blank row, answers shown One blank row, answers shown
After reload, answers shown After reload, answers shown
After reload, edit mode After reload, edit mode
Image answer in two rows, edit mode Image answer in two rows, edit mode
Image pool after reload Image pool after reload
Associate reused option, answers hidden Associate reused option, answers hidden
Associate pairs after reopen Associate pairs after reopen
Gap Match not offered as a response type Gap Match not offered as a response type
Gap Match item in view mode Gap Match item in view mode

Earlier round — Asia → Europe folds back to one chip:

s2-05-refold-asia-to-europe.webm

AI usage

Claude Code planned and implemented the fold and tests; verified with the QTIEditor Jest suite and pre-commit.


Written by rtibblesbot, an LLM-based coding agent.

@rtibblesbot

🟢 Updating PR

Last updated: 2026-10-26 22:02 UTC

@rtibblesbot
rtibblesbot marked this pull request as ready for review October 23, 2026 13:12
@rtibblesbot
rtibblesbot requested review from a team and leq-bot October 23, 2026 13:12
...state.value.distractors.map(choice => ({ content: choice.content, isCorrect: false })),
]);
// Kolibri's pool shows each choice of the response match set once.
const poolChoices = computed(() => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: let's give buildMatchInteractionXML and this pool one helper in match/parse.js for the answers fold, so the pool keeps listing what the response match set holds.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2ce52b1ad: resolveMatchAnswers(rows, claim) in match/parse.js returns the folded answers and each row's resolved answer ids; the build passes its shared id claimer, poolChoices uses the default one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this module at all? Could we just embed the hasRichTextContent logic into richTextComparisonKey and have richTextComparisonKey return an empty string if it does not have rich text content? With this, I think we could also just move hasRichTextContent to the tiptap module, along with the toText method. Given that we should not move the QTISanitizer to the TipTapModule, I think the only method we actually use from QTISanitizer, the stripTags method, could be just another utility function in the TipTapEditor's comparisonKey.js module, and get rid of the QTISanitizer class.

This way, the code is more readable, as "choiceKey" is not as readable as "richTextComparisonKey" when used in the interaction modules.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 45e1d7bb4:

  • hasRichTextContent, toText and stripTags moved to TipTapEditor/utils/comparisonKey.js; richTextComparisonKey returns '' when there is no content.
  • utils/richText.js, QTISanitizer and its spec deleted; nothing else used the class. Its spec's script/style cases moved to hasRichTextContent.spec.js.
  • choiceKey call sites (4) now call richTextComparisonKey.
  • Searched for the same pairing, hasRichTextContent and richTextComparisonKey on one choice: the read helpers in associate, match and gapMatch validation.js matched (3); they now derive filled from the key. Choice and ordering validation skip blanks before keying, so I left them.

rtibblesbot and others added 3 commits October 26, 2026 18:26
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rtibblesbot and others added 2 commits October 26, 2026 19:37
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@AlexVelezLl AlexVelezLl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes look good. Manual QA checks out! LGTM!

@AlexVelezLl
AlexVelezLl merged commit 3857a5e into learningequality:unstable Oct 26, 2026
67 checks passed
@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Quick check on rtibblesbot's work on this PR: react 👍 helpful · 😕 mixed · 👎 not helpful. Reply with anything specific.

@rtibblesbot
rtibblesbot deleted the issue-6324-32b178 branch October 26, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QTI] Match question: Reused target options appear as duplicates in Studio response pool

2 participants