Skip to content

feat(registry): register range as a bounded numeric type - #36

Merged
jstet merged 1 commit into
mainfrom
feat/range-type
Sep 24, 2026
Merged

jstet merged 1 commit into
mainfrom
feat/range-type

Conversation

@jstet

@jstet jstet commented Sep 24, 2026

Copy link
Copy Markdown
Member

Closes #33.

What

range is now a registered type:

Output
DDI numeric / contin (like decimal), which now matches qwacback
LimeSurvey TSV N with min_num_value_n / max_num_value_n from start / end, and num_value_int_only=1 when start and step are whole numbers
Reverse (TSV → XLSForm) an N with both bounds becomes range, with parameters rebuilt

Three new registry schema fields drive the parameters column, so the converter has no range-specific code:

  • xlsform.parameters: the keys the type reads, with pyxform's defaults (start=1 end=10 step=1)
  • limesurvey.parameterAttributes: which parameter goes into which LS attribute
  • limesurvey.integerOnly: when to set num_value_int_only

A non-numeric bound throws. Otherwise LimeSurvey would import the question without bounds.

Limits

  • Step: LimeSurvey's N has no step attribute. So step=5 is enforced only as "integers only", and it reads back as step=1. This is documented in useWhen and in the lstsv2xlsform README's known losses.
  • Sliders: LimeSurvey sliders exist only on multiple-numeric K, so a range question renders as a bounded number input.

Verified

  • vitest: 829 passed. npm run validate is clean. pytest tests/validation (XSD + Schematron, including the new ddi.xml): 37 passed.
  • Live LimeSurvey, full npm run test:live: the range entity imports and its response round-trips (blessed expected/range.json). A new test imports testA and reads back min_num_value_n=0, max_num_value_n=100, num_value_int_only=1 from the stored question.
  • qwacback equivalence against qwacback main: range now matches, so its xfail is removed. Only note remains, and that one is by design.

Snapshots

  • New entity registry/entities/range/ (fixture, tsv, ddi, docs, xlsx).
  • testA now has tsv.tsv/ddi.xml. Its reverse DDI renames three select_multiple variables through LimeSurvey's 5-character answer codes, which is recorded in REVERSE_DDI_STRUCTURAL_DIFF.

Also fixed

🤖 Generated with Claude Code

`range` wasn't in the registry: TSV conversion threw, subset validation
rejected it, and buildDdiXml without validation fell back to text, while
qwacback (and survey2ddi before it) emitted numeric/contin.

Registry: type:range, DDI numeric/contin (like decimal), LimeSurvey N. Three
new schema fields drive the parameters column without range-specific code:
- xlsform.parameters: keys the type reads, with pyxform's defaults
  (start=1 end=10 step=1)
- limesurvey.parameterAttributes: start → min_num_value_n,
  end → max_num_value_n
- limesurvey.integerOnly: num_value_int_only=1 when start and step are whole

src/pipelines/xlsform2lstsv/parameters.ts applies them. A non-numeric bound
throws, since LimeSurvey would otherwise import the question unbounded.
LimeSurvey has no step on N, so step is enforced only as integer vs. decimal.

Reverse (lstsv2xlsform): an N carrying both bounds reads back as range, with
step=1 when integer-only. A non-1 step isn't recoverable (README known losses).

Snapshots: new registry/entities/range (xlsform, tsv, ddi, docs, xlsx, live
answers + blessed response); testA gets tsv/ddi snapshots, its reverse DDI
diff (5-char code truncation) recorded in REVERSE_DDI_STRUCTURAL_DIFF.

Tests: range unit tests (bounds, defaults, fractional step, bad bound, DDI,
reverse); testA integration now checks the bounds; the live suite checks
LimeSurvey stores them after import. The qwacback equivalence case for range
passes, so its xfail is gone.

Also fixed on the way: the live long-list vocab test read
<entity>/xlsform.json instead of <entity>/fixtures/xlsform.json and failed
on main. HANDOVER_CDL_WP_EINS.md's supported-type table predated #24 and this.

Closes #33

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jstet
jstet merged commit 7becf1d into main Sep 24, 2026
4 checks passed
@jstet
jstet deleted the feat/range-type branch September 24, 2026 13:53
@jstet jstet mentioned this pull request Sep 24, 2026
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.

range is not a registered type: xlsform2ddi emits it as text, qwacback as numeric

1 participant