Skip to content

feat(ddi): skip logic and validation in the codebook (#151) - #156

Merged
jstet merged 1 commit into
mainfrom
feat/151-ddi-logic
Sep 27, 2026
Merged

jstet merged 1 commit into
mainfrom
feat/151-ddi-logic

Conversation

@jstet

@jstet jstet commented Sep 27, 2026

Copy link
Copy Markdown
Member

Closes #151. Part 1 of the round-trip plan, #155.

DDI Codebook 2.5 has no expression syntax, so each condition goes in twice (convention:logicMapping, new ddiEncoding):

  • Readable, in standard elements:
    • <universe clusion="I"> prose, built from the expression and the labels it refers to.
    • A simple numeric range (. >= 1 and . <= 10, . > 0) on an integer or decimal is also written as <valrng><range min/max/minExclusive/maxExclusive>.
  • Exact, in typed notes (the XSD's controlled-vocabulary extension point), written last in var and varGrp:
    • cdl:relevant and cdl:constraint, both with subject="xlsform-xpath".
    • cdl:constraint_message, one per xml:lang.
    • cdl:required.
<universe clusion="I">Nur wenn „Haben Sie einen Hund?“ = Ja</universe>
<universe clusion="I" xml:lang="en">Only if “Do you have a dog?” = Yes</universe>
…
<notes type="cdl:relevant" subject="xlsform-xpath">${hund} = 'ja'</notes>

The 8 agreed corrections

  1. Plain groups: their conditions are ANDed into each variable's own until DDI round trips: keep question order, every group, and notes apart from hints #152 gives them a varGrp.
  2. ${name} resolves to a var or a varGrp name. A select_multiple's logic sits on its varGrp type="multipleResp", and the semi-open multi's on its varGrp type="other". Each binary var also gets the universe prose, for readers.
  3. Element order follows the XSD: qstn, then valrng, then universe, then catgry, concept, varFormat, and notes last.
  4. Schematron: any number of typed notes, and at most one untyped note per language.
  5. LimeSurvey input: lstsv2ddi reverses relevance and constraints (expressions: true). An EM expression outside the dialect gets an em-unsupported warning and no note; the conversion no longer fails on it. lstsvToDdiXml gains onWarning, and the API and CLI pass it through.
  6. Prose templates are registry data, English and German only for now.
    • Covered: =, !=, comparisons, selected(), and, or, not(), and comparing with '' (answered / not answered).
    • Anything else gets no universe, but the note still carries the expression.
    • A language gets no universe if any label it needs is missing in that language. No text is filled in from another language.
    • An untagged codebook uses English.
  7. cdl:required is written only for required questions, with the text yes.
  8. valrng is added as described above.

Also

For DDI readers

The change only adds elements and relaxes one Schematron rule; nothing existing moves. qwacback (qwacback#37) stores <universe> and the typed notes.

Checks

  • vitest: 1236 passed. This includes the new tests/ts/unit/ddi/logic.test.ts.
  • pytest validation and codegen: 127 passed, 3 xfailed. XSD and Schematron pass on every snapshot except the two already listed in KNOWN_INVALID, which fail with the same errors as before.
  • npm run validate and the drift check are clean.

🤖 Generated with Claude Code

DDI Codebook 2.5 has no expression syntax, so each condition goes in twice
(convention:logicMapping, ddiEncoding):

- readable: `<universe clusion="I">` prose from the expression and the
  referenced labels, in the base language and each other language with a
  template (en, de); a simple numeric range also as `<valrng><range>`;
- exact: typed notes, last in var/varGrp: `cdl:relevant` and
  `cdl:constraint` (subject="xlsform-xpath"), `cdl:constraint_message` per
  xml:lang, `cdl:required`.

A select_multiple's logic is on its varGrp (the semi-open pair's on its
`other` varGrp), with the prose also on each binary var. Enclosing groups'
conditions are ANDed in until groups get a varGrp (#152). The or_other
companion gets pyxform's `${q} = 'other'` / `selected(${q}, 'other')`.

lstsv2ddi now reverses relevance and constraints; one outside the dialect
gets an `em-unsupported` warning and no note instead of failing.

The CDL Schematron allows any number of typed notes and one untyped note per
language (was: one notes element per var).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jstet
jstet merged commit f3aba25 into main Sep 27, 2026
4 checks passed
@jstet
jstet deleted the feat/151-ddi-logic branch September 27, 2026 15:57
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.

Convention: carry relevant/constraint in DDI as typed <notes> plus <universe> prose

1 participant