lens-content-processor 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-markup.d.ts +26 -1
- package/dist/authoring-markup.js +67 -16
- package/dist/authoring-markup.js.map +1 -1
- package/dist/bundler/article.d.ts +69 -9
- package/dist/bundler/article.js +159 -58
- package/dist/bundler/article.js.map +1 -1
- package/dist/content-schema.d.ts +3 -1
- package/dist/content-schema.js +51 -12
- package/dist/content-schema.js.map +1 -1
- package/dist/dedupe-errors.js +1 -7
- package/dist/dedupe-errors.js.map +1 -1
- package/dist/flattener/index.d.ts +23 -0
- package/dist/flattener/index.js +379 -262
- package/dist/flattener/index.js.map +1 -1
- package/dist/index.d.ts +29 -2
- package/dist/index.js +84 -15
- package/dist/index.js.map +1 -1
- package/dist/parser/article.js +13 -5
- package/dist/parser/article.js.map +1 -1
- package/dist/parser/course.js +19 -6
- package/dist/parser/course.js.map +1 -1
- package/dist/parser/learning-outcome.d.ts +0 -1
- package/dist/parser/learning-outcome.js +55 -18
- package/dist/parser/learning-outcome.js.map +1 -1
- package/dist/parser/lens.d.ts +6 -1
- package/dist/parser/lens.js +47 -41
- package/dist/parser/lens.js.map +1 -1
- package/dist/parser/module.js +9 -5
- package/dist/parser/module.js.map +1 -1
- package/dist/parser/response-segments.d.ts +54 -0
- package/dist/parser/response-segments.js +499 -0
- package/dist/parser/response-segments.js.map +1 -0
- package/dist/parser/survey.js +56 -15
- package/dist/parser/survey.js.map +1 -1
- package/dist/validator/article-structure.js +4 -18
- package/dist/validator/article-structure.js.map +1 -1
- package/dist/validator/directives.js +20 -42
- package/dist/validator/directives.js.map +1 -1
- package/dist/validator/emphasis.js +34 -55
- package/dist/validator/emphasis.js.map +1 -1
- package/dist/validator/html-tags.js +111 -103
- package/dist/validator/html-tags.js.map +1 -1
- package/dist/validator/markdown-parse.d.ts +3 -0
- package/dist/validator/markdown-parse.js +22 -0
- package/dist/validator/markdown-parse.js.map +1 -0
- package/dist/validator/output-integrity.d.ts +1 -1
- package/dist/validator/output-integrity.js +17 -23
- package/dist/validator/output-integrity.js.map +1 -1
- package/dist/validator/segment-fields.d.ts +4 -2
- package/dist/validator/segment-fields.js +27 -5
- package/dist/validator/segment-fields.js.map +1 -1
- package/dist/validator/test-segments.d.ts +11 -0
- package/dist/validator/test-segments.js +45 -0
- package/dist/validator/test-segments.js.map +1 -1
- package/dist/validator/uuid.d.ts +1 -2
- package/dist/validator/uuid.js +4 -6
- package/dist/validator/uuid.js.map +1 -1
- package/dist/validator/video-imports.d.ts +35 -0
- package/dist/validator/video-imports.js +183 -0
- package/dist/validator/video-imports.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
// src/parser/response-segments.ts
|
|
2
|
+
//
|
|
3
|
+
// Response (subtyped question) segments: `#### Question: Open|Rating|Choice|
|
|
4
|
+
// FillBlank|Ranking`. One segment family collects one learner response per
|
|
5
|
+
// segment, with the same syntax in lenses, Learning Outcome tests, and
|
|
6
|
+
// surveys. Context determines grading: surveys never grade. A bare
|
|
7
|
+
// `#### Question` (no title) stays the legacy segment everywhere and never
|
|
8
|
+
// reaches this module.
|
|
9
|
+
import { isValidUuid } from "../validator/uuid.js";
|
|
10
|
+
import { stripQuotes, validatePromptImportSyntax, } from "./lens.js";
|
|
11
|
+
export const RESPONSE_QUESTION_SUBTYPES = [
|
|
12
|
+
"open",
|
|
13
|
+
"rating",
|
|
14
|
+
"choice",
|
|
15
|
+
"fillblank",
|
|
16
|
+
"ranking",
|
|
17
|
+
];
|
|
18
|
+
/** Author-facing capitalization, for error messages. */
|
|
19
|
+
const SUBTYPE_DISPLAY = {
|
|
20
|
+
open: "Open",
|
|
21
|
+
rating: "Rating",
|
|
22
|
+
choice: "Choice",
|
|
23
|
+
fillblank: "FillBlank",
|
|
24
|
+
ranking: "Ranking",
|
|
25
|
+
};
|
|
26
|
+
const VALID_SUBTYPES_HINT = RESPONSE_QUESTION_SUBTYPES.map((s) => SUBTYPE_DISPLAY[s]).join(", ");
|
|
27
|
+
const DEFAULT_RATING_SCALE = 5;
|
|
28
|
+
const MIN_RATING_SCALE = 2;
|
|
29
|
+
const MAX_RATING_SCALE = 10;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a raw segment to its "question:<subtype>" schema key, for field
|
|
32
|
+
* validation against QUESTION_SUBTYPE_SCHEMAS. Returns null for anything
|
|
33
|
+
* that is not a titled question segment with a known subtype (legacy
|
|
34
|
+
* questions fall back to the plain "question" schema).
|
|
35
|
+
*/
|
|
36
|
+
export function responseSegmentSchemaKey(raw) {
|
|
37
|
+
if (raw.type !== "question" || !raw.title)
|
|
38
|
+
return null;
|
|
39
|
+
const subtype = raw.title.trim().toLowerCase();
|
|
40
|
+
return RESPONSE_QUESTION_SUBTYPES.includes(subtype)
|
|
41
|
+
? `question:${subtype}`
|
|
42
|
+
: null;
|
|
43
|
+
}
|
|
44
|
+
function parseBool(raw, field) {
|
|
45
|
+
return raw.fields[field]?.toLowerCase() === "true" ? true : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Parse a positive-integer field; error and drop on anything else. */
|
|
48
|
+
function parsePositiveInt(raw, field, file, errors) {
|
|
49
|
+
const value = raw.fields[field];
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
const trimmed = value.trim();
|
|
53
|
+
if (!/^\d+$/.test(trimmed) || parseInt(trimmed, 10) === 0) {
|
|
54
|
+
errors.push({
|
|
55
|
+
file,
|
|
56
|
+
line: raw.line,
|
|
57
|
+
message: `Field '${field}' must be a positive integer, got '${value}'`,
|
|
58
|
+
suggestion: `Use a number like '${field}:: 500', or remove the field`,
|
|
59
|
+
severity: "error",
|
|
60
|
+
});
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return parseInt(trimmed, 10);
|
|
64
|
+
}
|
|
65
|
+
/** Parse scale:: as an integer in [2, 10] (default 5). Null = hard error. */
|
|
66
|
+
function parseScale(raw, file, errors) {
|
|
67
|
+
const value = raw.fields.scale;
|
|
68
|
+
if (value === undefined)
|
|
69
|
+
return DEFAULT_RATING_SCALE;
|
|
70
|
+
const trimmed = value.trim();
|
|
71
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
72
|
+
errors.push({
|
|
73
|
+
file,
|
|
74
|
+
line: raw.line,
|
|
75
|
+
message: `Field 'scale' must be an integer, got '${value}'`,
|
|
76
|
+
suggestion: `Use a number from ${MIN_RATING_SCALE} to ${MAX_RATING_SCALE} (default ${DEFAULT_RATING_SCALE}), or omit the field`,
|
|
77
|
+
severity: "error",
|
|
78
|
+
});
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const scale = parseInt(trimmed, 10);
|
|
82
|
+
if (scale < MIN_RATING_SCALE || scale > MAX_RATING_SCALE) {
|
|
83
|
+
errors.push({
|
|
84
|
+
file,
|
|
85
|
+
line: raw.line,
|
|
86
|
+
message: `Field 'scale' is ${scale}; ratings support ${MIN_RATING_SCALE}–${MAX_RATING_SCALE}`,
|
|
87
|
+
suggestion: `Use a value from ${MIN_RATING_SCALE} to ${MAX_RATING_SCALE}`,
|
|
88
|
+
severity: "error",
|
|
89
|
+
});
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return scale;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse a multiline `- Item` list field shared by choice options:: and
|
|
96
|
+
* ranking items::. `allowCorrectMarks` (choice in lens/LO context) accepts
|
|
97
|
+
* `- [x] Item` and records its index; explicit-unchecked `- [ ]` is always
|
|
98
|
+
* an error, as is checkbox syntax where none is allowed.
|
|
99
|
+
*/
|
|
100
|
+
function parseListField(raw, field, allowCorrectMarks, file, errors) {
|
|
101
|
+
const value = raw.fields[field];
|
|
102
|
+
const capitalized = field === "options" ? "Choice" : "Ranking";
|
|
103
|
+
if (value === undefined || value.trim() === "") {
|
|
104
|
+
errors.push({
|
|
105
|
+
file,
|
|
106
|
+
line: raw.line,
|
|
107
|
+
message: `${capitalized} segment is missing ${field}::`,
|
|
108
|
+
suggestion: `Add a list:\n${field}::\n- First ${field === "options" ? "option" : "item"}\n- Second ${field === "options" ? "option" : "item"}`,
|
|
109
|
+
severity: "error",
|
|
110
|
+
});
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const items = [];
|
|
114
|
+
const correct = [];
|
|
115
|
+
for (const line of value.split("\n")) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
if (!trimmed)
|
|
118
|
+
continue;
|
|
119
|
+
const uncheckedBox = trimmed.match(/^-\s*\[\s?\]\s*(.*)$/);
|
|
120
|
+
if (uncheckedBox) {
|
|
121
|
+
errors.push({
|
|
122
|
+
file,
|
|
123
|
+
line: raw.line,
|
|
124
|
+
message: `Explicit-unchecked checkbox syntax is not supported in ${field}::: "${trimmed}"`,
|
|
125
|
+
suggestion: allowCorrectMarks
|
|
126
|
+
? "Use plain '- Option text' items; mark correct options with '- [x] Option text'"
|
|
127
|
+
: "Use plain '- Item text' items without brackets",
|
|
128
|
+
severity: "error",
|
|
129
|
+
});
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const checkedBox = trimmed.match(/^-\s*\[[xX]\]\s*(.*)$/);
|
|
133
|
+
if (checkedBox) {
|
|
134
|
+
if (!allowCorrectMarks) {
|
|
135
|
+
errors.push({
|
|
136
|
+
file,
|
|
137
|
+
line: raw.line,
|
|
138
|
+
message: field === "options"
|
|
139
|
+
? `Survey choices never have correct answers, got checkbox syntax: "${trimmed}"`
|
|
140
|
+
: `Ranking items must be plain list items, got checkbox syntax: "${trimmed}"`,
|
|
141
|
+
suggestion: field === "options"
|
|
142
|
+
? "Use plain '- Option text' items without brackets"
|
|
143
|
+
: "Use plain '- Item text' items in the intended order — the intended order is the answer",
|
|
144
|
+
severity: "error",
|
|
145
|
+
});
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const text = checkedBox[1].trim();
|
|
149
|
+
if (!text) {
|
|
150
|
+
errors.push({
|
|
151
|
+
file,
|
|
152
|
+
line: raw.line,
|
|
153
|
+
message: `Empty ${field}:: list item: "${trimmed}"`,
|
|
154
|
+
suggestion: "Every list item needs text after the marker",
|
|
155
|
+
severity: "error",
|
|
156
|
+
});
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
correct.push(items.length);
|
|
160
|
+
items.push(text);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const item = trimmed.match(/^-\s+(.+)$/);
|
|
164
|
+
if (!item) {
|
|
165
|
+
errors.push({
|
|
166
|
+
file,
|
|
167
|
+
line: raw.line,
|
|
168
|
+
message: `Line in ${field}:: is not a list item: "${trimmed}"`,
|
|
169
|
+
suggestion: `Every entry must be on its own '- ${field === "options" ? "Option" : "Item"} text' line`,
|
|
170
|
+
severity: "error",
|
|
171
|
+
});
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
items.push(item[1].trim());
|
|
175
|
+
}
|
|
176
|
+
if (items.length < 2) {
|
|
177
|
+
errors.push({
|
|
178
|
+
file,
|
|
179
|
+
line: raw.line,
|
|
180
|
+
message: `${capitalized} segment needs at least 2 ${field}, got ${items.length}`,
|
|
181
|
+
suggestion: `Add more '- …' lines under ${field}::`,
|
|
182
|
+
severity: "error",
|
|
183
|
+
});
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return { items, correct };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A number reference for a graded `{{number <ref>}}` blank: optional minus,
|
|
190
|
+
* digits either plain or with comma thousands separators in exact 3-digit
|
|
191
|
+
* groups, optional decimal part.
|
|
192
|
+
*/
|
|
193
|
+
const REFERENCE_NUMBER = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/;
|
|
194
|
+
/**
|
|
195
|
+
* Parse `{{…}}` blanks out of a fillblank content:: value:
|
|
196
|
+
* - `{{blank}}` — ungraded text blank
|
|
197
|
+
* - `{{number}}` — ungraded number blank
|
|
198
|
+
* - `{{number <ref>}}` — graded number blank (reference validated)
|
|
199
|
+
* - anything else — graded text blank, `|`-separated alternatives
|
|
200
|
+
*/
|
|
201
|
+
function parseFillBlanks(content, raw, file, errors) {
|
|
202
|
+
const blanks = [];
|
|
203
|
+
let failed = false;
|
|
204
|
+
const rewritten = content.replace(/\{\{(.*?)\}\}/gs, (match, inner) => {
|
|
205
|
+
const index = blanks.length;
|
|
206
|
+
const trimmed = String(inner).trim();
|
|
207
|
+
const keyword = trimmed.toLowerCase();
|
|
208
|
+
if (trimmed === "") {
|
|
209
|
+
errors.push({
|
|
210
|
+
file,
|
|
211
|
+
line: raw.line,
|
|
212
|
+
message: "Empty blank {{}} in fillblank content::",
|
|
213
|
+
suggestion: "Use {{blank}} for an ungraded blank, or {{expected answer}} for a graded one",
|
|
214
|
+
severity: "error",
|
|
215
|
+
});
|
|
216
|
+
failed = true;
|
|
217
|
+
return match;
|
|
218
|
+
}
|
|
219
|
+
if (keyword === "blank") {
|
|
220
|
+
blanks.push({ kind: "text", graded: false });
|
|
221
|
+
return `{{${index}}}`;
|
|
222
|
+
}
|
|
223
|
+
if (keyword === "number") {
|
|
224
|
+
blanks.push({ kind: "number", graded: false });
|
|
225
|
+
return `{{${index}}}`;
|
|
226
|
+
}
|
|
227
|
+
const numberRef = trimmed.match(/^number\s+(.+)$/i);
|
|
228
|
+
if (numberRef) {
|
|
229
|
+
const ref = numberRef[1].trim();
|
|
230
|
+
if (!REFERENCE_NUMBER.test(ref)) {
|
|
231
|
+
errors.push({
|
|
232
|
+
file,
|
|
233
|
+
line: raw.line,
|
|
234
|
+
message: `Malformed reference number in blank {{${trimmed}}}: '${ref}'`,
|
|
235
|
+
suggestion: "Use digits with optional comma thousands separators and decimal part, e.g. {{number 149,600,000}} or {{number 3.14}}",
|
|
236
|
+
severity: "error",
|
|
237
|
+
});
|
|
238
|
+
failed = true;
|
|
239
|
+
return match;
|
|
240
|
+
}
|
|
241
|
+
blanks.push({ kind: "number", graded: true, referenceNumber: ref });
|
|
242
|
+
return `{{${index}}}`;
|
|
243
|
+
}
|
|
244
|
+
const alternatives = trimmed.split("|").map((alt) => alt.trim());
|
|
245
|
+
if (alternatives.some((alt) => alt === "")) {
|
|
246
|
+
errors.push({
|
|
247
|
+
file,
|
|
248
|
+
line: raw.line,
|
|
249
|
+
message: `Empty alternative in blank {{${trimmed}}}`,
|
|
250
|
+
suggestion: "Separate accepted answers with '|', each non-empty: {{Paris|paris}}",
|
|
251
|
+
severity: "error",
|
|
252
|
+
});
|
|
253
|
+
failed = true;
|
|
254
|
+
return match;
|
|
255
|
+
}
|
|
256
|
+
blanks.push({ kind: "text", graded: true, expected: alternatives });
|
|
257
|
+
return `{{${index}}}`;
|
|
258
|
+
});
|
|
259
|
+
if (failed)
|
|
260
|
+
return null;
|
|
261
|
+
if (blanks.length === 0) {
|
|
262
|
+
errors.push({
|
|
263
|
+
file,
|
|
264
|
+
line: raw.line,
|
|
265
|
+
message: "FillBlank segment has no {{…}} blanks in content::",
|
|
266
|
+
suggestion: "Add at least one blank, e.g. 'The capital of France is {{Paris}}.'",
|
|
267
|
+
severity: "error",
|
|
268
|
+
});
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return { content: rewritten, blanks };
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Parse a `#### Question: <Subtype>` segment. Shared by lens parsing
|
|
275
|
+
* (context "lens", also used for LO tests via convertSegment) and survey
|
|
276
|
+
* parsing (context "survey": never graded, `[x]` forbidden,
|
|
277
|
+
* assessment-instructions warned but passed through).
|
|
278
|
+
*/
|
|
279
|
+
export function parseResponseSegment(raw, context, file) {
|
|
280
|
+
const errors = [];
|
|
281
|
+
const subtypeRaw = (raw.title ?? "").trim();
|
|
282
|
+
const subtype = subtypeRaw.toLowerCase();
|
|
283
|
+
if (!RESPONSE_QUESTION_SUBTYPES.includes(subtype)) {
|
|
284
|
+
errors.push({
|
|
285
|
+
file,
|
|
286
|
+
line: raw.line,
|
|
287
|
+
message: `Unknown question subtype '${subtypeRaw}'`,
|
|
288
|
+
suggestion: `Valid subtypes: ${VALID_SUBTYPES_HINT} (e.g. '#### Question: Open'), or use plain '#### Question' for a legacy question`,
|
|
289
|
+
severity: "error",
|
|
290
|
+
});
|
|
291
|
+
return { segment: null, errors };
|
|
292
|
+
}
|
|
293
|
+
const questionType = subtype;
|
|
294
|
+
const display = `Question: ${SUBTYPE_DISPLAY[questionType]}`;
|
|
295
|
+
// id:: — plain UUID, quotes stripped like roleplay ids (a quoted id would
|
|
296
|
+
// reach the frontend verbatim and fail server-side UUID parsing).
|
|
297
|
+
const rawId = raw.fields.id?.trim();
|
|
298
|
+
const responseId = rawId ? stripQuotes(rawId).trim() : rawId;
|
|
299
|
+
if (!responseId) {
|
|
300
|
+
errors.push({
|
|
301
|
+
file,
|
|
302
|
+
line: raw.line,
|
|
303
|
+
message: `${display} segment missing id:: field`,
|
|
304
|
+
suggestion: "Add 'id:: <uuid>' — the id is the stable response key and must never change",
|
|
305
|
+
severity: "error",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
else if (!isValidUuid(responseId)) {
|
|
309
|
+
errors.push({
|
|
310
|
+
file,
|
|
311
|
+
line: raw.line,
|
|
312
|
+
message: `${display} id:: is not a valid UUID: ${responseId}`,
|
|
313
|
+
suggestion: "Use a plain UUID, e.g. 'id:: 17a55209-deab-48c8-afb0-fc522722fe8f'",
|
|
314
|
+
severity: "error",
|
|
315
|
+
});
|
|
316
|
+
return { segment: null, errors };
|
|
317
|
+
}
|
|
318
|
+
const content = raw.fields.content;
|
|
319
|
+
if (!content || content.trim() === "") {
|
|
320
|
+
errors.push({
|
|
321
|
+
file,
|
|
322
|
+
line: raw.line,
|
|
323
|
+
message: `${display} segment missing content:: field`,
|
|
324
|
+
suggestion: "Add 'content:: Your question here'",
|
|
325
|
+
severity: "error",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (!responseId || !content || content.trim() === "") {
|
|
329
|
+
return { segment: null, errors };
|
|
330
|
+
}
|
|
331
|
+
// assessment-instructions:: — grading rubric. Ratings are never graded;
|
|
332
|
+
// surveys never grade (warn, but pass the field through).
|
|
333
|
+
let assessmentInstructions = raw.fields["assessment-instructions"] || undefined;
|
|
334
|
+
if (assessmentInstructions && questionType === "rating") {
|
|
335
|
+
errors.push({
|
|
336
|
+
file,
|
|
337
|
+
line: raw.line,
|
|
338
|
+
message: "assessment-instructions:: on a Question: Rating segment — ratings are never graded",
|
|
339
|
+
suggestion: "Remove the assessment-instructions:: field",
|
|
340
|
+
severity: "error",
|
|
341
|
+
});
|
|
342
|
+
assessmentInstructions = undefined;
|
|
343
|
+
}
|
|
344
|
+
if (assessmentInstructions) {
|
|
345
|
+
errors.push(...validatePromptImportSyntax(assessmentInstructions, "assessment-instructions", file, raw.line));
|
|
346
|
+
if (context === "survey") {
|
|
347
|
+
errors.push({
|
|
348
|
+
file,
|
|
349
|
+
line: raw.line,
|
|
350
|
+
message: "assessment-instructions:: in a survey segment — surveys never grade, so this field will be ignored",
|
|
351
|
+
suggestion: "Remove the field, or move the segment into a lens",
|
|
352
|
+
severity: "warning",
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const feedbackInstructions = raw.fields["feedback-instructions"] || undefined;
|
|
357
|
+
if (feedbackInstructions) {
|
|
358
|
+
errors.push(...validatePromptImportSyntax(feedbackInstructions, "feedback-instructions", file, raw.line));
|
|
359
|
+
}
|
|
360
|
+
const segment = {
|
|
361
|
+
type: "question",
|
|
362
|
+
questionType,
|
|
363
|
+
// Canonical lowercase: the backend matches segment ids against Postgres
|
|
364
|
+
// uuid strings (always lowercase), so the cache must never carry an
|
|
365
|
+
// uppercase-authored id.
|
|
366
|
+
responseId: responseId.toLowerCase(),
|
|
367
|
+
content,
|
|
368
|
+
graded: false, // derived per subtype below; always false in surveys
|
|
369
|
+
assessmentInstructions,
|
|
370
|
+
feedbackInstructions,
|
|
371
|
+
optional: parseBool(raw, "optional"),
|
|
372
|
+
};
|
|
373
|
+
switch (questionType) {
|
|
374
|
+
case "open": {
|
|
375
|
+
segment.maxChars = parsePositiveInt(raw, "max-chars", file, errors);
|
|
376
|
+
segment.maxTime = raw.fields["max-time"] || undefined;
|
|
377
|
+
const placeholder = raw.fields.placeholder?.trim();
|
|
378
|
+
if (placeholder)
|
|
379
|
+
segment.placeholder = placeholder;
|
|
380
|
+
segment.enforceVoice = parseBool(raw, "enforce-voice");
|
|
381
|
+
segment.graded = Boolean(assessmentInstructions);
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case "rating": {
|
|
385
|
+
const scale = parseScale(raw, file, errors);
|
|
386
|
+
if (scale === null)
|
|
387
|
+
return { segment: null, errors };
|
|
388
|
+
segment.scale = scale;
|
|
389
|
+
const lowLabel = raw.fields["low-label"]?.trim();
|
|
390
|
+
const highLabel = raw.fields["high-label"]?.trim();
|
|
391
|
+
if (lowLabel)
|
|
392
|
+
segment.lowLabel = lowLabel;
|
|
393
|
+
if (highLabel)
|
|
394
|
+
segment.highLabel = highLabel;
|
|
395
|
+
segment.graded = false;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case "choice": {
|
|
399
|
+
const parsed = parseListField(raw, "options", context === "lens", file, errors);
|
|
400
|
+
if (parsed === null)
|
|
401
|
+
return { segment: null, errors };
|
|
402
|
+
segment.multi = parseBool(raw, "multi");
|
|
403
|
+
if (parsed.correct.length > 1 && !segment.multi) {
|
|
404
|
+
// A single-choice submission carries exactly one selection, so a
|
|
405
|
+
// multi-option correct set could never be matched — every learner
|
|
406
|
+
// would score 0.
|
|
407
|
+
errors.push({
|
|
408
|
+
file,
|
|
409
|
+
line: raw.line,
|
|
410
|
+
message: `Choice has ${parsed.correct.length} correct options but is single-choice — add 'multi:: true' or mark exactly one option`,
|
|
411
|
+
suggestion: "Add 'multi:: true' to allow multiple selections, or keep exactly one '- [x]' option",
|
|
412
|
+
severity: "error",
|
|
413
|
+
});
|
|
414
|
+
return { segment: null, errors };
|
|
415
|
+
}
|
|
416
|
+
segment.options = parsed.items;
|
|
417
|
+
if (parsed.correct.length > 0)
|
|
418
|
+
segment.correctOptions = parsed.correct;
|
|
419
|
+
segment.shuffle = parseBool(raw, "shuffle");
|
|
420
|
+
segment.graded = parsed.correct.length > 0;
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
case "fillblank": {
|
|
424
|
+
const parsed = parseFillBlanks(content, raw, file, errors);
|
|
425
|
+
if (parsed === null)
|
|
426
|
+
return { segment: null, errors };
|
|
427
|
+
segment.content = parsed.content;
|
|
428
|
+
segment.blanks = parsed.blanks;
|
|
429
|
+
segment.graded =
|
|
430
|
+
parsed.blanks.some((b) => b.graded) || Boolean(assessmentInstructions);
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case "ranking": {
|
|
434
|
+
const parsed = parseListField(raw, "items", false, file, errors);
|
|
435
|
+
if (parsed === null)
|
|
436
|
+
return { segment: null, errors };
|
|
437
|
+
segment.items = parsed.items;
|
|
438
|
+
segment.graded = Boolean(assessmentInstructions);
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (context === "survey")
|
|
443
|
+
segment.graded = false;
|
|
444
|
+
return { segment, errors };
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Map a parsed response segment to its flattened QuestionSegment shape.
|
|
448
|
+
* Used directly by survey parsing (surveys have no flatten pass) and by the
|
|
449
|
+
* lens flattener, which then replaces assessmentInstructions /
|
|
450
|
+
* feedbackInstructions with their prompt-import-expanded values.
|
|
451
|
+
*/
|
|
452
|
+
export function toFlattenedResponseSegment(parsed) {
|
|
453
|
+
const segment = {
|
|
454
|
+
type: "question",
|
|
455
|
+
questionType: parsed.questionType,
|
|
456
|
+
responseId: parsed.responseId,
|
|
457
|
+
content: parsed.content,
|
|
458
|
+
graded: parsed.graded,
|
|
459
|
+
};
|
|
460
|
+
if (parsed.assessmentInstructions)
|
|
461
|
+
segment.assessmentInstructions = parsed.assessmentInstructions;
|
|
462
|
+
if (parsed.feedbackInstructions)
|
|
463
|
+
segment.feedbackInstructions = parsed.feedbackInstructions;
|
|
464
|
+
if (parsed.optional)
|
|
465
|
+
segment.optional = true;
|
|
466
|
+
// open:
|
|
467
|
+
if (parsed.maxTime)
|
|
468
|
+
segment.maxTime = parsed.maxTime;
|
|
469
|
+
if (parsed.maxChars !== undefined)
|
|
470
|
+
segment.maxChars = parsed.maxChars;
|
|
471
|
+
if (parsed.enforceVoice)
|
|
472
|
+
segment.enforceVoice = true;
|
|
473
|
+
if (parsed.placeholder)
|
|
474
|
+
segment.placeholder = parsed.placeholder;
|
|
475
|
+
// rating:
|
|
476
|
+
if (parsed.scale !== undefined)
|
|
477
|
+
segment.scale = parsed.scale;
|
|
478
|
+
if (parsed.lowLabel)
|
|
479
|
+
segment.lowLabel = parsed.lowLabel;
|
|
480
|
+
if (parsed.highLabel)
|
|
481
|
+
segment.highLabel = parsed.highLabel;
|
|
482
|
+
// choice:
|
|
483
|
+
if (parsed.options)
|
|
484
|
+
segment.options = parsed.options;
|
|
485
|
+
if (parsed.correctOptions)
|
|
486
|
+
segment.correctOptions = parsed.correctOptions;
|
|
487
|
+
if (parsed.multi)
|
|
488
|
+
segment.multi = true;
|
|
489
|
+
if (parsed.shuffle)
|
|
490
|
+
segment.shuffle = true;
|
|
491
|
+
// fillblank:
|
|
492
|
+
if (parsed.blanks)
|
|
493
|
+
segment.blanks = parsed.blanks;
|
|
494
|
+
// ranking:
|
|
495
|
+
if (parsed.items)
|
|
496
|
+
segment.items = parsed.items;
|
|
497
|
+
return segment;
|
|
498
|
+
}
|
|
499
|
+
//# sourceMappingURL=response-segments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-segments.js","sourceRoot":"","sources":["../../src/parser/response-segments.ts"],"names":[],"mappings":"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"}
|
package/dist/parser/survey.js
CHANGED
|
@@ -9,16 +9,21 @@
|
|
|
9
9
|
// breaking response continuity (and the platform can special-case keys like
|
|
10
10
|
// `buddy_texted`).
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
// correct answers, no AI assessment, and no reveal
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Legacy survey segments deliberately do NOT reuse the lens question
|
|
13
|
+
// machinery: there are no correct answers, no AI assessment, and no reveal
|
|
14
|
+
// semantics. New-style response segments (`#### Question: <Subtype>` with a
|
|
15
|
+
// stable id:: instead of key::) DO share the lens parser
|
|
16
|
+
// (parser/response-segments.ts), but in survey context: never graded, and
|
|
17
|
+
// correct-answer marks are forbidden.
|
|
16
18
|
import { parseFrontmatter } from "./frontmatter.js";
|
|
17
19
|
import { parseSections } from "./sections.js";
|
|
18
20
|
import { validateFrontmatter } from "../validator/validate-frontmatter.js";
|
|
19
21
|
import { detectFieldTypos } from "../validator/field-typos.js";
|
|
22
|
+
import { validateSegmentFields } from "../validator/segment-fields.js";
|
|
23
|
+
import { validateFieldValues } from "../validator/field-values.js";
|
|
20
24
|
import { stripAuthoringMarkup, stripAuthoringMarkupForValidation, usePublishedStrings, } from "../authoring-markup.js";
|
|
21
25
|
import { SURVEY_SEGMENT_SCHEMAS } from "../content-schema.js";
|
|
26
|
+
import { parseResponseSegment, responseSegmentSchemaKey, toFlattenedResponseSegment, } from "./response-segments.js";
|
|
22
27
|
// Valid segment types for survey H4 headers
|
|
23
28
|
export const SURVEY_SEGMENT_TYPES = new Set([
|
|
24
29
|
"text",
|
|
@@ -233,6 +238,18 @@ function validateSurveySegmentFields(raw, file) {
|
|
|
233
238
|
}
|
|
234
239
|
function convertSurveySegment(raw, file) {
|
|
235
240
|
const errors = [];
|
|
241
|
+
// `#### Question: <Subtype>` — new-style response segment, shared with
|
|
242
|
+
// lens parsing but in survey context: keyed by id:: (no key::), never
|
|
243
|
+
// graded, correct-answer marks forbidden. Legacy survey segments
|
|
244
|
+
// (untitled Question, and Rating/Choice) are untouched below.
|
|
245
|
+
if (raw.type === "question" && raw.title) {
|
|
246
|
+
const { segment, errors: responseErrors } = parseResponseSegment(raw, "survey", file);
|
|
247
|
+
errors.push(...responseErrors);
|
|
248
|
+
return {
|
|
249
|
+
segment: segment ? toFlattenedResponseSegment(segment) : null,
|
|
250
|
+
errors,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
236
253
|
if (raw.title) {
|
|
237
254
|
const capitalized = raw.type[0].toUpperCase() + raw.type.slice(1);
|
|
238
255
|
errors.push({
|
|
@@ -348,30 +365,50 @@ function parseSurveyPrepared(content, file) {
|
|
|
348
365
|
}
|
|
349
366
|
const title = typeof frontmatter.title === "string" ? frontmatter.title : undefined;
|
|
350
367
|
// Flat H4 segments, same shape as lens bodies
|
|
351
|
-
const { sections: rawSegments, errors: segmentErrors } = parseSections(body, 3, SURVEY_SEGMENT_TYPES, file, true
|
|
368
|
+
const { sections: rawSegments, errors: segmentErrors } = parseSections(body, 3, SURVEY_SEGMENT_TYPES, file, true);
|
|
369
|
+
for (const error of segmentErrors) {
|
|
370
|
+
if (error.line)
|
|
371
|
+
error.line += bodyStartLine - 1;
|
|
372
|
+
}
|
|
352
373
|
errors.push(...segmentErrors);
|
|
353
374
|
const segments = [];
|
|
354
375
|
const seenKeys = new Map(); // key -> first line
|
|
355
376
|
for (const rawSeg of rawSegments) {
|
|
356
|
-
|
|
377
|
+
rawSeg.line += bodyStartLine - 1;
|
|
378
|
+
// New-style response segments validate against their
|
|
379
|
+
// "question:<subtype>" schema; everything else against the survey
|
|
380
|
+
// vocabulary.
|
|
381
|
+
const responseSchemaKey = responseSegmentSchemaKey(rawSeg);
|
|
382
|
+
if (responseSchemaKey) {
|
|
383
|
+
errors.push(...validateSegmentFields(responseSchemaKey, rawSeg.fields, file, rawSeg.line));
|
|
384
|
+
errors.push(...validateFieldValues(rawSeg.fields, file, rawSeg.line));
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
errors.push(...validateSurveySegmentFields(rawSeg, file));
|
|
388
|
+
}
|
|
357
389
|
errors.push(...detectFieldTypos(rawSeg.fields, file, rawSeg.line));
|
|
358
390
|
const { segment, errors: conversionErrors } = convertSurveySegment(rawSeg, file);
|
|
359
391
|
errors.push(...conversionErrors);
|
|
360
392
|
if (!segment)
|
|
361
393
|
continue;
|
|
362
394
|
if (segment.type !== "text") {
|
|
363
|
-
|
|
395
|
+
// Answerable segments need a unique identifier: legacy segments their
|
|
396
|
+
// key::, new-style response segments their id:: (responseId).
|
|
397
|
+
const isLegacy = "key" in segment;
|
|
398
|
+
const dedupKey = isLegacy ? segment.key : segment.responseId;
|
|
399
|
+
const label = isLegacy ? "key" : "id";
|
|
400
|
+
const firstLine = seenKeys.get(dedupKey);
|
|
364
401
|
if (firstLine !== undefined) {
|
|
365
402
|
errors.push({
|
|
366
403
|
file,
|
|
367
404
|
line: rawSeg.line,
|
|
368
|
-
message: `Duplicate
|
|
369
|
-
suggestion:
|
|
405
|
+
message: `Duplicate ${label} '${dedupKey}' (already used at line ${firstLine})`,
|
|
406
|
+
suggestion: `Every answerable segment needs a unique ${label}`,
|
|
370
407
|
severity: "error",
|
|
371
408
|
});
|
|
372
409
|
continue;
|
|
373
410
|
}
|
|
374
|
-
seenKeys.set(
|
|
411
|
+
seenKeys.set(dedupKey, rawSeg.line);
|
|
375
412
|
}
|
|
376
413
|
segments.push(segment);
|
|
377
414
|
}
|
|
@@ -395,14 +432,18 @@ function parseSurveyPrepared(content, file) {
|
|
|
395
432
|
export function parseSurvey(content, file) {
|
|
396
433
|
const validationSource = stripAuthoringMarkupForValidation(content);
|
|
397
434
|
const result = parseSurveyPrepared(validationSource, file);
|
|
398
|
-
if (!result.survey)
|
|
399
|
-
return result;
|
|
400
435
|
const publishedSource = stripAuthoringMarkup(content);
|
|
401
436
|
if (publishedSource === validationSource)
|
|
402
437
|
return result;
|
|
403
438
|
const published = parseSurveyPrepared(publishedSource, file);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
439
|
+
// See parseLens: graft only when both parses have the same shape; otherwise
|
|
440
|
+
// (or when only the publishing parse succeeds) it stands on its own.
|
|
441
|
+
if (!published.survey)
|
|
442
|
+
return result;
|
|
443
|
+
if (!result.survey)
|
|
444
|
+
return published;
|
|
445
|
+
return usePublishedStrings(result.survey, published.survey)
|
|
446
|
+
? result
|
|
447
|
+
: published;
|
|
407
448
|
}
|
|
408
449
|
//# sourceMappingURL=survey.js.map
|