@playcademy/vite-plugin 1.3.2-beta.3 → 1.3.2-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/index.js +1196 -1927
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -24317,7 +24317,7 @@ import path2 from "node:path";
|
|
|
24317
24317
|
// package.json
|
|
24318
24318
|
var package_default = {
|
|
24319
24319
|
name: "@playcademy/vite-plugin",
|
|
24320
|
-
version: "1.3.2-beta.
|
|
24320
|
+
version: "1.3.2-beta.4",
|
|
24321
24321
|
type: "module",
|
|
24322
24322
|
exports: {
|
|
24323
24323
|
".": {
|
|
@@ -25058,7 +25058,6 @@ var TIMEBACK_GRADES;
|
|
|
25058
25058
|
var TIMEBACK_SUBJECTS;
|
|
25059
25059
|
var ASSESSMENT_PURPOSES;
|
|
25060
25060
|
var PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1;
|
|
25061
|
-
var PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION = 1;
|
|
25062
25061
|
var PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 1;
|
|
25063
25062
|
var PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY = "playcademyAssessment";
|
|
25064
25063
|
var PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY = "playcademyAssessmentItem";
|
|
@@ -25934,7 +25933,7 @@ var package_default2;
|
|
|
25934
25933
|
var init_package = __esm(() => {
|
|
25935
25934
|
package_default2 = {
|
|
25936
25935
|
name: "@playcademy/sandbox",
|
|
25937
|
-
version: "0.8.
|
|
25936
|
+
version: "0.8.1-beta.6",
|
|
25938
25937
|
description: "Local development server for Playcademy game development",
|
|
25939
25938
|
type: "module",
|
|
25940
25939
|
exports: {
|
|
@@ -34175,25 +34174,6 @@ var init_v3 = __esm(() => {
|
|
|
34175
34174
|
var init_esm = __esm(() => {
|
|
34176
34175
|
init_v3();
|
|
34177
34176
|
});
|
|
34178
|
-
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
34179
|
-
var init_assessment_runtime = __esm(() => {
|
|
34180
|
-
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
34181
|
-
});
|
|
34182
|
-
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1;
|
|
34183
|
-
var DIAGNOSTIC_ROUTING_STATE_VERSION = 1;
|
|
34184
|
-
var DIAGNOSTIC_TRACK_OUTCOMES;
|
|
34185
|
-
var init_diagnostic_routing = __esm(() => {
|
|
34186
|
-
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
34187
|
-
"provisional",
|
|
34188
|
-
"unresolved",
|
|
34189
|
-
"route-to-instruction",
|
|
34190
|
-
"not-assessed"
|
|
34191
|
-
];
|
|
34192
|
-
});
|
|
34193
|
-
var init_timeback4 = __esm(() => {
|
|
34194
|
-
init_assessment_runtime();
|
|
34195
|
-
init_diagnostic_routing();
|
|
34196
|
-
});
|
|
34197
34177
|
function isValidUUID(value) {
|
|
34198
34178
|
if (!value || typeof value !== "string") {
|
|
34199
34179
|
return false;
|
|
@@ -34215,6 +34195,25 @@ var UUID_REGEX;
|
|
|
34215
34195
|
var init_uuid = __esm(() => {
|
|
34216
34196
|
UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
34217
34197
|
});
|
|
34198
|
+
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
34199
|
+
var init_assessment_runtime = __esm(() => {
|
|
34200
|
+
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
34201
|
+
});
|
|
34202
|
+
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1;
|
|
34203
|
+
var DIAGNOSTIC_ROUTING_STATE_VERSION = 1;
|
|
34204
|
+
var DIAGNOSTIC_TRACK_OUTCOMES;
|
|
34205
|
+
var init_diagnostic_routing = __esm(() => {
|
|
34206
|
+
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
34207
|
+
"provisional",
|
|
34208
|
+
"unresolved",
|
|
34209
|
+
"route-to-instruction",
|
|
34210
|
+
"not-assessed"
|
|
34211
|
+
];
|
|
34212
|
+
});
|
|
34213
|
+
var init_timeback4 = __esm(() => {
|
|
34214
|
+
init_assessment_runtime();
|
|
34215
|
+
init_diagnostic_routing();
|
|
34216
|
+
});
|
|
34218
34217
|
function qtiDirectedPairValidationIssue(values, contract) {
|
|
34219
34218
|
const sourceCounts = new Map;
|
|
34220
34219
|
const targetCounts = new Map;
|
|
@@ -34492,6 +34491,25 @@ function assessmentReviewBankShortage(fulfillment) {
|
|
|
34492
34491
|
function assessmentFlowForPurpose(purpose) {
|
|
34493
34492
|
return purpose === "review" ? "item-submit" : "attempt-submit";
|
|
34494
34493
|
}
|
|
34494
|
+
function isObject(value) {
|
|
34495
|
+
return typeof value === "object" && value !== null;
|
|
34496
|
+
}
|
|
34497
|
+
function isTimebackGrade(value) {
|
|
34498
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
34499
|
+
}
|
|
34500
|
+
function isAssessmentSessionTiming(value) {
|
|
34501
|
+
return isObject(value) && typeof value.runId === "string" && isValidUUID(value.runId) && typeof value.resumeId === "string" && isValidUUID(value.resumeId) && isNonNegativeDuration(value.activeSeconds) && (value.inactiveSeconds === undefined || isNonNegativeDuration(value.inactiveSeconds));
|
|
34502
|
+
}
|
|
34503
|
+
function isNonNegativeDuration(value) {
|
|
34504
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
34505
|
+
}
|
|
34506
|
+
function isAssessmentPurpose(value) {
|
|
34507
|
+
return ASSESSMENT_PURPOSES.includes(value);
|
|
34508
|
+
}
|
|
34509
|
+
function isAssessmentResponseValue(value) {
|
|
34510
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
34511
|
+
return entries.length > 0 && entries.every((entry2) => typeof entry2 === "string" && entry2.length > 0 && entry2.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
34512
|
+
}
|
|
34495
34513
|
function compareCodeUnits(left, right) {
|
|
34496
34514
|
if (left < right) {
|
|
34497
34515
|
return -1;
|
|
@@ -35345,48 +35363,6 @@ function projectDiagnosticRoutingSnapshot(revision, state) {
|
|
|
35345
35363
|
}))
|
|
35346
35364
|
};
|
|
35347
35365
|
}
|
|
35348
|
-
function isObject(value) {
|
|
35349
|
-
return typeof value === "object" && value !== null;
|
|
35350
|
-
}
|
|
35351
|
-
function isTimebackGrade(value) {
|
|
35352
|
-
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
35353
|
-
}
|
|
35354
|
-
function isAssessmentSessionTiming(value) {
|
|
35355
|
-
return isObject(value) && typeof value.runId === "string" && isValidUUID(value.runId) && typeof value.resumeId === "string" && isValidUUID(value.resumeId) && isNonNegativeDuration(value.activeSeconds) && (value.inactiveSeconds === undefined || isNonNegativeDuration(value.inactiveSeconds));
|
|
35356
|
-
}
|
|
35357
|
-
function isNonNegativeDuration(value) {
|
|
35358
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
35359
|
-
}
|
|
35360
|
-
function isAssessmentPurpose(value) {
|
|
35361
|
-
return ASSESSMENT_PURPOSES.includes(value);
|
|
35362
|
-
}
|
|
35363
|
-
function isAssessmentResponseValue(value) {
|
|
35364
|
-
const entries = Array.isArray(value) ? value : [value];
|
|
35365
|
-
return entries.length > 0 && entries.every((entry2) => typeof entry2 === "string" && entry2.length > 0 && entry2.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
35366
|
-
}
|
|
35367
|
-
function applyAssessmentItemResponseUpdate(current, update) {
|
|
35368
|
-
const next = { ...current };
|
|
35369
|
-
for (const [responseId, value] of Object.entries(update)) {
|
|
35370
|
-
if (value === null) {
|
|
35371
|
-
delete next[responseId];
|
|
35372
|
-
} else {
|
|
35373
|
-
next[responseId] = value;
|
|
35374
|
-
}
|
|
35375
|
-
}
|
|
35376
|
-
return Object.keys(next).length === 0 ? undefined : next;
|
|
35377
|
-
}
|
|
35378
|
-
function applyAssessmentResponseUpdate(current, update) {
|
|
35379
|
-
const next = structuredClone(current);
|
|
35380
|
-
for (const [questionId, questionUpdate] of Object.entries(update)) {
|
|
35381
|
-
const questionResponses = applyAssessmentItemResponseUpdate(next[questionId], questionUpdate);
|
|
35382
|
-
if (questionResponses === undefined) {
|
|
35383
|
-
delete next[questionId];
|
|
35384
|
-
} else {
|
|
35385
|
-
next[questionId] = questionResponses;
|
|
35386
|
-
}
|
|
35387
|
-
}
|
|
35388
|
-
return next;
|
|
35389
|
-
}
|
|
35390
35366
|
function assessmentResponseUpdateValidationMessage(assessment, update) {
|
|
35391
35367
|
const items = new Map(assessment.items.map((item) => [item.identifier, item]));
|
|
35392
35368
|
for (const [itemIdentifier, itemUpdate] of Object.entries(update)) {
|
|
@@ -35548,147 +35524,154 @@ function interactionResponseValidationMessage(itemIdentifier, interaction, respo
|
|
|
35548
35524
|
}
|
|
35549
35525
|
return null;
|
|
35550
35526
|
}
|
|
35551
|
-
function
|
|
35552
|
-
return {
|
|
35527
|
+
function emptyAssessmentTranscript(attemptId, contentRevision) {
|
|
35528
|
+
return { version: 1, attemptId, contentRevision, administrations: [] };
|
|
35553
35529
|
}
|
|
35554
|
-
function
|
|
35555
|
-
|
|
35556
|
-
}
|
|
35557
|
-
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
35558
|
-
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
35559
|
-
}
|
|
35560
|
-
function responseValuesEqual(left, right) {
|
|
35561
|
-
if (left === undefined || right === undefined) {
|
|
35562
|
-
return left === right;
|
|
35563
|
-
}
|
|
35564
|
-
if (Array.isArray(left) || Array.isArray(right)) {
|
|
35565
|
-
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
35566
|
-
}
|
|
35567
|
-
return left === right;
|
|
35568
|
-
}
|
|
35569
|
-
function itemResponsesEqual(left, right) {
|
|
35570
|
-
const leftEntries = Object.entries(left ?? {});
|
|
35571
|
-
const rightEntries = Object.entries(right ?? {});
|
|
35572
|
-
return leftEntries.length === rightEntries.length && leftEntries.every(([identifier, value]) => responseValuesEqual(value, right?.[identifier]));
|
|
35573
|
-
}
|
|
35574
|
-
function itemResponseUpdateMatches(current, update) {
|
|
35575
|
-
return itemResponsesEqual(current, applyAssessmentItemResponseUpdate(current, update));
|
|
35576
|
-
}
|
|
35577
|
-
function resolveAssessmentItemReplay(state, input) {
|
|
35578
|
-
const flow = assessmentFlowForPurpose(state.purpose);
|
|
35579
|
-
if (flow !== "item-submit") {
|
|
35530
|
+
function assessmentProgressAdministration(purpose, input) {
|
|
35531
|
+
if (purpose !== "mastery") {
|
|
35580
35532
|
return {
|
|
35581
|
-
|
|
35582
|
-
failure: assessmentFlowViolation("
|
|
35533
|
+
ok: false,
|
|
35534
|
+
failure: assessmentFlowViolation("Background progress is only available for mastery.")
|
|
35583
35535
|
};
|
|
35584
35536
|
}
|
|
35585
|
-
const
|
|
35586
|
-
if (
|
|
35587
|
-
const sameRequest = priorSubmission.itemIdentifier === input.itemIdentifier && itemResponseUpdateMatches(state.responses[input.itemIdentifier], input.responses);
|
|
35588
|
-
if (!sameRequest) {
|
|
35589
|
-
return {
|
|
35590
|
-
action: "conflict",
|
|
35591
|
-
failure: assessmentFlowViolation("This item submission ID was already used for a different request.", {
|
|
35592
|
-
submissionId: input.submissionId,
|
|
35593
|
-
itemIdentifier: input.itemIdentifier,
|
|
35594
|
-
committedItemIdentifier: priorSubmission.itemIdentifier,
|
|
35595
|
-
flow
|
|
35596
|
-
})
|
|
35597
|
-
};
|
|
35598
|
-
}
|
|
35537
|
+
const { administrations } = input.transcript;
|
|
35538
|
+
if (input.expectedResponseVersion !== administrations.length) {
|
|
35599
35539
|
return {
|
|
35600
|
-
|
|
35601
|
-
|
|
35602
|
-
responses: state.responses,
|
|
35603
|
-
submission: priorSubmission
|
|
35540
|
+
ok: false,
|
|
35541
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, administrations.length)
|
|
35604
35542
|
};
|
|
35605
35543
|
}
|
|
35606
|
-
|
|
35544
|
+
const administration = administrations.find((entry2) => entry2.itemIdentifier === input.itemIdentifier);
|
|
35545
|
+
return administration ? { ok: true, submissionId: administration.submissionId } : {
|
|
35546
|
+
ok: false,
|
|
35547
|
+
failure: assessmentFlowViolation("The progress item must be present in the transcript.")
|
|
35548
|
+
};
|
|
35607
35549
|
}
|
|
35608
|
-
function
|
|
35609
|
-
const
|
|
35610
|
-
|
|
35611
|
-
|
|
35612
|
-
|
|
35613
|
-
|
|
35614
|
-
|
|
35615
|
-
|
|
35616
|
-
|
|
35550
|
+
function appendAssessmentAdministration(transcript, input) {
|
|
35551
|
+
const entry2 = {
|
|
35552
|
+
submissionId: input.submissionId,
|
|
35553
|
+
itemIdentifier: input.itemIdentifier,
|
|
35554
|
+
...input.routingNodeKey === undefined ? {} : { routingNodeKey: input.routingNodeKey },
|
|
35555
|
+
responses: Object.fromEntries(Object.entries(input.responses).filter((pair) => pair[1] !== null))
|
|
35556
|
+
};
|
|
35557
|
+
const priorIndex = transcript.administrations.findIndex((item) => item.submissionId === input.submissionId);
|
|
35558
|
+
const index = priorIndex === -1 ? transcript.administrations.length : priorIndex;
|
|
35559
|
+
if (input.expectedResponseVersion !== index) {
|
|
35617
35560
|
return {
|
|
35618
|
-
|
|
35619
|
-
failure: assessmentResponseVersionConflict(input.expectedResponseVersion,
|
|
35561
|
+
ok: false,
|
|
35562
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, index)
|
|
35620
35563
|
};
|
|
35621
35564
|
}
|
|
35622
|
-
|
|
35623
|
-
const previousItemIdentifier = state.itemSubmissions.at(-1)?.itemIdentifier;
|
|
35624
|
-
const previousItemIndex = state.assessment.items.findIndex((item) => item.identifier === previousItemIdentifier);
|
|
35625
|
-
if (itemIndex === -1 || itemIndex <= previousItemIndex) {
|
|
35565
|
+
if (priorIndex !== -1 && canonicalJson(transcript.administrations[priorIndex]) !== canonicalJson(entry2)) {
|
|
35626
35566
|
return {
|
|
35627
|
-
|
|
35628
|
-
failure: assessmentFlowViolation("
|
|
35629
|
-
itemIdentifier: input.itemIdentifier,
|
|
35630
|
-
previousItemIdentifier: previousItemIdentifier ?? null,
|
|
35631
|
-
flow: assessmentFlowForPurpose(state.purpose)
|
|
35632
|
-
})
|
|
35567
|
+
ok: false,
|
|
35568
|
+
failure: assessmentFlowViolation("This administration identity was reused with different answers.")
|
|
35633
35569
|
};
|
|
35634
35570
|
}
|
|
35635
|
-
const update = { [input.itemIdentifier]: input.responses };
|
|
35636
|
-
const updateMessage = assessmentResponseUpdateValidationMessage(state.assessment, update);
|
|
35637
|
-
if (updateMessage) {
|
|
35638
|
-
return { action: "reject", failure: invalidResponse(updateMessage) };
|
|
35639
|
-
}
|
|
35640
|
-
const responses = applyAssessmentResponseUpdate(state.responses, update);
|
|
35641
|
-
const validationMessage = assessmentResponseValidationMessage(state.assessment, responses);
|
|
35642
|
-
if (validationMessage) {
|
|
35643
|
-
return { action: "reject", failure: invalidResponse(validationMessage) };
|
|
35644
|
-
}
|
|
35645
35571
|
return {
|
|
35646
|
-
|
|
35647
|
-
|
|
35648
|
-
|
|
35649
|
-
responses,
|
|
35650
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0
|
|
35572
|
+
ok: true,
|
|
35573
|
+
index,
|
|
35574
|
+
transcript: priorIndex === -1 ? { ...transcript, administrations: [...transcript.administrations, entry2] } : transcript
|
|
35651
35575
|
};
|
|
35652
35576
|
}
|
|
35653
|
-
function
|
|
35654
|
-
const
|
|
35655
|
-
|
|
35656
|
-
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
|
|
35660
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
35663
|
-
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35667
|
-
|
|
35668
|
-
|
|
35669
|
-
|
|
35577
|
+
function prepareAssessmentTranscript(input) {
|
|
35578
|
+
const parsed = AssessmentTranscriptSchema.safeParse(input.transcript);
|
|
35579
|
+
if (!parsed.success) {
|
|
35580
|
+
throw new AssessmentTranscriptError("Invalid assessment transcript", 0, "INVALID_RESPONSE");
|
|
35581
|
+
}
|
|
35582
|
+
const transcript = parsed.data;
|
|
35583
|
+
if (transcript.attemptId !== input.attemptId || transcript.contentRevision !== input.contentRevision) {
|
|
35584
|
+
throw new AssessmentTranscriptError("Transcript does not belong to this attempt and revision");
|
|
35585
|
+
}
|
|
35586
|
+
const responses = {};
|
|
35587
|
+
const submissions = new Set;
|
|
35588
|
+
const items = new Set;
|
|
35589
|
+
let previousItemIndex = -1;
|
|
35590
|
+
for (const [index, entry2] of transcript.administrations.entries()) {
|
|
35591
|
+
const itemIndex = input.assessment.items.findIndex((item) => item.identifier === entry2.itemIdentifier);
|
|
35592
|
+
if (submissions.has(entry2.submissionId) || items.has(entry2.itemIdentifier)) {
|
|
35593
|
+
throw new AssessmentTranscriptError("Transcript contains a repeated administration or item", index);
|
|
35594
|
+
}
|
|
35595
|
+
if (itemIndex === -1) {
|
|
35596
|
+
throw new AssessmentTranscriptError("Transcript references an unknown assessment item", index, "INVALID_RESPONSE");
|
|
35597
|
+
}
|
|
35598
|
+
if (!input.routed && itemIndex <= previousItemIndex) {
|
|
35599
|
+
throw new AssessmentTranscriptError("Transcript items must follow the selected candidate order", index);
|
|
35600
|
+
}
|
|
35601
|
+
if (input.routed !== (entry2.routingNodeKey !== undefined)) {
|
|
35602
|
+
throw new AssessmentTranscriptError("Transcript routing identity does not match the assessment flow", index);
|
|
35603
|
+
}
|
|
35604
|
+
const itemResponses = { [entry2.itemIdentifier]: entry2.responses };
|
|
35605
|
+
const validate2 = input.routed || input.allowIncomplete ? assessmentDraftResponseValidationMessage : assessmentResponseValidationMessage;
|
|
35606
|
+
const invalid = Object.keys(entry2.responses).length === 0 ? null : validate2(input.assessment, itemResponses);
|
|
35607
|
+
if (invalid) {
|
|
35608
|
+
throw new AssessmentTranscriptError(invalid, index, "INVALID_RESPONSE");
|
|
35609
|
+
}
|
|
35610
|
+
submissions.add(entry2.submissionId);
|
|
35611
|
+
items.add(entry2.itemIdentifier);
|
|
35612
|
+
previousItemIndex = itemIndex;
|
|
35613
|
+
Object.assign(responses, itemResponses);
|
|
35614
|
+
}
|
|
35615
|
+
return { transcript, responses };
|
|
35616
|
+
}
|
|
35617
|
+
function completeAssessmentTranscript(input) {
|
|
35618
|
+
const itemSubmissions = [];
|
|
35619
|
+
let state;
|
|
35620
|
+
const ledger = [];
|
|
35621
|
+
if (input.manifest) {
|
|
35622
|
+
const initial = initializeDiagnosticRouting(input.manifest);
|
|
35623
|
+
if (!initial.ok) {
|
|
35624
|
+
throw new AssessmentTranscriptError("The pinned diagnostic routing manifest is invalid");
|
|
35625
|
+
}
|
|
35626
|
+
state = initial.state;
|
|
35627
|
+
}
|
|
35628
|
+
const scores = new Map(input.scores.map((score) => [score.itemIdentifier, score]));
|
|
35629
|
+
for (const [index, entry2] of input.transcript.administrations.entries()) {
|
|
35630
|
+
const scoring = scores.get(entry2.itemIdentifier);
|
|
35631
|
+
if (!scoring) {
|
|
35632
|
+
throw new AssessmentTranscriptError("Transcript item has no trusted grade", index);
|
|
35633
|
+
}
|
|
35634
|
+
if (input.manifest) {
|
|
35635
|
+
if (typeof scoring.isCorrect !== "boolean" || !entry2.routingNodeKey) {
|
|
35636
|
+
throw new AssessmentTranscriptError("Diagnostic grading must be determinate", index);
|
|
35637
|
+
}
|
|
35638
|
+
const advanced = advanceDiagnosticRouting(input.manifest, ledger, {
|
|
35639
|
+
routingNodeKey: entry2.routingNodeKey,
|
|
35640
|
+
itemIdentifier: entry2.itemIdentifier,
|
|
35641
|
+
isCorrect: scoring.isCorrect
|
|
35642
|
+
});
|
|
35643
|
+
if (!advanced.ok) {
|
|
35644
|
+
throw new AssessmentTranscriptError("Transcript does not follow the pinned diagnostic route", index);
|
|
35645
|
+
}
|
|
35646
|
+
ledger.push(advanced.ledgerEntry);
|
|
35647
|
+
state = advanced.state;
|
|
35648
|
+
}
|
|
35649
|
+
itemSubmissions.push({
|
|
35650
|
+
submissionId: entry2.submissionId,
|
|
35651
|
+
itemIdentifier: entry2.itemIdentifier,
|
|
35652
|
+
responseVersion: index + 1,
|
|
35653
|
+
submittedAt: input.submittedAt,
|
|
35654
|
+
answered: Object.keys(entry2.responses).length > 0,
|
|
35655
|
+
score: scoring.score,
|
|
35656
|
+
isCorrect: scoring.isCorrect,
|
|
35657
|
+
...scoring.grading ? { grading: scoring.grading } : {}
|
|
35658
|
+
});
|
|
35659
|
+
}
|
|
35660
|
+
if (input.terminal) {
|
|
35661
|
+
const count = itemSubmissions.length;
|
|
35662
|
+
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount;
|
|
35663
|
+
const complete = state ? state.status === "ready-to-complete" && count > 0 : conventionalComplete;
|
|
35664
|
+
if (!complete) {
|
|
35665
|
+
throw new AssessmentTranscriptError("A complete administered transcript is required for submission", count);
|
|
35666
|
+
}
|
|
35667
|
+
}
|
|
35668
|
+
return { itemSubmissions, ledger, state };
|
|
35670
35669
|
}
|
|
35671
|
-
function
|
|
35672
|
-
|
|
35673
|
-
const committedUpdate = Object.keys(update).find((identifier) => administered.has(identifier));
|
|
35674
|
-
return committedUpdate ? assessmentFlowViolation("A submitted item response cannot be edited.", {
|
|
35675
|
-
itemIdentifier: committedUpdate,
|
|
35676
|
-
flow: assessmentFlowForPurpose(purpose)
|
|
35677
|
-
}) : null;
|
|
35670
|
+
function administeredItemIdentifiers(itemSubmissions) {
|
|
35671
|
+
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
35678
35672
|
}
|
|
35679
|
-
function
|
|
35680
|
-
|
|
35681
|
-
if (flow !== "item-submit") {
|
|
35682
|
-
return null;
|
|
35683
|
-
}
|
|
35684
|
-
if (itemSubmissions.length > 0 && itemSubmissions.length <= itemCount) {
|
|
35685
|
-
return null;
|
|
35686
|
-
}
|
|
35687
|
-
return assessmentFlowViolation("At least one candidate must be administered before the review can be completed.", {
|
|
35688
|
-
flow,
|
|
35689
|
-
submittedItemCount: itemSubmissions.length,
|
|
35690
|
-
itemCount
|
|
35691
|
-
});
|
|
35673
|
+
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
35674
|
+
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
35692
35675
|
}
|
|
35693
35676
|
function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
|
|
35694
35677
|
if (candidates.length === 0) {
|
|
@@ -36021,9 +36004,6 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
36021
36004
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
36022
36005
|
return deterministicUUID(["playcademy:review-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
36023
36006
|
}
|
|
36024
|
-
function reviewQuestionResultId(parentAttemptId, childQuestionLineItemId) {
|
|
36025
|
-
return deterministicUUID(["playcademy:review-question-result:v1", parentAttemptId, childQuestionLineItemId].join("\x00"));
|
|
36026
|
-
}
|
|
36027
36007
|
function qtiQuestionByIdentifier(questions) {
|
|
36028
36008
|
const byIdentifier = new Map;
|
|
36029
36009
|
for (const entry2 of questions.questions) {
|
|
@@ -36583,11 +36563,8 @@ function isDiagnosticRoutingState(value) {
|
|
|
36583
36563
|
const nextValid = value.next === null || isRecord(value.next) && isNonEmptyString(value.next.stageKey) && isNonEmptyString(value.next.trackKey) && isNonEmptyString(value.next.nodeKey) && isNonEmptyString(value.next.itemIdentifier);
|
|
36584
36564
|
return stagesValid && tracksValid && nextValid && (value.status === "in-progress" && value.next !== null || value.status === "ready-to-complete" && value.next === null);
|
|
36585
36565
|
}
|
|
36586
|
-
function isDiagnosticRoutingSnapshot(value, revision) {
|
|
36587
|
-
return isRecord(value) && value.kind === "platform-routed-diagnostic" && value.revision === revision && (value.status === "in-progress" || value.status === "ready-to-complete") && (value.next === null || isRecord(value.next) && isNonEmptyString(value.next.stageKey) && isNonEmptyString(value.next.trackKey) && isNonEmptyString(value.next.nodeKey) && isNonEmptyString(value.next.itemIdentifier)) && Array.isArray(value.tracks) && value.tracks.every((track) => isRecord(track) && isNonEmptyString(track.trackKey) && (track.groupKey === undefined || isNonEmptyString(track.groupKey)) && ["pending", "active", "completed", "skipped"].includes(String(track.status)) && Number.isInteger(track.administeredCount) && Number(track.administeredCount) >= 0);
|
|
36588
|
-
}
|
|
36589
36566
|
function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersion) {
|
|
36590
|
-
if (!isRecord(value) || !isNonEmptyString(value.definitionId) || !isNonEmptyString(value.diagnosticKey) || !isNonEmptyString(value.testName) || !isNonEmptyString(value.routingRevision) || !Array.isArray(value.ledger) || !value.ledger.every(isDiagnosticRoutingLedgerEntry) ||
|
|
36567
|
+
if (!isRecord(value) || !isNonEmptyString(value.definitionId) || !isNonEmptyString(value.diagnosticKey) || !isNonEmptyString(value.testName) || !isNonEmptyString(value.routingRevision) || !Array.isArray(value.ledger) || !value.ledger.every(isDiagnosticRoutingLedgerEntry) || !isDiagnosticRoutingState(value.state) || value.ledger.length !== itemSubmissions.length || responseVersion !== itemSubmissions.length) {
|
|
36591
36568
|
return false;
|
|
36592
36569
|
}
|
|
36593
36570
|
const itemIdentifiers = new Set;
|
|
@@ -36641,19 +36618,7 @@ function isPlaycademyAssessmentResultMetadataV1(value) {
|
|
|
36641
36618
|
const review = metadata2.review;
|
|
36642
36619
|
const diagnostic = metadata2.diagnostic;
|
|
36643
36620
|
const routedDiagnostic = metadata2.purpose === "diagnostic" && diagnostic !== undefined && Array.isArray(metadata2.itemSubmissions) && Number.isInteger(metadata2.responseVersion) && isRoutedDiagnosticAttemptMetadata(diagnostic, metadata2.itemSubmissions, metadata2.responseVersion);
|
|
36644
|
-
return metadata2.version === 1 && typeof metadata2.activityId === "string" && isAssessmentPurpose(metadata2.purpose) && typeof metadata2.courseId === "string" && typeof metadata2.integrationId === "string" && typeof metadata2.enrollmentId === "string" && Boolean(selectedTest) && (selectedTest?.assessmentKey === undefined || isNonEmptyString(selectedTest.assessmentKey)) && typeof selectedTest?.identifier === "string" && typeof selectedTest.contentRevision === "string" && Number.isInteger(metadata2.attemptNumber) && Number.isInteger(metadata2.responseVersion) && Boolean(metadata2.responses) && typeof metadata2.responses === "object" && Array.isArray(metadata2.itemSubmissions) && metadata2.itemSubmissions.every(isAssessmentItemSubmission) && typeof metadata2.startedAt === "string" && typeof metadata2.updatedAt === "string" && (metadata2.finalizedAt === undefined || typeof metadata2.finalizedAt === "string") && isAssessmentAwardRecord(metadata2.award) && (metadata2.purpose === "mastery" ? isMasteryAttemptMetadata(mastery) : mastery === undefined) && (metadata2.purpose === "review" ? isReviewAttemptMetadata(review) : review === undefined) && (metadata2.purpose === "diagnostic" ? diagnostic === undefined || routedDiagnostic : diagnostic === undefined) && (metadata2.submissionId === undefined || typeof metadata2.submissionId === "string") && (metadata2.score === undefined || isAssessmentScore(metadata2.score)) && isCompletionMetadata(metadata2.completion, metadata2.purpose, routedDiagnostic);
|
|
36645
|
-
}
|
|
36646
|
-
function isPlaycademyReviewAssessmentItemResultMetadataV1(value) {
|
|
36647
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
36648
|
-
return false;
|
|
36649
|
-
}
|
|
36650
|
-
return value.version === 1 && typeof value.parentAttemptId === "string" && typeof value.activityId === "string" && typeof value.integrationId === "string" && typeof value.enrollmentId === "string" && typeof value.bankRevision === "string" && typeof value.itemIdentifier === "string" && isAssessmentStandardRef(value.standard) && typeof value.deliveredAt === "string" && Number.isInteger(value.responseVersion) && Object.values(value.responses).every(isAssessmentResponseValue) && (value.submissionId === undefined || typeof value.submissionId === "string") && (value.isCorrect === undefined || value.isCorrect === null || typeof value.isCorrect === "boolean");
|
|
36651
|
-
}
|
|
36652
|
-
function isPlaycademyDiagnosticAssessmentItemResultMetadataV1(value) {
|
|
36653
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
36654
|
-
return false;
|
|
36655
|
-
}
|
|
36656
|
-
return value.version === 1 && isNonEmptyString(value.parentAttemptId) && isNonEmptyString(value.activityId) && isNonEmptyString(value.integrationId) && isNonEmptyString(value.enrollmentId) && isNonEmptyString(value.definitionId) && isNonEmptyString(value.diagnosticKey) && isNonEmptyString(value.routingRevision) && isNonEmptyString(value.contentRevision) && isNonEmptyString(value.stageKey) && isNonEmptyString(value.trackKey) && isNonEmptyString(value.routingNodeKey) && isNonEmptyString(value.itemIdentifier) && isNonEmptyString(value.deliveredAt) && Number.isInteger(value.responseVersion) && Number(value.responseVersion) > 0 && Object.values(value.responses).every(isAssessmentResponseValue) && isNonEmptyString(value.submissionId) && typeof value.isCorrect === "boolean";
|
|
36621
|
+
return metadata2.version === 1 && typeof metadata2.activityId === "string" && isAssessmentPurpose(metadata2.purpose) && typeof metadata2.courseId === "string" && typeof metadata2.integrationId === "string" && typeof metadata2.enrollmentId === "string" && Boolean(selectedTest) && (selectedTest?.assessmentKey === undefined || isNonEmptyString(selectedTest.assessmentKey)) && typeof selectedTest?.identifier === "string" && typeof selectedTest.contentRevision === "string" && Number.isInteger(metadata2.attemptNumber) && Number.isInteger(metadata2.responseVersion) && Boolean(metadata2.responses) && typeof metadata2.responses === "object" && Array.isArray(metadata2.itemSubmissions) && metadata2.itemSubmissions.every(isAssessmentItemSubmission) && typeof metadata2.startedAt === "string" && typeof metadata2.updatedAt === "string" && (metadata2.finalizedAt === undefined || typeof metadata2.finalizedAt === "string") && isAssessmentAwardRecord(metadata2.award) && (metadata2.purpose === "mastery" ? isMasteryAttemptMetadata(mastery) : mastery === undefined) && (metadata2.purpose === "review" ? isReviewAttemptMetadata(review) : review === undefined) && (metadata2.purpose === "diagnostic" ? diagnostic === undefined || routedDiagnostic : diagnostic === undefined) && (metadata2.submissionId === undefined || typeof metadata2.submissionId === "string") && (metadata2.score === undefined || isAssessmentScore(metadata2.score)) && (metadata2.transcript === undefined || AssessmentTranscriptSchema.safeParse(metadata2.transcript).success) && isCompletionMetadata(metadata2.completion, metadata2.purpose, routedDiagnostic);
|
|
36657
36622
|
}
|
|
36658
36623
|
function playcademyAssessmentResultMetadata(value) {
|
|
36659
36624
|
if (!isRecord(value)) {
|
|
@@ -36673,12 +36638,6 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
36673
36638
|
diagnostic: { ...normalized.diagnostic, ledger: [] }
|
|
36674
36639
|
};
|
|
36675
36640
|
}
|
|
36676
|
-
if (isRecord(normalized.diagnostic) && Array.isArray(normalized.diagnostic.ledger) && normalized.diagnostic.ledger.length === 0 && normalized.diagnostic.routingSnapshots === undefined) {
|
|
36677
|
-
normalized = {
|
|
36678
|
-
...normalized,
|
|
36679
|
-
diagnostic: { ...normalized.diagnostic, routingSnapshots: [] }
|
|
36680
|
-
};
|
|
36681
|
-
}
|
|
36682
36641
|
if (isRecord(normalized.diagnostic) && isRecord(normalized.diagnostic.state)) {
|
|
36683
36642
|
const state = normalized.diagnostic.state;
|
|
36684
36643
|
const tracks = Array.isArray(state.tracks) ? state.tracks.map((track) => isRecord(track) ? {
|
|
@@ -36686,12 +36645,10 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
36686
36645
|
currentNodeKey: track.currentNodeKey ?? null,
|
|
36687
36646
|
terminal: track.terminal ?? null
|
|
36688
36647
|
} : track) : state.tracks;
|
|
36689
|
-
const routingSnapshots = Array.isArray(normalized.diagnostic.routingSnapshots) ? normalized.diagnostic.routingSnapshots.map((snapshot) => isRecord(snapshot) ? { ...snapshot, next: snapshot.next ?? null } : snapshot) : normalized.diagnostic.routingSnapshots;
|
|
36690
36648
|
normalized = {
|
|
36691
36649
|
...normalized,
|
|
36692
36650
|
diagnostic: {
|
|
36693
36651
|
...normalized.diagnostic,
|
|
36694
|
-
...routingSnapshots === undefined ? {} : { routingSnapshots },
|
|
36695
36652
|
state: {
|
|
36696
36653
|
...state,
|
|
36697
36654
|
...tracks === undefined ? {} : { tracks },
|
|
@@ -36701,6 +36658,16 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
36701
36658
|
};
|
|
36702
36659
|
}
|
|
36703
36660
|
const review = normalized.review;
|
|
36661
|
+
if (isRecord(normalized.transcript)) {
|
|
36662
|
+
const transcript = normalized.transcript;
|
|
36663
|
+
normalized = {
|
|
36664
|
+
...normalized,
|
|
36665
|
+
transcript: {
|
|
36666
|
+
...transcript,
|
|
36667
|
+
administrations: Array.isArray(transcript.administrations) ? transcript.administrations.map((entry2) => isRecord(entry2) ? { ...entry2, responses: entry2.responses ?? {} } : entry2) : transcript.administrations ?? []
|
|
36668
|
+
}
|
|
36669
|
+
};
|
|
36670
|
+
}
|
|
36704
36671
|
if (isRecord(review) && isRecord(review.fulfillment) && review.fulfillment.status === "complete" && review.fulfillment.shortages === undefined) {
|
|
36705
36672
|
normalized = {
|
|
36706
36673
|
...normalized,
|
|
@@ -36715,28 +36682,6 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
36715
36682
|
}
|
|
36716
36683
|
return isPlaycademyAssessmentResultMetadataV1(normalized) ? normalized : null;
|
|
36717
36684
|
}
|
|
36718
|
-
function playcademyAssessmentItemResultMetadata(value) {
|
|
36719
|
-
if (!isRecord(value)) {
|
|
36720
|
-
return null;
|
|
36721
|
-
}
|
|
36722
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
36723
|
-
if (!isRecord(metadata2)) {
|
|
36724
|
-
return null;
|
|
36725
|
-
}
|
|
36726
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
36727
|
-
return isPlaycademyReviewAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
36728
|
-
}
|
|
36729
|
-
function playcademyDiagnosticAssessmentItemResultMetadata(value) {
|
|
36730
|
-
if (!isRecord(value)) {
|
|
36731
|
-
return null;
|
|
36732
|
-
}
|
|
36733
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
36734
|
-
if (!isRecord(metadata2)) {
|
|
36735
|
-
return null;
|
|
36736
|
-
}
|
|
36737
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
36738
|
-
return isPlaycademyDiagnosticAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
36739
|
-
}
|
|
36740
36685
|
var SCRIPT_SCHEMES;
|
|
36741
36686
|
var DATA_RASTER_IMAGE_PATTERN;
|
|
36742
36687
|
var QTI_MATHML_ALLOWED_TAGS;
|
|
@@ -36794,6 +36739,7 @@ var ASSESSMENT_ATTEMPT_COMPLETED;
|
|
|
36794
36739
|
var ASSESSMENT_ATTEMPT_AWAITING_AWARD;
|
|
36795
36740
|
var ASSESSMENT_ATTEMPT_SUPERSEDED;
|
|
36796
36741
|
var ASSESSMENT_RUNTIME_ERROR_STATUS;
|
|
36742
|
+
var GRADE_VALUES;
|
|
36797
36743
|
var ROUTING_KEY_MAX_LENGTH = 128;
|
|
36798
36744
|
var ROUTING_RESULT_KEY_MAX_LENGTH = 256;
|
|
36799
36745
|
var ROUTING_STAGE_LIMIT = 32;
|
|
@@ -36809,8 +36755,10 @@ var DiagnosticTrackOutcomeSchema;
|
|
|
36809
36755
|
var DiagnosticActivationPredicateSchema;
|
|
36810
36756
|
var DiagnosticRoutingTransitionSchema;
|
|
36811
36757
|
var DiagnosticRoutingManifestV1Schema;
|
|
36812
|
-
var GRADE_VALUES;
|
|
36813
36758
|
var POINT_RESPONSE_PATTERN;
|
|
36759
|
+
var Identity;
|
|
36760
|
+
var AssessmentTranscriptSchema;
|
|
36761
|
+
var AssessmentTranscriptError;
|
|
36814
36762
|
var ZERO_WIDTH_CHARACTERS;
|
|
36815
36763
|
var CURLY_SINGLE_QUOTES;
|
|
36816
36764
|
var CURLY_DOUBLE_QUOTES;
|
|
@@ -36836,9 +36784,11 @@ var AssessmentChildResultAttemptV1Schema;
|
|
|
36836
36784
|
var AssessmentChildResultAdministrationV1Schema;
|
|
36837
36785
|
var AssessmentChildResultGradingV1Schema;
|
|
36838
36786
|
var ReviewChildResultContextV1Schema;
|
|
36787
|
+
var FixedChildResultContextV1Schema;
|
|
36839
36788
|
var DiagnosticChildResultContextV1Schema;
|
|
36840
36789
|
var CommonPayloadShape;
|
|
36841
36790
|
var ReviewChildResultPayloadV1Schema;
|
|
36791
|
+
var FixedChildResultPayloadV1Schema;
|
|
36842
36792
|
var DiagnosticChildResultPayloadV1Schema;
|
|
36843
36793
|
var AssessmentChildResultPayloadV1Schema;
|
|
36844
36794
|
var AssessmentChildResultCommandV1Schema;
|
|
@@ -36857,12 +36807,16 @@ var StartAssessmentBodySchema;
|
|
|
36857
36807
|
var AssessmentPreparationReceiptSchema;
|
|
36858
36808
|
var StartAssessmentRequestBodySchema;
|
|
36859
36809
|
var SaveAssessmentBodySchema;
|
|
36810
|
+
var RecordAssessmentProgressBodySchema;
|
|
36811
|
+
var ResumeAssessmentBodySchema;
|
|
36812
|
+
var ResumeRuntimeAssessmentRequestSchema;
|
|
36860
36813
|
var SubmitAssessmentItemBodySchema;
|
|
36861
36814
|
var SubmitAssessmentBodySchema;
|
|
36862
36815
|
var FinalizeAssessmentBodySchema;
|
|
36863
36816
|
var PrepareRuntimeAssessmentRequestSchema;
|
|
36864
36817
|
var StartRuntimeAssessmentRequestSchema;
|
|
36865
36818
|
var SaveRuntimeAssessmentRequestSchema;
|
|
36819
|
+
var RecordRuntimeAssessmentProgressRequestSchema;
|
|
36866
36820
|
var SubmitAssessmentItemRuntimeRequestSchema;
|
|
36867
36821
|
var SubmitRuntimeAssessmentRequestSchema;
|
|
36868
36822
|
var FinalizeRuntimeAssessmentRequestSchema;
|
|
@@ -36875,13 +36829,14 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
36875
36829
|
init_timeback3();
|
|
36876
36830
|
init_esm();
|
|
36877
36831
|
init_esm();
|
|
36832
|
+
init_src();
|
|
36833
|
+
init_uuid();
|
|
36834
|
+
init_esm();
|
|
36878
36835
|
init_timeback4();
|
|
36879
36836
|
init_src();
|
|
36880
36837
|
init_timeback3();
|
|
36881
36838
|
init_src();
|
|
36882
36839
|
init_uuid();
|
|
36883
|
-
init_src();
|
|
36884
|
-
init_uuid();
|
|
36885
36840
|
init_esm();
|
|
36886
36841
|
init_src();
|
|
36887
36842
|
init_uuid();
|
|
@@ -37177,6 +37132,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37177
37132
|
ATTEMPT_ALREADY_SUBMITTED: 409,
|
|
37178
37133
|
ASSESSMENT_FLOW_VIOLATION: 409
|
|
37179
37134
|
};
|
|
37135
|
+
GRADE_VALUES = TIMEBACK_GRADES;
|
|
37180
37136
|
RoutingKeySchema = exports_external.string().trim().min(1).max(ROUTING_KEY_MAX_LENGTH);
|
|
37181
37137
|
RoutingResultKeySchema = exports_external.string().trim().min(1).max(ROUTING_RESULT_KEY_MAX_LENGTH);
|
|
37182
37138
|
DiagnosticTrackOutcomeSchema = exports_external.enum(DIAGNOSTIC_TRACK_OUTCOMES);
|
|
@@ -37234,8 +37190,31 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37234
37190
|
resultKey: RoutingResultKeySchema
|
|
37235
37191
|
}).strict()).min(1).max(ROUTING_TERMINAL_LIMIT)
|
|
37236
37192
|
}).strict();
|
|
37237
|
-
GRADE_VALUES = TIMEBACK_GRADES;
|
|
37238
37193
|
POINT_RESPONSE_PATTERN = /^-?\d+(?:\.\d+)? -?\d+(?:\.\d+)?$/;
|
|
37194
|
+
Identity = exports_external.string().min(1).max(512).refine((value) => value.trim() === value);
|
|
37195
|
+
AssessmentTranscriptSchema = exports_external.object({
|
|
37196
|
+
version: exports_external.literal(1),
|
|
37197
|
+
attemptId: Identity,
|
|
37198
|
+
contentRevision: Identity,
|
|
37199
|
+
administrations: exports_external.array(exports_external.object({
|
|
37200
|
+
submissionId: Identity,
|
|
37201
|
+
itemIdentifier: Identity,
|
|
37202
|
+
routingNodeKey: Identity.optional(),
|
|
37203
|
+
responses: exports_external.record(Identity, exports_external.custom(isAssessmentResponseValue))
|
|
37204
|
+
}).strict()).max(1000)
|
|
37205
|
+
}).strict().refine((value) => new TextEncoder().encode(JSON.stringify(value)).length <= 256000, {
|
|
37206
|
+
message: "Assessment transcript exceeds 256 KB"
|
|
37207
|
+
});
|
|
37208
|
+
AssessmentTranscriptError = class AssessmentTranscriptError2 extends Error {
|
|
37209
|
+
index;
|
|
37210
|
+
code;
|
|
37211
|
+
constructor(message, index = 0, code = "ASSESSMENT_FLOW_VIOLATION") {
|
|
37212
|
+
super(message);
|
|
37213
|
+
this.name = "AssessmentTranscriptError";
|
|
37214
|
+
this.index = index;
|
|
37215
|
+
this.code = code;
|
|
37216
|
+
}
|
|
37217
|
+
};
|
|
37239
37218
|
ZERO_WIDTH_CHARACTERS = /[\u200B\uFEFF\u200C\u200D]/g;
|
|
37240
37219
|
CURLY_SINGLE_QUOTES = /[\u2018\u2019\u201A\u201B]/g;
|
|
37241
37220
|
CURLY_DOUBLE_QUOTES = /[\u201C\u201D\u201E\u201F]/g;
|
|
@@ -37327,6 +37306,10 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37327
37306
|
bankRevision: IdentitySchema,
|
|
37328
37307
|
standard: AssessmentStandardRefSchema
|
|
37329
37308
|
}).strict();
|
|
37309
|
+
FixedChildResultContextV1Schema = exports_external.object({
|
|
37310
|
+
kind: exports_external.literal("fixed"),
|
|
37311
|
+
purpose: exports_external.enum(["mastery", "end_of_course", "diagnostic"])
|
|
37312
|
+
}).strict();
|
|
37330
37313
|
DiagnosticChildResultContextV1Schema = exports_external.object({
|
|
37331
37314
|
kind: exports_external.literal("platform-routed-diagnostic"),
|
|
37332
37315
|
definitionId: IdentitySchema,
|
|
@@ -37346,6 +37329,11 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37346
37329
|
isCorrect: exports_external.boolean().optional(),
|
|
37347
37330
|
context: ReviewChildResultContextV1Schema
|
|
37348
37331
|
}).strict();
|
|
37332
|
+
FixedChildResultPayloadV1Schema = exports_external.object({
|
|
37333
|
+
...CommonPayloadShape,
|
|
37334
|
+
isCorrect: exports_external.boolean().optional(),
|
|
37335
|
+
context: FixedChildResultContextV1Schema
|
|
37336
|
+
}).strict();
|
|
37349
37337
|
DiagnosticChildResultPayloadV1Schema = exports_external.object({
|
|
37350
37338
|
...CommonPayloadShape,
|
|
37351
37339
|
isCorrect: exports_external.boolean(),
|
|
@@ -37361,7 +37349,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37361
37349
|
});
|
|
37362
37350
|
AssessmentChildResultPayloadV1Schema = exports_external.union([
|
|
37363
37351
|
ReviewChildResultPayloadV1Schema,
|
|
37364
|
-
DiagnosticChildResultPayloadV1Schema
|
|
37352
|
+
DiagnosticChildResultPayloadV1Schema,
|
|
37353
|
+
FixedChildResultPayloadV1Schema
|
|
37365
37354
|
]);
|
|
37366
37355
|
AssessmentChildResultCommandV1Schema = exports_external.object({
|
|
37367
37356
|
kind: exports_external.literal(ASSESSMENT_CHILD_RESULT_KIND),
|
|
@@ -37438,9 +37427,19 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37438
37427
|
StartAssessmentRequestBodySchema = exports_external.intersection(StartAssessmentBodySchema, exports_external.object({ preparationReceipt: AssessmentPreparationReceiptSchema.optional() }));
|
|
37439
37428
|
SaveAssessmentBodySchema = exports_external.object({
|
|
37440
37429
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
37441
|
-
|
|
37430
|
+
transcript: AssessmentTranscriptSchema
|
|
37431
|
+
});
|
|
37432
|
+
RecordAssessmentProgressBodySchema = SaveAssessmentBodySchema.extend({
|
|
37433
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
37434
|
+
itemIdentifier: ResponseKeySchema2
|
|
37435
|
+
});
|
|
37436
|
+
ResumeAssessmentBodySchema = exports_external.object({
|
|
37437
|
+
transcript: AssessmentTranscriptSchema.optional()
|
|
37442
37438
|
});
|
|
37439
|
+
ResumeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(ResumeAssessmentBodySchema.shape);
|
|
37443
37440
|
SubmitAssessmentItemBodySchema = exports_external.object({
|
|
37441
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
37442
|
+
transcript: AssessmentTranscriptSchema,
|
|
37444
37443
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
37445
37444
|
submissionId: exports_external.string().trim().min(1),
|
|
37446
37445
|
itemIdentifier: ResponseKeySchema2,
|
|
@@ -37448,6 +37447,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37448
37447
|
responses: exports_external.record(ResponseKeySchema2, AssessmentResponseValueSchema2.nullable())
|
|
37449
37448
|
});
|
|
37450
37449
|
SubmitAssessmentBodySchema = exports_external.object({
|
|
37450
|
+
transcript: AssessmentTranscriptSchema,
|
|
37451
37451
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
37452
37452
|
submissionId: exports_external.string().trim().min(1),
|
|
37453
37453
|
session: exports_external.custom(isAssessmentSessionTiming, {
|
|
@@ -37466,6 +37466,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
37466
37466
|
PrepareRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
|
|
37467
37467
|
StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentRequestBodySchema);
|
|
37468
37468
|
SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
|
|
37469
|
+
RecordRuntimeAssessmentProgressRequestSchema = AssessmentRuntimeIdentitySchema.extend(RecordAssessmentProgressBodySchema.shape);
|
|
37469
37470
|
SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
|
|
37470
37471
|
SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentBodySchema.shape).extend({
|
|
37471
37472
|
appName: exports_external.string().trim().min(1),
|
|
@@ -126102,22 +126103,6 @@ var init_locks = __esm(() => {
|
|
|
126102
126103
|
}
|
|
126103
126104
|
};
|
|
126104
126105
|
});
|
|
126105
|
-
async function crossAssessmentAttemptLockBarrier(lock, db2, attemptId) {
|
|
126106
|
-
const maxAttempts = 5;
|
|
126107
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
|
|
126108
|
-
try {
|
|
126109
|
-
await lock(db2, [assessmentRuntimeAttemptLockKey(attemptId)], async () => {
|
|
126110
|
-
return;
|
|
126111
|
-
});
|
|
126112
|
-
return;
|
|
126113
|
-
} catch (error88) {
|
|
126114
|
-
if (!(error88 instanceof ServiceUnavailableError) || attempt === maxAttempts) {
|
|
126115
|
-
throw error88;
|
|
126116
|
-
}
|
|
126117
|
-
await sleep(25);
|
|
126118
|
-
}
|
|
126119
|
-
}
|
|
126120
|
-
}
|
|
126121
126106
|
function normalizeKeys(keys) {
|
|
126122
126107
|
return [...new Set(keys)].toSorted();
|
|
126123
126108
|
}
|
|
@@ -126191,17 +126176,6 @@ var init_assessment_runtime_lock_util = __esm(() => {
|
|
|
126191
126176
|
}
|
|
126192
126177
|
};
|
|
126193
126178
|
});
|
|
126194
|
-
function assessmentPreparationContentionError(expectedResponseVersion, responseVersion, kind) {
|
|
126195
|
-
if (expectedResponseVersion === responseVersion) {
|
|
126196
|
-
return new ServiceUnavailableError(`The ${kind} response state changed repeatedly while this item was being prepared. Try again shortly.`, {
|
|
126197
|
-
retryable: true,
|
|
126198
|
-
reason: "PREPARATION_CONTENTION",
|
|
126199
|
-
expectedResponseVersion,
|
|
126200
|
-
responseVersion
|
|
126201
|
-
});
|
|
126202
|
-
}
|
|
126203
|
-
return AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, responseVersion));
|
|
126204
|
-
}
|
|
126205
126179
|
function assessmentTrackableCourse(integration) {
|
|
126206
126180
|
if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
|
|
126207
126181
|
return null;
|
|
@@ -126267,7 +126241,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
126267
126241
|
...input.activityData ? { activityData: input.activityData } : {},
|
|
126268
126242
|
assessment: assessmentPresentationForAttempt(input.assessment, input.attemptId),
|
|
126269
126243
|
responses: metadata2.responses,
|
|
126270
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
126244
|
+
itemSubmissions: assessmentFlowForPurpose(metadata2.purpose) === "item-submit" ? metadata2.itemSubmissions : [],
|
|
126271
126245
|
score: null,
|
|
126272
126246
|
selection
|
|
126273
126247
|
};
|
|
@@ -126317,151 +126291,9 @@ async function resolveAvailableAssessmentAttempt(initialDecision, select3, resum
|
|
|
126317
126291
|
}
|
|
126318
126292
|
return { kind: "continue", decision };
|
|
126319
126293
|
}
|
|
126320
|
-
function buildReviewItemResultMetadata(input) {
|
|
126321
|
-
const administration = itemAdministration(input.metadata.itemSubmissions, input.selection.itemIdentifier);
|
|
126322
|
-
if (!administration) {
|
|
126323
|
-
throw new Error(`Cannot create a review result for unadministered candidate ${input.selection.itemIdentifier}`);
|
|
126324
|
-
}
|
|
126325
|
-
return {
|
|
126326
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
126327
|
-
parentAttemptId: input.parentAttemptId,
|
|
126328
|
-
activityId: input.metadata.activityId,
|
|
126329
|
-
integrationId: input.metadata.integrationId,
|
|
126330
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
126331
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
126332
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
126333
|
-
standard: input.selection.standard,
|
|
126334
|
-
deliveredAt: administration.submittedAt,
|
|
126335
|
-
responseVersion: input.responseVersion ?? input.metadata.responseVersion,
|
|
126336
|
-
responses: input.metadata.responses[input.selection.itemIdentifier] ?? {},
|
|
126337
|
-
...input.submissionId ? { submissionId: input.submissionId } : {},
|
|
126338
|
-
...input.isCorrect === undefined ? {} : { isCorrect: input.isCorrect }
|
|
126339
|
-
};
|
|
126340
|
-
}
|
|
126341
|
-
function buildOpenReviewChildResult(input) {
|
|
126342
|
-
return {
|
|
126343
|
-
status: "active",
|
|
126344
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
126345
|
-
student: input.student,
|
|
126346
|
-
score: 0,
|
|
126347
|
-
scoreDate: input.metadata.deliveredAt,
|
|
126348
|
-
...ASSESSMENT_ATTEMPT_OPEN,
|
|
126349
|
-
metadata: {
|
|
126350
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: input.metadata
|
|
126351
|
-
}
|
|
126352
|
-
};
|
|
126353
|
-
}
|
|
126354
126294
|
function diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
126355
126295
|
return deterministicUUID(["playcademy:diagnostic-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
126356
126296
|
}
|
|
126357
|
-
function diagnosticQuestionResultId(parentAttemptId, childLineItemId) {
|
|
126358
|
-
return deterministicUUID(["playcademy:diagnostic-question-result:v1", parentAttemptId, childLineItemId].join("\x00"));
|
|
126359
|
-
}
|
|
126360
|
-
function diagnosticAdministration(input) {
|
|
126361
|
-
const ledgerIndex = input.metadata.diagnostic.ledger.findIndex((entry2) => entry2.routingNodeKey === input.ledgerEntry.routingNodeKey);
|
|
126362
|
-
const persistedEntry = input.metadata.diagnostic.ledger[ledgerIndex];
|
|
126363
|
-
const administration = input.metadata.itemSubmissions[ledgerIndex];
|
|
126364
|
-
if (ledgerIndex === -1 || !persistedEntry || !administration || persistedEntry.stageKey !== input.ledgerEntry.stageKey || persistedEntry.trackKey !== input.ledgerEntry.trackKey || persistedEntry.itemIdentifier !== input.ledgerEntry.itemIdentifier || persistedEntry.isCorrect !== input.ledgerEntry.isCorrect || administration.itemIdentifier !== input.ledgerEntry.itemIdentifier || administration.isCorrect !== input.ledgerEntry.isCorrect) {
|
|
126365
|
-
throw new Error(`Cannot create a diagnostic result for uncommitted routing node ${input.ledgerEntry.routingNodeKey}`);
|
|
126366
|
-
}
|
|
126367
|
-
return administration;
|
|
126368
|
-
}
|
|
126369
|
-
function buildDiagnosticItemResultMetadata(input) {
|
|
126370
|
-
const administration = diagnosticAdministration(input);
|
|
126371
|
-
return {
|
|
126372
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
126373
|
-
parentAttemptId: input.parentAttemptId,
|
|
126374
|
-
activityId: input.metadata.activityId,
|
|
126375
|
-
integrationId: input.metadata.integrationId,
|
|
126376
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
126377
|
-
definitionId: input.metadata.diagnostic.definitionId,
|
|
126378
|
-
diagnosticKey: input.metadata.diagnostic.diagnosticKey,
|
|
126379
|
-
routingRevision: input.metadata.diagnostic.routingRevision,
|
|
126380
|
-
contentRevision: input.metadata.selectedTest.contentRevision,
|
|
126381
|
-
stageKey: input.ledgerEntry.stageKey,
|
|
126382
|
-
trackKey: input.ledgerEntry.trackKey,
|
|
126383
|
-
routingNodeKey: input.ledgerEntry.routingNodeKey,
|
|
126384
|
-
itemIdentifier: input.ledgerEntry.itemIdentifier,
|
|
126385
|
-
deliveredAt: administration.submittedAt,
|
|
126386
|
-
responseVersion: administration.responseVersion,
|
|
126387
|
-
responses: input.metadata.responses[input.ledgerEntry.itemIdentifier] ?? {},
|
|
126388
|
-
submissionId: administration.submissionId,
|
|
126389
|
-
isCorrect: input.ledgerEntry.isCorrect
|
|
126390
|
-
};
|
|
126391
|
-
}
|
|
126392
|
-
function resolveDiagnosticChildResult(input) {
|
|
126393
|
-
if (!input.childResult) {
|
|
126394
|
-
return { action: "create" };
|
|
126395
|
-
}
|
|
126396
|
-
const childMetadata = playcademyDiagnosticAssessmentItemResultMetadata(input.childResult.metadata);
|
|
126397
|
-
const expectedMetadata = buildDiagnosticItemResultMetadata({
|
|
126398
|
-
metadata: input.parentMetadata,
|
|
126399
|
-
ledgerEntry: input.ledgerEntry,
|
|
126400
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
126401
|
-
});
|
|
126402
|
-
if (input.childResult.assessmentLineItem.sourcedId !== input.childLineItemId || input.childResult.student.sourcedId !== input.parentResult.student.sourcedId || !childMetadata || canonicalJson2(childMetadata) !== canonicalJson2(expectedMetadata)) {
|
|
126403
|
-
return { action: "restore", reason: "incompatible" };
|
|
126404
|
-
}
|
|
126405
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: expectedMetadata } : { action: "restore", reason: "inactive" };
|
|
126406
|
-
}
|
|
126407
|
-
function buildFinalizedDiagnosticChildResult(input) {
|
|
126408
|
-
const administration = diagnosticAdministration(input);
|
|
126409
|
-
const metadata2 = buildDiagnosticItemResultMetadata(input);
|
|
126410
|
-
return {
|
|
126411
|
-
resultUpdate: {
|
|
126412
|
-
status: "active",
|
|
126413
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
126414
|
-
student: input.student,
|
|
126415
|
-
score: administration.score.earned,
|
|
126416
|
-
scoreDate: administration.submittedAt,
|
|
126417
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
126418
|
-
metadata: {
|
|
126419
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
126420
|
-
}
|
|
126421
|
-
}
|
|
126422
|
-
};
|
|
126423
|
-
}
|
|
126424
|
-
function buildFinalizedReviewChildResult(input) {
|
|
126425
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
126426
|
-
metadata: input.metadata,
|
|
126427
|
-
selection: input.selection,
|
|
126428
|
-
parentAttemptId: input.parentAttemptId,
|
|
126429
|
-
submissionId: input.submissionId,
|
|
126430
|
-
isCorrect: input.itemResult.isCorrect
|
|
126431
|
-
});
|
|
126432
|
-
return {
|
|
126433
|
-
resultUpdate: {
|
|
126434
|
-
status: "active",
|
|
126435
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
126436
|
-
student: input.student,
|
|
126437
|
-
score: input.itemResult.score.earned,
|
|
126438
|
-
scoreDate: input.timestamp,
|
|
126439
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
126440
|
-
metadata: {
|
|
126441
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
126442
|
-
}
|
|
126443
|
-
},
|
|
126444
|
-
outcome: {
|
|
126445
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
126446
|
-
standard: input.selection.standard,
|
|
126447
|
-
answered: Object.keys(input.metadata.responses[input.selection.itemIdentifier] ?? {}).length > 0,
|
|
126448
|
-
score: input.itemResult.score,
|
|
126449
|
-
isCorrect: input.itemResult.isCorrect
|
|
126450
|
-
}
|
|
126451
|
-
};
|
|
126452
|
-
}
|
|
126453
|
-
function resolveReviewChildResult(input) {
|
|
126454
|
-
if (!input.childResult) {
|
|
126455
|
-
return { action: "create" };
|
|
126456
|
-
}
|
|
126457
|
-
const childMetadata = playcademyAssessmentItemResultMetadata(input.childResult.metadata);
|
|
126458
|
-
const expectedResponses = input.parentMetadata.responses[input.selection.itemIdentifier] ?? {};
|
|
126459
|
-
const administration = itemAdministration(input.parentMetadata.itemSubmissions, input.selection.itemIdentifier);
|
|
126460
|
-
if (input.childResult.assessmentLineItem.sourcedId !== input.childLineItemId || input.childResult.student.sourcedId !== input.parentResult.student.sourcedId || !childMetadata || childMetadata.parentAttemptId !== input.parentResult.sourcedId || childMetadata.activityId !== input.parentMetadata.activityId || childMetadata.integrationId !== input.parentMetadata.integrationId || childMetadata.enrollmentId !== input.parentMetadata.enrollmentId || childMetadata.bankRevision !== input.parentMetadata.review.bankRevision || childMetadata.itemIdentifier !== input.selection.itemIdentifier || childMetadata.standard.framework !== input.selection.standard.framework || childMetadata.standard.identifier !== input.selection.standard.identifier || childMetadata.deliveredAt !== administration?.submittedAt || childMetadata.responseVersion > input.parentMetadata.responseVersion || canonicalJson2(childMetadata.responses) !== canonicalJson2(expectedResponses)) {
|
|
126461
|
-
return { action: "restore", reason: "incompatible" };
|
|
126462
|
-
}
|
|
126463
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: childMetadata } : { action: "restore", reason: "inactive" };
|
|
126464
|
-
}
|
|
126465
126297
|
function buildAssessmentSubmitResult(input) {
|
|
126466
126298
|
if (!input.metadata.score || !input.metadata.submissionId) {
|
|
126467
126299
|
return null;
|
|
@@ -126847,32 +126679,12 @@ function assessmentFixtureScoringKeys(assessment, questions) {
|
|
|
126847
126679
|
...Object.keys(responseAreas).length > 0 ? { responseAreas } : {}
|
|
126848
126680
|
};
|
|
126849
126681
|
}
|
|
126850
|
-
function prepareDiagnosticAssessmentResponses(assessment, current, input) {
|
|
126851
|
-
const update2 = { [input.itemIdentifier]: input.responses };
|
|
126852
|
-
validateAssessmentResponseUpdate(assessment, update2);
|
|
126853
|
-
const responses = applyAssessmentResponseUpdate(current, update2);
|
|
126854
|
-
const itemResponses = responses[input.itemIdentifier];
|
|
126855
|
-
validateAssessmentDraftResponses(assessment, itemResponses ? { [input.itemIdentifier]: itemResponses } : {});
|
|
126856
|
-
return responses;
|
|
126857
|
-
}
|
|
126858
126682
|
function validateAssessmentResponseUpdate(assessment, update2) {
|
|
126859
126683
|
const message = assessmentResponseUpdateValidationMessage(assessment, update2);
|
|
126860
126684
|
if (message) {
|
|
126861
126685
|
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
126862
126686
|
}
|
|
126863
126687
|
}
|
|
126864
|
-
function validateAssessmentResponses(assessment, responses) {
|
|
126865
|
-
const message = assessmentResponseValidationMessage(assessment, responses);
|
|
126866
|
-
if (message) {
|
|
126867
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
126868
|
-
}
|
|
126869
|
-
}
|
|
126870
|
-
function validateAssessmentDraftResponses(assessment, responses) {
|
|
126871
|
-
const message = assessmentDraftResponseValidationMessage(assessment, responses);
|
|
126872
|
-
if (message) {
|
|
126873
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
126874
|
-
}
|
|
126875
|
-
}
|
|
126876
126688
|
function assessmentScoringRequests(assessment, responses) {
|
|
126877
126689
|
return assessment.items.map((item) => ({
|
|
126878
126690
|
itemIdentifier: item.identifier,
|
|
@@ -126912,6 +126724,21 @@ function assessmentScoreFromPercentage(percentage, possible) {
|
|
|
126912
126724
|
const earned = Math.round(normalized * possible * 1e6) / 1e6;
|
|
126913
126725
|
return { earned, possible, normalized };
|
|
126914
126726
|
}
|
|
126727
|
+
function buildAssessmentResultCheckpoint(input) {
|
|
126728
|
+
return {
|
|
126729
|
+
status: input.result.status,
|
|
126730
|
+
assessmentLineItem: input.result.assessmentLineItem,
|
|
126731
|
+
student: input.result.student,
|
|
126732
|
+
score: input.result.score ?? 0,
|
|
126733
|
+
scoreDate: input.result.scoreDate,
|
|
126734
|
+
...ASSESSMENT_ATTEMPT_OPEN,
|
|
126735
|
+
metadata: mergeAssessmentRuntimeMetadata(input.result.metadata, {
|
|
126736
|
+
...input.metadata,
|
|
126737
|
+
transcript: input.transcript,
|
|
126738
|
+
updatedAt: input.timestamp
|
|
126739
|
+
})
|
|
126740
|
+
};
|
|
126741
|
+
}
|
|
126915
126742
|
function buildAssessmentResultSubmission(input) {
|
|
126916
126743
|
const metadata2 = {
|
|
126917
126744
|
...input.metadata,
|
|
@@ -128316,6 +128143,184 @@ var init_timeback_assessment_artifacts_util = __esm(() => {
|
|
|
128316
128143
|
init_timeback_assessment_runtime_util();
|
|
128317
128144
|
init_timeback_review_mapping_util();
|
|
128318
128145
|
});
|
|
128146
|
+
async function scoreCachedAssessmentItems(input) {
|
|
128147
|
+
const selected = input.assessment.items.filter((item) => input.itemIdentifiers.has(item.identifier));
|
|
128148
|
+
const keys = new Map(await Promise.all(selected.map(async (item) => [
|
|
128149
|
+
item.identifier,
|
|
128150
|
+
await sha256Hex(canonicalJson2({
|
|
128151
|
+
identity: input.artifactIdentity,
|
|
128152
|
+
purpose: input.purpose,
|
|
128153
|
+
itemIdentifier: item.identifier,
|
|
128154
|
+
responses: input.responses[item.identifier] ?? {},
|
|
128155
|
+
strategy: input.artifact?.items.find((candidate) => candidate.itemIdentifier === item.identifier)?.strategy ?? "qti"
|
|
128156
|
+
}))
|
|
128157
|
+
])));
|
|
128158
|
+
const scored = new Map;
|
|
128159
|
+
for (const item of selected) {
|
|
128160
|
+
const cached3 = input.cache.get(keys.get(item.identifier));
|
|
128161
|
+
if (cached3) {
|
|
128162
|
+
scored.set(item.identifier, cached3);
|
|
128163
|
+
}
|
|
128164
|
+
}
|
|
128165
|
+
const missing = new Set(selected.filter((item) => !scored.has(item.identifier)).map((item) => item.identifier));
|
|
128166
|
+
const fresh = await scoreAssessmentItems({ ...input, itemIdentifiers: missing });
|
|
128167
|
+
for (const result of fresh) {
|
|
128168
|
+
input.cache.set(keys.get(result.itemIdentifier), result);
|
|
128169
|
+
scored.set(result.itemIdentifier, result);
|
|
128170
|
+
}
|
|
128171
|
+
return selected.map((item) => scored.get(item.identifier));
|
|
128172
|
+
}
|
|
128173
|
+
async function scoreAssessmentItems({
|
|
128174
|
+
assessment,
|
|
128175
|
+
responses,
|
|
128176
|
+
artifactIdentity,
|
|
128177
|
+
purpose,
|
|
128178
|
+
itemIdentifiers,
|
|
128179
|
+
artifact,
|
|
128180
|
+
processResponse,
|
|
128181
|
+
onEvent
|
|
128182
|
+
}) {
|
|
128183
|
+
const scoringItems = assessmentScoringRequests(assessment, responses).filter((item) => !itemIdentifiers || itemIdentifiers.has(item.itemIdentifier));
|
|
128184
|
+
const artifactItems = new Map(artifact?.items.map((item) => [item.itemIdentifier, item]));
|
|
128185
|
+
const scored = new Map;
|
|
128186
|
+
const qti2 = [];
|
|
128187
|
+
for (const item of scoringItems) {
|
|
128188
|
+
const artifactItem = artifactItems.get(item.itemIdentifier);
|
|
128189
|
+
let fallback2 = null;
|
|
128190
|
+
if (!artifact) {
|
|
128191
|
+
fallback2 = { reason: "artifact-unavailable" };
|
|
128192
|
+
} else if (!artifactItem) {
|
|
128193
|
+
fallback2 = { reason: "artifact-item-missing" };
|
|
128194
|
+
} else if (artifactItem.strategy === "qti") {
|
|
128195
|
+
fallback2 = { reason: "artifact-qti", artifactReason: artifactItem.reason };
|
|
128196
|
+
} else {
|
|
128197
|
+
const startedAt = Date.now();
|
|
128198
|
+
try {
|
|
128199
|
+
scored.set(item.itemIdentifier, {
|
|
128200
|
+
...scorePlatformAssessmentItem(artifactItem, responses[item.itemIdentifier]),
|
|
128201
|
+
grading: PLATFORM_CHILD_GRADING
|
|
128202
|
+
});
|
|
128203
|
+
onEvent("assessment.item_scoring", {
|
|
128204
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
128205
|
+
"app.assessment.purpose": purpose,
|
|
128206
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
128207
|
+
"app.assessment.grading_source": "platform-artifact",
|
|
128208
|
+
"app.assessment.grading_duration_ms": Date.now() - startedAt
|
|
128209
|
+
});
|
|
128210
|
+
} catch (error88) {
|
|
128211
|
+
fallback2 = { reason: "platform-grader-error", error: error88 };
|
|
128212
|
+
}
|
|
128213
|
+
}
|
|
128214
|
+
if (fallback2) {
|
|
128215
|
+
qti2.push({ item, ...fallback2 });
|
|
128216
|
+
}
|
|
128217
|
+
}
|
|
128218
|
+
if (qti2.length > 0) {
|
|
128219
|
+
const fallbackTasks = qti2.flatMap(({ item, reason, artifactReason, error: graderError }) => {
|
|
128220
|
+
const interactionResults = [];
|
|
128221
|
+
let startedAt;
|
|
128222
|
+
let completedInteractions = 0;
|
|
128223
|
+
let failed = false;
|
|
128224
|
+
const omittedResponseCount = item.requests.filter((request) => responses[item.itemIdentifier]?.[request.identifier] === undefined).length;
|
|
128225
|
+
let qtiRemoteInteractionCount = 0;
|
|
128226
|
+
let syntheticIncorrectInteractionCount = 0;
|
|
128227
|
+
function fallbackEvent(outcome) {
|
|
128228
|
+
onEvent("assessment.item_scoring_fallback", {
|
|
128229
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
128230
|
+
"app.assessment.purpose": purpose,
|
|
128231
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
128232
|
+
"app.assessment.grading_source": omittedResponseCount > 0 ? "platform-qti-adapter" : "timeback-qti",
|
|
128233
|
+
"app.assessment.fallback_reason": reason,
|
|
128234
|
+
"app.assessment.fallback_duration_ms": startedAt === undefined ? 0 : Date.now() - startedAt,
|
|
128235
|
+
"app.assessment.qti_remote_interaction_count": qtiRemoteInteractionCount,
|
|
128236
|
+
"app.assessment.synthetic_incorrect_interaction_count": syntheticIncorrectInteractionCount,
|
|
128237
|
+
...artifactReason ? { "app.assessment.artifact_fallback_reason": artifactReason } : {},
|
|
128238
|
+
...graderError ? {
|
|
128239
|
+
"app.assessment.platform_grader_exception_type": errorType(graderError)
|
|
128240
|
+
} : {},
|
|
128241
|
+
...outcome
|
|
128242
|
+
});
|
|
128243
|
+
}
|
|
128244
|
+
function completeItem() {
|
|
128245
|
+
const earned = assessmentItemEarnedScore(interactionResults.map((result) => Number.isFinite(result.score) ? result.score : 0), item.maxScore);
|
|
128246
|
+
const possible = item.maxScore;
|
|
128247
|
+
fallbackEvent({ "app.assessment.fallback_outcome": "succeeded" });
|
|
128248
|
+
scored.set(item.itemIdentifier, {
|
|
128249
|
+
itemIdentifier: item.itemIdentifier,
|
|
128250
|
+
score: {
|
|
128251
|
+
earned,
|
|
128252
|
+
possible,
|
|
128253
|
+
normalized: possible === 0 ? 0 : earned / possible
|
|
128254
|
+
},
|
|
128255
|
+
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
128256
|
+
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
128257
|
+
});
|
|
128258
|
+
}
|
|
128259
|
+
if (item.requests.length === 0) {
|
|
128260
|
+
completeItem();
|
|
128261
|
+
return [];
|
|
128262
|
+
}
|
|
128263
|
+
return item.requests.map((request, index2) => async () => {
|
|
128264
|
+
startedAt ??= Date.now();
|
|
128265
|
+
try {
|
|
128266
|
+
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
128267
|
+
if (response === undefined) {
|
|
128268
|
+
syntheticIncorrectInteractionCount += 1;
|
|
128269
|
+
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
128270
|
+
} else {
|
|
128271
|
+
qtiRemoteInteractionCount += 1;
|
|
128272
|
+
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
128273
|
+
}
|
|
128274
|
+
completedInteractions += 1;
|
|
128275
|
+
if (completedInteractions === item.requests.length && !failed) {
|
|
128276
|
+
completeItem();
|
|
128277
|
+
}
|
|
128278
|
+
} catch (error88) {
|
|
128279
|
+
if (!failed) {
|
|
128280
|
+
failed = true;
|
|
128281
|
+
fallbackEvent({
|
|
128282
|
+
"app.assessment.fallback_outcome": "failed",
|
|
128283
|
+
"exception.type": errorType(error88),
|
|
128284
|
+
"app.error.message": errorMessage2(error88)
|
|
128285
|
+
});
|
|
128286
|
+
}
|
|
128287
|
+
throw error88;
|
|
128288
|
+
}
|
|
128289
|
+
});
|
|
128290
|
+
});
|
|
128291
|
+
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
128292
|
+
}
|
|
128293
|
+
return scoringItems.map((item) => {
|
|
128294
|
+
const result = scored.get(item.itemIdentifier);
|
|
128295
|
+
if (!result) {
|
|
128296
|
+
throw new Error(`Assessment item ${item.itemIdentifier} was not scored`);
|
|
128297
|
+
}
|
|
128298
|
+
return result;
|
|
128299
|
+
});
|
|
128300
|
+
}
|
|
128301
|
+
var QTI_CHILD_GRADING;
|
|
128302
|
+
var PLATFORM_QTI_ADAPTER_CHILD_GRADING;
|
|
128303
|
+
var PLATFORM_CHILD_GRADING;
|
|
128304
|
+
var SCORING_CONCURRENCY = 4;
|
|
128305
|
+
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
128306
|
+
init_assessment_runtime2();
|
|
128307
|
+
init_assessment_artifact_envelope_util();
|
|
128308
|
+
init_timeback_assessment_runtime_util();
|
|
128309
|
+
QTI_CHILD_GRADING = {
|
|
128310
|
+
source: "timeback-qti",
|
|
128311
|
+
graderVersion: "timeback-qti-process-response-v1"
|
|
128312
|
+
};
|
|
128313
|
+
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
128314
|
+
source: "platform-qti-adapter",
|
|
128315
|
+
graderVersion: "platform-qti-adapter-v1",
|
|
128316
|
+
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
128317
|
+
};
|
|
128318
|
+
PLATFORM_CHILD_GRADING = {
|
|
128319
|
+
source: "platform-artifact",
|
|
128320
|
+
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
128321
|
+
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
128322
|
+
};
|
|
128323
|
+
});
|
|
128319
128324
|
async function buildSubmissionChildResultCommand(input) {
|
|
128320
128325
|
const { submission, context: context2 } = input;
|
|
128321
128326
|
const { isCorrect } = submission;
|
|
@@ -128330,12 +128335,9 @@ async function buildSubmissionChildResultCommand(input) {
|
|
|
128330
128335
|
score: submission.score,
|
|
128331
128336
|
grading: input.grading
|
|
128332
128337
|
};
|
|
128333
|
-
if (context2.kind
|
|
128334
|
-
|
|
128335
|
-
|
|
128336
|
-
...typeof isCorrect === "boolean" ? { isCorrect } : {},
|
|
128337
|
-
context: context2
|
|
128338
|
-
}, submission.submittedAt);
|
|
128338
|
+
if (context2.kind !== "platform-routed-diagnostic") {
|
|
128339
|
+
const payload = { ...common2, ...typeof isCorrect === "boolean" ? { isCorrect } : {} };
|
|
128340
|
+
return context2.kind === "review" ? buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt) : buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt);
|
|
128339
128341
|
}
|
|
128340
128342
|
if (typeof isCorrect !== "boolean") {
|
|
128341
128343
|
throw new Error("Diagnostic child results require determinate correctness");
|
|
@@ -128356,11 +128358,7 @@ function assessmentChildLineItemMetadataMatches(key, stored, expected) {
|
|
|
128356
128358
|
if (!Array.isArray(storedStandards) || !Array.isArray(expectedStandards)) {
|
|
128357
128359
|
return false;
|
|
128358
128360
|
}
|
|
128359
|
-
|
|
128360
|
-
function contains(standards, candidate) {
|
|
128361
|
-
return standards.some((standard) => canonicalJson2(standard) === canonicalJson2(candidate));
|
|
128362
|
-
}
|
|
128363
|
-
return sameDefinition && (expectedStandards.every((standard) => contains(storedStandards, standard)) || storedStandards.every((standard) => contains(expectedStandards, standard)));
|
|
128361
|
+
return canonicalJson2(reviewDefinitionWithoutStandards(storedDefinition)) === canonicalJson2(reviewDefinitionWithoutStandards(expectedDefinition));
|
|
128364
128362
|
}
|
|
128365
128363
|
function assessmentChildLineItemMatches(stored, expected) {
|
|
128366
128364
|
if (stored.status !== expected.status || stored.parentAssessmentLineItem?.sourcedId !== expected.parentAssessmentLineItem.sourcedId || stored.course?.sourcedId !== expected.course.sourcedId || stored.resultValueMin !== expected.resultValueMin || stored.resultValueMax !== expected.resultValueMax || typeof stored.metadata !== "object" || stored.metadata === null) {
|
|
@@ -128418,19 +128416,14 @@ async function classifyAssessmentChildResult(input) {
|
|
|
128418
128416
|
result.scoreScale,
|
|
128419
128417
|
result.scorePercentile,
|
|
128420
128418
|
result.comment,
|
|
128421
|
-
result.learningObjectiveSet
|
|
128422
|
-
result.incomplete,
|
|
128423
|
-
result.late,
|
|
128424
|
-
result.missing
|
|
128419
|
+
result.learningObjectiveSet
|
|
128425
128420
|
];
|
|
128426
|
-
|
|
128421
|
+
const flags2 = [result.incomplete, result.late, result.missing];
|
|
128422
|
+
if (auxiliaryFields.some((value) => value !== undefined && value !== null) || flags2.some((value) => value !== undefined && value !== null && value !== "false")) {
|
|
128427
128423
|
return { outcome: "conflict", reason: "auxiliary-field-mismatch" };
|
|
128428
128424
|
}
|
|
128429
128425
|
return { outcome: "replay" };
|
|
128430
128426
|
}
|
|
128431
|
-
function hasApiStatus(error88, statusCode) {
|
|
128432
|
-
return isApiError(error88) && error88.statusCode === statusCode;
|
|
128433
|
-
}
|
|
128434
128427
|
async function persistAssessmentChildResult(input) {
|
|
128435
128428
|
const { assessmentResults, childLineItemId } = input;
|
|
128436
128429
|
const verification2 = await verifyAssessmentChildResultCommand(input.command);
|
|
@@ -128439,38 +128432,127 @@ async function persistAssessmentChildResult(input) {
|
|
|
128439
128432
|
}
|
|
128440
128433
|
const { command } = verification2;
|
|
128441
128434
|
const resultId = await assessmentChildResultId(command.payload.attempt.attemptId, command.payload.administration.submissionId);
|
|
128442
|
-
|
|
128443
|
-
|
|
128444
|
-
|
|
128435
|
+
await assessmentResults.upsert(resultId, buildAssessmentChildResultUpdate(command, childLineItemId));
|
|
128436
|
+
}
|
|
128437
|
+
function assessmentTranscriptChildLineItemId(context2, itemIdentifier) {
|
|
128438
|
+
return context2.metadata.purpose === "review" ? reviewQuestionLineItemId(context2.parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(context2.parentLineItemId, itemIdentifier);
|
|
128439
|
+
}
|
|
128440
|
+
async function reconcileAssessmentTranscriptChildren({
|
|
128441
|
+
context: context2,
|
|
128442
|
+
graded,
|
|
128443
|
+
only,
|
|
128444
|
+
assessmentResults,
|
|
128445
|
+
ensureLineItems,
|
|
128446
|
+
onPersisted
|
|
128447
|
+
}) {
|
|
128448
|
+
const metadata2 = graded.metadata;
|
|
128449
|
+
const assessmentKey = metadata2.selectedTest.assessmentKey;
|
|
128450
|
+
if (!assessmentKey) {
|
|
128451
|
+
throw new Error("Assessment context requires a selected assessment key");
|
|
128452
|
+
}
|
|
128453
|
+
const submissions = metadata2.itemSubmissions.filter((submission) => !only || only.has(submission.submissionId));
|
|
128454
|
+
const administered = new Set(submissions.map((submission) => submission.itemIdentifier));
|
|
128455
|
+
await ensureLineItems({
|
|
128456
|
+
items: graded.assessment.items.filter((item) => administered.has(item.identifier))
|
|
128457
|
+
});
|
|
128458
|
+
await runWithConcurrency(submissions, 4, async (submission) => {
|
|
128459
|
+
const index2 = submission.responseVersion - 1;
|
|
128460
|
+
let flow;
|
|
128461
|
+
if (metadata2.purpose === "review") {
|
|
128462
|
+
const selection = metadata2.review.selections.find((item) => item.itemIdentifier === submission.itemIdentifier);
|
|
128463
|
+
flow = {
|
|
128464
|
+
kind: "review",
|
|
128465
|
+
bankRevision: metadata2.review.bankRevision,
|
|
128466
|
+
standard: selection.standard
|
|
128467
|
+
};
|
|
128468
|
+
} else if (metadata2.purpose === "diagnostic" && metadata2.diagnostic) {
|
|
128469
|
+
flow = {
|
|
128470
|
+
kind: "platform-routed-diagnostic",
|
|
128471
|
+
definitionId: metadata2.diagnostic.definitionId,
|
|
128472
|
+
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
128473
|
+
routingRevision: metadata2.diagnostic.routingRevision,
|
|
128474
|
+
transition: metadata2.diagnostic.ledger[index2]
|
|
128475
|
+
};
|
|
128476
|
+
} else {
|
|
128477
|
+
flow = { kind: "fixed", purpose: metadata2.purpose };
|
|
128478
|
+
}
|
|
128479
|
+
const command = await buildSubmissionChildResultCommand({
|
|
128480
|
+
attempt: {
|
|
128481
|
+
attemptId: context2.attemptId,
|
|
128482
|
+
gameId: context2.gameId,
|
|
128483
|
+
studentId: context2.studentId,
|
|
128484
|
+
activityId: metadata2.activityId,
|
|
128485
|
+
courseId: metadata2.courseId,
|
|
128486
|
+
integrationId: metadata2.integrationId,
|
|
128487
|
+
enrollmentId: metadata2.enrollmentId,
|
|
128488
|
+
selectedTest: { ...metadata2.selectedTest, assessmentKey }
|
|
128489
|
+
},
|
|
128490
|
+
submission,
|
|
128491
|
+
responses: graded.transcript.administrations[index2].responses,
|
|
128492
|
+
grading: submission.grading ?? QTI_CHILD_GRADING,
|
|
128493
|
+
context: flow
|
|
128494
|
+
});
|
|
128495
|
+
await persistAssessmentChildResult({
|
|
128496
|
+
assessmentResults,
|
|
128445
128497
|
command,
|
|
128446
|
-
childLineItemId
|
|
128498
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, submission.itemIdentifier)
|
|
128447
128499
|
});
|
|
128448
|
-
|
|
128500
|
+
onPersisted?.(submission.submissionId);
|
|
128501
|
+
});
|
|
128502
|
+
}
|
|
128503
|
+
async function recoveredAssessmentAdministration(context2, result) {
|
|
128504
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
128505
|
+
if (!verified.ok) {
|
|
128506
|
+
return null;
|
|
128449
128507
|
}
|
|
128450
|
-
|
|
128451
|
-
|
|
128452
|
-
|
|
128453
|
-
|
|
128454
|
-
|
|
128508
|
+
const { command } = verified;
|
|
128509
|
+
const { attempt, administration, responses, context: flow } = command.payload;
|
|
128510
|
+
if (attempt.attemptId !== context2.attemptId || attempt.gameId !== context2.gameId || attempt.studentId !== context2.studentId || attempt.integrationId !== context2.metadata.integrationId || attempt.enrollmentId !== context2.metadata.enrollmentId || attempt.courseId !== context2.metadata.courseId || attempt.activityId !== context2.metadata.activityId || canonicalJson2(attempt.selectedTest) !== canonicalJson2(context2.metadata.selectedTest) || result.sourcedId !== await assessmentChildResultId(context2.attemptId, administration.submissionId)) {
|
|
128511
|
+
return null;
|
|
128512
|
+
}
|
|
128513
|
+
const disposition = await classifyAssessmentChildResult({
|
|
128514
|
+
result,
|
|
128515
|
+
command,
|
|
128516
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, administration.itemIdentifier)
|
|
128517
|
+
});
|
|
128518
|
+
if (disposition.outcome !== "replay") {
|
|
128519
|
+
return null;
|
|
128520
|
+
}
|
|
128521
|
+
return {
|
|
128522
|
+
sequence: administration.responseVersion,
|
|
128523
|
+
entry: {
|
|
128524
|
+
submissionId: administration.submissionId,
|
|
128525
|
+
itemIdentifier: administration.itemIdentifier,
|
|
128526
|
+
responses,
|
|
128527
|
+
...flow.kind === "platform-routed-diagnostic" ? { routingNodeKey: flow.transition.routingNodeKey } : {}
|
|
128528
|
+
}
|
|
128529
|
+
};
|
|
128530
|
+
}
|
|
128531
|
+
async function recoverAssessmentTranscript(context2, results) {
|
|
128532
|
+
const entries = new Map;
|
|
128533
|
+
for (const result of results) {
|
|
128534
|
+
const recovered = await recoveredAssessmentAdministration(context2, result);
|
|
128535
|
+
if (recovered) {
|
|
128536
|
+
const atPosition = entries.get(recovered.sequence) ?? new Map;
|
|
128537
|
+
atPosition.set(recovered.entry.submissionId, recovered.entry);
|
|
128538
|
+
entries.set(recovered.sequence, atPosition);
|
|
128455
128539
|
}
|
|
128456
128540
|
}
|
|
128457
|
-
|
|
128458
|
-
|
|
128459
|
-
|
|
128460
|
-
|
|
128461
|
-
|
|
128462
|
-
return { outcome: "created", resultId };
|
|
128463
|
-
} catch (error88) {
|
|
128464
|
-
if (!hasApiStatus(error88, 409)) {
|
|
128465
|
-
throw error88;
|
|
128541
|
+
const transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision);
|
|
128542
|
+
for (let sequence2 = 1;sequence2 <= entries.size; sequence2 += 1) {
|
|
128543
|
+
const candidates = entries.get(sequence2);
|
|
128544
|
+
if (!candidates || candidates.size !== 1) {
|
|
128545
|
+
break;
|
|
128466
128546
|
}
|
|
128547
|
+
transcript.administrations.push(candidates.values().next().value);
|
|
128467
128548
|
}
|
|
128468
|
-
return
|
|
128549
|
+
return transcript;
|
|
128469
128550
|
}
|
|
128470
|
-
var init_timeback_assessment_child_result_util = __esm(
|
|
128551
|
+
var init_timeback_assessment_child_result_util = __esm(() => {
|
|
128471
128552
|
init_src();
|
|
128472
128553
|
init_assessment_runtime2();
|
|
128473
|
-
|
|
128554
|
+
init_timeback_assessment_runtime_util();
|
|
128555
|
+
init_timeback_assessment_scoring_util();
|
|
128474
128556
|
});
|
|
128475
128557
|
|
|
128476
128558
|
class AssessmentPreparationFlights {
|
|
@@ -128574,6 +128656,7 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
128574
128656
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
128575
128657
|
activityId: plan.activityId,
|
|
128576
128658
|
courseId: plan.courseId,
|
|
128659
|
+
courseName: plan.courseName,
|
|
128577
128660
|
integrationId: plan.integrationId,
|
|
128578
128661
|
enrollmentId: plan.enrollmentId,
|
|
128579
128662
|
selectedTest: plan.selectedTest,
|
|
@@ -128604,7 +128687,6 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
128604
128687
|
testName,
|
|
128605
128688
|
routingRevision: plan.diagnostic.routingRevision,
|
|
128606
128689
|
ledger: [],
|
|
128607
|
-
routingSnapshots: [],
|
|
128608
128690
|
state: plan.diagnostic.initialState
|
|
128609
128691
|
}
|
|
128610
128692
|
};
|
|
@@ -128900,123 +128982,6 @@ var init_timeback_assessment_publication_util = __esm(() => {
|
|
|
128900
128982
|
]);
|
|
128901
128983
|
MANAGED_ASSET_URL = new RegExp(`https?://[^\\s"')]+/(${ASSESSMENT_ASSET_KEY_PREFIX}v1/sha256/[a-f0-9]{64}\\.[a-z0-9]+)`, "gi");
|
|
128902
128984
|
});
|
|
128903
|
-
function assessmentChildProjectionIsTerminal(result) {
|
|
128904
|
-
const state = { inProgress: result.inProgress ?? "", scoreStatus: result.scoreStatus };
|
|
128905
|
-
return isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
128906
|
-
}
|
|
128907
|
-
async function reconcileReviewChildResults({
|
|
128908
|
-
initial,
|
|
128909
|
-
writeOpen,
|
|
128910
|
-
crossBarrier,
|
|
128911
|
-
readLatest,
|
|
128912
|
-
restoreTerminal
|
|
128913
|
-
}) {
|
|
128914
|
-
if (initial.metadata.purpose !== "review") {
|
|
128915
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
128916
|
-
}
|
|
128917
|
-
if (assessmentChildProjectionIsTerminal(initial.result)) {
|
|
128918
|
-
const settled = { result: initial.result, metadata: initial.metadata };
|
|
128919
|
-
await restoreTerminal(settled);
|
|
128920
|
-
return settled;
|
|
128921
|
-
}
|
|
128922
|
-
await writeOpen();
|
|
128923
|
-
await crossBarrier();
|
|
128924
|
-
const current = await readLatest();
|
|
128925
|
-
if (current.metadata.purpose !== "review") {
|
|
128926
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
128927
|
-
}
|
|
128928
|
-
const reviewed = { result: current.result, metadata: current.metadata };
|
|
128929
|
-
if (assessmentChildProjectionIsTerminal(current.result)) {
|
|
128930
|
-
await restoreTerminal(reviewed);
|
|
128931
|
-
}
|
|
128932
|
-
return reviewed;
|
|
128933
|
-
}
|
|
128934
|
-
var init_timeback_assessment_reconciliation_util = __esm(() => {
|
|
128935
|
-
init_assessment_runtime2();
|
|
128936
|
-
});
|
|
128937
|
-
function resolveDiagnosticItemReplay(metadata2, input) {
|
|
128938
|
-
const priorIndex = metadata2.itemSubmissions.findIndex((submission) => submission.submissionId === input.submissionId);
|
|
128939
|
-
if (priorIndex === -1) {
|
|
128940
|
-
return { action: "missing" };
|
|
128941
|
-
}
|
|
128942
|
-
const prior = metadata2.itemSubmissions[priorIndex];
|
|
128943
|
-
const ledgerEntry = metadata2.diagnostic.ledger[priorIndex];
|
|
128944
|
-
if (!prior || !ledgerEntry || prior.itemIdentifier !== input.itemIdentifier || ledgerEntry.routingNodeKey !== input.routingNodeKey || !itemResponseUpdateMatches(metadata2.responses[input.itemIdentifier], input.responses)) {
|
|
128945
|
-
return {
|
|
128946
|
-
action: "conflict",
|
|
128947
|
-
failure: assessmentFlowViolation("This diagnostic submission ID was already used for a different request.", {
|
|
128948
|
-
submissionId: input.submissionId,
|
|
128949
|
-
routingNodeKey: input.routingNodeKey,
|
|
128950
|
-
itemIdentifier: input.itemIdentifier
|
|
128951
|
-
})
|
|
128952
|
-
};
|
|
128953
|
-
}
|
|
128954
|
-
return {
|
|
128955
|
-
action: "replay",
|
|
128956
|
-
submission: prior,
|
|
128957
|
-
ledgerEntry,
|
|
128958
|
-
priorIndex,
|
|
128959
|
-
routing: metadata2.diagnostic.routingSnapshots?.[priorIndex]
|
|
128960
|
-
};
|
|
128961
|
-
}
|
|
128962
|
-
function buildAssessmentItemReplayResult(attemptId, metadata2, submission) {
|
|
128963
|
-
return {
|
|
128964
|
-
attemptId,
|
|
128965
|
-
responseVersion: metadata2.responseVersion,
|
|
128966
|
-
status: "in_progress",
|
|
128967
|
-
responses: metadata2.responses,
|
|
128968
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
128969
|
-
submission
|
|
128970
|
-
};
|
|
128971
|
-
}
|
|
128972
|
-
function diagnosticReceipt(submission, ledgerEntry) {
|
|
128973
|
-
return {
|
|
128974
|
-
submissionId: submission.submissionId,
|
|
128975
|
-
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
128976
|
-
itemIdentifier: submission.itemIdentifier,
|
|
128977
|
-
submittedAt: submission.submittedAt,
|
|
128978
|
-
responseVersion: submission.responseVersion,
|
|
128979
|
-
answered: submission.answered,
|
|
128980
|
-
score: submission.score,
|
|
128981
|
-
isCorrect: ledgerEntry.isCorrect
|
|
128982
|
-
};
|
|
128983
|
-
}
|
|
128984
|
-
function buildDiagnosticItemReplayResult(attemptId, submission, ledgerEntry, routing) {
|
|
128985
|
-
return {
|
|
128986
|
-
attemptId,
|
|
128987
|
-
responseVersion: submission.responseVersion,
|
|
128988
|
-
status: "in_progress",
|
|
128989
|
-
routing,
|
|
128990
|
-
submission: diagnosticReceipt(submission, ledgerEntry)
|
|
128991
|
-
};
|
|
128992
|
-
}
|
|
128993
|
-
function requireAssessmentMutationAccess(attemptId, integrationStatus, enrollment) {
|
|
128994
|
-
if (!enrollment || integrationStatus === "deactivated") {
|
|
128995
|
-
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
128996
|
-
}
|
|
128997
|
-
return enrollment;
|
|
128998
|
-
}
|
|
128999
|
-
function committedReviewItemResults(metadata2, assessment) {
|
|
129000
|
-
const available = new Set(assessment.items.map((item) => item.identifier));
|
|
129001
|
-
const seen = new Set;
|
|
129002
|
-
return metadata2.itemSubmissions.map((submission) => {
|
|
129003
|
-
if (!available.has(submission.itemIdentifier) || seen.has(submission.itemIdentifier)) {
|
|
129004
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The persisted review item-submission ledger is corrupt.", {
|
|
129005
|
-
itemIdentifier: submission.itemIdentifier
|
|
129006
|
-
}));
|
|
129007
|
-
}
|
|
129008
|
-
seen.add(submission.itemIdentifier);
|
|
129009
|
-
return {
|
|
129010
|
-
itemIdentifier: submission.itemIdentifier,
|
|
129011
|
-
score: submission.score,
|
|
129012
|
-
isCorrect: submission.isCorrect
|
|
129013
|
-
};
|
|
129014
|
-
});
|
|
129015
|
-
}
|
|
129016
|
-
var init_timeback_assessment_replay_util = __esm(() => {
|
|
129017
|
-
init_assessment_runtime2();
|
|
129018
|
-
init_errors2();
|
|
129019
|
-
});
|
|
129020
128985
|
function diagnosticRoutingFallbackReason(artifact, routingRevision) {
|
|
129021
128986
|
if (!artifact) {
|
|
129022
128987
|
return "artifact-unavailable";
|
|
@@ -129162,156 +129127,252 @@ function validateUniqueAssessmentIdentifiers(testIdentifiers) {
|
|
|
129162
129127
|
var init_timeback_assessment_rules_util = __esm(() => {
|
|
129163
129128
|
init_errors2();
|
|
129164
129129
|
});
|
|
129165
|
-
|
|
129166
|
-
|
|
129167
|
-
|
|
129168
|
-
|
|
129169
|
-
|
|
129170
|
-
|
|
129171
|
-
|
|
129172
|
-
|
|
129173
|
-
|
|
129174
|
-
|
|
129175
|
-
|
|
129176
|
-
|
|
129177
|
-
|
|
129178
|
-
const
|
|
129179
|
-
|
|
129180
|
-
|
|
129181
|
-
|
|
129182
|
-
|
|
129183
|
-
|
|
129184
|
-
|
|
129185
|
-
|
|
129186
|
-
|
|
129187
|
-
|
|
129188
|
-
|
|
129189
|
-
|
|
129190
|
-
|
|
129191
|
-
|
|
129192
|
-
|
|
129193
|
-
|
|
129194
|
-
|
|
129195
|
-
|
|
129196
|
-
|
|
129197
|
-
|
|
129198
|
-
|
|
129199
|
-
|
|
129200
|
-
|
|
129201
|
-
|
|
129202
|
-
|
|
129203
|
-
|
|
129130
|
+
function diagnosticReceipt(submission, ledgerEntry) {
|
|
129131
|
+
return {
|
|
129132
|
+
submissionId: submission.submissionId,
|
|
129133
|
+
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
129134
|
+
itemIdentifier: submission.itemIdentifier,
|
|
129135
|
+
submittedAt: submission.submittedAt,
|
|
129136
|
+
responseVersion: submission.responseVersion,
|
|
129137
|
+
answered: submission.answered,
|
|
129138
|
+
score: submission.score,
|
|
129139
|
+
isCorrect: ledgerEntry.isCorrect
|
|
129140
|
+
};
|
|
129141
|
+
}
|
|
129142
|
+
function parseAssessmentTranscript(value) {
|
|
129143
|
+
const parsed = AssessmentTranscriptSchema.safeParse(value);
|
|
129144
|
+
if (!parsed.success) {
|
|
129145
|
+
throw new ValidationError("Invalid assessment transcript");
|
|
129146
|
+
}
|
|
129147
|
+
return parsed.data;
|
|
129148
|
+
}
|
|
129149
|
+
function assessmentTokenMetadata(metadata2) {
|
|
129150
|
+
const context2 = { ...metadata2, updatedAt: metadata2.startedAt };
|
|
129151
|
+
delete context2.transcript;
|
|
129152
|
+
return context2;
|
|
129153
|
+
}
|
|
129154
|
+
function requireAssessmentTokenContext(context2, scope) {
|
|
129155
|
+
if (!context2 || context2.gameId !== scope.gameId || context2.studentId !== scope.studentId || context2.attemptId !== scope.attemptId) {
|
|
129156
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(scope.attemptId));
|
|
129157
|
+
}
|
|
129158
|
+
return context2;
|
|
129159
|
+
}
|
|
129160
|
+
function requireAssessmentFeedbackContext(tokenContext, scope) {
|
|
129161
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
129162
|
+
const metadata2 = context2.metadata;
|
|
129163
|
+
if (assessmentFlowForPurpose(metadata2.purpose) === "attempt-submit" && !(metadata2.purpose === "diagnostic" && metadata2.diagnostic)) {
|
|
129164
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("This assessment accepts a complete attempt, not per-item feedback."));
|
|
129165
|
+
}
|
|
129166
|
+
return context2;
|
|
129167
|
+
}
|
|
129168
|
+
function prepareAssessmentProgress(tokenContext, scope, input) {
|
|
129169
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
129170
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
129171
|
+
const progress = assessmentProgressAdministration(context2.metadata.purpose, {
|
|
129172
|
+
...input,
|
|
129173
|
+
transcript
|
|
129174
|
+
});
|
|
129175
|
+
if (!progress.ok) {
|
|
129176
|
+
throw AssessmentRuntimeError.from(progress.failure);
|
|
129177
|
+
}
|
|
129178
|
+
return { context: context2, transcript, submissionId: progress.submissionId };
|
|
129179
|
+
}
|
|
129180
|
+
function prepareAssessmentItemTranscript(assessment, input) {
|
|
129181
|
+
validateAssessmentResponseUpdate(assessment, { [input.itemIdentifier]: input.responses });
|
|
129182
|
+
const appended = appendAssessmentAdministration(input.transcript, input);
|
|
129183
|
+
if (!appended.ok) {
|
|
129184
|
+
throw AssessmentRuntimeError.from(appended.failure);
|
|
129185
|
+
}
|
|
129186
|
+
return appended;
|
|
129187
|
+
}
|
|
129188
|
+
function prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal = false) {
|
|
129189
|
+
return prepareAssessmentTranscript({
|
|
129190
|
+
transcript,
|
|
129191
|
+
attemptId: context2.attemptId,
|
|
129192
|
+
contentRevision: context2.metadata.selectedTest.contentRevision,
|
|
129193
|
+
assessment,
|
|
129194
|
+
routed: context2.metadata.purpose === "diagnostic" && context2.metadata.diagnostic !== undefined,
|
|
129195
|
+
allowIncomplete: !terminal && assessmentFlowForPurpose(context2.metadata.purpose) === "attempt-submit"
|
|
129196
|
+
});
|
|
129197
|
+
}
|
|
129198
|
+
function requireTranscriptWriteAccess(attemptId, integrationStatus) {
|
|
129199
|
+
if (integrationStatus === "deactivated") {
|
|
129200
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
129201
|
+
}
|
|
129202
|
+
}
|
|
129203
|
+
function requireTranscriptVersion(transcript, expectedResponseVersion) {
|
|
129204
|
+
if (expectedResponseVersion !== transcript.administrations.length) {
|
|
129205
|
+
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, transcript.administrations.length));
|
|
129206
|
+
}
|
|
129207
|
+
}
|
|
129208
|
+
function requireAssessmentTranscriptCheckpoint(attempt, integrationStatus, input) {
|
|
129209
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
129210
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
129211
|
+
if (!isAssessmentAttemptOpen({ ...attempt, inProgress: attempt.inProgress ?? "" })) {
|
|
129212
|
+
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(attempt.sourcedId));
|
|
129213
|
+
}
|
|
129214
|
+
}
|
|
129215
|
+
function assessmentTranscriptRequestHash(input) {
|
|
129216
|
+
return sha256Hex(canonicalJson2({
|
|
129217
|
+
transcript: input.transcript,
|
|
129218
|
+
...input.session ? { session: input.session } : {}
|
|
129219
|
+
}));
|
|
129220
|
+
}
|
|
129221
|
+
function resolveAssessmentTranscriptSubmission(attempt, metadata2, integrationStatus, input, requestHash) {
|
|
129222
|
+
const disposition = classifyAssessmentSubmission({
|
|
129223
|
+
inProgress: attempt.inProgress ?? "",
|
|
129224
|
+
scoreStatus: attempt.scoreStatus,
|
|
129225
|
+
submissionId: metadata2.submissionId
|
|
129226
|
+
}, input.submissionId);
|
|
129227
|
+
if (disposition === "replay" && metadata2.completion?.requestHash === requestHash) {
|
|
129228
|
+
return "replay";
|
|
129229
|
+
}
|
|
129230
|
+
if (disposition === "reject") {
|
|
129231
|
+
throw AssessmentRuntimeError.from(assessmentAlreadySubmitted(attempt.sourcedId));
|
|
129232
|
+
}
|
|
129233
|
+
if (disposition !== "submit") {
|
|
129234
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("A different completion was already accepted for this attempt."));
|
|
129235
|
+
}
|
|
129236
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
129237
|
+
if (input.session && input.session.runId !== attempt.sourcedId) {
|
|
129238
|
+
throw new ValidationError("Assessment session runId must match the attemptId.");
|
|
129239
|
+
}
|
|
129240
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
129241
|
+
return "submit";
|
|
129242
|
+
}
|
|
129243
|
+
function buildGradedAssessmentTranscript(input) {
|
|
129244
|
+
const { context: context2, prepared, itemResults, assessment, submittedAt, manifest } = input;
|
|
129245
|
+
const completed = completeAssessmentTranscript({
|
|
129246
|
+
transcript: prepared.transcript,
|
|
129247
|
+
scores: itemResults,
|
|
129248
|
+
purpose: context2.metadata.purpose,
|
|
129249
|
+
itemCount: assessment.items.length,
|
|
129250
|
+
submittedAt,
|
|
129251
|
+
manifest,
|
|
129252
|
+
terminal: input.terminal
|
|
129253
|
+
});
|
|
129254
|
+
const common2 = {
|
|
129255
|
+
...context2.metadata,
|
|
129256
|
+
responses: prepared.responses,
|
|
129257
|
+
responseVersion: completed.itemSubmissions.length,
|
|
129258
|
+
itemSubmissions: completed.itemSubmissions,
|
|
129259
|
+
updatedAt: submittedAt,
|
|
129260
|
+
transcript: prepared.transcript
|
|
129261
|
+
};
|
|
129262
|
+
const diagnostic = context2.metadata.purpose === "diagnostic" ? context2.metadata.diagnostic : undefined;
|
|
129263
|
+
const metadata2 = diagnostic ? {
|
|
129264
|
+
...common2,
|
|
129265
|
+
purpose: "diagnostic",
|
|
129266
|
+
diagnostic: { ...diagnostic, ledger: completed.ledger, state: completed.state }
|
|
129267
|
+
} : common2;
|
|
129268
|
+
return {
|
|
129269
|
+
metadata: metadata2,
|
|
129270
|
+
transcript: prepared.transcript,
|
|
129271
|
+
assessment,
|
|
129272
|
+
itemResults,
|
|
129273
|
+
manifest,
|
|
129274
|
+
score: aggregateAssessmentScore(itemResults.map((item) => item.score.earned), itemResults.reduce((sum, item) => sum + item.score.possible, 0)),
|
|
129275
|
+
correctQuestions: countCorrectQuestions(itemResults.map((item) => item.isCorrect))
|
|
129276
|
+
};
|
|
129277
|
+
}
|
|
129278
|
+
function assessmentTranscriptRuntimeError(error88) {
|
|
129279
|
+
return error88 instanceof AssessmentTranscriptError ? AssessmentRuntimeError.from({
|
|
129280
|
+
code: error88.code,
|
|
129281
|
+
message: error88.message,
|
|
129282
|
+
details: { transcriptIndex: error88.index }
|
|
129283
|
+
}) : error88;
|
|
129284
|
+
}
|
|
129285
|
+
function buildAssessmentTranscriptFeedback(attemptId, graded, index2) {
|
|
129286
|
+
const { metadata: metadata2 } = graded;
|
|
129287
|
+
const submission = metadata2.itemSubmissions[index2];
|
|
129288
|
+
const common2 = {
|
|
129289
|
+
attemptId,
|
|
129290
|
+
responseVersion: metadata2.responseVersion,
|
|
129291
|
+
status: "in_progress",
|
|
129292
|
+
transcript: graded.transcript
|
|
129293
|
+
};
|
|
129294
|
+
return metadata2.purpose === "diagnostic" && metadata2.diagnostic ? {
|
|
129295
|
+
...common2,
|
|
129296
|
+
routing: projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, metadata2.diagnostic.state),
|
|
129297
|
+
submission: diagnosticReceipt(submission, metadata2.diagnostic.ledger[index2])
|
|
129298
|
+
} : {
|
|
129299
|
+
...common2,
|
|
129300
|
+
responses: metadata2.responses,
|
|
129301
|
+
itemSubmissions: metadata2.itemSubmissions,
|
|
129302
|
+
submission
|
|
129303
|
+
};
|
|
129304
|
+
}
|
|
129305
|
+
async function resumeAssessmentTranscript(input) {
|
|
129306
|
+
const retained = input.suppliedTranscript ?? input.checkpointTranscript;
|
|
129307
|
+
let transcript = retained ?? await input.recover();
|
|
129308
|
+
while (true) {
|
|
129309
|
+
try {
|
|
129310
|
+
return await input.grade(transcript);
|
|
129311
|
+
} catch (error88) {
|
|
129312
|
+
const index2 = error88 instanceof AssessmentRuntimeError && isRecord(error88.details) ? error88.details.transcriptIndex : undefined;
|
|
129313
|
+
if (retained !== undefined || typeof index2 !== "number" || index2 < 0 || index2 >= transcript.administrations.length) {
|
|
129314
|
+
throw error88;
|
|
129204
129315
|
}
|
|
129205
|
-
|
|
129206
|
-
|
|
129207
|
-
|
|
129316
|
+
transcript = {
|
|
129317
|
+
...transcript,
|
|
129318
|
+
administrations: transcript.administrations.slice(0, index2)
|
|
129319
|
+
};
|
|
129208
129320
|
}
|
|
129209
129321
|
}
|
|
129210
|
-
|
|
129211
|
-
|
|
129212
|
-
|
|
129213
|
-
|
|
129214
|
-
|
|
129215
|
-
|
|
129216
|
-
|
|
129217
|
-
|
|
129218
|
-
|
|
129219
|
-
|
|
129220
|
-
|
|
129221
|
-
|
|
129222
|
-
|
|
129223
|
-
|
|
129224
|
-
|
|
129225
|
-
|
|
129226
|
-
|
|
129227
|
-
|
|
129228
|
-
|
|
129229
|
-
|
|
129230
|
-
|
|
129231
|
-
|
|
129232
|
-
|
|
129233
|
-
|
|
129234
|
-
|
|
129235
|
-
|
|
129236
|
-
|
|
129237
|
-
|
|
129238
|
-
const
|
|
129239
|
-
|
|
129240
|
-
|
|
129241
|
-
|
|
129242
|
-
|
|
129243
|
-
|
|
129244
|
-
|
|
129245
|
-
|
|
129246
|
-
},
|
|
129247
|
-
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
129248
|
-
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
129249
|
-
});
|
|
129250
|
-
}
|
|
129251
|
-
if (item.requests.length === 0) {
|
|
129252
|
-
completeItem();
|
|
129253
|
-
return [];
|
|
129322
|
+
}
|
|
129323
|
+
function assessmentReviewTranscriptOutcomes(input) {
|
|
129324
|
+
const resultByItem = new Map(input.itemResults.map((item) => [item.itemIdentifier, item]));
|
|
129325
|
+
const administered = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
129326
|
+
return input.metadata.review.selections.filter((selection) => administered.has(selection.itemIdentifier)).map((selection) => {
|
|
129327
|
+
const result = resultByItem.get(selection.itemIdentifier);
|
|
129328
|
+
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
129329
|
+
if (!result || !submission) {
|
|
129330
|
+
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
129331
|
+
}
|
|
129332
|
+
return {
|
|
129333
|
+
itemIdentifier: selection.itemIdentifier,
|
|
129334
|
+
standard: selection.standard,
|
|
129335
|
+
answered: submission.answered,
|
|
129336
|
+
score: result.score,
|
|
129337
|
+
isCorrect: result.isCorrect
|
|
129338
|
+
};
|
|
129339
|
+
});
|
|
129340
|
+
}
|
|
129341
|
+
function assessmentReviewTranscriptExposures(input) {
|
|
129342
|
+
const exposures = [];
|
|
129343
|
+
const authoritative = new Set;
|
|
129344
|
+
for (const { result, metadata: metadata2 } of input.attempts) {
|
|
129345
|
+
const state = { ...result, inProgress: result.inProgress ?? "" };
|
|
129346
|
+
const settled = isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
129347
|
+
if (metadata2.purpose === "review" && (metadata2.transcript !== undefined || settled)) {
|
|
129348
|
+
authoritative.add(result.sourcedId);
|
|
129349
|
+
if (metadata2.review.bankRevision === input.bankRevision) {
|
|
129350
|
+
const accepted = settled ? metadata2.itemSubmissions.map((submission) => ({
|
|
129351
|
+
itemIdentifier: submission.itemIdentifier,
|
|
129352
|
+
deliveredAt: submission.submittedAt
|
|
129353
|
+
})) : metadata2.transcript.administrations.map((entry2) => ({
|
|
129354
|
+
itemIdentifier: entry2.itemIdentifier,
|
|
129355
|
+
deliveredAt: metadata2.updatedAt
|
|
129356
|
+
}));
|
|
129357
|
+
exposures.push(...accepted);
|
|
129254
129358
|
}
|
|
129255
|
-
|
|
129256
|
-
startedAt ??= Date.now();
|
|
129257
|
-
try {
|
|
129258
|
-
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
129259
|
-
if (response === undefined) {
|
|
129260
|
-
syntheticIncorrectInteractionCount += 1;
|
|
129261
|
-
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
129262
|
-
} else {
|
|
129263
|
-
qtiRemoteInteractionCount += 1;
|
|
129264
|
-
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
129265
|
-
}
|
|
129266
|
-
completedInteractions += 1;
|
|
129267
|
-
if (completedInteractions === item.requests.length && !failed) {
|
|
129268
|
-
completeItem();
|
|
129269
|
-
}
|
|
129270
|
-
} catch (error88) {
|
|
129271
|
-
if (!failed) {
|
|
129272
|
-
failed = true;
|
|
129273
|
-
fallbackEvent({
|
|
129274
|
-
"app.assessment.fallback_outcome": "failed",
|
|
129275
|
-
"exception.type": errorType(error88),
|
|
129276
|
-
"app.error.message": errorMessage2(error88)
|
|
129277
|
-
});
|
|
129278
|
-
}
|
|
129279
|
-
throw error88;
|
|
129280
|
-
}
|
|
129281
|
-
});
|
|
129282
|
-
});
|
|
129283
|
-
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
129359
|
+
}
|
|
129284
129360
|
}
|
|
129285
|
-
|
|
129286
|
-
const
|
|
129287
|
-
if (!
|
|
129288
|
-
|
|
129361
|
+
for (const command of input.children) {
|
|
129362
|
+
const { attempt, administration, context: context2 } = command.payload;
|
|
129363
|
+
if (!authoritative.has(attempt.attemptId) && context2.kind === "review" && context2.bankRevision === input.bankRevision) {
|
|
129364
|
+
exposures.push({
|
|
129365
|
+
itemIdentifier: administration.itemIdentifier,
|
|
129366
|
+
deliveredAt: command.recordedAt
|
|
129367
|
+
});
|
|
129289
129368
|
}
|
|
129290
|
-
|
|
129291
|
-
|
|
129369
|
+
}
|
|
129370
|
+
return exposures;
|
|
129292
129371
|
}
|
|
129293
|
-
var
|
|
129294
|
-
var PLATFORM_QTI_ADAPTER_CHILD_GRADING;
|
|
129295
|
-
var PLATFORM_CHILD_GRADING;
|
|
129296
|
-
var SCORING_CONCURRENCY = 4;
|
|
129297
|
-
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
129372
|
+
var init_timeback_assessment_transcript_util = __esm(() => {
|
|
129298
129373
|
init_assessment_runtime2();
|
|
129299
|
-
|
|
129374
|
+
init_errors2();
|
|
129300
129375
|
init_timeback_assessment_runtime_util();
|
|
129301
|
-
QTI_CHILD_GRADING = {
|
|
129302
|
-
source: "timeback-qti",
|
|
129303
|
-
graderVersion: "timeback-qti-process-response-v1"
|
|
129304
|
-
};
|
|
129305
|
-
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
129306
|
-
source: "platform-qti-adapter",
|
|
129307
|
-
graderVersion: "platform-qti-adapter-v1",
|
|
129308
|
-
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
129309
|
-
};
|
|
129310
|
-
PLATFORM_CHILD_GRADING = {
|
|
129311
|
-
source: "platform-artifact",
|
|
129312
|
-
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
129313
|
-
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
129314
|
-
};
|
|
129315
129376
|
});
|
|
129316
129377
|
function qtiItemHref(client2, itemIdentifier) {
|
|
129317
129378
|
return `${client2.getQtiBaseUrl()}/assessment-items/${itemIdentifier}`;
|
|
@@ -129395,20 +129456,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129395
129456
|
init_assessment_artifact_runtime_util();
|
|
129396
129457
|
init_assessment_runtime_lock_util();
|
|
129397
129458
|
init_timeback_assessment_artifacts_util();
|
|
129459
|
+
init_timeback_assessment_child_result_util();
|
|
129398
129460
|
init_timeback_assessment_preparation_util();
|
|
129399
129461
|
init_timeback_assessment_publication_util();
|
|
129400
|
-
init_timeback_assessment_reconciliation_util();
|
|
129401
|
-
init_timeback_assessment_replay_util();
|
|
129402
129462
|
init_timeback_assessment_routing_util();
|
|
129403
129463
|
init_timeback_assessment_rules_util();
|
|
129404
129464
|
init_timeback_assessment_runtime_util();
|
|
129405
129465
|
init_timeback_assessment_scoring_util();
|
|
129466
|
+
init_timeback_assessment_transcript_util();
|
|
129406
129467
|
init_timeback_qti_hydration_util();
|
|
129407
129468
|
await __promiseAll([
|
|
129408
129469
|
init_dist5(),
|
|
129409
129470
|
init_errors8(),
|
|
129410
|
-
init_assessment_artifact_loader_util()
|
|
129411
|
-
init_timeback_assessment_child_result_util()
|
|
129471
|
+
init_assessment_artifact_loader_util()
|
|
129412
129472
|
]);
|
|
129413
129473
|
AssessmentPreparationPrerequisiteError = class AssessmentPreparationPrerequisiteError2 extends Error {
|
|
129414
129474
|
original;
|
|
@@ -129423,7 +129483,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129423
129483
|
static PREPARATION_IN_FLIGHT_LIMIT = 32;
|
|
129424
129484
|
static ASSESSMENT_CACHE_TTL_MS = 60000;
|
|
129425
129485
|
static EXPORT_CONCURRENCY = 4;
|
|
129426
|
-
static ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS = 3;
|
|
129427
129486
|
static ASSESSMENT_VERSION_CONFIRMATION_DELAY_MS = 25;
|
|
129428
129487
|
static SCORING_CONCURRENCY = 4;
|
|
129429
129488
|
static STALE_AWARD_AGE_MS = 900000;
|
|
@@ -129441,6 +129500,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129441
129500
|
maxSize: TimebackAssessmentRuntimeService2.ASSESSMENT_CACHE_LIMIT
|
|
129442
129501
|
});
|
|
129443
129502
|
artifactLoader;
|
|
129503
|
+
itemScoreCache = new TimebackCache({
|
|
129504
|
+
defaultTTL: 300000,
|
|
129505
|
+
maxSize: 512
|
|
129506
|
+
});
|
|
129507
|
+
childLineItemCache = new TimebackCache({
|
|
129508
|
+
defaultTTL: 300000,
|
|
129509
|
+
maxSize: 512
|
|
129510
|
+
});
|
|
129444
129511
|
preparations = new AssessmentPreparationFlights(TimebackAssessmentRuntimeService2.PREPARATION_IN_FLIGHT_LIMIT);
|
|
129445
129512
|
constructor(deps) {
|
|
129446
129513
|
this.deps = deps;
|
|
@@ -129567,6 +129634,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129567
129634
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
129568
129635
|
activityId: input.activityId,
|
|
129569
129636
|
courseId: context2.integration.courseId,
|
|
129637
|
+
courseName: context2.enrollment.course.title,
|
|
129570
129638
|
integrationId: context2.integration.id,
|
|
129571
129639
|
enrollmentId: context2.enrollment.id,
|
|
129572
129640
|
selectedTest: {
|
|
@@ -129623,7 +129691,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129623
129691
|
enrollmentId: context2.enrollment.id
|
|
129624
129692
|
};
|
|
129625
129693
|
const tests = await this.liveTests(context2.integration.id, params.input.purpose, this.deps.db, params.input.purpose === "mastery" ? params.input.standard : undefined, params.input.purpose === "diagnostic" ? params.input.diagnosticKey : undefined);
|
|
129626
|
-
const listing = this.attemptListing(results, attemptScope);
|
|
129694
|
+
const listing = await this.attemptListing(results, attemptScope);
|
|
129627
129695
|
const decision = selectRuntimeAssessment(tests, [
|
|
129628
129696
|
...this.attemptsMatchingRequest(listing.attempts, this.startRequestFingerprint(params.input)).values()
|
|
129629
129697
|
].map((entry2) => entry2.selection));
|
|
@@ -129646,6 +129714,18 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129646
129714
|
let prepared;
|
|
129647
129715
|
try {
|
|
129648
129716
|
prepared = await this.buildPreparationPlan(params, requestHash, preparation);
|
|
129717
|
+
const assessment = await this.loadPreparedPlanAssessment(prepared.plan);
|
|
129718
|
+
if (!assessment) {
|
|
129719
|
+
throw new Error("Prepared assessment artifact is unavailable");
|
|
129720
|
+
}
|
|
129721
|
+
await this.provisionPreparationPrerequisites(() => this.ensureTranscriptLineItems({
|
|
129722
|
+
version: 1,
|
|
129723
|
+
gameId: prepared.plan.gameId,
|
|
129724
|
+
studentId: prepared.plan.studentId,
|
|
129725
|
+
attemptId: prepared.plan.attemptId,
|
|
129726
|
+
parentLineItemId: prepared.plan.parentLineItemId,
|
|
129727
|
+
metadata: buildPreparedAssessmentMetadata(prepared.plan, new Date().toISOString(), assessment.title)
|
|
129728
|
+
}, assessment));
|
|
129649
129729
|
} catch (error88) {
|
|
129650
129730
|
if (error88 instanceof ValidationError || error88 instanceof AssessmentRuntimeError) {
|
|
129651
129731
|
throw error88;
|
|
@@ -129731,6 +129811,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129731
129811
|
integrationId: context2.integration.id,
|
|
129732
129812
|
enrollmentId: context2.enrollment.id,
|
|
129733
129813
|
courseId: context2.integration.courseId,
|
|
129814
|
+
courseName: context2.enrollment.course.title,
|
|
129734
129815
|
...attempt,
|
|
129735
129816
|
parentLineItemId,
|
|
129736
129817
|
selectedTest: {
|
|
@@ -129771,7 +129852,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129771
129852
|
const selected = selectReviewItems({
|
|
129772
129853
|
request: params.input,
|
|
129773
129854
|
bank: catalog.bank,
|
|
129774
|
-
exposures:
|
|
129855
|
+
exposures: assessmentReviewTranscriptExposures({
|
|
129856
|
+
attempts: listing.attempts.values(),
|
|
129857
|
+
children: listing.reviewChildren.values(),
|
|
129775
129858
|
bankRevision: catalog.bank.bankRevision
|
|
129776
129859
|
})
|
|
129777
129860
|
});
|
|
@@ -129820,6 +129903,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129820
129903
|
integrationId: context2.integration.id,
|
|
129821
129904
|
enrollmentId: context2.enrollment.id,
|
|
129822
129905
|
courseId: context2.integration.courseId,
|
|
129906
|
+
courseName: context2.enrollment.course.title,
|
|
129823
129907
|
...attempt,
|
|
129824
129908
|
parentLineItemId,
|
|
129825
129909
|
selectedTest: {
|
|
@@ -129971,6 +130055,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129971
130055
|
activityId: input.activityId,
|
|
129972
130056
|
purpose: "diagnostic",
|
|
129973
130057
|
courseId: context2.integration.courseId,
|
|
130058
|
+
courseName: context2.enrollment.course.title,
|
|
129974
130059
|
integrationId: context2.integration.id,
|
|
129975
130060
|
enrollmentId: context2.enrollment.id,
|
|
129976
130061
|
selectedTest: {
|
|
@@ -129990,7 +130075,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
129990
130075
|
testName: assessment.title,
|
|
129991
130076
|
routingRevision: routing.routingRevision,
|
|
129992
130077
|
ledger: [],
|
|
129993
|
-
routingSnapshots: [],
|
|
129994
130078
|
state: routing.state
|
|
129995
130079
|
}
|
|
129996
130080
|
};
|
|
@@ -130009,7 +130093,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130009
130093
|
})
|
|
130010
130094
|
});
|
|
130011
130095
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
130012
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration,
|
|
130096
|
+
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration, {
|
|
130097
|
+
state: routing.state
|
|
130098
|
+
});
|
|
130013
130099
|
});
|
|
130014
130100
|
}
|
|
130015
130101
|
async startReview({
|
|
@@ -130043,7 +130129,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130043
130129
|
this.listAttempts(attemptScope)
|
|
130044
130130
|
]);
|
|
130045
130131
|
const attempts = listing.attempts;
|
|
130046
|
-
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts,
|
|
130132
|
+
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts, context2));
|
|
130047
130133
|
if (initialResolution.kind === "resumed") {
|
|
130048
130134
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
130049
130135
|
return initialResolution.snapshot;
|
|
@@ -130062,7 +130148,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130062
130148
|
});
|
|
130063
130149
|
const refreshedListing = await this.listAttempts(attemptScope);
|
|
130064
130150
|
const refreshed = refreshedListing.attempts;
|
|
130065
|
-
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed,
|
|
130151
|
+
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed, context2));
|
|
130066
130152
|
if (refreshedResolution.kind === "resumed") {
|
|
130067
130153
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
130068
130154
|
return refreshedResolution.snapshot;
|
|
@@ -130075,7 +130161,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130075
130161
|
if (catalog.test.identifier !== decision.test.qtiTestIdentifier) {
|
|
130076
130162
|
catalog = await this.loadReviewBankCatalog(decision.test.qtiTestIdentifier);
|
|
130077
130163
|
}
|
|
130078
|
-
const exposures =
|
|
130164
|
+
const exposures = assessmentReviewTranscriptExposures({
|
|
130165
|
+
attempts: refreshedListing.attempts.values(),
|
|
130166
|
+
children: refreshedListing.reviewChildren.values(),
|
|
130079
130167
|
bankRevision: catalog.bank.bankRevision
|
|
130080
130168
|
});
|
|
130081
130169
|
const selected = selectReviewItems({
|
|
@@ -130114,6 +130202,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130114
130202
|
activityId: input.activityId,
|
|
130115
130203
|
purpose: input.purpose,
|
|
130116
130204
|
courseId: context2.integration.courseId,
|
|
130205
|
+
courseName: context2.enrollment.course.title,
|
|
130117
130206
|
integrationId: context2.integration.id,
|
|
130118
130207
|
enrollmentId: context2.enrollment.id,
|
|
130119
130208
|
selectedTest: {
|
|
@@ -130160,8 +130249,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130160
130249
|
});
|
|
130161
130250
|
}
|
|
130162
130251
|
async get(params) {
|
|
130252
|
+
return this.resume({ ...params, input: {} });
|
|
130253
|
+
}
|
|
130254
|
+
async resume({
|
|
130255
|
+
input,
|
|
130256
|
+
...params
|
|
130257
|
+
}) {
|
|
130163
130258
|
const attempt = await this.authorizeAttemptRead(params);
|
|
130164
|
-
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration);
|
|
130259
|
+
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration, input.transcript);
|
|
130165
130260
|
}
|
|
130166
130261
|
async latest({
|
|
130167
130262
|
gameId,
|
|
@@ -130223,496 +130318,104 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130223
130318
|
}
|
|
130224
130319
|
async save({ input, ...params }) {
|
|
130225
130320
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
130226
|
-
try {
|
|
130227
|
-
const warmup = await this.peekAttempt(params);
|
|
130228
|
-
if (this.isOpen(warmup.result) && !this.isPlatformRoutedDiagnosticMetadata(warmup.metadata)) {
|
|
130229
|
-
await this.loadAttemptAssessment(warmup.metadata);
|
|
130230
|
-
}
|
|
130231
|
-
} catch (error88) {
|
|
130232
|
-
addEvent("assessment.save_warmup_failed", {
|
|
130233
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
130234
|
-
"exception.type": errorType(error88),
|
|
130235
|
-
"app.error.message": errorMessage2(error88)
|
|
130236
|
-
});
|
|
130237
|
-
}
|
|
130238
130321
|
return this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
130239
|
-
const attempt = await this.
|
|
130322
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
130240
130323
|
db: db2,
|
|
130241
|
-
developerAccessValidated: true
|
|
130324
|
+
developerAccessValidated: true,
|
|
130325
|
+
includeEnrollment: false
|
|
130242
130326
|
});
|
|
130243
|
-
|
|
130244
|
-
|
|
130245
|
-
|
|
130246
|
-
|
|
130247
|
-
|
|
130248
|
-
|
|
130249
|
-
|
|
130250
|
-
|
|
130251
|
-
|
|
130252
|
-
|
|
130253
|
-
|
|
130254
|
-
|
|
130255
|
-
validateAssessmentDraftResponses(assessment, responses);
|
|
130256
|
-
const metadata2 = {
|
|
130257
|
-
...attempt.metadata,
|
|
130258
|
-
responses,
|
|
130259
|
-
responseVersion: attempt.metadata.responseVersion + 1,
|
|
130260
|
-
updatedAt: new Date().toISOString()
|
|
130261
|
-
};
|
|
130262
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
130327
|
+
requireAssessmentTranscriptCheckpoint(attempt.result, attempt.integration.status, input);
|
|
130328
|
+
const graded = await this.gradeTranscript({
|
|
130329
|
+
attemptId: params.attemptId,
|
|
130330
|
+
metadata: attempt.metadata
|
|
130331
|
+
}, input.transcript, false, db2);
|
|
130332
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, buildAssessmentResultCheckpoint({
|
|
130333
|
+
result: attempt.result,
|
|
130334
|
+
metadata: attempt.metadata,
|
|
130335
|
+
transcript: graded.transcript,
|
|
130336
|
+
timestamp: graded.metadata.updatedAt
|
|
130337
|
+
}));
|
|
130338
|
+
this.reconcileTranscriptChildren(this.tokenContext(attempt), graded).catch((error88) => this.recordTranscriptFailure("checkpoint_projection", params.attemptId, error88));
|
|
130263
130339
|
return {
|
|
130264
|
-
attemptId:
|
|
130265
|
-
responseVersion:
|
|
130340
|
+
attemptId: params.attemptId,
|
|
130341
|
+
responseVersion: graded.metadata.responseVersion,
|
|
130266
130342
|
status: "in_progress",
|
|
130267
|
-
responses
|
|
130343
|
+
responses: graded.metadata.responses,
|
|
130344
|
+
transcript: graded.transcript
|
|
130268
130345
|
};
|
|
130269
130346
|
});
|
|
130270
130347
|
}
|
|
130271
|
-
async
|
|
130348
|
+
async recordProgress({
|
|
130272
130349
|
input,
|
|
130273
130350
|
...params
|
|
130274
130351
|
}) {
|
|
130275
130352
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
130276
|
-
|
|
130277
|
-
|
|
130278
|
-
|
|
130279
|
-
|
|
130280
|
-
}
|
|
130281
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is not accepted by the hosted conventional item-submit flow.", { routingNodeKey: input.routingNodeKey }));
|
|
130282
|
-
}
|
|
130283
|
-
let preview = await this.prepareItemSubmission(params, input);
|
|
130284
|
-
let preparationAttempts = 1;
|
|
130285
|
-
while (true) {
|
|
130286
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
130287
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, {
|
|
130288
|
-
db: db2,
|
|
130289
|
-
developerAccessValidated: true
|
|
130290
|
-
});
|
|
130291
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
130292
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130293
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("Platform-routed diagnostic submissions require routingNodeKey.", { flow: "platform-routed-item-submit" }));
|
|
130294
|
-
}
|
|
130295
|
-
const replay = resolveAssessmentItemReplay(attempt.metadata, input);
|
|
130296
|
-
if (replay.action === "conflict") {
|
|
130297
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130298
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
130299
|
-
}
|
|
130300
|
-
if (replay.action === "replay") {
|
|
130301
|
-
const prior = replay.submission;
|
|
130302
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
130303
|
-
attempt,
|
|
130304
|
-
metadata: attempt.metadata,
|
|
130305
|
-
submission: prior
|
|
130306
|
-
}) : null;
|
|
130307
|
-
return {
|
|
130308
|
-
action: "complete",
|
|
130309
|
-
response: buildAssessmentItemReplayResult(attempt.result.sourcedId, attempt.metadata, prior),
|
|
130310
|
-
checkpoint: child2,
|
|
130311
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
130312
|
-
result: attempt.result,
|
|
130313
|
-
metadata: attempt.metadata,
|
|
130314
|
-
itemIdentifier: input.itemIdentifier,
|
|
130315
|
-
integration: attempt.integration
|
|
130316
|
-
} : null
|
|
130317
|
-
};
|
|
130318
|
-
}
|
|
130319
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130320
|
-
this.assertInProgress(attempt.result);
|
|
130321
|
-
if (!preview || preview.responseVersion !== attempt.metadata.responseVersion || preview.submissionId !== input.submissionId || preview.itemIdentifier !== input.itemIdentifier) {
|
|
130322
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService2.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
130323
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, attempt.metadata.responseVersion, "assessment");
|
|
130324
|
-
}
|
|
130325
|
-
return { action: "prepare" };
|
|
130326
|
-
}
|
|
130327
|
-
const transition = prepareAssessmentItemSubmission({
|
|
130328
|
-
purpose: attempt.metadata.purpose,
|
|
130329
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
130330
|
-
responses: attempt.metadata.responses,
|
|
130331
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
130332
|
-
assessment: preview.assessment
|
|
130333
|
-
}, input);
|
|
130334
|
-
if (transition.action === "reject") {
|
|
130335
|
-
throw AssessmentRuntimeError.from(transition.failure);
|
|
130336
|
-
}
|
|
130337
|
-
if (transition.action === "replay") {
|
|
130338
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
130339
|
-
attempt,
|
|
130340
|
-
metadata: attempt.metadata,
|
|
130341
|
-
submission: transition.submission
|
|
130342
|
-
}) : null;
|
|
130343
|
-
return {
|
|
130344
|
-
action: "complete",
|
|
130345
|
-
response: {
|
|
130346
|
-
attemptId: attempt.result.sourcedId,
|
|
130347
|
-
responseVersion: transition.responseVersion,
|
|
130348
|
-
status: "in_progress",
|
|
130349
|
-
responses: transition.responses,
|
|
130350
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
130351
|
-
submission: transition.submission
|
|
130352
|
-
},
|
|
130353
|
-
checkpoint: child2,
|
|
130354
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
130355
|
-
result: attempt.result,
|
|
130356
|
-
metadata: attempt.metadata,
|
|
130357
|
-
itemIdentifier: input.itemIdentifier,
|
|
130358
|
-
integration: attempt.integration
|
|
130359
|
-
} : null
|
|
130360
|
-
};
|
|
130361
|
-
}
|
|
130362
|
-
if (!preview.scoring) {
|
|
130363
|
-
throw new Error("Prepared item submission is missing its score");
|
|
130364
|
-
}
|
|
130365
|
-
const submittedAt = new Date().toISOString();
|
|
130366
|
-
const completed = completeAssessmentItemSubmission(attempt.metadata, input, transition, preview.scoring, submittedAt);
|
|
130367
|
-
const metadata2 = {
|
|
130368
|
-
...attempt.metadata,
|
|
130369
|
-
responses: completed.responses,
|
|
130370
|
-
responseVersion: completed.responseVersion,
|
|
130371
|
-
itemSubmissions: completed.itemSubmissions,
|
|
130372
|
-
updatedAt: submittedAt
|
|
130373
|
-
};
|
|
130374
|
-
const child = metadata2.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
130375
|
-
attempt,
|
|
130376
|
-
metadata: metadata2,
|
|
130377
|
-
submission: completed.submission
|
|
130378
|
-
}) : null;
|
|
130379
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
130380
|
-
return {
|
|
130381
|
-
action: "complete",
|
|
130382
|
-
response: {
|
|
130383
|
-
attemptId: attempt.result.sourcedId,
|
|
130384
|
-
responseVersion: completed.responseVersion,
|
|
130385
|
-
status: "in_progress",
|
|
130386
|
-
responses: completed.responses,
|
|
130387
|
-
itemSubmissions: completed.itemSubmissions,
|
|
130388
|
-
submission: completed.submission
|
|
130389
|
-
},
|
|
130390
|
-
checkpoint: child,
|
|
130391
|
-
projection: metadata2.purpose === "review" ? {
|
|
130392
|
-
result: attempt.result,
|
|
130393
|
-
metadata: metadata2,
|
|
130394
|
-
itemIdentifier: input.itemIdentifier,
|
|
130395
|
-
integration: attempt.integration
|
|
130396
|
-
} : null
|
|
130397
|
-
};
|
|
130398
|
-
});
|
|
130399
|
-
if (committed.action === "prepare") {
|
|
130400
|
-
preparationAttempts += 1;
|
|
130401
|
-
preview = await this.scoreItemSubmission(params, input);
|
|
130402
|
-
} else {
|
|
130403
|
-
if (committed.projection) {
|
|
130404
|
-
this.projectReviewItemResponse(params, committed.projection, committed.checkpoint);
|
|
130405
|
-
}
|
|
130406
|
-
return committed.response;
|
|
130407
|
-
}
|
|
130408
|
-
}
|
|
130409
|
-
}
|
|
130410
|
-
async submitDiagnosticItem(params, input) {
|
|
130411
|
-
let preview = await this.prepareDiagnosticItemSubmission(params, input);
|
|
130412
|
-
let preparationAttempts = 1;
|
|
130413
|
-
while (true) {
|
|
130414
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
130415
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, { db: db2, developerAccessValidated: true });
|
|
130416
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
130417
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130418
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is accepted only for a platform-routed diagnostic.", { routingNodeKey: input.routingNodeKey }));
|
|
130419
|
-
}
|
|
130420
|
-
const metadata2 = attempt.metadata;
|
|
130421
|
-
const replay = resolveDiagnosticItemReplay(metadata2, input);
|
|
130422
|
-
if (replay.action === "conflict") {
|
|
130423
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130424
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
130425
|
-
}
|
|
130426
|
-
if (replay.action === "replay") {
|
|
130427
|
-
const { submission: prior, ledgerEntry, priorIndex } = replay;
|
|
130428
|
-
let routing = replay.routing;
|
|
130429
|
-
if (!routing) {
|
|
130430
|
-
const assessment = preview?.assessment ?? await this.loadAttemptAssessment(metadata2);
|
|
130431
|
-
const loaded2 = await this.loadAttemptDiagnosticRouting(metadata2, assessment, db2);
|
|
130432
|
-
const replayed = replayDiagnosticRouting(loaded2.manifest, metadata2.diagnostic.ledger.slice(0, priorIndex + 1));
|
|
130433
|
-
if (!replayed.ok) {
|
|
130434
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The committed diagnostic routing state cannot be replayed.", { failure: replayed.failure }));
|
|
130435
|
-
}
|
|
130436
|
-
routing = this.diagnosticRoutingSnapshot(metadata2, replayed.state);
|
|
130437
|
-
}
|
|
130438
|
-
const child2 = await this.prepareDiagnosticAssessmentChildResult({
|
|
130439
|
-
attempt,
|
|
130440
|
-
metadata: metadata2,
|
|
130441
|
-
submission: prior,
|
|
130442
|
-
transition: ledgerEntry
|
|
130443
|
-
});
|
|
130444
|
-
return {
|
|
130445
|
-
action: "complete",
|
|
130446
|
-
response: buildDiagnosticItemReplayResult(attempt.result.sourcedId, prior, ledgerEntry, routing),
|
|
130447
|
-
checkpoint: child2,
|
|
130448
|
-
projection: {
|
|
130449
|
-
result: attempt.result,
|
|
130450
|
-
metadata: metadata2,
|
|
130451
|
-
ledgerEntry,
|
|
130452
|
-
integration: attempt.integration
|
|
130453
|
-
}
|
|
130454
|
-
};
|
|
130455
|
-
}
|
|
130456
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
130457
|
-
this.assertInProgress(attempt.result);
|
|
130458
|
-
if (!preview || preview.responseVersion !== metadata2.responseVersion || preview.submissionId !== input.submissionId || preview.routingNodeKey !== input.routingNodeKey || preview.itemIdentifier !== input.itemIdentifier) {
|
|
130459
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService2.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
130460
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, metadata2.responseVersion, "diagnostic");
|
|
130461
|
-
}
|
|
130462
|
-
return { action: "prepare" };
|
|
130463
|
-
}
|
|
130464
|
-
this.assertResponseVersion(metadata2, input.expectedResponseVersion);
|
|
130465
|
-
const loaded = await this.loadAttemptDiagnosticRouting(metadata2, preview.assessment, db2);
|
|
130466
|
-
const expected = loaded.state.next;
|
|
130467
|
-
if (loaded.state.status !== "in-progress" || !expected || expected.nodeKey !== input.routingNodeKey || expected.itemIdentifier !== input.itemIdentifier) {
|
|
130468
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(expected ? "Submitted diagnostic item is not the canonical next item." : "Diagnostic routing is already ready to complete.", {
|
|
130469
|
-
expectedRoutingNodeKey: expected?.nodeKey ?? null,
|
|
130470
|
-
routingNodeKey: input.routingNodeKey,
|
|
130471
|
-
expectedItemIdentifier: expected?.itemIdentifier ?? null,
|
|
130472
|
-
itemIdentifier: input.itemIdentifier
|
|
130473
|
-
}));
|
|
130474
|
-
}
|
|
130475
|
-
const responses = prepareDiagnosticAssessmentResponses(preview.assessment, metadata2.responses, input);
|
|
130476
|
-
const scoring = preview.scoring;
|
|
130477
|
-
if (!scoring || typeof scoring.isCorrect !== "boolean") {
|
|
130478
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(`Diagnostic item ${input.itemIdentifier} did not produce determinate binary grading.`, { itemIdentifier: input.itemIdentifier }));
|
|
130479
|
-
}
|
|
130480
|
-
const advanced = advanceDiagnosticRouting(loaded.manifest, metadata2.diagnostic.ledger, {
|
|
130481
|
-
routingNodeKey: input.routingNodeKey,
|
|
130482
|
-
itemIdentifier: input.itemIdentifier,
|
|
130483
|
-
isCorrect: scoring.isCorrect
|
|
130484
|
-
});
|
|
130485
|
-
if (!advanced.ok) {
|
|
130486
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The diagnostic routing transition was rejected.", { failure: advanced.failure }));
|
|
130487
|
-
}
|
|
130488
|
-
const submittedAt = new Date().toISOString();
|
|
130489
|
-
const responseVersion = metadata2.responseVersion + 1;
|
|
130490
|
-
const routingSnapshot = this.diagnosticRoutingSnapshot(metadata2, advanced.state);
|
|
130491
|
-
const routingSnapshots = metadata2.diagnostic.routingSnapshots ? [...metadata2.diagnostic.routingSnapshots, routingSnapshot] : undefined;
|
|
130492
|
-
const submission = {
|
|
130493
|
-
submissionId: input.submissionId,
|
|
130494
|
-
itemIdentifier: input.itemIdentifier,
|
|
130495
|
-
submittedAt,
|
|
130496
|
-
responseVersion,
|
|
130497
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0,
|
|
130498
|
-
score: scoring.score,
|
|
130499
|
-
isCorrect: scoring.isCorrect,
|
|
130500
|
-
grading: scoring.grading
|
|
130501
|
-
};
|
|
130502
|
-
const nextMetadata = {
|
|
130503
|
-
...metadata2,
|
|
130504
|
-
responses,
|
|
130505
|
-
responseVersion,
|
|
130506
|
-
itemSubmissions: [...metadata2.itemSubmissions, submission],
|
|
130507
|
-
updatedAt: submittedAt,
|
|
130508
|
-
diagnostic: {
|
|
130509
|
-
...metadata2.diagnostic,
|
|
130510
|
-
ledger: [...metadata2.diagnostic.ledger, advanced.ledgerEntry],
|
|
130511
|
-
...routingSnapshots ? { routingSnapshots } : {},
|
|
130512
|
-
state: advanced.state
|
|
130513
|
-
}
|
|
130514
|
-
};
|
|
130515
|
-
const child = await this.prepareDiagnosticAssessmentChildResult({
|
|
130516
|
-
attempt,
|
|
130517
|
-
metadata: nextMetadata,
|
|
130518
|
-
submission,
|
|
130519
|
-
transition: advanced.ledgerEntry
|
|
130520
|
-
});
|
|
130521
|
-
await this.putResultMetadata(attempt.result, nextMetadata);
|
|
130522
|
-
return {
|
|
130523
|
-
action: "complete",
|
|
130524
|
-
response: {
|
|
130525
|
-
attemptId: attempt.result.sourcedId,
|
|
130526
|
-
responseVersion,
|
|
130527
|
-
status: "in_progress",
|
|
130528
|
-
routing: routingSnapshot,
|
|
130529
|
-
submission: diagnosticReceipt(submission, advanced.ledgerEntry)
|
|
130530
|
-
},
|
|
130531
|
-
checkpoint: child,
|
|
130532
|
-
projection: {
|
|
130533
|
-
result: attempt.result,
|
|
130534
|
-
metadata: nextMetadata,
|
|
130535
|
-
ledgerEntry: advanced.ledgerEntry,
|
|
130536
|
-
integration: attempt.integration
|
|
130537
|
-
}
|
|
130538
|
-
};
|
|
130539
|
-
});
|
|
130540
|
-
if (committed.action === "prepare") {
|
|
130541
|
-
preparationAttempts += 1;
|
|
130542
|
-
preview = await this.scoreDiagnosticItemSubmission(params, input);
|
|
130543
|
-
} else {
|
|
130544
|
-
this.projectDiagnosticItemResponse(committed.projection, preview?.assessment, committed.checkpoint);
|
|
130545
|
-
return committed.response;
|
|
130546
|
-
}
|
|
130547
|
-
}
|
|
130353
|
+
const { context: context2, transcript, submissionId } = prepareAssessmentProgress(await this.deps.verifyAssessmentToken(input.assessmentToken), params, input);
|
|
130354
|
+
const graded = await this.gradeTranscript(context2, transcript);
|
|
130355
|
+
await this.reconcileTranscriptChildren(context2, graded, new Set([submissionId]));
|
|
130356
|
+
return { attemptId: params.attemptId };
|
|
130548
130357
|
}
|
|
130549
|
-
async
|
|
130358
|
+
async submitItem({
|
|
130359
|
+
input,
|
|
130360
|
+
...params
|
|
130361
|
+
}) {
|
|
130362
|
+
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
130363
|
+
const context2 = requireAssessmentFeedbackContext(await this.deps.verifyAssessmentToken(input.assessmentToken), params);
|
|
130364
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
130365
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
130366
|
+
const appended = prepareAssessmentItemTranscript(assessment, { ...input, transcript });
|
|
130367
|
+
const graded = await this.gradeTranscript(context2, appended.transcript);
|
|
130368
|
+
const submission = graded.metadata.itemSubmissions[appended.index];
|
|
130369
|
+
this.reconcileTranscriptChildren(context2, graded, new Set([submission.submissionId])).catch((error88) => this.recordTranscriptFailure("child_projection", params.attemptId, error88));
|
|
130370
|
+
return buildAssessmentTranscriptFeedback(params.attemptId, graded, appended.index);
|
|
130371
|
+
}
|
|
130372
|
+
async gradeTranscript(context2, transcript, terminal = false, db2 = this.deps.db) {
|
|
130373
|
+
const startedAt = Date.now();
|
|
130550
130374
|
try {
|
|
130551
|
-
|
|
130552
|
-
|
|
130553
|
-
|
|
130554
|
-
|
|
130555
|
-
|
|
130556
|
-
|
|
130557
|
-
|
|
130558
|
-
|
|
130559
|
-
|
|
130560
|
-
|
|
130561
|
-
|
|
130562
|
-
|
|
130563
|
-
|
|
130564
|
-
|
|
130565
|
-
|
|
130566
|
-
|
|
130567
|
-
|
|
130568
|
-
|
|
130569
|
-
|
|
130570
|
-
|
|
130571
|
-
const artifactIdentity = assessmentArtifactIdentityFromAttempt(attempt.metadata);
|
|
130572
|
-
const scoringArtifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
130573
|
-
const loaded = await this.loadAttemptDiagnosticRouting(attempt.metadata, assessment, this.deps.db, scoringArtifact);
|
|
130574
|
-
const expected = loaded.state.next;
|
|
130575
|
-
let scoring = null;
|
|
130576
|
-
if (loaded.state.status === "in-progress" && expected?.nodeKey === input.routingNodeKey && expected.itemIdentifier === input.itemIdentifier && attempt.metadata.responseVersion === input.expectedResponseVersion) {
|
|
130577
|
-
const responses = prepareDiagnosticAssessmentResponses(assessment, attempt.metadata.responses, input);
|
|
130578
|
-
scoring = await this.scoreItem({
|
|
130375
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
130376
|
+
const prepared = prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal);
|
|
130377
|
+
const artifactIdentity = assessmentArtifactIdentityFromAttempt(context2.metadata);
|
|
130378
|
+
const artifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
130379
|
+
const [itemResults, routing] = await Promise.all([
|
|
130380
|
+
scoreCachedAssessmentItems({
|
|
130381
|
+
assessment,
|
|
130382
|
+
responses: prepared.responses,
|
|
130383
|
+
artifactIdentity,
|
|
130384
|
+
purpose: context2.metadata.purpose,
|
|
130385
|
+
itemIdentifiers: new Set(prepared.transcript.administrations.map((entry2) => entry2.itemIdentifier)),
|
|
130386
|
+
artifact,
|
|
130387
|
+
cache: this.itemScoreCache,
|
|
130388
|
+
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
130389
|
+
onEvent: addEvent
|
|
130390
|
+
}),
|
|
130391
|
+
this.isPlatformRoutedDiagnosticMetadata(context2.metadata) ? this.loadAttemptDiagnosticRouting(context2.metadata, assessment, db2, artifact) : undefined
|
|
130392
|
+
]);
|
|
130393
|
+
const graded = buildGradedAssessmentTranscript({
|
|
130394
|
+
context: context2,
|
|
130579
130395
|
assessment,
|
|
130580
|
-
|
|
130581
|
-
|
|
130582
|
-
|
|
130583
|
-
|
|
130584
|
-
|
|
130396
|
+
prepared,
|
|
130397
|
+
itemResults,
|
|
130398
|
+
submittedAt: new Date().toISOString(),
|
|
130399
|
+
manifest: routing?.manifest,
|
|
130400
|
+
terminal
|
|
130401
|
+
});
|
|
130402
|
+
addEvent("assessment.transcript_validated", {
|
|
130403
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
130404
|
+
"app.assessment.administration_count": graded.metadata.responseVersion,
|
|
130405
|
+
"app.assessment.duration_ms": Date.now() - startedAt
|
|
130585
130406
|
});
|
|
130586
|
-
|
|
130587
|
-
return {
|
|
130588
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
130589
|
-
submissionId: input.submissionId,
|
|
130590
|
-
routingNodeKey: input.routingNodeKey,
|
|
130591
|
-
itemIdentifier: input.itemIdentifier,
|
|
130592
|
-
assessment,
|
|
130593
|
-
scoring
|
|
130594
|
-
};
|
|
130595
|
-
}
|
|
130596
|
-
async scoreItems({
|
|
130597
|
-
preparedArtifact,
|
|
130598
|
-
...input
|
|
130599
|
-
}) {
|
|
130600
|
-
const artifact = preparedArtifact === undefined ? await this.artifactLoader.loadMatchedScoring(input.assessment, input.artifactIdentity) : preparedArtifact;
|
|
130601
|
-
return scoreAssessmentItems({
|
|
130602
|
-
...input,
|
|
130603
|
-
artifact,
|
|
130604
|
-
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
130605
|
-
onEvent: addEvent
|
|
130606
|
-
});
|
|
130607
|
-
}
|
|
130608
|
-
async scoreItem(input) {
|
|
130609
|
-
const [scoring] = await this.scoreItems({
|
|
130610
|
-
assessment: input.assessment,
|
|
130611
|
-
responses: input.responses,
|
|
130612
|
-
artifactIdentity: input.artifactIdentity,
|
|
130613
|
-
purpose: input.purpose,
|
|
130614
|
-
itemIdentifiers: new Set([input.itemIdentifier]),
|
|
130615
|
-
preparedArtifact: input.preparedArtifact
|
|
130616
|
-
});
|
|
130617
|
-
return scoring;
|
|
130618
|
-
}
|
|
130619
|
-
async prepareItemSubmission(params, input) {
|
|
130620
|
-
try {
|
|
130621
|
-
return await this.scoreItemSubmission(params, input);
|
|
130407
|
+
return graded;
|
|
130622
130408
|
} catch (error88) {
|
|
130623
|
-
|
|
130624
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
130625
|
-
"exception.type": errorType(error88),
|
|
130626
|
-
"app.error.message": errorMessage2(error88)
|
|
130627
|
-
});
|
|
130628
|
-
return null;
|
|
130629
|
-
}
|
|
130630
|
-
}
|
|
130631
|
-
async scoreItemSubmission(params, input) {
|
|
130632
|
-
const attempt = await this.peekAttempt(params);
|
|
130633
|
-
const prior = attempt.metadata.itemSubmissions.find((submission) => submission.submissionId === input.submissionId);
|
|
130634
|
-
if (prior || !this.isOpen(attempt.result)) {
|
|
130635
|
-
return null;
|
|
130409
|
+
throw assessmentTranscriptRuntimeError(error88);
|
|
130636
130410
|
}
|
|
130637
|
-
const assessment = await this.loadAttemptAssessment(attempt.metadata);
|
|
130638
|
-
const transition = prepareAssessmentItemSubmission({
|
|
130639
|
-
purpose: attempt.metadata.purpose,
|
|
130640
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
130641
|
-
responses: attempt.metadata.responses,
|
|
130642
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
130643
|
-
assessment
|
|
130644
|
-
}, input);
|
|
130645
|
-
const scoring = transition.action === "commit" ? await this.scoreItem({
|
|
130646
|
-
assessment,
|
|
130647
|
-
responses: transition.responses,
|
|
130648
|
-
itemIdentifier: input.itemIdentifier,
|
|
130649
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
130650
|
-
purpose: attempt.metadata.purpose
|
|
130651
|
-
}) : null;
|
|
130652
|
-
return {
|
|
130653
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
130654
|
-
submissionId: input.submissionId,
|
|
130655
|
-
itemIdentifier: input.itemIdentifier,
|
|
130656
|
-
assessment,
|
|
130657
|
-
scoring
|
|
130658
|
-
};
|
|
130659
130411
|
}
|
|
130660
|
-
|
|
130661
|
-
|
|
130662
|
-
|
|
130663
|
-
|
|
130664
|
-
|
|
130665
|
-
|
|
130666
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
130667
|
-
purpose: attempt.metadata.purpose
|
|
130668
|
-
});
|
|
130669
|
-
return {
|
|
130670
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
130671
|
-
assessment,
|
|
130672
|
-
itemResults,
|
|
130673
|
-
score: aggregateAssessmentScore(itemResults.map((result) => result.score.earned), itemResults.reduce((total, result) => total + result.score.possible, 0)),
|
|
130674
|
-
correctQuestions: countCorrectQuestions(itemResults.map((result) => result.isCorrect))
|
|
130675
|
-
};
|
|
130676
|
-
}
|
|
130677
|
-
async peekAttempt({
|
|
130678
|
-
studentId,
|
|
130679
|
-
attemptId
|
|
130680
|
-
}) {
|
|
130681
|
-
const result = await this.requireClient().api.oneroster.assessmentResults.get(attemptId).catch((error88) => {
|
|
130682
|
-
if (isApiError(error88) && error88.statusCode === 404) {
|
|
130683
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
130684
|
-
}
|
|
130685
|
-
throw error88;
|
|
130412
|
+
recordTranscriptFailure(operation, attemptId, error88) {
|
|
130413
|
+
addEvent("assessment.transcript_operation_failed", {
|
|
130414
|
+
"app.assessment.operation": operation,
|
|
130415
|
+
"app.assessment.attempt_id": attemptId,
|
|
130416
|
+
"exception.type": errorType(error88),
|
|
130417
|
+
"app.error.message": errorMessage2(error88)
|
|
130686
130418
|
});
|
|
130687
|
-
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
130688
|
-
if (!metadata2 || result.student.sourcedId !== studentId || result.status !== ONEROSTER_STATUS2.active) {
|
|
130689
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
130690
|
-
}
|
|
130691
|
-
return { result, metadata: metadata2 };
|
|
130692
|
-
}
|
|
130693
|
-
async prepareSubmission(params, input) {
|
|
130694
|
-
try {
|
|
130695
|
-
const attempt = await this.peekAttempt(params);
|
|
130696
|
-
const disposition = classifyAssessmentSubmission({
|
|
130697
|
-
inProgress: attempt.result.inProgress ?? "",
|
|
130698
|
-
scoreStatus: attempt.result.scoreStatus,
|
|
130699
|
-
submissionId: attempt.metadata.submissionId
|
|
130700
|
-
}, input.submissionId);
|
|
130701
|
-
if (disposition !== "submit") {
|
|
130702
|
-
return null;
|
|
130703
|
-
}
|
|
130704
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
130705
|
-
return null;
|
|
130706
|
-
}
|
|
130707
|
-
return await this.scoreSubmission(attempt);
|
|
130708
|
-
} catch (error88) {
|
|
130709
|
-
addEvent("assessment.submission_preview_failed", {
|
|
130710
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
130711
|
-
"exception.type": errorType(error88),
|
|
130712
|
-
"app.error.message": errorMessage2(error88)
|
|
130713
|
-
});
|
|
130714
|
-
return null;
|
|
130715
|
-
}
|
|
130716
130419
|
}
|
|
130717
130420
|
async submit({
|
|
130718
130421
|
input,
|
|
@@ -130720,121 +130423,83 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130720
130423
|
...params
|
|
130721
130424
|
}) {
|
|
130722
130425
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
130723
|
-
const
|
|
130724
|
-
const
|
|
130725
|
-
|
|
130726
|
-
|
|
130727
|
-
|
|
130728
|
-
|
|
130729
|
-
|
|
130730
|
-
|
|
130731
|
-
|
|
130426
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
130427
|
+
const request = { ...input, transcript };
|
|
130428
|
+
const requestHash = await assessmentTranscriptRequestHash(request);
|
|
130429
|
+
const accepted = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
130430
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
130431
|
+
db: db2,
|
|
130432
|
+
developerAccessValidated: true,
|
|
130433
|
+
includeEnrollment: false
|
|
130434
|
+
});
|
|
130435
|
+
const disposition = resolveAssessmentTranscriptSubmission(attempt.result, attempt.metadata, attempt.integration.status, request, requestHash);
|
|
130436
|
+
if (disposition === "replay") {
|
|
130732
130437
|
return {
|
|
130733
|
-
response: this.submittedResult(
|
|
130734
|
-
emission: this.replayCompletion(
|
|
130438
|
+
response: this.submittedResult(attempt.result, attempt.metadata),
|
|
130439
|
+
emission: this.replayCompletion(attempt, input.submissionId, params, game2)
|
|
130735
130440
|
};
|
|
130736
130441
|
}
|
|
130737
|
-
const
|
|
130738
|
-
|
|
130739
|
-
|
|
130740
|
-
}
|
|
130741
|
-
|
|
130742
|
-
|
|
130743
|
-
|
|
130744
|
-
|
|
130745
|
-
|
|
130746
|
-
|
|
130747
|
-
attempt: { ...attempt, metadata: attempt.metadata },
|
|
130748
|
-
input,
|
|
130749
|
-
params,
|
|
130750
|
-
game: game2,
|
|
130751
|
-
db: db2
|
|
130752
|
-
});
|
|
130753
|
-
}
|
|
130754
|
-
if (assessmentFlowForPurpose(attempt.metadata.purpose) === "item-submit") {
|
|
130755
|
-
const assessment2 = await this.loadAttemptAssessment(attempt.metadata);
|
|
130756
|
-
const flowFailure = assessmentCompletionFlowFailure(attempt.metadata.purpose, attempt.metadata.itemSubmissions, assessment2.items.length);
|
|
130757
|
-
if (flowFailure) {
|
|
130758
|
-
throw AssessmentRuntimeError.from(flowFailure);
|
|
130759
|
-
}
|
|
130760
|
-
}
|
|
130761
|
-
const scored = preview && preview.responseVersion === attempt.metadata.responseVersion ? preview : await this.scoreSubmission(attempt);
|
|
130762
|
-
const { assessment, itemResults, score, correctQuestions } = scored;
|
|
130763
|
-
const client2 = this.requireClient();
|
|
130442
|
+
const graded = await this.gradeTranscript({
|
|
130443
|
+
attemptId: params.attemptId,
|
|
130444
|
+
metadata: attempt.metadata
|
|
130445
|
+
}, transcript, true, db2);
|
|
130446
|
+
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
130447
|
+
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
130448
|
+
metadata: graded.metadata,
|
|
130449
|
+
itemResults: graded.itemResults
|
|
130450
|
+
}) : undefined;
|
|
130451
|
+
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
130764
130452
|
const timestamp6 = new Date().toISOString();
|
|
130765
|
-
const reviewOutcomes = attempt.metadata.purpose === "review" ? this.reviewItemOutcomes({
|
|
130766
|
-
metadata: attempt.metadata,
|
|
130767
|
-
itemResults
|
|
130768
|
-
}) : null;
|
|
130769
130453
|
const finalization = buildAssessmentResultSubmission({
|
|
130770
130454
|
result: attempt.result,
|
|
130771
|
-
metadata:
|
|
130772
|
-
score,
|
|
130455
|
+
metadata: graded.metadata,
|
|
130456
|
+
score: graded.score,
|
|
130773
130457
|
submissionId: input.submissionId,
|
|
130774
130458
|
timestamp: timestamp6,
|
|
130775
|
-
testName: assessment.title,
|
|
130776
|
-
courseName
|
|
130777
|
-
totalQuestions: itemResults.length,
|
|
130778
|
-
correctQuestions,
|
|
130459
|
+
testName: graded.assessment.title,
|
|
130460
|
+
courseName,
|
|
130461
|
+
totalQuestions: graded.itemResults.length,
|
|
130462
|
+
correctQuestions: graded.correctQuestions,
|
|
130779
130463
|
...input.session ? { session: input.session } : {},
|
|
130780
|
-
...
|
|
130464
|
+
...itemOutcomes ? { itemOutcomes } : {},
|
|
130465
|
+
...diagnostic ? { diagnosticTracks: diagnostic.tracks } : {}
|
|
130781
130466
|
});
|
|
130782
|
-
const metadata2 =
|
|
130783
|
-
|
|
130784
|
-
|
|
130785
|
-
|
|
130786
|
-
|
|
130787
|
-
|
|
130788
|
-
|
|
130789
|
-
|
|
130467
|
+
const metadata2 = {
|
|
130468
|
+
...finalization.metadata,
|
|
130469
|
+
completion: { ...finalization.metadata.completion, requestHash }
|
|
130470
|
+
};
|
|
130471
|
+
const resultUpdate = {
|
|
130472
|
+
...finalization.resultUpdate,
|
|
130473
|
+
metadata: mergeAssessmentRuntimeMetadata(attempt.result.metadata, metadata2)
|
|
130474
|
+
};
|
|
130475
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, resultUpdate);
|
|
130476
|
+
const result = { ...attempt.result, ...resultUpdate };
|
|
130477
|
+
const emission = {
|
|
130478
|
+
attempt: { ...attempt, result, metadata: metadata2 },
|
|
130479
|
+
testIdentifier: graded.assessment.identifier,
|
|
130480
|
+
testName: graded.assessment.title,
|
|
130481
|
+
score: graded.score,
|
|
130482
|
+
totalQuestions: graded.itemResults.length,
|
|
130483
|
+
correctQuestions: graded.correctQuestions,
|
|
130484
|
+
submissionId: input.submissionId,
|
|
130485
|
+
gameId: params.gameId,
|
|
130486
|
+
studentId: params.studentId,
|
|
130487
|
+
courseName,
|
|
130488
|
+
game: game2
|
|
130790
130489
|
};
|
|
130791
|
-
addEvent("assessment.awaiting_award_created", {
|
|
130792
|
-
"app.assessment.attempt_id": submittedForEmission.sourcedId,
|
|
130793
|
-
"app.assessment.submission_id": input.submissionId,
|
|
130794
|
-
"app.assessment.purpose": metadata2.purpose,
|
|
130795
|
-
"app.assessment.response_version": metadata2.responseVersion
|
|
130796
|
-
});
|
|
130797
130490
|
return {
|
|
130798
|
-
response:
|
|
130799
|
-
|
|
130800
|
-
|
|
130801
|
-
responseVersion: metadata2.responseVersion,
|
|
130802
|
-
status: "awaiting_award",
|
|
130803
|
-
responses: metadata2.responses,
|
|
130804
|
-
score,
|
|
130805
|
-
...metadata2.purpose === "review" ? {
|
|
130806
|
-
purpose: metadata2.purpose,
|
|
130807
|
-
itemOutcomes: reviewOutcomes
|
|
130808
|
-
} : { purpose: metadata2.purpose }
|
|
130809
|
-
},
|
|
130810
|
-
emission: {
|
|
130811
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
130812
|
-
testIdentifier: assessment.identifier,
|
|
130813
|
-
testName: assessment.title,
|
|
130814
|
-
score,
|
|
130815
|
-
totalQuestions: itemResults.length,
|
|
130816
|
-
correctQuestions,
|
|
130817
|
-
submissionId: input.submissionId,
|
|
130818
|
-
gameId: params.gameId,
|
|
130819
|
-
studentId: params.studentId,
|
|
130820
|
-
courseName: attempt.enrollment.course.title,
|
|
130821
|
-
game: game2
|
|
130822
|
-
}
|
|
130491
|
+
response: this.submittedResult(result, metadata2),
|
|
130492
|
+
emission,
|
|
130493
|
+
repair: { context: this.tokenContext(attempt), graded }
|
|
130823
130494
|
};
|
|
130824
130495
|
});
|
|
130825
|
-
if (
|
|
130826
|
-
|
|
130827
|
-
|
|
130828
|
-
|
|
130829
|
-
|
|
130830
|
-
const { result, metadata: metadata2 } = submission.emission.attempt;
|
|
130831
|
-
await Promise.allSettled([
|
|
130832
|
-
this.emitSessionBestEffort(submission.emission),
|
|
130833
|
-
...diagnosticCompletion ? [this.restoreDiagnosticChildResults(diagnosticCompletion)] : [],
|
|
130834
|
-
...metadata2.purpose === "review" ? [this.restoreAllCompletedReviewChildResponses(result, metadata2)] : []
|
|
130835
|
-
]);
|
|
130496
|
+
if (accepted.repair) {
|
|
130497
|
+
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
130498
|
+
}
|
|
130499
|
+
if (accepted.emission) {
|
|
130500
|
+
this.emitSessionBestEffort(accepted.emission);
|
|
130836
130501
|
}
|
|
130837
|
-
return
|
|
130502
|
+
return accepted.response;
|
|
130838
130503
|
}
|
|
130839
130504
|
async finalize({
|
|
130840
130505
|
input,
|
|
@@ -130978,58 +130643,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
130978
130643
|
});
|
|
130979
130644
|
}
|
|
130980
130645
|
}
|
|
130981
|
-
async finalizeDiagnosticSubmission(input) {
|
|
130982
|
-
const { attempt } = input;
|
|
130983
|
-
const routing = await this.loadAttemptDiagnosticManifest(attempt.metadata, input.db);
|
|
130984
|
-
const { tracks, score, correctQuestions } = summarizeDiagnosticSubmission(attempt.metadata, routing.manifest);
|
|
130985
|
-
const timestamp6 = new Date().toISOString();
|
|
130986
|
-
const finalization = buildAssessmentResultSubmission({
|
|
130987
|
-
result: attempt.result,
|
|
130988
|
-
metadata: attempt.metadata,
|
|
130989
|
-
score,
|
|
130990
|
-
submissionId: input.input.submissionId,
|
|
130991
|
-
timestamp: timestamp6,
|
|
130992
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
130993
|
-
courseName: attempt.enrollment.course.title,
|
|
130994
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
130995
|
-
correctQuestions,
|
|
130996
|
-
...input.input.session ? { session: input.input.session } : {},
|
|
130997
|
-
diagnosticTracks: tracks
|
|
130998
|
-
});
|
|
130999
|
-
const metadata2 = finalization.metadata;
|
|
131000
|
-
const submitted = await this.requireClient().api.oneroster.assessmentResults.upsert(attempt.result.sourcedId, finalization.resultUpdate);
|
|
131001
|
-
const submittedForEmission = {
|
|
131002
|
-
...submitted,
|
|
131003
|
-
score: score.normalized * 100,
|
|
131004
|
-
scoreDate: timestamp6,
|
|
131005
|
-
...ASSESSMENT_ATTEMPT_AWAITING_AWARD,
|
|
131006
|
-
metadata: finalization.resultUpdate.metadata
|
|
131007
|
-
};
|
|
131008
|
-
return {
|
|
131009
|
-
response: {
|
|
131010
|
-
purpose: "diagnostic",
|
|
131011
|
-
attemptId: submittedForEmission.sourcedId,
|
|
131012
|
-
submissionId: input.input.submissionId,
|
|
131013
|
-
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
131014
|
-
responseVersion: metadata2.responseVersion,
|
|
131015
|
-
status: "awaiting_award",
|
|
131016
|
-
tracks
|
|
131017
|
-
},
|
|
131018
|
-
emission: {
|
|
131019
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
131020
|
-
testIdentifier: attempt.metadata.selectedTest.identifier,
|
|
131021
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
131022
|
-
score,
|
|
131023
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
131024
|
-
correctQuestions,
|
|
131025
|
-
submissionId: input.input.submissionId,
|
|
131026
|
-
gameId: input.params.gameId,
|
|
131027
|
-
studentId: input.params.studentId,
|
|
131028
|
-
courseName: attempt.enrollment.course.title,
|
|
131029
|
-
game: input.game
|
|
131030
|
-
}
|
|
131031
|
-
};
|
|
131032
|
-
}
|
|
131033
130646
|
async exportFixture({
|
|
131034
130647
|
gameId,
|
|
131035
130648
|
user
|
|
@@ -131229,14 +130842,32 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
131229
130842
|
async listAttempts(scope) {
|
|
131230
130843
|
return this.attemptListing(await this.studentAssessmentResults(scope.studentId), scope);
|
|
131231
130844
|
}
|
|
131232
|
-
attemptListing(results, scope) {
|
|
130845
|
+
async attemptListing(results, scope) {
|
|
131233
130846
|
const attempts = new Map;
|
|
131234
130847
|
const reviewChildren = new Map;
|
|
131235
130848
|
for (const result of results) {
|
|
131236
130849
|
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
131237
|
-
|
|
131238
|
-
|
|
131239
|
-
|
|
130850
|
+
if (scope.purpose === "review") {
|
|
130851
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
130852
|
+
if (verified.ok && verified.command.payload.context.kind === "review") {
|
|
130853
|
+
const { command } = verified;
|
|
130854
|
+
const { attempt, administration } = command.payload;
|
|
130855
|
+
const parent = await reviewQuestionLineItemId(await deterministicUUID([
|
|
130856
|
+
"playcademy:review-bank-line-item:v1",
|
|
130857
|
+
scope.integrationId,
|
|
130858
|
+
scope.activityId,
|
|
130859
|
+
attempt.selectedTest.identifier,
|
|
130860
|
+
verified.command.payload.context.bankRevision
|
|
130861
|
+
].join("\x00")), administration.itemIdentifier);
|
|
130862
|
+
const projection = await classifyAssessmentChildResult({
|
|
130863
|
+
result,
|
|
130864
|
+
command,
|
|
130865
|
+
childLineItemId: parent
|
|
130866
|
+
});
|
|
130867
|
+
if (attempt.activityId === scope.activityId && attempt.integrationId === scope.integrationId && attempt.enrollmentId === scope.enrollmentId && attempt.courseId === scope.courseId && attempt.studentId === scope.studentId && result.sourcedId === await assessmentChildResultId(attempt.attemptId, administration.submissionId) && projection.outcome === "replay") {
|
|
130868
|
+
reviewChildren.set(result.sourcedId, command);
|
|
130869
|
+
}
|
|
130870
|
+
}
|
|
131240
130871
|
}
|
|
131241
130872
|
if (metadata2 && metadata2.activityId === scope.activityId && metadata2.purpose === scope.purpose && metadata2.courseId === scope.courseId && metadata2.integrationId === scope.integrationId && result.student.sourcedId === scope.studentId) {
|
|
131242
130873
|
const selectedAssessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
@@ -131287,19 +130918,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
131287
130918
|
}
|
|
131288
130919
|
return true;
|
|
131289
130920
|
}
|
|
131290
|
-
reviewExposures(results, scope) {
|
|
131291
|
-
const exposures = [];
|
|
131292
|
-
for (const result of results) {
|
|
131293
|
-
const metadata2 = playcademyAssessmentItemResultMetadata(result.metadata);
|
|
131294
|
-
if (metadata2?.bankRevision === scope.bankRevision) {
|
|
131295
|
-
exposures.push({
|
|
131296
|
-
itemIdentifier: metadata2.itemIdentifier,
|
|
131297
|
-
deliveredAt: metadata2.deliveredAt
|
|
131298
|
-
});
|
|
131299
|
-
}
|
|
131300
|
-
}
|
|
131301
|
-
return exposures;
|
|
131302
|
-
}
|
|
131303
130921
|
requireDecision(decision, purpose) {
|
|
131304
130922
|
if (!decision) {
|
|
131305
130923
|
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(purpose));
|
|
@@ -131316,29 +130934,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
131316
130934
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
131317
130935
|
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
131318
130936
|
}
|
|
131319
|
-
async resumeDiagnosticSnapshot(decision, attempts, integration,
|
|
131320
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
131321
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
131322
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
131323
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
131324
|
-
});
|
|
131325
|
-
}
|
|
130937
|
+
async resumeDiagnosticSnapshot(decision, attempts, integration, _db = this.deps.db) {
|
|
131326
130938
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
131327
|
-
|
|
131328
|
-
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
131329
|
-
}
|
|
131330
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(resumed.metadata)) {
|
|
131331
|
-
throw new Error("A keyed diagnostic request selected a legacy fixed attempt");
|
|
131332
|
-
}
|
|
131333
|
-
const assessment = await this.loadAttemptAssessment(resumed.metadata);
|
|
131334
|
-
const routing = await this.loadAttemptDiagnosticRouting(resumed.metadata, assessment, db2);
|
|
131335
|
-
await this.restoreDiagnosticChildResults({
|
|
131336
|
-
result: resumed.result,
|
|
131337
|
-
metadata: resumed.metadata,
|
|
131338
|
-
integration,
|
|
131339
|
-
assessment
|
|
131340
|
-
});
|
|
131341
|
-
return this.diagnosticSnapshot(resumed.result, resumed.metadata, assessment, integration, routing.state);
|
|
130939
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
131342
130940
|
}
|
|
131343
130941
|
async resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, resume) {
|
|
131344
130942
|
const select3 = () => selectRuntimeAssessment(tests, [...this.attemptsMatchingRequest(attempts, requestFingerprint).values()].map((entry2) => entry2.selection));
|
|
@@ -131409,42 +131007,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
131409
131007
|
});
|
|
131410
131008
|
}
|
|
131411
131009
|
}
|
|
131412
|
-
async resumeReviewSnapshot(decision, attempts,
|
|
131413
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
131414
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
131415
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
131416
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
131417
|
-
});
|
|
131418
|
-
}
|
|
131010
|
+
async resumeReviewSnapshot(decision, attempts, context2) {
|
|
131419
131011
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
131420
|
-
|
|
131421
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
131422
|
-
}
|
|
131423
|
-
const reviewMetadata = resumed.metadata;
|
|
131424
|
-
let assessment;
|
|
131425
|
-
const current = await reconcileReviewChildResults({
|
|
131426
|
-
initial: resumed,
|
|
131427
|
-
writeOpen: async () => {
|
|
131428
|
-
const source = await this.loadPinnedReviewBankSource(reviewMetadata);
|
|
131429
|
-
assessment = projectReviewAssessment(source.assessment, source.bank, reviewMetadata.review.selections);
|
|
131430
|
-
await this.ensureReviewChildResults({
|
|
131431
|
-
result: resumed.result,
|
|
131432
|
-
metadata: reviewMetadata,
|
|
131433
|
-
integration: context2.integration,
|
|
131434
|
-
source,
|
|
131435
|
-
assessment,
|
|
131436
|
-
existingChildResults,
|
|
131437
|
-
lookupMissingResults: true
|
|
131438
|
-
});
|
|
131439
|
-
},
|
|
131440
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, resumed.result.sourcedId),
|
|
131441
|
-
readLatest: () => this.peekAttempt({
|
|
131442
|
-
studentId: resumed.result.student.sourcedId,
|
|
131443
|
-
attemptId: resumed.result.sourcedId
|
|
131444
|
-
}),
|
|
131445
|
-
restoreTerminal: (latest) => this.restoreAllCompletedReviewChildResponses(latest.result, latest.metadata)
|
|
131446
|
-
});
|
|
131447
|
-
return assessmentChildProjectionIsTerminal(current.result) ? this.snapshotForResult(current.result, current.metadata, context2.integration) : this.snapshot(current.result, current.metadata, assessment, context2.integration);
|
|
131012
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, context2.integration);
|
|
131448
131013
|
}
|
|
131449
131014
|
recordReviewPreparationPhase(phase, startedAt, counts = {}) {
|
|
131450
131015
|
addEvent("assessment.review_preparation_phase", {
|
|
@@ -131852,135 +131417,82 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
131852
131417
|
}
|
|
131853
131418
|
return lineItemId;
|
|
131854
131419
|
}
|
|
131855
|
-
|
|
131856
|
-
const { metadata: metadata2 } = attempt;
|
|
131857
|
-
const assessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
131858
|
-
if (!assessmentKey) {
|
|
131859
|
-
addEvent("assessment.child_result_legacy_attempt_skipped", {
|
|
131860
|
-
"app.assessment.attempt_id": attempt.result.sourcedId
|
|
131861
|
-
});
|
|
131862
|
-
return null;
|
|
131863
|
-
}
|
|
131420
|
+
tokenContext(attempt) {
|
|
131864
131421
|
return {
|
|
131865
|
-
|
|
131422
|
+
version: 1,
|
|
131866
131423
|
gameId: attempt.integration.gameId,
|
|
131867
131424
|
studentId: attempt.result.student.sourcedId,
|
|
131868
|
-
|
|
131869
|
-
|
|
131870
|
-
|
|
131871
|
-
enrollmentId: metadata2.enrollmentId,
|
|
131872
|
-
selectedTest: {
|
|
131873
|
-
assessmentKey,
|
|
131874
|
-
identifier: metadata2.selectedTest.identifier,
|
|
131875
|
-
contentRevision: metadata2.selectedTest.contentRevision
|
|
131876
|
-
}
|
|
131877
|
-
};
|
|
131878
|
-
}
|
|
131879
|
-
reviewChildResultContext(metadata2, submission) {
|
|
131880
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === submission.itemIdentifier);
|
|
131881
|
-
if (!selection) {
|
|
131882
|
-
throw new Error(`Committed review item ${submission.itemIdentifier} is not in its pinned selection`);
|
|
131883
|
-
}
|
|
131884
|
-
return {
|
|
131885
|
-
kind: "review",
|
|
131886
|
-
bankRevision: metadata2.review.bankRevision,
|
|
131887
|
-
standard: selection.standard
|
|
131425
|
+
attemptId: attempt.result.sourcedId,
|
|
131426
|
+
parentLineItemId: attempt.result.assessmentLineItem.sourcedId,
|
|
131427
|
+
metadata: assessmentTokenMetadata(attempt.metadata)
|
|
131888
131428
|
};
|
|
131889
131429
|
}
|
|
131890
|
-
|
|
131891
|
-
|
|
131892
|
-
|
|
131893
|
-
|
|
131894
|
-
|
|
131895
|
-
|
|
131430
|
+
async ensureTranscriptLineItems(context2, assessment) {
|
|
131431
|
+
await runWithConcurrency(assessment.items, TimebackAssessmentRuntimeService2.SCORING_CONCURRENCY, async (item) => {
|
|
131432
|
+
const id = await assessmentTranscriptChildLineItemId(context2, item.identifier);
|
|
131433
|
+
if (this.childLineItemCache.get(id)) {
|
|
131434
|
+
return;
|
|
131435
|
+
}
|
|
131436
|
+
const metadata2 = context2.metadata;
|
|
131437
|
+
const common2 = {
|
|
131438
|
+
parentLineItemId: context2.parentLineItemId,
|
|
131439
|
+
integration: { courseId: metadata2.courseId },
|
|
131440
|
+
item
|
|
131441
|
+
};
|
|
131442
|
+
if (metadata2.purpose === "review") {
|
|
131443
|
+
const standard = metadata2.review.selections.find((selection) => selection.itemIdentifier === item.identifier)?.standard;
|
|
131444
|
+
if (!standard) {
|
|
131445
|
+
throw new Error("Review candidate is not in the pinned selection");
|
|
131446
|
+
}
|
|
131447
|
+
await this.ensureReviewQuestionLineItem({
|
|
131448
|
+
...common2,
|
|
131449
|
+
bankRevision: metadata2.review.bankRevision,
|
|
131450
|
+
standards: [standard]
|
|
131451
|
+
});
|
|
131452
|
+
} else if (this.isPlatformRoutedDiagnosticMetadata(metadata2)) {
|
|
131453
|
+
await this.ensureDiagnosticQuestionLineItem({ ...common2, metadata: metadata2 });
|
|
131454
|
+
} else {
|
|
131455
|
+
const expected = {
|
|
131456
|
+
status: "active",
|
|
131457
|
+
parentAssessmentLineItem: { sourcedId: context2.parentLineItemId },
|
|
131458
|
+
course: { sourcedId: metadata2.courseId },
|
|
131459
|
+
resultValueMin: 0,
|
|
131460
|
+
resultValueMax: item.maxScore,
|
|
131461
|
+
metadata: {
|
|
131462
|
+
playcademyAssessmentQuestionDefinition: {
|
|
131463
|
+
version: 1,
|
|
131464
|
+
itemIdentifier: item.identifier,
|
|
131465
|
+
contentRevision: metadata2.selectedTest.contentRevision
|
|
131466
|
+
}
|
|
131467
|
+
}
|
|
131468
|
+
};
|
|
131469
|
+
const store = this.requireClient().api.oneroster.assessmentLineItems;
|
|
131470
|
+
try {
|
|
131471
|
+
await store.create({ sourcedId: id, title: item.title, ...expected });
|
|
131472
|
+
} catch (error88) {
|
|
131473
|
+
if (!isApiError(error88) || error88.statusCode !== 409) {
|
|
131474
|
+
throw error88;
|
|
131475
|
+
}
|
|
131476
|
+
this.assertAssessmentChildLineItem(id, await store.get(id), expected);
|
|
131477
|
+
}
|
|
131478
|
+
}
|
|
131479
|
+
this.childLineItemCache.set(id, true);
|
|
131896
131480
|
});
|
|
131897
131481
|
}
|
|
131898
|
-
|
|
131899
|
-
return
|
|
131900
|
-
|
|
131901
|
-
|
|
131902
|
-
|
|
131903
|
-
|
|
131904
|
-
|
|
131905
|
-
|
|
131906
|
-
|
|
131907
|
-
|
|
131908
|
-
|
|
131482
|
+
async reconcileTranscriptChildren(context2, graded, only) {
|
|
131483
|
+
return reconcileAssessmentTranscriptChildren({
|
|
131484
|
+
context: context2,
|
|
131485
|
+
graded,
|
|
131486
|
+
only,
|
|
131487
|
+
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
131488
|
+
ensureLineItems: (assessment) => this.ensureTranscriptLineItems(context2, assessment),
|
|
131489
|
+
onPersisted: (submissionId) => addEvent("assessment.child_result_persisted", {
|
|
131490
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
131491
|
+
"app.assessment.submission_id": submissionId,
|
|
131492
|
+
"app.assessment.child_result_outcome": "upserted"
|
|
131909
131493
|
})
|
|
131910
131494
|
});
|
|
131911
131495
|
}
|
|
131912
|
-
async prepareAssessmentChildResult(input) {
|
|
131913
|
-
try {
|
|
131914
|
-
const attemptContext = this.assessmentChildAttempt({
|
|
131915
|
-
...input.attempt,
|
|
131916
|
-
metadata: input.metadata
|
|
131917
|
-
});
|
|
131918
|
-
if (!attemptContext) {
|
|
131919
|
-
return null;
|
|
131920
|
-
}
|
|
131921
|
-
const parentLineItemId = input.attempt.result.assessmentLineItem.sourcedId;
|
|
131922
|
-
const { itemIdentifier } = input.submission;
|
|
131923
|
-
const context2 = input.context();
|
|
131924
|
-
const [command, childLineItemId] = await Promise.all([
|
|
131925
|
-
buildSubmissionChildResultCommand({
|
|
131926
|
-
attempt: attemptContext,
|
|
131927
|
-
submission: input.submission,
|
|
131928
|
-
responses: input.metadata.responses[itemIdentifier],
|
|
131929
|
-
grading: input.submission.grading ?? QTI_CHILD_GRADING,
|
|
131930
|
-
context: context2
|
|
131931
|
-
}),
|
|
131932
|
-
context2.kind === "review" ? reviewQuestionLineItemId(parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier)
|
|
131933
|
-
]);
|
|
131934
|
-
return { command, childLineItemId };
|
|
131935
|
-
} catch (error88) {
|
|
131936
|
-
addEvent("assessment.child_result_prepare_failed", {
|
|
131937
|
-
"app.assessment.attempt_id": input.attempt.result.sourcedId,
|
|
131938
|
-
"app.assessment.submission_id": input.submission.submissionId,
|
|
131939
|
-
"app.assessment.qti_item_identifier": input.submission.itemIdentifier,
|
|
131940
|
-
"exception.type": errorType(error88),
|
|
131941
|
-
"app.error.message": errorMessage2(error88)
|
|
131942
|
-
});
|
|
131943
|
-
return null;
|
|
131944
|
-
}
|
|
131945
|
-
}
|
|
131946
|
-
async projectPreparedAssessmentChildResult(prepared) {
|
|
131947
|
-
if (!prepared) {
|
|
131948
|
-
return;
|
|
131949
|
-
}
|
|
131950
|
-
const startedAt = Date.now();
|
|
131951
|
-
const identity = {
|
|
131952
|
-
"app.assessment.attempt_id": prepared.command.payload.attempt.attemptId,
|
|
131953
|
-
"app.assessment.submission_id": prepared.command.payload.administration.submissionId
|
|
131954
|
-
};
|
|
131955
|
-
try {
|
|
131956
|
-
const disposition = await persistAssessmentChildResult({
|
|
131957
|
-
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
131958
|
-
command: prepared.command,
|
|
131959
|
-
childLineItemId: prepared.childLineItemId
|
|
131960
|
-
});
|
|
131961
|
-
if (disposition.outcome === "conflict") {
|
|
131962
|
-
addEvent("assessment.child_result_conflict", {
|
|
131963
|
-
...identity,
|
|
131964
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
131965
|
-
"app.assessment.child_result_conflict_reason": disposition.reason
|
|
131966
|
-
});
|
|
131967
|
-
return;
|
|
131968
|
-
}
|
|
131969
|
-
addEvent("assessment.child_result_persisted", {
|
|
131970
|
-
...identity,
|
|
131971
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
131972
|
-
"app.assessment.child_result_outcome": disposition.outcome,
|
|
131973
|
-
"app.assessment.duration_ms": Date.now() - startedAt
|
|
131974
|
-
});
|
|
131975
|
-
} catch (error88) {
|
|
131976
|
-
addEvent("assessment.child_result_persist_failed", {
|
|
131977
|
-
...identity,
|
|
131978
|
-
"app.assessment.duration_ms": Date.now() - startedAt,
|
|
131979
|
-
"exception.type": errorType(error88),
|
|
131980
|
-
"app.error.message": errorMessage2(error88)
|
|
131981
|
-
});
|
|
131982
|
-
}
|
|
131983
|
-
}
|
|
131984
131496
|
assertAssessmentChildLineItem(lineItemId, stored, expected) {
|
|
131985
131497
|
if (assessmentChildLineItemMatches(stored, expected)) {
|
|
131986
131498
|
return;
|
|
@@ -132038,91 +131550,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132038
131550
|
}
|
|
132039
131551
|
return lineItemId;
|
|
132040
131552
|
}
|
|
132041
|
-
async projectDiagnosticItemResponse(projection, assessment, checkpoint = null) {
|
|
132042
|
-
try {
|
|
132043
|
-
const loadedAssessment = assessment ?? await this.loadAttemptAssessment(projection.metadata);
|
|
132044
|
-
const item = loadedAssessment.items.find((candidate) => candidate.identifier === projection.ledgerEntry.itemIdentifier);
|
|
132045
|
-
if (!item) {
|
|
132046
|
-
throw new Error(`Diagnostic assessment is missing item ${projection.ledgerEntry.itemIdentifier}`);
|
|
132047
|
-
}
|
|
132048
|
-
const childLineItemId = await this.ensureDiagnosticQuestionLineItem({
|
|
132049
|
-
parentLineItemId: projection.result.assessmentLineItem.sourcedId,
|
|
132050
|
-
integration: projection.integration,
|
|
132051
|
-
metadata: projection.metadata,
|
|
132052
|
-
item
|
|
132053
|
-
});
|
|
132054
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
132055
|
-
throw new Error("Prepared diagnostic checkpoint resolved a different line item");
|
|
132056
|
-
}
|
|
132057
|
-
const childResultId = await diagnosticQuestionResultId(projection.result.sourcedId, childLineItemId);
|
|
132058
|
-
const finalized = buildFinalizedDiagnosticChildResult({
|
|
132059
|
-
childLineItemId,
|
|
132060
|
-
student: projection.result.student,
|
|
132061
|
-
parentAttemptId: projection.result.sourcedId,
|
|
132062
|
-
metadata: projection.metadata,
|
|
132063
|
-
ledgerEntry: projection.ledgerEntry
|
|
132064
|
-
});
|
|
132065
|
-
await Promise.all([
|
|
132066
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
132067
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate)
|
|
132068
|
-
]);
|
|
132069
|
-
} catch (error88) {
|
|
132070
|
-
addEvent("assessment.diagnostic_child_projection_failed", {
|
|
132071
|
-
"app.assessment.attempt_id": projection.result.sourcedId,
|
|
132072
|
-
"app.assessment.qti_item_identifier": projection.ledgerEntry.itemIdentifier,
|
|
132073
|
-
"exception.type": errorType(error88),
|
|
132074
|
-
"app.error.message": errorMessage2(error88)
|
|
132075
|
-
});
|
|
132076
|
-
}
|
|
132077
|
-
}
|
|
132078
|
-
async restoreDiagnosticChildResults(attempt) {
|
|
132079
|
-
const assessment = attempt.assessment ?? await this.loadAttemptAssessment(attempt.metadata);
|
|
132080
|
-
await runWithConcurrency(attempt.metadata.diagnostic.ledger, TimebackAssessmentRuntimeService2.SCORING_CONCURRENCY, async (ledgerEntry) => {
|
|
132081
|
-
try {
|
|
132082
|
-
const childLineItemId = await diagnosticQuestionLineItemId(attempt.result.assessmentLineItem.sourcedId, ledgerEntry.itemIdentifier);
|
|
132083
|
-
const childResultId = await diagnosticQuestionResultId(attempt.result.sourcedId, childLineItemId);
|
|
132084
|
-
let childResult;
|
|
132085
|
-
try {
|
|
132086
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
132087
|
-
} catch (error88) {
|
|
132088
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
132089
|
-
throw error88;
|
|
132090
|
-
}
|
|
132091
|
-
}
|
|
132092
|
-
const resolution = resolveDiagnosticChildResult({
|
|
132093
|
-
childResult,
|
|
132094
|
-
childLineItemId,
|
|
132095
|
-
parentResult: attempt.result,
|
|
132096
|
-
parentMetadata: attempt.metadata,
|
|
132097
|
-
ledgerEntry
|
|
132098
|
-
});
|
|
132099
|
-
if (resolution.action === "reuse") {
|
|
132100
|
-
return;
|
|
132101
|
-
}
|
|
132102
|
-
if (resolution.action === "restore") {
|
|
132103
|
-
addEvent("assessment.diagnostic_child_result_restore", {
|
|
132104
|
-
"app.assessment.diagnostic_child_restore_reason": resolution.reason,
|
|
132105
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
132106
|
-
"app.assessment.child_result_id": childResultId,
|
|
132107
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier
|
|
132108
|
-
});
|
|
132109
|
-
}
|
|
132110
|
-
await this.projectDiagnosticItemResponse({
|
|
132111
|
-
result: attempt.result,
|
|
132112
|
-
metadata: attempt.metadata,
|
|
132113
|
-
ledgerEntry,
|
|
132114
|
-
integration: attempt.integration
|
|
132115
|
-
}, assessment);
|
|
132116
|
-
} catch (error88) {
|
|
132117
|
-
addEvent("assessment.diagnostic_child_reconciliation_failed", {
|
|
132118
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
132119
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier,
|
|
132120
|
-
"exception.type": errorType(error88),
|
|
132121
|
-
"app.error.message": errorMessage2(error88)
|
|
132122
|
-
});
|
|
132123
|
-
}
|
|
132124
|
-
});
|
|
132125
|
-
}
|
|
132126
131553
|
async ensureReviewQuestionLineItem(input) {
|
|
132127
131554
|
const client2 = this.requireClient();
|
|
132128
131555
|
const lineItemId = await reviewQuestionLineItemId(input.parentLineItemId, input.item.identifier);
|
|
@@ -132169,220 +131596,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132169
131596
|
}
|
|
132170
131597
|
return lineItemId;
|
|
132171
131598
|
}
|
|
132172
|
-
async putOpenReviewChildResult(input) {
|
|
132173
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
132174
|
-
metadata: input.parentMetadata,
|
|
132175
|
-
selection: input.selection,
|
|
132176
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
132177
|
-
});
|
|
132178
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(input.childResultId, buildOpenReviewChildResult({
|
|
132179
|
-
childLineItemId: input.childLineItemId,
|
|
132180
|
-
student: input.parentResult.student,
|
|
132181
|
-
metadata: metadata2
|
|
132182
|
-
}));
|
|
132183
|
-
}
|
|
132184
|
-
async ensureReviewChildResults(input) {
|
|
132185
|
-
const itemByIdentifier = new Map(input.assessment.items.map((item) => [item.identifier, item]));
|
|
132186
|
-
const bankItemByIdentifier = new Map(input.source.bank.items.map((item) => [item.itemIdentifier, item]));
|
|
132187
|
-
const physicalItems = new Set(input.metadata.review.selections.map((selection) => selection.itemIdentifier));
|
|
132188
|
-
if (physicalItems.size !== input.metadata.review.selections.length) {
|
|
132189
|
-
throw new Error("MVP review selection must use each physical item at most once");
|
|
132190
|
-
}
|
|
132191
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
132192
|
-
await runWithConcurrency(input.metadata.review.selections, TimebackAssessmentRuntimeService2.SCORING_CONCURRENCY, async (selection) => {
|
|
132193
|
-
const item = itemByIdentifier.get(selection.itemIdentifier);
|
|
132194
|
-
const indexed = bankItemByIdentifier.get(selection.itemIdentifier);
|
|
132195
|
-
if (!item || !indexed) {
|
|
132196
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
132197
|
-
}
|
|
132198
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
132199
|
-
parentLineItemId: input.result.assessmentLineItem.sourcedId,
|
|
132200
|
-
integration: input.integration,
|
|
132201
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
132202
|
-
standards: [...indexed.standards],
|
|
132203
|
-
item
|
|
132204
|
-
});
|
|
132205
|
-
if (!administeredItems.has(selection.itemIdentifier)) {
|
|
132206
|
-
return;
|
|
132207
|
-
}
|
|
132208
|
-
const childResultId = await reviewQuestionResultId(input.result.sourcedId, childLineItemId);
|
|
132209
|
-
const streamedChildResult = input.existingChildResults.get(childResultId);
|
|
132210
|
-
let childResult = streamedChildResult;
|
|
132211
|
-
if (!childResult && input.lookupMissingResults) {
|
|
132212
|
-
try {
|
|
132213
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
132214
|
-
} catch (error88) {
|
|
132215
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
132216
|
-
throw error88;
|
|
132217
|
-
}
|
|
132218
|
-
}
|
|
132219
|
-
}
|
|
132220
|
-
const resolution = resolveReviewChildResult({
|
|
132221
|
-
childResult,
|
|
132222
|
-
childLineItemId,
|
|
132223
|
-
parentResult: input.result,
|
|
132224
|
-
parentMetadata: input.metadata,
|
|
132225
|
-
selection
|
|
132226
|
-
});
|
|
132227
|
-
if (resolution.action === "reuse") {
|
|
132228
|
-
return;
|
|
132229
|
-
}
|
|
132230
|
-
if (resolution.action === "restore") {
|
|
132231
|
-
addEvent("assessment.review_child_result_restore", {
|
|
132232
|
-
"app.assessment.review_child_restore_reason": resolution.reason,
|
|
132233
|
-
"app.assessment.attempt_id": input.result.sourcedId,
|
|
132234
|
-
"app.assessment.child_result_id": childResultId,
|
|
132235
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier
|
|
132236
|
-
});
|
|
132237
|
-
}
|
|
132238
|
-
await this.putOpenReviewChildResult({
|
|
132239
|
-
childResultId,
|
|
132240
|
-
childLineItemId,
|
|
132241
|
-
parentResult: input.result,
|
|
132242
|
-
parentMetadata: input.metadata,
|
|
132243
|
-
selection
|
|
132244
|
-
});
|
|
132245
|
-
});
|
|
132246
|
-
}
|
|
132247
|
-
async putReviewChildResponse(projection, checkpoint) {
|
|
132248
|
-
const { result, metadata: metadata2, itemIdentifier, integration } = projection;
|
|
132249
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === itemIdentifier);
|
|
132250
|
-
if (!selection) {
|
|
132251
|
-
return;
|
|
132252
|
-
}
|
|
132253
|
-
try {
|
|
132254
|
-
const source = await this.loadPinnedReviewBankSource(metadata2);
|
|
132255
|
-
const item = source.assessment.items.find((candidate) => candidate.identifier === selection.itemIdentifier);
|
|
132256
|
-
const indexed = source.bank.items.find((candidate) => candidate.itemIdentifier === selection.itemIdentifier);
|
|
132257
|
-
if (!item || !indexed) {
|
|
132258
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
132259
|
-
}
|
|
132260
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
132261
|
-
parentLineItemId: result.assessmentLineItem.sourcedId,
|
|
132262
|
-
integration,
|
|
132263
|
-
bankRevision: metadata2.review.bankRevision,
|
|
132264
|
-
standards: [...indexed.standards],
|
|
132265
|
-
item
|
|
132266
|
-
});
|
|
132267
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
132268
|
-
throw new Error("Prepared review checkpoint resolved a different line item");
|
|
132269
|
-
}
|
|
132270
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
132271
|
-
const childMetadata = buildReviewItemResultMetadata({
|
|
132272
|
-
metadata: metadata2,
|
|
132273
|
-
selection,
|
|
132274
|
-
parentAttemptId: result.sourcedId
|
|
132275
|
-
});
|
|
132276
|
-
await Promise.all([
|
|
132277
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
132278
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, buildOpenReviewChildResult({
|
|
132279
|
-
childLineItemId,
|
|
132280
|
-
student: result.student,
|
|
132281
|
-
metadata: childMetadata
|
|
132282
|
-
}))
|
|
132283
|
-
]);
|
|
132284
|
-
} catch (error88) {
|
|
132285
|
-
addEvent("assessment.review_child_response_projection_failed", {
|
|
132286
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
132287
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
132288
|
-
"exception.type": errorType(error88),
|
|
132289
|
-
"app.error.message": errorMessage2(error88)
|
|
132290
|
-
});
|
|
132291
|
-
}
|
|
132292
|
-
}
|
|
132293
|
-
async projectReviewItemResponse(params, projection, checkpoint) {
|
|
132294
|
-
const terminal = assessmentChildProjectionIsTerminal(projection.result);
|
|
132295
|
-
try {
|
|
132296
|
-
await reconcileReviewChildResults({
|
|
132297
|
-
initial: projection,
|
|
132298
|
-
writeOpen: () => this.putReviewChildResponse(projection, checkpoint),
|
|
132299
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, params.attemptId),
|
|
132300
|
-
readLatest: () => this.peekAttempt(params),
|
|
132301
|
-
restoreTerminal: async (latest) => {
|
|
132302
|
-
const repair = this.restoreCompletedReviewChildResponses(latest.result, latest.metadata, new Set([projection.itemIdentifier]));
|
|
132303
|
-
if (terminal) {
|
|
132304
|
-
await Promise.allSettled([
|
|
132305
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
132306
|
-
repair
|
|
132307
|
-
]);
|
|
132308
|
-
} else {
|
|
132309
|
-
await repair;
|
|
132310
|
-
}
|
|
132311
|
-
}
|
|
132312
|
-
});
|
|
132313
|
-
} catch (error88) {
|
|
132314
|
-
addEvent("assessment.review_child_projection_barrier_failed", {
|
|
132315
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
132316
|
-
"app.assessment.qti_item_identifier": projection.itemIdentifier,
|
|
132317
|
-
"exception.type": errorType(error88),
|
|
132318
|
-
"app.error.message": errorMessage2(error88)
|
|
132319
|
-
});
|
|
132320
|
-
}
|
|
132321
|
-
}
|
|
132322
|
-
restoreAllCompletedReviewChildResponses(result, metadata2) {
|
|
132323
|
-
return this.restoreCompletedReviewChildResponses(result, metadata2, new Set(metadata2.itemSubmissions.map((item) => item.itemIdentifier)));
|
|
132324
|
-
}
|
|
132325
|
-
async restoreCompletedReviewChildResponses(result, metadata2, changedItemIdentifiers) {
|
|
132326
|
-
const outcomes = metadata2.completion?.itemOutcomes;
|
|
132327
|
-
const submissionId = metadata2.submissionId;
|
|
132328
|
-
if (!submissionId || !outcomes) {
|
|
132329
|
-
addEvent("assessment.review_child_terminal_restore_unavailable", {
|
|
132330
|
-
"app.assessment.attempt_id": result.sourcedId
|
|
132331
|
-
});
|
|
132332
|
-
return;
|
|
132333
|
-
}
|
|
132334
|
-
const outcomeByItem = new Map(outcomes.map((outcome) => [outcome.itemIdentifier, outcome]));
|
|
132335
|
-
await runWithConcurrency(metadata2.review.selections.filter((selection) => changedItemIdentifiers.has(selection.itemIdentifier)), TimebackAssessmentRuntimeService2.SCORING_CONCURRENCY, async (selection) => {
|
|
132336
|
-
try {
|
|
132337
|
-
const outcome = outcomeByItem.get(selection.itemIdentifier);
|
|
132338
|
-
if (!outcome) {
|
|
132339
|
-
throw new Error(`Completed review omitted item ${selection.itemIdentifier}`);
|
|
132340
|
-
}
|
|
132341
|
-
const childLineItemId = await reviewQuestionLineItemId(result.assessmentLineItem.sourcedId, selection.itemIdentifier);
|
|
132342
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
132343
|
-
const finalized = buildFinalizedReviewChildResult({
|
|
132344
|
-
childLineItemId,
|
|
132345
|
-
student: result.student,
|
|
132346
|
-
parentAttemptId: result.sourcedId,
|
|
132347
|
-
metadata: metadata2,
|
|
132348
|
-
selection,
|
|
132349
|
-
itemResult: {
|
|
132350
|
-
itemIdentifier: outcome.itemIdentifier,
|
|
132351
|
-
score: outcome.score,
|
|
132352
|
-
isCorrect: outcome.isCorrect
|
|
132353
|
-
},
|
|
132354
|
-
submissionId,
|
|
132355
|
-
timestamp: result.scoreDate
|
|
132356
|
-
});
|
|
132357
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate);
|
|
132358
|
-
} catch (error88) {
|
|
132359
|
-
addEvent("assessment.review_child_terminal_restore_failed", {
|
|
132360
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
132361
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
132362
|
-
"exception.type": errorType(error88),
|
|
132363
|
-
"app.error.message": errorMessage2(error88)
|
|
132364
|
-
});
|
|
132365
|
-
}
|
|
132366
|
-
});
|
|
132367
|
-
}
|
|
132368
|
-
reviewItemOutcomes(input) {
|
|
132369
|
-
const resultByItem = new Map(input.itemResults.map((itemResult) => [itemResult.itemIdentifier, itemResult]));
|
|
132370
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
132371
|
-
return input.metadata.review.selections.filter((selection) => administeredItems.has(selection.itemIdentifier)).map((selection) => {
|
|
132372
|
-
const itemResult = resultByItem.get(selection.itemIdentifier);
|
|
132373
|
-
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
132374
|
-
if (!itemResult || !submission) {
|
|
132375
|
-
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
132376
|
-
}
|
|
132377
|
-
return {
|
|
132378
|
-
itemIdentifier: selection.itemIdentifier,
|
|
132379
|
-
standard: selection.standard,
|
|
132380
|
-
answered: submission.answered,
|
|
132381
|
-
score: itemResult.score,
|
|
132382
|
-
isCorrect: itemResult.isCorrect
|
|
132383
|
-
};
|
|
132384
|
-
});
|
|
132385
|
-
}
|
|
132386
131599
|
async resolveAttemptAuthorization({ gameId, studentId, attemptId, user }, options = {}) {
|
|
132387
131600
|
if (!options.developerAccessValidated) {
|
|
132388
131601
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
@@ -132411,14 +131624,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132411
131624
|
const enrollment = enrollments.find((candidate) => candidate.id === metadata2.enrollmentId && candidate.course.id === integration.courseId);
|
|
132412
131625
|
return { attempt: { result, metadata: metadata2, integration }, enrollment };
|
|
132413
131626
|
}
|
|
132414
|
-
async authorizeAttempt(params, options = {}) {
|
|
132415
|
-
const authorization = await this.resolveAttemptAuthorization(params, options);
|
|
132416
|
-
return this.requireActiveAttempt(authorization, params.attemptId);
|
|
132417
|
-
}
|
|
132418
|
-
requireActiveAttempt(authorization, attemptId) {
|
|
132419
|
-
const enrollment = requireAssessmentMutationAccess(attemptId, authorization.attempt.integration.status, authorization.enrollment);
|
|
132420
|
-
return { ...authorization.attempt, enrollment };
|
|
132421
|
-
}
|
|
132422
131627
|
async authorizeAttemptRead(params) {
|
|
132423
131628
|
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params);
|
|
132424
131629
|
if ((!enrollment || attempt.integration.status === "deactivated") && !this.isSettled(attempt.result)) {
|
|
@@ -132429,7 +131634,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132429
131634
|
unauthorizedAttempt(attemptId) {
|
|
132430
131635
|
return AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
132431
131636
|
}
|
|
132432
|
-
async snapshotForResult(result, metadata2, integration) {
|
|
131637
|
+
async snapshotForResult(result, metadata2, integration, suppliedTranscript) {
|
|
132433
131638
|
if (isAssessmentAttemptSuperseded({
|
|
132434
131639
|
inProgress: result.inProgress ?? "",
|
|
132435
131640
|
scoreStatus: result.scoreStatus
|
|
@@ -132465,38 +131670,59 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132465
131670
|
result: buildCompletedAssessmentSubmitResult({ result, metadata: metadata2 })
|
|
132466
131671
|
};
|
|
132467
131672
|
}
|
|
132468
|
-
const
|
|
132469
|
-
|
|
132470
|
-
|
|
132471
|
-
|
|
132472
|
-
|
|
132473
|
-
|
|
132474
|
-
integration,
|
|
132475
|
-
assessment
|
|
132476
|
-
});
|
|
132477
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, integration, routing.state);
|
|
132478
|
-
}
|
|
132479
|
-
return this.snapshot(result, metadata2, assessment, integration);
|
|
132480
|
-
}
|
|
132481
|
-
diagnosticSnapshot(result, metadata2, assessment, integration, state) {
|
|
132482
|
-
return buildDiagnosticAssessmentSnapshot({
|
|
132483
|
-
attemptId: result.sourcedId,
|
|
132484
|
-
metadata: metadata2,
|
|
132485
|
-
assessment,
|
|
132486
|
-
state,
|
|
132487
|
-
activityData: this.activityData(metadata2, assessment, integration)
|
|
131673
|
+
const context2 = this.tokenContext({ result, metadata: metadata2, integration });
|
|
131674
|
+
const graded = await resumeAssessmentTranscript({
|
|
131675
|
+
suppliedTranscript,
|
|
131676
|
+
checkpointTranscript: metadata2.transcript,
|
|
131677
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.studentAssessmentResults(context2.studentId)),
|
|
131678
|
+
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
132488
131679
|
});
|
|
131680
|
+
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
131681
|
+
const common2 = {
|
|
131682
|
+
attemptId: result.sourcedId,
|
|
131683
|
+
assessment: graded.assessment,
|
|
131684
|
+
activityData: this.activityData(metadata2, graded.assessment, integration)
|
|
131685
|
+
};
|
|
131686
|
+
return this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? {
|
|
131687
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
131688
|
+
...common2,
|
|
131689
|
+
metadata: graded.metadata,
|
|
131690
|
+
state: graded.metadata.diagnostic.state
|
|
131691
|
+
}),
|
|
131692
|
+
...session2
|
|
131693
|
+
} : {
|
|
131694
|
+
...buildConventionalAssessmentSnapshot({ ...common2, metadata: graded.metadata }),
|
|
131695
|
+
...session2
|
|
131696
|
+
};
|
|
131697
|
+
}
|
|
131698
|
+
async assessmentSession(context2, transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision)) {
|
|
131699
|
+
return { transcript, assessmentToken: await this.deps.mintAssessmentToken(context2) };
|
|
131700
|
+
}
|
|
131701
|
+
async diagnosticSnapshot(result, metadata2, assessment, integration, options) {
|
|
131702
|
+
return {
|
|
131703
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
131704
|
+
attemptId: result.sourcedId,
|
|
131705
|
+
metadata: metadata2,
|
|
131706
|
+
assessment,
|
|
131707
|
+
state: options.state,
|
|
131708
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
131709
|
+
}),
|
|
131710
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
131711
|
+
};
|
|
132489
131712
|
}
|
|
132490
131713
|
diagnosticRoutingSnapshot(metadata2, state) {
|
|
132491
131714
|
return projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, state);
|
|
132492
131715
|
}
|
|
132493
|
-
snapshot(result, metadata2, assessment, integration) {
|
|
132494
|
-
return
|
|
132495
|
-
|
|
132496
|
-
|
|
132497
|
-
|
|
132498
|
-
|
|
132499
|
-
|
|
131716
|
+
async snapshot(result, metadata2, assessment, integration) {
|
|
131717
|
+
return {
|
|
131718
|
+
...buildConventionalAssessmentSnapshot({
|
|
131719
|
+
attemptId: result.sourcedId,
|
|
131720
|
+
metadata: metadata2,
|
|
131721
|
+
assessment,
|
|
131722
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
131723
|
+
}),
|
|
131724
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
131725
|
+
};
|
|
132500
131726
|
}
|
|
132501
131727
|
activityData(metadata2, assessment, integration) {
|
|
132502
131728
|
const course = this.trackableCourse(integration, "assessment.activity_tracking_invalid_course_metadata");
|
|
@@ -132557,16 +131783,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132557
131783
|
isSettled(result) {
|
|
132558
131784
|
return this.isAwaitingAward(result) || this.isCompleted(result);
|
|
132559
131785
|
}
|
|
132560
|
-
assertInProgress(result) {
|
|
132561
|
-
if (!this.isOpen(result)) {
|
|
132562
|
-
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(result.sourcedId));
|
|
132563
|
-
}
|
|
132564
|
-
}
|
|
132565
|
-
assertResponseVersion(metadata2, expectedResponseVersion) {
|
|
132566
|
-
if (metadata2.responseVersion !== expectedResponseVersion) {
|
|
132567
|
-
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, metadata2.responseVersion));
|
|
132568
|
-
}
|
|
132569
|
-
}
|
|
132570
131786
|
scoreFromResult(result, possible, metadata2) {
|
|
132571
131787
|
return metadata2.score ?? assessmentScoreFromPercentage(result.score, possible);
|
|
132572
131788
|
}
|
|
@@ -132634,7 +131850,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132634
131850
|
grade: integration.grade
|
|
132635
131851
|
})
|
|
132636
131852
|
});
|
|
132637
|
-
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration,
|
|
131853
|
+
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration, {
|
|
131854
|
+
state: metadata2.diagnostic.state
|
|
131855
|
+
}) : this.snapshot(result, metadata2, assessment, integration);
|
|
132638
131856
|
}
|
|
132639
131857
|
async claimAttemptId(scope, from) {
|
|
132640
131858
|
for (let attemptNumber = from;attemptNumber < from + 20; attemptNumber += 1) {
|
|
@@ -132679,18 +131897,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
132679
131897
|
game: game2
|
|
132680
131898
|
};
|
|
132681
131899
|
}
|
|
132682
|
-
async putResultMetadata(result, metadata2) {
|
|
132683
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(result.sourcedId, {
|
|
132684
|
-
status: ONEROSTER_STATUS2.active,
|
|
132685
|
-
assessmentLineItem: result.assessmentLineItem,
|
|
132686
|
-
student: result.student,
|
|
132687
|
-
score: result.score ?? 0,
|
|
132688
|
-
scoreDate: result.scoreDate,
|
|
132689
|
-
scoreStatus: result.scoreStatus,
|
|
132690
|
-
...result.inProgress === undefined || result.inProgress === null ? {} : { inProgress: result.inProgress },
|
|
132691
|
-
metadata: mergeAssessmentRuntimeMetadata(result.metadata, metadata2)
|
|
132692
|
-
});
|
|
132693
|
-
}
|
|
132694
131900
|
async emitCompletionBestEffort(input) {
|
|
132695
131901
|
const { attempt } = input;
|
|
132696
131902
|
const integration = attempt.integration;
|
|
@@ -136251,6 +135457,8 @@ function createPlatformServices(deps) {
|
|
|
136251
135457
|
mintPlatformServiceToken,
|
|
136252
135458
|
mintAssessmentPreparationReceipt,
|
|
136253
135459
|
verifyAssessmentPreparationReceipt,
|
|
135460
|
+
mintAssessmentToken,
|
|
135461
|
+
verifyAssessmentToken,
|
|
136254
135462
|
validateDeveloperAccessBySlug,
|
|
136255
135463
|
validateDeveloperAccess,
|
|
136256
135464
|
validateGameManagementAccess
|
|
@@ -136325,7 +135533,9 @@ function createPlatformServices(deps) {
|
|
|
136325
135533
|
validateDeveloperAccess,
|
|
136326
135534
|
assessmentRuntimeLock,
|
|
136327
135535
|
mintAssessmentPreparationReceipt,
|
|
136328
|
-
verifyAssessmentPreparationReceipt
|
|
135536
|
+
verifyAssessmentPreparationReceipt,
|
|
135537
|
+
mintAssessmentToken,
|
|
135538
|
+
verifyAssessmentToken
|
|
136329
135539
|
});
|
|
136330
135540
|
return {
|
|
136331
135541
|
assessmentAssets,
|
|
@@ -137060,6 +136270,8 @@ function createServices(ctx) {
|
|
|
137060
136270
|
mintPlatformServiceToken: auth2.mintPlatformServiceToken.bind(auth2),
|
|
137061
136271
|
mintAssessmentPreparationReceipt: auth2.mintAssessmentPreparationReceipt.bind(auth2),
|
|
137062
136272
|
verifyAssessmentPreparationReceipt: auth2.verifyAssessmentPreparationReceipt.bind(auth2),
|
|
136273
|
+
mintAssessmentToken: auth2.mintAssessmentToken.bind(auth2),
|
|
136274
|
+
verifyAssessmentToken: auth2.verifyAssessmentToken.bind(auth2),
|
|
137063
136275
|
...gaming.validators
|
|
137064
136276
|
});
|
|
137065
136277
|
const standalone = createStandaloneServices({ db: db2, auth: auth2, timeback: timeback2 });
|
|
@@ -137562,6 +136774,15 @@ function createSandboxAuthProvider() {
|
|
|
137562
136774
|
});
|
|
137563
136775
|
return { receipt, expiresAt: new Date(expiresAt).toISOString() };
|
|
137564
136776
|
},
|
|
136777
|
+
async mintAssessmentToken(context2) {
|
|
136778
|
+
const token = `sandbox_assessment_${crypto.randomUUID()}`;
|
|
136779
|
+
sandboxAssessmentTokens.set(token, structuredClone(context2));
|
|
136780
|
+
return token;
|
|
136781
|
+
},
|
|
136782
|
+
async verifyAssessmentToken(token) {
|
|
136783
|
+
const context2 = sandboxAssessmentTokens.get(token);
|
|
136784
|
+
return context2 ? structuredClone(context2) : null;
|
|
136785
|
+
},
|
|
137565
136786
|
async verifyAssessmentPreparationReceipt(receipt) {
|
|
137566
136787
|
const prepared = sandboxAssessmentPreparationReceipts.get(receipt);
|
|
137567
136788
|
if (!prepared) {
|
|
@@ -137617,12 +136838,18 @@ function createSandboxAuthProvider() {
|
|
|
137617
136838
|
var sandboxApiKeys;
|
|
137618
136839
|
var sandboxAssessmentPreparationReceipts;
|
|
137619
136840
|
var ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS;
|
|
137620
|
-
var
|
|
136841
|
+
var sandboxAssessmentTokens;
|
|
136842
|
+
var init_auth_provider = __esm(async () => {
|
|
137621
136843
|
init_src6();
|
|
137622
136844
|
init_constants();
|
|
136845
|
+
await init_dist5();
|
|
137623
136846
|
sandboxApiKeys = new Map;
|
|
137624
136847
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
137625
136848
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 300000;
|
|
136849
|
+
sandboxAssessmentTokens = new TimebackCache({
|
|
136850
|
+
defaultTTL: 7200000,
|
|
136851
|
+
maxSize: 1000
|
|
136852
|
+
});
|
|
137626
136853
|
});
|
|
137627
136854
|
function createSandboxCacheProvider() {
|
|
137628
136855
|
return {
|
|
@@ -137775,10 +137002,10 @@ var init_storage_provider = __esm(() => {
|
|
|
137775
137002
|
init_src6();
|
|
137776
137003
|
storage = new Map;
|
|
137777
137004
|
});
|
|
137778
|
-
var init_providers = __esm(() => {
|
|
137779
|
-
init_auth_provider();
|
|
137005
|
+
var init_providers = __esm(async () => {
|
|
137780
137006
|
init_cache_provider();
|
|
137781
137007
|
init_storage_provider();
|
|
137008
|
+
await init_auth_provider();
|
|
137782
137009
|
});
|
|
137783
137010
|
function buildConfig(options) {
|
|
137784
137011
|
const baseUrl = `http://localhost:${options.port ?? 3000}`;
|
|
@@ -137847,10 +137074,10 @@ var init_context = __esm(async () => {
|
|
|
137847
137074
|
init_config2();
|
|
137848
137075
|
init_utils7();
|
|
137849
137076
|
init_src6();
|
|
137850
|
-
init_providers();
|
|
137851
137077
|
await __promiseAll([
|
|
137852
137078
|
init_factory(),
|
|
137853
|
-
init_clients()
|
|
137079
|
+
init_clients(),
|
|
137080
|
+
init_providers()
|
|
137854
137081
|
]);
|
|
137855
137082
|
});
|
|
137856
137083
|
function resolveUser(c) {
|
|
@@ -196925,7 +196152,9 @@ var prepareRuntimeAssessment;
|
|
|
196925
196152
|
var startRuntimeAssessment;
|
|
196926
196153
|
var getRuntimeAssessment;
|
|
196927
196154
|
var getLatestRuntimeAssessment;
|
|
196155
|
+
var resumeRuntimeAssessment;
|
|
196928
196156
|
var saveRuntimeAssessment;
|
|
196157
|
+
var recordRuntimeAssessmentProgress;
|
|
196929
196158
|
var submitRuntimeAssessmentItem;
|
|
196930
196159
|
var submitRuntimeAssessment;
|
|
196931
196160
|
var finalizeRuntimeAssessment;
|
|
@@ -197266,6 +196495,20 @@ var init_timeback_controller = __esm(() => {
|
|
|
197266
196495
|
user: ctx.user
|
|
197267
196496
|
});
|
|
197268
196497
|
});
|
|
196498
|
+
resumeRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
196499
|
+
const attemptId = ctx.params.attemptId;
|
|
196500
|
+
const body2 = await parseRequestBody(ctx.request, ResumeRuntimeAssessmentRequestSchema);
|
|
196501
|
+
if (!attemptId) {
|
|
196502
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
196503
|
+
}
|
|
196504
|
+
return ctx.services.timebackAssessmentRuntime.resume({
|
|
196505
|
+
gameId: body2.gameId,
|
|
196506
|
+
studentId: body2.studentId,
|
|
196507
|
+
attemptId,
|
|
196508
|
+
input: { transcript: body2.transcript },
|
|
196509
|
+
user: ctx.user
|
|
196510
|
+
});
|
|
196511
|
+
});
|
|
197269
196512
|
saveRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
197270
196513
|
const attemptId = ctx.params.attemptId;
|
|
197271
196514
|
const body2 = await parseRequestBody(ctx.request, SaveRuntimeAssessmentRequestSchema);
|
|
@@ -197278,7 +196521,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
197278
196521
|
attemptId,
|
|
197279
196522
|
input: {
|
|
197280
196523
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
197281
|
-
|
|
196524
|
+
transcript: body2.transcript
|
|
196525
|
+
},
|
|
196526
|
+
user: ctx.user
|
|
196527
|
+
});
|
|
196528
|
+
});
|
|
196529
|
+
recordRuntimeAssessmentProgress = requireDeveloper(async (ctx) => {
|
|
196530
|
+
const attemptId = ctx.params.attemptId;
|
|
196531
|
+
const body2 = await parseRequestBody(ctx.request, RecordRuntimeAssessmentProgressRequestSchema);
|
|
196532
|
+
if (!attemptId) {
|
|
196533
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
196534
|
+
}
|
|
196535
|
+
return ctx.services.timebackAssessmentRuntime.recordProgress({
|
|
196536
|
+
gameId: body2.gameId,
|
|
196537
|
+
studentId: body2.studentId,
|
|
196538
|
+
attemptId,
|
|
196539
|
+
input: {
|
|
196540
|
+
assessmentToken: body2.assessmentToken,
|
|
196541
|
+
transcript: body2.transcript,
|
|
196542
|
+
expectedResponseVersion: body2.expectedResponseVersion,
|
|
196543
|
+
itemIdentifier: body2.itemIdentifier
|
|
197282
196544
|
},
|
|
197283
196545
|
user: ctx.user
|
|
197284
196546
|
});
|
|
@@ -197294,6 +196556,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
197294
196556
|
studentId: body2.studentId,
|
|
197295
196557
|
attemptId,
|
|
197296
196558
|
input: {
|
|
196559
|
+
assessmentToken: body2.assessmentToken,
|
|
196560
|
+
transcript: body2.transcript,
|
|
197297
196561
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
197298
196562
|
submissionId: body2.submissionId,
|
|
197299
196563
|
itemIdentifier: body2.itemIdentifier,
|
|
@@ -197316,6 +196580,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
197316
196580
|
input: {
|
|
197317
196581
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
197318
196582
|
submissionId: body2.submissionId,
|
|
196583
|
+
transcript: body2.transcript,
|
|
197319
196584
|
...body2.session ? { session: body2.session } : {}
|
|
197320
196585
|
},
|
|
197321
196586
|
game: {
|
|
@@ -197793,6 +197058,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
197793
197058
|
getLatestRuntimeAssessment,
|
|
197794
197059
|
getRuntimeAssessment,
|
|
197795
197060
|
saveRuntimeAssessment,
|
|
197061
|
+
resumeRuntimeAssessment,
|
|
197062
|
+
recordRuntimeAssessmentProgress,
|
|
197796
197063
|
submitRuntimeAssessmentItem,
|
|
197797
197064
|
submitRuntimeAssessment,
|
|
197798
197065
|
finalizeRuntimeAssessment,
|
|
@@ -198746,6 +198013,8 @@ var init_timeback8 = __esm(async () => {
|
|
|
198746
198013
|
timebackRouter.post("/assessments/start", handle2(timeback2.startRuntimeAssessment));
|
|
198747
198014
|
timebackRouter.get("/assessments/latest", handle2(timeback2.getLatestRuntimeAssessment));
|
|
198748
198015
|
timebackRouter.get("/assessments/:attemptId", handle2(timeback2.getRuntimeAssessment));
|
|
198016
|
+
timebackRouter.post("/assessments/:attemptId/resume", handle2(timeback2.resumeRuntimeAssessment));
|
|
198017
|
+
timebackRouter.post("/assessments/:attemptId/progress", handle2(timeback2.recordRuntimeAssessmentProgress));
|
|
198749
198018
|
timebackRouter.post("/assessments/:attemptId/save", handle2(timeback2.saveRuntimeAssessment));
|
|
198750
198019
|
timebackRouter.post("/assessments/:attemptId/submit-item", handle2(timeback2.submitRuntimeAssessmentItem));
|
|
198751
198020
|
timebackRouter.post("/assessments/:attemptId/submit", handle2(timeback2.submitRuntimeAssessment));
|
|
@@ -199147,13 +198416,13 @@ var init_server = __esm(async () => {
|
|
|
199147
198416
|
init_port();
|
|
199148
198417
|
init_package();
|
|
199149
198418
|
init_config();
|
|
199150
|
-
init_providers();
|
|
199151
198419
|
init_app();
|
|
199152
198420
|
init_database2();
|
|
199153
198421
|
init_options();
|
|
199154
198422
|
await __promiseAll([
|
|
199155
198423
|
init_api3(),
|
|
199156
|
-
init_context()
|
|
198424
|
+
init_context(),
|
|
198425
|
+
init_providers()
|
|
199157
198426
|
]);
|
|
199158
198427
|
version5 = package_default2.version;
|
|
199159
198428
|
});
|