@tinyweb_dev/oe-exam-sdk 0.2.5 → 0.2.7
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/components/exams/take/ExamTakingPageContainer.js +6 -0
- package/dist/components/exams/take/components/QuestionRenderer.js +10 -2
- package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.d.ts +22 -0
- package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.js +60 -0
- package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.d.ts +2 -2
- package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +58 -14
- package/dist/components/exams/take/components/question-renderers/MoversMatchingWithLinesGroupRenderer.d.ts +13 -0
- package/dist/components/exams/take/components/question-renderers/MoversMatchingWithLinesGroupRenderer.js +31 -0
- package/dist/components/exams/take/components/question-renderers/index.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/index.js +2 -0
- package/dist/components/exams/take/types.d.ts +12 -5
- package/dist/components/exams/take/utils/answer-transformers.js +17 -1
- package/dist/components/exams/take/utils/question-transformers.d.ts +45 -1
- package/dist/components/exams/take/utils/question-transformers.js +109 -53
- package/dist/components/questions/_shared/config/question-types.config.js +12 -0
- package/dist/components/questions/_shared/types/fill-in-blank-group.type.d.ts +50 -0
- package/dist/components/questions/_shared/types/fill-in-blank-group.type.js +2 -0
- package/dist/components/questions/_shared/types/fill-in-blank.type.d.ts +16 -1
- package/dist/components/questions/_shared/types/index.d.ts +2 -0
- package/dist/components/questions/_shared/types/index.js +2 -0
- package/dist/components/questions/_shared/types/matching-with-lines-group.type.d.ts +40 -0
- package/dist/components/questions/_shared/types/matching-with-lines-group.type.js +1 -0
- package/dist/components/questions/components/QuestionSearchDropdown.js +4 -0
- package/dist/components/questions/components/QuestionTypeSelector.js +2 -0
- package/dist/components/questions/creator/question-type-registry.js +4 -0
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.d.ts +1 -1
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +37 -60
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +122 -149
- package/dist/components/questions/types/fill-in-blank/answer-utils.d.ts +34 -0
- package/dist/components/questions/types/fill-in-blank/answer-utils.js +58 -0
- package/dist/components/questions/types/fill-in-blank/register.js +19 -6
- package/dist/components/questions/types/fill-in-blank/transform.js +23 -26
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupClient.d.ts +2 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupClient.js +64 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.d.ts +2 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +158 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupMediaField.d.ts +7 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupMediaField.js +30 -0
- package/dist/components/questions/types/fill-in-blank-group/blank-utils.d.ts +20 -0
- package/dist/components/questions/types/fill-in-blank-group/blank-utils.js +99 -0
- package/dist/components/questions/types/fill-in-blank-group/index.d.ts +5 -0
- package/dist/components/questions/types/fill-in-blank-group/index.js +5 -0
- package/dist/components/questions/types/fill-in-blank-group/map-fill-in-blank-group-data.d.ts +4 -0
- package/dist/components/questions/types/fill-in-blank-group/map-fill-in-blank-group-data.js +102 -0
- package/dist/components/questions/types/fill-in-blank-group/register.d.ts +3 -0
- package/dist/components/questions/types/fill-in-blank-group/register.js +37 -0
- package/dist/components/questions/types/fill-in-blank-group/transform.d.ts +2 -0
- package/dist/components/questions/types/fill-in-blank-group/transform.js +68 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesBoard.d.ts +20 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesBoard.js +109 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.d.ts +2 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.js +30 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.d.ts +2 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +180 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.d.ts +6 -0
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.js +26 -0
- package/dist/components/questions/types/matching-with-lines-group/index.d.ts +4 -0
- package/dist/components/questions/types/matching-with-lines-group/index.js +4 -0
- package/dist/components/questions/types/matching-with-lines-group/map-matching-with-lines-group-data.d.ts +8 -0
- package/dist/components/questions/types/matching-with-lines-group/map-matching-with-lines-group-data.js +104 -0
- package/dist/components/questions/types/matching-with-lines-group/register.d.ts +3 -0
- package/dist/components/questions/types/matching-with-lines-group/register.js +37 -0
- package/dist/components/questions/types/matching-with-lines-group/transform.d.ts +2 -0
- package/dist/components/questions/types/matching-with-lines-group/transform.js +44 -0
- package/dist/components/questions/viewer/FillInBlankGroupViewer.d.ts +10 -0
- package/dist/components/questions/viewer/FillInBlankGroupViewer.js +14 -0
- package/dist/components/questions/viewer/MatchingWithLinesGroupViewer.d.ts +7 -0
- package/dist/components/questions/viewer/MatchingWithLinesGroupViewer.js +48 -0
- package/dist/components/questions/viewer/QuestionViewer.js +6 -0
- package/dist/components/questions/viewer/index.d.ts +2 -0
- package/dist/components/questions/viewer/index.js +2 -0
- package/dist/components/results/renderers/ReviewListenAndWriteRenderer.d.ts +2 -3
- package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +17 -8
- package/dist/components/results/renderers/review-question-body-dedicated.js +2 -2
- package/dist/components/results/renderers/review-question-body-movers.js +37 -2
- package/dist/components/results/review-data.js +37 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shared/constants/question-skills.js +4 -0
- package/dist/shared/lib/utils/fill-in-blank.d.ts +46 -0
- package/dist/shared/lib/utils/fill-in-blank.js +96 -0
- package/dist/shared/lib/utils/question-reverse-transform.js +150 -3
- package/dist/shared/lib/utils/question-transform.js +4 -0
- package/dist/shared/types/common.types.d.ts +1 -1
- package/dist/shared/types/questions/fill-in-blank-group.d.ts +45 -0
- package/dist/shared/types/questions/fill-in-blank-group.js +1 -0
- package/dist/shared/types/questions/index.d.ts +2 -0
- package/dist/shared/types/questions/index.js +4 -0
- package/dist/shared/types/questions/matching-with-lines-group.d.ts +39 -0
- package/dist/shared/types/questions/matching-with-lines-group.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FILL_IN_BLANK — single source of truth for the whole flow.
|
|
3
|
+
*
|
|
4
|
+
* Wire format (never change one side without the other):
|
|
5
|
+
* content.question "A dog {0} swim, but it {1} fly."
|
|
6
|
+
* correct_answer.answers string[][] — answers[i] = accepted equivalents for blank {i}
|
|
7
|
+
* student answer { answers: string[] } — one entry per blank, index-aligned
|
|
8
|
+
*
|
|
9
|
+
* Matching rule (mirrors the API `normalizeText`): trim, lowercase, collapse inner
|
|
10
|
+
* whitespace, keep diacritics. A blank is correct when the input equals ANY equivalent.
|
|
11
|
+
*/
|
|
12
|
+
const PLACEHOLDER_PATTERN = /\{(\d+)\}/g;
|
|
13
|
+
/** Split a stem into the text / blank segments used by every renderer. */
|
|
14
|
+
export function parseFillBlankSegments(text) {
|
|
15
|
+
const segments = [];
|
|
16
|
+
const regex = new RegExp(PLACEHOLDER_PATTERN);
|
|
17
|
+
let lastIndex = 0;
|
|
18
|
+
let match;
|
|
19
|
+
while ((match = regex.exec(text)) !== null) {
|
|
20
|
+
if (match.index > lastIndex) {
|
|
21
|
+
segments.push({ type: 'text', value: text.slice(lastIndex, match.index) });
|
|
22
|
+
}
|
|
23
|
+
segments.push({ type: 'blank', index: Number.parseInt(match[1], 10) });
|
|
24
|
+
lastIndex = regex.lastIndex;
|
|
25
|
+
}
|
|
26
|
+
if (lastIndex < text.length) {
|
|
27
|
+
segments.push({ type: 'text', value: text.slice(lastIndex) });
|
|
28
|
+
}
|
|
29
|
+
return segments;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Renderer-facing parse: like `parseFillBlankSegments`, but guarantees at least
|
|
33
|
+
* one blank so a stem authored without `{0}` still gets an input box.
|
|
34
|
+
*/
|
|
35
|
+
export function parseFillBlankStem(text) {
|
|
36
|
+
const segments = parseFillBlankSegments(text);
|
|
37
|
+
if (!segments.some((segment) => segment.type === 'blank')) {
|
|
38
|
+
segments.push({ type: 'blank', index: 0 });
|
|
39
|
+
}
|
|
40
|
+
return segments;
|
|
41
|
+
}
|
|
42
|
+
/** Number of input boxes a stem needs (highest placeholder index + 1). */
|
|
43
|
+
export function countBlanks(segments) {
|
|
44
|
+
const highest = segments.reduce((max, segment) => (segment.type === 'blank' ? Math.max(max, segment.index) : max), -1);
|
|
45
|
+
return highest + 1 || 1;
|
|
46
|
+
}
|
|
47
|
+
/** Placeholder indices present in a stem, ascending and de-duplicated. */
|
|
48
|
+
export function extractPlaceholderIndices(text) {
|
|
49
|
+
const indices = new Set();
|
|
50
|
+
for (const segment of parseFillBlankSegments(text || '')) {
|
|
51
|
+
if (segment.type === 'blank')
|
|
52
|
+
indices.add(segment.index);
|
|
53
|
+
}
|
|
54
|
+
return [...indices].sort((a, b) => a - b);
|
|
55
|
+
}
|
|
56
|
+
/** Normalize one blank's stored value into a list of accepted equivalents. */
|
|
57
|
+
export function toAcceptedAnswers(value) {
|
|
58
|
+
const list = Array.isArray(value) ? value : [value];
|
|
59
|
+
return list.map((item) => String(item ?? '').trim()).filter(Boolean);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Normalize `correct_answer.answers` into `string[][]` padded to `blankCount`.
|
|
63
|
+
* Accepts the canonical `string[][]` and the legacy flat `string[]`.
|
|
64
|
+
*/
|
|
65
|
+
export function normalizeAcceptedAnswers(raw, blankCount) {
|
|
66
|
+
const source = Array.isArray(raw) ? raw : [raw];
|
|
67
|
+
return Array.from({ length: blankCount }, (_, index) => toAcceptedAnswers(source[index]));
|
|
68
|
+
}
|
|
69
|
+
/** Normalize a stored student answer into one string per blank. */
|
|
70
|
+
export function toBlankValues(raw, blankCount) {
|
|
71
|
+
const source = Array.isArray(raw) ? raw : [raw];
|
|
72
|
+
return Array.from({ length: blankCount }, (_, index) => String(source[index] ?? ''));
|
|
73
|
+
}
|
|
74
|
+
/** True when the student typed something into at least one blank. */
|
|
75
|
+
export function hasAnyBlankValue(raw) {
|
|
76
|
+
const source = Array.isArray(raw) ? raw : [raw];
|
|
77
|
+
return source.some((value) => String(value ?? '').trim().length > 0);
|
|
78
|
+
}
|
|
79
|
+
function normalizeForCompare(value) {
|
|
80
|
+
return value.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
81
|
+
}
|
|
82
|
+
/** Compare a student input against the accepted equivalents of one blank. */
|
|
83
|
+
export function isBlankCorrect(value, accepted) {
|
|
84
|
+
const input = normalizeForCompare(value ?? '');
|
|
85
|
+
if (!input || !accepted?.length)
|
|
86
|
+
return false;
|
|
87
|
+
return accepted.some((answer) => normalizeForCompare(answer) === input);
|
|
88
|
+
}
|
|
89
|
+
/** Join equivalents for display, e.g. "is / 's". */
|
|
90
|
+
export function formatAcceptedAnswers(accepted) {
|
|
91
|
+
return (accepted ?? []).map((answer) => answer.trim()).filter(Boolean).join(' / ');
|
|
92
|
+
}
|
|
93
|
+
/** First equivalent — used to prefill example questions and review hints. */
|
|
94
|
+
export function getPrimaryAnswer(accepted) {
|
|
95
|
+
return (accepted ?? []).map((answer) => answer.trim()).find(Boolean) || '';
|
|
96
|
+
}
|
|
@@ -66,19 +66,80 @@ const reversePictureChoice = (apiQuestion) => {
|
|
|
66
66
|
const reverseFillInBlank = (apiQuestion) => {
|
|
67
67
|
const content = asRecord(apiQuestion.content);
|
|
68
68
|
const answer = readAnswer(apiQuestion);
|
|
69
|
-
const
|
|
69
|
+
const toBlankAlts = (value) => {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
const cleaned = value
|
|
72
|
+
.map((item) => (item == null ? '' : String(item).trim()))
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
return cleaned.length > 0 ? cleaned : [''];
|
|
75
|
+
}
|
|
76
|
+
if (value == null)
|
|
77
|
+
return [''];
|
|
78
|
+
const text = String(value).trim();
|
|
79
|
+
return text ? [text] : [''];
|
|
80
|
+
};
|
|
81
|
+
const toAnswersMatrix = (raw, blankCount) => {
|
|
82
|
+
let result = [];
|
|
83
|
+
if (Array.isArray(raw)) {
|
|
84
|
+
result = raw.map((item) => toBlankAlts(item));
|
|
85
|
+
}
|
|
86
|
+
if (typeof blankCount === 'number' && blankCount > 0) {
|
|
87
|
+
while (result.length < blankCount) {
|
|
88
|
+
result.push(['']);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
};
|
|
93
|
+
if (content.question && Array.isArray(answer.answers)) {
|
|
94
|
+
return {
|
|
95
|
+
content: '',
|
|
96
|
+
answer: {
|
|
97
|
+
question: asString(content.question),
|
|
98
|
+
answers: toAnswersMatrix(answer.answers),
|
|
99
|
+
caseSensitive: Boolean(answer.caseSensitive ?? false),
|
|
100
|
+
imageUrl: asString(content.imageUrl),
|
|
101
|
+
audioUrl: asString(content.audioUrl),
|
|
102
|
+
showHints: Boolean(content.showHints ?? false),
|
|
103
|
+
hints: asArray(content.hints),
|
|
104
|
+
explanation: asString(apiQuestion.explanation),
|
|
105
|
+
points: readPoints(apiQuestion),
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const blanks = asArray(content.blanks);
|
|
110
|
+
const firstBlank = asRecord(blanks[0]);
|
|
111
|
+
const answersData = answer.answers ?? {};
|
|
112
|
+
let answersMatrix = [];
|
|
113
|
+
if (Array.isArray(answersData)) {
|
|
114
|
+
answersMatrix = toAnswersMatrix(answersData, blanks.length || undefined);
|
|
115
|
+
}
|
|
116
|
+
else if (typeof answersData === 'object' && answersData !== null) {
|
|
117
|
+
answersMatrix = blanks.map((blank) => toBlankAlts(asRecord(answersData)[asString(asRecord(blank).id)]));
|
|
118
|
+
}
|
|
70
119
|
return {
|
|
71
120
|
content: '',
|
|
72
121
|
answer: {
|
|
73
122
|
question: asString(content.question ?? firstBlank.text),
|
|
74
|
-
answers:
|
|
123
|
+
answers: answersMatrix,
|
|
75
124
|
caseSensitive: Boolean(answer.caseSensitive ?? false),
|
|
76
125
|
imageUrl: asString(content.imageUrl),
|
|
126
|
+
audioUrl: asString(content.audioUrl),
|
|
77
127
|
showHints: Boolean(content.showHints ?? false),
|
|
78
128
|
hints: asArray(content.hints),
|
|
79
129
|
explanation: asString(apiQuestion.explanation),
|
|
80
130
|
points: readPoints(apiQuestion),
|
|
81
|
-
blanks:
|
|
131
|
+
blanks: blanks.map((rawBlank, index) => {
|
|
132
|
+
const blank = asRecord(rawBlank);
|
|
133
|
+
const alts = typeof answersData === 'object' && !Array.isArray(answersData)
|
|
134
|
+
? toBlankAlts(asRecord(answersData)[asString(blank.id)])
|
|
135
|
+
: answersMatrix[index] || [''];
|
|
136
|
+
return {
|
|
137
|
+
id: blank.id,
|
|
138
|
+
text: blank.text,
|
|
139
|
+
correctAnswer: alts[0] || '',
|
|
140
|
+
correctAnswers: alts,
|
|
141
|
+
};
|
|
142
|
+
}),
|
|
82
143
|
},
|
|
83
144
|
};
|
|
84
145
|
};
|
|
@@ -308,6 +369,90 @@ const reverseTrueFalseCorrectGroup = (apiQuestion) => {
|
|
|
308
369
|
},
|
|
309
370
|
};
|
|
310
371
|
};
|
|
372
|
+
const reverseFillInBlankGroup = (apiQuestion) => {
|
|
373
|
+
const content = asRecord(apiQuestion.content);
|
|
374
|
+
const answer = readAnswer(apiQuestion);
|
|
375
|
+
const fallbackAnswers = asArray(answer.answer);
|
|
376
|
+
const meta = asRecord(content.meta);
|
|
377
|
+
const items = asArray(content.items).map((rawItem, index) => {
|
|
378
|
+
const item = asRecord(rawItem);
|
|
379
|
+
const itemContent = asRecord(item.content);
|
|
380
|
+
const itemAnswer = asRecord(item.correctAnswer);
|
|
381
|
+
const fallback = asRecord(fallbackAnswers[index]);
|
|
382
|
+
const isExample = item.isExample === true;
|
|
383
|
+
const question = asString(itemContent.question, '{0}');
|
|
384
|
+
const rawAnswers = itemAnswer.answers ?? fallback.answers;
|
|
385
|
+
const answers = asArray(rawAnswers).map((blank) => {
|
|
386
|
+
const alts = Array.isArray(blank)
|
|
387
|
+
? blank.map((alt) => String(alt ?? '').trim()).filter(Boolean)
|
|
388
|
+
: [String(blank ?? '').trim()].filter(Boolean);
|
|
389
|
+
return alts.length > 0 ? alts : [''];
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
question,
|
|
393
|
+
answers: answers.length > 0 ? answers : [['']],
|
|
394
|
+
caseSensitive: itemAnswer.caseSensitive === true || fallback.caseSensitive === true,
|
|
395
|
+
imageUrl: asString(itemContent.imageUrl),
|
|
396
|
+
audioUrl: asString(itemContent.audioUrl),
|
|
397
|
+
showHints: itemContent.showHints === true,
|
|
398
|
+
hints: asArray(itemContent.hints).map((hint) => String(hint ?? '').trim()).filter(Boolean),
|
|
399
|
+
isExample,
|
|
400
|
+
points: isExample ? 0 : asNumber(item.points, 1),
|
|
401
|
+
questionNumber: asNumber(item.questionNumber, index + 1),
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
return {
|
|
405
|
+
content: '',
|
|
406
|
+
answer: {
|
|
407
|
+
instruction: asString(meta.instruction, 'Complete the sentences.'),
|
|
408
|
+
audioUrl: asString(meta.audioUrl),
|
|
409
|
+
imageUrl: asString(meta.imageUrl),
|
|
410
|
+
showHints: meta.showHints === true,
|
|
411
|
+
hints: asArray(meta.hints).map((hint) => String(hint ?? '').trim()).filter(Boolean),
|
|
412
|
+
items,
|
|
413
|
+
explanation: asString(apiQuestion.explanation),
|
|
414
|
+
points: items.reduce((total, item) => (item.isExample ? total : total + item.points), 0),
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
const reverseMatchingWithLinesGroup = (apiQuestion) => {
|
|
419
|
+
const content = asRecord(apiQuestion.content);
|
|
420
|
+
const answer = readAnswer(apiQuestion);
|
|
421
|
+
const fallbackAnswers = asArray(answer.answer);
|
|
422
|
+
const meta = asRecord(content.meta);
|
|
423
|
+
const rightItems = asArray(content.rightItems).map((raw, index) => {
|
|
424
|
+
const item = asRecord(raw);
|
|
425
|
+
return {
|
|
426
|
+
id: asString(item.id, `r${index + 1}`),
|
|
427
|
+
text: asString(item.text),
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
const items = asArray(content.items).map((rawItem, index) => {
|
|
431
|
+
const item = asRecord(rawItem);
|
|
432
|
+
const itemContent = asRecord(item.content);
|
|
433
|
+
const itemAnswer = asRecord(item.correctAnswer);
|
|
434
|
+
const fallback = fallbackAnswers[index];
|
|
435
|
+
const isExample = item.isExample === true;
|
|
436
|
+
return {
|
|
437
|
+
imageUrl: asString(itemContent.imageUrl),
|
|
438
|
+
correctAnswer: asString(itemAnswer.answer, typeof fallback === 'string' ? fallback : ''),
|
|
439
|
+
isExample,
|
|
440
|
+
points: isExample ? 0 : asNumber(item.points, 1),
|
|
441
|
+
questionNumber: asNumber(item.questionNumber, index + 1),
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
return {
|
|
445
|
+
content: '',
|
|
446
|
+
answer: {
|
|
447
|
+
title: asString(meta.title),
|
|
448
|
+
instruction: asString(meta.instruction, 'Look and match.'),
|
|
449
|
+
rightItems,
|
|
450
|
+
items,
|
|
451
|
+
explanation: asString(apiQuestion.explanation),
|
|
452
|
+
points: items.reduce((total, item) => (item.isExample ? total : total + item.points), 0),
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
};
|
|
311
456
|
const reverseCrossOutWordGroup = (apiQuestion) => {
|
|
312
457
|
const content = asRecord(apiQuestion.content);
|
|
313
458
|
const answer = readAnswer(apiQuestion);
|
|
@@ -460,6 +605,8 @@ const handlers = {
|
|
|
460
605
|
ANSWER_THE_QUESTION_GROUP: reverseAnswerTheQuestionGroup,
|
|
461
606
|
TRUE_FALSE_GROUP: reverseTrueFalseGroup,
|
|
462
607
|
TRUE_FALSE_CORRECT_GROUP: reverseTrueFalseCorrectGroup,
|
|
608
|
+
FILL_IN_BLANK_GROUP: reverseFillInBlankGroup,
|
|
609
|
+
MATCHING_WITH_LINES_GROUP: reverseMatchingWithLinesGroup,
|
|
463
610
|
CROSS_OUT_WORD_GROUP: reverseCrossOutWordGroup,
|
|
464
611
|
WORD_ORDER_AND_MATCH_GROUP: reverseWordOrderAndMatchGroup,
|
|
465
612
|
WORD_ORDER_AND_MATCH: reverseWordOrderAndMatchGroup,
|
|
@@ -14,6 +14,7 @@ import { transformChooseTheCorrectAnswer } from '../../../components/questions/t
|
|
|
14
14
|
import { transformChooseTheCorrectAnswerGroup } from '../../../components/questions/types/choose-the-correct-answer-group/transform';
|
|
15
15
|
import { transformCrossOutWordGroup } from '../../../components/questions/types/cross-out-word-group/transform';
|
|
16
16
|
import { transformTrueFalseCorrectGroup } from '../../../components/questions/types/true-false-correct-group/transform';
|
|
17
|
+
import { transformFillInBlankGroup } from '../../../components/questions/types/fill-in-blank-group/transform';
|
|
17
18
|
import { transformFillInBlank } from '../../../components/questions/types/fill-in-blank/transform';
|
|
18
19
|
import { transformWriteCorrectVerbForm } from '../../../components/questions/types/write-correct-verb-form/transform';
|
|
19
20
|
import { transformChooseCorrectAdjective } from '../../../components/questions/types/choose-correct-adjective/transform';
|
|
@@ -24,6 +25,7 @@ import { transformWordOrdering } from '../../../components/questions/types/word-
|
|
|
24
25
|
import { transformWordFillParagraph } from '../../../components/questions/types/word-fill-paragraph/transform';
|
|
25
26
|
import { transformMatchByWritingAnswer } from '../../../components/questions/types/match-by-writing-answer/transform';
|
|
26
27
|
import { transformMatchWordToPicture } from '../../../components/questions/types/match-word-to-picture/transform';
|
|
28
|
+
import { transformMatchingWithLinesGroup } from '../../../components/questions/types/matching-with-lines-group/transform';
|
|
27
29
|
import { transformWriteSentences } from '../../../components/questions/types/write-sentences/transform';
|
|
28
30
|
import { transformListenAndChooseFromAnswerGroup } from '../../../components/questions/types/listen-and-choose-from-answer-group/transform';
|
|
29
31
|
import { transformListenAndChooseObjectsInScene } from '../../../components/questions/types/listen-and-choose-objects-in-scene/transform';
|
|
@@ -65,6 +67,7 @@ const questionTransformHandlers = {
|
|
|
65
67
|
CHOOSE_THE_CORRECT_ANSWER_GROUP: transformChooseTheCorrectAnswerGroup,
|
|
66
68
|
CROSS_OUT_WORD_GROUP: transformCrossOutWordGroup,
|
|
67
69
|
TRUE_FALSE_CORRECT_GROUP: transformTrueFalseCorrectGroup,
|
|
70
|
+
FILL_IN_BLANK_GROUP: transformFillInBlankGroup,
|
|
68
71
|
FILL_IN_BLANK: transformFillInBlank,
|
|
69
72
|
WRITE_CORRECT_VERB_FORM: transformWriteCorrectVerbForm,
|
|
70
73
|
CHOOSE_CORRECT_ADJECTIVE: transformChooseCorrectAdjective,
|
|
@@ -75,6 +78,7 @@ const questionTransformHandlers = {
|
|
|
75
78
|
WORD_FILL_PARAGRAPH: transformWordFillParagraph,
|
|
76
79
|
MATCH_BY_WRITING_ANSWER: transformMatchByWritingAnswer,
|
|
77
80
|
MATCH_WORD_TO_PICTURE: transformMatchWordToPicture,
|
|
81
|
+
MATCHING_WITH_LINES_GROUP: transformMatchingWithLinesGroup,
|
|
78
82
|
WRITE_SENTENCES: transformWriteSentences,
|
|
79
83
|
LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP: transformListenAndChooseFromAnswerGroup,
|
|
80
84
|
LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE: transformListenAndChooseObjectsInScene,
|
|
@@ -29,7 +29,7 @@ export declare enum StudentSelectionType {
|
|
|
29
29
|
}
|
|
30
30
|
export type ResultStatus = 'PENDING' | 'PARTIAL_GRADED' | 'GRADED';
|
|
31
31
|
export type StudentStatus = 'NOT_STARTED' | 'IN_PROGRESS' | 'SUBMITTED';
|
|
32
|
-
export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX';
|
|
32
|
+
export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX';
|
|
33
33
|
export type Level = 'Pre-A1' | 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
|
|
34
34
|
export type Difficulty = 'EASY' | 'MEDIUM' | 'HARD';
|
|
35
35
|
export type Skill = 'LISTENING' | 'READING' | 'WRITING' | 'SPEAKING';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const FILL_IN_BLANK_ITEM_TYPE: "FILL_IN_BLANK";
|
|
2
|
+
export interface FillInBlankGroupItemAnswer {
|
|
3
|
+
answers: string[][];
|
|
4
|
+
caseSensitive?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface FillInBlankGroupItemContent {
|
|
7
|
+
question: string;
|
|
8
|
+
imageUrl?: string;
|
|
9
|
+
audioUrl?: string;
|
|
10
|
+
showHints?: boolean;
|
|
11
|
+
hints?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface FillInBlankGroupItem {
|
|
14
|
+
questionType?: typeof FILL_IN_BLANK_ITEM_TYPE;
|
|
15
|
+
isExample?: boolean;
|
|
16
|
+
content: FillInBlankGroupItemContent;
|
|
17
|
+
correctAnswer?: FillInBlankGroupItemAnswer;
|
|
18
|
+
points?: number;
|
|
19
|
+
questionNumber?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface FillInBlankGroupContent {
|
|
22
|
+
meta?: {
|
|
23
|
+
instruction?: string;
|
|
24
|
+
audioUrl?: string;
|
|
25
|
+
imageUrl?: string;
|
|
26
|
+
showHints?: boolean;
|
|
27
|
+
hints?: string[];
|
|
28
|
+
};
|
|
29
|
+
items: FillInBlankGroupItem[];
|
|
30
|
+
}
|
|
31
|
+
export interface FillInBlankGroupCorrectAnswer {
|
|
32
|
+
answer: FillInBlankGroupItemAnswer[];
|
|
33
|
+
}
|
|
34
|
+
export interface FillInBlankGroupAPIPayload {
|
|
35
|
+
id?: string;
|
|
36
|
+
questionType: 'FILL_IN_BLANK_GROUP';
|
|
37
|
+
content: FillInBlankGroupContent;
|
|
38
|
+
correctAnswer: FillInBlankGroupCorrectAnswer;
|
|
39
|
+
points: number;
|
|
40
|
+
explanation?: string;
|
|
41
|
+
questionNumber: number;
|
|
42
|
+
partId?: string;
|
|
43
|
+
partNo?: number;
|
|
44
|
+
}
|
|
45
|
+
export type FillInBlankGroupLocalStorage = FillInBlankGroupAPIPayload;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FILL_IN_BLANK_ITEM_TYPE = 'FILL_IN_BLANK';
|
|
@@ -33,4 +33,6 @@ export * from './word-order-and-match-group';
|
|
|
33
33
|
export * from './crossword-puzzle';
|
|
34
34
|
export * from './find-words-in-matrix';
|
|
35
35
|
export * from './match-word-to-picture';
|
|
36
|
+
export * from './matching-with-lines-group';
|
|
36
37
|
export * from './true-false-correct-group';
|
|
38
|
+
export * from './fill-in-blank-group';
|
|
@@ -52,5 +52,9 @@ export * from './crossword-puzzle';
|
|
|
52
52
|
export * from './find-words-in-matrix';
|
|
53
53
|
// MATCH_WORD_TO_PICTURE
|
|
54
54
|
export * from './match-word-to-picture';
|
|
55
|
+
// MATCHING_WITH_LINES_GROUP
|
|
56
|
+
export * from './matching-with-lines-group';
|
|
55
57
|
// TRUE_FALSE_CORRECT_GROUP
|
|
56
58
|
export * from './true-false-correct-group';
|
|
59
|
+
// FILL_IN_BLANK_GROUP
|
|
60
|
+
export * from './fill-in-blank-group';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface MatchingWithLinesRightItem {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
export interface MatchingWithLinesGroupItemContent {
|
|
6
|
+
imageUrl: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MatchingWithLinesGroupItem {
|
|
9
|
+
isExample?: boolean;
|
|
10
|
+
content: MatchingWithLinesGroupItemContent;
|
|
11
|
+
correctAnswer?: {
|
|
12
|
+
answer: string;
|
|
13
|
+
};
|
|
14
|
+
points?: number;
|
|
15
|
+
questionNumber?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MatchingWithLinesGroupContent {
|
|
18
|
+
meta?: {
|
|
19
|
+
instruction?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
};
|
|
22
|
+
rightItems: MatchingWithLinesRightItem[];
|
|
23
|
+
items: MatchingWithLinesGroupItem[];
|
|
24
|
+
}
|
|
25
|
+
export interface MatchingWithLinesGroupCorrectAnswer {
|
|
26
|
+
answer: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface MatchingWithLinesGroupAPIPayload {
|
|
29
|
+
id?: string;
|
|
30
|
+
questionType: 'MATCHING_WITH_LINES_GROUP';
|
|
31
|
+
content: MatchingWithLinesGroupContent;
|
|
32
|
+
correctAnswer: MatchingWithLinesGroupCorrectAnswer;
|
|
33
|
+
points: number;
|
|
34
|
+
explanation?: string;
|
|
35
|
+
questionNumber: number;
|
|
36
|
+
partId?: string;
|
|
37
|
+
partNo?: number;
|
|
38
|
+
}
|
|
39
|
+
export type MatchingWithLinesGroupLocalStorage = MatchingWithLinesGroupAPIPayload;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|