@playcademy/sandbox 0.8.1-beta.5 → 0.8.1-beta.6
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 +1189 -1931
- package/dist/constants.js +1 -1
- package/dist/server.js +1189 -1931
- 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.6",
|
|
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,147 +10594,154 @@ 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));
|
|
10597
|
+
function emptyAssessmentTranscript(attemptId, contentRevision) {
|
|
10598
|
+
return { version: 1, attemptId, contentRevision, administrations: [] };
|
|
10625
10599
|
}
|
|
10626
|
-
function
|
|
10627
|
-
|
|
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]));
|
|
10642
|
-
}
|
|
10643
|
-
function itemResponseUpdateMatches(current, update) {
|
|
10644
|
-
return itemResponsesEqual(current, applyAssessmentItemResponseUpdate(current, update));
|
|
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) {
|
|
@@ -11090,9 +11074,6 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
11090
11074
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
11091
11075
|
return deterministicUUID(["playcademy:review-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
11092
11076
|
}
|
|
11093
|
-
function reviewQuestionResultId(parentAttemptId, childQuestionLineItemId) {
|
|
11094
|
-
return deterministicUUID(["playcademy:review-question-result:v1", parentAttemptId, childQuestionLineItemId].join("\x00"));
|
|
11095
|
-
}
|
|
11096
11077
|
function qtiQuestionByIdentifier(questions) {
|
|
11097
11078
|
const byIdentifier = new Map;
|
|
11098
11079
|
for (const entry of questions.questions) {
|
|
@@ -11652,11 +11633,8 @@ function isDiagnosticRoutingState(value) {
|
|
|
11652
11633
|
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
11634
|
return stagesValid && tracksValid && nextValid && (value.status === "in-progress" && value.next !== null || value.status === "ready-to-complete" && value.next === null);
|
|
11654
11635
|
}
|
|
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
11636
|
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) ||
|
|
11637
|
+
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
11638
|
return false;
|
|
11661
11639
|
}
|
|
11662
11640
|
const itemIdentifiers = new Set;
|
|
@@ -11710,19 +11688,7 @@ function isPlaycademyAssessmentResultMetadataV1(value) {
|
|
|
11710
11688
|
const review = metadata2.review;
|
|
11711
11689
|
const diagnostic = metadata2.diagnostic;
|
|
11712
11690
|
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";
|
|
11691
|
+
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
11692
|
}
|
|
11727
11693
|
function playcademyAssessmentResultMetadata(value) {
|
|
11728
11694
|
if (!isRecord(value)) {
|
|
@@ -11742,12 +11708,6 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11742
11708
|
diagnostic: { ...normalized.diagnostic, ledger: [] }
|
|
11743
11709
|
};
|
|
11744
11710
|
}
|
|
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
11711
|
if (isRecord(normalized.diagnostic) && isRecord(normalized.diagnostic.state)) {
|
|
11752
11712
|
const state = normalized.diagnostic.state;
|
|
11753
11713
|
const tracks = Array.isArray(state.tracks) ? state.tracks.map((track) => isRecord(track) ? {
|
|
@@ -11755,12 +11715,10 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11755
11715
|
currentNodeKey: track.currentNodeKey ?? null,
|
|
11756
11716
|
terminal: track.terminal ?? null
|
|
11757
11717
|
} : 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
11718
|
normalized = {
|
|
11760
11719
|
...normalized,
|
|
11761
11720
|
diagnostic: {
|
|
11762
11721
|
...normalized.diagnostic,
|
|
11763
|
-
...routingSnapshots === undefined ? {} : { routingSnapshots },
|
|
11764
11722
|
state: {
|
|
11765
11723
|
...state,
|
|
11766
11724
|
...tracks === undefined ? {} : { tracks },
|
|
@@ -11770,6 +11728,16 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11770
11728
|
};
|
|
11771
11729
|
}
|
|
11772
11730
|
const review = normalized.review;
|
|
11731
|
+
if (isRecord(normalized.transcript)) {
|
|
11732
|
+
const transcript = normalized.transcript;
|
|
11733
|
+
normalized = {
|
|
11734
|
+
...normalized,
|
|
11735
|
+
transcript: {
|
|
11736
|
+
...transcript,
|
|
11737
|
+
administrations: Array.isArray(transcript.administrations) ? transcript.administrations.map((entry) => isRecord(entry) ? { ...entry, responses: entry.responses ?? {} } : entry) : transcript.administrations ?? []
|
|
11738
|
+
}
|
|
11739
|
+
};
|
|
11740
|
+
}
|
|
11773
11741
|
if (isRecord(review) && isRecord(review.fulfillment) && review.fulfillment.status === "complete" && review.fulfillment.shortages === undefined) {
|
|
11774
11742
|
normalized = {
|
|
11775
11743
|
...normalized,
|
|
@@ -11784,29 +11752,7 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11784
11752
|
}
|
|
11785
11753
|
return isPlaycademyAssessmentResultMetadataV1(normalized) ? normalized : null;
|
|
11786
11754
|
}
|
|
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;
|
|
11755
|
+
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
11756
|
var init_assessment_runtime2 = __esm(() => {
|
|
11811
11757
|
init_timeback3();
|
|
11812
11758
|
init_timeback3();
|
|
@@ -11816,13 +11762,14 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
11816
11762
|
init_timeback3();
|
|
11817
11763
|
init_esm();
|
|
11818
11764
|
init_esm();
|
|
11765
|
+
init_src();
|
|
11766
|
+
init_uuid();
|
|
11767
|
+
init_esm();
|
|
11819
11768
|
init_timeback4();
|
|
11820
11769
|
init_src();
|
|
11821
11770
|
init_timeback3();
|
|
11822
11771
|
init_src();
|
|
11823
11772
|
init_uuid();
|
|
11824
|
-
init_src();
|
|
11825
|
-
init_uuid();
|
|
11826
11773
|
init_esm();
|
|
11827
11774
|
init_src();
|
|
11828
11775
|
init_uuid();
|
|
@@ -12118,6 +12065,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12118
12065
|
ATTEMPT_ALREADY_SUBMITTED: 409,
|
|
12119
12066
|
ASSESSMENT_FLOW_VIOLATION: 409
|
|
12120
12067
|
};
|
|
12068
|
+
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12121
12069
|
RoutingKeySchema = exports_external.string().trim().min(1).max(ROUTING_KEY_MAX_LENGTH);
|
|
12122
12070
|
RoutingResultKeySchema = exports_external.string().trim().min(1).max(ROUTING_RESULT_KEY_MAX_LENGTH);
|
|
12123
12071
|
DiagnosticTrackOutcomeSchema = exports_external.enum(DIAGNOSTIC_TRACK_OUTCOMES);
|
|
@@ -12175,8 +12123,31 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12175
12123
|
resultKey: RoutingResultKeySchema
|
|
12176
12124
|
}).strict()).min(1).max(ROUTING_TERMINAL_LIMIT)
|
|
12177
12125
|
}).strict();
|
|
12178
|
-
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12179
12126
|
POINT_RESPONSE_PATTERN = /^-?\d+(?:\.\d+)? -?\d+(?:\.\d+)?$/;
|
|
12127
|
+
Identity = exports_external.string().min(1).max(512).refine((value) => value.trim() === value);
|
|
12128
|
+
AssessmentTranscriptSchema = exports_external.object({
|
|
12129
|
+
version: exports_external.literal(1),
|
|
12130
|
+
attemptId: Identity,
|
|
12131
|
+
contentRevision: Identity,
|
|
12132
|
+
administrations: exports_external.array(exports_external.object({
|
|
12133
|
+
submissionId: Identity,
|
|
12134
|
+
itemIdentifier: Identity,
|
|
12135
|
+
routingNodeKey: Identity.optional(),
|
|
12136
|
+
responses: exports_external.record(Identity, exports_external.custom(isAssessmentResponseValue))
|
|
12137
|
+
}).strict()).max(1000)
|
|
12138
|
+
}).strict().refine((value) => new TextEncoder().encode(JSON.stringify(value)).length <= 256000, {
|
|
12139
|
+
message: "Assessment transcript exceeds 256 KB"
|
|
12140
|
+
});
|
|
12141
|
+
AssessmentTranscriptError = class AssessmentTranscriptError extends Error {
|
|
12142
|
+
index;
|
|
12143
|
+
code;
|
|
12144
|
+
constructor(message, index = 0, code = "ASSESSMENT_FLOW_VIOLATION") {
|
|
12145
|
+
super(message);
|
|
12146
|
+
this.name = "AssessmentTranscriptError";
|
|
12147
|
+
this.index = index;
|
|
12148
|
+
this.code = code;
|
|
12149
|
+
}
|
|
12150
|
+
};
|
|
12180
12151
|
ZERO_WIDTH_CHARACTERS = /[\u200B\uFEFF\u200C\u200D]/g;
|
|
12181
12152
|
CURLY_SINGLE_QUOTES = /[\u2018\u2019\u201A\u201B]/g;
|
|
12182
12153
|
CURLY_DOUBLE_QUOTES = /[\u201C\u201D\u201E\u201F]/g;
|
|
@@ -12268,6 +12239,10 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12268
12239
|
bankRevision: IdentitySchema,
|
|
12269
12240
|
standard: AssessmentStandardRefSchema
|
|
12270
12241
|
}).strict();
|
|
12242
|
+
FixedChildResultContextV1Schema = exports_external.object({
|
|
12243
|
+
kind: exports_external.literal("fixed"),
|
|
12244
|
+
purpose: exports_external.enum(["mastery", "end_of_course", "diagnostic"])
|
|
12245
|
+
}).strict();
|
|
12271
12246
|
DiagnosticChildResultContextV1Schema = exports_external.object({
|
|
12272
12247
|
kind: exports_external.literal("platform-routed-diagnostic"),
|
|
12273
12248
|
definitionId: IdentitySchema,
|
|
@@ -12287,6 +12262,11 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12287
12262
|
isCorrect: exports_external.boolean().optional(),
|
|
12288
12263
|
context: ReviewChildResultContextV1Schema
|
|
12289
12264
|
}).strict();
|
|
12265
|
+
FixedChildResultPayloadV1Schema = exports_external.object({
|
|
12266
|
+
...CommonPayloadShape,
|
|
12267
|
+
isCorrect: exports_external.boolean().optional(),
|
|
12268
|
+
context: FixedChildResultContextV1Schema
|
|
12269
|
+
}).strict();
|
|
12290
12270
|
DiagnosticChildResultPayloadV1Schema = exports_external.object({
|
|
12291
12271
|
...CommonPayloadShape,
|
|
12292
12272
|
isCorrect: exports_external.boolean(),
|
|
@@ -12302,7 +12282,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12302
12282
|
});
|
|
12303
12283
|
AssessmentChildResultPayloadV1Schema = exports_external.union([
|
|
12304
12284
|
ReviewChildResultPayloadV1Schema,
|
|
12305
|
-
DiagnosticChildResultPayloadV1Schema
|
|
12285
|
+
DiagnosticChildResultPayloadV1Schema,
|
|
12286
|
+
FixedChildResultPayloadV1Schema
|
|
12306
12287
|
]);
|
|
12307
12288
|
AssessmentChildResultCommandV1Schema = exports_external.object({
|
|
12308
12289
|
kind: exports_external.literal(ASSESSMENT_CHILD_RESULT_KIND),
|
|
@@ -12379,9 +12360,19 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12379
12360
|
StartAssessmentRequestBodySchema = exports_external.intersection(StartAssessmentBodySchema, exports_external.object({ preparationReceipt: AssessmentPreparationReceiptSchema.optional() }));
|
|
12380
12361
|
SaveAssessmentBodySchema = exports_external.object({
|
|
12381
12362
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12382
|
-
|
|
12363
|
+
transcript: AssessmentTranscriptSchema
|
|
12364
|
+
});
|
|
12365
|
+
RecordAssessmentProgressBodySchema = SaveAssessmentBodySchema.extend({
|
|
12366
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12367
|
+
itemIdentifier: ResponseKeySchema2
|
|
12383
12368
|
});
|
|
12369
|
+
ResumeAssessmentBodySchema = exports_external.object({
|
|
12370
|
+
transcript: AssessmentTranscriptSchema.optional()
|
|
12371
|
+
});
|
|
12372
|
+
ResumeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(ResumeAssessmentBodySchema.shape);
|
|
12384
12373
|
SubmitAssessmentItemBodySchema = exports_external.object({
|
|
12374
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12375
|
+
transcript: AssessmentTranscriptSchema,
|
|
12385
12376
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12386
12377
|
submissionId: exports_external.string().trim().min(1),
|
|
12387
12378
|
itemIdentifier: ResponseKeySchema2,
|
|
@@ -12389,6 +12380,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12389
12380
|
responses: exports_external.record(ResponseKeySchema2, AssessmentResponseValueSchema2.nullable())
|
|
12390
12381
|
});
|
|
12391
12382
|
SubmitAssessmentBodySchema = exports_external.object({
|
|
12383
|
+
transcript: AssessmentTranscriptSchema,
|
|
12392
12384
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12393
12385
|
submissionId: exports_external.string().trim().min(1),
|
|
12394
12386
|
session: exports_external.custom(isAssessmentSessionTiming, {
|
|
@@ -12407,6 +12399,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12407
12399
|
PrepareRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
|
|
12408
12400
|
StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentRequestBodySchema);
|
|
12409
12401
|
SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
|
|
12402
|
+
RecordRuntimeAssessmentProgressRequestSchema = AssessmentRuntimeIdentitySchema.extend(RecordAssessmentProgressBodySchema.shape);
|
|
12410
12403
|
SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
|
|
12411
12404
|
SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentBodySchema.shape).extend({
|
|
12412
12405
|
appName: exports_external.string().trim().min(1),
|
|
@@ -100206,22 +100199,6 @@ var init_locks = __esm(() => {
|
|
|
100206
100199
|
});
|
|
100207
100200
|
|
|
100208
100201
|
// ../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
100202
|
function normalizeKeys(keys) {
|
|
100226
100203
|
return [...new Set(keys)].toSorted();
|
|
100227
100204
|
}
|
|
@@ -100294,17 +100271,6 @@ var init_assessment_runtime_lock_util = __esm(() => {
|
|
|
100294
100271
|
});
|
|
100295
100272
|
|
|
100296
100273
|
// ../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
100274
|
function assessmentTrackableCourse(integration) {
|
|
100309
100275
|
if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
|
|
100310
100276
|
return null;
|
|
@@ -100370,7 +100336,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100370
100336
|
...input.activityData ? { activityData: input.activityData } : {},
|
|
100371
100337
|
assessment: assessmentPresentationForAttempt(input.assessment, input.attemptId),
|
|
100372
100338
|
responses: metadata2.responses,
|
|
100373
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
100339
|
+
itemSubmissions: assessmentFlowForPurpose(metadata2.purpose) === "item-submit" ? metadata2.itemSubmissions : [],
|
|
100374
100340
|
score: null,
|
|
100375
100341
|
selection
|
|
100376
100342
|
};
|
|
@@ -100420,151 +100386,9 @@ async function resolveAvailableAssessmentAttempt(initialDecision, select3, resum
|
|
|
100420
100386
|
}
|
|
100421
100387
|
return { kind: "continue", decision };
|
|
100422
100388
|
}
|
|
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
100389
|
function diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
100458
100390
|
return deterministicUUID(["playcademy:diagnostic-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
100459
100391
|
}
|
|
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
100392
|
function buildAssessmentSubmitResult(input) {
|
|
100569
100393
|
if (!input.metadata.score || !input.metadata.submissionId) {
|
|
100570
100394
|
return null;
|
|
@@ -100950,32 +100774,12 @@ function assessmentFixtureScoringKeys(assessment, questions) {
|
|
|
100950
100774
|
...Object.keys(responseAreas).length > 0 ? { responseAreas } : {}
|
|
100951
100775
|
};
|
|
100952
100776
|
}
|
|
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
100777
|
function validateAssessmentResponseUpdate(assessment, update2) {
|
|
100962
100778
|
const message = assessmentResponseUpdateValidationMessage(assessment, update2);
|
|
100963
100779
|
if (message) {
|
|
100964
100780
|
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100965
100781
|
}
|
|
100966
100782
|
}
|
|
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
100783
|
function assessmentScoringRequests(assessment, responses) {
|
|
100980
100784
|
return assessment.items.map((item) => ({
|
|
100981
100785
|
itemIdentifier: item.identifier,
|
|
@@ -101015,6 +100819,21 @@ function assessmentScoreFromPercentage(percentage, possible) {
|
|
|
101015
100819
|
const earned = Math.round(normalized * possible * 1e6) / 1e6;
|
|
101016
100820
|
return { earned, possible, normalized };
|
|
101017
100821
|
}
|
|
100822
|
+
function buildAssessmentResultCheckpoint(input) {
|
|
100823
|
+
return {
|
|
100824
|
+
status: input.result.status,
|
|
100825
|
+
assessmentLineItem: input.result.assessmentLineItem,
|
|
100826
|
+
student: input.result.student,
|
|
100827
|
+
score: input.result.score ?? 0,
|
|
100828
|
+
scoreDate: input.result.scoreDate,
|
|
100829
|
+
...ASSESSMENT_ATTEMPT_OPEN,
|
|
100830
|
+
metadata: mergeAssessmentRuntimeMetadata(input.result.metadata, {
|
|
100831
|
+
...input.metadata,
|
|
100832
|
+
transcript: input.transcript,
|
|
100833
|
+
updatedAt: input.timestamp
|
|
100834
|
+
})
|
|
100835
|
+
};
|
|
100836
|
+
}
|
|
101018
100837
|
function buildAssessmentResultSubmission(input) {
|
|
101019
100838
|
const metadata2 = {
|
|
101020
100839
|
...input.metadata,
|
|
@@ -102419,6 +102238,183 @@ var init_timeback_assessment_artifacts_util = __esm(() => {
|
|
|
102419
102238
|
init_timeback_review_mapping_util();
|
|
102420
102239
|
});
|
|
102421
102240
|
|
|
102241
|
+
// ../api-core/src/utils/timeback-assessment-scoring.util.ts
|
|
102242
|
+
async function scoreCachedAssessmentItems(input) {
|
|
102243
|
+
const selected = input.assessment.items.filter((item) => input.itemIdentifiers.has(item.identifier));
|
|
102244
|
+
const keys = new Map(await Promise.all(selected.map(async (item) => [
|
|
102245
|
+
item.identifier,
|
|
102246
|
+
await sha256Hex(canonicalJson2({
|
|
102247
|
+
identity: input.artifactIdentity,
|
|
102248
|
+
purpose: input.purpose,
|
|
102249
|
+
itemIdentifier: item.identifier,
|
|
102250
|
+
responses: input.responses[item.identifier] ?? {},
|
|
102251
|
+
strategy: input.artifact?.items.find((candidate) => candidate.itemIdentifier === item.identifier)?.strategy ?? "qti"
|
|
102252
|
+
}))
|
|
102253
|
+
])));
|
|
102254
|
+
const scored = new Map;
|
|
102255
|
+
for (const item of selected) {
|
|
102256
|
+
const cached3 = input.cache.get(keys.get(item.identifier));
|
|
102257
|
+
if (cached3) {
|
|
102258
|
+
scored.set(item.identifier, cached3);
|
|
102259
|
+
}
|
|
102260
|
+
}
|
|
102261
|
+
const missing = new Set(selected.filter((item) => !scored.has(item.identifier)).map((item) => item.identifier));
|
|
102262
|
+
const fresh = await scoreAssessmentItems({ ...input, itemIdentifiers: missing });
|
|
102263
|
+
for (const result of fresh) {
|
|
102264
|
+
input.cache.set(keys.get(result.itemIdentifier), result);
|
|
102265
|
+
scored.set(result.itemIdentifier, result);
|
|
102266
|
+
}
|
|
102267
|
+
return selected.map((item) => scored.get(item.identifier));
|
|
102268
|
+
}
|
|
102269
|
+
async function scoreAssessmentItems({
|
|
102270
|
+
assessment,
|
|
102271
|
+
responses,
|
|
102272
|
+
artifactIdentity,
|
|
102273
|
+
purpose,
|
|
102274
|
+
itemIdentifiers,
|
|
102275
|
+
artifact,
|
|
102276
|
+
processResponse,
|
|
102277
|
+
onEvent
|
|
102278
|
+
}) {
|
|
102279
|
+
const scoringItems = assessmentScoringRequests(assessment, responses).filter((item) => !itemIdentifiers || itemIdentifiers.has(item.itemIdentifier));
|
|
102280
|
+
const artifactItems = new Map(artifact?.items.map((item) => [item.itemIdentifier, item]));
|
|
102281
|
+
const scored = new Map;
|
|
102282
|
+
const qti2 = [];
|
|
102283
|
+
for (const item of scoringItems) {
|
|
102284
|
+
const artifactItem = artifactItems.get(item.itemIdentifier);
|
|
102285
|
+
let fallback2 = null;
|
|
102286
|
+
if (!artifact) {
|
|
102287
|
+
fallback2 = { reason: "artifact-unavailable" };
|
|
102288
|
+
} else if (!artifactItem) {
|
|
102289
|
+
fallback2 = { reason: "artifact-item-missing" };
|
|
102290
|
+
} else if (artifactItem.strategy === "qti") {
|
|
102291
|
+
fallback2 = { reason: "artifact-qti", artifactReason: artifactItem.reason };
|
|
102292
|
+
} else {
|
|
102293
|
+
const startedAt = Date.now();
|
|
102294
|
+
try {
|
|
102295
|
+
scored.set(item.itemIdentifier, {
|
|
102296
|
+
...scorePlatformAssessmentItem(artifactItem, responses[item.itemIdentifier]),
|
|
102297
|
+
grading: PLATFORM_CHILD_GRADING
|
|
102298
|
+
});
|
|
102299
|
+
onEvent("assessment.item_scoring", {
|
|
102300
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102301
|
+
"app.assessment.purpose": purpose,
|
|
102302
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102303
|
+
"app.assessment.grading_source": "platform-artifact",
|
|
102304
|
+
"app.assessment.grading_duration_ms": Date.now() - startedAt
|
|
102305
|
+
});
|
|
102306
|
+
} catch (error88) {
|
|
102307
|
+
fallback2 = { reason: "platform-grader-error", error: error88 };
|
|
102308
|
+
}
|
|
102309
|
+
}
|
|
102310
|
+
if (fallback2) {
|
|
102311
|
+
qti2.push({ item, ...fallback2 });
|
|
102312
|
+
}
|
|
102313
|
+
}
|
|
102314
|
+
if (qti2.length > 0) {
|
|
102315
|
+
const fallbackTasks = qti2.flatMap(({ item, reason, artifactReason, error: graderError }) => {
|
|
102316
|
+
const interactionResults = [];
|
|
102317
|
+
let startedAt;
|
|
102318
|
+
let completedInteractions = 0;
|
|
102319
|
+
let failed = false;
|
|
102320
|
+
const omittedResponseCount = item.requests.filter((request) => responses[item.itemIdentifier]?.[request.identifier] === undefined).length;
|
|
102321
|
+
let qtiRemoteInteractionCount = 0;
|
|
102322
|
+
let syntheticIncorrectInteractionCount = 0;
|
|
102323
|
+
function fallbackEvent(outcome) {
|
|
102324
|
+
onEvent("assessment.item_scoring_fallback", {
|
|
102325
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102326
|
+
"app.assessment.purpose": purpose,
|
|
102327
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102328
|
+
"app.assessment.grading_source": omittedResponseCount > 0 ? "platform-qti-adapter" : "timeback-qti",
|
|
102329
|
+
"app.assessment.fallback_reason": reason,
|
|
102330
|
+
"app.assessment.fallback_duration_ms": startedAt === undefined ? 0 : Date.now() - startedAt,
|
|
102331
|
+
"app.assessment.qti_remote_interaction_count": qtiRemoteInteractionCount,
|
|
102332
|
+
"app.assessment.synthetic_incorrect_interaction_count": syntheticIncorrectInteractionCount,
|
|
102333
|
+
...artifactReason ? { "app.assessment.artifact_fallback_reason": artifactReason } : {},
|
|
102334
|
+
...graderError ? {
|
|
102335
|
+
"app.assessment.platform_grader_exception_type": errorType(graderError)
|
|
102336
|
+
} : {},
|
|
102337
|
+
...outcome
|
|
102338
|
+
});
|
|
102339
|
+
}
|
|
102340
|
+
function completeItem() {
|
|
102341
|
+
const earned = assessmentItemEarnedScore(interactionResults.map((result) => Number.isFinite(result.score) ? result.score : 0), item.maxScore);
|
|
102342
|
+
const possible = item.maxScore;
|
|
102343
|
+
fallbackEvent({ "app.assessment.fallback_outcome": "succeeded" });
|
|
102344
|
+
scored.set(item.itemIdentifier, {
|
|
102345
|
+
itemIdentifier: item.itemIdentifier,
|
|
102346
|
+
score: {
|
|
102347
|
+
earned,
|
|
102348
|
+
possible,
|
|
102349
|
+
normalized: possible === 0 ? 0 : earned / possible
|
|
102350
|
+
},
|
|
102351
|
+
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
102352
|
+
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
102353
|
+
});
|
|
102354
|
+
}
|
|
102355
|
+
if (item.requests.length === 0) {
|
|
102356
|
+
completeItem();
|
|
102357
|
+
return [];
|
|
102358
|
+
}
|
|
102359
|
+
return item.requests.map((request, index2) => async () => {
|
|
102360
|
+
startedAt ??= Date.now();
|
|
102361
|
+
try {
|
|
102362
|
+
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
102363
|
+
if (response === undefined) {
|
|
102364
|
+
syntheticIncorrectInteractionCount += 1;
|
|
102365
|
+
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
102366
|
+
} else {
|
|
102367
|
+
qtiRemoteInteractionCount += 1;
|
|
102368
|
+
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
102369
|
+
}
|
|
102370
|
+
completedInteractions += 1;
|
|
102371
|
+
if (completedInteractions === item.requests.length && !failed) {
|
|
102372
|
+
completeItem();
|
|
102373
|
+
}
|
|
102374
|
+
} catch (error88) {
|
|
102375
|
+
if (!failed) {
|
|
102376
|
+
failed = true;
|
|
102377
|
+
fallbackEvent({
|
|
102378
|
+
"app.assessment.fallback_outcome": "failed",
|
|
102379
|
+
"exception.type": errorType(error88),
|
|
102380
|
+
"app.error.message": errorMessage(error88)
|
|
102381
|
+
});
|
|
102382
|
+
}
|
|
102383
|
+
throw error88;
|
|
102384
|
+
}
|
|
102385
|
+
});
|
|
102386
|
+
});
|
|
102387
|
+
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
102388
|
+
}
|
|
102389
|
+
return scoringItems.map((item) => {
|
|
102390
|
+
const result = scored.get(item.itemIdentifier);
|
|
102391
|
+
if (!result) {
|
|
102392
|
+
throw new Error(`Assessment item ${item.itemIdentifier} was not scored`);
|
|
102393
|
+
}
|
|
102394
|
+
return result;
|
|
102395
|
+
});
|
|
102396
|
+
}
|
|
102397
|
+
var QTI_CHILD_GRADING, PLATFORM_QTI_ADAPTER_CHILD_GRADING, PLATFORM_CHILD_GRADING, SCORING_CONCURRENCY = 4;
|
|
102398
|
+
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
102399
|
+
init_assessment_runtime2();
|
|
102400
|
+
init_assessment_artifact_envelope_util();
|
|
102401
|
+
init_timeback_assessment_runtime_util();
|
|
102402
|
+
QTI_CHILD_GRADING = {
|
|
102403
|
+
source: "timeback-qti",
|
|
102404
|
+
graderVersion: "timeback-qti-process-response-v1"
|
|
102405
|
+
};
|
|
102406
|
+
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
102407
|
+
source: "platform-qti-adapter",
|
|
102408
|
+
graderVersion: "platform-qti-adapter-v1",
|
|
102409
|
+
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
102410
|
+
};
|
|
102411
|
+
PLATFORM_CHILD_GRADING = {
|
|
102412
|
+
source: "platform-artifact",
|
|
102413
|
+
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
102414
|
+
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
102415
|
+
};
|
|
102416
|
+
});
|
|
102417
|
+
|
|
102422
102418
|
// ../api-core/src/utils/timeback-assessment-child-result.util.ts
|
|
102423
102419
|
async function buildSubmissionChildResultCommand(input) {
|
|
102424
102420
|
const { submission, context: context2 } = input;
|
|
@@ -102434,12 +102430,9 @@ async function buildSubmissionChildResultCommand(input) {
|
|
|
102434
102430
|
score: submission.score,
|
|
102435
102431
|
grading: input.grading
|
|
102436
102432
|
};
|
|
102437
|
-
if (context2.kind
|
|
102438
|
-
|
|
102439
|
-
|
|
102440
|
-
...typeof isCorrect === "boolean" ? { isCorrect } : {},
|
|
102441
|
-
context: context2
|
|
102442
|
-
}, submission.submittedAt);
|
|
102433
|
+
if (context2.kind !== "platform-routed-diagnostic") {
|
|
102434
|
+
const payload = { ...common2, ...typeof isCorrect === "boolean" ? { isCorrect } : {} };
|
|
102435
|
+
return context2.kind === "review" ? buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt) : buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt);
|
|
102443
102436
|
}
|
|
102444
102437
|
if (typeof isCorrect !== "boolean") {
|
|
102445
102438
|
throw new Error("Diagnostic child results require determinate correctness");
|
|
@@ -102460,11 +102453,7 @@ function assessmentChildLineItemMetadataMatches(key, stored, expected) {
|
|
|
102460
102453
|
if (!Array.isArray(storedStandards) || !Array.isArray(expectedStandards)) {
|
|
102461
102454
|
return false;
|
|
102462
102455
|
}
|
|
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)));
|
|
102456
|
+
return canonicalJson2(reviewDefinitionWithoutStandards(storedDefinition)) === canonicalJson2(reviewDefinitionWithoutStandards(expectedDefinition));
|
|
102468
102457
|
}
|
|
102469
102458
|
function assessmentChildLineItemMatches(stored, expected) {
|
|
102470
102459
|
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 +102511,14 @@ async function classifyAssessmentChildResult(input) {
|
|
|
102522
102511
|
result.scoreScale,
|
|
102523
102512
|
result.scorePercentile,
|
|
102524
102513
|
result.comment,
|
|
102525
|
-
result.learningObjectiveSet
|
|
102526
|
-
result.incomplete,
|
|
102527
|
-
result.late,
|
|
102528
|
-
result.missing
|
|
102514
|
+
result.learningObjectiveSet
|
|
102529
102515
|
];
|
|
102530
|
-
|
|
102516
|
+
const flags2 = [result.incomplete, result.late, result.missing];
|
|
102517
|
+
if (auxiliaryFields.some((value) => value !== undefined && value !== null) || flags2.some((value) => value !== undefined && value !== null && value !== "false")) {
|
|
102531
102518
|
return { outcome: "conflict", reason: "auxiliary-field-mismatch" };
|
|
102532
102519
|
}
|
|
102533
102520
|
return { outcome: "replay" };
|
|
102534
102521
|
}
|
|
102535
|
-
function hasApiStatus(error88, statusCode) {
|
|
102536
|
-
return isApiError(error88) && error88.statusCode === statusCode;
|
|
102537
|
-
}
|
|
102538
102522
|
async function persistAssessmentChildResult(input) {
|
|
102539
102523
|
const { assessmentResults, childLineItemId } = input;
|
|
102540
102524
|
const verification2 = await verifyAssessmentChildResultCommand(input.command);
|
|
@@ -102543,38 +102527,127 @@ async function persistAssessmentChildResult(input) {
|
|
|
102543
102527
|
}
|
|
102544
102528
|
const { command } = verification2;
|
|
102545
102529
|
const resultId = await assessmentChildResultId(command.payload.attempt.attemptId, command.payload.administration.submissionId);
|
|
102546
|
-
|
|
102547
|
-
|
|
102548
|
-
|
|
102530
|
+
await assessmentResults.upsert(resultId, buildAssessmentChildResultUpdate(command, childLineItemId));
|
|
102531
|
+
}
|
|
102532
|
+
function assessmentTranscriptChildLineItemId(context2, itemIdentifier) {
|
|
102533
|
+
return context2.metadata.purpose === "review" ? reviewQuestionLineItemId(context2.parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(context2.parentLineItemId, itemIdentifier);
|
|
102534
|
+
}
|
|
102535
|
+
async function reconcileAssessmentTranscriptChildren({
|
|
102536
|
+
context: context2,
|
|
102537
|
+
graded,
|
|
102538
|
+
only,
|
|
102539
|
+
assessmentResults,
|
|
102540
|
+
ensureLineItems,
|
|
102541
|
+
onPersisted
|
|
102542
|
+
}) {
|
|
102543
|
+
const metadata2 = graded.metadata;
|
|
102544
|
+
const assessmentKey = metadata2.selectedTest.assessmentKey;
|
|
102545
|
+
if (!assessmentKey) {
|
|
102546
|
+
throw new Error("Assessment context requires a selected assessment key");
|
|
102547
|
+
}
|
|
102548
|
+
const submissions = metadata2.itemSubmissions.filter((submission) => !only || only.has(submission.submissionId));
|
|
102549
|
+
const administered = new Set(submissions.map((submission) => submission.itemIdentifier));
|
|
102550
|
+
await ensureLineItems({
|
|
102551
|
+
items: graded.assessment.items.filter((item) => administered.has(item.identifier))
|
|
102552
|
+
});
|
|
102553
|
+
await runWithConcurrency(submissions, 4, async (submission) => {
|
|
102554
|
+
const index2 = submission.responseVersion - 1;
|
|
102555
|
+
let flow;
|
|
102556
|
+
if (metadata2.purpose === "review") {
|
|
102557
|
+
const selection = metadata2.review.selections.find((item) => item.itemIdentifier === submission.itemIdentifier);
|
|
102558
|
+
flow = {
|
|
102559
|
+
kind: "review",
|
|
102560
|
+
bankRevision: metadata2.review.bankRevision,
|
|
102561
|
+
standard: selection.standard
|
|
102562
|
+
};
|
|
102563
|
+
} else if (metadata2.purpose === "diagnostic" && metadata2.diagnostic) {
|
|
102564
|
+
flow = {
|
|
102565
|
+
kind: "platform-routed-diagnostic",
|
|
102566
|
+
definitionId: metadata2.diagnostic.definitionId,
|
|
102567
|
+
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
102568
|
+
routingRevision: metadata2.diagnostic.routingRevision,
|
|
102569
|
+
transition: metadata2.diagnostic.ledger[index2]
|
|
102570
|
+
};
|
|
102571
|
+
} else {
|
|
102572
|
+
flow = { kind: "fixed", purpose: metadata2.purpose };
|
|
102573
|
+
}
|
|
102574
|
+
const command = await buildSubmissionChildResultCommand({
|
|
102575
|
+
attempt: {
|
|
102576
|
+
attemptId: context2.attemptId,
|
|
102577
|
+
gameId: context2.gameId,
|
|
102578
|
+
studentId: context2.studentId,
|
|
102579
|
+
activityId: metadata2.activityId,
|
|
102580
|
+
courseId: metadata2.courseId,
|
|
102581
|
+
integrationId: metadata2.integrationId,
|
|
102582
|
+
enrollmentId: metadata2.enrollmentId,
|
|
102583
|
+
selectedTest: { ...metadata2.selectedTest, assessmentKey }
|
|
102584
|
+
},
|
|
102585
|
+
submission,
|
|
102586
|
+
responses: graded.transcript.administrations[index2].responses,
|
|
102587
|
+
grading: submission.grading ?? QTI_CHILD_GRADING,
|
|
102588
|
+
context: flow
|
|
102589
|
+
});
|
|
102590
|
+
await persistAssessmentChildResult({
|
|
102591
|
+
assessmentResults,
|
|
102549
102592
|
command,
|
|
102550
|
-
childLineItemId
|
|
102593
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, submission.itemIdentifier)
|
|
102551
102594
|
});
|
|
102552
|
-
|
|
102595
|
+
onPersisted?.(submission.submissionId);
|
|
102596
|
+
});
|
|
102597
|
+
}
|
|
102598
|
+
async function recoveredAssessmentAdministration(context2, result) {
|
|
102599
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
102600
|
+
if (!verified.ok) {
|
|
102601
|
+
return null;
|
|
102553
102602
|
}
|
|
102554
|
-
|
|
102555
|
-
|
|
102556
|
-
|
|
102557
|
-
|
|
102558
|
-
|
|
102603
|
+
const { command } = verified;
|
|
102604
|
+
const { attempt, administration, responses, context: flow } = command.payload;
|
|
102605
|
+
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)) {
|
|
102606
|
+
return null;
|
|
102607
|
+
}
|
|
102608
|
+
const disposition = await classifyAssessmentChildResult({
|
|
102609
|
+
result,
|
|
102610
|
+
command,
|
|
102611
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, administration.itemIdentifier)
|
|
102612
|
+
});
|
|
102613
|
+
if (disposition.outcome !== "replay") {
|
|
102614
|
+
return null;
|
|
102615
|
+
}
|
|
102616
|
+
return {
|
|
102617
|
+
sequence: administration.responseVersion,
|
|
102618
|
+
entry: {
|
|
102619
|
+
submissionId: administration.submissionId,
|
|
102620
|
+
itemIdentifier: administration.itemIdentifier,
|
|
102621
|
+
responses,
|
|
102622
|
+
...flow.kind === "platform-routed-diagnostic" ? { routingNodeKey: flow.transition.routingNodeKey } : {}
|
|
102623
|
+
}
|
|
102624
|
+
};
|
|
102625
|
+
}
|
|
102626
|
+
async function recoverAssessmentTranscript(context2, results) {
|
|
102627
|
+
const entries = new Map;
|
|
102628
|
+
for (const result of results) {
|
|
102629
|
+
const recovered = await recoveredAssessmentAdministration(context2, result);
|
|
102630
|
+
if (recovered) {
|
|
102631
|
+
const atPosition = entries.get(recovered.sequence) ?? new Map;
|
|
102632
|
+
atPosition.set(recovered.entry.submissionId, recovered.entry);
|
|
102633
|
+
entries.set(recovered.sequence, atPosition);
|
|
102559
102634
|
}
|
|
102560
102635
|
}
|
|
102561
|
-
|
|
102562
|
-
|
|
102563
|
-
|
|
102564
|
-
|
|
102565
|
-
|
|
102566
|
-
return { outcome: "created", resultId };
|
|
102567
|
-
} catch (error88) {
|
|
102568
|
-
if (!hasApiStatus(error88, 409)) {
|
|
102569
|
-
throw error88;
|
|
102636
|
+
const transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision);
|
|
102637
|
+
for (let sequence2 = 1;sequence2 <= entries.size; sequence2 += 1) {
|
|
102638
|
+
const candidates = entries.get(sequence2);
|
|
102639
|
+
if (!candidates || candidates.size !== 1) {
|
|
102640
|
+
break;
|
|
102570
102641
|
}
|
|
102642
|
+
transcript.administrations.push(candidates.values().next().value);
|
|
102571
102643
|
}
|
|
102572
|
-
return
|
|
102644
|
+
return transcript;
|
|
102573
102645
|
}
|
|
102574
|
-
var init_timeback_assessment_child_result_util = __esm(
|
|
102646
|
+
var init_timeback_assessment_child_result_util = __esm(() => {
|
|
102575
102647
|
init_src();
|
|
102576
102648
|
init_assessment_runtime2();
|
|
102577
|
-
|
|
102649
|
+
init_timeback_assessment_runtime_util();
|
|
102650
|
+
init_timeback_assessment_scoring_util();
|
|
102578
102651
|
});
|
|
102579
102652
|
|
|
102580
102653
|
// ../api-core/src/utils/timeback-assessment-preparation.util.ts
|
|
@@ -102679,6 +102752,7 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102679
102752
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
102680
102753
|
activityId: plan.activityId,
|
|
102681
102754
|
courseId: plan.courseId,
|
|
102755
|
+
courseName: plan.courseName,
|
|
102682
102756
|
integrationId: plan.integrationId,
|
|
102683
102757
|
enrollmentId: plan.enrollmentId,
|
|
102684
102758
|
selectedTest: plan.selectedTest,
|
|
@@ -102709,7 +102783,6 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102709
102783
|
testName,
|
|
102710
102784
|
routingRevision: plan.diagnostic.routingRevision,
|
|
102711
102785
|
ledger: [],
|
|
102712
|
-
routingSnapshots: [],
|
|
102713
102786
|
state: plan.diagnostic.initialState
|
|
102714
102787
|
}
|
|
102715
102788
|
};
|
|
@@ -103004,127 +103077,6 @@ var init_timeback_assessment_publication_util = __esm(() => {
|
|
|
103004
103077
|
MANAGED_ASSET_URL = new RegExp(`https?://[^\\s"')]+/(${ASSESSMENT_ASSET_KEY_PREFIX}v1/sha256/[a-f0-9]{64}\\.[a-z0-9]+)`, "gi");
|
|
103005
103078
|
});
|
|
103006
103079
|
|
|
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
103080
|
// ../api-core/src/utils/timeback-assessment-routing.util.ts
|
|
103129
103081
|
function diagnosticRoutingFallbackReason(artifact, routingRevision) {
|
|
103130
103082
|
if (!artifact) {
|
|
@@ -103274,154 +103226,255 @@ var init_timeback_assessment_rules_util = __esm(() => {
|
|
|
103274
103226
|
init_errors2();
|
|
103275
103227
|
});
|
|
103276
103228
|
|
|
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
|
-
|
|
103229
|
+
// ../api-core/src/utils/timeback-assessment-replay.util.ts
|
|
103230
|
+
function diagnosticReceipt(submission, ledgerEntry) {
|
|
103231
|
+
return {
|
|
103232
|
+
submissionId: submission.submissionId,
|
|
103233
|
+
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
103234
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103235
|
+
submittedAt: submission.submittedAt,
|
|
103236
|
+
responseVersion: submission.responseVersion,
|
|
103237
|
+
answered: submission.answered,
|
|
103238
|
+
score: submission.score,
|
|
103239
|
+
isCorrect: ledgerEntry.isCorrect
|
|
103240
|
+
};
|
|
103241
|
+
}
|
|
103242
|
+
|
|
103243
|
+
// ../api-core/src/utils/timeback-assessment-transcript.util.ts
|
|
103244
|
+
function parseAssessmentTranscript(value) {
|
|
103245
|
+
const parsed = AssessmentTranscriptSchema.safeParse(value);
|
|
103246
|
+
if (!parsed.success) {
|
|
103247
|
+
throw new ValidationError("Invalid assessment transcript");
|
|
103248
|
+
}
|
|
103249
|
+
return parsed.data;
|
|
103250
|
+
}
|
|
103251
|
+
function assessmentTokenMetadata(metadata2) {
|
|
103252
|
+
const context2 = { ...metadata2, updatedAt: metadata2.startedAt };
|
|
103253
|
+
delete context2.transcript;
|
|
103254
|
+
return context2;
|
|
103255
|
+
}
|
|
103256
|
+
function requireAssessmentTokenContext(context2, scope) {
|
|
103257
|
+
if (!context2 || context2.gameId !== scope.gameId || context2.studentId !== scope.studentId || context2.attemptId !== scope.attemptId) {
|
|
103258
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(scope.attemptId));
|
|
103259
|
+
}
|
|
103260
|
+
return context2;
|
|
103261
|
+
}
|
|
103262
|
+
function requireAssessmentFeedbackContext(tokenContext, scope) {
|
|
103263
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103264
|
+
const metadata2 = context2.metadata;
|
|
103265
|
+
if (assessmentFlowForPurpose(metadata2.purpose) === "attempt-submit" && !(metadata2.purpose === "diagnostic" && metadata2.diagnostic)) {
|
|
103266
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("This assessment accepts a complete attempt, not per-item feedback."));
|
|
103267
|
+
}
|
|
103268
|
+
return context2;
|
|
103269
|
+
}
|
|
103270
|
+
function prepareAssessmentProgress(tokenContext, scope, input) {
|
|
103271
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103272
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
103273
|
+
const progress = assessmentProgressAdministration(context2.metadata.purpose, {
|
|
103274
|
+
...input,
|
|
103275
|
+
transcript
|
|
103276
|
+
});
|
|
103277
|
+
if (!progress.ok) {
|
|
103278
|
+
throw AssessmentRuntimeError.from(progress.failure);
|
|
103279
|
+
}
|
|
103280
|
+
return { context: context2, transcript, submissionId: progress.submissionId };
|
|
103281
|
+
}
|
|
103282
|
+
function prepareAssessmentItemTranscript(assessment, input) {
|
|
103283
|
+
validateAssessmentResponseUpdate(assessment, { [input.itemIdentifier]: input.responses });
|
|
103284
|
+
const appended = appendAssessmentAdministration(input.transcript, input);
|
|
103285
|
+
if (!appended.ok) {
|
|
103286
|
+
throw AssessmentRuntimeError.from(appended.failure);
|
|
103287
|
+
}
|
|
103288
|
+
return appended;
|
|
103289
|
+
}
|
|
103290
|
+
function prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal = false) {
|
|
103291
|
+
return prepareAssessmentTranscript({
|
|
103292
|
+
transcript,
|
|
103293
|
+
attemptId: context2.attemptId,
|
|
103294
|
+
contentRevision: context2.metadata.selectedTest.contentRevision,
|
|
103295
|
+
assessment,
|
|
103296
|
+
routed: context2.metadata.purpose === "diagnostic" && context2.metadata.diagnostic !== undefined,
|
|
103297
|
+
allowIncomplete: !terminal && assessmentFlowForPurpose(context2.metadata.purpose) === "attempt-submit"
|
|
103298
|
+
});
|
|
103299
|
+
}
|
|
103300
|
+
function requireTranscriptWriteAccess(attemptId, integrationStatus) {
|
|
103301
|
+
if (integrationStatus === "deactivated") {
|
|
103302
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
103303
|
+
}
|
|
103304
|
+
}
|
|
103305
|
+
function requireTranscriptVersion(transcript, expectedResponseVersion) {
|
|
103306
|
+
if (expectedResponseVersion !== transcript.administrations.length) {
|
|
103307
|
+
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, transcript.administrations.length));
|
|
103308
|
+
}
|
|
103309
|
+
}
|
|
103310
|
+
function requireAssessmentTranscriptCheckpoint(attempt, integrationStatus, input) {
|
|
103311
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103312
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103313
|
+
if (!isAssessmentAttemptOpen({ ...attempt, inProgress: attempt.inProgress ?? "" })) {
|
|
103314
|
+
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(attempt.sourcedId));
|
|
103315
|
+
}
|
|
103316
|
+
}
|
|
103317
|
+
function assessmentTranscriptRequestHash(input) {
|
|
103318
|
+
return sha256Hex(canonicalJson2({
|
|
103319
|
+
transcript: input.transcript,
|
|
103320
|
+
...input.session ? { session: input.session } : {}
|
|
103321
|
+
}));
|
|
103322
|
+
}
|
|
103323
|
+
function resolveAssessmentTranscriptSubmission(attempt, metadata2, integrationStatus, input, requestHash) {
|
|
103324
|
+
const disposition = classifyAssessmentSubmission({
|
|
103325
|
+
inProgress: attempt.inProgress ?? "",
|
|
103326
|
+
scoreStatus: attempt.scoreStatus,
|
|
103327
|
+
submissionId: metadata2.submissionId
|
|
103328
|
+
}, input.submissionId);
|
|
103329
|
+
if (disposition === "replay" && metadata2.completion?.requestHash === requestHash) {
|
|
103330
|
+
return "replay";
|
|
103331
|
+
}
|
|
103332
|
+
if (disposition === "reject") {
|
|
103333
|
+
throw AssessmentRuntimeError.from(assessmentAlreadySubmitted(attempt.sourcedId));
|
|
103334
|
+
}
|
|
103335
|
+
if (disposition !== "submit") {
|
|
103336
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("A different completion was already accepted for this attempt."));
|
|
103337
|
+
}
|
|
103338
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103339
|
+
if (input.session && input.session.runId !== attempt.sourcedId) {
|
|
103340
|
+
throw new ValidationError("Assessment session runId must match the attemptId.");
|
|
103341
|
+
}
|
|
103342
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103343
|
+
return "submit";
|
|
103344
|
+
}
|
|
103345
|
+
function buildGradedAssessmentTranscript(input) {
|
|
103346
|
+
const { context: context2, prepared, itemResults, assessment, submittedAt, manifest } = input;
|
|
103347
|
+
const completed = completeAssessmentTranscript({
|
|
103348
|
+
transcript: prepared.transcript,
|
|
103349
|
+
scores: itemResults,
|
|
103350
|
+
purpose: context2.metadata.purpose,
|
|
103351
|
+
itemCount: assessment.items.length,
|
|
103352
|
+
submittedAt,
|
|
103353
|
+
manifest,
|
|
103354
|
+
terminal: input.terminal
|
|
103355
|
+
});
|
|
103356
|
+
const common2 = {
|
|
103357
|
+
...context2.metadata,
|
|
103358
|
+
responses: prepared.responses,
|
|
103359
|
+
responseVersion: completed.itemSubmissions.length,
|
|
103360
|
+
itemSubmissions: completed.itemSubmissions,
|
|
103361
|
+
updatedAt: submittedAt,
|
|
103362
|
+
transcript: prepared.transcript
|
|
103363
|
+
};
|
|
103364
|
+
const diagnostic = context2.metadata.purpose === "diagnostic" ? context2.metadata.diagnostic : undefined;
|
|
103365
|
+
const metadata2 = diagnostic ? {
|
|
103366
|
+
...common2,
|
|
103367
|
+
purpose: "diagnostic",
|
|
103368
|
+
diagnostic: { ...diagnostic, ledger: completed.ledger, state: completed.state }
|
|
103369
|
+
} : common2;
|
|
103370
|
+
return {
|
|
103371
|
+
metadata: metadata2,
|
|
103372
|
+
transcript: prepared.transcript,
|
|
103373
|
+
assessment,
|
|
103374
|
+
itemResults,
|
|
103375
|
+
manifest,
|
|
103376
|
+
score: aggregateAssessmentScore(itemResults.map((item) => item.score.earned), itemResults.reduce((sum, item) => sum + item.score.possible, 0)),
|
|
103377
|
+
correctQuestions: countCorrectQuestions(itemResults.map((item) => item.isCorrect))
|
|
103378
|
+
};
|
|
103379
|
+
}
|
|
103380
|
+
function assessmentTranscriptRuntimeError(error88) {
|
|
103381
|
+
return error88 instanceof AssessmentTranscriptError ? AssessmentRuntimeError.from({
|
|
103382
|
+
code: error88.code,
|
|
103383
|
+
message: error88.message,
|
|
103384
|
+
details: { transcriptIndex: error88.index }
|
|
103385
|
+
}) : error88;
|
|
103386
|
+
}
|
|
103387
|
+
function buildAssessmentTranscriptFeedback(attemptId, graded, index2) {
|
|
103388
|
+
const { metadata: metadata2 } = graded;
|
|
103389
|
+
const submission = metadata2.itemSubmissions[index2];
|
|
103390
|
+
const common2 = {
|
|
103391
|
+
attemptId,
|
|
103392
|
+
responseVersion: metadata2.responseVersion,
|
|
103393
|
+
status: "in_progress",
|
|
103394
|
+
transcript: graded.transcript
|
|
103395
|
+
};
|
|
103396
|
+
return metadata2.purpose === "diagnostic" && metadata2.diagnostic ? {
|
|
103397
|
+
...common2,
|
|
103398
|
+
routing: projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, metadata2.diagnostic.state),
|
|
103399
|
+
submission: diagnosticReceipt(submission, metadata2.diagnostic.ledger[index2])
|
|
103400
|
+
} : {
|
|
103401
|
+
...common2,
|
|
103402
|
+
responses: metadata2.responses,
|
|
103403
|
+
itemSubmissions: metadata2.itemSubmissions,
|
|
103404
|
+
submission
|
|
103405
|
+
};
|
|
103406
|
+
}
|
|
103407
|
+
async function resumeAssessmentTranscript(input) {
|
|
103408
|
+
const retained = input.suppliedTranscript ?? input.checkpointTranscript;
|
|
103409
|
+
let transcript = retained ?? await input.recover();
|
|
103410
|
+
while (true) {
|
|
103411
|
+
try {
|
|
103412
|
+
return await input.grade(transcript);
|
|
103413
|
+
} catch (error88) {
|
|
103414
|
+
const index2 = error88 instanceof AssessmentRuntimeError && isRecord(error88.details) ? error88.details.transcriptIndex : undefined;
|
|
103415
|
+
if (retained !== undefined || typeof index2 !== "number" || index2 < 0 || index2 >= transcript.administrations.length) {
|
|
103416
|
+
throw error88;
|
|
103317
103417
|
}
|
|
103318
|
-
|
|
103319
|
-
|
|
103320
|
-
|
|
103418
|
+
transcript = {
|
|
103419
|
+
...transcript,
|
|
103420
|
+
administrations: transcript.administrations.slice(0, index2)
|
|
103421
|
+
};
|
|
103321
103422
|
}
|
|
103322
103423
|
}
|
|
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
|
-
const
|
|
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 [];
|
|
103424
|
+
}
|
|
103425
|
+
function assessmentReviewTranscriptOutcomes(input) {
|
|
103426
|
+
const resultByItem = new Map(input.itemResults.map((item) => [item.itemIdentifier, item]));
|
|
103427
|
+
const administered = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
103428
|
+
return input.metadata.review.selections.filter((selection) => administered.has(selection.itemIdentifier)).map((selection) => {
|
|
103429
|
+
const result = resultByItem.get(selection.itemIdentifier);
|
|
103430
|
+
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
103431
|
+
if (!result || !submission) {
|
|
103432
|
+
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
103433
|
+
}
|
|
103434
|
+
return {
|
|
103435
|
+
itemIdentifier: selection.itemIdentifier,
|
|
103436
|
+
standard: selection.standard,
|
|
103437
|
+
answered: submission.answered,
|
|
103438
|
+
score: result.score,
|
|
103439
|
+
isCorrect: result.isCorrect
|
|
103440
|
+
};
|
|
103441
|
+
});
|
|
103442
|
+
}
|
|
103443
|
+
function assessmentReviewTranscriptExposures(input) {
|
|
103444
|
+
const exposures = [];
|
|
103445
|
+
const authoritative = new Set;
|
|
103446
|
+
for (const { result, metadata: metadata2 } of input.attempts) {
|
|
103447
|
+
const state = { ...result, inProgress: result.inProgress ?? "" };
|
|
103448
|
+
const settled = isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
103449
|
+
if (metadata2.purpose === "review" && (metadata2.transcript !== undefined || settled)) {
|
|
103450
|
+
authoritative.add(result.sourcedId);
|
|
103451
|
+
if (metadata2.review.bankRevision === input.bankRevision) {
|
|
103452
|
+
const accepted = settled ? metadata2.itemSubmissions.map((submission) => ({
|
|
103453
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103454
|
+
deliveredAt: submission.submittedAt
|
|
103455
|
+
})) : metadata2.transcript.administrations.map((entry) => ({
|
|
103456
|
+
itemIdentifier: entry.itemIdentifier,
|
|
103457
|
+
deliveredAt: metadata2.updatedAt
|
|
103458
|
+
}));
|
|
103459
|
+
exposures.push(...accepted);
|
|
103367
103460
|
}
|
|
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());
|
|
103461
|
+
}
|
|
103397
103462
|
}
|
|
103398
|
-
|
|
103399
|
-
const
|
|
103400
|
-
if (!
|
|
103401
|
-
|
|
103463
|
+
for (const command of input.children) {
|
|
103464
|
+
const { attempt, administration, context: context2 } = command.payload;
|
|
103465
|
+
if (!authoritative.has(attempt.attemptId) && context2.kind === "review" && context2.bankRevision === input.bankRevision) {
|
|
103466
|
+
exposures.push({
|
|
103467
|
+
itemIdentifier: administration.itemIdentifier,
|
|
103468
|
+
deliveredAt: command.recordedAt
|
|
103469
|
+
});
|
|
103402
103470
|
}
|
|
103403
|
-
|
|
103404
|
-
|
|
103471
|
+
}
|
|
103472
|
+
return exposures;
|
|
103405
103473
|
}
|
|
103406
|
-
var
|
|
103407
|
-
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
103474
|
+
var init_timeback_assessment_transcript_util = __esm(() => {
|
|
103408
103475
|
init_assessment_runtime2();
|
|
103409
|
-
|
|
103476
|
+
init_errors2();
|
|
103410
103477
|
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
103478
|
});
|
|
103426
103479
|
|
|
103427
103480
|
// ../api-core/src/utils/timeback-qti-hydration.util.ts
|
|
@@ -103508,20 +103561,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103508
103561
|
init_assessment_artifact_runtime_util();
|
|
103509
103562
|
init_assessment_runtime_lock_util();
|
|
103510
103563
|
init_timeback_assessment_artifacts_util();
|
|
103564
|
+
init_timeback_assessment_child_result_util();
|
|
103511
103565
|
init_timeback_assessment_preparation_util();
|
|
103512
103566
|
init_timeback_assessment_publication_util();
|
|
103513
|
-
init_timeback_assessment_reconciliation_util();
|
|
103514
|
-
init_timeback_assessment_replay_util();
|
|
103515
103567
|
init_timeback_assessment_routing_util();
|
|
103516
103568
|
init_timeback_assessment_rules_util();
|
|
103517
103569
|
init_timeback_assessment_runtime_util();
|
|
103518
103570
|
init_timeback_assessment_scoring_util();
|
|
103571
|
+
init_timeback_assessment_transcript_util();
|
|
103519
103572
|
init_timeback_qti_hydration_util();
|
|
103520
103573
|
await __promiseAll([
|
|
103521
103574
|
init_dist5(),
|
|
103522
103575
|
init_errors8(),
|
|
103523
|
-
init_assessment_artifact_loader_util()
|
|
103524
|
-
init_timeback_assessment_child_result_util()
|
|
103576
|
+
init_assessment_artifact_loader_util()
|
|
103525
103577
|
]);
|
|
103526
103578
|
AssessmentPreparationPrerequisiteError = class AssessmentPreparationPrerequisiteError extends Error {
|
|
103527
103579
|
original;
|
|
@@ -103536,7 +103588,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103536
103588
|
static PREPARATION_IN_FLIGHT_LIMIT = 32;
|
|
103537
103589
|
static ASSESSMENT_CACHE_TTL_MS = 60000;
|
|
103538
103590
|
static EXPORT_CONCURRENCY = 4;
|
|
103539
|
-
static ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS = 3;
|
|
103540
103591
|
static ASSESSMENT_VERSION_CONFIRMATION_DELAY_MS = 25;
|
|
103541
103592
|
static SCORING_CONCURRENCY = 4;
|
|
103542
103593
|
static STALE_AWARD_AGE_MS = 15 * 60 * 1000;
|
|
@@ -103554,6 +103605,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103554
103605
|
maxSize: TimebackAssessmentRuntimeService.ASSESSMENT_CACHE_LIMIT
|
|
103555
103606
|
});
|
|
103556
103607
|
artifactLoader;
|
|
103608
|
+
itemScoreCache = new TimebackCache({
|
|
103609
|
+
defaultTTL: 5 * 60000,
|
|
103610
|
+
maxSize: 512
|
|
103611
|
+
});
|
|
103612
|
+
childLineItemCache = new TimebackCache({
|
|
103613
|
+
defaultTTL: 5 * 60000,
|
|
103614
|
+
maxSize: 512
|
|
103615
|
+
});
|
|
103557
103616
|
preparations = new AssessmentPreparationFlights(TimebackAssessmentRuntimeService.PREPARATION_IN_FLIGHT_LIMIT);
|
|
103558
103617
|
constructor(deps) {
|
|
103559
103618
|
this.deps = deps;
|
|
@@ -103680,6 +103739,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103680
103739
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
103681
103740
|
activityId: input.activityId,
|
|
103682
103741
|
courseId: context2.integration.courseId,
|
|
103742
|
+
courseName: context2.enrollment.course.title,
|
|
103683
103743
|
integrationId: context2.integration.id,
|
|
103684
103744
|
enrollmentId: context2.enrollment.id,
|
|
103685
103745
|
selectedTest: {
|
|
@@ -103736,7 +103796,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103736
103796
|
enrollmentId: context2.enrollment.id
|
|
103737
103797
|
};
|
|
103738
103798
|
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);
|
|
103799
|
+
const listing = await this.attemptListing(results, attemptScope);
|
|
103740
103800
|
const decision = selectRuntimeAssessment(tests, [
|
|
103741
103801
|
...this.attemptsMatchingRequest(listing.attempts, this.startRequestFingerprint(params.input)).values()
|
|
103742
103802
|
].map((entry) => entry.selection));
|
|
@@ -103759,6 +103819,18 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103759
103819
|
let prepared;
|
|
103760
103820
|
try {
|
|
103761
103821
|
prepared = await this.buildPreparationPlan(params, requestHash, preparation);
|
|
103822
|
+
const assessment = await this.loadPreparedPlanAssessment(prepared.plan);
|
|
103823
|
+
if (!assessment) {
|
|
103824
|
+
throw new Error("Prepared assessment artifact is unavailable");
|
|
103825
|
+
}
|
|
103826
|
+
await this.provisionPreparationPrerequisites(() => this.ensureTranscriptLineItems({
|
|
103827
|
+
version: 1,
|
|
103828
|
+
gameId: prepared.plan.gameId,
|
|
103829
|
+
studentId: prepared.plan.studentId,
|
|
103830
|
+
attemptId: prepared.plan.attemptId,
|
|
103831
|
+
parentLineItemId: prepared.plan.parentLineItemId,
|
|
103832
|
+
metadata: buildPreparedAssessmentMetadata(prepared.plan, new Date().toISOString(), assessment.title)
|
|
103833
|
+
}, assessment));
|
|
103762
103834
|
} catch (error88) {
|
|
103763
103835
|
if (error88 instanceof ValidationError || error88 instanceof AssessmentRuntimeError) {
|
|
103764
103836
|
throw error88;
|
|
@@ -103844,6 +103916,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103844
103916
|
integrationId: context2.integration.id,
|
|
103845
103917
|
enrollmentId: context2.enrollment.id,
|
|
103846
103918
|
courseId: context2.integration.courseId,
|
|
103919
|
+
courseName: context2.enrollment.course.title,
|
|
103847
103920
|
...attempt,
|
|
103848
103921
|
parentLineItemId,
|
|
103849
103922
|
selectedTest: {
|
|
@@ -103884,7 +103957,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103884
103957
|
const selected = selectReviewItems({
|
|
103885
103958
|
request: params.input,
|
|
103886
103959
|
bank: catalog.bank,
|
|
103887
|
-
exposures:
|
|
103960
|
+
exposures: assessmentReviewTranscriptExposures({
|
|
103961
|
+
attempts: listing.attempts.values(),
|
|
103962
|
+
children: listing.reviewChildren.values(),
|
|
103888
103963
|
bankRevision: catalog.bank.bankRevision
|
|
103889
103964
|
})
|
|
103890
103965
|
});
|
|
@@ -103933,6 +104008,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103933
104008
|
integrationId: context2.integration.id,
|
|
103934
104009
|
enrollmentId: context2.enrollment.id,
|
|
103935
104010
|
courseId: context2.integration.courseId,
|
|
104011
|
+
courseName: context2.enrollment.course.title,
|
|
103936
104012
|
...attempt,
|
|
103937
104013
|
parentLineItemId,
|
|
103938
104014
|
selectedTest: {
|
|
@@ -104084,6 +104160,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104084
104160
|
activityId: input.activityId,
|
|
104085
104161
|
purpose: "diagnostic",
|
|
104086
104162
|
courseId: context2.integration.courseId,
|
|
104163
|
+
courseName: context2.enrollment.course.title,
|
|
104087
104164
|
integrationId: context2.integration.id,
|
|
104088
104165
|
enrollmentId: context2.enrollment.id,
|
|
104089
104166
|
selectedTest: {
|
|
@@ -104103,7 +104180,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104103
104180
|
testName: assessment.title,
|
|
104104
104181
|
routingRevision: routing.routingRevision,
|
|
104105
104182
|
ledger: [],
|
|
104106
|
-
routingSnapshots: [],
|
|
104107
104183
|
state: routing.state
|
|
104108
104184
|
}
|
|
104109
104185
|
};
|
|
@@ -104122,7 +104198,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104122
104198
|
})
|
|
104123
104199
|
});
|
|
104124
104200
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104125
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration,
|
|
104201
|
+
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration, {
|
|
104202
|
+
state: routing.state
|
|
104203
|
+
});
|
|
104126
104204
|
});
|
|
104127
104205
|
}
|
|
104128
104206
|
async startReview({
|
|
@@ -104156,7 +104234,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104156
104234
|
this.listAttempts(attemptScope)
|
|
104157
104235
|
]);
|
|
104158
104236
|
const attempts = listing.attempts;
|
|
104159
|
-
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts,
|
|
104237
|
+
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts, context2));
|
|
104160
104238
|
if (initialResolution.kind === "resumed") {
|
|
104161
104239
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104162
104240
|
return initialResolution.snapshot;
|
|
@@ -104175,7 +104253,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104175
104253
|
});
|
|
104176
104254
|
const refreshedListing = await this.listAttempts(attemptScope);
|
|
104177
104255
|
const refreshed = refreshedListing.attempts;
|
|
104178
|
-
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed,
|
|
104256
|
+
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed, context2));
|
|
104179
104257
|
if (refreshedResolution.kind === "resumed") {
|
|
104180
104258
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104181
104259
|
return refreshedResolution.snapshot;
|
|
@@ -104188,7 +104266,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104188
104266
|
if (catalog.test.identifier !== decision.test.qtiTestIdentifier) {
|
|
104189
104267
|
catalog = await this.loadReviewBankCatalog(decision.test.qtiTestIdentifier);
|
|
104190
104268
|
}
|
|
104191
|
-
const exposures =
|
|
104269
|
+
const exposures = assessmentReviewTranscriptExposures({
|
|
104270
|
+
attempts: refreshedListing.attempts.values(),
|
|
104271
|
+
children: refreshedListing.reviewChildren.values(),
|
|
104192
104272
|
bankRevision: catalog.bank.bankRevision
|
|
104193
104273
|
});
|
|
104194
104274
|
const selected = selectReviewItems({
|
|
@@ -104227,6 +104307,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104227
104307
|
activityId: input.activityId,
|
|
104228
104308
|
purpose: input.purpose,
|
|
104229
104309
|
courseId: context2.integration.courseId,
|
|
104310
|
+
courseName: context2.enrollment.course.title,
|
|
104230
104311
|
integrationId: context2.integration.id,
|
|
104231
104312
|
enrollmentId: context2.enrollment.id,
|
|
104232
104313
|
selectedTest: {
|
|
@@ -104273,8 +104354,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104273
104354
|
});
|
|
104274
104355
|
}
|
|
104275
104356
|
async get(params) {
|
|
104357
|
+
return this.resume({ ...params, input: {} });
|
|
104358
|
+
}
|
|
104359
|
+
async resume({
|
|
104360
|
+
input,
|
|
104361
|
+
...params
|
|
104362
|
+
}) {
|
|
104276
104363
|
const attempt = await this.authorizeAttemptRead(params);
|
|
104277
|
-
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration);
|
|
104364
|
+
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration, input.transcript);
|
|
104278
104365
|
}
|
|
104279
104366
|
async latest({
|
|
104280
104367
|
gameId,
|
|
@@ -104336,496 +104423,104 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104336
104423
|
}
|
|
104337
104424
|
async save({ input, ...params }) {
|
|
104338
104425
|
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
104426
|
return this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104352
|
-
const attempt = await this.
|
|
104427
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104353
104428
|
db: db2,
|
|
104354
|
-
developerAccessValidated: true
|
|
104429
|
+
developerAccessValidated: true,
|
|
104430
|
+
includeEnrollment: false
|
|
104355
104431
|
});
|
|
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);
|
|
104432
|
+
requireAssessmentTranscriptCheckpoint(attempt.result, attempt.integration.status, input);
|
|
104433
|
+
const graded = await this.gradeTranscript({
|
|
104434
|
+
attemptId: params.attemptId,
|
|
104435
|
+
metadata: attempt.metadata
|
|
104436
|
+
}, input.transcript, false, db2);
|
|
104437
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, buildAssessmentResultCheckpoint({
|
|
104438
|
+
result: attempt.result,
|
|
104439
|
+
metadata: attempt.metadata,
|
|
104440
|
+
transcript: graded.transcript,
|
|
104441
|
+
timestamp: graded.metadata.updatedAt
|
|
104442
|
+
}));
|
|
104443
|
+
this.reconcileTranscriptChildren(this.tokenContext(attempt), graded).catch((error88) => this.recordTranscriptFailure("checkpoint_projection", params.attemptId, error88));
|
|
104376
104444
|
return {
|
|
104377
|
-
attemptId:
|
|
104378
|
-
responseVersion:
|
|
104445
|
+
attemptId: params.attemptId,
|
|
104446
|
+
responseVersion: graded.metadata.responseVersion,
|
|
104379
104447
|
status: "in_progress",
|
|
104380
|
-
responses
|
|
104448
|
+
responses: graded.metadata.responses,
|
|
104449
|
+
transcript: graded.transcript
|
|
104381
104450
|
};
|
|
104382
104451
|
});
|
|
104383
104452
|
}
|
|
104384
|
-
async
|
|
104453
|
+
async recordProgress({
|
|
104385
104454
|
input,
|
|
104386
104455
|
...params
|
|
104387
104456
|
}) {
|
|
104388
104457
|
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
|
-
}
|
|
104458
|
+
const { context: context2, transcript, submissionId } = prepareAssessmentProgress(await this.deps.verifyAssessmentToken(input.assessmentToken), params, input);
|
|
104459
|
+
const graded = await this.gradeTranscript(context2, transcript);
|
|
104460
|
+
await this.reconcileTranscriptChildren(context2, graded, new Set([submissionId]));
|
|
104461
|
+
return { attemptId: params.attemptId };
|
|
104661
104462
|
}
|
|
104662
|
-
async
|
|
104463
|
+
async submitItem({
|
|
104464
|
+
input,
|
|
104465
|
+
...params
|
|
104466
|
+
}) {
|
|
104467
|
+
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104468
|
+
const context2 = requireAssessmentFeedbackContext(await this.deps.verifyAssessmentToken(input.assessmentToken), params);
|
|
104469
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104470
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104471
|
+
const appended = prepareAssessmentItemTranscript(assessment, { ...input, transcript });
|
|
104472
|
+
const graded = await this.gradeTranscript(context2, appended.transcript);
|
|
104473
|
+
const submission = graded.metadata.itemSubmissions[appended.index];
|
|
104474
|
+
this.reconcileTranscriptChildren(context2, graded, new Set([submission.submissionId])).catch((error88) => this.recordTranscriptFailure("child_projection", params.attemptId, error88));
|
|
104475
|
+
return buildAssessmentTranscriptFeedback(params.attemptId, graded, appended.index);
|
|
104476
|
+
}
|
|
104477
|
+
async gradeTranscript(context2, transcript, terminal = false, db2 = this.deps.db) {
|
|
104478
|
+
const startedAt = Date.now();
|
|
104663
104479
|
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({
|
|
104480
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104481
|
+
const prepared = prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal);
|
|
104482
|
+
const artifactIdentity = assessmentArtifactIdentityFromAttempt(context2.metadata);
|
|
104483
|
+
const artifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
104484
|
+
const [itemResults, routing] = await Promise.all([
|
|
104485
|
+
scoreCachedAssessmentItems({
|
|
104486
|
+
assessment,
|
|
104487
|
+
responses: prepared.responses,
|
|
104488
|
+
artifactIdentity,
|
|
104489
|
+
purpose: context2.metadata.purpose,
|
|
104490
|
+
itemIdentifiers: new Set(prepared.transcript.administrations.map((entry) => entry.itemIdentifier)),
|
|
104491
|
+
artifact,
|
|
104492
|
+
cache: this.itemScoreCache,
|
|
104493
|
+
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
104494
|
+
onEvent: addEvent
|
|
104495
|
+
}),
|
|
104496
|
+
this.isPlatformRoutedDiagnosticMetadata(context2.metadata) ? this.loadAttemptDiagnosticRouting(context2.metadata, assessment, db2, artifact) : undefined
|
|
104497
|
+
]);
|
|
104498
|
+
const graded = buildGradedAssessmentTranscript({
|
|
104499
|
+
context: context2,
|
|
104692
104500
|
assessment,
|
|
104693
|
-
|
|
104694
|
-
|
|
104695
|
-
|
|
104696
|
-
|
|
104697
|
-
|
|
104501
|
+
prepared,
|
|
104502
|
+
itemResults,
|
|
104503
|
+
submittedAt: new Date().toISOString(),
|
|
104504
|
+
manifest: routing?.manifest,
|
|
104505
|
+
terminal
|
|
104506
|
+
});
|
|
104507
|
+
addEvent("assessment.transcript_validated", {
|
|
104508
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
104509
|
+
"app.assessment.administration_count": graded.metadata.responseVersion,
|
|
104510
|
+
"app.assessment.duration_ms": Date.now() - startedAt
|
|
104698
104511
|
});
|
|
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);
|
|
104512
|
+
return graded;
|
|
104735
104513
|
} 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;
|
|
104514
|
+
throw assessmentTranscriptRuntimeError(error88);
|
|
104742
104515
|
}
|
|
104743
104516
|
}
|
|
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
|
|
104517
|
+
recordTranscriptFailure(operation, attemptId, error88) {
|
|
104518
|
+
addEvent("assessment.transcript_operation_failed", {
|
|
104519
|
+
"app.assessment.operation": operation,
|
|
104520
|
+
"app.assessment.attempt_id": attemptId,
|
|
104521
|
+
"exception.type": errorType(error88),
|
|
104522
|
+
"app.error.message": errorMessage(error88)
|
|
104781
104523
|
});
|
|
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;
|
|
104799
|
-
});
|
|
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
104524
|
}
|
|
104830
104525
|
async submit({
|
|
104831
104526
|
input,
|
|
@@ -104833,121 +104528,83 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104833
104528
|
...params
|
|
104834
104529
|
}) {
|
|
104835
104530
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104836
|
-
const
|
|
104837
|
-
const
|
|
104838
|
-
|
|
104839
|
-
|
|
104840
|
-
|
|
104841
|
-
|
|
104842
|
-
|
|
104843
|
-
|
|
104844
|
-
|
|
104531
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104532
|
+
const request = { ...input, transcript };
|
|
104533
|
+
const requestHash = await assessmentTranscriptRequestHash(request);
|
|
104534
|
+
const accepted = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104535
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104536
|
+
db: db2,
|
|
104537
|
+
developerAccessValidated: true,
|
|
104538
|
+
includeEnrollment: false
|
|
104539
|
+
});
|
|
104540
|
+
const disposition = resolveAssessmentTranscriptSubmission(attempt.result, attempt.metadata, attempt.integration.status, request, requestHash);
|
|
104541
|
+
if (disposition === "replay") {
|
|
104845
104542
|
return {
|
|
104846
|
-
response: this.submittedResult(
|
|
104847
|
-
emission: this.replayCompletion(
|
|
104543
|
+
response: this.submittedResult(attempt.result, attempt.metadata),
|
|
104544
|
+
emission: this.replayCompletion(attempt, input.submissionId, params, game2)
|
|
104848
104545
|
};
|
|
104849
104546
|
}
|
|
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();
|
|
104547
|
+
const graded = await this.gradeTranscript({
|
|
104548
|
+
attemptId: params.attemptId,
|
|
104549
|
+
metadata: attempt.metadata
|
|
104550
|
+
}, transcript, true, db2);
|
|
104551
|
+
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
104552
|
+
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
104553
|
+
metadata: graded.metadata,
|
|
104554
|
+
itemResults: graded.itemResults
|
|
104555
|
+
}) : undefined;
|
|
104556
|
+
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
104877
104557
|
const timestamp6 = new Date().toISOString();
|
|
104878
|
-
const reviewOutcomes = attempt.metadata.purpose === "review" ? this.reviewItemOutcomes({
|
|
104879
|
-
metadata: attempt.metadata,
|
|
104880
|
-
itemResults
|
|
104881
|
-
}) : null;
|
|
104882
104558
|
const finalization = buildAssessmentResultSubmission({
|
|
104883
104559
|
result: attempt.result,
|
|
104884
|
-
metadata:
|
|
104885
|
-
score,
|
|
104560
|
+
metadata: graded.metadata,
|
|
104561
|
+
score: graded.score,
|
|
104886
104562
|
submissionId: input.submissionId,
|
|
104887
104563
|
timestamp: timestamp6,
|
|
104888
|
-
testName: assessment.title,
|
|
104889
|
-
courseName
|
|
104890
|
-
totalQuestions: itemResults.length,
|
|
104891
|
-
correctQuestions,
|
|
104564
|
+
testName: graded.assessment.title,
|
|
104565
|
+
courseName,
|
|
104566
|
+
totalQuestions: graded.itemResults.length,
|
|
104567
|
+
correctQuestions: graded.correctQuestions,
|
|
104892
104568
|
...input.session ? { session: input.session } : {},
|
|
104893
|
-
...
|
|
104569
|
+
...itemOutcomes ? { itemOutcomes } : {},
|
|
104570
|
+
...diagnostic ? { diagnosticTracks: diagnostic.tracks } : {}
|
|
104894
104571
|
});
|
|
104895
|
-
const metadata2 =
|
|
104896
|
-
|
|
104897
|
-
|
|
104898
|
-
|
|
104899
|
-
|
|
104900
|
-
|
|
104901
|
-
|
|
104902
|
-
|
|
104572
|
+
const metadata2 = {
|
|
104573
|
+
...finalization.metadata,
|
|
104574
|
+
completion: { ...finalization.metadata.completion, requestHash }
|
|
104575
|
+
};
|
|
104576
|
+
const resultUpdate = {
|
|
104577
|
+
...finalization.resultUpdate,
|
|
104578
|
+
metadata: mergeAssessmentRuntimeMetadata(attempt.result.metadata, metadata2)
|
|
104579
|
+
};
|
|
104580
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, resultUpdate);
|
|
104581
|
+
const result = { ...attempt.result, ...resultUpdate };
|
|
104582
|
+
const emission = {
|
|
104583
|
+
attempt: { ...attempt, result, metadata: metadata2 },
|
|
104584
|
+
testIdentifier: graded.assessment.identifier,
|
|
104585
|
+
testName: graded.assessment.title,
|
|
104586
|
+
score: graded.score,
|
|
104587
|
+
totalQuestions: graded.itemResults.length,
|
|
104588
|
+
correctQuestions: graded.correctQuestions,
|
|
104589
|
+
submissionId: input.submissionId,
|
|
104590
|
+
gameId: params.gameId,
|
|
104591
|
+
studentId: params.studentId,
|
|
104592
|
+
courseName,
|
|
104593
|
+
game: game2
|
|
104903
104594
|
};
|
|
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
104595
|
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
|
-
}
|
|
104596
|
+
response: this.submittedResult(result, metadata2),
|
|
104597
|
+
emission,
|
|
104598
|
+
repair: { context: this.tokenContext(attempt), graded }
|
|
104936
104599
|
};
|
|
104937
104600
|
});
|
|
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
|
-
]);
|
|
104601
|
+
if (accepted.repair) {
|
|
104602
|
+
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
104603
|
+
}
|
|
104604
|
+
if (accepted.emission) {
|
|
104605
|
+
this.emitSessionBestEffort(accepted.emission);
|
|
104949
104606
|
}
|
|
104950
|
-
return
|
|
104607
|
+
return accepted.response;
|
|
104951
104608
|
}
|
|
104952
104609
|
async finalize({
|
|
104953
104610
|
input,
|
|
@@ -105091,58 +104748,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105091
104748
|
});
|
|
105092
104749
|
}
|
|
105093
104750
|
}
|
|
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
104751
|
async exportFixture({
|
|
105147
104752
|
gameId,
|
|
105148
104753
|
user
|
|
@@ -105342,14 +104947,32 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105342
104947
|
async listAttempts(scope) {
|
|
105343
104948
|
return this.attemptListing(await this.studentAssessmentResults(scope.studentId), scope);
|
|
105344
104949
|
}
|
|
105345
|
-
attemptListing(results, scope) {
|
|
104950
|
+
async attemptListing(results, scope) {
|
|
105346
104951
|
const attempts = new Map;
|
|
105347
104952
|
const reviewChildren = new Map;
|
|
105348
104953
|
for (const result of results) {
|
|
105349
104954
|
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
105350
|
-
|
|
105351
|
-
|
|
105352
|
-
|
|
104955
|
+
if (scope.purpose === "review") {
|
|
104956
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
104957
|
+
if (verified.ok && verified.command.payload.context.kind === "review") {
|
|
104958
|
+
const { command } = verified;
|
|
104959
|
+
const { attempt, administration } = command.payload;
|
|
104960
|
+
const parent = await reviewQuestionLineItemId(await deterministicUUID([
|
|
104961
|
+
"playcademy:review-bank-line-item:v1",
|
|
104962
|
+
scope.integrationId,
|
|
104963
|
+
scope.activityId,
|
|
104964
|
+
attempt.selectedTest.identifier,
|
|
104965
|
+
verified.command.payload.context.bankRevision
|
|
104966
|
+
].join("\x00")), administration.itemIdentifier);
|
|
104967
|
+
const projection = await classifyAssessmentChildResult({
|
|
104968
|
+
result,
|
|
104969
|
+
command,
|
|
104970
|
+
childLineItemId: parent
|
|
104971
|
+
});
|
|
104972
|
+
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") {
|
|
104973
|
+
reviewChildren.set(result.sourcedId, command);
|
|
104974
|
+
}
|
|
104975
|
+
}
|
|
105353
104976
|
}
|
|
105354
104977
|
if (metadata2 && metadata2.activityId === scope.activityId && metadata2.purpose === scope.purpose && metadata2.courseId === scope.courseId && metadata2.integrationId === scope.integrationId && result.student.sourcedId === scope.studentId) {
|
|
105355
104978
|
const selectedAssessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
@@ -105400,19 +105023,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105400
105023
|
}
|
|
105401
105024
|
return true;
|
|
105402
105025
|
}
|
|
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
105026
|
requireDecision(decision, purpose) {
|
|
105417
105027
|
if (!decision) {
|
|
105418
105028
|
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(purpose));
|
|
@@ -105429,29 +105039,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105429
105039
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105430
105040
|
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105431
105041
|
}
|
|
105432
|
-
async resumeDiagnosticSnapshot(decision, attempts, integration,
|
|
105433
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
105434
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
105435
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105436
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105437
|
-
});
|
|
105438
|
-
}
|
|
105042
|
+
async resumeDiagnosticSnapshot(decision, attempts, integration, _db = this.deps.db) {
|
|
105439
105043
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105440
|
-
|
|
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");
|
|
105445
|
-
}
|
|
105446
|
-
const assessment = await this.loadAttemptAssessment(resumed.metadata);
|
|
105447
|
-
const routing = await this.loadAttemptDiagnosticRouting(resumed.metadata, assessment, db2);
|
|
105448
|
-
await this.restoreDiagnosticChildResults({
|
|
105449
|
-
result: resumed.result,
|
|
105450
|
-
metadata: resumed.metadata,
|
|
105451
|
-
integration,
|
|
105452
|
-
assessment
|
|
105453
|
-
});
|
|
105454
|
-
return this.diagnosticSnapshot(resumed.result, resumed.metadata, assessment, integration, routing.state);
|
|
105044
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105455
105045
|
}
|
|
105456
105046
|
async resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, resume) {
|
|
105457
105047
|
const select3 = () => selectRuntimeAssessment(tests, [...this.attemptsMatchingRequest(attempts, requestFingerprint).values()].map((entry) => entry.selection));
|
|
@@ -105522,42 +105112,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105522
105112
|
});
|
|
105523
105113
|
}
|
|
105524
105114
|
}
|
|
105525
|
-
async resumeReviewSnapshot(decision, attempts,
|
|
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
|
-
}
|
|
105115
|
+
async resumeReviewSnapshot(decision, attempts, context2) {
|
|
105532
105116
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105533
|
-
|
|
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);
|
|
105117
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, context2.integration);
|
|
105561
105118
|
}
|
|
105562
105119
|
recordReviewPreparationPhase(phase, startedAt, counts = {}) {
|
|
105563
105120
|
addEvent("assessment.review_preparation_phase", {
|
|
@@ -105965,135 +105522,82 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105965
105522
|
}
|
|
105966
105523
|
return lineItemId;
|
|
105967
105524
|
}
|
|
105968
|
-
|
|
105969
|
-
const { metadata: metadata2 } = attempt;
|
|
105970
|
-
const assessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
105971
|
-
if (!assessmentKey) {
|
|
105972
|
-
addEvent("assessment.child_result_legacy_attempt_skipped", {
|
|
105973
|
-
"app.assessment.attempt_id": attempt.result.sourcedId
|
|
105974
|
-
});
|
|
105975
|
-
return null;
|
|
105976
|
-
}
|
|
105525
|
+
tokenContext(attempt) {
|
|
105977
105526
|
return {
|
|
105978
|
-
|
|
105527
|
+
version: 1,
|
|
105979
105528
|
gameId: attempt.integration.gameId,
|
|
105980
105529
|
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
|
|
105530
|
+
attemptId: attempt.result.sourcedId,
|
|
105531
|
+
parentLineItemId: attempt.result.assessmentLineItem.sourcedId,
|
|
105532
|
+
metadata: assessmentTokenMetadata(attempt.metadata)
|
|
106001
105533
|
};
|
|
106002
105534
|
}
|
|
106003
|
-
|
|
106004
|
-
|
|
106005
|
-
|
|
106006
|
-
|
|
106007
|
-
|
|
106008
|
-
|
|
105535
|
+
async ensureTranscriptLineItems(context2, assessment) {
|
|
105536
|
+
await runWithConcurrency(assessment.items, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (item) => {
|
|
105537
|
+
const id = await assessmentTranscriptChildLineItemId(context2, item.identifier);
|
|
105538
|
+
if (this.childLineItemCache.get(id)) {
|
|
105539
|
+
return;
|
|
105540
|
+
}
|
|
105541
|
+
const metadata2 = context2.metadata;
|
|
105542
|
+
const common2 = {
|
|
105543
|
+
parentLineItemId: context2.parentLineItemId,
|
|
105544
|
+
integration: { courseId: metadata2.courseId },
|
|
105545
|
+
item
|
|
105546
|
+
};
|
|
105547
|
+
if (metadata2.purpose === "review") {
|
|
105548
|
+
const standard = metadata2.review.selections.find((selection) => selection.itemIdentifier === item.identifier)?.standard;
|
|
105549
|
+
if (!standard) {
|
|
105550
|
+
throw new Error("Review candidate is not in the pinned selection");
|
|
105551
|
+
}
|
|
105552
|
+
await this.ensureReviewQuestionLineItem({
|
|
105553
|
+
...common2,
|
|
105554
|
+
bankRevision: metadata2.review.bankRevision,
|
|
105555
|
+
standards: [standard]
|
|
105556
|
+
});
|
|
105557
|
+
} else if (this.isPlatformRoutedDiagnosticMetadata(metadata2)) {
|
|
105558
|
+
await this.ensureDiagnosticQuestionLineItem({ ...common2, metadata: metadata2 });
|
|
105559
|
+
} else {
|
|
105560
|
+
const expected = {
|
|
105561
|
+
status: "active",
|
|
105562
|
+
parentAssessmentLineItem: { sourcedId: context2.parentLineItemId },
|
|
105563
|
+
course: { sourcedId: metadata2.courseId },
|
|
105564
|
+
resultValueMin: 0,
|
|
105565
|
+
resultValueMax: item.maxScore,
|
|
105566
|
+
metadata: {
|
|
105567
|
+
playcademyAssessmentQuestionDefinition: {
|
|
105568
|
+
version: 1,
|
|
105569
|
+
itemIdentifier: item.identifier,
|
|
105570
|
+
contentRevision: metadata2.selectedTest.contentRevision
|
|
105571
|
+
}
|
|
105572
|
+
}
|
|
105573
|
+
};
|
|
105574
|
+
const store = this.requireClient().api.oneroster.assessmentLineItems;
|
|
105575
|
+
try {
|
|
105576
|
+
await store.create({ sourcedId: id, title: item.title, ...expected });
|
|
105577
|
+
} catch (error88) {
|
|
105578
|
+
if (!isApiError(error88) || error88.statusCode !== 409) {
|
|
105579
|
+
throw error88;
|
|
105580
|
+
}
|
|
105581
|
+
this.assertAssessmentChildLineItem(id, await store.get(id), expected);
|
|
105582
|
+
}
|
|
105583
|
+
}
|
|
105584
|
+
this.childLineItemCache.set(id, true);
|
|
106009
105585
|
});
|
|
106010
105586
|
}
|
|
106011
|
-
|
|
106012
|
-
return
|
|
106013
|
-
|
|
106014
|
-
|
|
106015
|
-
|
|
106016
|
-
|
|
106017
|
-
|
|
106018
|
-
|
|
106019
|
-
|
|
106020
|
-
|
|
106021
|
-
|
|
105587
|
+
async reconcileTranscriptChildren(context2, graded, only) {
|
|
105588
|
+
return reconcileAssessmentTranscriptChildren({
|
|
105589
|
+
context: context2,
|
|
105590
|
+
graded,
|
|
105591
|
+
only,
|
|
105592
|
+
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
105593
|
+
ensureLineItems: (assessment) => this.ensureTranscriptLineItems(context2, assessment),
|
|
105594
|
+
onPersisted: (submissionId) => addEvent("assessment.child_result_persisted", {
|
|
105595
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
105596
|
+
"app.assessment.submission_id": submissionId,
|
|
105597
|
+
"app.assessment.child_result_outcome": "upserted"
|
|
106022
105598
|
})
|
|
106023
105599
|
});
|
|
106024
105600
|
}
|
|
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
105601
|
assertAssessmentChildLineItem(lineItemId, stored, expected) {
|
|
106098
105602
|
if (assessmentChildLineItemMatches(stored, expected)) {
|
|
106099
105603
|
return;
|
|
@@ -106151,91 +105655,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106151
105655
|
}
|
|
106152
105656
|
return lineItemId;
|
|
106153
105657
|
}
|
|
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
105658
|
async ensureReviewQuestionLineItem(input) {
|
|
106240
105659
|
const client2 = this.requireClient();
|
|
106241
105660
|
const lineItemId = await reviewQuestionLineItemId(input.parentLineItemId, input.item.identifier);
|
|
@@ -106282,220 +105701,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106282
105701
|
}
|
|
106283
105702
|
return lineItemId;
|
|
106284
105703
|
}
|
|
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
105704
|
async resolveAttemptAuthorization({ gameId, studentId, attemptId, user }, options = {}) {
|
|
106500
105705
|
if (!options.developerAccessValidated) {
|
|
106501
105706
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
@@ -106524,14 +105729,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106524
105729
|
const enrollment = enrollments.find((candidate) => candidate.id === metadata2.enrollmentId && candidate.course.id === integration.courseId);
|
|
106525
105730
|
return { attempt: { result, metadata: metadata2, integration }, enrollment };
|
|
106526
105731
|
}
|
|
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
105732
|
async authorizeAttemptRead(params) {
|
|
106536
105733
|
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params);
|
|
106537
105734
|
if ((!enrollment || attempt.integration.status === "deactivated") && !this.isSettled(attempt.result)) {
|
|
@@ -106542,7 +105739,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106542
105739
|
unauthorizedAttempt(attemptId) {
|
|
106543
105740
|
return AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
106544
105741
|
}
|
|
106545
|
-
async snapshotForResult(result, metadata2, integration) {
|
|
105742
|
+
async snapshotForResult(result, metadata2, integration, suppliedTranscript) {
|
|
106546
105743
|
if (isAssessmentAttemptSuperseded({
|
|
106547
105744
|
inProgress: result.inProgress ?? "",
|
|
106548
105745
|
scoreStatus: result.scoreStatus
|
|
@@ -106578,38 +105775,59 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106578
105775
|
result: buildCompletedAssessmentSubmitResult({ result, metadata: metadata2 })
|
|
106579
105776
|
};
|
|
106580
105777
|
}
|
|
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)
|
|
105778
|
+
const context2 = this.tokenContext({ result, metadata: metadata2, integration });
|
|
105779
|
+
const graded = await resumeAssessmentTranscript({
|
|
105780
|
+
suppliedTranscript,
|
|
105781
|
+
checkpointTranscript: metadata2.transcript,
|
|
105782
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.studentAssessmentResults(context2.studentId)),
|
|
105783
|
+
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
106601
105784
|
});
|
|
105785
|
+
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
105786
|
+
const common2 = {
|
|
105787
|
+
attemptId: result.sourcedId,
|
|
105788
|
+
assessment: graded.assessment,
|
|
105789
|
+
activityData: this.activityData(metadata2, graded.assessment, integration)
|
|
105790
|
+
};
|
|
105791
|
+
return this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? {
|
|
105792
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105793
|
+
...common2,
|
|
105794
|
+
metadata: graded.metadata,
|
|
105795
|
+
state: graded.metadata.diagnostic.state
|
|
105796
|
+
}),
|
|
105797
|
+
...session2
|
|
105798
|
+
} : {
|
|
105799
|
+
...buildConventionalAssessmentSnapshot({ ...common2, metadata: graded.metadata }),
|
|
105800
|
+
...session2
|
|
105801
|
+
};
|
|
105802
|
+
}
|
|
105803
|
+
async assessmentSession(context2, transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision)) {
|
|
105804
|
+
return { transcript, assessmentToken: await this.deps.mintAssessmentToken(context2) };
|
|
105805
|
+
}
|
|
105806
|
+
async diagnosticSnapshot(result, metadata2, assessment, integration, options) {
|
|
105807
|
+
return {
|
|
105808
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105809
|
+
attemptId: result.sourcedId,
|
|
105810
|
+
metadata: metadata2,
|
|
105811
|
+
assessment,
|
|
105812
|
+
state: options.state,
|
|
105813
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105814
|
+
}),
|
|
105815
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105816
|
+
};
|
|
106602
105817
|
}
|
|
106603
105818
|
diagnosticRoutingSnapshot(metadata2, state) {
|
|
106604
105819
|
return projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, state);
|
|
106605
105820
|
}
|
|
106606
|
-
snapshot(result, metadata2, assessment, integration) {
|
|
106607
|
-
return
|
|
106608
|
-
|
|
106609
|
-
|
|
106610
|
-
|
|
106611
|
-
|
|
106612
|
-
|
|
105821
|
+
async snapshot(result, metadata2, assessment, integration) {
|
|
105822
|
+
return {
|
|
105823
|
+
...buildConventionalAssessmentSnapshot({
|
|
105824
|
+
attemptId: result.sourcedId,
|
|
105825
|
+
metadata: metadata2,
|
|
105826
|
+
assessment,
|
|
105827
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105828
|
+
}),
|
|
105829
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105830
|
+
};
|
|
106613
105831
|
}
|
|
106614
105832
|
activityData(metadata2, assessment, integration) {
|
|
106615
105833
|
const course = this.trackableCourse(integration, "assessment.activity_tracking_invalid_course_metadata");
|
|
@@ -106670,16 +105888,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106670
105888
|
isSettled(result) {
|
|
106671
105889
|
return this.isAwaitingAward(result) || this.isCompleted(result);
|
|
106672
105890
|
}
|
|
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
105891
|
scoreFromResult(result, possible, metadata2) {
|
|
106684
105892
|
return metadata2.score ?? assessmentScoreFromPercentage(result.score, possible);
|
|
106685
105893
|
}
|
|
@@ -106747,7 +105955,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106747
105955
|
grade: integration.grade
|
|
106748
105956
|
})
|
|
106749
105957
|
});
|
|
106750
|
-
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration,
|
|
105958
|
+
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration, {
|
|
105959
|
+
state: metadata2.diagnostic.state
|
|
105960
|
+
}) : this.snapshot(result, metadata2, assessment, integration);
|
|
106751
105961
|
}
|
|
106752
105962
|
async claimAttemptId(scope, from) {
|
|
106753
105963
|
for (let attemptNumber = from;attemptNumber < from + 20; attemptNumber += 1) {
|
|
@@ -106792,18 +106002,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106792
106002
|
game: game2
|
|
106793
106003
|
};
|
|
106794
106004
|
}
|
|
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
106005
|
async emitCompletionBestEffort(input) {
|
|
106808
106006
|
const { attempt } = input;
|
|
106809
106007
|
const integration = attempt.integration;
|
|
@@ -110392,6 +109590,8 @@ function createPlatformServices(deps) {
|
|
|
110392
109590
|
mintPlatformServiceToken,
|
|
110393
109591
|
mintAssessmentPreparationReceipt,
|
|
110394
109592
|
verifyAssessmentPreparationReceipt,
|
|
109593
|
+
mintAssessmentToken,
|
|
109594
|
+
verifyAssessmentToken,
|
|
110395
109595
|
validateDeveloperAccessBySlug,
|
|
110396
109596
|
validateDeveloperAccess,
|
|
110397
109597
|
validateGameManagementAccess
|
|
@@ -110466,7 +109666,9 @@ function createPlatformServices(deps) {
|
|
|
110466
109666
|
validateDeveloperAccess,
|
|
110467
109667
|
assessmentRuntimeLock,
|
|
110468
109668
|
mintAssessmentPreparationReceipt,
|
|
110469
|
-
verifyAssessmentPreparationReceipt
|
|
109669
|
+
verifyAssessmentPreparationReceipt,
|
|
109670
|
+
mintAssessmentToken,
|
|
109671
|
+
verifyAssessmentToken
|
|
110470
109672
|
});
|
|
110471
109673
|
return {
|
|
110472
109674
|
assessmentAssets,
|
|
@@ -111216,6 +110418,8 @@ function createServices(ctx) {
|
|
|
111216
110418
|
mintPlatformServiceToken: auth2.mintPlatformServiceToken.bind(auth2),
|
|
111217
110419
|
mintAssessmentPreparationReceipt: auth2.mintAssessmentPreparationReceipt.bind(auth2),
|
|
111218
110420
|
verifyAssessmentPreparationReceipt: auth2.verifyAssessmentPreparationReceipt.bind(auth2),
|
|
110421
|
+
mintAssessmentToken: auth2.mintAssessmentToken.bind(auth2),
|
|
110422
|
+
verifyAssessmentToken: auth2.verifyAssessmentToken.bind(auth2),
|
|
111219
110423
|
...gaming.validators
|
|
111220
110424
|
});
|
|
111221
110425
|
const standalone = createStandaloneServices({ db: db2, auth: auth2, timeback: timeback2 });
|
|
@@ -111744,6 +110948,15 @@ function createSandboxAuthProvider() {
|
|
|
111744
110948
|
});
|
|
111745
110949
|
return { receipt, expiresAt: new Date(expiresAt).toISOString() };
|
|
111746
110950
|
},
|
|
110951
|
+
async mintAssessmentToken(context2) {
|
|
110952
|
+
const token = `sandbox_assessment_${crypto.randomUUID()}`;
|
|
110953
|
+
sandboxAssessmentTokens.set(token, structuredClone(context2));
|
|
110954
|
+
return token;
|
|
110955
|
+
},
|
|
110956
|
+
async verifyAssessmentToken(token) {
|
|
110957
|
+
const context2 = sandboxAssessmentTokens.get(token);
|
|
110958
|
+
return context2 ? structuredClone(context2) : null;
|
|
110959
|
+
},
|
|
111747
110960
|
async verifyAssessmentPreparationReceipt(receipt) {
|
|
111748
110961
|
const prepared = sandboxAssessmentPreparationReceipts.get(receipt);
|
|
111749
110962
|
if (!prepared) {
|
|
@@ -111796,13 +111009,18 @@ function createSandboxAuthProvider() {
|
|
|
111796
111009
|
}
|
|
111797
111010
|
};
|
|
111798
111011
|
}
|
|
111799
|
-
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS;
|
|
111800
|
-
var init_auth_provider = __esm(() => {
|
|
111012
|
+
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, sandboxAssessmentTokens;
|
|
111013
|
+
var init_auth_provider = __esm(async () => {
|
|
111801
111014
|
init_src6();
|
|
111802
111015
|
init_constants();
|
|
111016
|
+
await init_dist5();
|
|
111803
111017
|
sandboxApiKeys = new Map;
|
|
111804
111018
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
111805
111019
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 5 * 60 * 1000;
|
|
111020
|
+
sandboxAssessmentTokens = new TimebackCache({
|
|
111021
|
+
defaultTTL: 2 * 60 * 60 * 1000,
|
|
111022
|
+
maxSize: 1000
|
|
111023
|
+
});
|
|
111806
111024
|
});
|
|
111807
111025
|
|
|
111808
111026
|
// src/infrastructure/api/providers/cache.provider.ts
|
|
@@ -111963,10 +111181,10 @@ var init_storage_provider = __esm(() => {
|
|
|
111963
111181
|
});
|
|
111964
111182
|
|
|
111965
111183
|
// src/infrastructure/api/providers/index.ts
|
|
111966
|
-
var init_providers = __esm(() => {
|
|
111967
|
-
init_auth_provider();
|
|
111184
|
+
var init_providers = __esm(async () => {
|
|
111968
111185
|
init_cache_provider();
|
|
111969
111186
|
init_storage_provider();
|
|
111187
|
+
await init_auth_provider();
|
|
111970
111188
|
});
|
|
111971
111189
|
|
|
111972
111190
|
// src/infrastructure/api/context.ts
|
|
@@ -112036,10 +111254,10 @@ var init_context = __esm(async () => {
|
|
|
112036
111254
|
init_config2();
|
|
112037
111255
|
init_utils7();
|
|
112038
111256
|
init_src6();
|
|
112039
|
-
init_providers();
|
|
112040
111257
|
await __promiseAll([
|
|
112041
111258
|
init_factory(),
|
|
112042
|
-
init_clients()
|
|
111259
|
+
init_clients(),
|
|
111260
|
+
init_providers()
|
|
112043
111261
|
]);
|
|
112044
111262
|
});
|
|
112045
111263
|
|
|
@@ -168435,7 +167653,7 @@ function parseQtiLibraryParams(searchParams) {
|
|
|
168435
167653
|
limit
|
|
168436
167654
|
};
|
|
168437
167655
|
}
|
|
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;
|
|
167656
|
+
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
167657
|
var init_timeback_controller = __esm(() => {
|
|
168440
167658
|
init_esm();
|
|
168441
167659
|
init_src();
|
|
@@ -168737,6 +167955,20 @@ var init_timeback_controller = __esm(() => {
|
|
|
168737
167955
|
user: ctx.user
|
|
168738
167956
|
});
|
|
168739
167957
|
});
|
|
167958
|
+
resumeRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
167959
|
+
const attemptId = ctx.params.attemptId;
|
|
167960
|
+
const body2 = await parseRequestBody(ctx.request, ResumeRuntimeAssessmentRequestSchema);
|
|
167961
|
+
if (!attemptId) {
|
|
167962
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167963
|
+
}
|
|
167964
|
+
return ctx.services.timebackAssessmentRuntime.resume({
|
|
167965
|
+
gameId: body2.gameId,
|
|
167966
|
+
studentId: body2.studentId,
|
|
167967
|
+
attemptId,
|
|
167968
|
+
input: { transcript: body2.transcript },
|
|
167969
|
+
user: ctx.user
|
|
167970
|
+
});
|
|
167971
|
+
});
|
|
168740
167972
|
saveRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
168741
167973
|
const attemptId = ctx.params.attemptId;
|
|
168742
167974
|
const body2 = await parseRequestBody(ctx.request, SaveRuntimeAssessmentRequestSchema);
|
|
@@ -168749,7 +167981,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
168749
167981
|
attemptId,
|
|
168750
167982
|
input: {
|
|
168751
167983
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168752
|
-
|
|
167984
|
+
transcript: body2.transcript
|
|
167985
|
+
},
|
|
167986
|
+
user: ctx.user
|
|
167987
|
+
});
|
|
167988
|
+
});
|
|
167989
|
+
recordRuntimeAssessmentProgress = requireDeveloper(async (ctx) => {
|
|
167990
|
+
const attemptId = ctx.params.attemptId;
|
|
167991
|
+
const body2 = await parseRequestBody(ctx.request, RecordRuntimeAssessmentProgressRequestSchema);
|
|
167992
|
+
if (!attemptId) {
|
|
167993
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167994
|
+
}
|
|
167995
|
+
return ctx.services.timebackAssessmentRuntime.recordProgress({
|
|
167996
|
+
gameId: body2.gameId,
|
|
167997
|
+
studentId: body2.studentId,
|
|
167998
|
+
attemptId,
|
|
167999
|
+
input: {
|
|
168000
|
+
assessmentToken: body2.assessmentToken,
|
|
168001
|
+
transcript: body2.transcript,
|
|
168002
|
+
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168003
|
+
itemIdentifier: body2.itemIdentifier
|
|
168753
168004
|
},
|
|
168754
168005
|
user: ctx.user
|
|
168755
168006
|
});
|
|
@@ -168765,6 +168016,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
168765
168016
|
studentId: body2.studentId,
|
|
168766
168017
|
attemptId,
|
|
168767
168018
|
input: {
|
|
168019
|
+
assessmentToken: body2.assessmentToken,
|
|
168020
|
+
transcript: body2.transcript,
|
|
168768
168021
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168769
168022
|
submissionId: body2.submissionId,
|
|
168770
168023
|
itemIdentifier: body2.itemIdentifier,
|
|
@@ -168787,6 +168040,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
168787
168040
|
input: {
|
|
168788
168041
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168789
168042
|
submissionId: body2.submissionId,
|
|
168043
|
+
transcript: body2.transcript,
|
|
168790
168044
|
...body2.session ? { session: body2.session } : {}
|
|
168791
168045
|
},
|
|
168792
168046
|
game: {
|
|
@@ -169264,6 +168518,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
169264
168518
|
getLatestRuntimeAssessment,
|
|
169265
168519
|
getRuntimeAssessment,
|
|
169266
168520
|
saveRuntimeAssessment,
|
|
168521
|
+
resumeRuntimeAssessment,
|
|
168522
|
+
recordRuntimeAssessmentProgress,
|
|
169267
168523
|
submitRuntimeAssessmentItem,
|
|
169268
168524
|
submitRuntimeAssessment,
|
|
169269
168525
|
finalizeRuntimeAssessment,
|
|
@@ -170256,6 +169512,8 @@ var init_timeback8 = __esm(async () => {
|
|
|
170256
169512
|
timebackRouter.post("/assessments/start", handle2(timeback2.startRuntimeAssessment));
|
|
170257
169513
|
timebackRouter.get("/assessments/latest", handle2(timeback2.getLatestRuntimeAssessment));
|
|
170258
169514
|
timebackRouter.get("/assessments/:attemptId", handle2(timeback2.getRuntimeAssessment));
|
|
169515
|
+
timebackRouter.post("/assessments/:attemptId/resume", handle2(timeback2.resumeRuntimeAssessment));
|
|
169516
|
+
timebackRouter.post("/assessments/:attemptId/progress", handle2(timeback2.recordRuntimeAssessmentProgress));
|
|
170259
169517
|
timebackRouter.post("/assessments/:attemptId/save", handle2(timeback2.saveRuntimeAssessment));
|
|
170260
169518
|
timebackRouter.post("/assessments/:attemptId/submit-item", handle2(timeback2.submitRuntimeAssessmentItem));
|
|
170261
169519
|
timebackRouter.post("/assessments/:attemptId/submit", handle2(timeback2.submitRuntimeAssessment));
|
|
@@ -170664,13 +169922,13 @@ var init_server = __esm(async () => {
|
|
|
170664
169922
|
init_port();
|
|
170665
169923
|
init_package();
|
|
170666
169924
|
init_config();
|
|
170667
|
-
init_providers();
|
|
170668
169925
|
init_app();
|
|
170669
169926
|
init_database2();
|
|
170670
169927
|
init_options();
|
|
170671
169928
|
await __promiseAll([
|
|
170672
169929
|
init_api3(),
|
|
170673
|
-
init_context()
|
|
169930
|
+
init_context(),
|
|
169931
|
+
init_providers()
|
|
170674
169932
|
]);
|
|
170675
169933
|
version5 = package_default.version;
|
|
170676
169934
|
});
|