Skip to content

Legacy conversion writes the item language as language= instead of xml:lang, and validation cannot catch it #6098

Description

@AlexVelezLl

This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.

Summary

convert_legacy_question_to_qti writes the item's language as language="en-US" on
<qti-assessment-item>. QTI 3.0 has no such attribute — the schema declares xml:lang.
The item still validates, for two independent reasons, so nothing catches it.

The schema

AssessmentItemDType in utils/assessment/qti/schema/xsd/imsqti_itemv3p0p1_v1p0.xsd:

<xs:attribute name="identifier"     use="required" type="IdentifierDType" />
<xs:attribute name="title"          use="required" type="xs:normalizedString" />
<xs:attribute name="label"          use="optional" type="xs:normalizedString" />
<xs:attribute ref="xml:lang"        use="optional" />
<xs:attribute name="tool-name"      use="optional" type="xs:normalizedString" />
<xs:attribute name="tool-version"   use="optional" type="xs:normalizedString" />
<xs:attribute name="adaptive"       use="optional" default="false" type="xs:boolean" />
<xs:attribute name="time-dependent" use="required" type="xs:boolean" />
<xs:attributeGroup ref="extensionAnyTypeLax.Attr" />

xml:lang is the language attribute. language is not declared.

Why validation passes anyway

  1. extensionAnyTypeLax.Attr is a lax wildcard, so any unrecognised attribute on the
    item validates silently.
  2. The same XSD does carry a Schematron rule that would reject it — it enumerates the
    legal attribute names and fails anything else with
    [RULE ROOT ELEMENT (qti-assessment-item): Assertion N] Invalid XML attribute in position N with name of …. validate_qti_item uses lxml.etree.XMLSchema, which does
    not execute Schematron, so the rule never runs.

So validate_qti_item cannot currently catch any "well-structured but illegal attribute"
mistake. That is arguably the more valuable half of this issue.

Reproducing

from contentcuration.utils.assessment.qti.ingest import convert_legacy_question_to_qti
res = convert_legacy_question_to_qti({
    'type': 'single_selection', 'question': 'Q', 'answers': '[]', 'hints': '[]',
    'randomize': False, 'assessment_id': 'a' * 32, 'language': 'es',
})
# root carries language="es"; no xml:lang

Every fixture under contentcuration/tests/utils/qti/fixtures/ shows the same, e.g.
single_selection.xml has language="en-US".

Impact

  • A migrated item's language is not where a QTI consumer looks for it. Nothing in Studio
    reads it back (perseus_derive does not; archive.py takes the language from the
    content node), so the effect is on what we hand to Kolibri and to any other QTI
    consumer of a published package.
  • The QTI editor writes xml:lang (the spec attribute), so a converted item's language
    currently has to be read from one attribute and written to another. Replace the legacy assessment editor with the QTI editor #6095 tolerates
    language on read for exactly this reason; that tolerance can be deleted once this is
    fixed.
  • No data migration needed: consolidate() converts on read without persisting, so no
    stored raw_data carries language=. Verified on a dev database — of 24 rows with
    raw_data, 0 contain language= and 24 contain xml:lang=. The global backfill
    ([QTI] Backfill all non-raw AssessmentItems to QTI items #6007) would persist it, so this is worth fixing before that runs.

Suggested fix

  1. Serialise the pydantic AssessmentItem.language field as xml:lang
    (utils/assessment/qti/assessment_item.py), and regenerate the fixtures.
  2. Separately: make validate_qti_item run the XSD's Schematron rules — or add an
    explicit allow-list check for the root element's attributes — so this class of mistake
    fails a test rather than reaching a package.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions