@playcademy/sandbox 0.10.1-beta.2 → 0.10.1-beta.4
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/cli.js +134 -77
- package/dist/constants.js +1 -1
- package/dist/server.js +134 -77
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -285,7 +285,7 @@ var init_platform = __esm(() => {
|
|
|
285
285
|
var PLATFORM_TIMEZONE = "America/New_York";
|
|
286
286
|
|
|
287
287
|
// ../constants/src/timeback.ts
|
|
288
|
-
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION =
|
|
288
|
+
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY = "playcademyAssessment", PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY = "playcademyAssessmentItem", TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH = 2048, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
|
|
289
289
|
var init_timeback2 = __esm(() => {
|
|
290
290
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
291
291
|
TIMEBACK_SUBJECTS = [
|
|
@@ -1128,7 +1128,7 @@ var package_default;
|
|
|
1128
1128
|
var init_package = __esm(() => {
|
|
1129
1129
|
package_default = {
|
|
1130
1130
|
name: "@playcademy/sandbox",
|
|
1131
|
-
version: "0.10.1-beta.
|
|
1131
|
+
version: "0.10.1-beta.4",
|
|
1132
1132
|
description: "Local development server for Playcademy game development",
|
|
1133
1133
|
type: "module",
|
|
1134
1134
|
exports: {
|
|
@@ -10834,30 +10834,60 @@ function comparePresentationText(left, right) {
|
|
|
10834
10834
|
}
|
|
10835
10835
|
return 0;
|
|
10836
10836
|
}
|
|
10837
|
+
function presentationRandomIndex(seed) {
|
|
10838
|
+
let state = presentationHash(seed);
|
|
10839
|
+
const range = 2 ** 32;
|
|
10840
|
+
return (bound) => {
|
|
10841
|
+
const limit = range - range % bound;
|
|
10842
|
+
let value;
|
|
10843
|
+
do {
|
|
10844
|
+
state = state + 1831565813 >>> 0;
|
|
10845
|
+
let mixed = Math.imul(state ^ state >>> 15, state | 1);
|
|
10846
|
+
mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, mixed | 61);
|
|
10847
|
+
value = (mixed ^ mixed >>> 14) >>> 0;
|
|
10848
|
+
} while (value >= limit);
|
|
10849
|
+
return value % bound;
|
|
10850
|
+
};
|
|
10851
|
+
}
|
|
10837
10852
|
function presentationChoices(choices, attemptId, itemIdentifier, domain) {
|
|
10838
|
-
const
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10853
|
+
const randomIndex = presentationRandomIndex(JSON.stringify([attemptId, itemIdentifier, domain]));
|
|
10854
|
+
const movable = choices.filter((choice) => !choice.fixed).toSorted((left, right) => comparePresentationText(left.identifier, right.identifier));
|
|
10855
|
+
for (let index = movable.length - 1;index > 0; index--) {
|
|
10856
|
+
const other = randomIndex(index + 1);
|
|
10857
|
+
[movable[index], movable[other]] = [movable[other], movable[index]];
|
|
10858
|
+
}
|
|
10859
|
+
let next = 0;
|
|
10860
|
+
return choices.map((choice) => choice.fixed ? choice : movable[next++]);
|
|
10843
10861
|
}
|
|
10844
10862
|
function assessmentPresentationForAttempt(assessment, attemptId) {
|
|
10845
10863
|
const items = assessment.items.map((item) => {
|
|
10846
10864
|
const interactions = item.interactions.map((interaction) => {
|
|
10847
|
-
if (
|
|
10848
|
-
return
|
|
10849
|
-
...interaction,
|
|
10850
|
-
choices: presentationChoices(interaction.choices, attemptId, item.identifier, `order\x00${interaction.responseIdentifier}`)
|
|
10851
|
-
};
|
|
10865
|
+
if (!("shuffle" in interaction) || !interaction.shuffle) {
|
|
10866
|
+
return interaction;
|
|
10852
10867
|
}
|
|
10853
|
-
|
|
10854
|
-
return {
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10868
|
+
function shuffled(choices, domain) {
|
|
10869
|
+
return presentationChoices(choices, attemptId, item.identifier, `${domain}\x00${interaction.responseIdentifier}`);
|
|
10870
|
+
}
|
|
10871
|
+
switch (interaction.type) {
|
|
10872
|
+
case "choice":
|
|
10873
|
+
case "inline-choice":
|
|
10874
|
+
case "order": {
|
|
10875
|
+
return {
|
|
10876
|
+
...interaction,
|
|
10877
|
+
choices: shuffled(interaction.choices, interaction.type)
|
|
10878
|
+
};
|
|
10879
|
+
}
|
|
10880
|
+
case "gap-match": {
|
|
10881
|
+
return { ...interaction, gapTexts: shuffled(interaction.gapTexts, "gap-match") };
|
|
10882
|
+
}
|
|
10883
|
+
case "match": {
|
|
10884
|
+
return {
|
|
10885
|
+
...interaction,
|
|
10886
|
+
sourceChoices: shuffled(interaction.sourceChoices, "match-source"),
|
|
10887
|
+
targetChoices: shuffled(interaction.targetChoices, "match-target")
|
|
10888
|
+
};
|
|
10889
|
+
}
|
|
10859
10890
|
}
|
|
10860
|
-
return interaction;
|
|
10861
10891
|
});
|
|
10862
10892
|
return { ...item, interactions };
|
|
10863
10893
|
});
|
|
@@ -11002,19 +11032,22 @@ function scorePlatformAssessmentItem(item, responses) {
|
|
|
11002
11032
|
isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
|
|
11003
11033
|
};
|
|
11004
11034
|
}
|
|
11035
|
+
function reviewStandardRef({
|
|
11036
|
+
framework,
|
|
11037
|
+
identifier
|
|
11038
|
+
}) {
|
|
11039
|
+
return { framework, identifier };
|
|
11040
|
+
}
|
|
11005
11041
|
function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
|
|
11006
|
-
if (!review.requireBothQuestions) {
|
|
11007
|
-
return;
|
|
11008
|
-
}
|
|
11009
11042
|
const administered = new Set(administeredItemIdentifiers2);
|
|
11010
11043
|
const failures = [];
|
|
11011
|
-
for (const standard of review.standards) {
|
|
11044
|
+
for (const standard of review.standards.filter((entry) => entry.requireBothQuestions)) {
|
|
11012
11045
|
const key = assessmentStandardRefKey(standard);
|
|
11013
11046
|
const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
|
|
11014
11047
|
const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
|
|
11015
11048
|
if (selected.size < 2 || administeredCount === 1) {
|
|
11016
11049
|
failures.push({
|
|
11017
|
-
standard,
|
|
11050
|
+
standard: reviewStandardRef(standard),
|
|
11018
11051
|
reason: selected.size < 2 ? "selection_shortage" : "not_administered",
|
|
11019
11052
|
requiredCount: 2,
|
|
11020
11053
|
selectedCount: selected.size,
|
|
@@ -11057,6 +11090,9 @@ function canonicalRequestStandards(standards) {
|
|
|
11057
11090
|
}
|
|
11058
11091
|
const canonical = new Map;
|
|
11059
11092
|
for (const input of standards) {
|
|
11093
|
+
if (input.requireBothQuestions !== undefined && typeof input.requireBothQuestions !== "boolean") {
|
|
11094
|
+
throw new Error("review standard requireBothQuestions must be a boolean");
|
|
11095
|
+
}
|
|
11060
11096
|
const standard = canonicalAssessmentStandardRef(input);
|
|
11061
11097
|
if (!reviewStandardFieldsWithinLimits(standard)) {
|
|
11062
11098
|
throw new RangeError(`Review standard fields may contain at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength} characters`);
|
|
@@ -11065,13 +11101,19 @@ function canonicalRequestStandards(standards) {
|
|
|
11065
11101
|
throw new Error("Review standards require a framework and identifier");
|
|
11066
11102
|
}
|
|
11067
11103
|
const key = assessmentStandardRefKey(standard);
|
|
11068
|
-
if (!canonical.has(key)) {
|
|
11069
|
-
canonical.set(key,
|
|
11104
|
+
if (!canonical.has(key) || input.requireBothQuestions) {
|
|
11105
|
+
canonical.set(key, {
|
|
11106
|
+
...standard,
|
|
11107
|
+
...input.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11108
|
+
});
|
|
11070
11109
|
}
|
|
11071
11110
|
}
|
|
11072
|
-
return [...canonical.values()]
|
|
11111
|
+
return [...canonical.values()];
|
|
11073
11112
|
}
|
|
11074
11113
|
function normalizeReviewRequest(request) {
|
|
11114
|
+
if ("requireBothQuestions" in request && request.requireBothQuestions !== undefined) {
|
|
11115
|
+
throw new Error("Set requireBothQuestions on individual review standards, not the review request");
|
|
11116
|
+
}
|
|
11075
11117
|
const candidateItemsPerStandard = request.candidateItemsPerStandard ?? 1;
|
|
11076
11118
|
if (!Number.isInteger(candidateItemsPerStandard) || candidateItemsPerStandard <= 0 || candidateItemsPerStandard > TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard) {
|
|
11077
11119
|
throw new RangeError(`candidateItemsPerStandard must be a positive integer at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard}`);
|
|
@@ -11082,20 +11124,20 @@ function normalizeReviewRequest(request) {
|
|
|
11082
11124
|
}
|
|
11083
11125
|
return {
|
|
11084
11126
|
standards,
|
|
11085
|
-
candidateItemsPerStandard
|
|
11086
|
-
...request.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11127
|
+
candidateItemsPerStandard
|
|
11087
11128
|
};
|
|
11088
11129
|
}
|
|
11089
11130
|
function reviewRequestFingerprint(request, policy) {
|
|
11090
11131
|
const normalized = normalizeReviewRequest(request);
|
|
11132
|
+
const requiredStandardKeys = normalized.standards.filter((standard) => standard.requireBothQuestions).map(assessmentStandardRefKey);
|
|
11091
11133
|
return JSON.stringify({
|
|
11092
11134
|
kind: "standards-review",
|
|
11093
11135
|
policy: {
|
|
11094
11136
|
version: policy.version
|
|
11095
11137
|
},
|
|
11096
11138
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
11097
|
-
|
|
11098
|
-
|
|
11139
|
+
standardKeys: normalized.standards.map(assessmentStandardRefKey),
|
|
11140
|
+
...requiredStandardKeys.length > 0 ? { requiredStandardKeys } : {}
|
|
11099
11141
|
});
|
|
11100
11142
|
}
|
|
11101
11143
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
@@ -11321,9 +11363,10 @@ function rankedCandidatesByStandard(bank, exposures) {
|
|
|
11321
11363
|
return byStandard;
|
|
11322
11364
|
}
|
|
11323
11365
|
function reviewSlots(request) {
|
|
11324
|
-
return request.standards.flatMap((standard) => Array.from({ length: request.candidateItemsPerStandard }, () => ({
|
|
11325
|
-
standard,
|
|
11326
|
-
standardKey: assessmentStandardRefKey(standard)
|
|
11366
|
+
return request.standards.flatMap((standard, servingIndex) => Array.from({ length: request.candidateItemsPerStandard }, () => ({
|
|
11367
|
+
standard: reviewStandardRef(standard),
|
|
11368
|
+
standardKey: assessmentStandardRefKey(standard),
|
|
11369
|
+
servingIndex
|
|
11327
11370
|
})));
|
|
11328
11371
|
}
|
|
11329
11372
|
function matchingCandidates(candidatesByStandard, slotCount) {
|
|
@@ -11436,7 +11479,7 @@ function fulfillment(request, candidatesByStandard, selections) {
|
|
|
11436
11479
|
const selected = selectedByStandard.get(key) ?? 0;
|
|
11437
11480
|
return selected < request.candidateItemsPerStandard ? [
|
|
11438
11481
|
{
|
|
11439
|
-
standard,
|
|
11482
|
+
standard: reviewStandardRef(standard),
|
|
11440
11483
|
requested: request.candidateItemsPerStandard,
|
|
11441
11484
|
selected,
|
|
11442
11485
|
eligible: candidatesByStandard.get(key)?.length ?? 0
|
|
@@ -11461,10 +11504,10 @@ function fulfillment(request, candidatesByStandard, selections) {
|
|
|
11461
11504
|
function selectReviewItems(input) {
|
|
11462
11505
|
const request = normalizeReviewRequest(input.request);
|
|
11463
11506
|
const candidatesByStandard = rankedCandidatesByStandard(input.bank, input.exposures);
|
|
11464
|
-
const slots = reviewSlots(request);
|
|
11507
|
+
const slots = reviewSlots(request).toSorted((left, right) => compareCodeUnits(left.standardKey, right.standardKey));
|
|
11465
11508
|
const boundedCandidates = matchingCandidates(candidatesByStandard, slots.length);
|
|
11466
11509
|
const selected = preferredDistinctMatching(slots, boundedCandidates);
|
|
11467
|
-
const selections = [...selected.entries()].toSorted(([left], [right]) => left - right).map(([index, itemIdentifier]) => ({
|
|
11510
|
+
const selections = [...selected.entries()].toSorted(([left], [right]) => slots[left].servingIndex - slots[right].servingIndex || left - right).map(([index, itemIdentifier]) => ({
|
|
11468
11511
|
standard: slots[index].standard,
|
|
11469
11512
|
itemIdentifier
|
|
11470
11513
|
}));
|
|
@@ -11603,6 +11646,9 @@ function isAssessmentScore(value) {
|
|
|
11603
11646
|
function isAssessmentStandardRef(value) {
|
|
11604
11647
|
return isRecord(value) && typeof value.framework === "string" && value.framework.length > 0 && typeof value.identifier === "string" && value.identifier.length > 0;
|
|
11605
11648
|
}
|
|
11649
|
+
function isAssessmentReviewStandard(value) {
|
|
11650
|
+
return isRecord(value) && isAssessmentStandardRef(value) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean");
|
|
11651
|
+
}
|
|
11606
11652
|
function isReviewFulfillment(value) {
|
|
11607
11653
|
if (!isRecord(value) || value.status !== "complete" && value.status !== "partial" || !Number.isInteger(value.requestedItemCount) || !Number.isInteger(value.selectedItemCount) || !Array.isArray(value.shortages)) {
|
|
11608
11654
|
return false;
|
|
@@ -11677,7 +11723,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
|
|
|
11677
11723
|
});
|
|
11678
11724
|
}
|
|
11679
11725
|
function isReviewAttemptMetadata(value) {
|
|
11680
|
-
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(
|
|
11726
|
+
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentReviewStandard) && Number.isInteger(value.candidateItemsPerStandard) && value.requireBothQuestions === undefined && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11681
11727
|
}
|
|
11682
11728
|
function isOptionalInteger(value) {
|
|
11683
11729
|
return value === undefined || Number.isInteger(value);
|
|
@@ -11796,6 +11842,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
11796
11842
|
init_src();
|
|
11797
11843
|
init_timeback3();
|
|
11798
11844
|
init_src();
|
|
11845
|
+
init_src();
|
|
11799
11846
|
init_uuid();
|
|
11800
11847
|
init_esm();
|
|
11801
11848
|
init_src();
|
|
@@ -12375,8 +12422,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12375
12422
|
}),
|
|
12376
12423
|
StartAssessmentBaseSchema.extend({
|
|
12377
12424
|
purpose: exports_external.literal("review"),
|
|
12378
|
-
requireBothQuestions: exports_external.
|
|
12379
|
-
standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12425
|
+
requireBothQuestions: exports_external.never().optional(),
|
|
12426
|
+
standards: exports_external.array(AssessmentStandardRefSchema2.and(exports_external.object({ requireBothQuestions: exports_external.boolean().optional() }))).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12380
12427
|
candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
|
|
12381
12428
|
}),
|
|
12382
12429
|
StartAssessmentBaseSchema.extend({
|
|
@@ -39193,7 +39240,7 @@ function assertMatchingAssessmentArtifactPair(playable, scoring, expectedIdentit
|
|
|
39193
39240
|
throw new ValidationError("Assessment artifact pair does not describe one revision");
|
|
39194
39241
|
}
|
|
39195
39242
|
}
|
|
39196
|
-
var ASSESSMENT_ARTIFACT_SCHEMA_VERSION =
|
|
39243
|
+
var ASSESSMENT_ARTIFACT_SCHEMA_VERSION = 2, ASSESSMENT_GRADER_PROTOCOL_VERSION = 1, ARTIFACT_KEY_PREFIX = "assessment-artifacts", SHA256_HEX, textEncoder;
|
|
39197
39244
|
var init_assessment_artifact_envelope_util = __esm(() => {
|
|
39198
39245
|
init_errors2();
|
|
39199
39246
|
SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
@@ -94287,6 +94334,30 @@ var init_timeback_game_metrics_comparison_util = __esm(() => {
|
|
|
94287
94334
|
});
|
|
94288
94335
|
|
|
94289
94336
|
// ../timeback/dist/engines/qti/index.js
|
|
94337
|
+
function qtiAttributeNumber(attributes, name3, fallback) {
|
|
94338
|
+
const raw = attributes[name3]?.trim();
|
|
94339
|
+
if (!raw) {
|
|
94340
|
+
return fallback;
|
|
94341
|
+
}
|
|
94342
|
+
const value = Number(raw);
|
|
94343
|
+
return Number.isFinite(value) ? value : fallback;
|
|
94344
|
+
}
|
|
94345
|
+
function qtiAttributeCapacity(attributes, name3, fallback) {
|
|
94346
|
+
const raw = attributes[name3]?.trim();
|
|
94347
|
+
if (!raw) {
|
|
94348
|
+
return fallback;
|
|
94349
|
+
}
|
|
94350
|
+
const value = Number(raw);
|
|
94351
|
+
return Number.isInteger(value) && value >= 0 ? value : null;
|
|
94352
|
+
}
|
|
94353
|
+
function qtiAttributeMaximum(attributes, name3, fallback, unlimited) {
|
|
94354
|
+
const maximum = qtiAttributeNumber(attributes, name3, fallback);
|
|
94355
|
+
return maximum === 0 ? unlimited : maximum;
|
|
94356
|
+
}
|
|
94357
|
+
function qtiAttributeBoolean(attributes, name3) {
|
|
94358
|
+
const value = attributes[name3];
|
|
94359
|
+
return value === "true" || value === "1";
|
|
94360
|
+
}
|
|
94290
94361
|
function decodeCssEscapes(value) {
|
|
94291
94362
|
return value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_, hex3) => {
|
|
94292
94363
|
const code = Number.parseInt(hex3, 16);
|
|
@@ -94874,7 +94945,8 @@ function parseGapTexts(interaction) {
|
|
|
94874
94945
|
return {
|
|
94875
94946
|
identifier: attributes2.identifier || "",
|
|
94876
94947
|
content: plainTextFromNodes(gapText.children),
|
|
94877
|
-
matchMax: finiteNumber(attributes2["match-max"]) ?? 1
|
|
94948
|
+
matchMax: finiteNumber(attributes2["match-max"]) ?? 1,
|
|
94949
|
+
...qtiAttributeBoolean(attributes2, "fixed") ? { fixed: true } : {}
|
|
94878
94950
|
};
|
|
94879
94951
|
}).filter((gapText) => gapText.identifier);
|
|
94880
94952
|
}
|
|
@@ -95974,30 +96046,6 @@ ${body2}
|
|
|
95974
96046
|
${processing}` : ""}
|
|
95975
96047
|
</qti-assessment-item>`;
|
|
95976
96048
|
}
|
|
95977
|
-
function qtiAttributeNumber(attributes2, name3, fallback) {
|
|
95978
|
-
const raw = attributes2[name3]?.trim();
|
|
95979
|
-
if (!raw) {
|
|
95980
|
-
return fallback;
|
|
95981
|
-
}
|
|
95982
|
-
const value = Number(raw);
|
|
95983
|
-
return Number.isFinite(value) ? value : fallback;
|
|
95984
|
-
}
|
|
95985
|
-
function qtiAttributeCapacity(attributes2, name3, fallback) {
|
|
95986
|
-
const raw = attributes2[name3]?.trim();
|
|
95987
|
-
if (!raw) {
|
|
95988
|
-
return fallback;
|
|
95989
|
-
}
|
|
95990
|
-
const value = Number(raw);
|
|
95991
|
-
return Number.isInteger(value) && value >= 0 ? value : null;
|
|
95992
|
-
}
|
|
95993
|
-
function qtiAttributeMaximum(attributes2, name3, fallback, unlimited) {
|
|
95994
|
-
const maximum = qtiAttributeNumber(attributes2, name3, fallback);
|
|
95995
|
-
return maximum === 0 ? unlimited : maximum;
|
|
95996
|
-
}
|
|
95997
|
-
function qtiAttributeBoolean(attributes2, name3) {
|
|
95998
|
-
const value = attributes2[name3];
|
|
95999
|
-
return value === "true" || value === "1";
|
|
96000
|
-
}
|
|
96001
96049
|
function gapContentIdentifiers(nodes) {
|
|
96002
96050
|
return nodes.flatMap((node) => {
|
|
96003
96051
|
if (node.kind === "gap") {
|
|
@@ -96078,6 +96126,7 @@ function authorableQtiInteractionProjection(interaction) {
|
|
|
96078
96126
|
type: interaction.type,
|
|
96079
96127
|
choices: interaction.choices,
|
|
96080
96128
|
minChoices: qtiAttributeNumber(attributes2, "min-choices", 1),
|
|
96129
|
+
shuffle: qtiAttributeBoolean(attributes2, "shuffle"),
|
|
96081
96130
|
maxChoices: qtiAttributeMaximum(attributes2, "max-choices", 1, interaction.choices.length)
|
|
96082
96131
|
};
|
|
96083
96132
|
}
|
|
@@ -96086,6 +96135,7 @@ function authorableQtiInteractionProjection(interaction) {
|
|
|
96086
96135
|
...base,
|
|
96087
96136
|
type: interaction.type,
|
|
96088
96137
|
choices: interaction.choices,
|
|
96138
|
+
shuffle: qtiAttributeBoolean(attributes2, "shuffle"),
|
|
96089
96139
|
blankIndex: interaction.blankIndex ?? 0
|
|
96090
96140
|
} : null;
|
|
96091
96141
|
}
|
|
@@ -99917,11 +99967,13 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99917
99967
|
playableContentNodesSchema = exports_external.array(exports_external.unknown());
|
|
99918
99968
|
playableChoiceSchema = exports_external.object({
|
|
99919
99969
|
identifier: trimmedNonEmptyString3,
|
|
99970
|
+
fixed: exports_external.boolean().optional(),
|
|
99920
99971
|
content: exports_external.string(),
|
|
99921
99972
|
contentNodes: playableContentNodesSchema.optional()
|
|
99922
99973
|
}).strict();
|
|
99923
99974
|
playableMatchChoiceSchema = exports_external.object({
|
|
99924
99975
|
identifier: trimmedNonEmptyString3,
|
|
99976
|
+
fixed: exports_external.boolean().optional(),
|
|
99925
99977
|
content: exports_external.string(),
|
|
99926
99978
|
contentNodes: playableContentNodesSchema.optional(),
|
|
99927
99979
|
matchMax: nonnegativeInteger
|
|
@@ -99941,6 +99993,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99941
99993
|
}).strict();
|
|
99942
99994
|
playableGapTextSchema = exports_external.object({
|
|
99943
99995
|
identifier: trimmedNonEmptyString3,
|
|
99996
|
+
fixed: exports_external.boolean().optional(),
|
|
99944
99997
|
content: exports_external.string(),
|
|
99945
99998
|
matchMax: nonnegativeInteger
|
|
99946
99999
|
}).strict();
|
|
@@ -99958,6 +100011,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99958
100011
|
exports_external.object({
|
|
99959
100012
|
...playableInteractionBase,
|
|
99960
100013
|
type: exports_external.literal("choice"),
|
|
100014
|
+
shuffle: exports_external.boolean(),
|
|
99961
100015
|
choices: exports_external.array(playableChoiceSchema),
|
|
99962
100016
|
minChoices: nonnegativeInteger,
|
|
99963
100017
|
maxChoices: nonnegativeInteger
|
|
@@ -99965,6 +100019,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99965
100019
|
exports_external.object({
|
|
99966
100020
|
...playableInteractionBase,
|
|
99967
100021
|
type: exports_external.literal("inline-choice"),
|
|
100022
|
+
shuffle: exports_external.boolean(),
|
|
99968
100023
|
choices: exports_external.array(playableChoiceSchema)
|
|
99969
100024
|
}).strict(),
|
|
99970
100025
|
exports_external.object({
|
|
@@ -100008,6 +100063,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
100008
100063
|
exports_external.object({
|
|
100009
100064
|
...playableInteractionBase,
|
|
100010
100065
|
type: exports_external.literal("gap-match"),
|
|
100066
|
+
shuffle: exports_external.boolean(),
|
|
100011
100067
|
content: exports_external.string(),
|
|
100012
100068
|
contentNodes: playableContentNodesSchema,
|
|
100013
100069
|
gapTexts: exports_external.array(playableGapTextSchema),
|
|
@@ -100346,7 +100402,6 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100346
100402
|
purpose: metadata2.purpose,
|
|
100347
100403
|
standards: [...metadata2.review.standards],
|
|
100348
100404
|
candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
|
|
100349
|
-
...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
100350
100405
|
selections: [...metadata2.review.selections],
|
|
100351
100406
|
fulfillment: metadata2.review.fulfillment
|
|
100352
100407
|
};
|
|
@@ -100521,6 +100576,7 @@ function playableChoice(choice) {
|
|
|
100521
100576
|
return {
|
|
100522
100577
|
identifier: choice.identifier,
|
|
100523
100578
|
content: choice.content,
|
|
100579
|
+
...qtiAttributeBoolean(choice.attributes, "fixed") ? { fixed: true } : {},
|
|
100524
100580
|
...choice.contentNodes ? { contentNodes: choice.contentNodes } : {}
|
|
100525
100581
|
};
|
|
100526
100582
|
}
|
|
@@ -100583,6 +100639,7 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100583
100639
|
responseIdentifier,
|
|
100584
100640
|
...prompt,
|
|
100585
100641
|
choices,
|
|
100642
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle"),
|
|
100586
100643
|
minChoices: qtiAttributeNumber(interaction.attributes, "min-choices", 1),
|
|
100587
100644
|
maxChoices: qtiAttributeMaximum(interaction.attributes, "max-choices", 1, choices.length)
|
|
100588
100645
|
};
|
|
@@ -100592,7 +100649,8 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100592
100649
|
type,
|
|
100593
100650
|
responseIdentifier,
|
|
100594
100651
|
...prompt,
|
|
100595
|
-
choices: requireChoices2(interaction.choices.map(playableChoice))
|
|
100652
|
+
choices: requireChoices2(interaction.choices.map(playableChoice)),
|
|
100653
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle")
|
|
100596
100654
|
};
|
|
100597
100655
|
}
|
|
100598
100656
|
if (type === "text-entry") {
|
|
@@ -100653,11 +100711,13 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100653
100711
|
responseIdentifier,
|
|
100654
100712
|
...prompt,
|
|
100655
100713
|
content: interaction.textWithGaps,
|
|
100714
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle"),
|
|
100656
100715
|
contentNodes: interaction.gapContent,
|
|
100657
100716
|
gapTexts: interaction.gapTexts.map((gapText) => ({
|
|
100658
100717
|
identifier: gapText.identifier,
|
|
100659
100718
|
content: gapText.content,
|
|
100660
|
-
matchMax: gapText.matchMax
|
|
100719
|
+
matchMax: gapText.matchMax,
|
|
100720
|
+
...gapText.fixed ? { fixed: true } : {}
|
|
100661
100721
|
})),
|
|
100662
100722
|
gaps: [...interaction.gaps],
|
|
100663
100723
|
maxAssociations: qtiAttributeNumber(interaction.attributes, "max-associations", 0)
|
|
@@ -101476,7 +101536,7 @@ function buildInlineChoiceQuestionXml(input, identifier, title) {
|
|
|
101476
101536
|
if (!envelope) {
|
|
101477
101537
|
return null;
|
|
101478
101538
|
}
|
|
101479
|
-
const { structure, responseIdentifier, prompt } = envelope;
|
|
101539
|
+
const { interaction, structure, responseIdentifier, prompt } = envelope;
|
|
101480
101540
|
const promptParts = prompt.split(INLINE_CHOICE_BLANK);
|
|
101481
101541
|
if (promptParts.length !== 2) {
|
|
101482
101542
|
throw new ValidationError("Inline-choice questions require exactly one blank");
|
|
@@ -101490,7 +101550,7 @@ function buildInlineChoiceQuestionXml(input, identifier, title) {
|
|
|
101490
101550
|
const choicesXml = choices.map((choice) => ` <qti-inline-choice identifier="${escapeXml(choice.identifier)}">${escapeXml(choice.content)}</qti-inline-choice>`).join(`
|
|
101491
101551
|
`);
|
|
101492
101552
|
const [before = "", after = ""] = promptParts;
|
|
101493
|
-
const itemBody = ` <p>${escapeXml(before)}<qti-inline-choice-interaction response-identifier="${escapeXml(responseIdentifier)}">
|
|
101553
|
+
const itemBody = ` <p>${escapeXml(before)}<qti-inline-choice-interaction response-identifier="${escapeXml(responseIdentifier)}" shuffle="${interaction.shuffle === true}">
|
|
101494
101554
|
${choicesXml}
|
|
101495
101555
|
</qti-inline-choice-interaction>${escapeXml(after)}</p>`;
|
|
101496
101556
|
return buildExactMatchQuestionXml({
|
|
@@ -102105,7 +102165,7 @@ async function prepareReviewMappingUpdate(test, questions) {
|
|
|
102105
102165
|
const contentRevision = await assessmentContentRevision(test, questions);
|
|
102106
102166
|
const assessment = {
|
|
102107
102167
|
identifier: test.identifier,
|
|
102108
|
-
contractVersion:
|
|
102168
|
+
contractVersion: PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION,
|
|
102109
102169
|
contentRevision,
|
|
102110
102170
|
title: test.title,
|
|
102111
102171
|
items: questions.questions.map(({ question }) => ({
|
|
@@ -102144,6 +102204,7 @@ async function prepareReviewMappingUpdate(test, questions) {
|
|
|
102144
102204
|
var PLAYCADEMY_REVIEW_BANK_TITLE = "Review Bank", PLAYCADEMY_REVIEW_BANK_SINK_METADATA_KEY = "playcademyReviewBankSink", PLAYCADEMY_REVIEW_BANK_SINK_VERSION = 1, StoredSinkWarningSchema, ReviewBankSinkMarkerSchema;
|
|
102145
102205
|
var init_timeback_review_mapping_util = __esm(() => {
|
|
102146
102206
|
init_esm();
|
|
102207
|
+
init_src();
|
|
102147
102208
|
init_assessment_runtime2();
|
|
102148
102209
|
init_qti();
|
|
102149
102210
|
init_timeback3();
|
|
@@ -102818,13 +102879,11 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102818
102879
|
review: {
|
|
102819
102880
|
requestFingerprint: reviewRequestFingerprint({
|
|
102820
102881
|
standards: plan.review.standards,
|
|
102821
|
-
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102822
|
-
requireBothQuestions: plan.review.requireBothQuestions
|
|
102882
|
+
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102823
102883
|
}, DEFAULT_REVIEW_SELECTION_POLICY),
|
|
102824
102884
|
bankRevision: plan.review.bankRevision,
|
|
102825
102885
|
standards: plan.review.standards,
|
|
102826
102886
|
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102827
|
-
...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
102828
102887
|
selections: plan.review.selections,
|
|
102829
102888
|
fulfillment: plan.review.fulfillment
|
|
102830
102889
|
}
|
|
@@ -104007,7 +104066,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104007
104066
|
bankRevision: catalog.bank.bankRevision,
|
|
104008
104067
|
standards: [...normalized.standards],
|
|
104009
104068
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
104010
|
-
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104011
104069
|
selections: [...selected.selections],
|
|
104012
104070
|
fulfillment: selected.fulfillment
|
|
104013
104071
|
}
|
|
@@ -104279,7 +104337,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104279
104337
|
bankRevision: source.bank.bankRevision,
|
|
104280
104338
|
standards: [...normalizedRequest.standards],
|
|
104281
104339
|
candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
|
|
104282
|
-
...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104283
104340
|
selections: [...selected.selections],
|
|
104284
104341
|
fulfillment: selected.fulfillment
|
|
104285
104342
|
}
|
package/dist/constants.js
CHANGED
|
@@ -100,7 +100,7 @@ var init_platform = __esm(() => {
|
|
|
100
100
|
var PLATFORM_TIMEZONE = "America/New_York";
|
|
101
101
|
|
|
102
102
|
// ../constants/src/timeback.ts
|
|
103
|
-
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION =
|
|
103
|
+
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY = "playcademyAssessment", PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY = "playcademyAssessmentItem", TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH = 2048, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
|
|
104
104
|
var init_timeback = __esm(() => {
|
|
105
105
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
106
106
|
TIMEBACK_SUBJECTS = [
|
package/dist/server.js
CHANGED
|
@@ -284,7 +284,7 @@ var init_platform = __esm(() => {
|
|
|
284
284
|
var PLATFORM_TIMEZONE = "America/New_York";
|
|
285
285
|
|
|
286
286
|
// ../constants/src/timeback.ts
|
|
287
|
-
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION =
|
|
287
|
+
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY = "playcademyAssessment", PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY = "playcademyAssessmentItem", TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH = 2048, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
|
|
288
288
|
var init_timeback2 = __esm(() => {
|
|
289
289
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
290
290
|
TIMEBACK_SUBJECTS = [
|
|
@@ -1127,7 +1127,7 @@ var package_default;
|
|
|
1127
1127
|
var init_package = __esm(() => {
|
|
1128
1128
|
package_default = {
|
|
1129
1129
|
name: "@playcademy/sandbox",
|
|
1130
|
-
version: "0.10.1-beta.
|
|
1130
|
+
version: "0.10.1-beta.4",
|
|
1131
1131
|
description: "Local development server for Playcademy game development",
|
|
1132
1132
|
type: "module",
|
|
1133
1133
|
exports: {
|
|
@@ -10833,30 +10833,60 @@ function comparePresentationText(left, right) {
|
|
|
10833
10833
|
}
|
|
10834
10834
|
return 0;
|
|
10835
10835
|
}
|
|
10836
|
+
function presentationRandomIndex(seed) {
|
|
10837
|
+
let state = presentationHash(seed);
|
|
10838
|
+
const range = 2 ** 32;
|
|
10839
|
+
return (bound) => {
|
|
10840
|
+
const limit = range - range % bound;
|
|
10841
|
+
let value;
|
|
10842
|
+
do {
|
|
10843
|
+
state = state + 1831565813 >>> 0;
|
|
10844
|
+
let mixed = Math.imul(state ^ state >>> 15, state | 1);
|
|
10845
|
+
mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, mixed | 61);
|
|
10846
|
+
value = (mixed ^ mixed >>> 14) >>> 0;
|
|
10847
|
+
} while (value >= limit);
|
|
10848
|
+
return value % bound;
|
|
10849
|
+
};
|
|
10850
|
+
}
|
|
10836
10851
|
function presentationChoices(choices, attemptId, itemIdentifier, domain) {
|
|
10837
|
-
const
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10852
|
+
const randomIndex = presentationRandomIndex(JSON.stringify([attemptId, itemIdentifier, domain]));
|
|
10853
|
+
const movable = choices.filter((choice) => !choice.fixed).toSorted((left, right) => comparePresentationText(left.identifier, right.identifier));
|
|
10854
|
+
for (let index = movable.length - 1;index > 0; index--) {
|
|
10855
|
+
const other = randomIndex(index + 1);
|
|
10856
|
+
[movable[index], movable[other]] = [movable[other], movable[index]];
|
|
10857
|
+
}
|
|
10858
|
+
let next = 0;
|
|
10859
|
+
return choices.map((choice) => choice.fixed ? choice : movable[next++]);
|
|
10842
10860
|
}
|
|
10843
10861
|
function assessmentPresentationForAttempt(assessment, attemptId) {
|
|
10844
10862
|
const items = assessment.items.map((item) => {
|
|
10845
10863
|
const interactions = item.interactions.map((interaction) => {
|
|
10846
|
-
if (
|
|
10847
|
-
return
|
|
10848
|
-
...interaction,
|
|
10849
|
-
choices: presentationChoices(interaction.choices, attemptId, item.identifier, `order\x00${interaction.responseIdentifier}`)
|
|
10850
|
-
};
|
|
10864
|
+
if (!("shuffle" in interaction) || !interaction.shuffle) {
|
|
10865
|
+
return interaction;
|
|
10851
10866
|
}
|
|
10852
|
-
|
|
10853
|
-
return {
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10867
|
+
function shuffled(choices, domain) {
|
|
10868
|
+
return presentationChoices(choices, attemptId, item.identifier, `${domain}\x00${interaction.responseIdentifier}`);
|
|
10869
|
+
}
|
|
10870
|
+
switch (interaction.type) {
|
|
10871
|
+
case "choice":
|
|
10872
|
+
case "inline-choice":
|
|
10873
|
+
case "order": {
|
|
10874
|
+
return {
|
|
10875
|
+
...interaction,
|
|
10876
|
+
choices: shuffled(interaction.choices, interaction.type)
|
|
10877
|
+
};
|
|
10878
|
+
}
|
|
10879
|
+
case "gap-match": {
|
|
10880
|
+
return { ...interaction, gapTexts: shuffled(interaction.gapTexts, "gap-match") };
|
|
10881
|
+
}
|
|
10882
|
+
case "match": {
|
|
10883
|
+
return {
|
|
10884
|
+
...interaction,
|
|
10885
|
+
sourceChoices: shuffled(interaction.sourceChoices, "match-source"),
|
|
10886
|
+
targetChoices: shuffled(interaction.targetChoices, "match-target")
|
|
10887
|
+
};
|
|
10888
|
+
}
|
|
10858
10889
|
}
|
|
10859
|
-
return interaction;
|
|
10860
10890
|
});
|
|
10861
10891
|
return { ...item, interactions };
|
|
10862
10892
|
});
|
|
@@ -11001,19 +11031,22 @@ function scorePlatformAssessmentItem(item, responses) {
|
|
|
11001
11031
|
isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
|
|
11002
11032
|
};
|
|
11003
11033
|
}
|
|
11034
|
+
function reviewStandardRef({
|
|
11035
|
+
framework,
|
|
11036
|
+
identifier
|
|
11037
|
+
}) {
|
|
11038
|
+
return { framework, identifier };
|
|
11039
|
+
}
|
|
11004
11040
|
function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
|
|
11005
|
-
if (!review.requireBothQuestions) {
|
|
11006
|
-
return;
|
|
11007
|
-
}
|
|
11008
11041
|
const administered = new Set(administeredItemIdentifiers2);
|
|
11009
11042
|
const failures = [];
|
|
11010
|
-
for (const standard of review.standards) {
|
|
11043
|
+
for (const standard of review.standards.filter((entry) => entry.requireBothQuestions)) {
|
|
11011
11044
|
const key = assessmentStandardRefKey(standard);
|
|
11012
11045
|
const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
|
|
11013
11046
|
const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
|
|
11014
11047
|
if (selected.size < 2 || administeredCount === 1) {
|
|
11015
11048
|
failures.push({
|
|
11016
|
-
standard,
|
|
11049
|
+
standard: reviewStandardRef(standard),
|
|
11017
11050
|
reason: selected.size < 2 ? "selection_shortage" : "not_administered",
|
|
11018
11051
|
requiredCount: 2,
|
|
11019
11052
|
selectedCount: selected.size,
|
|
@@ -11056,6 +11089,9 @@ function canonicalRequestStandards(standards) {
|
|
|
11056
11089
|
}
|
|
11057
11090
|
const canonical = new Map;
|
|
11058
11091
|
for (const input of standards) {
|
|
11092
|
+
if (input.requireBothQuestions !== undefined && typeof input.requireBothQuestions !== "boolean") {
|
|
11093
|
+
throw new Error("review standard requireBothQuestions must be a boolean");
|
|
11094
|
+
}
|
|
11059
11095
|
const standard = canonicalAssessmentStandardRef(input);
|
|
11060
11096
|
if (!reviewStandardFieldsWithinLimits(standard)) {
|
|
11061
11097
|
throw new RangeError(`Review standard fields may contain at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength} characters`);
|
|
@@ -11064,13 +11100,19 @@ function canonicalRequestStandards(standards) {
|
|
|
11064
11100
|
throw new Error("Review standards require a framework and identifier");
|
|
11065
11101
|
}
|
|
11066
11102
|
const key = assessmentStandardRefKey(standard);
|
|
11067
|
-
if (!canonical.has(key)) {
|
|
11068
|
-
canonical.set(key,
|
|
11103
|
+
if (!canonical.has(key) || input.requireBothQuestions) {
|
|
11104
|
+
canonical.set(key, {
|
|
11105
|
+
...standard,
|
|
11106
|
+
...input.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11107
|
+
});
|
|
11069
11108
|
}
|
|
11070
11109
|
}
|
|
11071
|
-
return [...canonical.values()]
|
|
11110
|
+
return [...canonical.values()];
|
|
11072
11111
|
}
|
|
11073
11112
|
function normalizeReviewRequest(request) {
|
|
11113
|
+
if ("requireBothQuestions" in request && request.requireBothQuestions !== undefined) {
|
|
11114
|
+
throw new Error("Set requireBothQuestions on individual review standards, not the review request");
|
|
11115
|
+
}
|
|
11074
11116
|
const candidateItemsPerStandard = request.candidateItemsPerStandard ?? 1;
|
|
11075
11117
|
if (!Number.isInteger(candidateItemsPerStandard) || candidateItemsPerStandard <= 0 || candidateItemsPerStandard > TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard) {
|
|
11076
11118
|
throw new RangeError(`candidateItemsPerStandard must be a positive integer at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard}`);
|
|
@@ -11081,20 +11123,20 @@ function normalizeReviewRequest(request) {
|
|
|
11081
11123
|
}
|
|
11082
11124
|
return {
|
|
11083
11125
|
standards,
|
|
11084
|
-
candidateItemsPerStandard
|
|
11085
|
-
...request.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11126
|
+
candidateItemsPerStandard
|
|
11086
11127
|
};
|
|
11087
11128
|
}
|
|
11088
11129
|
function reviewRequestFingerprint(request, policy) {
|
|
11089
11130
|
const normalized = normalizeReviewRequest(request);
|
|
11131
|
+
const requiredStandardKeys = normalized.standards.filter((standard) => standard.requireBothQuestions).map(assessmentStandardRefKey);
|
|
11090
11132
|
return JSON.stringify({
|
|
11091
11133
|
kind: "standards-review",
|
|
11092
11134
|
policy: {
|
|
11093
11135
|
version: policy.version
|
|
11094
11136
|
},
|
|
11095
11137
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
11096
|
-
|
|
11097
|
-
|
|
11138
|
+
standardKeys: normalized.standards.map(assessmentStandardRefKey),
|
|
11139
|
+
...requiredStandardKeys.length > 0 ? { requiredStandardKeys } : {}
|
|
11098
11140
|
});
|
|
11099
11141
|
}
|
|
11100
11142
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
@@ -11320,9 +11362,10 @@ function rankedCandidatesByStandard(bank, exposures) {
|
|
|
11320
11362
|
return byStandard;
|
|
11321
11363
|
}
|
|
11322
11364
|
function reviewSlots(request) {
|
|
11323
|
-
return request.standards.flatMap((standard) => Array.from({ length: request.candidateItemsPerStandard }, () => ({
|
|
11324
|
-
standard,
|
|
11325
|
-
standardKey: assessmentStandardRefKey(standard)
|
|
11365
|
+
return request.standards.flatMap((standard, servingIndex) => Array.from({ length: request.candidateItemsPerStandard }, () => ({
|
|
11366
|
+
standard: reviewStandardRef(standard),
|
|
11367
|
+
standardKey: assessmentStandardRefKey(standard),
|
|
11368
|
+
servingIndex
|
|
11326
11369
|
})));
|
|
11327
11370
|
}
|
|
11328
11371
|
function matchingCandidates(candidatesByStandard, slotCount) {
|
|
@@ -11435,7 +11478,7 @@ function fulfillment(request, candidatesByStandard, selections) {
|
|
|
11435
11478
|
const selected = selectedByStandard.get(key) ?? 0;
|
|
11436
11479
|
return selected < request.candidateItemsPerStandard ? [
|
|
11437
11480
|
{
|
|
11438
|
-
standard,
|
|
11481
|
+
standard: reviewStandardRef(standard),
|
|
11439
11482
|
requested: request.candidateItemsPerStandard,
|
|
11440
11483
|
selected,
|
|
11441
11484
|
eligible: candidatesByStandard.get(key)?.length ?? 0
|
|
@@ -11460,10 +11503,10 @@ function fulfillment(request, candidatesByStandard, selections) {
|
|
|
11460
11503
|
function selectReviewItems(input) {
|
|
11461
11504
|
const request = normalizeReviewRequest(input.request);
|
|
11462
11505
|
const candidatesByStandard = rankedCandidatesByStandard(input.bank, input.exposures);
|
|
11463
|
-
const slots = reviewSlots(request);
|
|
11506
|
+
const slots = reviewSlots(request).toSorted((left, right) => compareCodeUnits(left.standardKey, right.standardKey));
|
|
11464
11507
|
const boundedCandidates = matchingCandidates(candidatesByStandard, slots.length);
|
|
11465
11508
|
const selected = preferredDistinctMatching(slots, boundedCandidates);
|
|
11466
|
-
const selections = [...selected.entries()].toSorted(([left], [right]) => left - right).map(([index, itemIdentifier]) => ({
|
|
11509
|
+
const selections = [...selected.entries()].toSorted(([left], [right]) => slots[left].servingIndex - slots[right].servingIndex || left - right).map(([index, itemIdentifier]) => ({
|
|
11467
11510
|
standard: slots[index].standard,
|
|
11468
11511
|
itemIdentifier
|
|
11469
11512
|
}));
|
|
@@ -11602,6 +11645,9 @@ function isAssessmentScore(value) {
|
|
|
11602
11645
|
function isAssessmentStandardRef(value) {
|
|
11603
11646
|
return isRecord(value) && typeof value.framework === "string" && value.framework.length > 0 && typeof value.identifier === "string" && value.identifier.length > 0;
|
|
11604
11647
|
}
|
|
11648
|
+
function isAssessmentReviewStandard(value) {
|
|
11649
|
+
return isRecord(value) && isAssessmentStandardRef(value) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean");
|
|
11650
|
+
}
|
|
11605
11651
|
function isReviewFulfillment(value) {
|
|
11606
11652
|
if (!isRecord(value) || value.status !== "complete" && value.status !== "partial" || !Number.isInteger(value.requestedItemCount) || !Number.isInteger(value.selectedItemCount) || !Array.isArray(value.shortages)) {
|
|
11607
11653
|
return false;
|
|
@@ -11676,7 +11722,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
|
|
|
11676
11722
|
});
|
|
11677
11723
|
}
|
|
11678
11724
|
function isReviewAttemptMetadata(value) {
|
|
11679
|
-
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(
|
|
11725
|
+
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentReviewStandard) && Number.isInteger(value.candidateItemsPerStandard) && value.requireBothQuestions === undefined && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11680
11726
|
}
|
|
11681
11727
|
function isOptionalInteger(value) {
|
|
11682
11728
|
return value === undefined || Number.isInteger(value);
|
|
@@ -11795,6 +11841,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
11795
11841
|
init_src();
|
|
11796
11842
|
init_timeback3();
|
|
11797
11843
|
init_src();
|
|
11844
|
+
init_src();
|
|
11798
11845
|
init_uuid();
|
|
11799
11846
|
init_esm();
|
|
11800
11847
|
init_src();
|
|
@@ -12374,8 +12421,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12374
12421
|
}),
|
|
12375
12422
|
StartAssessmentBaseSchema.extend({
|
|
12376
12423
|
purpose: exports_external.literal("review"),
|
|
12377
|
-
requireBothQuestions: exports_external.
|
|
12378
|
-
standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12424
|
+
requireBothQuestions: exports_external.never().optional(),
|
|
12425
|
+
standards: exports_external.array(AssessmentStandardRefSchema2.and(exports_external.object({ requireBothQuestions: exports_external.boolean().optional() }))).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12379
12426
|
candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
|
|
12380
12427
|
}),
|
|
12381
12428
|
StartAssessmentBaseSchema.extend({
|
|
@@ -39192,7 +39239,7 @@ function assertMatchingAssessmentArtifactPair(playable, scoring, expectedIdentit
|
|
|
39192
39239
|
throw new ValidationError("Assessment artifact pair does not describe one revision");
|
|
39193
39240
|
}
|
|
39194
39241
|
}
|
|
39195
|
-
var ASSESSMENT_ARTIFACT_SCHEMA_VERSION =
|
|
39242
|
+
var ASSESSMENT_ARTIFACT_SCHEMA_VERSION = 2, ASSESSMENT_GRADER_PROTOCOL_VERSION = 1, ARTIFACT_KEY_PREFIX = "assessment-artifacts", SHA256_HEX, textEncoder;
|
|
39196
39243
|
var init_assessment_artifact_envelope_util = __esm(() => {
|
|
39197
39244
|
init_errors2();
|
|
39198
39245
|
SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
@@ -94286,6 +94333,30 @@ var init_timeback_game_metrics_comparison_util = __esm(() => {
|
|
|
94286
94333
|
});
|
|
94287
94334
|
|
|
94288
94335
|
// ../timeback/dist/engines/qti/index.js
|
|
94336
|
+
function qtiAttributeNumber(attributes, name3, fallback) {
|
|
94337
|
+
const raw = attributes[name3]?.trim();
|
|
94338
|
+
if (!raw) {
|
|
94339
|
+
return fallback;
|
|
94340
|
+
}
|
|
94341
|
+
const value = Number(raw);
|
|
94342
|
+
return Number.isFinite(value) ? value : fallback;
|
|
94343
|
+
}
|
|
94344
|
+
function qtiAttributeCapacity(attributes, name3, fallback) {
|
|
94345
|
+
const raw = attributes[name3]?.trim();
|
|
94346
|
+
if (!raw) {
|
|
94347
|
+
return fallback;
|
|
94348
|
+
}
|
|
94349
|
+
const value = Number(raw);
|
|
94350
|
+
return Number.isInteger(value) && value >= 0 ? value : null;
|
|
94351
|
+
}
|
|
94352
|
+
function qtiAttributeMaximum(attributes, name3, fallback, unlimited) {
|
|
94353
|
+
const maximum = qtiAttributeNumber(attributes, name3, fallback);
|
|
94354
|
+
return maximum === 0 ? unlimited : maximum;
|
|
94355
|
+
}
|
|
94356
|
+
function qtiAttributeBoolean(attributes, name3) {
|
|
94357
|
+
const value = attributes[name3];
|
|
94358
|
+
return value === "true" || value === "1";
|
|
94359
|
+
}
|
|
94289
94360
|
function decodeCssEscapes(value) {
|
|
94290
94361
|
return value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_, hex3) => {
|
|
94291
94362
|
const code = Number.parseInt(hex3, 16);
|
|
@@ -94873,7 +94944,8 @@ function parseGapTexts(interaction) {
|
|
|
94873
94944
|
return {
|
|
94874
94945
|
identifier: attributes2.identifier || "",
|
|
94875
94946
|
content: plainTextFromNodes(gapText.children),
|
|
94876
|
-
matchMax: finiteNumber(attributes2["match-max"]) ?? 1
|
|
94947
|
+
matchMax: finiteNumber(attributes2["match-max"]) ?? 1,
|
|
94948
|
+
...qtiAttributeBoolean(attributes2, "fixed") ? { fixed: true } : {}
|
|
94877
94949
|
};
|
|
94878
94950
|
}).filter((gapText) => gapText.identifier);
|
|
94879
94951
|
}
|
|
@@ -95973,30 +96045,6 @@ ${body2}
|
|
|
95973
96045
|
${processing}` : ""}
|
|
95974
96046
|
</qti-assessment-item>`;
|
|
95975
96047
|
}
|
|
95976
|
-
function qtiAttributeNumber(attributes2, name3, fallback) {
|
|
95977
|
-
const raw = attributes2[name3]?.trim();
|
|
95978
|
-
if (!raw) {
|
|
95979
|
-
return fallback;
|
|
95980
|
-
}
|
|
95981
|
-
const value = Number(raw);
|
|
95982
|
-
return Number.isFinite(value) ? value : fallback;
|
|
95983
|
-
}
|
|
95984
|
-
function qtiAttributeCapacity(attributes2, name3, fallback) {
|
|
95985
|
-
const raw = attributes2[name3]?.trim();
|
|
95986
|
-
if (!raw) {
|
|
95987
|
-
return fallback;
|
|
95988
|
-
}
|
|
95989
|
-
const value = Number(raw);
|
|
95990
|
-
return Number.isInteger(value) && value >= 0 ? value : null;
|
|
95991
|
-
}
|
|
95992
|
-
function qtiAttributeMaximum(attributes2, name3, fallback, unlimited) {
|
|
95993
|
-
const maximum = qtiAttributeNumber(attributes2, name3, fallback);
|
|
95994
|
-
return maximum === 0 ? unlimited : maximum;
|
|
95995
|
-
}
|
|
95996
|
-
function qtiAttributeBoolean(attributes2, name3) {
|
|
95997
|
-
const value = attributes2[name3];
|
|
95998
|
-
return value === "true" || value === "1";
|
|
95999
|
-
}
|
|
96000
96048
|
function gapContentIdentifiers(nodes) {
|
|
96001
96049
|
return nodes.flatMap((node) => {
|
|
96002
96050
|
if (node.kind === "gap") {
|
|
@@ -96077,6 +96125,7 @@ function authorableQtiInteractionProjection(interaction) {
|
|
|
96077
96125
|
type: interaction.type,
|
|
96078
96126
|
choices: interaction.choices,
|
|
96079
96127
|
minChoices: qtiAttributeNumber(attributes2, "min-choices", 1),
|
|
96128
|
+
shuffle: qtiAttributeBoolean(attributes2, "shuffle"),
|
|
96080
96129
|
maxChoices: qtiAttributeMaximum(attributes2, "max-choices", 1, interaction.choices.length)
|
|
96081
96130
|
};
|
|
96082
96131
|
}
|
|
@@ -96085,6 +96134,7 @@ function authorableQtiInteractionProjection(interaction) {
|
|
|
96085
96134
|
...base,
|
|
96086
96135
|
type: interaction.type,
|
|
96087
96136
|
choices: interaction.choices,
|
|
96137
|
+
shuffle: qtiAttributeBoolean(attributes2, "shuffle"),
|
|
96088
96138
|
blankIndex: interaction.blankIndex ?? 0
|
|
96089
96139
|
} : null;
|
|
96090
96140
|
}
|
|
@@ -99916,11 +99966,13 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99916
99966
|
playableContentNodesSchema = exports_external.array(exports_external.unknown());
|
|
99917
99967
|
playableChoiceSchema = exports_external.object({
|
|
99918
99968
|
identifier: trimmedNonEmptyString3,
|
|
99969
|
+
fixed: exports_external.boolean().optional(),
|
|
99919
99970
|
content: exports_external.string(),
|
|
99920
99971
|
contentNodes: playableContentNodesSchema.optional()
|
|
99921
99972
|
}).strict();
|
|
99922
99973
|
playableMatchChoiceSchema = exports_external.object({
|
|
99923
99974
|
identifier: trimmedNonEmptyString3,
|
|
99975
|
+
fixed: exports_external.boolean().optional(),
|
|
99924
99976
|
content: exports_external.string(),
|
|
99925
99977
|
contentNodes: playableContentNodesSchema.optional(),
|
|
99926
99978
|
matchMax: nonnegativeInteger
|
|
@@ -99940,6 +99992,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99940
99992
|
}).strict();
|
|
99941
99993
|
playableGapTextSchema = exports_external.object({
|
|
99942
99994
|
identifier: trimmedNonEmptyString3,
|
|
99995
|
+
fixed: exports_external.boolean().optional(),
|
|
99943
99996
|
content: exports_external.string(),
|
|
99944
99997
|
matchMax: nonnegativeInteger
|
|
99945
99998
|
}).strict();
|
|
@@ -99957,6 +100010,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99957
100010
|
exports_external.object({
|
|
99958
100011
|
...playableInteractionBase,
|
|
99959
100012
|
type: exports_external.literal("choice"),
|
|
100013
|
+
shuffle: exports_external.boolean(),
|
|
99960
100014
|
choices: exports_external.array(playableChoiceSchema),
|
|
99961
100015
|
minChoices: nonnegativeInteger,
|
|
99962
100016
|
maxChoices: nonnegativeInteger
|
|
@@ -99964,6 +100018,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
99964
100018
|
exports_external.object({
|
|
99965
100019
|
...playableInteractionBase,
|
|
99966
100020
|
type: exports_external.literal("inline-choice"),
|
|
100021
|
+
shuffle: exports_external.boolean(),
|
|
99967
100022
|
choices: exports_external.array(playableChoiceSchema)
|
|
99968
100023
|
}).strict(),
|
|
99969
100024
|
exports_external.object({
|
|
@@ -100007,6 +100062,7 @@ var init_assessment_artifact_runtime_util = __esm(() => {
|
|
|
100007
100062
|
exports_external.object({
|
|
100008
100063
|
...playableInteractionBase,
|
|
100009
100064
|
type: exports_external.literal("gap-match"),
|
|
100065
|
+
shuffle: exports_external.boolean(),
|
|
100010
100066
|
content: exports_external.string(),
|
|
100011
100067
|
contentNodes: playableContentNodesSchema,
|
|
100012
100068
|
gapTexts: exports_external.array(playableGapTextSchema),
|
|
@@ -100345,7 +100401,6 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100345
100401
|
purpose: metadata2.purpose,
|
|
100346
100402
|
standards: [...metadata2.review.standards],
|
|
100347
100403
|
candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
|
|
100348
|
-
...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
100349
100404
|
selections: [...metadata2.review.selections],
|
|
100350
100405
|
fulfillment: metadata2.review.fulfillment
|
|
100351
100406
|
};
|
|
@@ -100520,6 +100575,7 @@ function playableChoice(choice) {
|
|
|
100520
100575
|
return {
|
|
100521
100576
|
identifier: choice.identifier,
|
|
100522
100577
|
content: choice.content,
|
|
100578
|
+
...qtiAttributeBoolean(choice.attributes, "fixed") ? { fixed: true } : {},
|
|
100523
100579
|
...choice.contentNodes ? { contentNodes: choice.contentNodes } : {}
|
|
100524
100580
|
};
|
|
100525
100581
|
}
|
|
@@ -100582,6 +100638,7 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100582
100638
|
responseIdentifier,
|
|
100583
100639
|
...prompt,
|
|
100584
100640
|
choices,
|
|
100641
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle"),
|
|
100585
100642
|
minChoices: qtiAttributeNumber(interaction.attributes, "min-choices", 1),
|
|
100586
100643
|
maxChoices: qtiAttributeMaximum(interaction.attributes, "max-choices", 1, choices.length)
|
|
100587
100644
|
};
|
|
@@ -100591,7 +100648,8 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100591
100648
|
type,
|
|
100592
100649
|
responseIdentifier,
|
|
100593
100650
|
...prompt,
|
|
100594
|
-
choices: requireChoices2(interaction.choices.map(playableChoice))
|
|
100651
|
+
choices: requireChoices2(interaction.choices.map(playableChoice)),
|
|
100652
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle")
|
|
100595
100653
|
};
|
|
100596
100654
|
}
|
|
100597
100655
|
if (type === "text-entry") {
|
|
@@ -100652,11 +100710,13 @@ function parseInteraction2(questionIdentifier, interaction) {
|
|
|
100652
100710
|
responseIdentifier,
|
|
100653
100711
|
...prompt,
|
|
100654
100712
|
content: interaction.textWithGaps,
|
|
100713
|
+
shuffle: qtiAttributeBoolean(interaction.attributes, "shuffle"),
|
|
100655
100714
|
contentNodes: interaction.gapContent,
|
|
100656
100715
|
gapTexts: interaction.gapTexts.map((gapText) => ({
|
|
100657
100716
|
identifier: gapText.identifier,
|
|
100658
100717
|
content: gapText.content,
|
|
100659
|
-
matchMax: gapText.matchMax
|
|
100718
|
+
matchMax: gapText.matchMax,
|
|
100719
|
+
...gapText.fixed ? { fixed: true } : {}
|
|
100660
100720
|
})),
|
|
100661
100721
|
gaps: [...interaction.gaps],
|
|
100662
100722
|
maxAssociations: qtiAttributeNumber(interaction.attributes, "max-associations", 0)
|
|
@@ -101475,7 +101535,7 @@ function buildInlineChoiceQuestionXml(input, identifier, title) {
|
|
|
101475
101535
|
if (!envelope) {
|
|
101476
101536
|
return null;
|
|
101477
101537
|
}
|
|
101478
|
-
const { structure, responseIdentifier, prompt } = envelope;
|
|
101538
|
+
const { interaction, structure, responseIdentifier, prompt } = envelope;
|
|
101479
101539
|
const promptParts = prompt.split(INLINE_CHOICE_BLANK);
|
|
101480
101540
|
if (promptParts.length !== 2) {
|
|
101481
101541
|
throw new ValidationError("Inline-choice questions require exactly one blank");
|
|
@@ -101489,7 +101549,7 @@ function buildInlineChoiceQuestionXml(input, identifier, title) {
|
|
|
101489
101549
|
const choicesXml = choices.map((choice) => ` <qti-inline-choice identifier="${escapeXml(choice.identifier)}">${escapeXml(choice.content)}</qti-inline-choice>`).join(`
|
|
101490
101550
|
`);
|
|
101491
101551
|
const [before = "", after = ""] = promptParts;
|
|
101492
|
-
const itemBody = ` <p>${escapeXml(before)}<qti-inline-choice-interaction response-identifier="${escapeXml(responseIdentifier)}">
|
|
101552
|
+
const itemBody = ` <p>${escapeXml(before)}<qti-inline-choice-interaction response-identifier="${escapeXml(responseIdentifier)}" shuffle="${interaction.shuffle === true}">
|
|
101493
101553
|
${choicesXml}
|
|
101494
101554
|
</qti-inline-choice-interaction>${escapeXml(after)}</p>`;
|
|
101495
101555
|
return buildExactMatchQuestionXml({
|
|
@@ -102104,7 +102164,7 @@ async function prepareReviewMappingUpdate(test, questions) {
|
|
|
102104
102164
|
const contentRevision = await assessmentContentRevision(test, questions);
|
|
102105
102165
|
const assessment = {
|
|
102106
102166
|
identifier: test.identifier,
|
|
102107
|
-
contractVersion:
|
|
102167
|
+
contractVersion: PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION,
|
|
102108
102168
|
contentRevision,
|
|
102109
102169
|
title: test.title,
|
|
102110
102170
|
items: questions.questions.map(({ question }) => ({
|
|
@@ -102143,6 +102203,7 @@ async function prepareReviewMappingUpdate(test, questions) {
|
|
|
102143
102203
|
var PLAYCADEMY_REVIEW_BANK_TITLE = "Review Bank", PLAYCADEMY_REVIEW_BANK_SINK_METADATA_KEY = "playcademyReviewBankSink", PLAYCADEMY_REVIEW_BANK_SINK_VERSION = 1, StoredSinkWarningSchema, ReviewBankSinkMarkerSchema;
|
|
102144
102204
|
var init_timeback_review_mapping_util = __esm(() => {
|
|
102145
102205
|
init_esm();
|
|
102206
|
+
init_src();
|
|
102146
102207
|
init_assessment_runtime2();
|
|
102147
102208
|
init_qti();
|
|
102148
102209
|
init_timeback3();
|
|
@@ -102817,13 +102878,11 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102817
102878
|
review: {
|
|
102818
102879
|
requestFingerprint: reviewRequestFingerprint({
|
|
102819
102880
|
standards: plan.review.standards,
|
|
102820
|
-
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102821
|
-
requireBothQuestions: plan.review.requireBothQuestions
|
|
102881
|
+
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102822
102882
|
}, DEFAULT_REVIEW_SELECTION_POLICY),
|
|
102823
102883
|
bankRevision: plan.review.bankRevision,
|
|
102824
102884
|
standards: plan.review.standards,
|
|
102825
102885
|
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102826
|
-
...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
102827
102886
|
selections: plan.review.selections,
|
|
102828
102887
|
fulfillment: plan.review.fulfillment
|
|
102829
102888
|
}
|
|
@@ -104006,7 +104065,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104006
104065
|
bankRevision: catalog.bank.bankRevision,
|
|
104007
104066
|
standards: [...normalized.standards],
|
|
104008
104067
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
104009
|
-
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104010
104068
|
selections: [...selected.selections],
|
|
104011
104069
|
fulfillment: selected.fulfillment
|
|
104012
104070
|
}
|
|
@@ -104278,7 +104336,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104278
104336
|
bankRevision: source.bank.bankRevision,
|
|
104279
104337
|
standards: [...normalizedRequest.standards],
|
|
104280
104338
|
candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
|
|
104281
|
-
...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104282
104339
|
selections: [...selected.selections],
|
|
104283
104340
|
fulfillment: selected.fulfillment
|
|
104284
104341
|
}
|