@playcademy/sandbox 0.8.1-beta.5 → 0.8.1-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1367 -2279
- package/dist/constants.js +1 -1
- package/dist/server.js +1367 -2279
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -284,7 +284,7 @@ var init_platform = __esm(() => {
|
|
|
284
284
|
var PLATFORM_TIMEZONE = "America/New_York";
|
|
285
285
|
|
|
286
286
|
// ../constants/src/timeback.ts
|
|
287
|
-
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1,
|
|
287
|
+
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 1, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY = "playcademyAssessment", PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY = "playcademyAssessmentItem", TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH = 2048, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
|
|
288
288
|
var init_timeback2 = __esm(() => {
|
|
289
289
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
290
290
|
TIMEBACK_SUBJECTS = [
|
|
@@ -1127,7 +1127,7 @@ var package_default;
|
|
|
1127
1127
|
var init_package = __esm(() => {
|
|
1128
1128
|
package_default = {
|
|
1129
1129
|
name: "@playcademy/sandbox",
|
|
1130
|
-
version: "0.8.1-beta.
|
|
1130
|
+
version: "0.8.1-beta.7",
|
|
1131
1131
|
description: "Local development server for Playcademy game development",
|
|
1132
1132
|
type: "module",
|
|
1133
1133
|
exports: {
|
|
@@ -9239,26 +9239,6 @@ var init_v3 = __esm(() => {
|
|
|
9239
9239
|
var init_esm = __esm(() => {
|
|
9240
9240
|
init_v3();
|
|
9241
9241
|
});
|
|
9242
|
-
// ../types/src/timeback/assessment-runtime.ts
|
|
9243
|
-
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
9244
|
-
var init_assessment_runtime = __esm(() => {
|
|
9245
|
-
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
9246
|
-
});
|
|
9247
|
-
// ../types/src/timeback/diagnostic-routing.ts
|
|
9248
|
-
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1, DIAGNOSTIC_ROUTING_STATE_VERSION = 1, DIAGNOSTIC_TRACK_OUTCOMES;
|
|
9249
|
-
var init_diagnostic_routing = __esm(() => {
|
|
9250
|
-
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
9251
|
-
"provisional",
|
|
9252
|
-
"unresolved",
|
|
9253
|
-
"route-to-instruction",
|
|
9254
|
-
"not-assessed"
|
|
9255
|
-
];
|
|
9256
|
-
});
|
|
9257
|
-
// ../types/src/timeback/index.ts
|
|
9258
|
-
var init_timeback4 = __esm(() => {
|
|
9259
|
-
init_assessment_runtime();
|
|
9260
|
-
init_diagnostic_routing();
|
|
9261
|
-
});
|
|
9262
9242
|
|
|
9263
9243
|
// ../utils/src/uuid.ts
|
|
9264
9244
|
function isValidUUID(value) {
|
|
@@ -9282,6 +9262,26 @@ var UUID_REGEX;
|
|
|
9282
9262
|
var init_uuid = __esm(() => {
|
|
9283
9263
|
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}$/;
|
|
9284
9264
|
});
|
|
9265
|
+
// ../types/src/timeback/assessment-runtime.ts
|
|
9266
|
+
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
9267
|
+
var init_assessment_runtime = __esm(() => {
|
|
9268
|
+
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
9269
|
+
});
|
|
9270
|
+
// ../types/src/timeback/diagnostic-routing.ts
|
|
9271
|
+
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1, DIAGNOSTIC_ROUTING_STATE_VERSION = 1, DIAGNOSTIC_TRACK_OUTCOMES;
|
|
9272
|
+
var init_diagnostic_routing = __esm(() => {
|
|
9273
|
+
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
9274
|
+
"provisional",
|
|
9275
|
+
"unresolved",
|
|
9276
|
+
"route-to-instruction",
|
|
9277
|
+
"not-assessed"
|
|
9278
|
+
];
|
|
9279
|
+
});
|
|
9280
|
+
// ../types/src/timeback/index.ts
|
|
9281
|
+
var init_timeback4 = __esm(() => {
|
|
9282
|
+
init_assessment_runtime();
|
|
9283
|
+
init_diagnostic_routing();
|
|
9284
|
+
});
|
|
9285
9285
|
|
|
9286
9286
|
// ../timeback/dist/engines/assessment-runtime/index.js
|
|
9287
9287
|
function qtiDirectedPairValidationIssue(values, contract) {
|
|
@@ -9561,6 +9561,25 @@ function assessmentReviewBankShortage(fulfillment) {
|
|
|
9561
9561
|
function assessmentFlowForPurpose(purpose) {
|
|
9562
9562
|
return purpose === "review" ? "item-submit" : "attempt-submit";
|
|
9563
9563
|
}
|
|
9564
|
+
function isObject(value) {
|
|
9565
|
+
return typeof value === "object" && value !== null;
|
|
9566
|
+
}
|
|
9567
|
+
function isTimebackGrade(value) {
|
|
9568
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
9569
|
+
}
|
|
9570
|
+
function isAssessmentSessionTiming(value) {
|
|
9571
|
+
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));
|
|
9572
|
+
}
|
|
9573
|
+
function isNonNegativeDuration(value) {
|
|
9574
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
9575
|
+
}
|
|
9576
|
+
function isAssessmentPurpose(value) {
|
|
9577
|
+
return ASSESSMENT_PURPOSES.includes(value);
|
|
9578
|
+
}
|
|
9579
|
+
function isAssessmentResponseValue(value) {
|
|
9580
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
9581
|
+
return entries.length > 0 && entries.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
9582
|
+
}
|
|
9564
9583
|
function compareCodeUnits(left, right) {
|
|
9565
9584
|
if (left < right) {
|
|
9566
9585
|
return -1;
|
|
@@ -10414,48 +10433,6 @@ function projectDiagnosticRoutingSnapshot(revision, state) {
|
|
|
10414
10433
|
}))
|
|
10415
10434
|
};
|
|
10416
10435
|
}
|
|
10417
|
-
function isObject(value) {
|
|
10418
|
-
return typeof value === "object" && value !== null;
|
|
10419
|
-
}
|
|
10420
|
-
function isTimebackGrade(value) {
|
|
10421
|
-
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
10422
|
-
}
|
|
10423
|
-
function isAssessmentSessionTiming(value) {
|
|
10424
|
-
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));
|
|
10425
|
-
}
|
|
10426
|
-
function isNonNegativeDuration(value) {
|
|
10427
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
10428
|
-
}
|
|
10429
|
-
function isAssessmentPurpose(value) {
|
|
10430
|
-
return ASSESSMENT_PURPOSES.includes(value);
|
|
10431
|
-
}
|
|
10432
|
-
function isAssessmentResponseValue(value) {
|
|
10433
|
-
const entries = Array.isArray(value) ? value : [value];
|
|
10434
|
-
return entries.length > 0 && entries.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
10435
|
-
}
|
|
10436
|
-
function applyAssessmentItemResponseUpdate(current, update) {
|
|
10437
|
-
const next = { ...current };
|
|
10438
|
-
for (const [responseId, value] of Object.entries(update)) {
|
|
10439
|
-
if (value === null) {
|
|
10440
|
-
delete next[responseId];
|
|
10441
|
-
} else {
|
|
10442
|
-
next[responseId] = value;
|
|
10443
|
-
}
|
|
10444
|
-
}
|
|
10445
|
-
return Object.keys(next).length === 0 ? undefined : next;
|
|
10446
|
-
}
|
|
10447
|
-
function applyAssessmentResponseUpdate(current, update) {
|
|
10448
|
-
const next = structuredClone(current);
|
|
10449
|
-
for (const [questionId, questionUpdate] of Object.entries(update)) {
|
|
10450
|
-
const questionResponses = applyAssessmentItemResponseUpdate(next[questionId], questionUpdate);
|
|
10451
|
-
if (questionResponses === undefined) {
|
|
10452
|
-
delete next[questionId];
|
|
10453
|
-
} else {
|
|
10454
|
-
next[questionId] = questionResponses;
|
|
10455
|
-
}
|
|
10456
|
-
}
|
|
10457
|
-
return next;
|
|
10458
|
-
}
|
|
10459
10436
|
function assessmentResponseUpdateValidationMessage(assessment, update) {
|
|
10460
10437
|
const items = new Map(assessment.items.map((item) => [item.identifier, item]));
|
|
10461
10438
|
for (const [itemIdentifier, itemUpdate] of Object.entries(update)) {
|
|
@@ -10617,158 +10594,163 @@ function interactionResponseValidationMessage(itemIdentifier, interaction, respo
|
|
|
10617
10594
|
}
|
|
10618
10595
|
return null;
|
|
10619
10596
|
}
|
|
10620
|
-
function
|
|
10621
|
-
return {
|
|
10622
|
-
}
|
|
10623
|
-
function administeredItemIdentifiers(itemSubmissions) {
|
|
10624
|
-
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10625
|
-
}
|
|
10626
|
-
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
10627
|
-
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
10628
|
-
}
|
|
10629
|
-
function responseValuesEqual(left, right) {
|
|
10630
|
-
if (left === undefined || right === undefined) {
|
|
10631
|
-
return left === right;
|
|
10632
|
-
}
|
|
10633
|
-
if (Array.isArray(left) || Array.isArray(right)) {
|
|
10634
|
-
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
10635
|
-
}
|
|
10636
|
-
return left === right;
|
|
10637
|
-
}
|
|
10638
|
-
function itemResponsesEqual(left, right) {
|
|
10639
|
-
const leftEntries = Object.entries(left ?? {});
|
|
10640
|
-
const rightEntries = Object.entries(right ?? {});
|
|
10641
|
-
return leftEntries.length === rightEntries.length && leftEntries.every(([identifier, value]) => responseValuesEqual(value, right?.[identifier]));
|
|
10597
|
+
function emptyAssessmentTranscript(attemptId, contentRevision) {
|
|
10598
|
+
return { version: 1, attemptId, contentRevision, administrations: [] };
|
|
10642
10599
|
}
|
|
10643
|
-
function
|
|
10644
|
-
|
|
10645
|
-
}
|
|
10646
|
-
function resolveAssessmentItemReplay(state, input) {
|
|
10647
|
-
const flow = assessmentFlowForPurpose(state.purpose);
|
|
10648
|
-
if (flow !== "item-submit") {
|
|
10600
|
+
function assessmentProgressAdministration(purpose, input) {
|
|
10601
|
+
if (purpose !== "mastery") {
|
|
10649
10602
|
return {
|
|
10650
|
-
|
|
10651
|
-
failure: assessmentFlowViolation("
|
|
10603
|
+
ok: false,
|
|
10604
|
+
failure: assessmentFlowViolation("Background progress is only available for mastery.")
|
|
10652
10605
|
};
|
|
10653
10606
|
}
|
|
10654
|
-
const
|
|
10655
|
-
if (
|
|
10656
|
-
const sameRequest = priorSubmission.itemIdentifier === input.itemIdentifier && itemResponseUpdateMatches(state.responses[input.itemIdentifier], input.responses);
|
|
10657
|
-
if (!sameRequest) {
|
|
10658
|
-
return {
|
|
10659
|
-
action: "conflict",
|
|
10660
|
-
failure: assessmentFlowViolation("This item submission ID was already used for a different request.", {
|
|
10661
|
-
submissionId: input.submissionId,
|
|
10662
|
-
itemIdentifier: input.itemIdentifier,
|
|
10663
|
-
committedItemIdentifier: priorSubmission.itemIdentifier,
|
|
10664
|
-
flow
|
|
10665
|
-
})
|
|
10666
|
-
};
|
|
10667
|
-
}
|
|
10607
|
+
const { administrations } = input.transcript;
|
|
10608
|
+
if (input.expectedResponseVersion !== administrations.length) {
|
|
10668
10609
|
return {
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
responses: state.responses,
|
|
10672
|
-
submission: priorSubmission
|
|
10610
|
+
ok: false,
|
|
10611
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, administrations.length)
|
|
10673
10612
|
};
|
|
10674
10613
|
}
|
|
10675
|
-
|
|
10614
|
+
const administration = administrations.find((entry) => entry.itemIdentifier === input.itemIdentifier);
|
|
10615
|
+
return administration ? { ok: true, submissionId: administration.submissionId } : {
|
|
10616
|
+
ok: false,
|
|
10617
|
+
failure: assessmentFlowViolation("The progress item must be present in the transcript.")
|
|
10618
|
+
};
|
|
10676
10619
|
}
|
|
10677
|
-
function
|
|
10678
|
-
const
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10620
|
+
function appendAssessmentAdministration(transcript, input) {
|
|
10621
|
+
const entry = {
|
|
10622
|
+
submissionId: input.submissionId,
|
|
10623
|
+
itemIdentifier: input.itemIdentifier,
|
|
10624
|
+
...input.routingNodeKey === undefined ? {} : { routingNodeKey: input.routingNodeKey },
|
|
10625
|
+
responses: Object.fromEntries(Object.entries(input.responses).filter((pair) => pair[1] !== null))
|
|
10626
|
+
};
|
|
10627
|
+
const priorIndex = transcript.administrations.findIndex((item) => item.submissionId === input.submissionId);
|
|
10628
|
+
const index = priorIndex === -1 ? transcript.administrations.length : priorIndex;
|
|
10629
|
+
if (input.expectedResponseVersion !== index) {
|
|
10686
10630
|
return {
|
|
10687
|
-
|
|
10688
|
-
failure: assessmentResponseVersionConflict(input.expectedResponseVersion,
|
|
10631
|
+
ok: false,
|
|
10632
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, index)
|
|
10689
10633
|
};
|
|
10690
10634
|
}
|
|
10691
|
-
|
|
10692
|
-
const previousItemIdentifier = state.itemSubmissions.at(-1)?.itemIdentifier;
|
|
10693
|
-
const previousItemIndex = state.assessment.items.findIndex((item) => item.identifier === previousItemIdentifier);
|
|
10694
|
-
if (itemIndex === -1 || itemIndex <= previousItemIndex) {
|
|
10635
|
+
if (priorIndex !== -1 && canonicalJson(transcript.administrations[priorIndex]) !== canonicalJson(entry)) {
|
|
10695
10636
|
return {
|
|
10696
|
-
|
|
10697
|
-
failure: assessmentFlowViolation("
|
|
10698
|
-
itemIdentifier: input.itemIdentifier,
|
|
10699
|
-
previousItemIdentifier: previousItemIdentifier ?? null,
|
|
10700
|
-
flow: assessmentFlowForPurpose(state.purpose)
|
|
10701
|
-
})
|
|
10637
|
+
ok: false,
|
|
10638
|
+
failure: assessmentFlowViolation("This administration identity was reused with different answers.")
|
|
10702
10639
|
};
|
|
10703
10640
|
}
|
|
10704
|
-
const update = { [input.itemIdentifier]: input.responses };
|
|
10705
|
-
const updateMessage = assessmentResponseUpdateValidationMessage(state.assessment, update);
|
|
10706
|
-
if (updateMessage) {
|
|
10707
|
-
return { action: "reject", failure: invalidResponse(updateMessage) };
|
|
10708
|
-
}
|
|
10709
|
-
const responses = applyAssessmentResponseUpdate(state.responses, update);
|
|
10710
|
-
const validationMessage = assessmentResponseValidationMessage(state.assessment, responses);
|
|
10711
|
-
if (validationMessage) {
|
|
10712
|
-
return { action: "reject", failure: invalidResponse(validationMessage) };
|
|
10713
|
-
}
|
|
10714
10641
|
return {
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
responses,
|
|
10719
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0
|
|
10642
|
+
ok: true,
|
|
10643
|
+
index,
|
|
10644
|
+
transcript: priorIndex === -1 ? { ...transcript, administrations: [...transcript.administrations, entry] } : transcript
|
|
10720
10645
|
};
|
|
10721
10646
|
}
|
|
10722
|
-
function
|
|
10723
|
-
const
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10647
|
+
function prepareAssessmentTranscript(input) {
|
|
10648
|
+
const parsed = AssessmentTranscriptSchema.safeParse(input.transcript);
|
|
10649
|
+
if (!parsed.success) {
|
|
10650
|
+
throw new AssessmentTranscriptError("Invalid assessment transcript", 0, "INVALID_RESPONSE");
|
|
10651
|
+
}
|
|
10652
|
+
const transcript = parsed.data;
|
|
10653
|
+
if (transcript.attemptId !== input.attemptId || transcript.contentRevision !== input.contentRevision) {
|
|
10654
|
+
throw new AssessmentTranscriptError("Transcript does not belong to this attempt and revision");
|
|
10655
|
+
}
|
|
10656
|
+
const responses = {};
|
|
10657
|
+
const submissions = new Set;
|
|
10658
|
+
const items = new Set;
|
|
10659
|
+
let previousItemIndex = -1;
|
|
10660
|
+
for (const [index, entry] of transcript.administrations.entries()) {
|
|
10661
|
+
const itemIndex = input.assessment.items.findIndex((item) => item.identifier === entry.itemIdentifier);
|
|
10662
|
+
if (submissions.has(entry.submissionId) || items.has(entry.itemIdentifier)) {
|
|
10663
|
+
throw new AssessmentTranscriptError("Transcript contains a repeated administration or item", index);
|
|
10664
|
+
}
|
|
10665
|
+
if (itemIndex === -1) {
|
|
10666
|
+
throw new AssessmentTranscriptError("Transcript references an unknown assessment item", index, "INVALID_RESPONSE");
|
|
10667
|
+
}
|
|
10668
|
+
if (!input.routed && itemIndex <= previousItemIndex) {
|
|
10669
|
+
throw new AssessmentTranscriptError("Transcript items must follow the selected candidate order", index);
|
|
10670
|
+
}
|
|
10671
|
+
if (input.routed !== (entry.routingNodeKey !== undefined)) {
|
|
10672
|
+
throw new AssessmentTranscriptError("Transcript routing identity does not match the assessment flow", index);
|
|
10673
|
+
}
|
|
10674
|
+
const itemResponses = { [entry.itemIdentifier]: entry.responses };
|
|
10675
|
+
const validate2 = input.routed || input.allowIncomplete ? assessmentDraftResponseValidationMessage : assessmentResponseValidationMessage;
|
|
10676
|
+
const invalid = Object.keys(entry.responses).length === 0 ? null : validate2(input.assessment, itemResponses);
|
|
10677
|
+
if (invalid) {
|
|
10678
|
+
throw new AssessmentTranscriptError(invalid, index, "INVALID_RESPONSE");
|
|
10679
|
+
}
|
|
10680
|
+
submissions.add(entry.submissionId);
|
|
10681
|
+
items.add(entry.itemIdentifier);
|
|
10682
|
+
previousItemIndex = itemIndex;
|
|
10683
|
+
Object.assign(responses, itemResponses);
|
|
10684
|
+
}
|
|
10685
|
+
return { transcript, responses };
|
|
10686
|
+
}
|
|
10687
|
+
function completeAssessmentTranscript(input) {
|
|
10688
|
+
const itemSubmissions = [];
|
|
10689
|
+
let state;
|
|
10690
|
+
const ledger = [];
|
|
10691
|
+
if (input.manifest) {
|
|
10692
|
+
const initial = initializeDiagnosticRouting(input.manifest);
|
|
10693
|
+
if (!initial.ok) {
|
|
10694
|
+
throw new AssessmentTranscriptError("The pinned diagnostic routing manifest is invalid");
|
|
10695
|
+
}
|
|
10696
|
+
state = initial.state;
|
|
10697
|
+
}
|
|
10698
|
+
const scores = new Map(input.scores.map((score) => [score.itemIdentifier, score]));
|
|
10699
|
+
for (const [index, entry] of input.transcript.administrations.entries()) {
|
|
10700
|
+
const scoring = scores.get(entry.itemIdentifier);
|
|
10701
|
+
if (!scoring) {
|
|
10702
|
+
throw new AssessmentTranscriptError("Transcript item has no trusted grade", index);
|
|
10703
|
+
}
|
|
10704
|
+
if (input.manifest) {
|
|
10705
|
+
if (typeof scoring.isCorrect !== "boolean" || !entry.routingNodeKey) {
|
|
10706
|
+
throw new AssessmentTranscriptError("Diagnostic grading must be determinate", index);
|
|
10707
|
+
}
|
|
10708
|
+
const advanced = advanceDiagnosticRouting(input.manifest, ledger, {
|
|
10709
|
+
routingNodeKey: entry.routingNodeKey,
|
|
10710
|
+
itemIdentifier: entry.itemIdentifier,
|
|
10711
|
+
isCorrect: scoring.isCorrect
|
|
10712
|
+
});
|
|
10713
|
+
if (!advanced.ok) {
|
|
10714
|
+
throw new AssessmentTranscriptError("Transcript does not follow the pinned diagnostic route", index);
|
|
10715
|
+
}
|
|
10716
|
+
ledger.push(advanced.ledgerEntry);
|
|
10717
|
+
state = advanced.state;
|
|
10718
|
+
}
|
|
10719
|
+
itemSubmissions.push({
|
|
10720
|
+
submissionId: entry.submissionId,
|
|
10721
|
+
itemIdentifier: entry.itemIdentifier,
|
|
10722
|
+
responseVersion: index + 1,
|
|
10723
|
+
submittedAt: input.submittedAt,
|
|
10724
|
+
answered: Object.keys(entry.responses).length > 0,
|
|
10725
|
+
score: scoring.score,
|
|
10726
|
+
isCorrect: scoring.isCorrect,
|
|
10727
|
+
...scoring.grading ? { grading: scoring.grading } : {}
|
|
10728
|
+
});
|
|
10729
|
+
}
|
|
10730
|
+
if (input.terminal) {
|
|
10731
|
+
const count = itemSubmissions.length;
|
|
10732
|
+
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount;
|
|
10733
|
+
const complete = state ? state.status === "ready-to-complete" && count > 0 : conventionalComplete;
|
|
10734
|
+
if (!complete) {
|
|
10735
|
+
throw new AssessmentTranscriptError("A complete administered transcript is required for submission", count);
|
|
10736
|
+
}
|
|
10737
|
+
}
|
|
10738
|
+
return { itemSubmissions, ledger, state };
|
|
10739
10739
|
}
|
|
10740
|
-
function
|
|
10741
|
-
|
|
10742
|
-
const committedUpdate = Object.keys(update).find((identifier) => administered.has(identifier));
|
|
10743
|
-
return committedUpdate ? assessmentFlowViolation("A submitted item response cannot be edited.", {
|
|
10744
|
-
itemIdentifier: committedUpdate,
|
|
10745
|
-
flow: assessmentFlowForPurpose(purpose)
|
|
10746
|
-
}) : null;
|
|
10740
|
+
function administeredItemIdentifiers(itemSubmissions) {
|
|
10741
|
+
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10747
10742
|
}
|
|
10748
|
-
function
|
|
10749
|
-
|
|
10750
|
-
if (flow !== "item-submit") {
|
|
10751
|
-
return null;
|
|
10752
|
-
}
|
|
10753
|
-
if (itemSubmissions.length > 0 && itemSubmissions.length <= itemCount) {
|
|
10754
|
-
return null;
|
|
10755
|
-
}
|
|
10756
|
-
return assessmentFlowViolation("At least one candidate must be administered before the review can be completed.", {
|
|
10757
|
-
flow,
|
|
10758
|
-
submittedItemCount: itemSubmissions.length,
|
|
10759
|
-
itemCount
|
|
10760
|
-
});
|
|
10743
|
+
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
10744
|
+
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
10761
10745
|
}
|
|
10762
10746
|
function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
|
|
10763
10747
|
if (candidates.length === 0) {
|
|
10764
10748
|
return { outcome: "none" };
|
|
10765
10749
|
}
|
|
10766
|
-
const
|
|
10767
|
-
if (
|
|
10750
|
+
const eligible = candidates.filter((candidate) => candidate.hasLiveTests);
|
|
10751
|
+
if (eligible.length === 0) {
|
|
10768
10752
|
return { outcome: "no_eligible_tests" };
|
|
10769
10753
|
}
|
|
10770
|
-
const withOpenAttempt = serviceable.filter((candidate) => candidate.hasOpenAttempt);
|
|
10771
|
-
const eligible = withOpenAttempt.length > 0 ? withOpenAttempt : serviceable;
|
|
10772
10754
|
const subjects = new Set(eligible.map((candidate) => candidate.subject)).size;
|
|
10773
10755
|
if (!subjectSpecified && subjects > 1) {
|
|
10774
10756
|
return { outcome: "ambiguous", subjects };
|
|
@@ -10780,12 +10762,9 @@ function assessmentAttemptTimestamp(value) {
|
|
|
10780
10762
|
const parsed = Date.parse(value);
|
|
10781
10763
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
10782
10764
|
}
|
|
10783
|
-
function newestAttempt(left, right, field) {
|
|
10784
|
-
const dateDifference = assessmentAttemptTimestamp(right[field]) - assessmentAttemptTimestamp(left[field]);
|
|
10785
|
-
return dateDifference || left.attemptId.localeCompare(right.attemptId);
|
|
10786
|
-
}
|
|
10787
10765
|
function compareNewestCompletedAssessmentAttempt(left, right) {
|
|
10788
|
-
|
|
10766
|
+
const dateDifference = assessmentAttemptTimestamp(right.scoreDate) - assessmentAttemptTimestamp(left.scoreDate);
|
|
10767
|
+
return dateDifference || left.attemptId.localeCompare(right.attemptId);
|
|
10789
10768
|
}
|
|
10790
10769
|
function compareAssessmentCatalogOrder(left, right) {
|
|
10791
10770
|
if (left.assessmentKey !== null && right.assessmentKey !== null) {
|
|
@@ -10808,31 +10787,22 @@ function orderRuntimeAssessmentTests(tests) {
|
|
|
10808
10787
|
return [...tests].toSorted(compareAssessmentCatalogOrder);
|
|
10809
10788
|
}
|
|
10810
10789
|
function selectRuntimeAssessment(liveTests, attempts) {
|
|
10811
|
-
const resumable = attempts.filter((attempt) => attempt.resumable !== false && (isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt))).toSorted((left, right) => newestAttempt(left, right, "updatedAt"));
|
|
10812
|
-
if (resumable.length > 0) {
|
|
10813
|
-
return {
|
|
10814
|
-
kind: "resume",
|
|
10815
|
-
attempt: resumable[0],
|
|
10816
|
-
additionalResumableAttemptIds: resumable.slice(1).map((candidate) => candidate.attemptId)
|
|
10817
|
-
};
|
|
10818
|
-
}
|
|
10819
10790
|
const orderedTests = orderRuntimeAssessmentTests(liveTests);
|
|
10820
10791
|
if (orderedTests.length === 0) {
|
|
10821
10792
|
return null;
|
|
10822
10793
|
}
|
|
10823
|
-
const
|
|
10824
|
-
const exposedAssessmentKeys = new Set(
|
|
10794
|
+
const history = attempts.filter((attempt) => isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt) || isAssessmentAttemptCompleted(attempt) || isAssessmentAttemptSuperseded(attempt));
|
|
10795
|
+
const exposedAssessmentKeys = new Set(history.map((attempt) => attempt.selectedAssessmentKey));
|
|
10825
10796
|
const unattempted = orderedTests.find((test) => !exposedAssessmentKeys.has(test.assessmentKey));
|
|
10826
10797
|
if (unattempted) {
|
|
10827
|
-
return {
|
|
10798
|
+
return { test: unattempted, reason: "unattempted" };
|
|
10828
10799
|
}
|
|
10829
|
-
const
|
|
10830
|
-
const latestTestIndex =
|
|
10800
|
+
const latest = history.toSorted(compareNewestCompletedAssessmentAttempt)[0];
|
|
10801
|
+
const latestTestIndex = latest ? orderedTests.findIndex((test) => test.assessmentKey === latest.selectedAssessmentKey) : -1;
|
|
10831
10802
|
if (latestTestIndex === -1) {
|
|
10832
|
-
return {
|
|
10803
|
+
return { test: orderedTests[0], reason: "first_live" };
|
|
10833
10804
|
}
|
|
10834
10805
|
return {
|
|
10835
|
-
kind: "start",
|
|
10836
10806
|
test: orderedTests[(latestTestIndex + 1) % orderedTests.length],
|
|
10837
10807
|
reason: "round_robin"
|
|
10838
10808
|
};
|
|
@@ -11090,9 +11060,6 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
11090
11060
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
11091
11061
|
return deterministicUUID(["playcademy:review-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
11092
11062
|
}
|
|
11093
|
-
function reviewQuestionResultId(parentAttemptId, childQuestionLineItemId) {
|
|
11094
|
-
return deterministicUUID(["playcademy:review-question-result:v1", parentAttemptId, childQuestionLineItemId].join("\x00"));
|
|
11095
|
-
}
|
|
11096
11063
|
function qtiQuestionByIdentifier(questions) {
|
|
11097
11064
|
const byIdentifier = new Map;
|
|
11098
11065
|
for (const entry of questions.questions) {
|
|
@@ -11652,11 +11619,8 @@ function isDiagnosticRoutingState(value) {
|
|
|
11652
11619
|
const nextValid = value.next === null || isRecord(value.next) && isNonEmptyString(value.next.stageKey) && isNonEmptyString(value.next.trackKey) && isNonEmptyString(value.next.nodeKey) && isNonEmptyString(value.next.itemIdentifier);
|
|
11653
11620
|
return stagesValid && tracksValid && nextValid && (value.status === "in-progress" && value.next !== null || value.status === "ready-to-complete" && value.next === null);
|
|
11654
11621
|
}
|
|
11655
|
-
function isDiagnosticRoutingSnapshot(value, revision) {
|
|
11656
|
-
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);
|
|
11657
|
-
}
|
|
11658
11622
|
function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersion) {
|
|
11659
|
-
if (!isRecord(value) || !isNonEmptyString(value.definitionId) || !isNonEmptyString(value.diagnosticKey) || !isNonEmptyString(value.testName) || !isNonEmptyString(value.routingRevision) || !Array.isArray(value.ledger) || !value.ledger.every(isDiagnosticRoutingLedgerEntry) ||
|
|
11623
|
+
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) {
|
|
11660
11624
|
return false;
|
|
11661
11625
|
}
|
|
11662
11626
|
const itemIdentifiers = new Set;
|
|
@@ -11710,19 +11674,7 @@ function isPlaycademyAssessmentResultMetadataV1(value) {
|
|
|
11710
11674
|
const review = metadata2.review;
|
|
11711
11675
|
const diagnostic = metadata2.diagnostic;
|
|
11712
11676
|
const routedDiagnostic = metadata2.purpose === "diagnostic" && diagnostic !== undefined && Array.isArray(metadata2.itemSubmissions) && Number.isInteger(metadata2.responseVersion) && isRoutedDiagnosticAttemptMetadata(diagnostic, metadata2.itemSubmissions, metadata2.responseVersion);
|
|
11713
|
-
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);
|
|
11714
|
-
}
|
|
11715
|
-
function isPlaycademyReviewAssessmentItemResultMetadataV1(value) {
|
|
11716
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
11717
|
-
return false;
|
|
11718
|
-
}
|
|
11719
|
-
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");
|
|
11720
|
-
}
|
|
11721
|
-
function isPlaycademyDiagnosticAssessmentItemResultMetadataV1(value) {
|
|
11722
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
11723
|
-
return false;
|
|
11724
|
-
}
|
|
11725
|
-
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";
|
|
11677
|
+
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);
|
|
11726
11678
|
}
|
|
11727
11679
|
function playcademyAssessmentResultMetadata(value) {
|
|
11728
11680
|
if (!isRecord(value)) {
|
|
@@ -11742,12 +11694,6 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11742
11694
|
diagnostic: { ...normalized.diagnostic, ledger: [] }
|
|
11743
11695
|
};
|
|
11744
11696
|
}
|
|
11745
|
-
if (isRecord(normalized.diagnostic) && Array.isArray(normalized.diagnostic.ledger) && normalized.diagnostic.ledger.length === 0 && normalized.diagnostic.routingSnapshots === undefined) {
|
|
11746
|
-
normalized = {
|
|
11747
|
-
...normalized,
|
|
11748
|
-
diagnostic: { ...normalized.diagnostic, routingSnapshots: [] }
|
|
11749
|
-
};
|
|
11750
|
-
}
|
|
11751
11697
|
if (isRecord(normalized.diagnostic) && isRecord(normalized.diagnostic.state)) {
|
|
11752
11698
|
const state = normalized.diagnostic.state;
|
|
11753
11699
|
const tracks = Array.isArray(state.tracks) ? state.tracks.map((track) => isRecord(track) ? {
|
|
@@ -11755,12 +11701,10 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11755
11701
|
currentNodeKey: track.currentNodeKey ?? null,
|
|
11756
11702
|
terminal: track.terminal ?? null
|
|
11757
11703
|
} : track) : state.tracks;
|
|
11758
|
-
const routingSnapshots = Array.isArray(normalized.diagnostic.routingSnapshots) ? normalized.diagnostic.routingSnapshots.map((snapshot) => isRecord(snapshot) ? { ...snapshot, next: snapshot.next ?? null } : snapshot) : normalized.diagnostic.routingSnapshots;
|
|
11759
11704
|
normalized = {
|
|
11760
11705
|
...normalized,
|
|
11761
11706
|
diagnostic: {
|
|
11762
11707
|
...normalized.diagnostic,
|
|
11763
|
-
...routingSnapshots === undefined ? {} : { routingSnapshots },
|
|
11764
11708
|
state: {
|
|
11765
11709
|
...state,
|
|
11766
11710
|
...tracks === undefined ? {} : { tracks },
|
|
@@ -11770,6 +11714,16 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11770
11714
|
};
|
|
11771
11715
|
}
|
|
11772
11716
|
const review = normalized.review;
|
|
11717
|
+
if (isRecord(normalized.transcript)) {
|
|
11718
|
+
const transcript = normalized.transcript;
|
|
11719
|
+
normalized = {
|
|
11720
|
+
...normalized,
|
|
11721
|
+
transcript: {
|
|
11722
|
+
...transcript,
|
|
11723
|
+
administrations: Array.isArray(transcript.administrations) ? transcript.administrations.map((entry) => isRecord(entry) ? { ...entry, responses: entry.responses ?? {} } : entry) : transcript.administrations ?? []
|
|
11724
|
+
}
|
|
11725
|
+
};
|
|
11726
|
+
}
|
|
11773
11727
|
if (isRecord(review) && isRecord(review.fulfillment) && review.fulfillment.status === "complete" && review.fulfillment.shortages === undefined) {
|
|
11774
11728
|
normalized = {
|
|
11775
11729
|
...normalized,
|
|
@@ -11784,29 +11738,7 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11784
11738
|
}
|
|
11785
11739
|
return isPlaycademyAssessmentResultMetadataV1(normalized) ? normalized : null;
|
|
11786
11740
|
}
|
|
11787
|
-
|
|
11788
|
-
if (!isRecord(value)) {
|
|
11789
|
-
return null;
|
|
11790
|
-
}
|
|
11791
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
11792
|
-
if (!isRecord(metadata2)) {
|
|
11793
|
-
return null;
|
|
11794
|
-
}
|
|
11795
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
11796
|
-
return isPlaycademyReviewAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
11797
|
-
}
|
|
11798
|
-
function playcademyDiagnosticAssessmentItemResultMetadata(value) {
|
|
11799
|
-
if (!isRecord(value)) {
|
|
11800
|
-
return null;
|
|
11801
|
-
}
|
|
11802
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
11803
|
-
if (!isRecord(metadata2)) {
|
|
11804
|
-
return null;
|
|
11805
|
-
}
|
|
11806
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
11807
|
-
return isPlaycademyDiagnosticAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
11808
|
-
}
|
|
11809
|
-
var SCRIPT_SCHEMES, DATA_RASTER_IMAGE_PATTERN, QTI_MATHML_ALLOWED_TAGS, metadataSymbol, parser, SUPPORTED_QTI_INTERACTION_TYPES, URL_ATTRIBUTES, IMAGE_URL_ATTRIBUTES, INTERACTION_TYPES, EMPHASIS_TAGS, STRONG_TAGS, CONTENT_SKIPPED_TAGS, BLOCK_CONTENT_KINDS, GAP_MATCH_TOKEN_TAGS, STAGE_GRAPHIC_EXCLUDED_CONTAINERS, BLANK_SENTINEL = "", MARKED_BLANK_PATTERN, MARKED_BLANK_GLOBAL_PATTERN, MATCH_CORRECT_TEMPLATE = "http://www.imsglobal.org/question/qti_v3p0/rptemplates/match_correct", MAP_RESPONSE_TEMPLATE = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response", MAP_RESPONSE_TEMPLATES, RESPONSE_PROCESSING_TEMPLATES, VOID_BODY_ELEMENTS, URL_ATTRIBUTES2, IMAGE_URL_ATTRIBUTES2, VOID_CHOICE_ELEMENTS, NUMERIC_COMPARISON_ATTRIBUTES, RESPONSE_CARDINALITIES, RESPONSE_BASE_TYPES, POINT_INTERACTION_TYPES, INDEPENDENT_PROCESSING_TAGS, PRIVATE_PLAYABLE_KEYS, trimmedNonEmptyString, finiteNumber2, positiveFiniteNumber, IDENTIFIER_VALUE, DIRECTED_PAIR_VALUE, scoringMatchComparisonSchema, scoringNumericEqualComparisonSchema, scoringNumericEqualRoundedComparisonSchema, scoringRuleSchema, scoringRulesSchema, scoringItemSchema, assessmentScoringArtifactItemsSchema, assessmentScoringArtifactSchema, SUPPORTED_BASE_TYPES, SUPPORTED_CARDINALITIES, MATCH_CORRECT_TEMPLATES, UUID_PATTERN, COMMON_CORE_MATH_FRAMEWORK_ALIASES, COMMON_CORE_ELA_FRAMEWORK_ALIASES, COMMON_CORE_FRAMEWORK_ALIASES, ASSESSMENT_ATTEMPT_OPEN, ASSESSMENT_ATTEMPT_COMPLETED, ASSESSMENT_ATTEMPT_AWAITING_AWARD, ASSESSMENT_ATTEMPT_SUPERSEDED, ASSESSMENT_RUNTIME_ERROR_STATUS, ROUTING_KEY_MAX_LENGTH = 128, ROUTING_RESULT_KEY_MAX_LENGTH = 256, ROUTING_STAGE_LIMIT = 32, ROUTING_TRACK_LIMIT = 128, ROUTING_NODE_LIMIT = 1000, ROUTING_TERMINAL_LIMIT = 1000, ROUTING_GROUP_LIMIT = 128, ROUTING_PREDICATE_DEPTH_LIMIT = 8, ROUTING_EXPLORATION_STATE_LIMIT = 200000, RoutingKeySchema, RoutingResultKeySchema, DiagnosticTrackOutcomeSchema, DiagnosticActivationPredicateSchema, DiagnosticRoutingTransitionSchema, DiagnosticRoutingManifestV1Schema, GRADE_VALUES, POINT_RESPONSE_PATTERN, ZERO_WIDTH_CHARACTERS, CURLY_SINGLE_QUOTES, CURLY_DOUBLE_QUOTES, UNICODE_DASHES, ARITHMETIC_OPERATOR_SPACING, THOUSANDS_GROUP, REVIEW_SELECTION_POLICY_VERSION = "unseen-first-least-recently-delivered-v1", PLAYCADEMY_REVIEW_BANK_MANIFEST_METADATA_KEY = "playcademyReviewBank", PLAYCADEMY_REVIEW_BANK_MANIFEST_VERSION = 1, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION = 8, ASSESSMENT_CHILD_RESULT_KIND = "assessment-child-result", ASSESSMENT_CHILD_RESULT_VERSION = 1, ASSESSMENT_CHILD_RESULT_ID_NAMESPACE = "playcademy:assessment-child-result:v1", IdentitySchema, RecordedAtSchema, ResponseKeySchema, AssessmentResponseValueSchema, AssessmentScoreSchema, AssessmentStandardRefSchema, DiagnosticRoutingLedgerEntryV1Schema, AssessmentChildResultAttemptV1Schema, AssessmentChildResultAdministrationV1Schema, AssessmentChildResultGradingV1Schema, ReviewChildResultContextV1Schema, DiagnosticChildResultContextV1Schema, CommonPayloadShape, ReviewChildResultPayloadV1Schema, DiagnosticChildResultPayloadV1Schema, AssessmentChildResultPayloadV1Schema, AssessmentChildResultCommandV1Schema, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema2, AssessmentRuntimeIdentitySchema, ResponseKeySchema2, StartAssessmentBaseSchema, AssessmentStandardRefSchema2, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, AssessmentPreparationReceiptSchema, StartAssessmentRequestBodySchema, SaveAssessmentBodySchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, FinalizeAssessmentBodySchema, PrepareRuntimeAssessmentRequestSchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema, FinalizeRuntimeAssessmentRequestSchema;
|
|
11741
|
+
var SCRIPT_SCHEMES, DATA_RASTER_IMAGE_PATTERN, QTI_MATHML_ALLOWED_TAGS, metadataSymbol, parser, SUPPORTED_QTI_INTERACTION_TYPES, URL_ATTRIBUTES, IMAGE_URL_ATTRIBUTES, INTERACTION_TYPES, EMPHASIS_TAGS, STRONG_TAGS, CONTENT_SKIPPED_TAGS, BLOCK_CONTENT_KINDS, GAP_MATCH_TOKEN_TAGS, STAGE_GRAPHIC_EXCLUDED_CONTAINERS, BLANK_SENTINEL = "", MARKED_BLANK_PATTERN, MARKED_BLANK_GLOBAL_PATTERN, MATCH_CORRECT_TEMPLATE = "http://www.imsglobal.org/question/qti_v3p0/rptemplates/match_correct", MAP_RESPONSE_TEMPLATE = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response", MAP_RESPONSE_TEMPLATES, RESPONSE_PROCESSING_TEMPLATES, VOID_BODY_ELEMENTS, URL_ATTRIBUTES2, IMAGE_URL_ATTRIBUTES2, VOID_CHOICE_ELEMENTS, NUMERIC_COMPARISON_ATTRIBUTES, RESPONSE_CARDINALITIES, RESPONSE_BASE_TYPES, POINT_INTERACTION_TYPES, INDEPENDENT_PROCESSING_TAGS, PRIVATE_PLAYABLE_KEYS, trimmedNonEmptyString, finiteNumber2, positiveFiniteNumber, IDENTIFIER_VALUE, DIRECTED_PAIR_VALUE, scoringMatchComparisonSchema, scoringNumericEqualComparisonSchema, scoringNumericEqualRoundedComparisonSchema, scoringRuleSchema, scoringRulesSchema, scoringItemSchema, assessmentScoringArtifactItemsSchema, assessmentScoringArtifactSchema, SUPPORTED_BASE_TYPES, SUPPORTED_CARDINALITIES, MATCH_CORRECT_TEMPLATES, UUID_PATTERN, COMMON_CORE_MATH_FRAMEWORK_ALIASES, COMMON_CORE_ELA_FRAMEWORK_ALIASES, COMMON_CORE_FRAMEWORK_ALIASES, ASSESSMENT_ATTEMPT_OPEN, ASSESSMENT_ATTEMPT_COMPLETED, ASSESSMENT_ATTEMPT_AWAITING_AWARD, ASSESSMENT_ATTEMPT_SUPERSEDED, ASSESSMENT_RUNTIME_ERROR_STATUS, GRADE_VALUES, ROUTING_KEY_MAX_LENGTH = 128, ROUTING_RESULT_KEY_MAX_LENGTH = 256, ROUTING_STAGE_LIMIT = 32, ROUTING_TRACK_LIMIT = 128, ROUTING_NODE_LIMIT = 1000, ROUTING_TERMINAL_LIMIT = 1000, ROUTING_GROUP_LIMIT = 128, ROUTING_PREDICATE_DEPTH_LIMIT = 8, ROUTING_EXPLORATION_STATE_LIMIT = 200000, RoutingKeySchema, RoutingResultKeySchema, DiagnosticTrackOutcomeSchema, DiagnosticActivationPredicateSchema, DiagnosticRoutingTransitionSchema, DiagnosticRoutingManifestV1Schema, POINT_RESPONSE_PATTERN, Identity, AssessmentTranscriptSchema, AssessmentTranscriptError, ZERO_WIDTH_CHARACTERS, CURLY_SINGLE_QUOTES, CURLY_DOUBLE_QUOTES, UNICODE_DASHES, ARITHMETIC_OPERATOR_SPACING, THOUSANDS_GROUP, REVIEW_SELECTION_POLICY_VERSION = "unseen-first-least-recently-delivered-v1", PLAYCADEMY_REVIEW_BANK_MANIFEST_METADATA_KEY = "playcademyReviewBank", PLAYCADEMY_REVIEW_BANK_MANIFEST_VERSION = 1, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION = 8, ASSESSMENT_CHILD_RESULT_KIND = "assessment-child-result", ASSESSMENT_CHILD_RESULT_VERSION = 1, ASSESSMENT_CHILD_RESULT_ID_NAMESPACE = "playcademy:assessment-child-result:v1", IdentitySchema, RecordedAtSchema, ResponseKeySchema, AssessmentResponseValueSchema, AssessmentScoreSchema, AssessmentStandardRefSchema, DiagnosticRoutingLedgerEntryV1Schema, AssessmentChildResultAttemptV1Schema, AssessmentChildResultAdministrationV1Schema, AssessmentChildResultGradingV1Schema, ReviewChildResultContextV1Schema, FixedChildResultContextV1Schema, DiagnosticChildResultContextV1Schema, CommonPayloadShape, ReviewChildResultPayloadV1Schema, FixedChildResultPayloadV1Schema, DiagnosticChildResultPayloadV1Schema, AssessmentChildResultPayloadV1Schema, AssessmentChildResultCommandV1Schema, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema2, AssessmentRuntimeIdentitySchema, ResponseKeySchema2, StartAssessmentBaseSchema, AssessmentStandardRefSchema2, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, AssessmentPreparationReceiptSchema, StartAssessmentRequestBodySchema, SaveAssessmentBodySchema, RecordAssessmentProgressBodySchema, ResumeAssessmentBodySchema, ResumeRuntimeAssessmentRequestSchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, FinalizeAssessmentBodySchema, PrepareRuntimeAssessmentRequestSchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, RecordRuntimeAssessmentProgressRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema, FinalizeRuntimeAssessmentRequestSchema;
|
|
11810
11742
|
var init_assessment_runtime2 = __esm(() => {
|
|
11811
11743
|
init_timeback3();
|
|
11812
11744
|
init_timeback3();
|
|
@@ -11816,13 +11748,14 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
11816
11748
|
init_timeback3();
|
|
11817
11749
|
init_esm();
|
|
11818
11750
|
init_esm();
|
|
11751
|
+
init_src();
|
|
11752
|
+
init_uuid();
|
|
11753
|
+
init_esm();
|
|
11819
11754
|
init_timeback4();
|
|
11820
11755
|
init_src();
|
|
11821
11756
|
init_timeback3();
|
|
11822
11757
|
init_src();
|
|
11823
11758
|
init_uuid();
|
|
11824
|
-
init_src();
|
|
11825
|
-
init_uuid();
|
|
11826
11759
|
init_esm();
|
|
11827
11760
|
init_src();
|
|
11828
11761
|
init_uuid();
|
|
@@ -12118,6 +12051,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12118
12051
|
ATTEMPT_ALREADY_SUBMITTED: 409,
|
|
12119
12052
|
ASSESSMENT_FLOW_VIOLATION: 409
|
|
12120
12053
|
};
|
|
12054
|
+
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12121
12055
|
RoutingKeySchema = exports_external.string().trim().min(1).max(ROUTING_KEY_MAX_LENGTH);
|
|
12122
12056
|
RoutingResultKeySchema = exports_external.string().trim().min(1).max(ROUTING_RESULT_KEY_MAX_LENGTH);
|
|
12123
12057
|
DiagnosticTrackOutcomeSchema = exports_external.enum(DIAGNOSTIC_TRACK_OUTCOMES);
|
|
@@ -12175,8 +12109,31 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12175
12109
|
resultKey: RoutingResultKeySchema
|
|
12176
12110
|
}).strict()).min(1).max(ROUTING_TERMINAL_LIMIT)
|
|
12177
12111
|
}).strict();
|
|
12178
|
-
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12179
12112
|
POINT_RESPONSE_PATTERN = /^-?\d+(?:\.\d+)? -?\d+(?:\.\d+)?$/;
|
|
12113
|
+
Identity = exports_external.string().min(1).max(512).refine((value) => value.trim() === value);
|
|
12114
|
+
AssessmentTranscriptSchema = exports_external.object({
|
|
12115
|
+
version: exports_external.literal(1),
|
|
12116
|
+
attemptId: Identity,
|
|
12117
|
+
contentRevision: Identity,
|
|
12118
|
+
administrations: exports_external.array(exports_external.object({
|
|
12119
|
+
submissionId: Identity,
|
|
12120
|
+
itemIdentifier: Identity,
|
|
12121
|
+
routingNodeKey: Identity.optional(),
|
|
12122
|
+
responses: exports_external.record(Identity, exports_external.custom(isAssessmentResponseValue))
|
|
12123
|
+
}).strict()).max(1000)
|
|
12124
|
+
}).strict().refine((value) => new TextEncoder().encode(JSON.stringify(value)).length <= 256000, {
|
|
12125
|
+
message: "Assessment transcript exceeds 256 KB"
|
|
12126
|
+
});
|
|
12127
|
+
AssessmentTranscriptError = class AssessmentTranscriptError extends Error {
|
|
12128
|
+
index;
|
|
12129
|
+
code;
|
|
12130
|
+
constructor(message, index = 0, code = "ASSESSMENT_FLOW_VIOLATION") {
|
|
12131
|
+
super(message);
|
|
12132
|
+
this.name = "AssessmentTranscriptError";
|
|
12133
|
+
this.index = index;
|
|
12134
|
+
this.code = code;
|
|
12135
|
+
}
|
|
12136
|
+
};
|
|
12180
12137
|
ZERO_WIDTH_CHARACTERS = /[\u200B\uFEFF\u200C\u200D]/g;
|
|
12181
12138
|
CURLY_SINGLE_QUOTES = /[\u2018\u2019\u201A\u201B]/g;
|
|
12182
12139
|
CURLY_DOUBLE_QUOTES = /[\u201C\u201D\u201E\u201F]/g;
|
|
@@ -12268,6 +12225,10 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12268
12225
|
bankRevision: IdentitySchema,
|
|
12269
12226
|
standard: AssessmentStandardRefSchema
|
|
12270
12227
|
}).strict();
|
|
12228
|
+
FixedChildResultContextV1Schema = exports_external.object({
|
|
12229
|
+
kind: exports_external.literal("fixed"),
|
|
12230
|
+
purpose: exports_external.enum(["mastery", "end_of_course", "diagnostic"])
|
|
12231
|
+
}).strict();
|
|
12271
12232
|
DiagnosticChildResultContextV1Schema = exports_external.object({
|
|
12272
12233
|
kind: exports_external.literal("platform-routed-diagnostic"),
|
|
12273
12234
|
definitionId: IdentitySchema,
|
|
@@ -12287,6 +12248,11 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12287
12248
|
isCorrect: exports_external.boolean().optional(),
|
|
12288
12249
|
context: ReviewChildResultContextV1Schema
|
|
12289
12250
|
}).strict();
|
|
12251
|
+
FixedChildResultPayloadV1Schema = exports_external.object({
|
|
12252
|
+
...CommonPayloadShape,
|
|
12253
|
+
isCorrect: exports_external.boolean().optional(),
|
|
12254
|
+
context: FixedChildResultContextV1Schema
|
|
12255
|
+
}).strict();
|
|
12290
12256
|
DiagnosticChildResultPayloadV1Schema = exports_external.object({
|
|
12291
12257
|
...CommonPayloadShape,
|
|
12292
12258
|
isCorrect: exports_external.boolean(),
|
|
@@ -12302,7 +12268,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12302
12268
|
});
|
|
12303
12269
|
AssessmentChildResultPayloadV1Schema = exports_external.union([
|
|
12304
12270
|
ReviewChildResultPayloadV1Schema,
|
|
12305
|
-
DiagnosticChildResultPayloadV1Schema
|
|
12271
|
+
DiagnosticChildResultPayloadV1Schema,
|
|
12272
|
+
FixedChildResultPayloadV1Schema
|
|
12306
12273
|
]);
|
|
12307
12274
|
AssessmentChildResultCommandV1Schema = exports_external.object({
|
|
12308
12275
|
kind: exports_external.literal(ASSESSMENT_CHILD_RESULT_KIND),
|
|
@@ -12379,9 +12346,19 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12379
12346
|
StartAssessmentRequestBodySchema = exports_external.intersection(StartAssessmentBodySchema, exports_external.object({ preparationReceipt: AssessmentPreparationReceiptSchema.optional() }));
|
|
12380
12347
|
SaveAssessmentBodySchema = exports_external.object({
|
|
12381
12348
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12382
|
-
|
|
12349
|
+
transcript: AssessmentTranscriptSchema
|
|
12350
|
+
});
|
|
12351
|
+
RecordAssessmentProgressBodySchema = SaveAssessmentBodySchema.extend({
|
|
12352
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12353
|
+
itemIdentifier: ResponseKeySchema2
|
|
12383
12354
|
});
|
|
12355
|
+
ResumeAssessmentBodySchema = exports_external.object({
|
|
12356
|
+
transcript: AssessmentTranscriptSchema.optional()
|
|
12357
|
+
});
|
|
12358
|
+
ResumeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(ResumeAssessmentBodySchema.shape);
|
|
12384
12359
|
SubmitAssessmentItemBodySchema = exports_external.object({
|
|
12360
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12361
|
+
transcript: AssessmentTranscriptSchema,
|
|
12385
12362
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12386
12363
|
submissionId: exports_external.string().trim().min(1),
|
|
12387
12364
|
itemIdentifier: ResponseKeySchema2,
|
|
@@ -12389,6 +12366,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12389
12366
|
responses: exports_external.record(ResponseKeySchema2, AssessmentResponseValueSchema2.nullable())
|
|
12390
12367
|
});
|
|
12391
12368
|
SubmitAssessmentBodySchema = exports_external.object({
|
|
12369
|
+
transcript: AssessmentTranscriptSchema,
|
|
12392
12370
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12393
12371
|
submissionId: exports_external.string().trim().min(1),
|
|
12394
12372
|
session: exports_external.custom(isAssessmentSessionTiming, {
|
|
@@ -12407,6 +12385,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12407
12385
|
PrepareRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
|
|
12408
12386
|
StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentRequestBodySchema);
|
|
12409
12387
|
SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
|
|
12388
|
+
RecordRuntimeAssessmentProgressRequestSchema = AssessmentRuntimeIdentitySchema.extend(RecordAssessmentProgressBodySchema.shape);
|
|
12410
12389
|
SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
|
|
12411
12390
|
SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentBodySchema.shape).extend({
|
|
12412
12391
|
appName: exports_external.string().trim().min(1),
|
|
@@ -100206,22 +100185,6 @@ var init_locks = __esm(() => {
|
|
|
100206
100185
|
});
|
|
100207
100186
|
|
|
100208
100187
|
// ../api-core/src/utils/assessment-runtime-lock.util.ts
|
|
100209
|
-
async function crossAssessmentAttemptLockBarrier(lock, db2, attemptId) {
|
|
100210
|
-
const maxAttempts = 5;
|
|
100211
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
|
|
100212
|
-
try {
|
|
100213
|
-
await lock(db2, [assessmentRuntimeAttemptLockKey(attemptId)], async () => {
|
|
100214
|
-
return;
|
|
100215
|
-
});
|
|
100216
|
-
return;
|
|
100217
|
-
} catch (error88) {
|
|
100218
|
-
if (!(error88 instanceof ServiceUnavailableError) || attempt === maxAttempts) {
|
|
100219
|
-
throw error88;
|
|
100220
|
-
}
|
|
100221
|
-
await sleep(25);
|
|
100222
|
-
}
|
|
100223
|
-
}
|
|
100224
|
-
}
|
|
100225
100188
|
function normalizeKeys(keys) {
|
|
100226
100189
|
return [...new Set(keys)].toSorted();
|
|
100227
100190
|
}
|
|
@@ -100294,17 +100257,6 @@ var init_assessment_runtime_lock_util = __esm(() => {
|
|
|
100294
100257
|
});
|
|
100295
100258
|
|
|
100296
100259
|
// ../api-core/src/utils/timeback-assessment-runtime.util.ts
|
|
100297
|
-
function assessmentPreparationContentionError(expectedResponseVersion, responseVersion, kind) {
|
|
100298
|
-
if (expectedResponseVersion === responseVersion) {
|
|
100299
|
-
return new ServiceUnavailableError(`The ${kind} response state changed repeatedly while this item was being prepared. Try again shortly.`, {
|
|
100300
|
-
retryable: true,
|
|
100301
|
-
reason: "PREPARATION_CONTENTION",
|
|
100302
|
-
expectedResponseVersion,
|
|
100303
|
-
responseVersion
|
|
100304
|
-
});
|
|
100305
|
-
}
|
|
100306
|
-
return AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, responseVersion));
|
|
100307
|
-
}
|
|
100308
100260
|
function assessmentTrackableCourse(integration) {
|
|
100309
100261
|
if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
|
|
100310
100262
|
return null;
|
|
@@ -100370,7 +100322,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100370
100322
|
...input.activityData ? { activityData: input.activityData } : {},
|
|
100371
100323
|
assessment: assessmentPresentationForAttempt(input.assessment, input.attemptId),
|
|
100372
100324
|
responses: metadata2.responses,
|
|
100373
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
100325
|
+
itemSubmissions: assessmentFlowForPurpose(metadata2.purpose) === "item-submit" ? metadata2.itemSubmissions : [],
|
|
100374
100326
|
score: null,
|
|
100375
100327
|
selection
|
|
100376
100328
|
};
|
|
@@ -100385,13 +100337,6 @@ function buildAssessmentAwardRecord(input, mastery) {
|
|
|
100385
100337
|
...mastery.warnings ? { warnings: mastery.warnings } : {}
|
|
100386
100338
|
};
|
|
100387
100339
|
}
|
|
100388
|
-
function stageAssessmentAttemptSupersession(attempt) {
|
|
100389
|
-
Object.assign(attempt.result, ASSESSMENT_ATTEMPT_SUPERSEDED);
|
|
100390
|
-
Object.assign(attempt.selection, ASSESSMENT_ATTEMPT_SUPERSEDED);
|
|
100391
|
-
}
|
|
100392
|
-
function shouldConfirmAssessmentVersionUnavailable(details) {
|
|
100393
|
-
return typeof details === "object" && details !== null && "expectedRevision" in details && typeof details.expectedRevision === "string" && !("currentRevision" in details);
|
|
100394
|
-
}
|
|
100395
100340
|
function buildAssessmentAttemptSupersessionUpdate(result) {
|
|
100396
100341
|
if (result.status !== "active" || !isAssessmentAttemptOpen({
|
|
100397
100342
|
inProgress: result.inProgress ?? "",
|
|
@@ -100409,162 +100354,9 @@ function buildAssessmentAttemptSupersessionUpdate(result) {
|
|
|
100409
100354
|
metadata: result.metadata ?? {}
|
|
100410
100355
|
};
|
|
100411
100356
|
}
|
|
100412
|
-
async function resolveAvailableAssessmentAttempt(initialDecision, select3, resume) {
|
|
100413
|
-
let decision = initialDecision;
|
|
100414
|
-
while (decision?.kind === "resume") {
|
|
100415
|
-
const snapshot = await resume(decision);
|
|
100416
|
-
if (snapshot !== null) {
|
|
100417
|
-
return { kind: "resumed", snapshot };
|
|
100418
|
-
}
|
|
100419
|
-
decision = select3();
|
|
100420
|
-
}
|
|
100421
|
-
return { kind: "continue", decision };
|
|
100422
|
-
}
|
|
100423
|
-
function buildReviewItemResultMetadata(input) {
|
|
100424
|
-
const administration = itemAdministration(input.metadata.itemSubmissions, input.selection.itemIdentifier);
|
|
100425
|
-
if (!administration) {
|
|
100426
|
-
throw new Error(`Cannot create a review result for unadministered candidate ${input.selection.itemIdentifier}`);
|
|
100427
|
-
}
|
|
100428
|
-
return {
|
|
100429
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
100430
|
-
parentAttemptId: input.parentAttemptId,
|
|
100431
|
-
activityId: input.metadata.activityId,
|
|
100432
|
-
integrationId: input.metadata.integrationId,
|
|
100433
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
100434
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
100435
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
100436
|
-
standard: input.selection.standard,
|
|
100437
|
-
deliveredAt: administration.submittedAt,
|
|
100438
|
-
responseVersion: input.responseVersion ?? input.metadata.responseVersion,
|
|
100439
|
-
responses: input.metadata.responses[input.selection.itemIdentifier] ?? {},
|
|
100440
|
-
...input.submissionId ? { submissionId: input.submissionId } : {},
|
|
100441
|
-
...input.isCorrect === undefined ? {} : { isCorrect: input.isCorrect }
|
|
100442
|
-
};
|
|
100443
|
-
}
|
|
100444
|
-
function buildOpenReviewChildResult(input) {
|
|
100445
|
-
return {
|
|
100446
|
-
status: "active",
|
|
100447
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100448
|
-
student: input.student,
|
|
100449
|
-
score: 0,
|
|
100450
|
-
scoreDate: input.metadata.deliveredAt,
|
|
100451
|
-
...ASSESSMENT_ATTEMPT_OPEN,
|
|
100452
|
-
metadata: {
|
|
100453
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: input.metadata
|
|
100454
|
-
}
|
|
100455
|
-
};
|
|
100456
|
-
}
|
|
100457
100357
|
function diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
100458
100358
|
return deterministicUUID(["playcademy:diagnostic-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
100459
100359
|
}
|
|
100460
|
-
function diagnosticQuestionResultId(parentAttemptId, childLineItemId) {
|
|
100461
|
-
return deterministicUUID(["playcademy:diagnostic-question-result:v1", parentAttemptId, childLineItemId].join("\x00"));
|
|
100462
|
-
}
|
|
100463
|
-
function diagnosticAdministration(input) {
|
|
100464
|
-
const ledgerIndex = input.metadata.diagnostic.ledger.findIndex((entry) => entry.routingNodeKey === input.ledgerEntry.routingNodeKey);
|
|
100465
|
-
const persistedEntry = input.metadata.diagnostic.ledger[ledgerIndex];
|
|
100466
|
-
const administration = input.metadata.itemSubmissions[ledgerIndex];
|
|
100467
|
-
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) {
|
|
100468
|
-
throw new Error(`Cannot create a diagnostic result for uncommitted routing node ${input.ledgerEntry.routingNodeKey}`);
|
|
100469
|
-
}
|
|
100470
|
-
return administration;
|
|
100471
|
-
}
|
|
100472
|
-
function buildDiagnosticItemResultMetadata(input) {
|
|
100473
|
-
const administration = diagnosticAdministration(input);
|
|
100474
|
-
return {
|
|
100475
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
100476
|
-
parentAttemptId: input.parentAttemptId,
|
|
100477
|
-
activityId: input.metadata.activityId,
|
|
100478
|
-
integrationId: input.metadata.integrationId,
|
|
100479
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
100480
|
-
definitionId: input.metadata.diagnostic.definitionId,
|
|
100481
|
-
diagnosticKey: input.metadata.diagnostic.diagnosticKey,
|
|
100482
|
-
routingRevision: input.metadata.diagnostic.routingRevision,
|
|
100483
|
-
contentRevision: input.metadata.selectedTest.contentRevision,
|
|
100484
|
-
stageKey: input.ledgerEntry.stageKey,
|
|
100485
|
-
trackKey: input.ledgerEntry.trackKey,
|
|
100486
|
-
routingNodeKey: input.ledgerEntry.routingNodeKey,
|
|
100487
|
-
itemIdentifier: input.ledgerEntry.itemIdentifier,
|
|
100488
|
-
deliveredAt: administration.submittedAt,
|
|
100489
|
-
responseVersion: administration.responseVersion,
|
|
100490
|
-
responses: input.metadata.responses[input.ledgerEntry.itemIdentifier] ?? {},
|
|
100491
|
-
submissionId: administration.submissionId,
|
|
100492
|
-
isCorrect: input.ledgerEntry.isCorrect
|
|
100493
|
-
};
|
|
100494
|
-
}
|
|
100495
|
-
function resolveDiagnosticChildResult(input) {
|
|
100496
|
-
if (!input.childResult) {
|
|
100497
|
-
return { action: "create" };
|
|
100498
|
-
}
|
|
100499
|
-
const childMetadata = playcademyDiagnosticAssessmentItemResultMetadata(input.childResult.metadata);
|
|
100500
|
-
const expectedMetadata = buildDiagnosticItemResultMetadata({
|
|
100501
|
-
metadata: input.parentMetadata,
|
|
100502
|
-
ledgerEntry: input.ledgerEntry,
|
|
100503
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
100504
|
-
});
|
|
100505
|
-
if (input.childResult.assessmentLineItem.sourcedId !== input.childLineItemId || input.childResult.student.sourcedId !== input.parentResult.student.sourcedId || !childMetadata || canonicalJson2(childMetadata) !== canonicalJson2(expectedMetadata)) {
|
|
100506
|
-
return { action: "restore", reason: "incompatible" };
|
|
100507
|
-
}
|
|
100508
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: expectedMetadata } : { action: "restore", reason: "inactive" };
|
|
100509
|
-
}
|
|
100510
|
-
function buildFinalizedDiagnosticChildResult(input) {
|
|
100511
|
-
const administration = diagnosticAdministration(input);
|
|
100512
|
-
const metadata2 = buildDiagnosticItemResultMetadata(input);
|
|
100513
|
-
return {
|
|
100514
|
-
resultUpdate: {
|
|
100515
|
-
status: "active",
|
|
100516
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100517
|
-
student: input.student,
|
|
100518
|
-
score: administration.score.earned,
|
|
100519
|
-
scoreDate: administration.submittedAt,
|
|
100520
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
100521
|
-
metadata: {
|
|
100522
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
100523
|
-
}
|
|
100524
|
-
}
|
|
100525
|
-
};
|
|
100526
|
-
}
|
|
100527
|
-
function buildFinalizedReviewChildResult(input) {
|
|
100528
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
100529
|
-
metadata: input.metadata,
|
|
100530
|
-
selection: input.selection,
|
|
100531
|
-
parentAttemptId: input.parentAttemptId,
|
|
100532
|
-
submissionId: input.submissionId,
|
|
100533
|
-
isCorrect: input.itemResult.isCorrect
|
|
100534
|
-
});
|
|
100535
|
-
return {
|
|
100536
|
-
resultUpdate: {
|
|
100537
|
-
status: "active",
|
|
100538
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100539
|
-
student: input.student,
|
|
100540
|
-
score: input.itemResult.score.earned,
|
|
100541
|
-
scoreDate: input.timestamp,
|
|
100542
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
100543
|
-
metadata: {
|
|
100544
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
100545
|
-
}
|
|
100546
|
-
},
|
|
100547
|
-
outcome: {
|
|
100548
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
100549
|
-
standard: input.selection.standard,
|
|
100550
|
-
answered: Object.keys(input.metadata.responses[input.selection.itemIdentifier] ?? {}).length > 0,
|
|
100551
|
-
score: input.itemResult.score,
|
|
100552
|
-
isCorrect: input.itemResult.isCorrect
|
|
100553
|
-
}
|
|
100554
|
-
};
|
|
100555
|
-
}
|
|
100556
|
-
function resolveReviewChildResult(input) {
|
|
100557
|
-
if (!input.childResult) {
|
|
100558
|
-
return { action: "create" };
|
|
100559
|
-
}
|
|
100560
|
-
const childMetadata = playcademyAssessmentItemResultMetadata(input.childResult.metadata);
|
|
100561
|
-
const expectedResponses = input.parentMetadata.responses[input.selection.itemIdentifier] ?? {};
|
|
100562
|
-
const administration = itemAdministration(input.parentMetadata.itemSubmissions, input.selection.itemIdentifier);
|
|
100563
|
-
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)) {
|
|
100564
|
-
return { action: "restore", reason: "incompatible" };
|
|
100565
|
-
}
|
|
100566
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: childMetadata } : { action: "restore", reason: "inactive" };
|
|
100567
|
-
}
|
|
100568
100360
|
function buildAssessmentSubmitResult(input) {
|
|
100569
100361
|
if (!input.metadata.score || !input.metadata.submissionId) {
|
|
100570
100362
|
return null;
|
|
@@ -100950,32 +100742,12 @@ function assessmentFixtureScoringKeys(assessment, questions) {
|
|
|
100950
100742
|
...Object.keys(responseAreas).length > 0 ? { responseAreas } : {}
|
|
100951
100743
|
};
|
|
100952
100744
|
}
|
|
100953
|
-
function prepareDiagnosticAssessmentResponses(assessment, current, input) {
|
|
100954
|
-
const update2 = { [input.itemIdentifier]: input.responses };
|
|
100955
|
-
validateAssessmentResponseUpdate(assessment, update2);
|
|
100956
|
-
const responses = applyAssessmentResponseUpdate(current, update2);
|
|
100957
|
-
const itemResponses = responses[input.itemIdentifier];
|
|
100958
|
-
validateAssessmentDraftResponses(assessment, itemResponses ? { [input.itemIdentifier]: itemResponses } : {});
|
|
100959
|
-
return responses;
|
|
100960
|
-
}
|
|
100961
100745
|
function validateAssessmentResponseUpdate(assessment, update2) {
|
|
100962
100746
|
const message = assessmentResponseUpdateValidationMessage(assessment, update2);
|
|
100963
100747
|
if (message) {
|
|
100964
100748
|
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100965
100749
|
}
|
|
100966
100750
|
}
|
|
100967
|
-
function validateAssessmentResponses(assessment, responses) {
|
|
100968
|
-
const message = assessmentResponseValidationMessage(assessment, responses);
|
|
100969
|
-
if (message) {
|
|
100970
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100971
|
-
}
|
|
100972
|
-
}
|
|
100973
|
-
function validateAssessmentDraftResponses(assessment, responses) {
|
|
100974
|
-
const message = assessmentDraftResponseValidationMessage(assessment, responses);
|
|
100975
|
-
if (message) {
|
|
100976
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100977
|
-
}
|
|
100978
|
-
}
|
|
100979
100751
|
function assessmentScoringRequests(assessment, responses) {
|
|
100980
100752
|
return assessment.items.map((item) => ({
|
|
100981
100753
|
itemIdentifier: item.identifier,
|
|
@@ -101015,6 +100787,21 @@ function assessmentScoreFromPercentage(percentage, possible) {
|
|
|
101015
100787
|
const earned = Math.round(normalized * possible * 1e6) / 1e6;
|
|
101016
100788
|
return { earned, possible, normalized };
|
|
101017
100789
|
}
|
|
100790
|
+
function buildAssessmentResultCheckpoint(input) {
|
|
100791
|
+
return {
|
|
100792
|
+
status: input.result.status,
|
|
100793
|
+
assessmentLineItem: input.result.assessmentLineItem,
|
|
100794
|
+
student: input.result.student,
|
|
100795
|
+
score: input.result.score ?? 0,
|
|
100796
|
+
scoreDate: input.result.scoreDate,
|
|
100797
|
+
...ASSESSMENT_ATTEMPT_OPEN,
|
|
100798
|
+
metadata: mergeAssessmentRuntimeMetadata(input.result.metadata, {
|
|
100799
|
+
...input.metadata,
|
|
100800
|
+
transcript: input.transcript,
|
|
100801
|
+
updatedAt: input.timestamp
|
|
100802
|
+
})
|
|
100803
|
+
};
|
|
100804
|
+
}
|
|
101018
100805
|
function buildAssessmentResultSubmission(input) {
|
|
101019
100806
|
const metadata2 = {
|
|
101020
100807
|
...input.metadata,
|
|
@@ -102419,6 +102206,183 @@ var init_timeback_assessment_artifacts_util = __esm(() => {
|
|
|
102419
102206
|
init_timeback_review_mapping_util();
|
|
102420
102207
|
});
|
|
102421
102208
|
|
|
102209
|
+
// ../api-core/src/utils/timeback-assessment-scoring.util.ts
|
|
102210
|
+
async function scoreCachedAssessmentItems(input) {
|
|
102211
|
+
const selected = input.assessment.items.filter((item) => input.itemIdentifiers.has(item.identifier));
|
|
102212
|
+
const keys = new Map(await Promise.all(selected.map(async (item) => [
|
|
102213
|
+
item.identifier,
|
|
102214
|
+
await sha256Hex(canonicalJson2({
|
|
102215
|
+
identity: input.artifactIdentity,
|
|
102216
|
+
purpose: input.purpose,
|
|
102217
|
+
itemIdentifier: item.identifier,
|
|
102218
|
+
responses: input.responses[item.identifier] ?? {},
|
|
102219
|
+
strategy: input.artifact?.items.find((candidate) => candidate.itemIdentifier === item.identifier)?.strategy ?? "qti"
|
|
102220
|
+
}))
|
|
102221
|
+
])));
|
|
102222
|
+
const scored = new Map;
|
|
102223
|
+
for (const item of selected) {
|
|
102224
|
+
const cached3 = input.cache.get(keys.get(item.identifier));
|
|
102225
|
+
if (cached3) {
|
|
102226
|
+
scored.set(item.identifier, cached3);
|
|
102227
|
+
}
|
|
102228
|
+
}
|
|
102229
|
+
const missing = new Set(selected.filter((item) => !scored.has(item.identifier)).map((item) => item.identifier));
|
|
102230
|
+
const fresh = await scoreAssessmentItems({ ...input, itemIdentifiers: missing });
|
|
102231
|
+
for (const result of fresh) {
|
|
102232
|
+
input.cache.set(keys.get(result.itemIdentifier), result);
|
|
102233
|
+
scored.set(result.itemIdentifier, result);
|
|
102234
|
+
}
|
|
102235
|
+
return selected.map((item) => scored.get(item.identifier));
|
|
102236
|
+
}
|
|
102237
|
+
async function scoreAssessmentItems({
|
|
102238
|
+
assessment,
|
|
102239
|
+
responses,
|
|
102240
|
+
artifactIdentity,
|
|
102241
|
+
purpose,
|
|
102242
|
+
itemIdentifiers,
|
|
102243
|
+
artifact,
|
|
102244
|
+
processResponse,
|
|
102245
|
+
onEvent
|
|
102246
|
+
}) {
|
|
102247
|
+
const scoringItems = assessmentScoringRequests(assessment, responses).filter((item) => !itemIdentifiers || itemIdentifiers.has(item.itemIdentifier));
|
|
102248
|
+
const artifactItems = new Map(artifact?.items.map((item) => [item.itemIdentifier, item]));
|
|
102249
|
+
const scored = new Map;
|
|
102250
|
+
const qti2 = [];
|
|
102251
|
+
for (const item of scoringItems) {
|
|
102252
|
+
const artifactItem = artifactItems.get(item.itemIdentifier);
|
|
102253
|
+
let fallback2 = null;
|
|
102254
|
+
if (!artifact) {
|
|
102255
|
+
fallback2 = { reason: "artifact-unavailable" };
|
|
102256
|
+
} else if (!artifactItem) {
|
|
102257
|
+
fallback2 = { reason: "artifact-item-missing" };
|
|
102258
|
+
} else if (artifactItem.strategy === "qti") {
|
|
102259
|
+
fallback2 = { reason: "artifact-qti", artifactReason: artifactItem.reason };
|
|
102260
|
+
} else {
|
|
102261
|
+
const startedAt = Date.now();
|
|
102262
|
+
try {
|
|
102263
|
+
scored.set(item.itemIdentifier, {
|
|
102264
|
+
...scorePlatformAssessmentItem(artifactItem, responses[item.itemIdentifier]),
|
|
102265
|
+
grading: PLATFORM_CHILD_GRADING
|
|
102266
|
+
});
|
|
102267
|
+
onEvent("assessment.item_scoring", {
|
|
102268
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102269
|
+
"app.assessment.purpose": purpose,
|
|
102270
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102271
|
+
"app.assessment.grading_source": "platform-artifact",
|
|
102272
|
+
"app.assessment.grading_duration_ms": Date.now() - startedAt
|
|
102273
|
+
});
|
|
102274
|
+
} catch (error88) {
|
|
102275
|
+
fallback2 = { reason: "platform-grader-error", error: error88 };
|
|
102276
|
+
}
|
|
102277
|
+
}
|
|
102278
|
+
if (fallback2) {
|
|
102279
|
+
qti2.push({ item, ...fallback2 });
|
|
102280
|
+
}
|
|
102281
|
+
}
|
|
102282
|
+
if (qti2.length > 0) {
|
|
102283
|
+
const fallbackTasks = qti2.flatMap(({ item, reason, artifactReason, error: graderError }) => {
|
|
102284
|
+
const interactionResults = [];
|
|
102285
|
+
let startedAt;
|
|
102286
|
+
let completedInteractions = 0;
|
|
102287
|
+
let failed = false;
|
|
102288
|
+
const omittedResponseCount = item.requests.filter((request) => responses[item.itemIdentifier]?.[request.identifier] === undefined).length;
|
|
102289
|
+
let qtiRemoteInteractionCount = 0;
|
|
102290
|
+
let syntheticIncorrectInteractionCount = 0;
|
|
102291
|
+
function fallbackEvent(outcome) {
|
|
102292
|
+
onEvent("assessment.item_scoring_fallback", {
|
|
102293
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102294
|
+
"app.assessment.purpose": purpose,
|
|
102295
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102296
|
+
"app.assessment.grading_source": omittedResponseCount > 0 ? "platform-qti-adapter" : "timeback-qti",
|
|
102297
|
+
"app.assessment.fallback_reason": reason,
|
|
102298
|
+
"app.assessment.fallback_duration_ms": startedAt === undefined ? 0 : Date.now() - startedAt,
|
|
102299
|
+
"app.assessment.qti_remote_interaction_count": qtiRemoteInteractionCount,
|
|
102300
|
+
"app.assessment.synthetic_incorrect_interaction_count": syntheticIncorrectInteractionCount,
|
|
102301
|
+
...artifactReason ? { "app.assessment.artifact_fallback_reason": artifactReason } : {},
|
|
102302
|
+
...graderError ? {
|
|
102303
|
+
"app.assessment.platform_grader_exception_type": errorType(graderError)
|
|
102304
|
+
} : {},
|
|
102305
|
+
...outcome
|
|
102306
|
+
});
|
|
102307
|
+
}
|
|
102308
|
+
function completeItem() {
|
|
102309
|
+
const earned = assessmentItemEarnedScore(interactionResults.map((result) => Number.isFinite(result.score) ? result.score : 0), item.maxScore);
|
|
102310
|
+
const possible = item.maxScore;
|
|
102311
|
+
fallbackEvent({ "app.assessment.fallback_outcome": "succeeded" });
|
|
102312
|
+
scored.set(item.itemIdentifier, {
|
|
102313
|
+
itemIdentifier: item.itemIdentifier,
|
|
102314
|
+
score: {
|
|
102315
|
+
earned,
|
|
102316
|
+
possible,
|
|
102317
|
+
normalized: possible === 0 ? 0 : earned / possible
|
|
102318
|
+
},
|
|
102319
|
+
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
102320
|
+
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
102321
|
+
});
|
|
102322
|
+
}
|
|
102323
|
+
if (item.requests.length === 0) {
|
|
102324
|
+
completeItem();
|
|
102325
|
+
return [];
|
|
102326
|
+
}
|
|
102327
|
+
return item.requests.map((request, index2) => async () => {
|
|
102328
|
+
startedAt ??= Date.now();
|
|
102329
|
+
try {
|
|
102330
|
+
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
102331
|
+
if (response === undefined) {
|
|
102332
|
+
syntheticIncorrectInteractionCount += 1;
|
|
102333
|
+
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
102334
|
+
} else {
|
|
102335
|
+
qtiRemoteInteractionCount += 1;
|
|
102336
|
+
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
102337
|
+
}
|
|
102338
|
+
completedInteractions += 1;
|
|
102339
|
+
if (completedInteractions === item.requests.length && !failed) {
|
|
102340
|
+
completeItem();
|
|
102341
|
+
}
|
|
102342
|
+
} catch (error88) {
|
|
102343
|
+
if (!failed) {
|
|
102344
|
+
failed = true;
|
|
102345
|
+
fallbackEvent({
|
|
102346
|
+
"app.assessment.fallback_outcome": "failed",
|
|
102347
|
+
"exception.type": errorType(error88),
|
|
102348
|
+
"app.error.message": errorMessage(error88)
|
|
102349
|
+
});
|
|
102350
|
+
}
|
|
102351
|
+
throw error88;
|
|
102352
|
+
}
|
|
102353
|
+
});
|
|
102354
|
+
});
|
|
102355
|
+
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
102356
|
+
}
|
|
102357
|
+
return scoringItems.map((item) => {
|
|
102358
|
+
const result = scored.get(item.itemIdentifier);
|
|
102359
|
+
if (!result) {
|
|
102360
|
+
throw new Error(`Assessment item ${item.itemIdentifier} was not scored`);
|
|
102361
|
+
}
|
|
102362
|
+
return result;
|
|
102363
|
+
});
|
|
102364
|
+
}
|
|
102365
|
+
var QTI_CHILD_GRADING, PLATFORM_QTI_ADAPTER_CHILD_GRADING, PLATFORM_CHILD_GRADING, SCORING_CONCURRENCY = 4;
|
|
102366
|
+
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
102367
|
+
init_assessment_runtime2();
|
|
102368
|
+
init_assessment_artifact_envelope_util();
|
|
102369
|
+
init_timeback_assessment_runtime_util();
|
|
102370
|
+
QTI_CHILD_GRADING = {
|
|
102371
|
+
source: "timeback-qti",
|
|
102372
|
+
graderVersion: "timeback-qti-process-response-v1"
|
|
102373
|
+
};
|
|
102374
|
+
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
102375
|
+
source: "platform-qti-adapter",
|
|
102376
|
+
graderVersion: "platform-qti-adapter-v1",
|
|
102377
|
+
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
102378
|
+
};
|
|
102379
|
+
PLATFORM_CHILD_GRADING = {
|
|
102380
|
+
source: "platform-artifact",
|
|
102381
|
+
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
102382
|
+
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
102383
|
+
};
|
|
102384
|
+
});
|
|
102385
|
+
|
|
102422
102386
|
// ../api-core/src/utils/timeback-assessment-child-result.util.ts
|
|
102423
102387
|
async function buildSubmissionChildResultCommand(input) {
|
|
102424
102388
|
const { submission, context: context2 } = input;
|
|
@@ -102434,12 +102398,9 @@ async function buildSubmissionChildResultCommand(input) {
|
|
|
102434
102398
|
score: submission.score,
|
|
102435
102399
|
grading: input.grading
|
|
102436
102400
|
};
|
|
102437
|
-
if (context2.kind
|
|
102438
|
-
|
|
102439
|
-
|
|
102440
|
-
...typeof isCorrect === "boolean" ? { isCorrect } : {},
|
|
102441
|
-
context: context2
|
|
102442
|
-
}, submission.submittedAt);
|
|
102401
|
+
if (context2.kind !== "platform-routed-diagnostic") {
|
|
102402
|
+
const payload = { ...common2, ...typeof isCorrect === "boolean" ? { isCorrect } : {} };
|
|
102403
|
+
return context2.kind === "review" ? buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt) : buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt);
|
|
102443
102404
|
}
|
|
102444
102405
|
if (typeof isCorrect !== "boolean") {
|
|
102445
102406
|
throw new Error("Diagnostic child results require determinate correctness");
|
|
@@ -102460,11 +102421,7 @@ function assessmentChildLineItemMetadataMatches(key, stored, expected) {
|
|
|
102460
102421
|
if (!Array.isArray(storedStandards) || !Array.isArray(expectedStandards)) {
|
|
102461
102422
|
return false;
|
|
102462
102423
|
}
|
|
102463
|
-
|
|
102464
|
-
function contains(standards, candidate) {
|
|
102465
|
-
return standards.some((standard) => canonicalJson2(standard) === canonicalJson2(candidate));
|
|
102466
|
-
}
|
|
102467
|
-
return sameDefinition && (expectedStandards.every((standard) => contains(storedStandards, standard)) || storedStandards.every((standard) => contains(expectedStandards, standard)));
|
|
102424
|
+
return canonicalJson2(reviewDefinitionWithoutStandards(storedDefinition)) === canonicalJson2(reviewDefinitionWithoutStandards(expectedDefinition));
|
|
102468
102425
|
}
|
|
102469
102426
|
function assessmentChildLineItemMatches(stored, expected) {
|
|
102470
102427
|
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) {
|
|
@@ -102522,19 +102479,14 @@ async function classifyAssessmentChildResult(input) {
|
|
|
102522
102479
|
result.scoreScale,
|
|
102523
102480
|
result.scorePercentile,
|
|
102524
102481
|
result.comment,
|
|
102525
|
-
result.learningObjectiveSet
|
|
102526
|
-
result.incomplete,
|
|
102527
|
-
result.late,
|
|
102528
|
-
result.missing
|
|
102482
|
+
result.learningObjectiveSet
|
|
102529
102483
|
];
|
|
102530
|
-
|
|
102484
|
+
const flags2 = [result.incomplete, result.late, result.missing];
|
|
102485
|
+
if (auxiliaryFields.some((value) => value !== undefined && value !== null) || flags2.some((value) => value !== undefined && value !== null && value !== "false")) {
|
|
102531
102486
|
return { outcome: "conflict", reason: "auxiliary-field-mismatch" };
|
|
102532
102487
|
}
|
|
102533
102488
|
return { outcome: "replay" };
|
|
102534
102489
|
}
|
|
102535
|
-
function hasApiStatus(error88, statusCode) {
|
|
102536
|
-
return isApiError(error88) && error88.statusCode === statusCode;
|
|
102537
|
-
}
|
|
102538
102490
|
async function persistAssessmentChildResult(input) {
|
|
102539
102491
|
const { assessmentResults, childLineItemId } = input;
|
|
102540
102492
|
const verification2 = await verifyAssessmentChildResultCommand(input.command);
|
|
@@ -102543,38 +102495,127 @@ async function persistAssessmentChildResult(input) {
|
|
|
102543
102495
|
}
|
|
102544
102496
|
const { command } = verification2;
|
|
102545
102497
|
const resultId = await assessmentChildResultId(command.payload.attempt.attemptId, command.payload.administration.submissionId);
|
|
102546
|
-
|
|
102547
|
-
|
|
102548
|
-
|
|
102498
|
+
await assessmentResults.upsert(resultId, buildAssessmentChildResultUpdate(command, childLineItemId));
|
|
102499
|
+
}
|
|
102500
|
+
function assessmentTranscriptChildLineItemId(context2, itemIdentifier) {
|
|
102501
|
+
return context2.metadata.purpose === "review" ? reviewQuestionLineItemId(context2.parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(context2.parentLineItemId, itemIdentifier);
|
|
102502
|
+
}
|
|
102503
|
+
async function reconcileAssessmentTranscriptChildren({
|
|
102504
|
+
context: context2,
|
|
102505
|
+
graded,
|
|
102506
|
+
only,
|
|
102507
|
+
assessmentResults,
|
|
102508
|
+
ensureLineItems,
|
|
102509
|
+
onPersisted
|
|
102510
|
+
}) {
|
|
102511
|
+
const metadata2 = graded.metadata;
|
|
102512
|
+
const assessmentKey = metadata2.selectedTest.assessmentKey;
|
|
102513
|
+
if (!assessmentKey) {
|
|
102514
|
+
throw new Error("Assessment context requires a selected assessment key");
|
|
102515
|
+
}
|
|
102516
|
+
const submissions = metadata2.itemSubmissions.filter((submission) => !only || only.has(submission.submissionId));
|
|
102517
|
+
const administered = new Set(submissions.map((submission) => submission.itemIdentifier));
|
|
102518
|
+
await ensureLineItems({
|
|
102519
|
+
items: graded.assessment.items.filter((item) => administered.has(item.identifier))
|
|
102520
|
+
});
|
|
102521
|
+
await runWithConcurrency(submissions, 4, async (submission) => {
|
|
102522
|
+
const index2 = submission.responseVersion - 1;
|
|
102523
|
+
let flow;
|
|
102524
|
+
if (metadata2.purpose === "review") {
|
|
102525
|
+
const selection = metadata2.review.selections.find((item) => item.itemIdentifier === submission.itemIdentifier);
|
|
102526
|
+
flow = {
|
|
102527
|
+
kind: "review",
|
|
102528
|
+
bankRevision: metadata2.review.bankRevision,
|
|
102529
|
+
standard: selection.standard
|
|
102530
|
+
};
|
|
102531
|
+
} else if (metadata2.purpose === "diagnostic" && metadata2.diagnostic) {
|
|
102532
|
+
flow = {
|
|
102533
|
+
kind: "platform-routed-diagnostic",
|
|
102534
|
+
definitionId: metadata2.diagnostic.definitionId,
|
|
102535
|
+
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
102536
|
+
routingRevision: metadata2.diagnostic.routingRevision,
|
|
102537
|
+
transition: metadata2.diagnostic.ledger[index2]
|
|
102538
|
+
};
|
|
102539
|
+
} else {
|
|
102540
|
+
flow = { kind: "fixed", purpose: metadata2.purpose };
|
|
102541
|
+
}
|
|
102542
|
+
const command = await buildSubmissionChildResultCommand({
|
|
102543
|
+
attempt: {
|
|
102544
|
+
attemptId: context2.attemptId,
|
|
102545
|
+
gameId: context2.gameId,
|
|
102546
|
+
studentId: context2.studentId,
|
|
102547
|
+
activityId: metadata2.activityId,
|
|
102548
|
+
courseId: metadata2.courseId,
|
|
102549
|
+
integrationId: metadata2.integrationId,
|
|
102550
|
+
enrollmentId: metadata2.enrollmentId,
|
|
102551
|
+
selectedTest: { ...metadata2.selectedTest, assessmentKey }
|
|
102552
|
+
},
|
|
102553
|
+
submission,
|
|
102554
|
+
responses: graded.transcript.administrations[index2].responses,
|
|
102555
|
+
grading: submission.grading ?? QTI_CHILD_GRADING,
|
|
102556
|
+
context: flow
|
|
102557
|
+
});
|
|
102558
|
+
await persistAssessmentChildResult({
|
|
102559
|
+
assessmentResults,
|
|
102549
102560
|
command,
|
|
102550
|
-
childLineItemId
|
|
102561
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, submission.itemIdentifier)
|
|
102551
102562
|
});
|
|
102552
|
-
|
|
102563
|
+
onPersisted?.(submission.submissionId);
|
|
102564
|
+
});
|
|
102565
|
+
}
|
|
102566
|
+
async function recoveredAssessmentAdministration(context2, result) {
|
|
102567
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
102568
|
+
if (!verified.ok) {
|
|
102569
|
+
return null;
|
|
102553
102570
|
}
|
|
102554
|
-
|
|
102555
|
-
|
|
102556
|
-
|
|
102557
|
-
|
|
102558
|
-
|
|
102571
|
+
const { command } = verified;
|
|
102572
|
+
const { attempt, administration, responses, context: flow } = command.payload;
|
|
102573
|
+
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)) {
|
|
102574
|
+
return null;
|
|
102575
|
+
}
|
|
102576
|
+
const disposition = await classifyAssessmentChildResult({
|
|
102577
|
+
result,
|
|
102578
|
+
command,
|
|
102579
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, administration.itemIdentifier)
|
|
102580
|
+
});
|
|
102581
|
+
if (disposition.outcome !== "replay") {
|
|
102582
|
+
return null;
|
|
102583
|
+
}
|
|
102584
|
+
return {
|
|
102585
|
+
sequence: administration.responseVersion,
|
|
102586
|
+
entry: {
|
|
102587
|
+
submissionId: administration.submissionId,
|
|
102588
|
+
itemIdentifier: administration.itemIdentifier,
|
|
102589
|
+
responses,
|
|
102590
|
+
...flow.kind === "platform-routed-diagnostic" ? { routingNodeKey: flow.transition.routingNodeKey } : {}
|
|
102591
|
+
}
|
|
102592
|
+
};
|
|
102593
|
+
}
|
|
102594
|
+
async function recoverAssessmentTranscript(context2, results) {
|
|
102595
|
+
const entries = new Map;
|
|
102596
|
+
for (const result of results) {
|
|
102597
|
+
const recovered = await recoveredAssessmentAdministration(context2, result);
|
|
102598
|
+
if (recovered) {
|
|
102599
|
+
const atPosition = entries.get(recovered.sequence) ?? new Map;
|
|
102600
|
+
atPosition.set(recovered.entry.submissionId, recovered.entry);
|
|
102601
|
+
entries.set(recovered.sequence, atPosition);
|
|
102559
102602
|
}
|
|
102560
102603
|
}
|
|
102561
|
-
|
|
102562
|
-
|
|
102563
|
-
|
|
102564
|
-
|
|
102565
|
-
|
|
102566
|
-
return { outcome: "created", resultId };
|
|
102567
|
-
} catch (error88) {
|
|
102568
|
-
if (!hasApiStatus(error88, 409)) {
|
|
102569
|
-
throw error88;
|
|
102604
|
+
const transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision);
|
|
102605
|
+
for (let sequence2 = 1;sequence2 <= entries.size; sequence2 += 1) {
|
|
102606
|
+
const candidates = entries.get(sequence2);
|
|
102607
|
+
if (!candidates || candidates.size !== 1) {
|
|
102608
|
+
break;
|
|
102570
102609
|
}
|
|
102610
|
+
transcript.administrations.push(candidates.values().next().value);
|
|
102571
102611
|
}
|
|
102572
|
-
return
|
|
102612
|
+
return transcript;
|
|
102573
102613
|
}
|
|
102574
|
-
var init_timeback_assessment_child_result_util = __esm(
|
|
102614
|
+
var init_timeback_assessment_child_result_util = __esm(() => {
|
|
102575
102615
|
init_src();
|
|
102576
102616
|
init_assessment_runtime2();
|
|
102577
|
-
|
|
102617
|
+
init_timeback_assessment_runtime_util();
|
|
102618
|
+
init_timeback_assessment_scoring_util();
|
|
102578
102619
|
});
|
|
102579
102620
|
|
|
102580
102621
|
// ../api-core/src/utils/timeback-assessment-preparation.util.ts
|
|
@@ -102613,21 +102654,6 @@ function assessmentPreparationFlightKey(input) {
|
|
|
102613
102654
|
async function assessmentPreparationReceiptMatches(plan, request) {
|
|
102614
102655
|
return plan.gameId === request.gameId && plan.studentId === request.studentId && plan.activityId === request.input.activityId && plan.purpose === request.input.purpose && plan.requestHash === await assessmentPreparationRequestHash(request.input);
|
|
102615
102656
|
}
|
|
102616
|
-
function resolveAssessmentPreparationSelection(decision, candidateCourseCount) {
|
|
102617
|
-
if (decision?.kind === "resume") {
|
|
102618
|
-
return {
|
|
102619
|
-
status: "blocked",
|
|
102620
|
-
result: { status: "canonical_required", reason: "resumable_attempt" }
|
|
102621
|
-
};
|
|
102622
|
-
}
|
|
102623
|
-
if (candidateCourseCount !== 1) {
|
|
102624
|
-
return {
|
|
102625
|
-
status: "blocked",
|
|
102626
|
-
result: { status: "unprepared", reason: "prerequisite_unavailable" }
|
|
102627
|
-
};
|
|
102628
|
-
}
|
|
102629
|
-
return decision ? { status: "ready", decision } : { status: "unavailable" };
|
|
102630
|
-
}
|
|
102631
102657
|
function preparedAssessmentIntegration(plan, candidates) {
|
|
102632
102658
|
if (candidates.rows.length !== 1) {
|
|
102633
102659
|
return null;
|
|
@@ -102679,6 +102705,7 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102679
102705
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
102680
102706
|
activityId: plan.activityId,
|
|
102681
102707
|
courseId: plan.courseId,
|
|
102708
|
+
courseName: plan.courseName,
|
|
102682
102709
|
integrationId: plan.integrationId,
|
|
102683
102710
|
enrollmentId: plan.enrollmentId,
|
|
102684
102711
|
selectedTest: plan.selectedTest,
|
|
@@ -102709,7 +102736,6 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102709
102736
|
testName,
|
|
102710
102737
|
routingRevision: plan.diagnostic.routingRevision,
|
|
102711
102738
|
ledger: [],
|
|
102712
|
-
routingSnapshots: [],
|
|
102713
102739
|
state: plan.diagnostic.initialState
|
|
102714
102740
|
}
|
|
102715
102741
|
};
|
|
@@ -103004,127 +103030,6 @@ var init_timeback_assessment_publication_util = __esm(() => {
|
|
|
103004
103030
|
MANAGED_ASSET_URL = new RegExp(`https?://[^\\s"')]+/(${ASSESSMENT_ASSET_KEY_PREFIX}v1/sha256/[a-f0-9]{64}\\.[a-z0-9]+)`, "gi");
|
|
103005
103031
|
});
|
|
103006
103032
|
|
|
103007
|
-
// ../api-core/src/utils/timeback-assessment-reconciliation.util.ts
|
|
103008
|
-
function assessmentChildProjectionIsTerminal(result) {
|
|
103009
|
-
const state = { inProgress: result.inProgress ?? "", scoreStatus: result.scoreStatus };
|
|
103010
|
-
return isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
103011
|
-
}
|
|
103012
|
-
async function reconcileReviewChildResults({
|
|
103013
|
-
initial,
|
|
103014
|
-
writeOpen,
|
|
103015
|
-
crossBarrier,
|
|
103016
|
-
readLatest,
|
|
103017
|
-
restoreTerminal
|
|
103018
|
-
}) {
|
|
103019
|
-
if (initial.metadata.purpose !== "review") {
|
|
103020
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
103021
|
-
}
|
|
103022
|
-
if (assessmentChildProjectionIsTerminal(initial.result)) {
|
|
103023
|
-
const settled = { result: initial.result, metadata: initial.metadata };
|
|
103024
|
-
await restoreTerminal(settled);
|
|
103025
|
-
return settled;
|
|
103026
|
-
}
|
|
103027
|
-
await writeOpen();
|
|
103028
|
-
await crossBarrier();
|
|
103029
|
-
const current = await readLatest();
|
|
103030
|
-
if (current.metadata.purpose !== "review") {
|
|
103031
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
103032
|
-
}
|
|
103033
|
-
const reviewed = { result: current.result, metadata: current.metadata };
|
|
103034
|
-
if (assessmentChildProjectionIsTerminal(current.result)) {
|
|
103035
|
-
await restoreTerminal(reviewed);
|
|
103036
|
-
}
|
|
103037
|
-
return reviewed;
|
|
103038
|
-
}
|
|
103039
|
-
var init_timeback_assessment_reconciliation_util = __esm(() => {
|
|
103040
|
-
init_assessment_runtime2();
|
|
103041
|
-
});
|
|
103042
|
-
|
|
103043
|
-
// ../api-core/src/utils/timeback-assessment-replay.util.ts
|
|
103044
|
-
function resolveDiagnosticItemReplay(metadata2, input) {
|
|
103045
|
-
const priorIndex = metadata2.itemSubmissions.findIndex((submission) => submission.submissionId === input.submissionId);
|
|
103046
|
-
if (priorIndex === -1) {
|
|
103047
|
-
return { action: "missing" };
|
|
103048
|
-
}
|
|
103049
|
-
const prior = metadata2.itemSubmissions[priorIndex];
|
|
103050
|
-
const ledgerEntry = metadata2.diagnostic.ledger[priorIndex];
|
|
103051
|
-
if (!prior || !ledgerEntry || prior.itemIdentifier !== input.itemIdentifier || ledgerEntry.routingNodeKey !== input.routingNodeKey || !itemResponseUpdateMatches(metadata2.responses[input.itemIdentifier], input.responses)) {
|
|
103052
|
-
return {
|
|
103053
|
-
action: "conflict",
|
|
103054
|
-
failure: assessmentFlowViolation("This diagnostic submission ID was already used for a different request.", {
|
|
103055
|
-
submissionId: input.submissionId,
|
|
103056
|
-
routingNodeKey: input.routingNodeKey,
|
|
103057
|
-
itemIdentifier: input.itemIdentifier
|
|
103058
|
-
})
|
|
103059
|
-
};
|
|
103060
|
-
}
|
|
103061
|
-
return {
|
|
103062
|
-
action: "replay",
|
|
103063
|
-
submission: prior,
|
|
103064
|
-
ledgerEntry,
|
|
103065
|
-
priorIndex,
|
|
103066
|
-
routing: metadata2.diagnostic.routingSnapshots?.[priorIndex]
|
|
103067
|
-
};
|
|
103068
|
-
}
|
|
103069
|
-
function buildAssessmentItemReplayResult(attemptId, metadata2, submission) {
|
|
103070
|
-
return {
|
|
103071
|
-
attemptId,
|
|
103072
|
-
responseVersion: metadata2.responseVersion,
|
|
103073
|
-
status: "in_progress",
|
|
103074
|
-
responses: metadata2.responses,
|
|
103075
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
103076
|
-
submission
|
|
103077
|
-
};
|
|
103078
|
-
}
|
|
103079
|
-
function diagnosticReceipt(submission, ledgerEntry) {
|
|
103080
|
-
return {
|
|
103081
|
-
submissionId: submission.submissionId,
|
|
103082
|
-
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
103083
|
-
itemIdentifier: submission.itemIdentifier,
|
|
103084
|
-
submittedAt: submission.submittedAt,
|
|
103085
|
-
responseVersion: submission.responseVersion,
|
|
103086
|
-
answered: submission.answered,
|
|
103087
|
-
score: submission.score,
|
|
103088
|
-
isCorrect: ledgerEntry.isCorrect
|
|
103089
|
-
};
|
|
103090
|
-
}
|
|
103091
|
-
function buildDiagnosticItemReplayResult(attemptId, submission, ledgerEntry, routing) {
|
|
103092
|
-
return {
|
|
103093
|
-
attemptId,
|
|
103094
|
-
responseVersion: submission.responseVersion,
|
|
103095
|
-
status: "in_progress",
|
|
103096
|
-
routing,
|
|
103097
|
-
submission: diagnosticReceipt(submission, ledgerEntry)
|
|
103098
|
-
};
|
|
103099
|
-
}
|
|
103100
|
-
function requireAssessmentMutationAccess(attemptId, integrationStatus, enrollment) {
|
|
103101
|
-
if (!enrollment || integrationStatus === "deactivated") {
|
|
103102
|
-
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
103103
|
-
}
|
|
103104
|
-
return enrollment;
|
|
103105
|
-
}
|
|
103106
|
-
function committedReviewItemResults(metadata2, assessment) {
|
|
103107
|
-
const available = new Set(assessment.items.map((item) => item.identifier));
|
|
103108
|
-
const seen = new Set;
|
|
103109
|
-
return metadata2.itemSubmissions.map((submission) => {
|
|
103110
|
-
if (!available.has(submission.itemIdentifier) || seen.has(submission.itemIdentifier)) {
|
|
103111
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The persisted review item-submission ledger is corrupt.", {
|
|
103112
|
-
itemIdentifier: submission.itemIdentifier
|
|
103113
|
-
}));
|
|
103114
|
-
}
|
|
103115
|
-
seen.add(submission.itemIdentifier);
|
|
103116
|
-
return {
|
|
103117
|
-
itemIdentifier: submission.itemIdentifier,
|
|
103118
|
-
score: submission.score,
|
|
103119
|
-
isCorrect: submission.isCorrect
|
|
103120
|
-
};
|
|
103121
|
-
});
|
|
103122
|
-
}
|
|
103123
|
-
var init_timeback_assessment_replay_util = __esm(() => {
|
|
103124
|
-
init_assessment_runtime2();
|
|
103125
|
-
init_errors2();
|
|
103126
|
-
});
|
|
103127
|
-
|
|
103128
103033
|
// ../api-core/src/utils/timeback-assessment-routing.util.ts
|
|
103129
103034
|
function diagnosticRoutingFallbackReason(artifact, routingRevision) {
|
|
103130
103035
|
if (!artifact) {
|
|
@@ -103274,154 +103179,256 @@ var init_timeback_assessment_rules_util = __esm(() => {
|
|
|
103274
103179
|
init_errors2();
|
|
103275
103180
|
});
|
|
103276
103181
|
|
|
103277
|
-
// ../api-core/src/utils/timeback-assessment-
|
|
103278
|
-
|
|
103279
|
-
|
|
103280
|
-
|
|
103281
|
-
|
|
103282
|
-
|
|
103283
|
-
|
|
103284
|
-
|
|
103285
|
-
|
|
103286
|
-
|
|
103287
|
-
|
|
103288
|
-
|
|
103289
|
-
|
|
103290
|
-
|
|
103291
|
-
|
|
103292
|
-
|
|
103293
|
-
|
|
103294
|
-
|
|
103295
|
-
|
|
103296
|
-
|
|
103297
|
-
|
|
103298
|
-
|
|
103299
|
-
|
|
103300
|
-
|
|
103301
|
-
|
|
103302
|
-
|
|
103303
|
-
|
|
103304
|
-
|
|
103305
|
-
|
|
103306
|
-
|
|
103307
|
-
|
|
103308
|
-
|
|
103309
|
-
|
|
103310
|
-
|
|
103311
|
-
|
|
103312
|
-
|
|
103313
|
-
|
|
103314
|
-
|
|
103315
|
-
|
|
103316
|
-
|
|
103182
|
+
// ../api-core/src/utils/timeback-assessment-replay.util.ts
|
|
103183
|
+
function diagnosticReceipt(submission, ledgerEntry) {
|
|
103184
|
+
return {
|
|
103185
|
+
submissionId: submission.submissionId,
|
|
103186
|
+
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
103187
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103188
|
+
submittedAt: submission.submittedAt,
|
|
103189
|
+
responseVersion: submission.responseVersion,
|
|
103190
|
+
answered: submission.answered,
|
|
103191
|
+
score: submission.score,
|
|
103192
|
+
isCorrect: ledgerEntry.isCorrect
|
|
103193
|
+
};
|
|
103194
|
+
}
|
|
103195
|
+
|
|
103196
|
+
// ../api-core/src/utils/timeback-assessment-transcript.util.ts
|
|
103197
|
+
function parseAssessmentTranscript(value) {
|
|
103198
|
+
const parsed = AssessmentTranscriptSchema.safeParse(value);
|
|
103199
|
+
if (!parsed.success) {
|
|
103200
|
+
throw new ValidationError("Invalid assessment transcript");
|
|
103201
|
+
}
|
|
103202
|
+
return parsed.data;
|
|
103203
|
+
}
|
|
103204
|
+
function assessmentTokenMetadata(metadata2) {
|
|
103205
|
+
const context2 = { ...metadata2, updatedAt: metadata2.startedAt };
|
|
103206
|
+
delete context2.transcript;
|
|
103207
|
+
return context2;
|
|
103208
|
+
}
|
|
103209
|
+
function requireAssessmentTokenContext(context2, scope) {
|
|
103210
|
+
if (!context2 || context2.gameId !== scope.gameId || context2.studentId !== scope.studentId || context2.attemptId !== scope.attemptId) {
|
|
103211
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(scope.attemptId));
|
|
103212
|
+
}
|
|
103213
|
+
return context2;
|
|
103214
|
+
}
|
|
103215
|
+
function requireAssessmentFeedbackContext(tokenContext, scope) {
|
|
103216
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103217
|
+
const metadata2 = context2.metadata;
|
|
103218
|
+
if (assessmentFlowForPurpose(metadata2.purpose) === "attempt-submit" && !(metadata2.purpose === "diagnostic" && metadata2.diagnostic)) {
|
|
103219
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("This assessment accepts a complete attempt, not per-item feedback."));
|
|
103220
|
+
}
|
|
103221
|
+
return context2;
|
|
103222
|
+
}
|
|
103223
|
+
function prepareAssessmentProgress(tokenContext, scope, input) {
|
|
103224
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103225
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
103226
|
+
const progress = assessmentProgressAdministration(context2.metadata.purpose, {
|
|
103227
|
+
...input,
|
|
103228
|
+
transcript
|
|
103229
|
+
});
|
|
103230
|
+
if (!progress.ok) {
|
|
103231
|
+
throw AssessmentRuntimeError.from(progress.failure);
|
|
103232
|
+
}
|
|
103233
|
+
return { context: context2, transcript, submissionId: progress.submissionId };
|
|
103234
|
+
}
|
|
103235
|
+
function prepareAssessmentItemTranscript(assessment, input) {
|
|
103236
|
+
validateAssessmentResponseUpdate(assessment, { [input.itemIdentifier]: input.responses });
|
|
103237
|
+
const appended = appendAssessmentAdministration(input.transcript, input);
|
|
103238
|
+
if (!appended.ok) {
|
|
103239
|
+
throw AssessmentRuntimeError.from(appended.failure);
|
|
103240
|
+
}
|
|
103241
|
+
return appended;
|
|
103242
|
+
}
|
|
103243
|
+
function prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal = false) {
|
|
103244
|
+
return prepareAssessmentTranscript({
|
|
103245
|
+
transcript,
|
|
103246
|
+
attemptId: context2.attemptId,
|
|
103247
|
+
contentRevision: context2.metadata.selectedTest.contentRevision,
|
|
103248
|
+
assessment,
|
|
103249
|
+
routed: context2.metadata.purpose === "diagnostic" && context2.metadata.diagnostic !== undefined,
|
|
103250
|
+
allowIncomplete: !terminal && assessmentFlowForPurpose(context2.metadata.purpose) === "attempt-submit"
|
|
103251
|
+
});
|
|
103252
|
+
}
|
|
103253
|
+
function requireTranscriptWriteAccess(attemptId, integrationStatus) {
|
|
103254
|
+
if (integrationStatus === "deactivated") {
|
|
103255
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
103256
|
+
}
|
|
103257
|
+
}
|
|
103258
|
+
function requireTranscriptVersion(transcript, expectedResponseVersion) {
|
|
103259
|
+
if (expectedResponseVersion !== transcript.administrations.length) {
|
|
103260
|
+
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, transcript.administrations.length));
|
|
103261
|
+
}
|
|
103262
|
+
}
|
|
103263
|
+
function requireAssessmentTranscriptCheckpoint(attempt, integrationStatus, input) {
|
|
103264
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103265
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103266
|
+
if (!isAssessmentAttemptOpen({ ...attempt, inProgress: attempt.inProgress ?? "" })) {
|
|
103267
|
+
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(attempt.sourcedId));
|
|
103268
|
+
}
|
|
103269
|
+
}
|
|
103270
|
+
function assessmentTranscriptRequestHash(input) {
|
|
103271
|
+
return sha256Hex(canonicalJson2({
|
|
103272
|
+
transcript: input.transcript,
|
|
103273
|
+
...input.session ? { session: input.session } : {}
|
|
103274
|
+
}));
|
|
103275
|
+
}
|
|
103276
|
+
function resolveAssessmentTranscriptSubmission(attempt, metadata2, integrationStatus, input, requestHash) {
|
|
103277
|
+
const disposition = classifyAssessmentSubmission({
|
|
103278
|
+
inProgress: attempt.inProgress ?? "",
|
|
103279
|
+
scoreStatus: attempt.scoreStatus,
|
|
103280
|
+
submissionId: metadata2.submissionId
|
|
103281
|
+
}, input.submissionId);
|
|
103282
|
+
if (disposition === "replay" && metadata2.completion?.requestHash === requestHash) {
|
|
103283
|
+
return "replay";
|
|
103284
|
+
}
|
|
103285
|
+
if (disposition === "reject") {
|
|
103286
|
+
throw AssessmentRuntimeError.from(assessmentAlreadySubmitted(attempt.sourcedId));
|
|
103287
|
+
}
|
|
103288
|
+
if (disposition !== "submit") {
|
|
103289
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("A different completion was already accepted for this attempt."));
|
|
103290
|
+
}
|
|
103291
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103292
|
+
if (input.session && input.session.runId !== attempt.sourcedId) {
|
|
103293
|
+
throw new ValidationError("Assessment session runId must match the attemptId.");
|
|
103294
|
+
}
|
|
103295
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103296
|
+
return "submit";
|
|
103297
|
+
}
|
|
103298
|
+
function buildGradedAssessmentTranscript(input) {
|
|
103299
|
+
const { context: context2, prepared, itemResults, assessment, submittedAt, manifest } = input;
|
|
103300
|
+
const completed = completeAssessmentTranscript({
|
|
103301
|
+
transcript: prepared.transcript,
|
|
103302
|
+
scores: itemResults,
|
|
103303
|
+
purpose: context2.metadata.purpose,
|
|
103304
|
+
itemCount: assessment.items.length,
|
|
103305
|
+
submittedAt,
|
|
103306
|
+
manifest,
|
|
103307
|
+
terminal: input.terminal
|
|
103308
|
+
});
|
|
103309
|
+
const common2 = {
|
|
103310
|
+
...context2.metadata,
|
|
103311
|
+
responses: prepared.responses,
|
|
103312
|
+
responseVersion: completed.itemSubmissions.length,
|
|
103313
|
+
itemSubmissions: completed.itemSubmissions,
|
|
103314
|
+
updatedAt: submittedAt,
|
|
103315
|
+
transcript: prepared.transcript
|
|
103316
|
+
};
|
|
103317
|
+
const diagnostic = context2.metadata.purpose === "diagnostic" ? context2.metadata.diagnostic : undefined;
|
|
103318
|
+
const metadata2 = diagnostic ? {
|
|
103319
|
+
...common2,
|
|
103320
|
+
purpose: "diagnostic",
|
|
103321
|
+
diagnostic: { ...diagnostic, ledger: completed.ledger, state: completed.state }
|
|
103322
|
+
} : common2;
|
|
103323
|
+
return {
|
|
103324
|
+
metadata: metadata2,
|
|
103325
|
+
transcript: prepared.transcript,
|
|
103326
|
+
assessment,
|
|
103327
|
+
itemResults,
|
|
103328
|
+
manifest,
|
|
103329
|
+
score: aggregateAssessmentScore(itemResults.map((item) => item.score.earned), itemResults.reduce((sum, item) => sum + item.score.possible, 0)),
|
|
103330
|
+
correctQuestions: countCorrectQuestions(itemResults.map((item) => item.isCorrect))
|
|
103331
|
+
};
|
|
103332
|
+
}
|
|
103333
|
+
function assessmentTranscriptRuntimeError(error88) {
|
|
103334
|
+
return error88 instanceof AssessmentTranscriptError ? AssessmentRuntimeError.from({
|
|
103335
|
+
code: error88.code,
|
|
103336
|
+
message: error88.message,
|
|
103337
|
+
details: { transcriptIndex: error88.index }
|
|
103338
|
+
}) : error88;
|
|
103339
|
+
}
|
|
103340
|
+
function buildAssessmentTranscriptFeedback(attemptId, graded, index2) {
|
|
103341
|
+
const { metadata: metadata2 } = graded;
|
|
103342
|
+
const submission = metadata2.itemSubmissions[index2];
|
|
103343
|
+
const common2 = {
|
|
103344
|
+
attemptId,
|
|
103345
|
+
responseVersion: metadata2.responseVersion,
|
|
103346
|
+
status: "in_progress",
|
|
103347
|
+
transcript: graded.transcript
|
|
103348
|
+
};
|
|
103349
|
+
return metadata2.purpose === "diagnostic" && metadata2.diagnostic ? {
|
|
103350
|
+
...common2,
|
|
103351
|
+
routing: projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, metadata2.diagnostic.state),
|
|
103352
|
+
submission: diagnosticReceipt(submission, metadata2.diagnostic.ledger[index2])
|
|
103353
|
+
} : {
|
|
103354
|
+
...common2,
|
|
103355
|
+
responses: metadata2.responses,
|
|
103356
|
+
itemSubmissions: metadata2.itemSubmissions,
|
|
103357
|
+
submission
|
|
103358
|
+
};
|
|
103359
|
+
}
|
|
103360
|
+
async function resumeAssessmentTranscript(input) {
|
|
103361
|
+
const retained = input.suppliedTranscript ?? input.checkpointTranscript;
|
|
103362
|
+
let transcript = retained ?? await input.recover();
|
|
103363
|
+
while (true) {
|
|
103364
|
+
try {
|
|
103365
|
+
return await input.grade(transcript);
|
|
103366
|
+
} catch (error88) {
|
|
103367
|
+
const index2 = error88 instanceof AssessmentRuntimeError && isRecord(error88.details) ? error88.details.transcriptIndex : undefined;
|
|
103368
|
+
if (retained !== undefined || typeof index2 !== "number" || index2 < 0 || index2 >= transcript.administrations.length) {
|
|
103369
|
+
throw error88;
|
|
103317
103370
|
}
|
|
103318
|
-
|
|
103319
|
-
|
|
103320
|
-
|
|
103371
|
+
transcript = {
|
|
103372
|
+
...transcript,
|
|
103373
|
+
administrations: transcript.administrations.slice(0, index2)
|
|
103374
|
+
};
|
|
103321
103375
|
}
|
|
103322
103376
|
}
|
|
103323
|
-
|
|
103324
|
-
|
|
103325
|
-
|
|
103326
|
-
|
|
103327
|
-
|
|
103328
|
-
|
|
103329
|
-
|
|
103330
|
-
|
|
103331
|
-
|
|
103332
|
-
|
|
103333
|
-
|
|
103334
|
-
|
|
103335
|
-
|
|
103336
|
-
|
|
103337
|
-
|
|
103338
|
-
|
|
103339
|
-
|
|
103340
|
-
|
|
103341
|
-
|
|
103342
|
-
|
|
103343
|
-
|
|
103344
|
-
|
|
103345
|
-
|
|
103346
|
-
|
|
103347
|
-
|
|
103348
|
-
|
|
103349
|
-
|
|
103350
|
-
|
|
103351
|
-
|
|
103352
|
-
|
|
103353
|
-
|
|
103354
|
-
|
|
103355
|
-
|
|
103356
|
-
|
|
103357
|
-
|
|
103358
|
-
|
|
103359
|
-
|
|
103360
|
-
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
103361
|
-
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
103362
|
-
});
|
|
103363
|
-
}
|
|
103364
|
-
if (item.requests.length === 0) {
|
|
103365
|
-
completeItem();
|
|
103366
|
-
return [];
|
|
103377
|
+
}
|
|
103378
|
+
function assessmentReviewTranscriptOutcomes(input) {
|
|
103379
|
+
const resultByItem = new Map(input.itemResults.map((item) => [item.itemIdentifier, item]));
|
|
103380
|
+
const administered = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
103381
|
+
return input.metadata.review.selections.filter((selection) => administered.has(selection.itemIdentifier)).map((selection) => {
|
|
103382
|
+
const result = resultByItem.get(selection.itemIdentifier);
|
|
103383
|
+
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
103384
|
+
if (!result || !submission) {
|
|
103385
|
+
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
103386
|
+
}
|
|
103387
|
+
return {
|
|
103388
|
+
itemIdentifier: selection.itemIdentifier,
|
|
103389
|
+
standard: selection.standard,
|
|
103390
|
+
answered: submission.answered,
|
|
103391
|
+
score: result.score,
|
|
103392
|
+
isCorrect: result.isCorrect
|
|
103393
|
+
};
|
|
103394
|
+
});
|
|
103395
|
+
}
|
|
103396
|
+
function assessmentReviewTranscriptExposures(input) {
|
|
103397
|
+
const exposures = [];
|
|
103398
|
+
const authoritative = new Set;
|
|
103399
|
+
for (const { result, metadata: metadata2 } of input.attempts) {
|
|
103400
|
+
const state = { ...result, inProgress: result.inProgress ?? "" };
|
|
103401
|
+
const settled = isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
103402
|
+
const useTranscript = !settled && metadata2.transcript !== undefined;
|
|
103403
|
+
if (metadata2.purpose === "review" && (useTranscript || settled)) {
|
|
103404
|
+
authoritative.add(result.sourcedId);
|
|
103405
|
+
if (metadata2.review.bankRevision === input.bankRevision) {
|
|
103406
|
+
const accepted = useTranscript ? metadata2.transcript.administrations.map((entry) => ({
|
|
103407
|
+
itemIdentifier: entry.itemIdentifier,
|
|
103408
|
+
deliveredAt: metadata2.updatedAt
|
|
103409
|
+
})) : metadata2.itemSubmissions.map((submission) => ({
|
|
103410
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103411
|
+
deliveredAt: submission.submittedAt
|
|
103412
|
+
}));
|
|
103413
|
+
exposures.push(...accepted);
|
|
103367
103414
|
}
|
|
103368
|
-
|
|
103369
|
-
startedAt ??= Date.now();
|
|
103370
|
-
try {
|
|
103371
|
-
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
103372
|
-
if (response === undefined) {
|
|
103373
|
-
syntheticIncorrectInteractionCount += 1;
|
|
103374
|
-
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
103375
|
-
} else {
|
|
103376
|
-
qtiRemoteInteractionCount += 1;
|
|
103377
|
-
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
103378
|
-
}
|
|
103379
|
-
completedInteractions += 1;
|
|
103380
|
-
if (completedInteractions === item.requests.length && !failed) {
|
|
103381
|
-
completeItem();
|
|
103382
|
-
}
|
|
103383
|
-
} catch (error88) {
|
|
103384
|
-
if (!failed) {
|
|
103385
|
-
failed = true;
|
|
103386
|
-
fallbackEvent({
|
|
103387
|
-
"app.assessment.fallback_outcome": "failed",
|
|
103388
|
-
"exception.type": errorType(error88),
|
|
103389
|
-
"app.error.message": errorMessage(error88)
|
|
103390
|
-
});
|
|
103391
|
-
}
|
|
103392
|
-
throw error88;
|
|
103393
|
-
}
|
|
103394
|
-
});
|
|
103395
|
-
});
|
|
103396
|
-
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
103415
|
+
}
|
|
103397
103416
|
}
|
|
103398
|
-
|
|
103399
|
-
const
|
|
103400
|
-
if (!
|
|
103401
|
-
|
|
103417
|
+
for (const command of input.children) {
|
|
103418
|
+
const { attempt, administration, context: context2 } = command.payload;
|
|
103419
|
+
if (!authoritative.has(attempt.attemptId) && context2.kind === "review" && context2.bankRevision === input.bankRevision) {
|
|
103420
|
+
exposures.push({
|
|
103421
|
+
itemIdentifier: administration.itemIdentifier,
|
|
103422
|
+
deliveredAt: command.recordedAt
|
|
103423
|
+
});
|
|
103402
103424
|
}
|
|
103403
|
-
|
|
103404
|
-
|
|
103425
|
+
}
|
|
103426
|
+
return exposures;
|
|
103405
103427
|
}
|
|
103406
|
-
var
|
|
103407
|
-
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
103428
|
+
var init_timeback_assessment_transcript_util = __esm(() => {
|
|
103408
103429
|
init_assessment_runtime2();
|
|
103409
|
-
|
|
103430
|
+
init_errors2();
|
|
103410
103431
|
init_timeback_assessment_runtime_util();
|
|
103411
|
-
QTI_CHILD_GRADING = {
|
|
103412
|
-
source: "timeback-qti",
|
|
103413
|
-
graderVersion: "timeback-qti-process-response-v1"
|
|
103414
|
-
};
|
|
103415
|
-
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
103416
|
-
source: "platform-qti-adapter",
|
|
103417
|
-
graderVersion: "platform-qti-adapter-v1",
|
|
103418
|
-
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
103419
|
-
};
|
|
103420
|
-
PLATFORM_CHILD_GRADING = {
|
|
103421
|
-
source: "platform-artifact",
|
|
103422
|
-
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
103423
|
-
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
103424
|
-
};
|
|
103425
103432
|
});
|
|
103426
103433
|
|
|
103427
103434
|
// ../api-core/src/utils/timeback-qti-hydration.util.ts
|
|
@@ -103508,20 +103515,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103508
103515
|
init_assessment_artifact_runtime_util();
|
|
103509
103516
|
init_assessment_runtime_lock_util();
|
|
103510
103517
|
init_timeback_assessment_artifacts_util();
|
|
103518
|
+
init_timeback_assessment_child_result_util();
|
|
103511
103519
|
init_timeback_assessment_preparation_util();
|
|
103512
103520
|
init_timeback_assessment_publication_util();
|
|
103513
|
-
init_timeback_assessment_reconciliation_util();
|
|
103514
|
-
init_timeback_assessment_replay_util();
|
|
103515
103521
|
init_timeback_assessment_routing_util();
|
|
103516
103522
|
init_timeback_assessment_rules_util();
|
|
103517
103523
|
init_timeback_assessment_runtime_util();
|
|
103518
103524
|
init_timeback_assessment_scoring_util();
|
|
103525
|
+
init_timeback_assessment_transcript_util();
|
|
103519
103526
|
init_timeback_qti_hydration_util();
|
|
103520
103527
|
await __promiseAll([
|
|
103521
103528
|
init_dist5(),
|
|
103522
103529
|
init_errors8(),
|
|
103523
|
-
init_assessment_artifact_loader_util()
|
|
103524
|
-
init_timeback_assessment_child_result_util()
|
|
103530
|
+
init_assessment_artifact_loader_util()
|
|
103525
103531
|
]);
|
|
103526
103532
|
AssessmentPreparationPrerequisiteError = class AssessmentPreparationPrerequisiteError extends Error {
|
|
103527
103533
|
original;
|
|
@@ -103536,8 +103542,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103536
103542
|
static PREPARATION_IN_FLIGHT_LIMIT = 32;
|
|
103537
103543
|
static ASSESSMENT_CACHE_TTL_MS = 60000;
|
|
103538
103544
|
static EXPORT_CONCURRENCY = 4;
|
|
103539
|
-
static ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS = 3;
|
|
103540
|
-
static ASSESSMENT_VERSION_CONFIRMATION_DELAY_MS = 25;
|
|
103541
103545
|
static SCORING_CONCURRENCY = 4;
|
|
103542
103546
|
static STALE_AWARD_AGE_MS = 15 * 60 * 1000;
|
|
103543
103547
|
deps;
|
|
@@ -103554,6 +103558,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103554
103558
|
maxSize: TimebackAssessmentRuntimeService.ASSESSMENT_CACHE_LIMIT
|
|
103555
103559
|
});
|
|
103556
103560
|
artifactLoader;
|
|
103561
|
+
itemScoreCache = new TimebackCache({
|
|
103562
|
+
defaultTTL: 5 * 60000,
|
|
103563
|
+
maxSize: 512
|
|
103564
|
+
});
|
|
103565
|
+
childLineItemCache = new TimebackCache({
|
|
103566
|
+
defaultTTL: 5 * 60000,
|
|
103567
|
+
maxSize: 512
|
|
103568
|
+
});
|
|
103557
103569
|
preparations = new AssessmentPreparationFlights(TimebackAssessmentRuntimeService.PREPARATION_IN_FLIGHT_LIMIT);
|
|
103558
103570
|
constructor(deps) {
|
|
103559
103571
|
this.deps = deps;
|
|
@@ -103619,8 +103631,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103619
103631
|
return this.startReview({ gameId, studentId, input, user });
|
|
103620
103632
|
}
|
|
103621
103633
|
const masteryStandard = input.purpose === "mastery" ? normalizeMasteryStandard(input.standard) : undefined;
|
|
103622
|
-
const requestFingerprint = this.startRequestFingerprint(input);
|
|
103623
|
-
const pendingSupersessions = new Set;
|
|
103624
103634
|
const candidates = await this.candidateIntegrations(gameId, studentId, input);
|
|
103625
103635
|
const lockKeys = candidates.rows.map((row) => assessmentRuntimeStartLockKey({
|
|
103626
103636
|
studentId,
|
|
@@ -103629,7 +103639,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103629
103639
|
courseId: row.courseId
|
|
103630
103640
|
}));
|
|
103631
103641
|
return this.deps.assessmentRuntimeLock(this.deps.db, lockKeys, async (db2) => {
|
|
103632
|
-
const context2 = await this.selectIntegration(candidates,
|
|
103642
|
+
const context2 = await this.selectIntegration(candidates, input);
|
|
103633
103643
|
const attemptScope = {
|
|
103634
103644
|
studentId,
|
|
103635
103645
|
activityId: input.activityId,
|
|
@@ -103642,44 +103652,34 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103642
103652
|
this.liveTests(context2.integration.id, input.purpose, db2, masteryStandard),
|
|
103643
103653
|
this.listAttempts(attemptScope)
|
|
103644
103654
|
]);
|
|
103645
|
-
const
|
|
103646
|
-
|
|
103647
|
-
|
|
103648
|
-
|
|
103649
|
-
|
|
103650
|
-
}
|
|
103651
|
-
|
|
103652
|
-
|
|
103653
|
-
|
|
103654
|
-
|
|
103655
|
-
|
|
103656
|
-
|
|
103657
|
-
|
|
103658
|
-
|
|
103659
|
-
|
|
103660
|
-
|
|
103661
|
-
if (assessment.identifier !== decision.test.qtiTestIdentifier) {
|
|
103662
|
-
assessment = await this.loadAssessment(decision.test.qtiTestIdentifier);
|
|
103663
|
-
}
|
|
103664
|
-
const classId = await this.resolveClassId(context2.integration.courseId, context2.enrollment, studentId);
|
|
103655
|
+
const pending = await this.pendingAwardSnapshot(listing, context2);
|
|
103656
|
+
if (pending) {
|
|
103657
|
+
return pending;
|
|
103658
|
+
}
|
|
103659
|
+
const decision = this.selectFreshStart(tests, listing, input.purpose);
|
|
103660
|
+
const [assessment, classId, { attemptNumber, attemptId }] = await Promise.all([
|
|
103661
|
+
this.loadAssessment(decision.test.qtiTestIdentifier),
|
|
103662
|
+
this.resolveClassId(context2.integration.courseId, context2.enrollment, studentId),
|
|
103663
|
+
this.claimAttemptId({
|
|
103664
|
+
gameId,
|
|
103665
|
+
studentId,
|
|
103666
|
+
courseId: context2.integration.courseId,
|
|
103667
|
+
activityId: input.activityId,
|
|
103668
|
+
purpose: input.purpose
|
|
103669
|
+
}, nextAssessmentAttemptNumber(listing.attempts.values()))
|
|
103670
|
+
]);
|
|
103665
103671
|
const lineItemId = await this.ensureLineItem({
|
|
103666
103672
|
classId,
|
|
103667
103673
|
integration: context2.integration,
|
|
103668
103674
|
purpose: input.purpose,
|
|
103669
103675
|
assessment
|
|
103670
103676
|
});
|
|
103671
|
-
const { attemptNumber, attemptId } = await this.claimAttemptId({
|
|
103672
|
-
gameId,
|
|
103673
|
-
studentId,
|
|
103674
|
-
courseId: context2.integration.courseId,
|
|
103675
|
-
activityId: input.activityId,
|
|
103676
|
-
purpose: input.purpose
|
|
103677
|
-
}, Math.max(0, ...[...refreshed.values()].map((entry) => entry.metadata.attemptNumber)) + 1);
|
|
103678
103677
|
const timestamp6 = new Date().toISOString();
|
|
103679
103678
|
const metadataBase = {
|
|
103680
103679
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
103681
103680
|
activityId: input.activityId,
|
|
103682
103681
|
courseId: context2.integration.courseId,
|
|
103682
|
+
courseName: context2.enrollment.course.title,
|
|
103683
103683
|
integrationId: context2.integration.id,
|
|
103684
103684
|
enrollmentId: context2.enrollment.id,
|
|
103685
103685
|
selectedTest: {
|
|
@@ -103698,7 +103698,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103698
103698
|
...metadataBase,
|
|
103699
103699
|
purpose: input.purpose,
|
|
103700
103700
|
mastery: {
|
|
103701
|
-
requestFingerprint,
|
|
103701
|
+
requestFingerprint: masteryRequestFingerprint(input),
|
|
103702
103702
|
standard: masteryStandard
|
|
103703
103703
|
}
|
|
103704
103704
|
} : { ...metadataBase, purpose: input.purpose };
|
|
@@ -103716,8 +103716,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103716
103716
|
grade: context2.integration.grade
|
|
103717
103717
|
})
|
|
103718
103718
|
});
|
|
103719
|
-
await
|
|
103720
|
-
|
|
103719
|
+
const [snapshot] = await Promise.all([
|
|
103720
|
+
this.snapshot(result, metadata2, assessment, context2.integration),
|
|
103721
|
+
this.supersedeReplacedAttempts(db2, listing.attempts, context2.enrollment.id)
|
|
103722
|
+
]);
|
|
103723
|
+
return snapshot;
|
|
103721
103724
|
});
|
|
103722
103725
|
}
|
|
103723
103726
|
async prepareNewAttempt(params, requestHash) {
|
|
@@ -103726,7 +103729,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103726
103729
|
this.candidateIntegrations(params.gameId, params.studentId, params.input),
|
|
103727
103730
|
this.studentAssessmentResults(params.studentId)
|
|
103728
103731
|
]);
|
|
103729
|
-
const context2 = await this.selectIntegration(candidates, params.
|
|
103732
|
+
const context2 = await this.selectIntegration(candidates, params.input);
|
|
103730
103733
|
const attemptScope = {
|
|
103731
103734
|
studentId: params.studentId,
|
|
103732
103735
|
activityId: params.input.activityId,
|
|
@@ -103736,29 +103739,31 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103736
103739
|
enrollmentId: context2.enrollment.id
|
|
103737
103740
|
};
|
|
103738
103741
|
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);
|
|
103739
|
-
const listing = this.attemptListing(results, attemptScope);
|
|
103740
|
-
|
|
103741
|
-
|
|
103742
|
-
|
|
103743
|
-
const selection = resolveAssessmentPreparationSelection(decision, candidates.rows.length);
|
|
103744
|
-
if (selection.status === "blocked") {
|
|
103745
|
-
this.recordPreparation(selection.result.status, params.input.purpose, startedAt, {
|
|
103746
|
-
reason: selection.result.status === "canonical_required" ? selection.result.reason : "multiple_candidate_courses"
|
|
103742
|
+
const listing = await this.attemptListing(results, attemptScope);
|
|
103743
|
+
if (this.pendingAwardAttempt(listing, context2.enrollment.id)) {
|
|
103744
|
+
this.recordPreparation("unprepared", params.input.purpose, startedAt, {
|
|
103745
|
+
reason: "pending_award"
|
|
103747
103746
|
});
|
|
103748
|
-
return
|
|
103747
|
+
return { status: "unprepared", reason: "prerequisite_unavailable" };
|
|
103749
103748
|
}
|
|
103750
|
-
if (
|
|
103751
|
-
|
|
103749
|
+
if (candidates.rows.length !== 1) {
|
|
103750
|
+
this.recordPreparation("unprepared", params.input.purpose, startedAt, {
|
|
103751
|
+
reason: "multiple_candidate_courses"
|
|
103752
|
+
});
|
|
103753
|
+
return { status: "unprepared", reason: "prerequisite_unavailable" };
|
|
103752
103754
|
}
|
|
103753
103755
|
const preparation = {
|
|
103754
103756
|
context: context2,
|
|
103755
103757
|
tests,
|
|
103756
103758
|
listing,
|
|
103757
|
-
decision:
|
|
103759
|
+
decision: this.selectFreshStart(tests, listing, params.input.purpose)
|
|
103758
103760
|
};
|
|
103759
103761
|
let prepared;
|
|
103760
103762
|
try {
|
|
103761
103763
|
prepared = await this.buildPreparationPlan(params, requestHash, preparation);
|
|
103764
|
+
if (!await this.loadPreparedPlanAssessment(prepared.plan)) {
|
|
103765
|
+
throw new Error("Prepared assessment artifact is unavailable");
|
|
103766
|
+
}
|
|
103762
103767
|
} catch (error88) {
|
|
103763
103768
|
if (error88 instanceof ValidationError || error88 instanceof AssessmentRuntimeError) {
|
|
103764
103769
|
throw error88;
|
|
@@ -103844,8 +103849,10 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103844
103849
|
integrationId: context2.integration.id,
|
|
103845
103850
|
enrollmentId: context2.enrollment.id,
|
|
103846
103851
|
courseId: context2.integration.courseId,
|
|
103852
|
+
courseName: context2.enrollment.course.title,
|
|
103847
103853
|
...attempt,
|
|
103848
103854
|
parentLineItemId,
|
|
103855
|
+
supersedes: [...this.replacedAttemptIds(listing.attempts, context2.enrollment.id)],
|
|
103849
103856
|
selectedTest: {
|
|
103850
103857
|
assessmentKey: decision.test.assessmentKey,
|
|
103851
103858
|
identifier: assessment.identifier,
|
|
@@ -103884,7 +103891,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103884
103891
|
const selected = selectReviewItems({
|
|
103885
103892
|
request: params.input,
|
|
103886
103893
|
bank: catalog.bank,
|
|
103887
|
-
exposures:
|
|
103894
|
+
exposures: assessmentReviewTranscriptExposures({
|
|
103895
|
+
attempts: listing.attempts.values(),
|
|
103896
|
+
children: listing.reviewChildren.values(),
|
|
103888
103897
|
bankRevision: catalog.bank.bankRevision
|
|
103889
103898
|
})
|
|
103890
103899
|
});
|
|
@@ -103895,26 +103904,12 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103895
103904
|
throw AssessmentRuntimeError.from(assessmentReviewBankShortage(selected.fulfillment));
|
|
103896
103905
|
}
|
|
103897
103906
|
const artifact = await this.loadOrCompileReviewBankArtifact(catalog);
|
|
103898
|
-
const assessment = projectReviewAssessment(artifact.assessment, catalog.bank, selected.selections);
|
|
103899
103907
|
const source = { assessment: artifact.assessment, bank: catalog.bank };
|
|
103900
103908
|
const parentLineItemId = await this.provisionPreparationPrerequisites(() => this.ensureReviewBankLineItem({
|
|
103901
103909
|
integration: context2.integration,
|
|
103902
103910
|
activityId: params.input.activityId,
|
|
103903
103911
|
source
|
|
103904
103912
|
}));
|
|
103905
|
-
await this.provisionPreparationPrerequisites(() => runWithConcurrency(assessment.items, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, (item) => {
|
|
103906
|
-
const indexed = catalog.bank.items.find((candidate) => candidate.itemIdentifier === item.identifier);
|
|
103907
|
-
if (!indexed) {
|
|
103908
|
-
throw new Error(`Review-bank index is missing item ${item.identifier}`);
|
|
103909
|
-
}
|
|
103910
|
-
return this.ensureReviewQuestionLineItem({
|
|
103911
|
-
parentLineItemId,
|
|
103912
|
-
integration: context2.integration,
|
|
103913
|
-
bankRevision: catalog.bank.bankRevision,
|
|
103914
|
-
standards: [...indexed.standards],
|
|
103915
|
-
item
|
|
103916
|
-
});
|
|
103917
|
-
}));
|
|
103918
103913
|
const attempt = await this.provisionPreparationPrerequisites(() => this.claimAttemptId({
|
|
103919
103914
|
gameId: params.gameId,
|
|
103920
103915
|
studentId: params.studentId,
|
|
@@ -103933,8 +103928,10 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103933
103928
|
integrationId: context2.integration.id,
|
|
103934
103929
|
enrollmentId: context2.enrollment.id,
|
|
103935
103930
|
courseId: context2.integration.courseId,
|
|
103931
|
+
courseName: context2.enrollment.course.title,
|
|
103936
103932
|
...attempt,
|
|
103937
103933
|
parentLineItemId,
|
|
103934
|
+
supersedes: [...this.replacedAttemptIds(listing.attempts, context2.enrollment.id)],
|
|
103938
103935
|
selectedTest: {
|
|
103939
103936
|
assessmentKey: decision.test.assessmentKey,
|
|
103940
103937
|
identifier: artifact.assessment.identifier,
|
|
@@ -103983,10 +103980,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103983
103980
|
if (!integration || !await this.preparedPlanTestIsLive(verified, db2)) {
|
|
103984
103981
|
return null;
|
|
103985
103982
|
}
|
|
103986
|
-
|
|
103987
|
-
return null;
|
|
103988
|
-
}
|
|
103989
|
-
return this.createPreparedAttempt(verified, assessment, integration);
|
|
103983
|
+
return this.claimPreparedAttempt(db2, verified, assessment, integration);
|
|
103990
103984
|
});
|
|
103991
103985
|
if (!outcome) {
|
|
103992
103986
|
this.recordPreparedStart("fallback", params.input.purpose, startedAt, {
|
|
@@ -104011,8 +104005,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104011
104005
|
studentId,
|
|
104012
104006
|
input
|
|
104013
104007
|
}) {
|
|
104014
|
-
const requestFingerprint = this.startRequestFingerprint(input);
|
|
104015
|
-
const pendingSupersessions = new Set;
|
|
104016
104008
|
const candidates = await this.candidateIntegrations(gameId, studentId, input);
|
|
104017
104009
|
const lockKeys = candidates.rows.map((row) => assessmentRuntimeStartLockKey({
|
|
104018
104010
|
studentId,
|
|
@@ -104021,7 +104013,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104021
104013
|
courseId: row.courseId
|
|
104022
104014
|
}));
|
|
104023
104015
|
return this.deps.assessmentRuntimeLock(this.deps.db, lockKeys, async (db2) => {
|
|
104024
|
-
const context2 = await this.selectIntegration(candidates,
|
|
104016
|
+
const context2 = await this.selectIntegration(candidates, input);
|
|
104025
104017
|
const attemptScope = {
|
|
104026
104018
|
studentId,
|
|
104027
104019
|
activityId: input.activityId,
|
|
@@ -104034,32 +104026,25 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104034
104026
|
this.liveTests(context2.integration.id, input.purpose, db2, undefined, input.diagnosticKey),
|
|
104035
104027
|
this.listAttempts(attemptScope)
|
|
104036
104028
|
]);
|
|
104037
|
-
const
|
|
104038
|
-
if (
|
|
104039
|
-
|
|
104040
|
-
return initialResolution.snapshot;
|
|
104029
|
+
const pending = await this.pendingAwardSnapshot(listing, context2);
|
|
104030
|
+
if (pending) {
|
|
104031
|
+
return pending;
|
|
104041
104032
|
}
|
|
104042
|
-
|
|
104033
|
+
const decision = this.selectFreshStart(tests, listing, input.purpose);
|
|
104043
104034
|
let definition = await this.requireDiagnosticDefinition(decision.test.id, context2.integration.id, db2, true);
|
|
104044
|
-
|
|
104045
|
-
|
|
104046
|
-
|
|
104047
|
-
|
|
104048
|
-
|
|
104049
|
-
|
|
104050
|
-
|
|
104051
|
-
|
|
104052
|
-
|
|
104053
|
-
|
|
104054
|
-
|
|
104055
|
-
|
|
104056
|
-
|
|
104057
|
-
routing = await this.initializeHostedDiagnostic(definition, assessment);
|
|
104058
|
-
} else {
|
|
104059
|
-
definition = await this.requireDiagnosticDefinition(definition.id, context2.integration.id, db2, true);
|
|
104060
|
-
routing = await this.initializeHostedDiagnostic(definition, assessment);
|
|
104061
|
-
}
|
|
104062
|
-
const classId = await this.resolveClassId(context2.integration.courseId, context2.enrollment, studentId);
|
|
104035
|
+
const [assessment, classId, { attemptNumber, attemptId }] = await Promise.all([
|
|
104036
|
+
this.loadAssessment(definition.qtiTestIdentifier),
|
|
104037
|
+
this.resolveClassId(context2.integration.courseId, context2.enrollment, studentId),
|
|
104038
|
+
this.claimAttemptId({
|
|
104039
|
+
gameId,
|
|
104040
|
+
studentId,
|
|
104041
|
+
courseId: context2.integration.courseId,
|
|
104042
|
+
activityId: input.activityId,
|
|
104043
|
+
purpose: input.purpose
|
|
104044
|
+
}, nextAssessmentAttemptNumber(listing.attempts.values()))
|
|
104045
|
+
]);
|
|
104046
|
+
definition = await this.requireDiagnosticDefinition(definition.id, context2.integration.id, db2, true);
|
|
104047
|
+
const routing = await this.initializeHostedDiagnostic(definition, assessment);
|
|
104063
104048
|
const lineItemId = await this.ensureLineItem({
|
|
104064
104049
|
classId,
|
|
104065
104050
|
integration: context2.integration,
|
|
@@ -104071,19 +104056,13 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104071
104056
|
routingRevision: routing.routingRevision
|
|
104072
104057
|
}
|
|
104073
104058
|
});
|
|
104074
|
-
const { attemptNumber, attemptId } = await this.claimAttemptId({
|
|
104075
|
-
gameId,
|
|
104076
|
-
studentId,
|
|
104077
|
-
courseId: context2.integration.courseId,
|
|
104078
|
-
activityId: input.activityId,
|
|
104079
|
-
purpose: input.purpose
|
|
104080
|
-
}, Math.max(0, ...[...refreshed.values()].map((entry) => entry.metadata.attemptNumber)) + 1);
|
|
104081
104059
|
const timestamp6 = new Date().toISOString();
|
|
104082
104060
|
const metadata2 = {
|
|
104083
104061
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
104084
104062
|
activityId: input.activityId,
|
|
104085
104063
|
purpose: "diagnostic",
|
|
104086
104064
|
courseId: context2.integration.courseId,
|
|
104065
|
+
courseName: context2.enrollment.course.title,
|
|
104087
104066
|
integrationId: context2.integration.id,
|
|
104088
104067
|
enrollmentId: context2.enrollment.id,
|
|
104089
104068
|
selectedTest: {
|
|
@@ -104103,7 +104082,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104103
104082
|
testName: assessment.title,
|
|
104104
104083
|
routingRevision: routing.routingRevision,
|
|
104105
104084
|
ledger: [],
|
|
104106
|
-
routingSnapshots: [],
|
|
104107
104085
|
state: routing.state
|
|
104108
104086
|
}
|
|
104109
104087
|
};
|
|
@@ -104121,8 +104099,13 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104121
104099
|
grade: context2.integration.grade
|
|
104122
104100
|
})
|
|
104123
104101
|
});
|
|
104124
|
-
await
|
|
104125
|
-
|
|
104102
|
+
const [snapshot] = await Promise.all([
|
|
104103
|
+
this.diagnosticSnapshot(result, metadata2, assessment, context2.integration, {
|
|
104104
|
+
state: routing.state
|
|
104105
|
+
}),
|
|
104106
|
+
this.supersedeReplacedAttempts(db2, listing.attempts, context2.enrollment.id)
|
|
104107
|
+
]);
|
|
104108
|
+
return snapshot;
|
|
104126
104109
|
});
|
|
104127
104110
|
}
|
|
104128
104111
|
async startReview({
|
|
@@ -104132,7 +104115,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104132
104115
|
}) {
|
|
104133
104116
|
const preparationStartedAt = Date.now();
|
|
104134
104117
|
const requestFingerprint = reviewRequestFingerprint(input, DEFAULT_REVIEW_SELECTION_POLICY);
|
|
104135
|
-
const pendingSupersessions = new Set;
|
|
104136
104118
|
const normalizedRequest = normalizeReviewRequest(input);
|
|
104137
104119
|
const candidates = await this.candidateIntegrations(gameId, studentId, input);
|
|
104138
104120
|
const lockKeys = candidates.rows.map((row) => assessmentRuntimeStartLockKey({
|
|
@@ -104142,7 +104124,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104142
104124
|
courseId: row.courseId
|
|
104143
104125
|
}));
|
|
104144
104126
|
return this.deps.assessmentRuntimeLock(this.deps.db, lockKeys, async (db2) => {
|
|
104145
|
-
const context2 = await this.selectIntegration(candidates,
|
|
104127
|
+
const context2 = await this.selectIntegration(candidates, input);
|
|
104146
104128
|
const attemptScope = {
|
|
104147
104129
|
studentId,
|
|
104148
104130
|
activityId: input.activityId,
|
|
@@ -104155,40 +104137,22 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104155
104137
|
this.liveTests(context2.integration.id, input.purpose, db2),
|
|
104156
104138
|
this.listAttempts(attemptScope)
|
|
104157
104139
|
]);
|
|
104158
|
-
const
|
|
104159
|
-
|
|
104160
|
-
|
|
104161
|
-
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104162
|
-
return initialResolution.snapshot;
|
|
104163
|
-
}
|
|
104164
|
-
let decision = initialResolution.decision;
|
|
104165
|
-
if (tests.length === 0 || !decision) {
|
|
104166
|
-
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(input.purpose));
|
|
104140
|
+
const pending = await this.pendingAwardSnapshot(listing, context2);
|
|
104141
|
+
if (pending) {
|
|
104142
|
+
return pending;
|
|
104167
104143
|
}
|
|
104144
|
+
const decision = this.selectFreshStart(tests, listing, input.purpose);
|
|
104168
104145
|
if (tests.length > 1) {
|
|
104169
104146
|
throw new ValidationError("Standards review requires exactly one live review-bank assessment.");
|
|
104170
104147
|
}
|
|
104171
104148
|
const manifestStartedAt = Date.now();
|
|
104172
|
-
|
|
104149
|
+
const catalog = await this.loadReviewBankCatalog(decision.test.qtiTestIdentifier);
|
|
104173
104150
|
this.recordReviewPreparationPhase("manifest_load", manifestStartedAt, {
|
|
104174
104151
|
candidateCount: catalog.bank.items.length
|
|
104175
104152
|
});
|
|
104176
|
-
const
|
|
104177
|
-
|
|
104178
|
-
|
|
104179
|
-
if (refreshedResolution.kind === "resumed") {
|
|
104180
|
-
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104181
|
-
return refreshedResolution.snapshot;
|
|
104182
|
-
}
|
|
104183
|
-
const refreshedDecision = refreshedResolution.decision;
|
|
104184
|
-
if (!refreshedDecision || refreshedDecision.kind !== "start") {
|
|
104185
|
-
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(input.purpose));
|
|
104186
|
-
}
|
|
104187
|
-
decision = refreshedDecision;
|
|
104188
|
-
if (catalog.test.identifier !== decision.test.qtiTestIdentifier) {
|
|
104189
|
-
catalog = await this.loadReviewBankCatalog(decision.test.qtiTestIdentifier);
|
|
104190
|
-
}
|
|
104191
|
-
const exposures = this.reviewExposures(refreshedListing.reviewChildren.values(), {
|
|
104153
|
+
const exposures = assessmentReviewTranscriptExposures({
|
|
104154
|
+
attempts: listing.attempts.values(),
|
|
104155
|
+
children: listing.reviewChildren.values(),
|
|
104192
104156
|
bankRevision: catalog.bank.bankRevision
|
|
104193
104157
|
});
|
|
104194
104158
|
const selected = selectReviewItems({
|
|
@@ -104209,24 +104173,27 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104209
104173
|
});
|
|
104210
104174
|
const assessment = projectReviewAssessment(source.assessment, source.bank, selected.selections);
|
|
104211
104175
|
const provisioningStartedAt = Date.now();
|
|
104212
|
-
const parentLineItemId = await
|
|
104213
|
-
|
|
104214
|
-
|
|
104215
|
-
|
|
104216
|
-
|
|
104217
|
-
|
|
104218
|
-
|
|
104219
|
-
|
|
104220
|
-
|
|
104221
|
-
|
|
104222
|
-
|
|
104223
|
-
|
|
104176
|
+
const [parentLineItemId, { attemptNumber, attemptId }] = await Promise.all([
|
|
104177
|
+
this.ensureReviewBankLineItem({
|
|
104178
|
+
integration: context2.integration,
|
|
104179
|
+
activityId: input.activityId,
|
|
104180
|
+
source
|
|
104181
|
+
}),
|
|
104182
|
+
this.claimAttemptId({
|
|
104183
|
+
gameId,
|
|
104184
|
+
studentId,
|
|
104185
|
+
courseId: context2.integration.courseId,
|
|
104186
|
+
activityId: input.activityId,
|
|
104187
|
+
purpose: input.purpose
|
|
104188
|
+
}, nextAssessmentAttemptNumber(listing.attempts.values()))
|
|
104189
|
+
]);
|
|
104224
104190
|
const timestamp6 = new Date().toISOString();
|
|
104225
104191
|
const metadata2 = {
|
|
104226
104192
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
104227
104193
|
activityId: input.activityId,
|
|
104228
104194
|
purpose: input.purpose,
|
|
104229
104195
|
courseId: context2.integration.courseId,
|
|
104196
|
+
courseName: context2.enrollment.course.title,
|
|
104230
104197
|
integrationId: context2.integration.id,
|
|
104231
104198
|
enrollmentId: context2.enrollment.id,
|
|
104232
104199
|
selectedTest: {
|
|
@@ -104263,18 +104230,27 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104263
104230
|
grade: context2.integration.grade
|
|
104264
104231
|
})
|
|
104265
104232
|
});
|
|
104266
|
-
await
|
|
104233
|
+
const [snapshot] = await Promise.all([
|
|
104234
|
+
this.snapshot(result, metadata2, assessment, context2.integration),
|
|
104235
|
+
this.supersedeReplacedAttempts(db2, listing.attempts, context2.enrollment.id)
|
|
104236
|
+
]);
|
|
104267
104237
|
this.recordReviewPreparationPhase("provisioning", provisioningStartedAt);
|
|
104268
104238
|
this.recordReviewPreparationPhase("total", preparationStartedAt, {
|
|
104269
104239
|
candidateCount: catalog.bank.items.length,
|
|
104270
104240
|
selectedCount: selected.selections.length
|
|
104271
104241
|
});
|
|
104272
|
-
return
|
|
104242
|
+
return snapshot;
|
|
104273
104243
|
});
|
|
104274
104244
|
}
|
|
104275
104245
|
async get(params) {
|
|
104246
|
+
return this.resume({ ...params, input: {} });
|
|
104247
|
+
}
|
|
104248
|
+
async resume({
|
|
104249
|
+
input,
|
|
104250
|
+
...params
|
|
104251
|
+
}) {
|
|
104276
104252
|
const attempt = await this.authorizeAttemptRead(params);
|
|
104277
|
-
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration);
|
|
104253
|
+
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration, input.transcript);
|
|
104278
104254
|
}
|
|
104279
104255
|
async latest({
|
|
104280
104256
|
gameId,
|
|
@@ -104336,496 +104312,104 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104336
104312
|
}
|
|
104337
104313
|
async save({ input, ...params }) {
|
|
104338
104314
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104339
|
-
try {
|
|
104340
|
-
const warmup = await this.peekAttempt(params);
|
|
104341
|
-
if (this.isOpen(warmup.result) && !this.isPlatformRoutedDiagnosticMetadata(warmup.metadata)) {
|
|
104342
|
-
await this.loadAttemptAssessment(warmup.metadata);
|
|
104343
|
-
}
|
|
104344
|
-
} catch (error88) {
|
|
104345
|
-
addEvent("assessment.save_warmup_failed", {
|
|
104346
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104347
|
-
"exception.type": errorType(error88),
|
|
104348
|
-
"app.error.message": errorMessage(error88)
|
|
104349
|
-
});
|
|
104350
|
-
}
|
|
104351
104315
|
return this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104352
|
-
const attempt = await this.
|
|
104316
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104353
104317
|
db: db2,
|
|
104354
|
-
developerAccessValidated: true
|
|
104318
|
+
developerAccessValidated: true,
|
|
104319
|
+
includeEnrollment: false
|
|
104355
104320
|
});
|
|
104356
|
-
|
|
104357
|
-
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
|
|
104362
|
-
|
|
104363
|
-
|
|
104364
|
-
|
|
104365
|
-
|
|
104366
|
-
|
|
104367
|
-
|
|
104368
|
-
validateAssessmentDraftResponses(assessment, responses);
|
|
104369
|
-
const metadata2 = {
|
|
104370
|
-
...attempt.metadata,
|
|
104371
|
-
responses,
|
|
104372
|
-
responseVersion: attempt.metadata.responseVersion + 1,
|
|
104373
|
-
updatedAt: new Date().toISOString()
|
|
104374
|
-
};
|
|
104375
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
104321
|
+
requireAssessmentTranscriptCheckpoint(attempt.result, attempt.integration.status, input);
|
|
104322
|
+
const graded = await this.gradeTranscript({
|
|
104323
|
+
attemptId: params.attemptId,
|
|
104324
|
+
metadata: attempt.metadata
|
|
104325
|
+
}, input.transcript, false, db2);
|
|
104326
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, buildAssessmentResultCheckpoint({
|
|
104327
|
+
result: attempt.result,
|
|
104328
|
+
metadata: attempt.metadata,
|
|
104329
|
+
transcript: graded.transcript,
|
|
104330
|
+
timestamp: graded.metadata.updatedAt
|
|
104331
|
+
}));
|
|
104332
|
+
this.reconcileTranscriptChildren(this.tokenContext(attempt), graded).catch((error88) => this.recordTranscriptFailure("checkpoint_projection", params.attemptId, error88));
|
|
104376
104333
|
return {
|
|
104377
|
-
attemptId:
|
|
104378
|
-
responseVersion:
|
|
104334
|
+
attemptId: params.attemptId,
|
|
104335
|
+
responseVersion: graded.metadata.responseVersion,
|
|
104379
104336
|
status: "in_progress",
|
|
104380
|
-
responses
|
|
104337
|
+
responses: graded.metadata.responses,
|
|
104338
|
+
transcript: graded.transcript
|
|
104381
104339
|
};
|
|
104382
104340
|
});
|
|
104383
104341
|
}
|
|
104384
|
-
async
|
|
104342
|
+
async recordProgress({
|
|
104385
104343
|
input,
|
|
104386
104344
|
...params
|
|
104387
104345
|
}) {
|
|
104388
104346
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104389
|
-
|
|
104390
|
-
|
|
104391
|
-
|
|
104392
|
-
|
|
104393
|
-
}
|
|
104394
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is not accepted by the hosted conventional item-submit flow.", { routingNodeKey: input.routingNodeKey }));
|
|
104395
|
-
}
|
|
104396
|
-
let preview = await this.prepareItemSubmission(params, input);
|
|
104397
|
-
let preparationAttempts = 1;
|
|
104398
|
-
while (true) {
|
|
104399
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104400
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, {
|
|
104401
|
-
db: db2,
|
|
104402
|
-
developerAccessValidated: true
|
|
104403
|
-
});
|
|
104404
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104405
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104406
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("Platform-routed diagnostic submissions require routingNodeKey.", { flow: "platform-routed-item-submit" }));
|
|
104407
|
-
}
|
|
104408
|
-
const replay = resolveAssessmentItemReplay(attempt.metadata, input);
|
|
104409
|
-
if (replay.action === "conflict") {
|
|
104410
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104411
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
104412
|
-
}
|
|
104413
|
-
if (replay.action === "replay") {
|
|
104414
|
-
const prior = replay.submission;
|
|
104415
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104416
|
-
attempt,
|
|
104417
|
-
metadata: attempt.metadata,
|
|
104418
|
-
submission: prior
|
|
104419
|
-
}) : null;
|
|
104420
|
-
return {
|
|
104421
|
-
action: "complete",
|
|
104422
|
-
response: buildAssessmentItemReplayResult(attempt.result.sourcedId, attempt.metadata, prior),
|
|
104423
|
-
checkpoint: child2,
|
|
104424
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
104425
|
-
result: attempt.result,
|
|
104426
|
-
metadata: attempt.metadata,
|
|
104427
|
-
itemIdentifier: input.itemIdentifier,
|
|
104428
|
-
integration: attempt.integration
|
|
104429
|
-
} : null
|
|
104430
|
-
};
|
|
104431
|
-
}
|
|
104432
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104433
|
-
this.assertInProgress(attempt.result);
|
|
104434
|
-
if (!preview || preview.responseVersion !== attempt.metadata.responseVersion || preview.submissionId !== input.submissionId || preview.itemIdentifier !== input.itemIdentifier) {
|
|
104435
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
104436
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, attempt.metadata.responseVersion, "assessment");
|
|
104437
|
-
}
|
|
104438
|
-
return { action: "prepare" };
|
|
104439
|
-
}
|
|
104440
|
-
const transition = prepareAssessmentItemSubmission({
|
|
104441
|
-
purpose: attempt.metadata.purpose,
|
|
104442
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104443
|
-
responses: attempt.metadata.responses,
|
|
104444
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104445
|
-
assessment: preview.assessment
|
|
104446
|
-
}, input);
|
|
104447
|
-
if (transition.action === "reject") {
|
|
104448
|
-
throw AssessmentRuntimeError.from(transition.failure);
|
|
104449
|
-
}
|
|
104450
|
-
if (transition.action === "replay") {
|
|
104451
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104452
|
-
attempt,
|
|
104453
|
-
metadata: attempt.metadata,
|
|
104454
|
-
submission: transition.submission
|
|
104455
|
-
}) : null;
|
|
104456
|
-
return {
|
|
104457
|
-
action: "complete",
|
|
104458
|
-
response: {
|
|
104459
|
-
attemptId: attempt.result.sourcedId,
|
|
104460
|
-
responseVersion: transition.responseVersion,
|
|
104461
|
-
status: "in_progress",
|
|
104462
|
-
responses: transition.responses,
|
|
104463
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104464
|
-
submission: transition.submission
|
|
104465
|
-
},
|
|
104466
|
-
checkpoint: child2,
|
|
104467
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
104468
|
-
result: attempt.result,
|
|
104469
|
-
metadata: attempt.metadata,
|
|
104470
|
-
itemIdentifier: input.itemIdentifier,
|
|
104471
|
-
integration: attempt.integration
|
|
104472
|
-
} : null
|
|
104473
|
-
};
|
|
104474
|
-
}
|
|
104475
|
-
if (!preview.scoring) {
|
|
104476
|
-
throw new Error("Prepared item submission is missing its score");
|
|
104477
|
-
}
|
|
104478
|
-
const submittedAt = new Date().toISOString();
|
|
104479
|
-
const completed = completeAssessmentItemSubmission(attempt.metadata, input, transition, preview.scoring, submittedAt);
|
|
104480
|
-
const metadata2 = {
|
|
104481
|
-
...attempt.metadata,
|
|
104482
|
-
responses: completed.responses,
|
|
104483
|
-
responseVersion: completed.responseVersion,
|
|
104484
|
-
itemSubmissions: completed.itemSubmissions,
|
|
104485
|
-
updatedAt: submittedAt
|
|
104486
|
-
};
|
|
104487
|
-
const child = metadata2.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104488
|
-
attempt,
|
|
104489
|
-
metadata: metadata2,
|
|
104490
|
-
submission: completed.submission
|
|
104491
|
-
}) : null;
|
|
104492
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
104493
|
-
return {
|
|
104494
|
-
action: "complete",
|
|
104495
|
-
response: {
|
|
104496
|
-
attemptId: attempt.result.sourcedId,
|
|
104497
|
-
responseVersion: completed.responseVersion,
|
|
104498
|
-
status: "in_progress",
|
|
104499
|
-
responses: completed.responses,
|
|
104500
|
-
itemSubmissions: completed.itemSubmissions,
|
|
104501
|
-
submission: completed.submission
|
|
104502
|
-
},
|
|
104503
|
-
checkpoint: child,
|
|
104504
|
-
projection: metadata2.purpose === "review" ? {
|
|
104505
|
-
result: attempt.result,
|
|
104506
|
-
metadata: metadata2,
|
|
104507
|
-
itemIdentifier: input.itemIdentifier,
|
|
104508
|
-
integration: attempt.integration
|
|
104509
|
-
} : null
|
|
104510
|
-
};
|
|
104511
|
-
});
|
|
104512
|
-
if (committed.action === "prepare") {
|
|
104513
|
-
preparationAttempts += 1;
|
|
104514
|
-
preview = await this.scoreItemSubmission(params, input);
|
|
104515
|
-
} else {
|
|
104516
|
-
if (committed.projection) {
|
|
104517
|
-
this.projectReviewItemResponse(params, committed.projection, committed.checkpoint);
|
|
104518
|
-
}
|
|
104519
|
-
return committed.response;
|
|
104520
|
-
}
|
|
104521
|
-
}
|
|
104522
|
-
}
|
|
104523
|
-
async submitDiagnosticItem(params, input) {
|
|
104524
|
-
let preview = await this.prepareDiagnosticItemSubmission(params, input);
|
|
104525
|
-
let preparationAttempts = 1;
|
|
104526
|
-
while (true) {
|
|
104527
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104528
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, { db: db2, developerAccessValidated: true });
|
|
104529
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104530
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104531
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is accepted only for a platform-routed diagnostic.", { routingNodeKey: input.routingNodeKey }));
|
|
104532
|
-
}
|
|
104533
|
-
const metadata2 = attempt.metadata;
|
|
104534
|
-
const replay = resolveDiagnosticItemReplay(metadata2, input);
|
|
104535
|
-
if (replay.action === "conflict") {
|
|
104536
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104537
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
104538
|
-
}
|
|
104539
|
-
if (replay.action === "replay") {
|
|
104540
|
-
const { submission: prior, ledgerEntry, priorIndex } = replay;
|
|
104541
|
-
let routing = replay.routing;
|
|
104542
|
-
if (!routing) {
|
|
104543
|
-
const assessment = preview?.assessment ?? await this.loadAttemptAssessment(metadata2);
|
|
104544
|
-
const loaded2 = await this.loadAttemptDiagnosticRouting(metadata2, assessment, db2);
|
|
104545
|
-
const replayed = replayDiagnosticRouting(loaded2.manifest, metadata2.diagnostic.ledger.slice(0, priorIndex + 1));
|
|
104546
|
-
if (!replayed.ok) {
|
|
104547
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The committed diagnostic routing state cannot be replayed.", { failure: replayed.failure }));
|
|
104548
|
-
}
|
|
104549
|
-
routing = this.diagnosticRoutingSnapshot(metadata2, replayed.state);
|
|
104550
|
-
}
|
|
104551
|
-
const child2 = await this.prepareDiagnosticAssessmentChildResult({
|
|
104552
|
-
attempt,
|
|
104553
|
-
metadata: metadata2,
|
|
104554
|
-
submission: prior,
|
|
104555
|
-
transition: ledgerEntry
|
|
104556
|
-
});
|
|
104557
|
-
return {
|
|
104558
|
-
action: "complete",
|
|
104559
|
-
response: buildDiagnosticItemReplayResult(attempt.result.sourcedId, prior, ledgerEntry, routing),
|
|
104560
|
-
checkpoint: child2,
|
|
104561
|
-
projection: {
|
|
104562
|
-
result: attempt.result,
|
|
104563
|
-
metadata: metadata2,
|
|
104564
|
-
ledgerEntry,
|
|
104565
|
-
integration: attempt.integration
|
|
104566
|
-
}
|
|
104567
|
-
};
|
|
104568
|
-
}
|
|
104569
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104570
|
-
this.assertInProgress(attempt.result);
|
|
104571
|
-
if (!preview || preview.responseVersion !== metadata2.responseVersion || preview.submissionId !== input.submissionId || preview.routingNodeKey !== input.routingNodeKey || preview.itemIdentifier !== input.itemIdentifier) {
|
|
104572
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
104573
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, metadata2.responseVersion, "diagnostic");
|
|
104574
|
-
}
|
|
104575
|
-
return { action: "prepare" };
|
|
104576
|
-
}
|
|
104577
|
-
this.assertResponseVersion(metadata2, input.expectedResponseVersion);
|
|
104578
|
-
const loaded = await this.loadAttemptDiagnosticRouting(metadata2, preview.assessment, db2);
|
|
104579
|
-
const expected = loaded.state.next;
|
|
104580
|
-
if (loaded.state.status !== "in-progress" || !expected || expected.nodeKey !== input.routingNodeKey || expected.itemIdentifier !== input.itemIdentifier) {
|
|
104581
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(expected ? "Submitted diagnostic item is not the canonical next item." : "Diagnostic routing is already ready to complete.", {
|
|
104582
|
-
expectedRoutingNodeKey: expected?.nodeKey ?? null,
|
|
104583
|
-
routingNodeKey: input.routingNodeKey,
|
|
104584
|
-
expectedItemIdentifier: expected?.itemIdentifier ?? null,
|
|
104585
|
-
itemIdentifier: input.itemIdentifier
|
|
104586
|
-
}));
|
|
104587
|
-
}
|
|
104588
|
-
const responses = prepareDiagnosticAssessmentResponses(preview.assessment, metadata2.responses, input);
|
|
104589
|
-
const scoring = preview.scoring;
|
|
104590
|
-
if (!scoring || typeof scoring.isCorrect !== "boolean") {
|
|
104591
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(`Diagnostic item ${input.itemIdentifier} did not produce determinate binary grading.`, { itemIdentifier: input.itemIdentifier }));
|
|
104592
|
-
}
|
|
104593
|
-
const advanced = advanceDiagnosticRouting(loaded.manifest, metadata2.diagnostic.ledger, {
|
|
104594
|
-
routingNodeKey: input.routingNodeKey,
|
|
104595
|
-
itemIdentifier: input.itemIdentifier,
|
|
104596
|
-
isCorrect: scoring.isCorrect
|
|
104597
|
-
});
|
|
104598
|
-
if (!advanced.ok) {
|
|
104599
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The diagnostic routing transition was rejected.", { failure: advanced.failure }));
|
|
104600
|
-
}
|
|
104601
|
-
const submittedAt = new Date().toISOString();
|
|
104602
|
-
const responseVersion = metadata2.responseVersion + 1;
|
|
104603
|
-
const routingSnapshot = this.diagnosticRoutingSnapshot(metadata2, advanced.state);
|
|
104604
|
-
const routingSnapshots = metadata2.diagnostic.routingSnapshots ? [...metadata2.diagnostic.routingSnapshots, routingSnapshot] : undefined;
|
|
104605
|
-
const submission = {
|
|
104606
|
-
submissionId: input.submissionId,
|
|
104607
|
-
itemIdentifier: input.itemIdentifier,
|
|
104608
|
-
submittedAt,
|
|
104609
|
-
responseVersion,
|
|
104610
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0,
|
|
104611
|
-
score: scoring.score,
|
|
104612
|
-
isCorrect: scoring.isCorrect,
|
|
104613
|
-
grading: scoring.grading
|
|
104614
|
-
};
|
|
104615
|
-
const nextMetadata = {
|
|
104616
|
-
...metadata2,
|
|
104617
|
-
responses,
|
|
104618
|
-
responseVersion,
|
|
104619
|
-
itemSubmissions: [...metadata2.itemSubmissions, submission],
|
|
104620
|
-
updatedAt: submittedAt,
|
|
104621
|
-
diagnostic: {
|
|
104622
|
-
...metadata2.diagnostic,
|
|
104623
|
-
ledger: [...metadata2.diagnostic.ledger, advanced.ledgerEntry],
|
|
104624
|
-
...routingSnapshots ? { routingSnapshots } : {},
|
|
104625
|
-
state: advanced.state
|
|
104626
|
-
}
|
|
104627
|
-
};
|
|
104628
|
-
const child = await this.prepareDiagnosticAssessmentChildResult({
|
|
104629
|
-
attempt,
|
|
104630
|
-
metadata: nextMetadata,
|
|
104631
|
-
submission,
|
|
104632
|
-
transition: advanced.ledgerEntry
|
|
104633
|
-
});
|
|
104634
|
-
await this.putResultMetadata(attempt.result, nextMetadata);
|
|
104635
|
-
return {
|
|
104636
|
-
action: "complete",
|
|
104637
|
-
response: {
|
|
104638
|
-
attemptId: attempt.result.sourcedId,
|
|
104639
|
-
responseVersion,
|
|
104640
|
-
status: "in_progress",
|
|
104641
|
-
routing: routingSnapshot,
|
|
104642
|
-
submission: diagnosticReceipt(submission, advanced.ledgerEntry)
|
|
104643
|
-
},
|
|
104644
|
-
checkpoint: child,
|
|
104645
|
-
projection: {
|
|
104646
|
-
result: attempt.result,
|
|
104647
|
-
metadata: nextMetadata,
|
|
104648
|
-
ledgerEntry: advanced.ledgerEntry,
|
|
104649
|
-
integration: attempt.integration
|
|
104650
|
-
}
|
|
104651
|
-
};
|
|
104652
|
-
});
|
|
104653
|
-
if (committed.action === "prepare") {
|
|
104654
|
-
preparationAttempts += 1;
|
|
104655
|
-
preview = await this.scoreDiagnosticItemSubmission(params, input);
|
|
104656
|
-
} else {
|
|
104657
|
-
this.projectDiagnosticItemResponse(committed.projection, preview?.assessment, committed.checkpoint);
|
|
104658
|
-
return committed.response;
|
|
104659
|
-
}
|
|
104660
|
-
}
|
|
104347
|
+
const { context: context2, transcript, submissionId } = prepareAssessmentProgress(await this.deps.verifyAssessmentToken(input.assessmentToken), params, input);
|
|
104348
|
+
const graded = await this.gradeTranscript(context2, transcript);
|
|
104349
|
+
await this.reconcileTranscriptChildren(context2, graded, new Set([submissionId]));
|
|
104350
|
+
return { attemptId: params.attemptId };
|
|
104661
104351
|
}
|
|
104662
|
-
async
|
|
104352
|
+
async submitItem({
|
|
104353
|
+
input,
|
|
104354
|
+
...params
|
|
104355
|
+
}) {
|
|
104356
|
+
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104357
|
+
const context2 = requireAssessmentFeedbackContext(await this.deps.verifyAssessmentToken(input.assessmentToken), params);
|
|
104358
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104359
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104360
|
+
const appended = prepareAssessmentItemTranscript(assessment, { ...input, transcript });
|
|
104361
|
+
const graded = await this.gradeTranscript(context2, appended.transcript);
|
|
104362
|
+
const submission = graded.metadata.itemSubmissions[appended.index];
|
|
104363
|
+
this.reconcileTranscriptChildren(context2, graded, new Set([submission.submissionId])).catch((error88) => this.recordTranscriptFailure("child_projection", params.attemptId, error88));
|
|
104364
|
+
return buildAssessmentTranscriptFeedback(params.attemptId, graded, appended.index);
|
|
104365
|
+
}
|
|
104366
|
+
async gradeTranscript(context2, transcript, terminal = false, db2 = this.deps.db) {
|
|
104367
|
+
const startedAt = Date.now();
|
|
104663
104368
|
try {
|
|
104664
|
-
|
|
104665
|
-
|
|
104666
|
-
|
|
104667
|
-
|
|
104668
|
-
|
|
104669
|
-
|
|
104670
|
-
|
|
104671
|
-
|
|
104672
|
-
|
|
104673
|
-
|
|
104674
|
-
|
|
104675
|
-
|
|
104676
|
-
|
|
104677
|
-
|
|
104678
|
-
|
|
104679
|
-
|
|
104680
|
-
|
|
104681
|
-
|
|
104682
|
-
|
|
104683
|
-
|
|
104684
|
-
const artifactIdentity = assessmentArtifactIdentityFromAttempt(attempt.metadata);
|
|
104685
|
-
const scoringArtifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
104686
|
-
const loaded = await this.loadAttemptDiagnosticRouting(attempt.metadata, assessment, this.deps.db, scoringArtifact);
|
|
104687
|
-
const expected = loaded.state.next;
|
|
104688
|
-
let scoring = null;
|
|
104689
|
-
if (loaded.state.status === "in-progress" && expected?.nodeKey === input.routingNodeKey && expected.itemIdentifier === input.itemIdentifier && attempt.metadata.responseVersion === input.expectedResponseVersion) {
|
|
104690
|
-
const responses = prepareDiagnosticAssessmentResponses(assessment, attempt.metadata.responses, input);
|
|
104691
|
-
scoring = await this.scoreItem({
|
|
104369
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104370
|
+
const prepared = prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal);
|
|
104371
|
+
const artifactIdentity = assessmentArtifactIdentityFromAttempt(context2.metadata);
|
|
104372
|
+
const artifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
104373
|
+
const [itemResults, routing] = await Promise.all([
|
|
104374
|
+
scoreCachedAssessmentItems({
|
|
104375
|
+
assessment,
|
|
104376
|
+
responses: prepared.responses,
|
|
104377
|
+
artifactIdentity,
|
|
104378
|
+
purpose: context2.metadata.purpose,
|
|
104379
|
+
itemIdentifiers: new Set(prepared.transcript.administrations.map((entry) => entry.itemIdentifier)),
|
|
104380
|
+
artifact,
|
|
104381
|
+
cache: this.itemScoreCache,
|
|
104382
|
+
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
104383
|
+
onEvent: addEvent
|
|
104384
|
+
}),
|
|
104385
|
+
this.isPlatformRoutedDiagnosticMetadata(context2.metadata) ? this.loadAttemptDiagnosticRouting(context2.metadata, assessment, db2, artifact) : undefined
|
|
104386
|
+
]);
|
|
104387
|
+
const graded = buildGradedAssessmentTranscript({
|
|
104388
|
+
context: context2,
|
|
104692
104389
|
assessment,
|
|
104693
|
-
|
|
104694
|
-
|
|
104695
|
-
|
|
104696
|
-
|
|
104697
|
-
|
|
104390
|
+
prepared,
|
|
104391
|
+
itemResults,
|
|
104392
|
+
submittedAt: new Date().toISOString(),
|
|
104393
|
+
manifest: routing?.manifest,
|
|
104394
|
+
terminal
|
|
104395
|
+
});
|
|
104396
|
+
addEvent("assessment.transcript_validated", {
|
|
104397
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
104398
|
+
"app.assessment.administration_count": graded.metadata.responseVersion,
|
|
104399
|
+
"app.assessment.duration_ms": Date.now() - startedAt
|
|
104698
104400
|
});
|
|
104699
|
-
|
|
104700
|
-
return {
|
|
104701
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104702
|
-
submissionId: input.submissionId,
|
|
104703
|
-
routingNodeKey: input.routingNodeKey,
|
|
104704
|
-
itemIdentifier: input.itemIdentifier,
|
|
104705
|
-
assessment,
|
|
104706
|
-
scoring
|
|
104707
|
-
};
|
|
104708
|
-
}
|
|
104709
|
-
async scoreItems({
|
|
104710
|
-
preparedArtifact,
|
|
104711
|
-
...input
|
|
104712
|
-
}) {
|
|
104713
|
-
const artifact = preparedArtifact === undefined ? await this.artifactLoader.loadMatchedScoring(input.assessment, input.artifactIdentity) : preparedArtifact;
|
|
104714
|
-
return scoreAssessmentItems({
|
|
104715
|
-
...input,
|
|
104716
|
-
artifact,
|
|
104717
|
-
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
104718
|
-
onEvent: addEvent
|
|
104719
|
-
});
|
|
104720
|
-
}
|
|
104721
|
-
async scoreItem(input) {
|
|
104722
|
-
const [scoring] = await this.scoreItems({
|
|
104723
|
-
assessment: input.assessment,
|
|
104724
|
-
responses: input.responses,
|
|
104725
|
-
artifactIdentity: input.artifactIdentity,
|
|
104726
|
-
purpose: input.purpose,
|
|
104727
|
-
itemIdentifiers: new Set([input.itemIdentifier]),
|
|
104728
|
-
preparedArtifact: input.preparedArtifact
|
|
104729
|
-
});
|
|
104730
|
-
return scoring;
|
|
104731
|
-
}
|
|
104732
|
-
async prepareItemSubmission(params, input) {
|
|
104733
|
-
try {
|
|
104734
|
-
return await this.scoreItemSubmission(params, input);
|
|
104401
|
+
return graded;
|
|
104735
104402
|
} catch (error88) {
|
|
104736
|
-
|
|
104737
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104738
|
-
"exception.type": errorType(error88),
|
|
104739
|
-
"app.error.message": errorMessage(error88)
|
|
104740
|
-
});
|
|
104741
|
-
return null;
|
|
104403
|
+
throw assessmentTranscriptRuntimeError(error88);
|
|
104742
104404
|
}
|
|
104743
104405
|
}
|
|
104744
|
-
|
|
104745
|
-
|
|
104746
|
-
|
|
104747
|
-
|
|
104748
|
-
|
|
104749
|
-
|
|
104750
|
-
const assessment = await this.loadAttemptAssessment(attempt.metadata);
|
|
104751
|
-
const transition = prepareAssessmentItemSubmission({
|
|
104752
|
-
purpose: attempt.metadata.purpose,
|
|
104753
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104754
|
-
responses: attempt.metadata.responses,
|
|
104755
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104756
|
-
assessment
|
|
104757
|
-
}, input);
|
|
104758
|
-
const scoring = transition.action === "commit" ? await this.scoreItem({
|
|
104759
|
-
assessment,
|
|
104760
|
-
responses: transition.responses,
|
|
104761
|
-
itemIdentifier: input.itemIdentifier,
|
|
104762
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
104763
|
-
purpose: attempt.metadata.purpose
|
|
104764
|
-
}) : null;
|
|
104765
|
-
return {
|
|
104766
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104767
|
-
submissionId: input.submissionId,
|
|
104768
|
-
itemIdentifier: input.itemIdentifier,
|
|
104769
|
-
assessment,
|
|
104770
|
-
scoring
|
|
104771
|
-
};
|
|
104772
|
-
}
|
|
104773
|
-
async scoreSubmission(attempt) {
|
|
104774
|
-
const assessment = await this.loadAttemptAssessment(attempt.metadata);
|
|
104775
|
-
validateAssessmentResponses(assessment, attempt.metadata.responses);
|
|
104776
|
-
const itemResults = attempt.metadata.purpose === "review" ? committedReviewItemResults(attempt.metadata, assessment) : await this.scoreItems({
|
|
104777
|
-
assessment,
|
|
104778
|
-
responses: attempt.metadata.responses,
|
|
104779
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
104780
|
-
purpose: attempt.metadata.purpose
|
|
104781
|
-
});
|
|
104782
|
-
return {
|
|
104783
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104784
|
-
assessment,
|
|
104785
|
-
itemResults,
|
|
104786
|
-
score: aggregateAssessmentScore(itemResults.map((result) => result.score.earned), itemResults.reduce((total, result) => total + result.score.possible, 0)),
|
|
104787
|
-
correctQuestions: countCorrectQuestions(itemResults.map((result) => result.isCorrect))
|
|
104788
|
-
};
|
|
104789
|
-
}
|
|
104790
|
-
async peekAttempt({
|
|
104791
|
-
studentId,
|
|
104792
|
-
attemptId
|
|
104793
|
-
}) {
|
|
104794
|
-
const result = await this.requireClient().api.oneroster.assessmentResults.get(attemptId).catch((error88) => {
|
|
104795
|
-
if (isApiError(error88) && error88.statusCode === 404) {
|
|
104796
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
104797
|
-
}
|
|
104798
|
-
throw error88;
|
|
104406
|
+
recordTranscriptFailure(operation, attemptId, error88) {
|
|
104407
|
+
addEvent("assessment.transcript_operation_failed", {
|
|
104408
|
+
"app.assessment.operation": operation,
|
|
104409
|
+
"app.assessment.attempt_id": attemptId,
|
|
104410
|
+
"exception.type": errorType(error88),
|
|
104411
|
+
"app.error.message": errorMessage(error88)
|
|
104799
104412
|
});
|
|
104800
|
-
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
104801
|
-
if (!metadata2 || result.student.sourcedId !== studentId || result.status !== ONEROSTER_STATUS2.active) {
|
|
104802
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
104803
|
-
}
|
|
104804
|
-
return { result, metadata: metadata2 };
|
|
104805
|
-
}
|
|
104806
|
-
async prepareSubmission(params, input) {
|
|
104807
|
-
try {
|
|
104808
|
-
const attempt = await this.peekAttempt(params);
|
|
104809
|
-
const disposition = classifyAssessmentSubmission({
|
|
104810
|
-
inProgress: attempt.result.inProgress ?? "",
|
|
104811
|
-
scoreStatus: attempt.result.scoreStatus,
|
|
104812
|
-
submissionId: attempt.metadata.submissionId
|
|
104813
|
-
}, input.submissionId);
|
|
104814
|
-
if (disposition !== "submit") {
|
|
104815
|
-
return null;
|
|
104816
|
-
}
|
|
104817
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104818
|
-
return null;
|
|
104819
|
-
}
|
|
104820
|
-
return await this.scoreSubmission(attempt);
|
|
104821
|
-
} catch (error88) {
|
|
104822
|
-
addEvent("assessment.submission_preview_failed", {
|
|
104823
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104824
|
-
"exception.type": errorType(error88),
|
|
104825
|
-
"app.error.message": errorMessage(error88)
|
|
104826
|
-
});
|
|
104827
|
-
return null;
|
|
104828
|
-
}
|
|
104829
104413
|
}
|
|
104830
104414
|
async submit({
|
|
104831
104415
|
input,
|
|
@@ -104833,121 +104417,83 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104833
104417
|
...params
|
|
104834
104418
|
}) {
|
|
104835
104419
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104836
|
-
const
|
|
104837
|
-
const
|
|
104838
|
-
|
|
104839
|
-
|
|
104840
|
-
|
|
104841
|
-
|
|
104842
|
-
|
|
104843
|
-
|
|
104844
|
-
|
|
104420
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104421
|
+
const request = { ...input, transcript };
|
|
104422
|
+
const requestHash = await assessmentTranscriptRequestHash(request);
|
|
104423
|
+
const accepted = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104424
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104425
|
+
db: db2,
|
|
104426
|
+
developerAccessValidated: true,
|
|
104427
|
+
includeEnrollment: false
|
|
104428
|
+
});
|
|
104429
|
+
const disposition = resolveAssessmentTranscriptSubmission(attempt.result, attempt.metadata, attempt.integration.status, request, requestHash);
|
|
104430
|
+
if (disposition === "replay") {
|
|
104845
104431
|
return {
|
|
104846
|
-
response: this.submittedResult(
|
|
104847
|
-
emission: this.replayCompletion(
|
|
104432
|
+
response: this.submittedResult(attempt.result, attempt.metadata),
|
|
104433
|
+
emission: this.replayCompletion(attempt, input.submissionId, params, game2)
|
|
104848
104434
|
};
|
|
104849
104435
|
}
|
|
104850
|
-
const
|
|
104851
|
-
|
|
104852
|
-
|
|
104853
|
-
}
|
|
104854
|
-
|
|
104855
|
-
|
|
104856
|
-
|
|
104857
|
-
|
|
104858
|
-
|
|
104859
|
-
|
|
104860
|
-
attempt: { ...attempt, metadata: attempt.metadata },
|
|
104861
|
-
input,
|
|
104862
|
-
params,
|
|
104863
|
-
game: game2,
|
|
104864
|
-
db: db2
|
|
104865
|
-
});
|
|
104866
|
-
}
|
|
104867
|
-
if (assessmentFlowForPurpose(attempt.metadata.purpose) === "item-submit") {
|
|
104868
|
-
const assessment2 = await this.loadAttemptAssessment(attempt.metadata);
|
|
104869
|
-
const flowFailure = assessmentCompletionFlowFailure(attempt.metadata.purpose, attempt.metadata.itemSubmissions, assessment2.items.length);
|
|
104870
|
-
if (flowFailure) {
|
|
104871
|
-
throw AssessmentRuntimeError.from(flowFailure);
|
|
104872
|
-
}
|
|
104873
|
-
}
|
|
104874
|
-
const scored = preview && preview.responseVersion === attempt.metadata.responseVersion ? preview : await this.scoreSubmission(attempt);
|
|
104875
|
-
const { assessment, itemResults, score, correctQuestions } = scored;
|
|
104876
|
-
const client2 = this.requireClient();
|
|
104436
|
+
const graded = await this.gradeTranscript({
|
|
104437
|
+
attemptId: params.attemptId,
|
|
104438
|
+
metadata: attempt.metadata
|
|
104439
|
+
}, transcript, true, db2);
|
|
104440
|
+
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
104441
|
+
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
104442
|
+
metadata: graded.metadata,
|
|
104443
|
+
itemResults: graded.itemResults
|
|
104444
|
+
}) : undefined;
|
|
104445
|
+
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
104877
104446
|
const timestamp6 = new Date().toISOString();
|
|
104878
|
-
const reviewOutcomes = attempt.metadata.purpose === "review" ? this.reviewItemOutcomes({
|
|
104879
|
-
metadata: attempt.metadata,
|
|
104880
|
-
itemResults
|
|
104881
|
-
}) : null;
|
|
104882
104447
|
const finalization = buildAssessmentResultSubmission({
|
|
104883
104448
|
result: attempt.result,
|
|
104884
|
-
metadata:
|
|
104885
|
-
score,
|
|
104449
|
+
metadata: graded.metadata,
|
|
104450
|
+
score: graded.score,
|
|
104886
104451
|
submissionId: input.submissionId,
|
|
104887
104452
|
timestamp: timestamp6,
|
|
104888
|
-
testName: assessment.title,
|
|
104889
|
-
courseName
|
|
104890
|
-
totalQuestions: itemResults.length,
|
|
104891
|
-
correctQuestions,
|
|
104453
|
+
testName: graded.assessment.title,
|
|
104454
|
+
courseName,
|
|
104455
|
+
totalQuestions: graded.itemResults.length,
|
|
104456
|
+
correctQuestions: graded.correctQuestions,
|
|
104892
104457
|
...input.session ? { session: input.session } : {},
|
|
104893
|
-
...
|
|
104458
|
+
...itemOutcomes ? { itemOutcomes } : {},
|
|
104459
|
+
...diagnostic ? { diagnosticTracks: diagnostic.tracks } : {}
|
|
104894
104460
|
});
|
|
104895
|
-
const metadata2 =
|
|
104896
|
-
|
|
104897
|
-
|
|
104898
|
-
|
|
104899
|
-
|
|
104900
|
-
|
|
104901
|
-
|
|
104902
|
-
|
|
104461
|
+
const metadata2 = {
|
|
104462
|
+
...finalization.metadata,
|
|
104463
|
+
completion: { ...finalization.metadata.completion, requestHash }
|
|
104464
|
+
};
|
|
104465
|
+
const resultUpdate = {
|
|
104466
|
+
...finalization.resultUpdate,
|
|
104467
|
+
metadata: mergeAssessmentRuntimeMetadata(attempt.result.metadata, metadata2)
|
|
104468
|
+
};
|
|
104469
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, resultUpdate);
|
|
104470
|
+
const result = { ...attempt.result, ...resultUpdate };
|
|
104471
|
+
const emission = {
|
|
104472
|
+
attempt: { ...attempt, result, metadata: metadata2 },
|
|
104473
|
+
testIdentifier: graded.assessment.identifier,
|
|
104474
|
+
testName: graded.assessment.title,
|
|
104475
|
+
score: graded.score,
|
|
104476
|
+
totalQuestions: graded.itemResults.length,
|
|
104477
|
+
correctQuestions: graded.correctQuestions,
|
|
104478
|
+
submissionId: input.submissionId,
|
|
104479
|
+
gameId: params.gameId,
|
|
104480
|
+
studentId: params.studentId,
|
|
104481
|
+
courseName,
|
|
104482
|
+
game: game2
|
|
104903
104483
|
};
|
|
104904
|
-
addEvent("assessment.awaiting_award_created", {
|
|
104905
|
-
"app.assessment.attempt_id": submittedForEmission.sourcedId,
|
|
104906
|
-
"app.assessment.submission_id": input.submissionId,
|
|
104907
|
-
"app.assessment.purpose": metadata2.purpose,
|
|
104908
|
-
"app.assessment.response_version": metadata2.responseVersion
|
|
104909
|
-
});
|
|
104910
104484
|
return {
|
|
104911
|
-
response:
|
|
104912
|
-
|
|
104913
|
-
|
|
104914
|
-
responseVersion: metadata2.responseVersion,
|
|
104915
|
-
status: "awaiting_award",
|
|
104916
|
-
responses: metadata2.responses,
|
|
104917
|
-
score,
|
|
104918
|
-
...metadata2.purpose === "review" ? {
|
|
104919
|
-
purpose: metadata2.purpose,
|
|
104920
|
-
itemOutcomes: reviewOutcomes
|
|
104921
|
-
} : { purpose: metadata2.purpose }
|
|
104922
|
-
},
|
|
104923
|
-
emission: {
|
|
104924
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
104925
|
-
testIdentifier: assessment.identifier,
|
|
104926
|
-
testName: assessment.title,
|
|
104927
|
-
score,
|
|
104928
|
-
totalQuestions: itemResults.length,
|
|
104929
|
-
correctQuestions,
|
|
104930
|
-
submissionId: input.submissionId,
|
|
104931
|
-
gameId: params.gameId,
|
|
104932
|
-
studentId: params.studentId,
|
|
104933
|
-
courseName: attempt.enrollment.course.title,
|
|
104934
|
-
game: game2
|
|
104935
|
-
}
|
|
104485
|
+
response: this.submittedResult(result, metadata2),
|
|
104486
|
+
emission,
|
|
104487
|
+
repair: { context: this.tokenContext(attempt), graded }
|
|
104936
104488
|
};
|
|
104937
104489
|
});
|
|
104938
|
-
if (
|
|
104939
|
-
|
|
104940
|
-
|
|
104941
|
-
|
|
104942
|
-
|
|
104943
|
-
const { result, metadata: metadata2 } = submission.emission.attempt;
|
|
104944
|
-
await Promise.allSettled([
|
|
104945
|
-
this.emitSessionBestEffort(submission.emission),
|
|
104946
|
-
...diagnosticCompletion ? [this.restoreDiagnosticChildResults(diagnosticCompletion)] : [],
|
|
104947
|
-
...metadata2.purpose === "review" ? [this.restoreAllCompletedReviewChildResponses(result, metadata2)] : []
|
|
104948
|
-
]);
|
|
104490
|
+
if (accepted.repair) {
|
|
104491
|
+
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
104492
|
+
}
|
|
104493
|
+
if (accepted.emission) {
|
|
104494
|
+
this.emitSessionBestEffort(accepted.emission);
|
|
104949
104495
|
}
|
|
104950
|
-
return
|
|
104496
|
+
return accepted.response;
|
|
104951
104497
|
}
|
|
104952
104498
|
async finalize({
|
|
104953
104499
|
input,
|
|
@@ -105091,58 +104637,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105091
104637
|
});
|
|
105092
104638
|
}
|
|
105093
104639
|
}
|
|
105094
|
-
async finalizeDiagnosticSubmission(input) {
|
|
105095
|
-
const { attempt } = input;
|
|
105096
|
-
const routing = await this.loadAttemptDiagnosticManifest(attempt.metadata, input.db);
|
|
105097
|
-
const { tracks, score, correctQuestions } = summarizeDiagnosticSubmission(attempt.metadata, routing.manifest);
|
|
105098
|
-
const timestamp6 = new Date().toISOString();
|
|
105099
|
-
const finalization = buildAssessmentResultSubmission({
|
|
105100
|
-
result: attempt.result,
|
|
105101
|
-
metadata: attempt.metadata,
|
|
105102
|
-
score,
|
|
105103
|
-
submissionId: input.input.submissionId,
|
|
105104
|
-
timestamp: timestamp6,
|
|
105105
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
105106
|
-
courseName: attempt.enrollment.course.title,
|
|
105107
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
105108
|
-
correctQuestions,
|
|
105109
|
-
...input.input.session ? { session: input.input.session } : {},
|
|
105110
|
-
diagnosticTracks: tracks
|
|
105111
|
-
});
|
|
105112
|
-
const metadata2 = finalization.metadata;
|
|
105113
|
-
const submitted = await this.requireClient().api.oneroster.assessmentResults.upsert(attempt.result.sourcedId, finalization.resultUpdate);
|
|
105114
|
-
const submittedForEmission = {
|
|
105115
|
-
...submitted,
|
|
105116
|
-
score: score.normalized * 100,
|
|
105117
|
-
scoreDate: timestamp6,
|
|
105118
|
-
...ASSESSMENT_ATTEMPT_AWAITING_AWARD,
|
|
105119
|
-
metadata: finalization.resultUpdate.metadata
|
|
105120
|
-
};
|
|
105121
|
-
return {
|
|
105122
|
-
response: {
|
|
105123
|
-
purpose: "diagnostic",
|
|
105124
|
-
attemptId: submittedForEmission.sourcedId,
|
|
105125
|
-
submissionId: input.input.submissionId,
|
|
105126
|
-
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
105127
|
-
responseVersion: metadata2.responseVersion,
|
|
105128
|
-
status: "awaiting_award",
|
|
105129
|
-
tracks
|
|
105130
|
-
},
|
|
105131
|
-
emission: {
|
|
105132
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
105133
|
-
testIdentifier: attempt.metadata.selectedTest.identifier,
|
|
105134
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
105135
|
-
score,
|
|
105136
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
105137
|
-
correctQuestions,
|
|
105138
|
-
submissionId: input.input.submissionId,
|
|
105139
|
-
gameId: input.params.gameId,
|
|
105140
|
-
studentId: input.params.studentId,
|
|
105141
|
-
courseName: attempt.enrollment.course.title,
|
|
105142
|
-
game: input.game
|
|
105143
|
-
}
|
|
105144
|
-
};
|
|
105145
|
-
}
|
|
105146
104640
|
async exportFixture({
|
|
105147
104641
|
gameId,
|
|
105148
104642
|
user
|
|
@@ -105263,28 +104757,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105263
104757
|
liveTestIntegrationIds: integrationIdsWithLiveTests
|
|
105264
104758
|
};
|
|
105265
104759
|
}
|
|
105266
|
-
async selectIntegration(candidates,
|
|
105267
|
-
const {
|
|
105268
|
-
rows: candidateRows,
|
|
105269
|
-
enrollmentByCourse,
|
|
105270
|
-
liveTestIntegrationIds: integrationIdsWithLiveTests
|
|
105271
|
-
} = candidates;
|
|
105272
|
-
const needsOpenAnnotation = candidateRows.length > 1 || candidateRows.some((row) => !integrationIdsWithLiveTests.has(row.id));
|
|
105273
|
-
const openAttemptScope = {
|
|
105274
|
-
studentId,
|
|
105275
|
-
activityId: input.activityId,
|
|
105276
|
-
purpose: input.purpose,
|
|
105277
|
-
enrollmentIdByCourse: new Map(candidateRows.map((row) => [row.courseId, enrollmentByCourse.get(row.courseId).id])),
|
|
105278
|
-
requestFingerprint: this.startRequestFingerprint(input)
|
|
105279
|
-
};
|
|
105280
|
-
let openAttemptCourseIds = new Set;
|
|
105281
|
-
if (needsOpenAnnotation) {
|
|
105282
|
-
openAttemptCourseIds = results ? this.openAttemptCourseIdsFromResults(results, openAttemptScope) : await this.openAttemptCourseIds(openAttemptScope);
|
|
105283
|
-
}
|
|
104760
|
+
async selectIntegration(candidates, input) {
|
|
104761
|
+
const { rows: candidateRows, enrollmentByCourse, liveTestIntegrationIds } = candidates;
|
|
105284
104762
|
const annotated = candidateRows.map((row) => ({
|
|
105285
104763
|
...row,
|
|
105286
|
-
hasLiveTests:
|
|
105287
|
-
hasOpenAttempt: openAttemptCourseIds.has(row.courseId)
|
|
104764
|
+
hasLiveTests: liveTestIntegrationIds.has(row.id)
|
|
105288
104765
|
}));
|
|
105289
104766
|
const selection = selectAssessmentCourseCandidate(annotated, Boolean(input.subject));
|
|
105290
104767
|
if (selection.outcome === "none") {
|
|
@@ -105313,9 +104790,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105313
104790
|
standard: assessmentStandardForRow(row)
|
|
105314
104791
|
})).filter((test) => !matchesMasteryStandard || matchesMasteryStandard(test.standard));
|
|
105315
104792
|
}
|
|
105316
|
-
async openAttemptCourseIds(scope) {
|
|
105317
|
-
return this.openAttemptCourseIdsFromResults(await this.studentAssessmentResults(scope.studentId), scope);
|
|
105318
|
-
}
|
|
105319
104793
|
async studentAssessmentResults(studentId) {
|
|
105320
104794
|
const stream = this.requireClient().api.oneroster.assessmentResults.stream({
|
|
105321
104795
|
where: {
|
|
@@ -105329,29 +104803,37 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105329
104803
|
}
|
|
105330
104804
|
return results;
|
|
105331
104805
|
}
|
|
105332
|
-
openAttemptCourseIdsFromResults(results, scope) {
|
|
105333
|
-
const courseIds = new Set;
|
|
105334
|
-
for (const result of results) {
|
|
105335
|
-
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
105336
|
-
if (metadata2 && metadata2.activityId === scope.activityId && metadata2.purpose === scope.purpose && this.attemptMatchesRequestFingerprint(metadata2, scope.requestFingerprint) && result.student.sourcedId === scope.studentId && metadata2.enrollmentId === scope.enrollmentIdByCourse.get(metadata2.courseId) && (this.isOpen(result) || this.isAwaitingAward(result))) {
|
|
105337
|
-
courseIds.add(metadata2.courseId);
|
|
105338
|
-
}
|
|
105339
|
-
}
|
|
105340
|
-
return courseIds;
|
|
105341
|
-
}
|
|
105342
104806
|
async listAttempts(scope) {
|
|
105343
104807
|
return this.attemptListing(await this.studentAssessmentResults(scope.studentId), scope);
|
|
105344
104808
|
}
|
|
105345
|
-
attemptListing(results, scope) {
|
|
104809
|
+
async attemptListing(results, scope) {
|
|
105346
104810
|
const attempts = new Map;
|
|
105347
104811
|
const reviewChildren = new Map;
|
|
105348
104812
|
for (const result of results) {
|
|
105349
104813
|
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
105350
|
-
|
|
105351
|
-
|
|
105352
|
-
|
|
104814
|
+
if (scope.purpose === "review") {
|
|
104815
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
104816
|
+
if (verified.ok && verified.command.payload.context.kind === "review") {
|
|
104817
|
+
const { command } = verified;
|
|
104818
|
+
const { attempt, administration } = command.payload;
|
|
104819
|
+
const parent = await reviewQuestionLineItemId(await deterministicUUID([
|
|
104820
|
+
"playcademy:review-bank-line-item:v1",
|
|
104821
|
+
scope.integrationId,
|
|
104822
|
+
scope.activityId,
|
|
104823
|
+
attempt.selectedTest.identifier,
|
|
104824
|
+
verified.command.payload.context.bankRevision
|
|
104825
|
+
].join("\x00")), administration.itemIdentifier);
|
|
104826
|
+
const projection = await classifyAssessmentChildResult({
|
|
104827
|
+
result,
|
|
104828
|
+
command,
|
|
104829
|
+
childLineItemId: parent
|
|
104830
|
+
});
|
|
104831
|
+
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") {
|
|
104832
|
+
reviewChildren.set(result.sourcedId, command);
|
|
104833
|
+
}
|
|
104834
|
+
}
|
|
105353
104835
|
}
|
|
105354
|
-
if (metadata2 &&
|
|
104836
|
+
if (metadata2 && this.attemptInScope(result, metadata2, scope)) {
|
|
105355
104837
|
const selectedAssessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
105356
104838
|
if (selectedAssessmentKey) {
|
|
105357
104839
|
attempts.set(result.sourcedId, {
|
|
@@ -105360,12 +104842,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105360
104842
|
selection: {
|
|
105361
104843
|
attemptId: result.sourcedId,
|
|
105362
104844
|
selectedAssessmentKey,
|
|
105363
|
-
selectedTestIdentifier: metadata2.selectedTest.identifier,
|
|
105364
104845
|
inProgress: result.inProgress ?? "",
|
|
105365
104846
|
scoreStatus: result.scoreStatus,
|
|
105366
|
-
scoreDate: result.scoreDate
|
|
105367
|
-
updatedAt: result.dateLastModified ?? metadata2.updatedAt,
|
|
105368
|
-
resumable: metadata2.enrollmentId === scope.enrollmentId
|
|
104847
|
+
scoreDate: result.scoreDate
|
|
105369
104848
|
}
|
|
105370
104849
|
});
|
|
105371
104850
|
}
|
|
@@ -105373,147 +104852,61 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105373
104852
|
}
|
|
105374
104853
|
return { attempts, reviewChildren };
|
|
105375
104854
|
}
|
|
105376
|
-
|
|
105377
|
-
|
|
105378
|
-
return attempts;
|
|
105379
|
-
}
|
|
105380
|
-
return new Map([...attempts].filter(([, attempt]) => this.attemptMatchesRequestFingerprint(attempt.metadata, requestFingerprint)));
|
|
105381
|
-
}
|
|
105382
|
-
startRequestFingerprint(input) {
|
|
105383
|
-
if (input.purpose === "review") {
|
|
105384
|
-
return reviewRequestFingerprint(input, DEFAULT_REVIEW_SELECTION_POLICY);
|
|
105385
|
-
}
|
|
105386
|
-
if (input.purpose === "mastery") {
|
|
105387
|
-
return masteryRequestFingerprint(input);
|
|
105388
|
-
}
|
|
105389
|
-
return input.purpose === "diagnostic" ? input.diagnosticKey : undefined;
|
|
105390
|
-
}
|
|
105391
|
-
attemptMatchesRequestFingerprint(metadata2, requestFingerprint) {
|
|
105392
|
-
if (metadata2.purpose === "review") {
|
|
105393
|
-
return metadata2.review.requestFingerprint === requestFingerprint;
|
|
105394
|
-
}
|
|
105395
|
-
if (metadata2.purpose === "mastery") {
|
|
105396
|
-
return metadata2.mastery.requestFingerprint === requestFingerprint;
|
|
105397
|
-
}
|
|
105398
|
-
if (metadata2.purpose === "diagnostic" && requestFingerprint !== undefined) {
|
|
105399
|
-
return this.isPlatformRoutedDiagnosticMetadata(metadata2) && metadata2.diagnostic.diagnosticKey === requestFingerprint;
|
|
105400
|
-
}
|
|
105401
|
-
return true;
|
|
105402
|
-
}
|
|
105403
|
-
reviewExposures(results, scope) {
|
|
105404
|
-
const exposures = [];
|
|
105405
|
-
for (const result of results) {
|
|
105406
|
-
const metadata2 = playcademyAssessmentItemResultMetadata(result.metadata);
|
|
105407
|
-
if (metadata2?.bankRevision === scope.bankRevision) {
|
|
105408
|
-
exposures.push({
|
|
105409
|
-
itemIdentifier: metadata2.itemIdentifier,
|
|
105410
|
-
deliveredAt: metadata2.deliveredAt
|
|
105411
|
-
});
|
|
105412
|
-
}
|
|
105413
|
-
}
|
|
105414
|
-
return exposures;
|
|
105415
|
-
}
|
|
105416
|
-
requireDecision(decision, purpose) {
|
|
104855
|
+
selectFreshStart(tests, listing, purpose) {
|
|
104856
|
+
const decision = selectRuntimeAssessment(tests, [...listing.attempts.values()].map((entry) => entry.selection));
|
|
105417
104857
|
if (!decision) {
|
|
105418
104858
|
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(purpose));
|
|
105419
104859
|
}
|
|
105420
104860
|
return decision;
|
|
105421
104861
|
}
|
|
105422
|
-
|
|
105423
|
-
|
|
105424
|
-
|
|
105425
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105426
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105427
|
-
});
|
|
105428
|
-
}
|
|
105429
|
-
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105430
|
-
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
104862
|
+
pendingAwardAttempt(listing, enrollmentId) {
|
|
104863
|
+
const pending = [...listing.attempts.values()].filter((attempt) => attempt.metadata.enrollmentId === enrollmentId && this.isAwaitingAward(attempt.result));
|
|
104864
|
+
return pending.toSorted((left, right) => compareNewestCompletedAssessmentAttempt(left.selection, right.selection))[0] ?? null;
|
|
105431
104865
|
}
|
|
105432
|
-
async
|
|
105433
|
-
|
|
105434
|
-
|
|
105435
|
-
|
|
105436
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105437
|
-
});
|
|
105438
|
-
}
|
|
105439
|
-
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105440
|
-
if (this.isSettled(resumed.result)) {
|
|
105441
|
-
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105442
|
-
}
|
|
105443
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(resumed.metadata)) {
|
|
105444
|
-
throw new Error("A keyed diagnostic request selected a legacy fixed attempt");
|
|
104866
|
+
async pendingAwardSnapshot(listing, context2) {
|
|
104867
|
+
const pending = this.pendingAwardAttempt(listing, context2.enrollment.id);
|
|
104868
|
+
if (!pending) {
|
|
104869
|
+
return null;
|
|
105445
104870
|
}
|
|
105446
|
-
|
|
105447
|
-
|
|
105448
|
-
await this.restoreDiagnosticChildResults({
|
|
105449
|
-
result: resumed.result,
|
|
105450
|
-
metadata: resumed.metadata,
|
|
105451
|
-
integration,
|
|
105452
|
-
assessment
|
|
104871
|
+
addEvent("assessment.pending_award_returned", {
|
|
104872
|
+
"app.assessment.attempt_id": pending.result.sourcedId
|
|
105453
104873
|
});
|
|
105454
|
-
return this.
|
|
105455
|
-
}
|
|
105456
|
-
async resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, resume) {
|
|
105457
|
-
const select3 = () => selectRuntimeAssessment(tests, [...this.attemptsMatchingRequest(attempts, requestFingerprint).values()].map((entry) => entry.selection));
|
|
105458
|
-
return resolveAvailableAssessmentAttempt(select3(), select3, (decision) => this.resumeSnapshotOrStageSupersession(decision, attempts, pendingSupersessions, () => resume(decision)));
|
|
104874
|
+
return this.snapshotForResult(pending.result, pending.metadata, context2.integration);
|
|
105459
104875
|
}
|
|
105460
|
-
|
|
105461
|
-
|
|
105462
|
-
|
|
105463
|
-
|
|
105464
|
-
|
|
105465
|
-
throw error88;
|
|
104876
|
+
replacedAttemptIds(attempts, enrollmentId) {
|
|
104877
|
+
const replaced = new Set;
|
|
104878
|
+
for (const attempt of attempts.values()) {
|
|
104879
|
+
if (attempt.metadata.enrollmentId === enrollmentId && this.isOpen(attempt.result)) {
|
|
104880
|
+
replaced.add(attempt.result.sourcedId);
|
|
105466
104881
|
}
|
|
105467
|
-
if (shouldConfirmAssessmentVersionUnavailable(error88.details)) {
|
|
105468
|
-
await sleep(TimebackAssessmentRuntimeService.ASSESSMENT_VERSION_CONFIRMATION_DELAY_MS);
|
|
105469
|
-
try {
|
|
105470
|
-
return await resume();
|
|
105471
|
-
} catch (confirmationError) {
|
|
105472
|
-
if (!(confirmationError instanceof AssessmentRuntimeError) || confirmationError.runtimeCode !== "SELECTED_TEST_VERSION_UNAVAILABLE") {
|
|
105473
|
-
throw confirmationError;
|
|
105474
|
-
}
|
|
105475
|
-
}
|
|
105476
|
-
}
|
|
105477
|
-
const staged = attempts.get(decision.attempt.attemptId);
|
|
105478
|
-
pendingSupersessions.add(staged.result.sourcedId);
|
|
105479
|
-
stageAssessmentAttemptSupersession(staged);
|
|
105480
|
-
addEvent("assessment.attempt_supersession_staged", {
|
|
105481
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105482
|
-
"app.assessment.reason": "selected_test_version_unavailable"
|
|
105483
|
-
});
|
|
105484
|
-
return null;
|
|
105485
104882
|
}
|
|
104883
|
+
return replaced;
|
|
104884
|
+
}
|
|
104885
|
+
supersedeReplacedAttempts(db2, attempts, enrollmentId) {
|
|
104886
|
+
return this.supersedeAttemptsBestEffort(db2, this.replacedAttemptIds(attempts, enrollmentId));
|
|
105486
104887
|
}
|
|
105487
|
-
async
|
|
104888
|
+
async supersedeAttempts(db2, attemptIds) {
|
|
105488
104889
|
for (const attemptId of [...attemptIds].toSorted()) {
|
|
105489
104890
|
await this.deps.assessmentRuntimeLock(db2, [assessmentRuntimeAttemptLockKey(attemptId)], async () => {
|
|
105490
|
-
|
|
105491
|
-
|
|
105492
|
-
result = await this.requireClient().api.oneroster.assessmentResults.get(attemptId);
|
|
105493
|
-
} catch (error88) {
|
|
105494
|
-
if (isApiError(error88) && error88.statusCode === 404) {
|
|
105495
|
-
return;
|
|
105496
|
-
}
|
|
105497
|
-
throw error88;
|
|
105498
|
-
}
|
|
105499
|
-
const update2 = buildAssessmentAttemptSupersessionUpdate(result);
|
|
104891
|
+
const result = await this.findAttemptResult(attemptId);
|
|
104892
|
+
const update2 = result && buildAssessmentAttemptSupersessionUpdate(result);
|
|
105500
104893
|
if (!update2) {
|
|
105501
104894
|
return;
|
|
105502
104895
|
}
|
|
105503
104896
|
await this.requireClient().api.oneroster.assessmentResults.upsert(attemptId, update2);
|
|
105504
104897
|
addEvent("assessment.attempt_superseded", {
|
|
105505
104898
|
"app.assessment.attempt_id": attemptId,
|
|
105506
|
-
"app.assessment.reason": "
|
|
104899
|
+
"app.assessment.reason": "replaced_by_fresh_start"
|
|
105507
104900
|
});
|
|
105508
104901
|
});
|
|
105509
104902
|
}
|
|
105510
104903
|
}
|
|
105511
|
-
async
|
|
104904
|
+
async supersedeAttemptsBestEffort(db2, attemptIds) {
|
|
105512
104905
|
if (attemptIds.size === 0) {
|
|
105513
104906
|
return;
|
|
105514
104907
|
}
|
|
105515
104908
|
try {
|
|
105516
|
-
await this.
|
|
104909
|
+
await this.supersedeAttempts(db2, attemptIds);
|
|
105517
104910
|
} catch (error88) {
|
|
105518
104911
|
addEvent("assessment.attempt_supersession_persist_failed", {
|
|
105519
104912
|
"app.assessment.attempt_ids": [...attemptIds].toSorted().join(","),
|
|
@@ -105522,43 +104915,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105522
104915
|
});
|
|
105523
104916
|
}
|
|
105524
104917
|
}
|
|
105525
|
-
async resumeReviewSnapshot(decision, attempts, existingChildResults, context2) {
|
|
105526
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
105527
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
105528
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105529
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105530
|
-
});
|
|
105531
|
-
}
|
|
105532
|
-
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105533
|
-
if (resumed.metadata.purpose !== "review") {
|
|
105534
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
105535
|
-
}
|
|
105536
|
-
const reviewMetadata = resumed.metadata;
|
|
105537
|
-
let assessment;
|
|
105538
|
-
const current = await reconcileReviewChildResults({
|
|
105539
|
-
initial: resumed,
|
|
105540
|
-
writeOpen: async () => {
|
|
105541
|
-
const source = await this.loadPinnedReviewBankSource(reviewMetadata);
|
|
105542
|
-
assessment = projectReviewAssessment(source.assessment, source.bank, reviewMetadata.review.selections);
|
|
105543
|
-
await this.ensureReviewChildResults({
|
|
105544
|
-
result: resumed.result,
|
|
105545
|
-
metadata: reviewMetadata,
|
|
105546
|
-
integration: context2.integration,
|
|
105547
|
-
source,
|
|
105548
|
-
assessment,
|
|
105549
|
-
existingChildResults,
|
|
105550
|
-
lookupMissingResults: true
|
|
105551
|
-
});
|
|
105552
|
-
},
|
|
105553
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, resumed.result.sourcedId),
|
|
105554
|
-
readLatest: () => this.peekAttempt({
|
|
105555
|
-
studentId: resumed.result.student.sourcedId,
|
|
105556
|
-
attemptId: resumed.result.sourcedId
|
|
105557
|
-
}),
|
|
105558
|
-
restoreTerminal: (latest) => this.restoreAllCompletedReviewChildResponses(latest.result, latest.metadata)
|
|
105559
|
-
});
|
|
105560
|
-
return assessmentChildProjectionIsTerminal(current.result) ? this.snapshotForResult(current.result, current.metadata, context2.integration) : this.snapshot(current.result, current.metadata, assessment, context2.integration);
|
|
105561
|
-
}
|
|
105562
104918
|
recordReviewPreparationPhase(phase, startedAt, counts = {}) {
|
|
105563
104919
|
addEvent("assessment.review_preparation_phase", {
|
|
105564
104920
|
"app.assessment.review_phase": phase,
|
|
@@ -105909,12 +105265,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105909
105265
|
}
|
|
105910
105266
|
});
|
|
105911
105267
|
} catch (error88) {
|
|
105912
|
-
|
|
105913
|
-
throw error88;
|
|
105914
|
-
}
|
|
105915
|
-
addEvent("assessment.line_item_create_conflict", {
|
|
105916
|
-
"app.assessment.line_item_id": lineItemId
|
|
105917
|
-
});
|
|
105268
|
+
await this.confirmLineItemAfterRefusedCreate(lineItemId, error88, "assessment.line_item_create_conflict", () => client2.api.oneroster.assessmentLineItems.get(lineItemId));
|
|
105918
105269
|
}
|
|
105919
105270
|
return lineItemId;
|
|
105920
105271
|
}
|
|
@@ -105956,144 +105307,99 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105956
105307
|
}
|
|
105957
105308
|
});
|
|
105958
105309
|
} catch (error88) {
|
|
105959
|
-
|
|
105960
|
-
throw error88;
|
|
105961
|
-
}
|
|
105962
|
-
addEvent("assessment.review_bank_line_item_create_conflict", {
|
|
105963
|
-
"app.assessment.line_item_id": lineItemId
|
|
105964
|
-
});
|
|
105310
|
+
await this.confirmLineItemAfterRefusedCreate(lineItemId, error88, "assessment.review_bank_line_item_create_conflict", () => client2.api.oneroster.assessmentLineItems.get(lineItemId));
|
|
105965
105311
|
}
|
|
105966
105312
|
return lineItemId;
|
|
105967
105313
|
}
|
|
105968
|
-
|
|
105969
|
-
|
|
105970
|
-
|
|
105971
|
-
|
|
105972
|
-
|
|
105973
|
-
|
|
105974
|
-
|
|
105975
|
-
|
|
105314
|
+
async confirmLineItemAfterRefusedCreate(lineItemId, createError, event, read) {
|
|
105315
|
+
let stored;
|
|
105316
|
+
try {
|
|
105317
|
+
stored = await read();
|
|
105318
|
+
} catch (readError) {
|
|
105319
|
+
if (isApiError(readError) && readError.statusCode === 404) {
|
|
105320
|
+
throw createError;
|
|
105321
|
+
}
|
|
105322
|
+
throw readError;
|
|
105976
105323
|
}
|
|
105324
|
+
addEvent(event, {
|
|
105325
|
+
"app.assessment.line_item_id": lineItemId,
|
|
105326
|
+
...isApiError(createError) ? { "app.timeback.status": createError.statusCode } : {}
|
|
105327
|
+
});
|
|
105328
|
+
return stored;
|
|
105329
|
+
}
|
|
105330
|
+
tokenContext(attempt) {
|
|
105977
105331
|
return {
|
|
105978
|
-
|
|
105332
|
+
version: 1,
|
|
105979
105333
|
gameId: attempt.integration.gameId,
|
|
105980
105334
|
studentId: attempt.result.student.sourcedId,
|
|
105981
|
-
|
|
105982
|
-
|
|
105983
|
-
|
|
105984
|
-
enrollmentId: metadata2.enrollmentId,
|
|
105985
|
-
selectedTest: {
|
|
105986
|
-
assessmentKey,
|
|
105987
|
-
identifier: metadata2.selectedTest.identifier,
|
|
105988
|
-
contentRevision: metadata2.selectedTest.contentRevision
|
|
105989
|
-
}
|
|
105990
|
-
};
|
|
105991
|
-
}
|
|
105992
|
-
reviewChildResultContext(metadata2, submission) {
|
|
105993
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === submission.itemIdentifier);
|
|
105994
|
-
if (!selection) {
|
|
105995
|
-
throw new Error(`Committed review item ${submission.itemIdentifier} is not in its pinned selection`);
|
|
105996
|
-
}
|
|
105997
|
-
return {
|
|
105998
|
-
kind: "review",
|
|
105999
|
-
bankRevision: metadata2.review.bankRevision,
|
|
106000
|
-
standard: selection.standard
|
|
105335
|
+
attemptId: attempt.result.sourcedId,
|
|
105336
|
+
parentLineItemId: attempt.result.assessmentLineItem.sourcedId,
|
|
105337
|
+
metadata: assessmentTokenMetadata(attempt.metadata)
|
|
106001
105338
|
};
|
|
106002
105339
|
}
|
|
106003
|
-
|
|
106004
|
-
|
|
106005
|
-
|
|
106006
|
-
|
|
106007
|
-
|
|
106008
|
-
|
|
105340
|
+
async ensureTranscriptLineItems(context2, assessment) {
|
|
105341
|
+
await runWithConcurrency(assessment.items, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (item) => {
|
|
105342
|
+
const id = await assessmentTranscriptChildLineItemId(context2, item.identifier);
|
|
105343
|
+
if (this.childLineItemCache.get(id)) {
|
|
105344
|
+
return;
|
|
105345
|
+
}
|
|
105346
|
+
const metadata2 = context2.metadata;
|
|
105347
|
+
const common2 = {
|
|
105348
|
+
parentLineItemId: context2.parentLineItemId,
|
|
105349
|
+
integration: { courseId: metadata2.courseId },
|
|
105350
|
+
item
|
|
105351
|
+
};
|
|
105352
|
+
if (metadata2.purpose === "review") {
|
|
105353
|
+
const standard = metadata2.review.selections.find((selection) => selection.itemIdentifier === item.identifier)?.standard;
|
|
105354
|
+
if (!standard) {
|
|
105355
|
+
throw new Error("Review candidate is not in the pinned selection");
|
|
105356
|
+
}
|
|
105357
|
+
await this.ensureReviewQuestionLineItem({
|
|
105358
|
+
...common2,
|
|
105359
|
+
bankRevision: metadata2.review.bankRevision,
|
|
105360
|
+
standards: [standard]
|
|
105361
|
+
});
|
|
105362
|
+
} else if (this.isPlatformRoutedDiagnosticMetadata(metadata2)) {
|
|
105363
|
+
await this.ensureDiagnosticQuestionLineItem({ ...common2, metadata: metadata2 });
|
|
105364
|
+
} else {
|
|
105365
|
+
const expected = {
|
|
105366
|
+
status: "active",
|
|
105367
|
+
parentAssessmentLineItem: { sourcedId: context2.parentLineItemId },
|
|
105368
|
+
course: { sourcedId: metadata2.courseId },
|
|
105369
|
+
resultValueMin: 0,
|
|
105370
|
+
resultValueMax: item.maxScore,
|
|
105371
|
+
metadata: {
|
|
105372
|
+
playcademyAssessmentQuestionDefinition: {
|
|
105373
|
+
version: 1,
|
|
105374
|
+
itemIdentifier: item.identifier,
|
|
105375
|
+
contentRevision: metadata2.selectedTest.contentRevision
|
|
105376
|
+
}
|
|
105377
|
+
}
|
|
105378
|
+
};
|
|
105379
|
+
const store = this.requireClient().api.oneroster.assessmentLineItems;
|
|
105380
|
+
try {
|
|
105381
|
+
await store.create({ sourcedId: id, title: item.title, ...expected });
|
|
105382
|
+
} catch (error88) {
|
|
105383
|
+
this.assertAssessmentChildLineItem(id, await this.confirmLineItemAfterRefusedCreate(id, error88, "assessment.question_line_item_create_conflict", () => store.get(id)), expected);
|
|
105384
|
+
}
|
|
105385
|
+
}
|
|
105386
|
+
this.childLineItemCache.set(id, true);
|
|
106009
105387
|
});
|
|
106010
105388
|
}
|
|
106011
|
-
|
|
106012
|
-
return
|
|
106013
|
-
|
|
106014
|
-
|
|
106015
|
-
|
|
106016
|
-
|
|
106017
|
-
|
|
106018
|
-
|
|
106019
|
-
|
|
106020
|
-
|
|
106021
|
-
|
|
105389
|
+
async reconcileTranscriptChildren(context2, graded, only) {
|
|
105390
|
+
return reconcileAssessmentTranscriptChildren({
|
|
105391
|
+
context: context2,
|
|
105392
|
+
graded,
|
|
105393
|
+
only,
|
|
105394
|
+
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
105395
|
+
ensureLineItems: (assessment) => this.ensureTranscriptLineItems(context2, assessment),
|
|
105396
|
+
onPersisted: (submissionId) => addEvent("assessment.child_result_persisted", {
|
|
105397
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
105398
|
+
"app.assessment.submission_id": submissionId,
|
|
105399
|
+
"app.assessment.child_result_outcome": "upserted"
|
|
106022
105400
|
})
|
|
106023
105401
|
});
|
|
106024
105402
|
}
|
|
106025
|
-
async prepareAssessmentChildResult(input) {
|
|
106026
|
-
try {
|
|
106027
|
-
const attemptContext = this.assessmentChildAttempt({
|
|
106028
|
-
...input.attempt,
|
|
106029
|
-
metadata: input.metadata
|
|
106030
|
-
});
|
|
106031
|
-
if (!attemptContext) {
|
|
106032
|
-
return null;
|
|
106033
|
-
}
|
|
106034
|
-
const parentLineItemId = input.attempt.result.assessmentLineItem.sourcedId;
|
|
106035
|
-
const { itemIdentifier } = input.submission;
|
|
106036
|
-
const context2 = input.context();
|
|
106037
|
-
const [command, childLineItemId] = await Promise.all([
|
|
106038
|
-
buildSubmissionChildResultCommand({
|
|
106039
|
-
attempt: attemptContext,
|
|
106040
|
-
submission: input.submission,
|
|
106041
|
-
responses: input.metadata.responses[itemIdentifier],
|
|
106042
|
-
grading: input.submission.grading ?? QTI_CHILD_GRADING,
|
|
106043
|
-
context: context2
|
|
106044
|
-
}),
|
|
106045
|
-
context2.kind === "review" ? reviewQuestionLineItemId(parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier)
|
|
106046
|
-
]);
|
|
106047
|
-
return { command, childLineItemId };
|
|
106048
|
-
} catch (error88) {
|
|
106049
|
-
addEvent("assessment.child_result_prepare_failed", {
|
|
106050
|
-
"app.assessment.attempt_id": input.attempt.result.sourcedId,
|
|
106051
|
-
"app.assessment.submission_id": input.submission.submissionId,
|
|
106052
|
-
"app.assessment.qti_item_identifier": input.submission.itemIdentifier,
|
|
106053
|
-
"exception.type": errorType(error88),
|
|
106054
|
-
"app.error.message": errorMessage(error88)
|
|
106055
|
-
});
|
|
106056
|
-
return null;
|
|
106057
|
-
}
|
|
106058
|
-
}
|
|
106059
|
-
async projectPreparedAssessmentChildResult(prepared) {
|
|
106060
|
-
if (!prepared) {
|
|
106061
|
-
return;
|
|
106062
|
-
}
|
|
106063
|
-
const startedAt = Date.now();
|
|
106064
|
-
const identity = {
|
|
106065
|
-
"app.assessment.attempt_id": prepared.command.payload.attempt.attemptId,
|
|
106066
|
-
"app.assessment.submission_id": prepared.command.payload.administration.submissionId
|
|
106067
|
-
};
|
|
106068
|
-
try {
|
|
106069
|
-
const disposition = await persistAssessmentChildResult({
|
|
106070
|
-
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
106071
|
-
command: prepared.command,
|
|
106072
|
-
childLineItemId: prepared.childLineItemId
|
|
106073
|
-
});
|
|
106074
|
-
if (disposition.outcome === "conflict") {
|
|
106075
|
-
addEvent("assessment.child_result_conflict", {
|
|
106076
|
-
...identity,
|
|
106077
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
106078
|
-
"app.assessment.child_result_conflict_reason": disposition.reason
|
|
106079
|
-
});
|
|
106080
|
-
return;
|
|
106081
|
-
}
|
|
106082
|
-
addEvent("assessment.child_result_persisted", {
|
|
106083
|
-
...identity,
|
|
106084
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
106085
|
-
"app.assessment.child_result_outcome": disposition.outcome,
|
|
106086
|
-
"app.assessment.duration_ms": Date.now() - startedAt
|
|
106087
|
-
});
|
|
106088
|
-
} catch (error88) {
|
|
106089
|
-
addEvent("assessment.child_result_persist_failed", {
|
|
106090
|
-
...identity,
|
|
106091
|
-
"app.assessment.duration_ms": Date.now() - startedAt,
|
|
106092
|
-
"exception.type": errorType(error88),
|
|
106093
|
-
"app.error.message": errorMessage(error88)
|
|
106094
|
-
});
|
|
106095
|
-
}
|
|
106096
|
-
}
|
|
106097
105403
|
assertAssessmentChildLineItem(lineItemId, stored, expected) {
|
|
106098
105404
|
if (assessmentChildLineItemMatches(stored, expected)) {
|
|
106099
105405
|
return;
|
|
@@ -106140,102 +105446,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106140
105446
|
...expected
|
|
106141
105447
|
});
|
|
106142
105448
|
} catch (error88) {
|
|
106143
|
-
|
|
106144
|
-
throw error88;
|
|
106145
|
-
}
|
|
106146
|
-
const stored = await client2.api.oneroster.assessmentLineItems.get(lineItemId);
|
|
105449
|
+
const stored = await this.confirmLineItemAfterRefusedCreate(lineItemId, error88, "assessment.diagnostic_question_line_item_create_conflict", () => client2.api.oneroster.assessmentLineItems.get(lineItemId));
|
|
106147
105450
|
this.assertAssessmentChildLineItem(lineItemId, stored, expected);
|
|
106148
|
-
addEvent("assessment.diagnostic_question_line_item_create_conflict", {
|
|
106149
|
-
"app.assessment.line_item_id": lineItemId
|
|
106150
|
-
});
|
|
106151
105451
|
}
|
|
106152
105452
|
return lineItemId;
|
|
106153
105453
|
}
|
|
106154
|
-
async projectDiagnosticItemResponse(projection, assessment, checkpoint = null) {
|
|
106155
|
-
try {
|
|
106156
|
-
const loadedAssessment = assessment ?? await this.loadAttemptAssessment(projection.metadata);
|
|
106157
|
-
const item = loadedAssessment.items.find((candidate) => candidate.identifier === projection.ledgerEntry.itemIdentifier);
|
|
106158
|
-
if (!item) {
|
|
106159
|
-
throw new Error(`Diagnostic assessment is missing item ${projection.ledgerEntry.itemIdentifier}`);
|
|
106160
|
-
}
|
|
106161
|
-
const childLineItemId = await this.ensureDiagnosticQuestionLineItem({
|
|
106162
|
-
parentLineItemId: projection.result.assessmentLineItem.sourcedId,
|
|
106163
|
-
integration: projection.integration,
|
|
106164
|
-
metadata: projection.metadata,
|
|
106165
|
-
item
|
|
106166
|
-
});
|
|
106167
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
106168
|
-
throw new Error("Prepared diagnostic checkpoint resolved a different line item");
|
|
106169
|
-
}
|
|
106170
|
-
const childResultId = await diagnosticQuestionResultId(projection.result.sourcedId, childLineItemId);
|
|
106171
|
-
const finalized = buildFinalizedDiagnosticChildResult({
|
|
106172
|
-
childLineItemId,
|
|
106173
|
-
student: projection.result.student,
|
|
106174
|
-
parentAttemptId: projection.result.sourcedId,
|
|
106175
|
-
metadata: projection.metadata,
|
|
106176
|
-
ledgerEntry: projection.ledgerEntry
|
|
106177
|
-
});
|
|
106178
|
-
await Promise.all([
|
|
106179
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106180
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate)
|
|
106181
|
-
]);
|
|
106182
|
-
} catch (error88) {
|
|
106183
|
-
addEvent("assessment.diagnostic_child_projection_failed", {
|
|
106184
|
-
"app.assessment.attempt_id": projection.result.sourcedId,
|
|
106185
|
-
"app.assessment.qti_item_identifier": projection.ledgerEntry.itemIdentifier,
|
|
106186
|
-
"exception.type": errorType(error88),
|
|
106187
|
-
"app.error.message": errorMessage(error88)
|
|
106188
|
-
});
|
|
106189
|
-
}
|
|
106190
|
-
}
|
|
106191
|
-
async restoreDiagnosticChildResults(attempt) {
|
|
106192
|
-
const assessment = attempt.assessment ?? await this.loadAttemptAssessment(attempt.metadata);
|
|
106193
|
-
await runWithConcurrency(attempt.metadata.diagnostic.ledger, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (ledgerEntry) => {
|
|
106194
|
-
try {
|
|
106195
|
-
const childLineItemId = await diagnosticQuestionLineItemId(attempt.result.assessmentLineItem.sourcedId, ledgerEntry.itemIdentifier);
|
|
106196
|
-
const childResultId = await diagnosticQuestionResultId(attempt.result.sourcedId, childLineItemId);
|
|
106197
|
-
let childResult;
|
|
106198
|
-
try {
|
|
106199
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
106200
|
-
} catch (error88) {
|
|
106201
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
106202
|
-
throw error88;
|
|
106203
|
-
}
|
|
106204
|
-
}
|
|
106205
|
-
const resolution = resolveDiagnosticChildResult({
|
|
106206
|
-
childResult,
|
|
106207
|
-
childLineItemId,
|
|
106208
|
-
parentResult: attempt.result,
|
|
106209
|
-
parentMetadata: attempt.metadata,
|
|
106210
|
-
ledgerEntry
|
|
106211
|
-
});
|
|
106212
|
-
if (resolution.action === "reuse") {
|
|
106213
|
-
return;
|
|
106214
|
-
}
|
|
106215
|
-
if (resolution.action === "restore") {
|
|
106216
|
-
addEvent("assessment.diagnostic_child_result_restore", {
|
|
106217
|
-
"app.assessment.diagnostic_child_restore_reason": resolution.reason,
|
|
106218
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
106219
|
-
"app.assessment.child_result_id": childResultId,
|
|
106220
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier
|
|
106221
|
-
});
|
|
106222
|
-
}
|
|
106223
|
-
await this.projectDiagnosticItemResponse({
|
|
106224
|
-
result: attempt.result,
|
|
106225
|
-
metadata: attempt.metadata,
|
|
106226
|
-
ledgerEntry,
|
|
106227
|
-
integration: attempt.integration
|
|
106228
|
-
}, assessment);
|
|
106229
|
-
} catch (error88) {
|
|
106230
|
-
addEvent("assessment.diagnostic_child_reconciliation_failed", {
|
|
106231
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
106232
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier,
|
|
106233
|
-
"exception.type": errorType(error88),
|
|
106234
|
-
"app.error.message": errorMessage(error88)
|
|
106235
|
-
});
|
|
106236
|
-
}
|
|
106237
|
-
});
|
|
106238
|
-
}
|
|
106239
105454
|
async ensureReviewQuestionLineItem(input) {
|
|
106240
105455
|
const client2 = this.requireClient();
|
|
106241
105456
|
const lineItemId = await reviewQuestionLineItemId(input.parentLineItemId, input.item.identifier);
|
|
@@ -106271,231 +105486,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106271
105486
|
...expected
|
|
106272
105487
|
});
|
|
106273
105488
|
} catch (error88) {
|
|
106274
|
-
|
|
106275
|
-
throw error88;
|
|
106276
|
-
}
|
|
106277
|
-
const stored = await client2.api.oneroster.assessmentLineItems.get(lineItemId);
|
|
105489
|
+
const stored = await this.confirmLineItemAfterRefusedCreate(lineItemId, error88, "assessment.review_question_line_item_create_conflict", () => client2.api.oneroster.assessmentLineItems.get(lineItemId));
|
|
106278
105490
|
this.assertAssessmentChildLineItem(lineItemId, stored, expected);
|
|
106279
|
-
addEvent("assessment.review_question_line_item_create_conflict", {
|
|
106280
|
-
"app.assessment.line_item_id": lineItemId
|
|
106281
|
-
});
|
|
106282
105491
|
}
|
|
106283
105492
|
return lineItemId;
|
|
106284
105493
|
}
|
|
106285
|
-
async putOpenReviewChildResult(input) {
|
|
106286
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
106287
|
-
metadata: input.parentMetadata,
|
|
106288
|
-
selection: input.selection,
|
|
106289
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
106290
|
-
});
|
|
106291
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(input.childResultId, buildOpenReviewChildResult({
|
|
106292
|
-
childLineItemId: input.childLineItemId,
|
|
106293
|
-
student: input.parentResult.student,
|
|
106294
|
-
metadata: metadata2
|
|
106295
|
-
}));
|
|
106296
|
-
}
|
|
106297
|
-
async ensureReviewChildResults(input) {
|
|
106298
|
-
const itemByIdentifier = new Map(input.assessment.items.map((item) => [item.identifier, item]));
|
|
106299
|
-
const bankItemByIdentifier = new Map(input.source.bank.items.map((item) => [item.itemIdentifier, item]));
|
|
106300
|
-
const physicalItems = new Set(input.metadata.review.selections.map((selection) => selection.itemIdentifier));
|
|
106301
|
-
if (physicalItems.size !== input.metadata.review.selections.length) {
|
|
106302
|
-
throw new Error("MVP review selection must use each physical item at most once");
|
|
106303
|
-
}
|
|
106304
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
106305
|
-
await runWithConcurrency(input.metadata.review.selections, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (selection) => {
|
|
106306
|
-
const item = itemByIdentifier.get(selection.itemIdentifier);
|
|
106307
|
-
const indexed = bankItemByIdentifier.get(selection.itemIdentifier);
|
|
106308
|
-
if (!item || !indexed) {
|
|
106309
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
106310
|
-
}
|
|
106311
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
106312
|
-
parentLineItemId: input.result.assessmentLineItem.sourcedId,
|
|
106313
|
-
integration: input.integration,
|
|
106314
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
106315
|
-
standards: [...indexed.standards],
|
|
106316
|
-
item
|
|
106317
|
-
});
|
|
106318
|
-
if (!administeredItems.has(selection.itemIdentifier)) {
|
|
106319
|
-
return;
|
|
106320
|
-
}
|
|
106321
|
-
const childResultId = await reviewQuestionResultId(input.result.sourcedId, childLineItemId);
|
|
106322
|
-
const streamedChildResult = input.existingChildResults.get(childResultId);
|
|
106323
|
-
let childResult = streamedChildResult;
|
|
106324
|
-
if (!childResult && input.lookupMissingResults) {
|
|
106325
|
-
try {
|
|
106326
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
106327
|
-
} catch (error88) {
|
|
106328
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
106329
|
-
throw error88;
|
|
106330
|
-
}
|
|
106331
|
-
}
|
|
106332
|
-
}
|
|
106333
|
-
const resolution = resolveReviewChildResult({
|
|
106334
|
-
childResult,
|
|
106335
|
-
childLineItemId,
|
|
106336
|
-
parentResult: input.result,
|
|
106337
|
-
parentMetadata: input.metadata,
|
|
106338
|
-
selection
|
|
106339
|
-
});
|
|
106340
|
-
if (resolution.action === "reuse") {
|
|
106341
|
-
return;
|
|
106342
|
-
}
|
|
106343
|
-
if (resolution.action === "restore") {
|
|
106344
|
-
addEvent("assessment.review_child_result_restore", {
|
|
106345
|
-
"app.assessment.review_child_restore_reason": resolution.reason,
|
|
106346
|
-
"app.assessment.attempt_id": input.result.sourcedId,
|
|
106347
|
-
"app.assessment.child_result_id": childResultId,
|
|
106348
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier
|
|
106349
|
-
});
|
|
106350
|
-
}
|
|
106351
|
-
await this.putOpenReviewChildResult({
|
|
106352
|
-
childResultId,
|
|
106353
|
-
childLineItemId,
|
|
106354
|
-
parentResult: input.result,
|
|
106355
|
-
parentMetadata: input.metadata,
|
|
106356
|
-
selection
|
|
106357
|
-
});
|
|
106358
|
-
});
|
|
106359
|
-
}
|
|
106360
|
-
async putReviewChildResponse(projection, checkpoint) {
|
|
106361
|
-
const { result, metadata: metadata2, itemIdentifier, integration } = projection;
|
|
106362
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === itemIdentifier);
|
|
106363
|
-
if (!selection) {
|
|
106364
|
-
return;
|
|
106365
|
-
}
|
|
106366
|
-
try {
|
|
106367
|
-
const source = await this.loadPinnedReviewBankSource(metadata2);
|
|
106368
|
-
const item = source.assessment.items.find((candidate) => candidate.identifier === selection.itemIdentifier);
|
|
106369
|
-
const indexed = source.bank.items.find((candidate) => candidate.itemIdentifier === selection.itemIdentifier);
|
|
106370
|
-
if (!item || !indexed) {
|
|
106371
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
106372
|
-
}
|
|
106373
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
106374
|
-
parentLineItemId: result.assessmentLineItem.sourcedId,
|
|
106375
|
-
integration,
|
|
106376
|
-
bankRevision: metadata2.review.bankRevision,
|
|
106377
|
-
standards: [...indexed.standards],
|
|
106378
|
-
item
|
|
106379
|
-
});
|
|
106380
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
106381
|
-
throw new Error("Prepared review checkpoint resolved a different line item");
|
|
106382
|
-
}
|
|
106383
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
106384
|
-
const childMetadata = buildReviewItemResultMetadata({
|
|
106385
|
-
metadata: metadata2,
|
|
106386
|
-
selection,
|
|
106387
|
-
parentAttemptId: result.sourcedId
|
|
106388
|
-
});
|
|
106389
|
-
await Promise.all([
|
|
106390
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106391
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, buildOpenReviewChildResult({
|
|
106392
|
-
childLineItemId,
|
|
106393
|
-
student: result.student,
|
|
106394
|
-
metadata: childMetadata
|
|
106395
|
-
}))
|
|
106396
|
-
]);
|
|
106397
|
-
} catch (error88) {
|
|
106398
|
-
addEvent("assessment.review_child_response_projection_failed", {
|
|
106399
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
106400
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
106401
|
-
"exception.type": errorType(error88),
|
|
106402
|
-
"app.error.message": errorMessage(error88)
|
|
106403
|
-
});
|
|
106404
|
-
}
|
|
106405
|
-
}
|
|
106406
|
-
async projectReviewItemResponse(params, projection, checkpoint) {
|
|
106407
|
-
const terminal = assessmentChildProjectionIsTerminal(projection.result);
|
|
106408
|
-
try {
|
|
106409
|
-
await reconcileReviewChildResults({
|
|
106410
|
-
initial: projection,
|
|
106411
|
-
writeOpen: () => this.putReviewChildResponse(projection, checkpoint),
|
|
106412
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, params.attemptId),
|
|
106413
|
-
readLatest: () => this.peekAttempt(params),
|
|
106414
|
-
restoreTerminal: async (latest) => {
|
|
106415
|
-
const repair = this.restoreCompletedReviewChildResponses(latest.result, latest.metadata, new Set([projection.itemIdentifier]));
|
|
106416
|
-
if (terminal) {
|
|
106417
|
-
await Promise.allSettled([
|
|
106418
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106419
|
-
repair
|
|
106420
|
-
]);
|
|
106421
|
-
} else {
|
|
106422
|
-
await repair;
|
|
106423
|
-
}
|
|
106424
|
-
}
|
|
106425
|
-
});
|
|
106426
|
-
} catch (error88) {
|
|
106427
|
-
addEvent("assessment.review_child_projection_barrier_failed", {
|
|
106428
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
106429
|
-
"app.assessment.qti_item_identifier": projection.itemIdentifier,
|
|
106430
|
-
"exception.type": errorType(error88),
|
|
106431
|
-
"app.error.message": errorMessage(error88)
|
|
106432
|
-
});
|
|
106433
|
-
}
|
|
106434
|
-
}
|
|
106435
|
-
restoreAllCompletedReviewChildResponses(result, metadata2) {
|
|
106436
|
-
return this.restoreCompletedReviewChildResponses(result, metadata2, new Set(metadata2.itemSubmissions.map((item) => item.itemIdentifier)));
|
|
106437
|
-
}
|
|
106438
|
-
async restoreCompletedReviewChildResponses(result, metadata2, changedItemIdentifiers) {
|
|
106439
|
-
const outcomes = metadata2.completion?.itemOutcomes;
|
|
106440
|
-
const submissionId = metadata2.submissionId;
|
|
106441
|
-
if (!submissionId || !outcomes) {
|
|
106442
|
-
addEvent("assessment.review_child_terminal_restore_unavailable", {
|
|
106443
|
-
"app.assessment.attempt_id": result.sourcedId
|
|
106444
|
-
});
|
|
106445
|
-
return;
|
|
106446
|
-
}
|
|
106447
|
-
const outcomeByItem = new Map(outcomes.map((outcome) => [outcome.itemIdentifier, outcome]));
|
|
106448
|
-
await runWithConcurrency(metadata2.review.selections.filter((selection) => changedItemIdentifiers.has(selection.itemIdentifier)), TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (selection) => {
|
|
106449
|
-
try {
|
|
106450
|
-
const outcome = outcomeByItem.get(selection.itemIdentifier);
|
|
106451
|
-
if (!outcome) {
|
|
106452
|
-
throw new Error(`Completed review omitted item ${selection.itemIdentifier}`);
|
|
106453
|
-
}
|
|
106454
|
-
const childLineItemId = await reviewQuestionLineItemId(result.assessmentLineItem.sourcedId, selection.itemIdentifier);
|
|
106455
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
106456
|
-
const finalized = buildFinalizedReviewChildResult({
|
|
106457
|
-
childLineItemId,
|
|
106458
|
-
student: result.student,
|
|
106459
|
-
parentAttemptId: result.sourcedId,
|
|
106460
|
-
metadata: metadata2,
|
|
106461
|
-
selection,
|
|
106462
|
-
itemResult: {
|
|
106463
|
-
itemIdentifier: outcome.itemIdentifier,
|
|
106464
|
-
score: outcome.score,
|
|
106465
|
-
isCorrect: outcome.isCorrect
|
|
106466
|
-
},
|
|
106467
|
-
submissionId,
|
|
106468
|
-
timestamp: result.scoreDate
|
|
106469
|
-
});
|
|
106470
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate);
|
|
106471
|
-
} catch (error88) {
|
|
106472
|
-
addEvent("assessment.review_child_terminal_restore_failed", {
|
|
106473
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
106474
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
106475
|
-
"exception.type": errorType(error88),
|
|
106476
|
-
"app.error.message": errorMessage(error88)
|
|
106477
|
-
});
|
|
106478
|
-
}
|
|
106479
|
-
});
|
|
106480
|
-
}
|
|
106481
|
-
reviewItemOutcomes(input) {
|
|
106482
|
-
const resultByItem = new Map(input.itemResults.map((itemResult) => [itemResult.itemIdentifier, itemResult]));
|
|
106483
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
106484
|
-
return input.metadata.review.selections.filter((selection) => administeredItems.has(selection.itemIdentifier)).map((selection) => {
|
|
106485
|
-
const itemResult = resultByItem.get(selection.itemIdentifier);
|
|
106486
|
-
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
106487
|
-
if (!itemResult || !submission) {
|
|
106488
|
-
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
106489
|
-
}
|
|
106490
|
-
return {
|
|
106491
|
-
itemIdentifier: selection.itemIdentifier,
|
|
106492
|
-
standard: selection.standard,
|
|
106493
|
-
answered: submission.answered,
|
|
106494
|
-
score: itemResult.score,
|
|
106495
|
-
isCorrect: itemResult.isCorrect
|
|
106496
|
-
};
|
|
106497
|
-
});
|
|
106498
|
-
}
|
|
106499
105494
|
async resolveAttemptAuthorization({ gameId, studentId, attemptId, user }, options = {}) {
|
|
106500
105495
|
if (!options.developerAccessValidated) {
|
|
106501
105496
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
@@ -106524,14 +105519,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106524
105519
|
const enrollment = enrollments.find((candidate) => candidate.id === metadata2.enrollmentId && candidate.course.id === integration.courseId);
|
|
106525
105520
|
return { attempt: { result, metadata: metadata2, integration }, enrollment };
|
|
106526
105521
|
}
|
|
106527
|
-
async authorizeAttempt(params, options = {}) {
|
|
106528
|
-
const authorization = await this.resolveAttemptAuthorization(params, options);
|
|
106529
|
-
return this.requireActiveAttempt(authorization, params.attemptId);
|
|
106530
|
-
}
|
|
106531
|
-
requireActiveAttempt(authorization, attemptId) {
|
|
106532
|
-
const enrollment = requireAssessmentMutationAccess(attemptId, authorization.attempt.integration.status, authorization.enrollment);
|
|
106533
|
-
return { ...authorization.attempt, enrollment };
|
|
106534
|
-
}
|
|
106535
105522
|
async authorizeAttemptRead(params) {
|
|
106536
105523
|
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params);
|
|
106537
105524
|
if ((!enrollment || attempt.integration.status === "deactivated") && !this.isSettled(attempt.result)) {
|
|
@@ -106542,7 +105529,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106542
105529
|
unauthorizedAttempt(attemptId) {
|
|
106543
105530
|
return AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
106544
105531
|
}
|
|
106545
|
-
async snapshotForResult(result, metadata2, integration) {
|
|
105532
|
+
async snapshotForResult(result, metadata2, integration, suppliedTranscript) {
|
|
106546
105533
|
if (isAssessmentAttemptSuperseded({
|
|
106547
105534
|
inProgress: result.inProgress ?? "",
|
|
106548
105535
|
scoreStatus: result.scoreStatus
|
|
@@ -106578,38 +105565,59 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106578
105565
|
result: buildCompletedAssessmentSubmitResult({ result, metadata: metadata2 })
|
|
106579
105566
|
};
|
|
106580
105567
|
}
|
|
106581
|
-
const
|
|
106582
|
-
|
|
106583
|
-
|
|
106584
|
-
|
|
106585
|
-
|
|
106586
|
-
|
|
106587
|
-
integration,
|
|
106588
|
-
assessment
|
|
106589
|
-
});
|
|
106590
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, integration, routing.state);
|
|
106591
|
-
}
|
|
106592
|
-
return this.snapshot(result, metadata2, assessment, integration);
|
|
106593
|
-
}
|
|
106594
|
-
diagnosticSnapshot(result, metadata2, assessment, integration, state) {
|
|
106595
|
-
return buildDiagnosticAssessmentSnapshot({
|
|
106596
|
-
attemptId: result.sourcedId,
|
|
106597
|
-
metadata: metadata2,
|
|
106598
|
-
assessment,
|
|
106599
|
-
state,
|
|
106600
|
-
activityData: this.activityData(metadata2, assessment, integration)
|
|
105568
|
+
const context2 = this.tokenContext({ result, metadata: metadata2, integration });
|
|
105569
|
+
const graded = await resumeAssessmentTranscript({
|
|
105570
|
+
suppliedTranscript,
|
|
105571
|
+
checkpointTranscript: metadata2.transcript,
|
|
105572
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.studentAssessmentResults(context2.studentId)),
|
|
105573
|
+
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
106601
105574
|
});
|
|
105575
|
+
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
105576
|
+
const common2 = {
|
|
105577
|
+
attemptId: result.sourcedId,
|
|
105578
|
+
assessment: graded.assessment,
|
|
105579
|
+
activityData: this.activityData(metadata2, graded.assessment, integration)
|
|
105580
|
+
};
|
|
105581
|
+
return this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? {
|
|
105582
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105583
|
+
...common2,
|
|
105584
|
+
metadata: graded.metadata,
|
|
105585
|
+
state: graded.metadata.diagnostic.state
|
|
105586
|
+
}),
|
|
105587
|
+
...session2
|
|
105588
|
+
} : {
|
|
105589
|
+
...buildConventionalAssessmentSnapshot({ ...common2, metadata: graded.metadata }),
|
|
105590
|
+
...session2
|
|
105591
|
+
};
|
|
105592
|
+
}
|
|
105593
|
+
async assessmentSession(context2, transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision)) {
|
|
105594
|
+
return { transcript, assessmentToken: await this.deps.mintAssessmentToken(context2) };
|
|
105595
|
+
}
|
|
105596
|
+
async diagnosticSnapshot(result, metadata2, assessment, integration, options) {
|
|
105597
|
+
return {
|
|
105598
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105599
|
+
attemptId: result.sourcedId,
|
|
105600
|
+
metadata: metadata2,
|
|
105601
|
+
assessment,
|
|
105602
|
+
state: options.state,
|
|
105603
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105604
|
+
}),
|
|
105605
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105606
|
+
};
|
|
106602
105607
|
}
|
|
106603
105608
|
diagnosticRoutingSnapshot(metadata2, state) {
|
|
106604
105609
|
return projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, state);
|
|
106605
105610
|
}
|
|
106606
|
-
snapshot(result, metadata2, assessment, integration) {
|
|
106607
|
-
return
|
|
106608
|
-
|
|
106609
|
-
|
|
106610
|
-
|
|
106611
|
-
|
|
106612
|
-
|
|
105611
|
+
async snapshot(result, metadata2, assessment, integration) {
|
|
105612
|
+
return {
|
|
105613
|
+
...buildConventionalAssessmentSnapshot({
|
|
105614
|
+
attemptId: result.sourcedId,
|
|
105615
|
+
metadata: metadata2,
|
|
105616
|
+
assessment,
|
|
105617
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105618
|
+
}),
|
|
105619
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105620
|
+
};
|
|
106613
105621
|
}
|
|
106614
105622
|
activityData(metadata2, assessment, integration) {
|
|
106615
105623
|
const course = this.trackableCourse(integration, "assessment.activity_tracking_invalid_course_metadata");
|
|
@@ -106670,16 +105678,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106670
105678
|
isSettled(result) {
|
|
106671
105679
|
return this.isAwaitingAward(result) || this.isCompleted(result);
|
|
106672
105680
|
}
|
|
106673
|
-
assertInProgress(result) {
|
|
106674
|
-
if (!this.isOpen(result)) {
|
|
106675
|
-
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(result.sourcedId));
|
|
106676
|
-
}
|
|
106677
|
-
}
|
|
106678
|
-
assertResponseVersion(metadata2, expectedResponseVersion) {
|
|
106679
|
-
if (metadata2.responseVersion !== expectedResponseVersion) {
|
|
106680
|
-
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, metadata2.responseVersion));
|
|
106681
|
-
}
|
|
106682
|
-
}
|
|
106683
105681
|
scoreFromResult(result, possible, metadata2) {
|
|
106684
105682
|
return metadata2.score ?? assessmentScoreFromPercentage(result.score, possible);
|
|
106685
105683
|
}
|
|
@@ -106730,7 +105728,22 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106730
105728
|
}
|
|
106731
105729
|
return projectReviewAssessment(playable, preparedReviewBankIndex(plan), plan.review.selections);
|
|
106732
105730
|
}
|
|
106733
|
-
async
|
|
105731
|
+
async claimPreparedAttempt(db2, plan, assessment, integration) {
|
|
105732
|
+
const occupant = await this.findAttemptResult(plan.attemptId);
|
|
105733
|
+
if (occupant) {
|
|
105734
|
+
const metadata3 = playcademyAssessmentResultMetadata(occupant.metadata);
|
|
105735
|
+
if (occupant.status !== ONEROSTER_STATUS2.active || !metadata3 || !this.attemptInScope(occupant, metadata3, plan) || metadata3.enrollmentId !== plan.enrollmentId || !this.isOpen(occupant) || !this.attemptMatchesPlan(metadata3, plan)) {
|
|
105736
|
+
return null;
|
|
105737
|
+
}
|
|
105738
|
+
addEvent("assessment.prepared_attempt_recovered", {
|
|
105739
|
+
"app.assessment.attempt_id": plan.attemptId
|
|
105740
|
+
});
|
|
105741
|
+
const [snapshot2] = await Promise.all([
|
|
105742
|
+
this.snapshotForResult(occupant, metadata3, integration),
|
|
105743
|
+
this.supersedeAttemptsBestEffort(db2, new Set(plan.supersedes))
|
|
105744
|
+
]);
|
|
105745
|
+
return snapshot2;
|
|
105746
|
+
}
|
|
106734
105747
|
const timestamp6 = new Date().toISOString();
|
|
106735
105748
|
const metadata2 = buildPreparedAssessmentMetadata(plan, timestamp6, assessment.title);
|
|
106736
105749
|
const result = await this.requireClient().api.oneroster.assessmentResults.upsert(plan.attemptId, {
|
|
@@ -106747,7 +105760,42 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106747
105760
|
grade: integration.grade
|
|
106748
105761
|
})
|
|
106749
105762
|
});
|
|
106750
|
-
|
|
105763
|
+
const [snapshot] = await Promise.all([
|
|
105764
|
+
this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration, {
|
|
105765
|
+
state: metadata2.diagnostic.state
|
|
105766
|
+
}) : this.snapshot(result, metadata2, assessment, integration),
|
|
105767
|
+
this.supersedeAttemptsBestEffort(db2, new Set(plan.supersedes))
|
|
105768
|
+
]);
|
|
105769
|
+
return snapshot;
|
|
105770
|
+
}
|
|
105771
|
+
attemptMatchesPlan(metadata2, plan) {
|
|
105772
|
+
const planned = buildPreparedAssessmentMetadata(plan, metadata2.startedAt, "");
|
|
105773
|
+
if (metadata2.selectedTest.assessmentKey !== planned.selectedTest.assessmentKey || metadata2.selectedTest.identifier !== planned.selectedTest.identifier || metadata2.selectedTest.contentRevision !== planned.selectedTest.contentRevision) {
|
|
105774
|
+
return false;
|
|
105775
|
+
}
|
|
105776
|
+
if (metadata2.purpose === "review" && planned.purpose === "review") {
|
|
105777
|
+
return metadata2.review.requestFingerprint === planned.review.requestFingerprint && metadata2.review.bankRevision === planned.review.bankRevision && metadata2.review.selections.length === planned.review.selections.length && metadata2.review.selections.every((selection, index2) => selection.itemIdentifier === planned.review.selections[index2].itemIdentifier);
|
|
105778
|
+
}
|
|
105779
|
+
if (metadata2.purpose === "mastery" && planned.purpose === "mastery") {
|
|
105780
|
+
return metadata2.mastery.requestFingerprint === planned.mastery.requestFingerprint;
|
|
105781
|
+
}
|
|
105782
|
+
if (metadata2.purpose === "diagnostic" && plan.purpose === "diagnostic") {
|
|
105783
|
+
return this.isPlatformRoutedDiagnosticMetadata(metadata2) && metadata2.diagnostic.definitionId === plan.diagnostic.definitionId && metadata2.diagnostic.routingRevision === plan.diagnostic.routingRevision;
|
|
105784
|
+
}
|
|
105785
|
+
return metadata2.purpose === planned.purpose;
|
|
105786
|
+
}
|
|
105787
|
+
async findAttemptResult(attemptId) {
|
|
105788
|
+
try {
|
|
105789
|
+
return await this.requireClient().api.oneroster.assessmentResults.get(attemptId);
|
|
105790
|
+
} catch (error88) {
|
|
105791
|
+
if (isApiError(error88) && error88.statusCode === 404) {
|
|
105792
|
+
return null;
|
|
105793
|
+
}
|
|
105794
|
+
throw error88;
|
|
105795
|
+
}
|
|
105796
|
+
}
|
|
105797
|
+
attemptInScope(result, metadata2, scope) {
|
|
105798
|
+
return metadata2.activityId === scope.activityId && metadata2.purpose === scope.purpose && metadata2.courseId === scope.courseId && metadata2.integrationId === scope.integrationId && result.student.sourcedId === scope.studentId;
|
|
106751
105799
|
}
|
|
106752
105800
|
async claimAttemptId(scope, from) {
|
|
106753
105801
|
for (let attemptNumber = from;attemptNumber < from + 20; attemptNumber += 1) {
|
|
@@ -106763,15 +105811,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106763
105811
|
throw new Error(`Assessment attempt numbers ${from} through ${from + 19} are all occupied by records this code cannot read; refusing to overwrite them.`);
|
|
106764
105812
|
}
|
|
106765
105813
|
async attemptIdOccupied(attemptId) {
|
|
106766
|
-
|
|
106767
|
-
await this.requireClient().api.oneroster.assessmentResults.get(attemptId);
|
|
106768
|
-
} catch (error88) {
|
|
106769
|
-
if (isApiError(error88) && error88.statusCode === 404) {
|
|
106770
|
-
return false;
|
|
106771
|
-
}
|
|
106772
|
-
throw error88;
|
|
106773
|
-
}
|
|
106774
|
-
return true;
|
|
105814
|
+
return await this.findAttemptResult(attemptId) !== null;
|
|
106775
105815
|
}
|
|
106776
105816
|
replayCompletion(attempt, submissionId, params, game2) {
|
|
106777
105817
|
const { score, completion } = attempt.metadata;
|
|
@@ -106792,18 +105832,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106792
105832
|
game: game2
|
|
106793
105833
|
};
|
|
106794
105834
|
}
|
|
106795
|
-
async putResultMetadata(result, metadata2) {
|
|
106796
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(result.sourcedId, {
|
|
106797
|
-
status: ONEROSTER_STATUS2.active,
|
|
106798
|
-
assessmentLineItem: result.assessmentLineItem,
|
|
106799
|
-
student: result.student,
|
|
106800
|
-
score: result.score ?? 0,
|
|
106801
|
-
scoreDate: result.scoreDate,
|
|
106802
|
-
scoreStatus: result.scoreStatus,
|
|
106803
|
-
...result.inProgress === undefined || result.inProgress === null ? {} : { inProgress: result.inProgress },
|
|
106804
|
-
metadata: mergeAssessmentRuntimeMetadata(result.metadata, metadata2)
|
|
106805
|
-
});
|
|
106806
|
-
}
|
|
106807
105835
|
async emitCompletionBestEffort(input) {
|
|
106808
105836
|
const { attempt } = input;
|
|
106809
105837
|
const integration = attempt.integration;
|
|
@@ -110392,6 +109420,8 @@ function createPlatformServices(deps) {
|
|
|
110392
109420
|
mintPlatformServiceToken,
|
|
110393
109421
|
mintAssessmentPreparationReceipt,
|
|
110394
109422
|
verifyAssessmentPreparationReceipt,
|
|
109423
|
+
mintAssessmentToken,
|
|
109424
|
+
verifyAssessmentToken,
|
|
110395
109425
|
validateDeveloperAccessBySlug,
|
|
110396
109426
|
validateDeveloperAccess,
|
|
110397
109427
|
validateGameManagementAccess
|
|
@@ -110466,7 +109496,9 @@ function createPlatformServices(deps) {
|
|
|
110466
109496
|
validateDeveloperAccess,
|
|
110467
109497
|
assessmentRuntimeLock,
|
|
110468
109498
|
mintAssessmentPreparationReceipt,
|
|
110469
|
-
verifyAssessmentPreparationReceipt
|
|
109499
|
+
verifyAssessmentPreparationReceipt,
|
|
109500
|
+
mintAssessmentToken,
|
|
109501
|
+
verifyAssessmentToken
|
|
110470
109502
|
});
|
|
110471
109503
|
return {
|
|
110472
109504
|
assessmentAssets,
|
|
@@ -111216,6 +110248,8 @@ function createServices(ctx) {
|
|
|
111216
110248
|
mintPlatformServiceToken: auth2.mintPlatformServiceToken.bind(auth2),
|
|
111217
110249
|
mintAssessmentPreparationReceipt: auth2.mintAssessmentPreparationReceipt.bind(auth2),
|
|
111218
110250
|
verifyAssessmentPreparationReceipt: auth2.verifyAssessmentPreparationReceipt.bind(auth2),
|
|
110251
|
+
mintAssessmentToken: auth2.mintAssessmentToken.bind(auth2),
|
|
110252
|
+
verifyAssessmentToken: auth2.verifyAssessmentToken.bind(auth2),
|
|
111219
110253
|
...gaming.validators
|
|
111220
110254
|
});
|
|
111221
110255
|
const standalone = createStandaloneServices({ db: db2, auth: auth2, timeback: timeback2 });
|
|
@@ -111744,6 +110778,15 @@ function createSandboxAuthProvider() {
|
|
|
111744
110778
|
});
|
|
111745
110779
|
return { receipt, expiresAt: new Date(expiresAt).toISOString() };
|
|
111746
110780
|
},
|
|
110781
|
+
async mintAssessmentToken(context2) {
|
|
110782
|
+
const token = `sandbox_assessment_${crypto.randomUUID()}`;
|
|
110783
|
+
sandboxAssessmentTokens.set(token, structuredClone(context2));
|
|
110784
|
+
return token;
|
|
110785
|
+
},
|
|
110786
|
+
async verifyAssessmentToken(token) {
|
|
110787
|
+
const context2 = sandboxAssessmentTokens.get(token);
|
|
110788
|
+
return context2 ? structuredClone(context2) : null;
|
|
110789
|
+
},
|
|
111747
110790
|
async verifyAssessmentPreparationReceipt(receipt) {
|
|
111748
110791
|
const prepared = sandboxAssessmentPreparationReceipts.get(receipt);
|
|
111749
110792
|
if (!prepared) {
|
|
@@ -111796,13 +110839,18 @@ function createSandboxAuthProvider() {
|
|
|
111796
110839
|
}
|
|
111797
110840
|
};
|
|
111798
110841
|
}
|
|
111799
|
-
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS;
|
|
111800
|
-
var init_auth_provider = __esm(() => {
|
|
110842
|
+
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, sandboxAssessmentTokens;
|
|
110843
|
+
var init_auth_provider = __esm(async () => {
|
|
111801
110844
|
init_src6();
|
|
111802
110845
|
init_constants();
|
|
110846
|
+
await init_dist5();
|
|
111803
110847
|
sandboxApiKeys = new Map;
|
|
111804
110848
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
111805
110849
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 5 * 60 * 1000;
|
|
110850
|
+
sandboxAssessmentTokens = new TimebackCache({
|
|
110851
|
+
defaultTTL: 2 * 60 * 60 * 1000,
|
|
110852
|
+
maxSize: 1000
|
|
110853
|
+
});
|
|
111806
110854
|
});
|
|
111807
110855
|
|
|
111808
110856
|
// src/infrastructure/api/providers/cache.provider.ts
|
|
@@ -111963,10 +111011,10 @@ var init_storage_provider = __esm(() => {
|
|
|
111963
111011
|
});
|
|
111964
111012
|
|
|
111965
111013
|
// src/infrastructure/api/providers/index.ts
|
|
111966
|
-
var init_providers = __esm(() => {
|
|
111967
|
-
init_auth_provider();
|
|
111014
|
+
var init_providers = __esm(async () => {
|
|
111968
111015
|
init_cache_provider();
|
|
111969
111016
|
init_storage_provider();
|
|
111017
|
+
await init_auth_provider();
|
|
111970
111018
|
});
|
|
111971
111019
|
|
|
111972
111020
|
// src/infrastructure/api/context.ts
|
|
@@ -112036,10 +111084,10 @@ var init_context = __esm(async () => {
|
|
|
112036
111084
|
init_config2();
|
|
112037
111085
|
init_utils7();
|
|
112038
111086
|
init_src6();
|
|
112039
|
-
init_providers();
|
|
112040
111087
|
await __promiseAll([
|
|
112041
111088
|
init_factory(),
|
|
112042
|
-
init_clients()
|
|
111089
|
+
init_clients(),
|
|
111090
|
+
init_providers()
|
|
112043
111091
|
]);
|
|
112044
111092
|
});
|
|
112045
111093
|
|
|
@@ -168435,7 +167483,7 @@ function parseQtiLibraryParams(searchParams) {
|
|
|
168435
167483
|
limit
|
|
168436
167484
|
};
|
|
168437
167485
|
}
|
|
168438
|
-
var populateStudent, getUser, getUserEnrollments, getUserById, setupIntegration, getIntegrations, getRemovedIntegrations, createIntegration, updateIntegration, deactivateCourse, reactivateCourse, getIntegrationConfig, verifyIntegration, getConfig, deleteIntegrations, endActivity, heartbeat, advanceCourse, unenrollCourse, prepareRuntimeAssessment, startRuntimeAssessment, getRuntimeAssessment, getLatestRuntimeAssessment, saveRuntimeAssessment, submitRuntimeAssessmentItem, submitRuntimeAssessment, finalizeRuntimeAssessment, exportRuntimeAssessmentFixture, getStudentXp, getStudentMastery, getStudentHighestGradeMastered, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getGradeLevelTestResults, getGradeLevelTestReview, getActivityDetail, listMetricDiscrepancies, verifyMetricDiscrepancy, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, attachExistingAssessments, updateAssessment, synchronizeReviewBank, reorderQuestions, removeAssessment, listQuestions, listQuestionLibrary, listTestLibrary, copyAssessment, createQuestion, updateQuestion, removeQuestion, timeback2;
|
|
167486
|
+
var populateStudent, getUser, getUserEnrollments, getUserById, setupIntegration, getIntegrations, getRemovedIntegrations, createIntegration, updateIntegration, deactivateCourse, reactivateCourse, getIntegrationConfig, verifyIntegration, getConfig, deleteIntegrations, endActivity, heartbeat, advanceCourse, unenrollCourse, prepareRuntimeAssessment, startRuntimeAssessment, getRuntimeAssessment, getLatestRuntimeAssessment, resumeRuntimeAssessment, saveRuntimeAssessment, recordRuntimeAssessmentProgress, submitRuntimeAssessmentItem, submitRuntimeAssessment, finalizeRuntimeAssessment, exportRuntimeAssessmentFixture, getStudentXp, getStudentMastery, getStudentHighestGradeMastered, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getGradeLevelTestResults, getGradeLevelTestReview, getActivityDetail, listMetricDiscrepancies, verifyMetricDiscrepancy, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, attachExistingAssessments, updateAssessment, synchronizeReviewBank, reorderQuestions, removeAssessment, listQuestions, listQuestionLibrary, listTestLibrary, copyAssessment, createQuestion, updateQuestion, removeQuestion, timeback2;
|
|
168439
167487
|
var init_timeback_controller = __esm(() => {
|
|
168440
167488
|
init_esm();
|
|
168441
167489
|
init_src();
|
|
@@ -168737,6 +167785,20 @@ var init_timeback_controller = __esm(() => {
|
|
|
168737
167785
|
user: ctx.user
|
|
168738
167786
|
});
|
|
168739
167787
|
});
|
|
167788
|
+
resumeRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
167789
|
+
const attemptId = ctx.params.attemptId;
|
|
167790
|
+
const body2 = await parseRequestBody(ctx.request, ResumeRuntimeAssessmentRequestSchema);
|
|
167791
|
+
if (!attemptId) {
|
|
167792
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167793
|
+
}
|
|
167794
|
+
return ctx.services.timebackAssessmentRuntime.resume({
|
|
167795
|
+
gameId: body2.gameId,
|
|
167796
|
+
studentId: body2.studentId,
|
|
167797
|
+
attemptId,
|
|
167798
|
+
input: { transcript: body2.transcript },
|
|
167799
|
+
user: ctx.user
|
|
167800
|
+
});
|
|
167801
|
+
});
|
|
168740
167802
|
saveRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
168741
167803
|
const attemptId = ctx.params.attemptId;
|
|
168742
167804
|
const body2 = await parseRequestBody(ctx.request, SaveRuntimeAssessmentRequestSchema);
|
|
@@ -168749,7 +167811,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
168749
167811
|
attemptId,
|
|
168750
167812
|
input: {
|
|
168751
167813
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168752
|
-
|
|
167814
|
+
transcript: body2.transcript
|
|
167815
|
+
},
|
|
167816
|
+
user: ctx.user
|
|
167817
|
+
});
|
|
167818
|
+
});
|
|
167819
|
+
recordRuntimeAssessmentProgress = requireDeveloper(async (ctx) => {
|
|
167820
|
+
const attemptId = ctx.params.attemptId;
|
|
167821
|
+
const body2 = await parseRequestBody(ctx.request, RecordRuntimeAssessmentProgressRequestSchema);
|
|
167822
|
+
if (!attemptId) {
|
|
167823
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167824
|
+
}
|
|
167825
|
+
return ctx.services.timebackAssessmentRuntime.recordProgress({
|
|
167826
|
+
gameId: body2.gameId,
|
|
167827
|
+
studentId: body2.studentId,
|
|
167828
|
+
attemptId,
|
|
167829
|
+
input: {
|
|
167830
|
+
assessmentToken: body2.assessmentToken,
|
|
167831
|
+
transcript: body2.transcript,
|
|
167832
|
+
expectedResponseVersion: body2.expectedResponseVersion,
|
|
167833
|
+
itemIdentifier: body2.itemIdentifier
|
|
168753
167834
|
},
|
|
168754
167835
|
user: ctx.user
|
|
168755
167836
|
});
|
|
@@ -168765,6 +167846,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
168765
167846
|
studentId: body2.studentId,
|
|
168766
167847
|
attemptId,
|
|
168767
167848
|
input: {
|
|
167849
|
+
assessmentToken: body2.assessmentToken,
|
|
167850
|
+
transcript: body2.transcript,
|
|
168768
167851
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168769
167852
|
submissionId: body2.submissionId,
|
|
168770
167853
|
itemIdentifier: body2.itemIdentifier,
|
|
@@ -168787,6 +167870,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
168787
167870
|
input: {
|
|
168788
167871
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168789
167872
|
submissionId: body2.submissionId,
|
|
167873
|
+
transcript: body2.transcript,
|
|
168790
167874
|
...body2.session ? { session: body2.session } : {}
|
|
168791
167875
|
},
|
|
168792
167876
|
game: {
|
|
@@ -169264,6 +168348,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
169264
168348
|
getLatestRuntimeAssessment,
|
|
169265
168349
|
getRuntimeAssessment,
|
|
169266
168350
|
saveRuntimeAssessment,
|
|
168351
|
+
resumeRuntimeAssessment,
|
|
168352
|
+
recordRuntimeAssessmentProgress,
|
|
169267
168353
|
submitRuntimeAssessmentItem,
|
|
169268
168354
|
submitRuntimeAssessment,
|
|
169269
168355
|
finalizeRuntimeAssessment,
|
|
@@ -170256,6 +169342,8 @@ var init_timeback8 = __esm(async () => {
|
|
|
170256
169342
|
timebackRouter.post("/assessments/start", handle2(timeback2.startRuntimeAssessment));
|
|
170257
169343
|
timebackRouter.get("/assessments/latest", handle2(timeback2.getLatestRuntimeAssessment));
|
|
170258
169344
|
timebackRouter.get("/assessments/:attemptId", handle2(timeback2.getRuntimeAssessment));
|
|
169345
|
+
timebackRouter.post("/assessments/:attemptId/resume", handle2(timeback2.resumeRuntimeAssessment));
|
|
169346
|
+
timebackRouter.post("/assessments/:attemptId/progress", handle2(timeback2.recordRuntimeAssessmentProgress));
|
|
170259
169347
|
timebackRouter.post("/assessments/:attemptId/save", handle2(timeback2.saveRuntimeAssessment));
|
|
170260
169348
|
timebackRouter.post("/assessments/:attemptId/submit-item", handle2(timeback2.submitRuntimeAssessmentItem));
|
|
170261
169349
|
timebackRouter.post("/assessments/:attemptId/submit", handle2(timeback2.submitRuntimeAssessment));
|
|
@@ -170664,13 +169752,13 @@ var init_server = __esm(async () => {
|
|
|
170664
169752
|
init_port();
|
|
170665
169753
|
init_package();
|
|
170666
169754
|
init_config();
|
|
170667
|
-
init_providers();
|
|
170668
169755
|
init_app();
|
|
170669
169756
|
init_database2();
|
|
170670
169757
|
init_options();
|
|
170671
169758
|
await __promiseAll([
|
|
170672
169759
|
init_api3(),
|
|
170673
|
-
init_context()
|
|
169760
|
+
init_context(),
|
|
169761
|
+
init_providers()
|
|
170674
169762
|
]);
|
|
170675
169763
|
version5 = package_default.version;
|
|
170676
169764
|
});
|