@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/cli.js
CHANGED
|
@@ -285,7 +285,7 @@ var init_platform = __esm(() => {
|
|
|
285
285
|
var PLATFORM_TIMEZONE = "America/New_York";
|
|
286
286
|
|
|
287
287
|
// ../constants/src/timeback.ts
|
|
288
|
-
var TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION = 1,
|
|
288
|
+
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;
|
|
289
289
|
var init_timeback2 = __esm(() => {
|
|
290
290
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
291
291
|
TIMEBACK_SUBJECTS = [
|
|
@@ -1128,7 +1128,7 @@ var package_default;
|
|
|
1128
1128
|
var init_package = __esm(() => {
|
|
1129
1129
|
package_default = {
|
|
1130
1130
|
name: "@playcademy/sandbox",
|
|
1131
|
-
version: "0.8.1-beta.
|
|
1131
|
+
version: "0.8.1-beta.6",
|
|
1132
1132
|
description: "Local development server for Playcademy game development",
|
|
1133
1133
|
type: "module",
|
|
1134
1134
|
exports: {
|
|
@@ -9240,26 +9240,6 @@ var init_v3 = __esm(() => {
|
|
|
9240
9240
|
var init_esm = __esm(() => {
|
|
9241
9241
|
init_v3();
|
|
9242
9242
|
});
|
|
9243
|
-
// ../types/src/timeback/assessment-runtime.ts
|
|
9244
|
-
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
9245
|
-
var init_assessment_runtime = __esm(() => {
|
|
9246
|
-
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
9247
|
-
});
|
|
9248
|
-
// ../types/src/timeback/diagnostic-routing.ts
|
|
9249
|
-
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1, DIAGNOSTIC_ROUTING_STATE_VERSION = 1, DIAGNOSTIC_TRACK_OUTCOMES;
|
|
9250
|
-
var init_diagnostic_routing = __esm(() => {
|
|
9251
|
-
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
9252
|
-
"provisional",
|
|
9253
|
-
"unresolved",
|
|
9254
|
-
"route-to-instruction",
|
|
9255
|
-
"not-assessed"
|
|
9256
|
-
];
|
|
9257
|
-
});
|
|
9258
|
-
// ../types/src/timeback/index.ts
|
|
9259
|
-
var init_timeback4 = __esm(() => {
|
|
9260
|
-
init_assessment_runtime();
|
|
9261
|
-
init_diagnostic_routing();
|
|
9262
|
-
});
|
|
9263
9243
|
|
|
9264
9244
|
// ../utils/src/uuid.ts
|
|
9265
9245
|
function isValidUUID(value) {
|
|
@@ -9283,6 +9263,26 @@ var UUID_REGEX;
|
|
|
9283
9263
|
var init_uuid = __esm(() => {
|
|
9284
9264
|
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}$/;
|
|
9285
9265
|
});
|
|
9266
|
+
// ../types/src/timeback/assessment-runtime.ts
|
|
9267
|
+
var PLAYABLE_ASSESSMENT_SHAPES;
|
|
9268
|
+
var init_assessment_runtime = __esm(() => {
|
|
9269
|
+
PLAYABLE_ASSESSMENT_SHAPES = ["circle", "ellipse", "rect", "poly", "default"];
|
|
9270
|
+
});
|
|
9271
|
+
// ../types/src/timeback/diagnostic-routing.ts
|
|
9272
|
+
var DIAGNOSTIC_ROUTING_MANIFEST_VERSION = 1, DIAGNOSTIC_ROUTING_STATE_VERSION = 1, DIAGNOSTIC_TRACK_OUTCOMES;
|
|
9273
|
+
var init_diagnostic_routing = __esm(() => {
|
|
9274
|
+
DIAGNOSTIC_TRACK_OUTCOMES = [
|
|
9275
|
+
"provisional",
|
|
9276
|
+
"unresolved",
|
|
9277
|
+
"route-to-instruction",
|
|
9278
|
+
"not-assessed"
|
|
9279
|
+
];
|
|
9280
|
+
});
|
|
9281
|
+
// ../types/src/timeback/index.ts
|
|
9282
|
+
var init_timeback4 = __esm(() => {
|
|
9283
|
+
init_assessment_runtime();
|
|
9284
|
+
init_diagnostic_routing();
|
|
9285
|
+
});
|
|
9286
9286
|
|
|
9287
9287
|
// ../timeback/dist/engines/assessment-runtime/index.js
|
|
9288
9288
|
function qtiDirectedPairValidationIssue(values, contract) {
|
|
@@ -9562,6 +9562,25 @@ function assessmentReviewBankShortage(fulfillment) {
|
|
|
9562
9562
|
function assessmentFlowForPurpose(purpose) {
|
|
9563
9563
|
return purpose === "review" ? "item-submit" : "attempt-submit";
|
|
9564
9564
|
}
|
|
9565
|
+
function isObject(value) {
|
|
9566
|
+
return typeof value === "object" && value !== null;
|
|
9567
|
+
}
|
|
9568
|
+
function isTimebackGrade(value) {
|
|
9569
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
9570
|
+
}
|
|
9571
|
+
function isAssessmentSessionTiming(value) {
|
|
9572
|
+
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));
|
|
9573
|
+
}
|
|
9574
|
+
function isNonNegativeDuration(value) {
|
|
9575
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
9576
|
+
}
|
|
9577
|
+
function isAssessmentPurpose(value) {
|
|
9578
|
+
return ASSESSMENT_PURPOSES.includes(value);
|
|
9579
|
+
}
|
|
9580
|
+
function isAssessmentResponseValue(value) {
|
|
9581
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
9582
|
+
return entries.length > 0 && entries.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
9583
|
+
}
|
|
9565
9584
|
function compareCodeUnits(left, right) {
|
|
9566
9585
|
if (left < right) {
|
|
9567
9586
|
return -1;
|
|
@@ -10415,48 +10434,6 @@ function projectDiagnosticRoutingSnapshot(revision, state) {
|
|
|
10415
10434
|
}))
|
|
10416
10435
|
};
|
|
10417
10436
|
}
|
|
10418
|
-
function isObject(value) {
|
|
10419
|
-
return typeof value === "object" && value !== null;
|
|
10420
|
-
}
|
|
10421
|
-
function isTimebackGrade(value) {
|
|
10422
|
-
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
10423
|
-
}
|
|
10424
|
-
function isAssessmentSessionTiming(value) {
|
|
10425
|
-
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));
|
|
10426
|
-
}
|
|
10427
|
-
function isNonNegativeDuration(value) {
|
|
10428
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
10429
|
-
}
|
|
10430
|
-
function isAssessmentPurpose(value) {
|
|
10431
|
-
return ASSESSMENT_PURPOSES.includes(value);
|
|
10432
|
-
}
|
|
10433
|
-
function isAssessmentResponseValue(value) {
|
|
10434
|
-
const entries = Array.isArray(value) ? value : [value];
|
|
10435
|
-
return entries.length > 0 && entries.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH);
|
|
10436
|
-
}
|
|
10437
|
-
function applyAssessmentItemResponseUpdate(current, update) {
|
|
10438
|
-
const next = { ...current };
|
|
10439
|
-
for (const [responseId, value] of Object.entries(update)) {
|
|
10440
|
-
if (value === null) {
|
|
10441
|
-
delete next[responseId];
|
|
10442
|
-
} else {
|
|
10443
|
-
next[responseId] = value;
|
|
10444
|
-
}
|
|
10445
|
-
}
|
|
10446
|
-
return Object.keys(next).length === 0 ? undefined : next;
|
|
10447
|
-
}
|
|
10448
|
-
function applyAssessmentResponseUpdate(current, update) {
|
|
10449
|
-
const next = structuredClone(current);
|
|
10450
|
-
for (const [questionId, questionUpdate] of Object.entries(update)) {
|
|
10451
|
-
const questionResponses = applyAssessmentItemResponseUpdate(next[questionId], questionUpdate);
|
|
10452
|
-
if (questionResponses === undefined) {
|
|
10453
|
-
delete next[questionId];
|
|
10454
|
-
} else {
|
|
10455
|
-
next[questionId] = questionResponses;
|
|
10456
|
-
}
|
|
10457
|
-
}
|
|
10458
|
-
return next;
|
|
10459
|
-
}
|
|
10460
10437
|
function assessmentResponseUpdateValidationMessage(assessment, update) {
|
|
10461
10438
|
const items = new Map(assessment.items.map((item) => [item.identifier, item]));
|
|
10462
10439
|
for (const [itemIdentifier, itemUpdate] of Object.entries(update)) {
|
|
@@ -10618,147 +10595,154 @@ function interactionResponseValidationMessage(itemIdentifier, interaction, respo
|
|
|
10618
10595
|
}
|
|
10619
10596
|
return null;
|
|
10620
10597
|
}
|
|
10621
|
-
function
|
|
10622
|
-
return {
|
|
10623
|
-
}
|
|
10624
|
-
function administeredItemIdentifiers(itemSubmissions) {
|
|
10625
|
-
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10598
|
+
function emptyAssessmentTranscript(attemptId, contentRevision) {
|
|
10599
|
+
return { version: 1, attemptId, contentRevision, administrations: [] };
|
|
10626
10600
|
}
|
|
10627
|
-
function
|
|
10628
|
-
|
|
10629
|
-
}
|
|
10630
|
-
function responseValuesEqual(left, right) {
|
|
10631
|
-
if (left === undefined || right === undefined) {
|
|
10632
|
-
return left === right;
|
|
10633
|
-
}
|
|
10634
|
-
if (Array.isArray(left) || Array.isArray(right)) {
|
|
10635
|
-
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
10636
|
-
}
|
|
10637
|
-
return left === right;
|
|
10638
|
-
}
|
|
10639
|
-
function itemResponsesEqual(left, right) {
|
|
10640
|
-
const leftEntries = Object.entries(left ?? {});
|
|
10641
|
-
const rightEntries = Object.entries(right ?? {});
|
|
10642
|
-
return leftEntries.length === rightEntries.length && leftEntries.every(([identifier, value]) => responseValuesEqual(value, right?.[identifier]));
|
|
10643
|
-
}
|
|
10644
|
-
function itemResponseUpdateMatches(current, update) {
|
|
10645
|
-
return itemResponsesEqual(current, applyAssessmentItemResponseUpdate(current, update));
|
|
10646
|
-
}
|
|
10647
|
-
function resolveAssessmentItemReplay(state, input) {
|
|
10648
|
-
const flow = assessmentFlowForPurpose(state.purpose);
|
|
10649
|
-
if (flow !== "item-submit") {
|
|
10601
|
+
function assessmentProgressAdministration(purpose, input) {
|
|
10602
|
+
if (purpose !== "mastery") {
|
|
10650
10603
|
return {
|
|
10651
|
-
|
|
10652
|
-
failure: assessmentFlowViolation("
|
|
10604
|
+
ok: false,
|
|
10605
|
+
failure: assessmentFlowViolation("Background progress is only available for mastery.")
|
|
10653
10606
|
};
|
|
10654
10607
|
}
|
|
10655
|
-
const
|
|
10656
|
-
if (
|
|
10657
|
-
const sameRequest = priorSubmission.itemIdentifier === input.itemIdentifier && itemResponseUpdateMatches(state.responses[input.itemIdentifier], input.responses);
|
|
10658
|
-
if (!sameRequest) {
|
|
10659
|
-
return {
|
|
10660
|
-
action: "conflict",
|
|
10661
|
-
failure: assessmentFlowViolation("This item submission ID was already used for a different request.", {
|
|
10662
|
-
submissionId: input.submissionId,
|
|
10663
|
-
itemIdentifier: input.itemIdentifier,
|
|
10664
|
-
committedItemIdentifier: priorSubmission.itemIdentifier,
|
|
10665
|
-
flow
|
|
10666
|
-
})
|
|
10667
|
-
};
|
|
10668
|
-
}
|
|
10608
|
+
const { administrations } = input.transcript;
|
|
10609
|
+
if (input.expectedResponseVersion !== administrations.length) {
|
|
10669
10610
|
return {
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
responses: state.responses,
|
|
10673
|
-
submission: priorSubmission
|
|
10611
|
+
ok: false,
|
|
10612
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, administrations.length)
|
|
10674
10613
|
};
|
|
10675
10614
|
}
|
|
10676
|
-
|
|
10615
|
+
const administration = administrations.find((entry) => entry.itemIdentifier === input.itemIdentifier);
|
|
10616
|
+
return administration ? { ok: true, submissionId: administration.submissionId } : {
|
|
10617
|
+
ok: false,
|
|
10618
|
+
failure: assessmentFlowViolation("The progress item must be present in the transcript.")
|
|
10619
|
+
};
|
|
10677
10620
|
}
|
|
10678
|
-
function
|
|
10679
|
-
const
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
10621
|
+
function appendAssessmentAdministration(transcript, input) {
|
|
10622
|
+
const entry = {
|
|
10623
|
+
submissionId: input.submissionId,
|
|
10624
|
+
itemIdentifier: input.itemIdentifier,
|
|
10625
|
+
...input.routingNodeKey === undefined ? {} : { routingNodeKey: input.routingNodeKey },
|
|
10626
|
+
responses: Object.fromEntries(Object.entries(input.responses).filter((pair) => pair[1] !== null))
|
|
10627
|
+
};
|
|
10628
|
+
const priorIndex = transcript.administrations.findIndex((item) => item.submissionId === input.submissionId);
|
|
10629
|
+
const index = priorIndex === -1 ? transcript.administrations.length : priorIndex;
|
|
10630
|
+
if (input.expectedResponseVersion !== index) {
|
|
10687
10631
|
return {
|
|
10688
|
-
|
|
10689
|
-
failure: assessmentResponseVersionConflict(input.expectedResponseVersion,
|
|
10632
|
+
ok: false,
|
|
10633
|
+
failure: assessmentResponseVersionConflict(input.expectedResponseVersion, index)
|
|
10690
10634
|
};
|
|
10691
10635
|
}
|
|
10692
|
-
|
|
10693
|
-
const previousItemIdentifier = state.itemSubmissions.at(-1)?.itemIdentifier;
|
|
10694
|
-
const previousItemIndex = state.assessment.items.findIndex((item) => item.identifier === previousItemIdentifier);
|
|
10695
|
-
if (itemIndex === -1 || itemIndex <= previousItemIndex) {
|
|
10636
|
+
if (priorIndex !== -1 && canonicalJson(transcript.administrations[priorIndex]) !== canonicalJson(entry)) {
|
|
10696
10637
|
return {
|
|
10697
|
-
|
|
10698
|
-
failure: assessmentFlowViolation("
|
|
10699
|
-
itemIdentifier: input.itemIdentifier,
|
|
10700
|
-
previousItemIdentifier: previousItemIdentifier ?? null,
|
|
10701
|
-
flow: assessmentFlowForPurpose(state.purpose)
|
|
10702
|
-
})
|
|
10638
|
+
ok: false,
|
|
10639
|
+
failure: assessmentFlowViolation("This administration identity was reused with different answers.")
|
|
10703
10640
|
};
|
|
10704
10641
|
}
|
|
10705
|
-
const update = { [input.itemIdentifier]: input.responses };
|
|
10706
|
-
const updateMessage = assessmentResponseUpdateValidationMessage(state.assessment, update);
|
|
10707
|
-
if (updateMessage) {
|
|
10708
|
-
return { action: "reject", failure: invalidResponse(updateMessage) };
|
|
10709
|
-
}
|
|
10710
|
-
const responses = applyAssessmentResponseUpdate(state.responses, update);
|
|
10711
|
-
const validationMessage = assessmentResponseValidationMessage(state.assessment, responses);
|
|
10712
|
-
if (validationMessage) {
|
|
10713
|
-
return { action: "reject", failure: invalidResponse(validationMessage) };
|
|
10714
|
-
}
|
|
10715
10642
|
return {
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
responses,
|
|
10720
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0
|
|
10643
|
+
ok: true,
|
|
10644
|
+
index,
|
|
10645
|
+
transcript: priorIndex === -1 ? { ...transcript, administrations: [...transcript.administrations, entry] } : transcript
|
|
10721
10646
|
};
|
|
10722
10647
|
}
|
|
10723
|
-
function
|
|
10724
|
-
const
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10648
|
+
function prepareAssessmentTranscript(input) {
|
|
10649
|
+
const parsed = AssessmentTranscriptSchema.safeParse(input.transcript);
|
|
10650
|
+
if (!parsed.success) {
|
|
10651
|
+
throw new AssessmentTranscriptError("Invalid assessment transcript", 0, "INVALID_RESPONSE");
|
|
10652
|
+
}
|
|
10653
|
+
const transcript = parsed.data;
|
|
10654
|
+
if (transcript.attemptId !== input.attemptId || transcript.contentRevision !== input.contentRevision) {
|
|
10655
|
+
throw new AssessmentTranscriptError("Transcript does not belong to this attempt and revision");
|
|
10656
|
+
}
|
|
10657
|
+
const responses = {};
|
|
10658
|
+
const submissions = new Set;
|
|
10659
|
+
const items = new Set;
|
|
10660
|
+
let previousItemIndex = -1;
|
|
10661
|
+
for (const [index, entry] of transcript.administrations.entries()) {
|
|
10662
|
+
const itemIndex = input.assessment.items.findIndex((item) => item.identifier === entry.itemIdentifier);
|
|
10663
|
+
if (submissions.has(entry.submissionId) || items.has(entry.itemIdentifier)) {
|
|
10664
|
+
throw new AssessmentTranscriptError("Transcript contains a repeated administration or item", index);
|
|
10665
|
+
}
|
|
10666
|
+
if (itemIndex === -1) {
|
|
10667
|
+
throw new AssessmentTranscriptError("Transcript references an unknown assessment item", index, "INVALID_RESPONSE");
|
|
10668
|
+
}
|
|
10669
|
+
if (!input.routed && itemIndex <= previousItemIndex) {
|
|
10670
|
+
throw new AssessmentTranscriptError("Transcript items must follow the selected candidate order", index);
|
|
10671
|
+
}
|
|
10672
|
+
if (input.routed !== (entry.routingNodeKey !== undefined)) {
|
|
10673
|
+
throw new AssessmentTranscriptError("Transcript routing identity does not match the assessment flow", index);
|
|
10674
|
+
}
|
|
10675
|
+
const itemResponses = { [entry.itemIdentifier]: entry.responses };
|
|
10676
|
+
const validate2 = input.routed || input.allowIncomplete ? assessmentDraftResponseValidationMessage : assessmentResponseValidationMessage;
|
|
10677
|
+
const invalid = Object.keys(entry.responses).length === 0 ? null : validate2(input.assessment, itemResponses);
|
|
10678
|
+
if (invalid) {
|
|
10679
|
+
throw new AssessmentTranscriptError(invalid, index, "INVALID_RESPONSE");
|
|
10680
|
+
}
|
|
10681
|
+
submissions.add(entry.submissionId);
|
|
10682
|
+
items.add(entry.itemIdentifier);
|
|
10683
|
+
previousItemIndex = itemIndex;
|
|
10684
|
+
Object.assign(responses, itemResponses);
|
|
10685
|
+
}
|
|
10686
|
+
return { transcript, responses };
|
|
10687
|
+
}
|
|
10688
|
+
function completeAssessmentTranscript(input) {
|
|
10689
|
+
const itemSubmissions = [];
|
|
10690
|
+
let state;
|
|
10691
|
+
const ledger = [];
|
|
10692
|
+
if (input.manifest) {
|
|
10693
|
+
const initial = initializeDiagnosticRouting(input.manifest);
|
|
10694
|
+
if (!initial.ok) {
|
|
10695
|
+
throw new AssessmentTranscriptError("The pinned diagnostic routing manifest is invalid");
|
|
10696
|
+
}
|
|
10697
|
+
state = initial.state;
|
|
10698
|
+
}
|
|
10699
|
+
const scores = new Map(input.scores.map((score) => [score.itemIdentifier, score]));
|
|
10700
|
+
for (const [index, entry] of input.transcript.administrations.entries()) {
|
|
10701
|
+
const scoring = scores.get(entry.itemIdentifier);
|
|
10702
|
+
if (!scoring) {
|
|
10703
|
+
throw new AssessmentTranscriptError("Transcript item has no trusted grade", index);
|
|
10704
|
+
}
|
|
10705
|
+
if (input.manifest) {
|
|
10706
|
+
if (typeof scoring.isCorrect !== "boolean" || !entry.routingNodeKey) {
|
|
10707
|
+
throw new AssessmentTranscriptError("Diagnostic grading must be determinate", index);
|
|
10708
|
+
}
|
|
10709
|
+
const advanced = advanceDiagnosticRouting(input.manifest, ledger, {
|
|
10710
|
+
routingNodeKey: entry.routingNodeKey,
|
|
10711
|
+
itemIdentifier: entry.itemIdentifier,
|
|
10712
|
+
isCorrect: scoring.isCorrect
|
|
10713
|
+
});
|
|
10714
|
+
if (!advanced.ok) {
|
|
10715
|
+
throw new AssessmentTranscriptError("Transcript does not follow the pinned diagnostic route", index);
|
|
10716
|
+
}
|
|
10717
|
+
ledger.push(advanced.ledgerEntry);
|
|
10718
|
+
state = advanced.state;
|
|
10719
|
+
}
|
|
10720
|
+
itemSubmissions.push({
|
|
10721
|
+
submissionId: entry.submissionId,
|
|
10722
|
+
itemIdentifier: entry.itemIdentifier,
|
|
10723
|
+
responseVersion: index + 1,
|
|
10724
|
+
submittedAt: input.submittedAt,
|
|
10725
|
+
answered: Object.keys(entry.responses).length > 0,
|
|
10726
|
+
score: scoring.score,
|
|
10727
|
+
isCorrect: scoring.isCorrect,
|
|
10728
|
+
...scoring.grading ? { grading: scoring.grading } : {}
|
|
10729
|
+
});
|
|
10730
|
+
}
|
|
10731
|
+
if (input.terminal) {
|
|
10732
|
+
const count = itemSubmissions.length;
|
|
10733
|
+
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount;
|
|
10734
|
+
const complete = state ? state.status === "ready-to-complete" && count > 0 : conventionalComplete;
|
|
10735
|
+
if (!complete) {
|
|
10736
|
+
throw new AssessmentTranscriptError("A complete administered transcript is required for submission", count);
|
|
10737
|
+
}
|
|
10738
|
+
}
|
|
10739
|
+
return { itemSubmissions, ledger, state };
|
|
10740
10740
|
}
|
|
10741
|
-
function
|
|
10742
|
-
|
|
10743
|
-
const committedUpdate = Object.keys(update).find((identifier) => administered.has(identifier));
|
|
10744
|
-
return committedUpdate ? assessmentFlowViolation("A submitted item response cannot be edited.", {
|
|
10745
|
-
itemIdentifier: committedUpdate,
|
|
10746
|
-
flow: assessmentFlowForPurpose(purpose)
|
|
10747
|
-
}) : null;
|
|
10741
|
+
function administeredItemIdentifiers(itemSubmissions) {
|
|
10742
|
+
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10748
10743
|
}
|
|
10749
|
-
function
|
|
10750
|
-
|
|
10751
|
-
if (flow !== "item-submit") {
|
|
10752
|
-
return null;
|
|
10753
|
-
}
|
|
10754
|
-
if (itemSubmissions.length > 0 && itemSubmissions.length <= itemCount) {
|
|
10755
|
-
return null;
|
|
10756
|
-
}
|
|
10757
|
-
return assessmentFlowViolation("At least one candidate must be administered before the review can be completed.", {
|
|
10758
|
-
flow,
|
|
10759
|
-
submittedItemCount: itemSubmissions.length,
|
|
10760
|
-
itemCount
|
|
10761
|
-
});
|
|
10744
|
+
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
10745
|
+
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
10762
10746
|
}
|
|
10763
10747
|
function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
|
|
10764
10748
|
if (candidates.length === 0) {
|
|
@@ -11091,9 +11075,6 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
11091
11075
|
function reviewQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
11092
11076
|
return deterministicUUID(["playcademy:review-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
11093
11077
|
}
|
|
11094
|
-
function reviewQuestionResultId(parentAttemptId, childQuestionLineItemId) {
|
|
11095
|
-
return deterministicUUID(["playcademy:review-question-result:v1", parentAttemptId, childQuestionLineItemId].join("\x00"));
|
|
11096
|
-
}
|
|
11097
11078
|
function qtiQuestionByIdentifier(questions) {
|
|
11098
11079
|
const byIdentifier = new Map;
|
|
11099
11080
|
for (const entry of questions.questions) {
|
|
@@ -11653,11 +11634,8 @@ function isDiagnosticRoutingState(value) {
|
|
|
11653
11634
|
const nextValid = value.next === null || isRecord(value.next) && isNonEmptyString(value.next.stageKey) && isNonEmptyString(value.next.trackKey) && isNonEmptyString(value.next.nodeKey) && isNonEmptyString(value.next.itemIdentifier);
|
|
11654
11635
|
return stagesValid && tracksValid && nextValid && (value.status === "in-progress" && value.next !== null || value.status === "ready-to-complete" && value.next === null);
|
|
11655
11636
|
}
|
|
11656
|
-
function isDiagnosticRoutingSnapshot(value, revision) {
|
|
11657
|
-
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);
|
|
11658
|
-
}
|
|
11659
11637
|
function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersion) {
|
|
11660
|
-
if (!isRecord(value) || !isNonEmptyString(value.definitionId) || !isNonEmptyString(value.diagnosticKey) || !isNonEmptyString(value.testName) || !isNonEmptyString(value.routingRevision) || !Array.isArray(value.ledger) || !value.ledger.every(isDiagnosticRoutingLedgerEntry) ||
|
|
11638
|
+
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) {
|
|
11661
11639
|
return false;
|
|
11662
11640
|
}
|
|
11663
11641
|
const itemIdentifiers = new Set;
|
|
@@ -11711,19 +11689,7 @@ function isPlaycademyAssessmentResultMetadataV1(value) {
|
|
|
11711
11689
|
const review = metadata2.review;
|
|
11712
11690
|
const diagnostic = metadata2.diagnostic;
|
|
11713
11691
|
const routedDiagnostic = metadata2.purpose === "diagnostic" && diagnostic !== undefined && Array.isArray(metadata2.itemSubmissions) && Number.isInteger(metadata2.responseVersion) && isRoutedDiagnosticAttemptMetadata(diagnostic, metadata2.itemSubmissions, metadata2.responseVersion);
|
|
11714
|
-
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);
|
|
11715
|
-
}
|
|
11716
|
-
function isPlaycademyReviewAssessmentItemResultMetadataV1(value) {
|
|
11717
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
11718
|
-
return false;
|
|
11719
|
-
}
|
|
11720
|
-
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");
|
|
11721
|
-
}
|
|
11722
|
-
function isPlaycademyDiagnosticAssessmentItemResultMetadataV1(value) {
|
|
11723
|
-
if (!isRecord(value) || !isRecord(value.responses)) {
|
|
11724
|
-
return false;
|
|
11725
|
-
}
|
|
11726
|
-
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";
|
|
11692
|
+
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);
|
|
11727
11693
|
}
|
|
11728
11694
|
function playcademyAssessmentResultMetadata(value) {
|
|
11729
11695
|
if (!isRecord(value)) {
|
|
@@ -11743,12 +11709,6 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11743
11709
|
diagnostic: { ...normalized.diagnostic, ledger: [] }
|
|
11744
11710
|
};
|
|
11745
11711
|
}
|
|
11746
|
-
if (isRecord(normalized.diagnostic) && Array.isArray(normalized.diagnostic.ledger) && normalized.diagnostic.ledger.length === 0 && normalized.diagnostic.routingSnapshots === undefined) {
|
|
11747
|
-
normalized = {
|
|
11748
|
-
...normalized,
|
|
11749
|
-
diagnostic: { ...normalized.diagnostic, routingSnapshots: [] }
|
|
11750
|
-
};
|
|
11751
|
-
}
|
|
11752
11712
|
if (isRecord(normalized.diagnostic) && isRecord(normalized.diagnostic.state)) {
|
|
11753
11713
|
const state = normalized.diagnostic.state;
|
|
11754
11714
|
const tracks = Array.isArray(state.tracks) ? state.tracks.map((track) => isRecord(track) ? {
|
|
@@ -11756,12 +11716,10 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11756
11716
|
currentNodeKey: track.currentNodeKey ?? null,
|
|
11757
11717
|
terminal: track.terminal ?? null
|
|
11758
11718
|
} : track) : state.tracks;
|
|
11759
|
-
const routingSnapshots = Array.isArray(normalized.diagnostic.routingSnapshots) ? normalized.diagnostic.routingSnapshots.map((snapshot) => isRecord(snapshot) ? { ...snapshot, next: snapshot.next ?? null } : snapshot) : normalized.diagnostic.routingSnapshots;
|
|
11760
11719
|
normalized = {
|
|
11761
11720
|
...normalized,
|
|
11762
11721
|
diagnostic: {
|
|
11763
11722
|
...normalized.diagnostic,
|
|
11764
|
-
...routingSnapshots === undefined ? {} : { routingSnapshots },
|
|
11765
11723
|
state: {
|
|
11766
11724
|
...state,
|
|
11767
11725
|
...tracks === undefined ? {} : { tracks },
|
|
@@ -11771,6 +11729,16 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11771
11729
|
};
|
|
11772
11730
|
}
|
|
11773
11731
|
const review = normalized.review;
|
|
11732
|
+
if (isRecord(normalized.transcript)) {
|
|
11733
|
+
const transcript = normalized.transcript;
|
|
11734
|
+
normalized = {
|
|
11735
|
+
...normalized,
|
|
11736
|
+
transcript: {
|
|
11737
|
+
...transcript,
|
|
11738
|
+
administrations: Array.isArray(transcript.administrations) ? transcript.administrations.map((entry) => isRecord(entry) ? { ...entry, responses: entry.responses ?? {} } : entry) : transcript.administrations ?? []
|
|
11739
|
+
}
|
|
11740
|
+
};
|
|
11741
|
+
}
|
|
11774
11742
|
if (isRecord(review) && isRecord(review.fulfillment) && review.fulfillment.status === "complete" && review.fulfillment.shortages === undefined) {
|
|
11775
11743
|
normalized = {
|
|
11776
11744
|
...normalized,
|
|
@@ -11785,29 +11753,7 @@ function playcademyAssessmentResultMetadata(value) {
|
|
|
11785
11753
|
}
|
|
11786
11754
|
return isPlaycademyAssessmentResultMetadataV1(normalized) ? normalized : null;
|
|
11787
11755
|
}
|
|
11788
|
-
|
|
11789
|
-
if (!isRecord(value)) {
|
|
11790
|
-
return null;
|
|
11791
|
-
}
|
|
11792
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
11793
|
-
if (!isRecord(metadata2)) {
|
|
11794
|
-
return null;
|
|
11795
|
-
}
|
|
11796
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
11797
|
-
return isPlaycademyReviewAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
11798
|
-
}
|
|
11799
|
-
function playcademyDiagnosticAssessmentItemResultMetadata(value) {
|
|
11800
|
-
if (!isRecord(value)) {
|
|
11801
|
-
return null;
|
|
11802
|
-
}
|
|
11803
|
-
const metadata2 = value[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY];
|
|
11804
|
-
if (!isRecord(metadata2)) {
|
|
11805
|
-
return null;
|
|
11806
|
-
}
|
|
11807
|
-
const normalized = metadata2.responses === undefined ? { ...metadata2, responses: {} } : metadata2;
|
|
11808
|
-
return isPlaycademyDiagnosticAssessmentItemResultMetadataV1(normalized) ? normalized : null;
|
|
11809
|
-
}
|
|
11810
|
-
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;
|
|
11756
|
+
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;
|
|
11811
11757
|
var init_assessment_runtime2 = __esm(() => {
|
|
11812
11758
|
init_timeback3();
|
|
11813
11759
|
init_timeback3();
|
|
@@ -11817,13 +11763,14 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
11817
11763
|
init_timeback3();
|
|
11818
11764
|
init_esm();
|
|
11819
11765
|
init_esm();
|
|
11766
|
+
init_src();
|
|
11767
|
+
init_uuid();
|
|
11768
|
+
init_esm();
|
|
11820
11769
|
init_timeback4();
|
|
11821
11770
|
init_src();
|
|
11822
11771
|
init_timeback3();
|
|
11823
11772
|
init_src();
|
|
11824
11773
|
init_uuid();
|
|
11825
|
-
init_src();
|
|
11826
|
-
init_uuid();
|
|
11827
11774
|
init_esm();
|
|
11828
11775
|
init_src();
|
|
11829
11776
|
init_uuid();
|
|
@@ -12119,6 +12066,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12119
12066
|
ATTEMPT_ALREADY_SUBMITTED: 409,
|
|
12120
12067
|
ASSESSMENT_FLOW_VIOLATION: 409
|
|
12121
12068
|
};
|
|
12069
|
+
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12122
12070
|
RoutingKeySchema = exports_external.string().trim().min(1).max(ROUTING_KEY_MAX_LENGTH);
|
|
12123
12071
|
RoutingResultKeySchema = exports_external.string().trim().min(1).max(ROUTING_RESULT_KEY_MAX_LENGTH);
|
|
12124
12072
|
DiagnosticTrackOutcomeSchema = exports_external.enum(DIAGNOSTIC_TRACK_OUTCOMES);
|
|
@@ -12176,8 +12124,31 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12176
12124
|
resultKey: RoutingResultKeySchema
|
|
12177
12125
|
}).strict()).min(1).max(ROUTING_TERMINAL_LIMIT)
|
|
12178
12126
|
}).strict();
|
|
12179
|
-
GRADE_VALUES = TIMEBACK_GRADES;
|
|
12180
12127
|
POINT_RESPONSE_PATTERN = /^-?\d+(?:\.\d+)? -?\d+(?:\.\d+)?$/;
|
|
12128
|
+
Identity = exports_external.string().min(1).max(512).refine((value) => value.trim() === value);
|
|
12129
|
+
AssessmentTranscriptSchema = exports_external.object({
|
|
12130
|
+
version: exports_external.literal(1),
|
|
12131
|
+
attemptId: Identity,
|
|
12132
|
+
contentRevision: Identity,
|
|
12133
|
+
administrations: exports_external.array(exports_external.object({
|
|
12134
|
+
submissionId: Identity,
|
|
12135
|
+
itemIdentifier: Identity,
|
|
12136
|
+
routingNodeKey: Identity.optional(),
|
|
12137
|
+
responses: exports_external.record(Identity, exports_external.custom(isAssessmentResponseValue))
|
|
12138
|
+
}).strict()).max(1000)
|
|
12139
|
+
}).strict().refine((value) => new TextEncoder().encode(JSON.stringify(value)).length <= 256000, {
|
|
12140
|
+
message: "Assessment transcript exceeds 256 KB"
|
|
12141
|
+
});
|
|
12142
|
+
AssessmentTranscriptError = class AssessmentTranscriptError extends Error {
|
|
12143
|
+
index;
|
|
12144
|
+
code;
|
|
12145
|
+
constructor(message, index = 0, code = "ASSESSMENT_FLOW_VIOLATION") {
|
|
12146
|
+
super(message);
|
|
12147
|
+
this.name = "AssessmentTranscriptError";
|
|
12148
|
+
this.index = index;
|
|
12149
|
+
this.code = code;
|
|
12150
|
+
}
|
|
12151
|
+
};
|
|
12181
12152
|
ZERO_WIDTH_CHARACTERS = /[\u200B\uFEFF\u200C\u200D]/g;
|
|
12182
12153
|
CURLY_SINGLE_QUOTES = /[\u2018\u2019\u201A\u201B]/g;
|
|
12183
12154
|
CURLY_DOUBLE_QUOTES = /[\u201C\u201D\u201E\u201F]/g;
|
|
@@ -12269,6 +12240,10 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12269
12240
|
bankRevision: IdentitySchema,
|
|
12270
12241
|
standard: AssessmentStandardRefSchema
|
|
12271
12242
|
}).strict();
|
|
12243
|
+
FixedChildResultContextV1Schema = exports_external.object({
|
|
12244
|
+
kind: exports_external.literal("fixed"),
|
|
12245
|
+
purpose: exports_external.enum(["mastery", "end_of_course", "diagnostic"])
|
|
12246
|
+
}).strict();
|
|
12272
12247
|
DiagnosticChildResultContextV1Schema = exports_external.object({
|
|
12273
12248
|
kind: exports_external.literal("platform-routed-diagnostic"),
|
|
12274
12249
|
definitionId: IdentitySchema,
|
|
@@ -12288,6 +12263,11 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12288
12263
|
isCorrect: exports_external.boolean().optional(),
|
|
12289
12264
|
context: ReviewChildResultContextV1Schema
|
|
12290
12265
|
}).strict();
|
|
12266
|
+
FixedChildResultPayloadV1Schema = exports_external.object({
|
|
12267
|
+
...CommonPayloadShape,
|
|
12268
|
+
isCorrect: exports_external.boolean().optional(),
|
|
12269
|
+
context: FixedChildResultContextV1Schema
|
|
12270
|
+
}).strict();
|
|
12291
12271
|
DiagnosticChildResultPayloadV1Schema = exports_external.object({
|
|
12292
12272
|
...CommonPayloadShape,
|
|
12293
12273
|
isCorrect: exports_external.boolean(),
|
|
@@ -12303,7 +12283,8 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12303
12283
|
});
|
|
12304
12284
|
AssessmentChildResultPayloadV1Schema = exports_external.union([
|
|
12305
12285
|
ReviewChildResultPayloadV1Schema,
|
|
12306
|
-
DiagnosticChildResultPayloadV1Schema
|
|
12286
|
+
DiagnosticChildResultPayloadV1Schema,
|
|
12287
|
+
FixedChildResultPayloadV1Schema
|
|
12307
12288
|
]);
|
|
12308
12289
|
AssessmentChildResultCommandV1Schema = exports_external.object({
|
|
12309
12290
|
kind: exports_external.literal(ASSESSMENT_CHILD_RESULT_KIND),
|
|
@@ -12380,9 +12361,19 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12380
12361
|
StartAssessmentRequestBodySchema = exports_external.intersection(StartAssessmentBodySchema, exports_external.object({ preparationReceipt: AssessmentPreparationReceiptSchema.optional() }));
|
|
12381
12362
|
SaveAssessmentBodySchema = exports_external.object({
|
|
12382
12363
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12383
|
-
|
|
12364
|
+
transcript: AssessmentTranscriptSchema
|
|
12365
|
+
});
|
|
12366
|
+
RecordAssessmentProgressBodySchema = SaveAssessmentBodySchema.extend({
|
|
12367
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12368
|
+
itemIdentifier: ResponseKeySchema2
|
|
12384
12369
|
});
|
|
12370
|
+
ResumeAssessmentBodySchema = exports_external.object({
|
|
12371
|
+
transcript: AssessmentTranscriptSchema.optional()
|
|
12372
|
+
});
|
|
12373
|
+
ResumeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(ResumeAssessmentBodySchema.shape);
|
|
12385
12374
|
SubmitAssessmentItemBodySchema = exports_external.object({
|
|
12375
|
+
assessmentToken: exports_external.string().min(1).max(32000),
|
|
12376
|
+
transcript: AssessmentTranscriptSchema,
|
|
12386
12377
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12387
12378
|
submissionId: exports_external.string().trim().min(1),
|
|
12388
12379
|
itemIdentifier: ResponseKeySchema2,
|
|
@@ -12390,6 +12381,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12390
12381
|
responses: exports_external.record(ResponseKeySchema2, AssessmentResponseValueSchema2.nullable())
|
|
12391
12382
|
});
|
|
12392
12383
|
SubmitAssessmentBodySchema = exports_external.object({
|
|
12384
|
+
transcript: AssessmentTranscriptSchema,
|
|
12393
12385
|
expectedResponseVersion: exports_external.number().int().nonnegative(),
|
|
12394
12386
|
submissionId: exports_external.string().trim().min(1),
|
|
12395
12387
|
session: exports_external.custom(isAssessmentSessionTiming, {
|
|
@@ -12408,6 +12400,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12408
12400
|
PrepareRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
|
|
12409
12401
|
StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentRequestBodySchema);
|
|
12410
12402
|
SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
|
|
12403
|
+
RecordRuntimeAssessmentProgressRequestSchema = AssessmentRuntimeIdentitySchema.extend(RecordAssessmentProgressBodySchema.shape);
|
|
12411
12404
|
SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
|
|
12412
12405
|
SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentBodySchema.shape).extend({
|
|
12413
12406
|
appName: exports_external.string().trim().min(1),
|
|
@@ -100207,22 +100200,6 @@ var init_locks = __esm(() => {
|
|
|
100207
100200
|
});
|
|
100208
100201
|
|
|
100209
100202
|
// ../api-core/src/utils/assessment-runtime-lock.util.ts
|
|
100210
|
-
async function crossAssessmentAttemptLockBarrier(lock, db2, attemptId) {
|
|
100211
|
-
const maxAttempts = 5;
|
|
100212
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
|
|
100213
|
-
try {
|
|
100214
|
-
await lock(db2, [assessmentRuntimeAttemptLockKey(attemptId)], async () => {
|
|
100215
|
-
return;
|
|
100216
|
-
});
|
|
100217
|
-
return;
|
|
100218
|
-
} catch (error88) {
|
|
100219
|
-
if (!(error88 instanceof ServiceUnavailableError) || attempt === maxAttempts) {
|
|
100220
|
-
throw error88;
|
|
100221
|
-
}
|
|
100222
|
-
await sleep(25);
|
|
100223
|
-
}
|
|
100224
|
-
}
|
|
100225
|
-
}
|
|
100226
100203
|
function normalizeKeys(keys) {
|
|
100227
100204
|
return [...new Set(keys)].toSorted();
|
|
100228
100205
|
}
|
|
@@ -100295,17 +100272,6 @@ var init_assessment_runtime_lock_util = __esm(() => {
|
|
|
100295
100272
|
});
|
|
100296
100273
|
|
|
100297
100274
|
// ../api-core/src/utils/timeback-assessment-runtime.util.ts
|
|
100298
|
-
function assessmentPreparationContentionError(expectedResponseVersion, responseVersion, kind) {
|
|
100299
|
-
if (expectedResponseVersion === responseVersion) {
|
|
100300
|
-
return new ServiceUnavailableError(`The ${kind} response state changed repeatedly while this item was being prepared. Try again shortly.`, {
|
|
100301
|
-
retryable: true,
|
|
100302
|
-
reason: "PREPARATION_CONTENTION",
|
|
100303
|
-
expectedResponseVersion,
|
|
100304
|
-
responseVersion
|
|
100305
|
-
});
|
|
100306
|
-
}
|
|
100307
|
-
return AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, responseVersion));
|
|
100308
|
-
}
|
|
100309
100275
|
function assessmentTrackableCourse(integration) {
|
|
100310
100276
|
if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
|
|
100311
100277
|
return null;
|
|
@@ -100371,7 +100337,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100371
100337
|
...input.activityData ? { activityData: input.activityData } : {},
|
|
100372
100338
|
assessment: assessmentPresentationForAttempt(input.assessment, input.attemptId),
|
|
100373
100339
|
responses: metadata2.responses,
|
|
100374
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
100340
|
+
itemSubmissions: assessmentFlowForPurpose(metadata2.purpose) === "item-submit" ? metadata2.itemSubmissions : [],
|
|
100375
100341
|
score: null,
|
|
100376
100342
|
selection
|
|
100377
100343
|
};
|
|
@@ -100421,151 +100387,9 @@ async function resolveAvailableAssessmentAttempt(initialDecision, select3, resum
|
|
|
100421
100387
|
}
|
|
100422
100388
|
return { kind: "continue", decision };
|
|
100423
100389
|
}
|
|
100424
|
-
function buildReviewItemResultMetadata(input) {
|
|
100425
|
-
const administration = itemAdministration(input.metadata.itemSubmissions, input.selection.itemIdentifier);
|
|
100426
|
-
if (!administration) {
|
|
100427
|
-
throw new Error(`Cannot create a review result for unadministered candidate ${input.selection.itemIdentifier}`);
|
|
100428
|
-
}
|
|
100429
|
-
return {
|
|
100430
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
100431
|
-
parentAttemptId: input.parentAttemptId,
|
|
100432
|
-
activityId: input.metadata.activityId,
|
|
100433
|
-
integrationId: input.metadata.integrationId,
|
|
100434
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
100435
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
100436
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
100437
|
-
standard: input.selection.standard,
|
|
100438
|
-
deliveredAt: administration.submittedAt,
|
|
100439
|
-
responseVersion: input.responseVersion ?? input.metadata.responseVersion,
|
|
100440
|
-
responses: input.metadata.responses[input.selection.itemIdentifier] ?? {},
|
|
100441
|
-
...input.submissionId ? { submissionId: input.submissionId } : {},
|
|
100442
|
-
...input.isCorrect === undefined ? {} : { isCorrect: input.isCorrect }
|
|
100443
|
-
};
|
|
100444
|
-
}
|
|
100445
|
-
function buildOpenReviewChildResult(input) {
|
|
100446
|
-
return {
|
|
100447
|
-
status: "active",
|
|
100448
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100449
|
-
student: input.student,
|
|
100450
|
-
score: 0,
|
|
100451
|
-
scoreDate: input.metadata.deliveredAt,
|
|
100452
|
-
...ASSESSMENT_ATTEMPT_OPEN,
|
|
100453
|
-
metadata: {
|
|
100454
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: input.metadata
|
|
100455
|
-
}
|
|
100456
|
-
};
|
|
100457
|
-
}
|
|
100458
100390
|
function diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier) {
|
|
100459
100391
|
return deterministicUUID(["playcademy:diagnostic-question-line-item:v1", parentLineItemId, itemIdentifier].join("\x00"));
|
|
100460
100392
|
}
|
|
100461
|
-
function diagnosticQuestionResultId(parentAttemptId, childLineItemId) {
|
|
100462
|
-
return deterministicUUID(["playcademy:diagnostic-question-result:v1", parentAttemptId, childLineItemId].join("\x00"));
|
|
100463
|
-
}
|
|
100464
|
-
function diagnosticAdministration(input) {
|
|
100465
|
-
const ledgerIndex = input.metadata.diagnostic.ledger.findIndex((entry) => entry.routingNodeKey === input.ledgerEntry.routingNodeKey);
|
|
100466
|
-
const persistedEntry = input.metadata.diagnostic.ledger[ledgerIndex];
|
|
100467
|
-
const administration = input.metadata.itemSubmissions[ledgerIndex];
|
|
100468
|
-
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) {
|
|
100469
|
-
throw new Error(`Cannot create a diagnostic result for uncommitted routing node ${input.ledgerEntry.routingNodeKey}`);
|
|
100470
|
-
}
|
|
100471
|
-
return administration;
|
|
100472
|
-
}
|
|
100473
|
-
function buildDiagnosticItemResultMetadata(input) {
|
|
100474
|
-
const administration = diagnosticAdministration(input);
|
|
100475
|
-
return {
|
|
100476
|
-
version: PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_VERSION,
|
|
100477
|
-
parentAttemptId: input.parentAttemptId,
|
|
100478
|
-
activityId: input.metadata.activityId,
|
|
100479
|
-
integrationId: input.metadata.integrationId,
|
|
100480
|
-
enrollmentId: input.metadata.enrollmentId,
|
|
100481
|
-
definitionId: input.metadata.diagnostic.definitionId,
|
|
100482
|
-
diagnosticKey: input.metadata.diagnostic.diagnosticKey,
|
|
100483
|
-
routingRevision: input.metadata.diagnostic.routingRevision,
|
|
100484
|
-
contentRevision: input.metadata.selectedTest.contentRevision,
|
|
100485
|
-
stageKey: input.ledgerEntry.stageKey,
|
|
100486
|
-
trackKey: input.ledgerEntry.trackKey,
|
|
100487
|
-
routingNodeKey: input.ledgerEntry.routingNodeKey,
|
|
100488
|
-
itemIdentifier: input.ledgerEntry.itemIdentifier,
|
|
100489
|
-
deliveredAt: administration.submittedAt,
|
|
100490
|
-
responseVersion: administration.responseVersion,
|
|
100491
|
-
responses: input.metadata.responses[input.ledgerEntry.itemIdentifier] ?? {},
|
|
100492
|
-
submissionId: administration.submissionId,
|
|
100493
|
-
isCorrect: input.ledgerEntry.isCorrect
|
|
100494
|
-
};
|
|
100495
|
-
}
|
|
100496
|
-
function resolveDiagnosticChildResult(input) {
|
|
100497
|
-
if (!input.childResult) {
|
|
100498
|
-
return { action: "create" };
|
|
100499
|
-
}
|
|
100500
|
-
const childMetadata = playcademyDiagnosticAssessmentItemResultMetadata(input.childResult.metadata);
|
|
100501
|
-
const expectedMetadata = buildDiagnosticItemResultMetadata({
|
|
100502
|
-
metadata: input.parentMetadata,
|
|
100503
|
-
ledgerEntry: input.ledgerEntry,
|
|
100504
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
100505
|
-
});
|
|
100506
|
-
if (input.childResult.assessmentLineItem.sourcedId !== input.childLineItemId || input.childResult.student.sourcedId !== input.parentResult.student.sourcedId || !childMetadata || canonicalJson2(childMetadata) !== canonicalJson2(expectedMetadata)) {
|
|
100507
|
-
return { action: "restore", reason: "incompatible" };
|
|
100508
|
-
}
|
|
100509
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: expectedMetadata } : { action: "restore", reason: "inactive" };
|
|
100510
|
-
}
|
|
100511
|
-
function buildFinalizedDiagnosticChildResult(input) {
|
|
100512
|
-
const administration = diagnosticAdministration(input);
|
|
100513
|
-
const metadata2 = buildDiagnosticItemResultMetadata(input);
|
|
100514
|
-
return {
|
|
100515
|
-
resultUpdate: {
|
|
100516
|
-
status: "active",
|
|
100517
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100518
|
-
student: input.student,
|
|
100519
|
-
score: administration.score.earned,
|
|
100520
|
-
scoreDate: administration.submittedAt,
|
|
100521
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
100522
|
-
metadata: {
|
|
100523
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
100524
|
-
}
|
|
100525
|
-
}
|
|
100526
|
-
};
|
|
100527
|
-
}
|
|
100528
|
-
function buildFinalizedReviewChildResult(input) {
|
|
100529
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
100530
|
-
metadata: input.metadata,
|
|
100531
|
-
selection: input.selection,
|
|
100532
|
-
parentAttemptId: input.parentAttemptId,
|
|
100533
|
-
submissionId: input.submissionId,
|
|
100534
|
-
isCorrect: input.itemResult.isCorrect
|
|
100535
|
-
});
|
|
100536
|
-
return {
|
|
100537
|
-
resultUpdate: {
|
|
100538
|
-
status: "active",
|
|
100539
|
-
assessmentLineItem: { sourcedId: input.childLineItemId },
|
|
100540
|
-
student: input.student,
|
|
100541
|
-
score: input.itemResult.score.earned,
|
|
100542
|
-
scoreDate: input.timestamp,
|
|
100543
|
-
...ASSESSMENT_ATTEMPT_COMPLETED,
|
|
100544
|
-
metadata: {
|
|
100545
|
-
[PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY]: metadata2
|
|
100546
|
-
}
|
|
100547
|
-
},
|
|
100548
|
-
outcome: {
|
|
100549
|
-
itemIdentifier: input.selection.itemIdentifier,
|
|
100550
|
-
standard: input.selection.standard,
|
|
100551
|
-
answered: Object.keys(input.metadata.responses[input.selection.itemIdentifier] ?? {}).length > 0,
|
|
100552
|
-
score: input.itemResult.score,
|
|
100553
|
-
isCorrect: input.itemResult.isCorrect
|
|
100554
|
-
}
|
|
100555
|
-
};
|
|
100556
|
-
}
|
|
100557
|
-
function resolveReviewChildResult(input) {
|
|
100558
|
-
if (!input.childResult) {
|
|
100559
|
-
return { action: "create" };
|
|
100560
|
-
}
|
|
100561
|
-
const childMetadata = playcademyAssessmentItemResultMetadata(input.childResult.metadata);
|
|
100562
|
-
const expectedResponses = input.parentMetadata.responses[input.selection.itemIdentifier] ?? {};
|
|
100563
|
-
const administration = itemAdministration(input.parentMetadata.itemSubmissions, input.selection.itemIdentifier);
|
|
100564
|
-
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)) {
|
|
100565
|
-
return { action: "restore", reason: "incompatible" };
|
|
100566
|
-
}
|
|
100567
|
-
return input.childResult.status === "active" ? { action: "reuse", metadata: childMetadata } : { action: "restore", reason: "inactive" };
|
|
100568
|
-
}
|
|
100569
100393
|
function buildAssessmentSubmitResult(input) {
|
|
100570
100394
|
if (!input.metadata.score || !input.metadata.submissionId) {
|
|
100571
100395
|
return null;
|
|
@@ -100951,32 +100775,12 @@ function assessmentFixtureScoringKeys(assessment, questions) {
|
|
|
100951
100775
|
...Object.keys(responseAreas).length > 0 ? { responseAreas } : {}
|
|
100952
100776
|
};
|
|
100953
100777
|
}
|
|
100954
|
-
function prepareDiagnosticAssessmentResponses(assessment, current, input) {
|
|
100955
|
-
const update2 = { [input.itemIdentifier]: input.responses };
|
|
100956
|
-
validateAssessmentResponseUpdate(assessment, update2);
|
|
100957
|
-
const responses = applyAssessmentResponseUpdate(current, update2);
|
|
100958
|
-
const itemResponses = responses[input.itemIdentifier];
|
|
100959
|
-
validateAssessmentDraftResponses(assessment, itemResponses ? { [input.itemIdentifier]: itemResponses } : {});
|
|
100960
|
-
return responses;
|
|
100961
|
-
}
|
|
100962
100778
|
function validateAssessmentResponseUpdate(assessment, update2) {
|
|
100963
100779
|
const message = assessmentResponseUpdateValidationMessage(assessment, update2);
|
|
100964
100780
|
if (message) {
|
|
100965
100781
|
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100966
100782
|
}
|
|
100967
100783
|
}
|
|
100968
|
-
function validateAssessmentResponses(assessment, responses) {
|
|
100969
|
-
const message = assessmentResponseValidationMessage(assessment, responses);
|
|
100970
|
-
if (message) {
|
|
100971
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100972
|
-
}
|
|
100973
|
-
}
|
|
100974
|
-
function validateAssessmentDraftResponses(assessment, responses) {
|
|
100975
|
-
const message = assessmentDraftResponseValidationMessage(assessment, responses);
|
|
100976
|
-
if (message) {
|
|
100977
|
-
throw new AssessmentRuntimeError("INVALID_RESPONSE", message);
|
|
100978
|
-
}
|
|
100979
|
-
}
|
|
100980
100784
|
function assessmentScoringRequests(assessment, responses) {
|
|
100981
100785
|
return assessment.items.map((item) => ({
|
|
100982
100786
|
itemIdentifier: item.identifier,
|
|
@@ -101016,6 +100820,21 @@ function assessmentScoreFromPercentage(percentage, possible) {
|
|
|
101016
100820
|
const earned = Math.round(normalized * possible * 1e6) / 1e6;
|
|
101017
100821
|
return { earned, possible, normalized };
|
|
101018
100822
|
}
|
|
100823
|
+
function buildAssessmentResultCheckpoint(input) {
|
|
100824
|
+
return {
|
|
100825
|
+
status: input.result.status,
|
|
100826
|
+
assessmentLineItem: input.result.assessmentLineItem,
|
|
100827
|
+
student: input.result.student,
|
|
100828
|
+
score: input.result.score ?? 0,
|
|
100829
|
+
scoreDate: input.result.scoreDate,
|
|
100830
|
+
...ASSESSMENT_ATTEMPT_OPEN,
|
|
100831
|
+
metadata: mergeAssessmentRuntimeMetadata(input.result.metadata, {
|
|
100832
|
+
...input.metadata,
|
|
100833
|
+
transcript: input.transcript,
|
|
100834
|
+
updatedAt: input.timestamp
|
|
100835
|
+
})
|
|
100836
|
+
};
|
|
100837
|
+
}
|
|
101019
100838
|
function buildAssessmentResultSubmission(input) {
|
|
101020
100839
|
const metadata2 = {
|
|
101021
100840
|
...input.metadata,
|
|
@@ -102420,6 +102239,183 @@ var init_timeback_assessment_artifacts_util = __esm(() => {
|
|
|
102420
102239
|
init_timeback_review_mapping_util();
|
|
102421
102240
|
});
|
|
102422
102241
|
|
|
102242
|
+
// ../api-core/src/utils/timeback-assessment-scoring.util.ts
|
|
102243
|
+
async function scoreCachedAssessmentItems(input) {
|
|
102244
|
+
const selected = input.assessment.items.filter((item) => input.itemIdentifiers.has(item.identifier));
|
|
102245
|
+
const keys = new Map(await Promise.all(selected.map(async (item) => [
|
|
102246
|
+
item.identifier,
|
|
102247
|
+
await sha256Hex(canonicalJson2({
|
|
102248
|
+
identity: input.artifactIdentity,
|
|
102249
|
+
purpose: input.purpose,
|
|
102250
|
+
itemIdentifier: item.identifier,
|
|
102251
|
+
responses: input.responses[item.identifier] ?? {},
|
|
102252
|
+
strategy: input.artifact?.items.find((candidate) => candidate.itemIdentifier === item.identifier)?.strategy ?? "qti"
|
|
102253
|
+
}))
|
|
102254
|
+
])));
|
|
102255
|
+
const scored = new Map;
|
|
102256
|
+
for (const item of selected) {
|
|
102257
|
+
const cached3 = input.cache.get(keys.get(item.identifier));
|
|
102258
|
+
if (cached3) {
|
|
102259
|
+
scored.set(item.identifier, cached3);
|
|
102260
|
+
}
|
|
102261
|
+
}
|
|
102262
|
+
const missing = new Set(selected.filter((item) => !scored.has(item.identifier)).map((item) => item.identifier));
|
|
102263
|
+
const fresh = await scoreAssessmentItems({ ...input, itemIdentifiers: missing });
|
|
102264
|
+
for (const result of fresh) {
|
|
102265
|
+
input.cache.set(keys.get(result.itemIdentifier), result);
|
|
102266
|
+
scored.set(result.itemIdentifier, result);
|
|
102267
|
+
}
|
|
102268
|
+
return selected.map((item) => scored.get(item.identifier));
|
|
102269
|
+
}
|
|
102270
|
+
async function scoreAssessmentItems({
|
|
102271
|
+
assessment,
|
|
102272
|
+
responses,
|
|
102273
|
+
artifactIdentity,
|
|
102274
|
+
purpose,
|
|
102275
|
+
itemIdentifiers,
|
|
102276
|
+
artifact,
|
|
102277
|
+
processResponse,
|
|
102278
|
+
onEvent
|
|
102279
|
+
}) {
|
|
102280
|
+
const scoringItems = assessmentScoringRequests(assessment, responses).filter((item) => !itemIdentifiers || itemIdentifiers.has(item.itemIdentifier));
|
|
102281
|
+
const artifactItems = new Map(artifact?.items.map((item) => [item.itemIdentifier, item]));
|
|
102282
|
+
const scored = new Map;
|
|
102283
|
+
const qti2 = [];
|
|
102284
|
+
for (const item of scoringItems) {
|
|
102285
|
+
const artifactItem = artifactItems.get(item.itemIdentifier);
|
|
102286
|
+
let fallback2 = null;
|
|
102287
|
+
if (!artifact) {
|
|
102288
|
+
fallback2 = { reason: "artifact-unavailable" };
|
|
102289
|
+
} else if (!artifactItem) {
|
|
102290
|
+
fallback2 = { reason: "artifact-item-missing" };
|
|
102291
|
+
} else if (artifactItem.strategy === "qti") {
|
|
102292
|
+
fallback2 = { reason: "artifact-qti", artifactReason: artifactItem.reason };
|
|
102293
|
+
} else {
|
|
102294
|
+
const startedAt = Date.now();
|
|
102295
|
+
try {
|
|
102296
|
+
scored.set(item.itemIdentifier, {
|
|
102297
|
+
...scorePlatformAssessmentItem(artifactItem, responses[item.itemIdentifier]),
|
|
102298
|
+
grading: PLATFORM_CHILD_GRADING
|
|
102299
|
+
});
|
|
102300
|
+
onEvent("assessment.item_scoring", {
|
|
102301
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102302
|
+
"app.assessment.purpose": purpose,
|
|
102303
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102304
|
+
"app.assessment.grading_source": "platform-artifact",
|
|
102305
|
+
"app.assessment.grading_duration_ms": Date.now() - startedAt
|
|
102306
|
+
});
|
|
102307
|
+
} catch (error88) {
|
|
102308
|
+
fallback2 = { reason: "platform-grader-error", error: error88 };
|
|
102309
|
+
}
|
|
102310
|
+
}
|
|
102311
|
+
if (fallback2) {
|
|
102312
|
+
qti2.push({ item, ...fallback2 });
|
|
102313
|
+
}
|
|
102314
|
+
}
|
|
102315
|
+
if (qti2.length > 0) {
|
|
102316
|
+
const fallbackTasks = qti2.flatMap(({ item, reason, artifactReason, error: graderError }) => {
|
|
102317
|
+
const interactionResults = [];
|
|
102318
|
+
let startedAt;
|
|
102319
|
+
let completedInteractions = 0;
|
|
102320
|
+
let failed = false;
|
|
102321
|
+
const omittedResponseCount = item.requests.filter((request) => responses[item.itemIdentifier]?.[request.identifier] === undefined).length;
|
|
102322
|
+
let qtiRemoteInteractionCount = 0;
|
|
102323
|
+
let syntheticIncorrectInteractionCount = 0;
|
|
102324
|
+
function fallbackEvent(outcome) {
|
|
102325
|
+
onEvent("assessment.item_scoring_fallback", {
|
|
102326
|
+
"app.assessment.qti_item_identifier": item.itemIdentifier,
|
|
102327
|
+
"app.assessment.purpose": purpose,
|
|
102328
|
+
"app.assessment.artifact_identity_kind": artifactIdentity.kind,
|
|
102329
|
+
"app.assessment.grading_source": omittedResponseCount > 0 ? "platform-qti-adapter" : "timeback-qti",
|
|
102330
|
+
"app.assessment.fallback_reason": reason,
|
|
102331
|
+
"app.assessment.fallback_duration_ms": startedAt === undefined ? 0 : Date.now() - startedAt,
|
|
102332
|
+
"app.assessment.qti_remote_interaction_count": qtiRemoteInteractionCount,
|
|
102333
|
+
"app.assessment.synthetic_incorrect_interaction_count": syntheticIncorrectInteractionCount,
|
|
102334
|
+
...artifactReason ? { "app.assessment.artifact_fallback_reason": artifactReason } : {},
|
|
102335
|
+
...graderError ? {
|
|
102336
|
+
"app.assessment.platform_grader_exception_type": errorType(graderError)
|
|
102337
|
+
} : {},
|
|
102338
|
+
...outcome
|
|
102339
|
+
});
|
|
102340
|
+
}
|
|
102341
|
+
function completeItem() {
|
|
102342
|
+
const earned = assessmentItemEarnedScore(interactionResults.map((result) => Number.isFinite(result.score) ? result.score : 0), item.maxScore);
|
|
102343
|
+
const possible = item.maxScore;
|
|
102344
|
+
fallbackEvent({ "app.assessment.fallback_outcome": "succeeded" });
|
|
102345
|
+
scored.set(item.itemIdentifier, {
|
|
102346
|
+
itemIdentifier: item.itemIdentifier,
|
|
102347
|
+
score: {
|
|
102348
|
+
earned,
|
|
102349
|
+
possible,
|
|
102350
|
+
normalized: possible === 0 ? 0 : earned / possible
|
|
102351
|
+
},
|
|
102352
|
+
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
102353
|
+
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
102354
|
+
});
|
|
102355
|
+
}
|
|
102356
|
+
if (item.requests.length === 0) {
|
|
102357
|
+
completeItem();
|
|
102358
|
+
return [];
|
|
102359
|
+
}
|
|
102360
|
+
return item.requests.map((request, index2) => async () => {
|
|
102361
|
+
startedAt ??= Date.now();
|
|
102362
|
+
try {
|
|
102363
|
+
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
102364
|
+
if (response === undefined) {
|
|
102365
|
+
syntheticIncorrectInteractionCount += 1;
|
|
102366
|
+
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
102367
|
+
} else {
|
|
102368
|
+
qtiRemoteInteractionCount += 1;
|
|
102369
|
+
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
102370
|
+
}
|
|
102371
|
+
completedInteractions += 1;
|
|
102372
|
+
if (completedInteractions === item.requests.length && !failed) {
|
|
102373
|
+
completeItem();
|
|
102374
|
+
}
|
|
102375
|
+
} catch (error88) {
|
|
102376
|
+
if (!failed) {
|
|
102377
|
+
failed = true;
|
|
102378
|
+
fallbackEvent({
|
|
102379
|
+
"app.assessment.fallback_outcome": "failed",
|
|
102380
|
+
"exception.type": errorType(error88),
|
|
102381
|
+
"app.error.message": errorMessage(error88)
|
|
102382
|
+
});
|
|
102383
|
+
}
|
|
102384
|
+
throw error88;
|
|
102385
|
+
}
|
|
102386
|
+
});
|
|
102387
|
+
});
|
|
102388
|
+
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
102389
|
+
}
|
|
102390
|
+
return scoringItems.map((item) => {
|
|
102391
|
+
const result = scored.get(item.itemIdentifier);
|
|
102392
|
+
if (!result) {
|
|
102393
|
+
throw new Error(`Assessment item ${item.itemIdentifier} was not scored`);
|
|
102394
|
+
}
|
|
102395
|
+
return result;
|
|
102396
|
+
});
|
|
102397
|
+
}
|
|
102398
|
+
var QTI_CHILD_GRADING, PLATFORM_QTI_ADAPTER_CHILD_GRADING, PLATFORM_CHILD_GRADING, SCORING_CONCURRENCY = 4;
|
|
102399
|
+
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
102400
|
+
init_assessment_runtime2();
|
|
102401
|
+
init_assessment_artifact_envelope_util();
|
|
102402
|
+
init_timeback_assessment_runtime_util();
|
|
102403
|
+
QTI_CHILD_GRADING = {
|
|
102404
|
+
source: "timeback-qti",
|
|
102405
|
+
graderVersion: "timeback-qti-process-response-v1"
|
|
102406
|
+
};
|
|
102407
|
+
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
102408
|
+
source: "platform-qti-adapter",
|
|
102409
|
+
graderVersion: "platform-qti-adapter-v1",
|
|
102410
|
+
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
102411
|
+
};
|
|
102412
|
+
PLATFORM_CHILD_GRADING = {
|
|
102413
|
+
source: "platform-artifact",
|
|
102414
|
+
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
102415
|
+
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
102416
|
+
};
|
|
102417
|
+
});
|
|
102418
|
+
|
|
102423
102419
|
// ../api-core/src/utils/timeback-assessment-child-result.util.ts
|
|
102424
102420
|
async function buildSubmissionChildResultCommand(input) {
|
|
102425
102421
|
const { submission, context: context2 } = input;
|
|
@@ -102435,12 +102431,9 @@ async function buildSubmissionChildResultCommand(input) {
|
|
|
102435
102431
|
score: submission.score,
|
|
102436
102432
|
grading: input.grading
|
|
102437
102433
|
};
|
|
102438
|
-
if (context2.kind
|
|
102439
|
-
|
|
102440
|
-
|
|
102441
|
-
...typeof isCorrect === "boolean" ? { isCorrect } : {},
|
|
102442
|
-
context: context2
|
|
102443
|
-
}, submission.submittedAt);
|
|
102434
|
+
if (context2.kind !== "platform-routed-diagnostic") {
|
|
102435
|
+
const payload = { ...common2, ...typeof isCorrect === "boolean" ? { isCorrect } : {} };
|
|
102436
|
+
return context2.kind === "review" ? buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt) : buildAssessmentChildResultCommand({ ...payload, context: context2 }, submission.submittedAt);
|
|
102444
102437
|
}
|
|
102445
102438
|
if (typeof isCorrect !== "boolean") {
|
|
102446
102439
|
throw new Error("Diagnostic child results require determinate correctness");
|
|
@@ -102461,11 +102454,7 @@ function assessmentChildLineItemMetadataMatches(key, stored, expected) {
|
|
|
102461
102454
|
if (!Array.isArray(storedStandards) || !Array.isArray(expectedStandards)) {
|
|
102462
102455
|
return false;
|
|
102463
102456
|
}
|
|
102464
|
-
|
|
102465
|
-
function contains(standards, candidate) {
|
|
102466
|
-
return standards.some((standard) => canonicalJson2(standard) === canonicalJson2(candidate));
|
|
102467
|
-
}
|
|
102468
|
-
return sameDefinition && (expectedStandards.every((standard) => contains(storedStandards, standard)) || storedStandards.every((standard) => contains(expectedStandards, standard)));
|
|
102457
|
+
return canonicalJson2(reviewDefinitionWithoutStandards(storedDefinition)) === canonicalJson2(reviewDefinitionWithoutStandards(expectedDefinition));
|
|
102469
102458
|
}
|
|
102470
102459
|
function assessmentChildLineItemMatches(stored, expected) {
|
|
102471
102460
|
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) {
|
|
@@ -102523,19 +102512,14 @@ async function classifyAssessmentChildResult(input) {
|
|
|
102523
102512
|
result.scoreScale,
|
|
102524
102513
|
result.scorePercentile,
|
|
102525
102514
|
result.comment,
|
|
102526
|
-
result.learningObjectiveSet
|
|
102527
|
-
result.incomplete,
|
|
102528
|
-
result.late,
|
|
102529
|
-
result.missing
|
|
102515
|
+
result.learningObjectiveSet
|
|
102530
102516
|
];
|
|
102531
|
-
|
|
102517
|
+
const flags2 = [result.incomplete, result.late, result.missing];
|
|
102518
|
+
if (auxiliaryFields.some((value) => value !== undefined && value !== null) || flags2.some((value) => value !== undefined && value !== null && value !== "false")) {
|
|
102532
102519
|
return { outcome: "conflict", reason: "auxiliary-field-mismatch" };
|
|
102533
102520
|
}
|
|
102534
102521
|
return { outcome: "replay" };
|
|
102535
102522
|
}
|
|
102536
|
-
function hasApiStatus(error88, statusCode) {
|
|
102537
|
-
return isApiError(error88) && error88.statusCode === statusCode;
|
|
102538
|
-
}
|
|
102539
102523
|
async function persistAssessmentChildResult(input) {
|
|
102540
102524
|
const { assessmentResults, childLineItemId } = input;
|
|
102541
102525
|
const verification2 = await verifyAssessmentChildResultCommand(input.command);
|
|
@@ -102544,38 +102528,127 @@ async function persistAssessmentChildResult(input) {
|
|
|
102544
102528
|
}
|
|
102545
102529
|
const { command } = verification2;
|
|
102546
102530
|
const resultId = await assessmentChildResultId(command.payload.attempt.attemptId, command.payload.administration.submissionId);
|
|
102547
|
-
|
|
102548
|
-
|
|
102549
|
-
|
|
102531
|
+
await assessmentResults.upsert(resultId, buildAssessmentChildResultUpdate(command, childLineItemId));
|
|
102532
|
+
}
|
|
102533
|
+
function assessmentTranscriptChildLineItemId(context2, itemIdentifier) {
|
|
102534
|
+
return context2.metadata.purpose === "review" ? reviewQuestionLineItemId(context2.parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(context2.parentLineItemId, itemIdentifier);
|
|
102535
|
+
}
|
|
102536
|
+
async function reconcileAssessmentTranscriptChildren({
|
|
102537
|
+
context: context2,
|
|
102538
|
+
graded,
|
|
102539
|
+
only,
|
|
102540
|
+
assessmentResults,
|
|
102541
|
+
ensureLineItems,
|
|
102542
|
+
onPersisted
|
|
102543
|
+
}) {
|
|
102544
|
+
const metadata2 = graded.metadata;
|
|
102545
|
+
const assessmentKey = metadata2.selectedTest.assessmentKey;
|
|
102546
|
+
if (!assessmentKey) {
|
|
102547
|
+
throw new Error("Assessment context requires a selected assessment key");
|
|
102548
|
+
}
|
|
102549
|
+
const submissions = metadata2.itemSubmissions.filter((submission) => !only || only.has(submission.submissionId));
|
|
102550
|
+
const administered = new Set(submissions.map((submission) => submission.itemIdentifier));
|
|
102551
|
+
await ensureLineItems({
|
|
102552
|
+
items: graded.assessment.items.filter((item) => administered.has(item.identifier))
|
|
102553
|
+
});
|
|
102554
|
+
await runWithConcurrency(submissions, 4, async (submission) => {
|
|
102555
|
+
const index2 = submission.responseVersion - 1;
|
|
102556
|
+
let flow;
|
|
102557
|
+
if (metadata2.purpose === "review") {
|
|
102558
|
+
const selection = metadata2.review.selections.find((item) => item.itemIdentifier === submission.itemIdentifier);
|
|
102559
|
+
flow = {
|
|
102560
|
+
kind: "review",
|
|
102561
|
+
bankRevision: metadata2.review.bankRevision,
|
|
102562
|
+
standard: selection.standard
|
|
102563
|
+
};
|
|
102564
|
+
} else if (metadata2.purpose === "diagnostic" && metadata2.diagnostic) {
|
|
102565
|
+
flow = {
|
|
102566
|
+
kind: "platform-routed-diagnostic",
|
|
102567
|
+
definitionId: metadata2.diagnostic.definitionId,
|
|
102568
|
+
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
102569
|
+
routingRevision: metadata2.diagnostic.routingRevision,
|
|
102570
|
+
transition: metadata2.diagnostic.ledger[index2]
|
|
102571
|
+
};
|
|
102572
|
+
} else {
|
|
102573
|
+
flow = { kind: "fixed", purpose: metadata2.purpose };
|
|
102574
|
+
}
|
|
102575
|
+
const command = await buildSubmissionChildResultCommand({
|
|
102576
|
+
attempt: {
|
|
102577
|
+
attemptId: context2.attemptId,
|
|
102578
|
+
gameId: context2.gameId,
|
|
102579
|
+
studentId: context2.studentId,
|
|
102580
|
+
activityId: metadata2.activityId,
|
|
102581
|
+
courseId: metadata2.courseId,
|
|
102582
|
+
integrationId: metadata2.integrationId,
|
|
102583
|
+
enrollmentId: metadata2.enrollmentId,
|
|
102584
|
+
selectedTest: { ...metadata2.selectedTest, assessmentKey }
|
|
102585
|
+
},
|
|
102586
|
+
submission,
|
|
102587
|
+
responses: graded.transcript.administrations[index2].responses,
|
|
102588
|
+
grading: submission.grading ?? QTI_CHILD_GRADING,
|
|
102589
|
+
context: flow
|
|
102590
|
+
});
|
|
102591
|
+
await persistAssessmentChildResult({
|
|
102592
|
+
assessmentResults,
|
|
102550
102593
|
command,
|
|
102551
|
-
childLineItemId
|
|
102594
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, submission.itemIdentifier)
|
|
102552
102595
|
});
|
|
102553
|
-
|
|
102596
|
+
onPersisted?.(submission.submissionId);
|
|
102597
|
+
});
|
|
102598
|
+
}
|
|
102599
|
+
async function recoveredAssessmentAdministration(context2, result) {
|
|
102600
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
102601
|
+
if (!verified.ok) {
|
|
102602
|
+
return null;
|
|
102554
102603
|
}
|
|
102555
|
-
|
|
102556
|
-
|
|
102557
|
-
|
|
102558
|
-
|
|
102559
|
-
|
|
102604
|
+
const { command } = verified;
|
|
102605
|
+
const { attempt, administration, responses, context: flow } = command.payload;
|
|
102606
|
+
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)) {
|
|
102607
|
+
return null;
|
|
102608
|
+
}
|
|
102609
|
+
const disposition = await classifyAssessmentChildResult({
|
|
102610
|
+
result,
|
|
102611
|
+
command,
|
|
102612
|
+
childLineItemId: await assessmentTranscriptChildLineItemId(context2, administration.itemIdentifier)
|
|
102613
|
+
});
|
|
102614
|
+
if (disposition.outcome !== "replay") {
|
|
102615
|
+
return null;
|
|
102616
|
+
}
|
|
102617
|
+
return {
|
|
102618
|
+
sequence: administration.responseVersion,
|
|
102619
|
+
entry: {
|
|
102620
|
+
submissionId: administration.submissionId,
|
|
102621
|
+
itemIdentifier: administration.itemIdentifier,
|
|
102622
|
+
responses,
|
|
102623
|
+
...flow.kind === "platform-routed-diagnostic" ? { routingNodeKey: flow.transition.routingNodeKey } : {}
|
|
102624
|
+
}
|
|
102625
|
+
};
|
|
102626
|
+
}
|
|
102627
|
+
async function recoverAssessmentTranscript(context2, results) {
|
|
102628
|
+
const entries = new Map;
|
|
102629
|
+
for (const result of results) {
|
|
102630
|
+
const recovered = await recoveredAssessmentAdministration(context2, result);
|
|
102631
|
+
if (recovered) {
|
|
102632
|
+
const atPosition = entries.get(recovered.sequence) ?? new Map;
|
|
102633
|
+
atPosition.set(recovered.entry.submissionId, recovered.entry);
|
|
102634
|
+
entries.set(recovered.sequence, atPosition);
|
|
102560
102635
|
}
|
|
102561
102636
|
}
|
|
102562
|
-
|
|
102563
|
-
|
|
102564
|
-
|
|
102565
|
-
|
|
102566
|
-
|
|
102567
|
-
return { outcome: "created", resultId };
|
|
102568
|
-
} catch (error88) {
|
|
102569
|
-
if (!hasApiStatus(error88, 409)) {
|
|
102570
|
-
throw error88;
|
|
102637
|
+
const transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision);
|
|
102638
|
+
for (let sequence2 = 1;sequence2 <= entries.size; sequence2 += 1) {
|
|
102639
|
+
const candidates = entries.get(sequence2);
|
|
102640
|
+
if (!candidates || candidates.size !== 1) {
|
|
102641
|
+
break;
|
|
102571
102642
|
}
|
|
102643
|
+
transcript.administrations.push(candidates.values().next().value);
|
|
102572
102644
|
}
|
|
102573
|
-
return
|
|
102645
|
+
return transcript;
|
|
102574
102646
|
}
|
|
102575
|
-
var init_timeback_assessment_child_result_util = __esm(
|
|
102647
|
+
var init_timeback_assessment_child_result_util = __esm(() => {
|
|
102576
102648
|
init_src();
|
|
102577
102649
|
init_assessment_runtime2();
|
|
102578
|
-
|
|
102650
|
+
init_timeback_assessment_runtime_util();
|
|
102651
|
+
init_timeback_assessment_scoring_util();
|
|
102579
102652
|
});
|
|
102580
102653
|
|
|
102581
102654
|
// ../api-core/src/utils/timeback-assessment-preparation.util.ts
|
|
@@ -102680,6 +102753,7 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102680
102753
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
102681
102754
|
activityId: plan.activityId,
|
|
102682
102755
|
courseId: plan.courseId,
|
|
102756
|
+
courseName: plan.courseName,
|
|
102683
102757
|
integrationId: plan.integrationId,
|
|
102684
102758
|
enrollmentId: plan.enrollmentId,
|
|
102685
102759
|
selectedTest: plan.selectedTest,
|
|
@@ -102710,7 +102784,6 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102710
102784
|
testName,
|
|
102711
102785
|
routingRevision: plan.diagnostic.routingRevision,
|
|
102712
102786
|
ledger: [],
|
|
102713
|
-
routingSnapshots: [],
|
|
102714
102787
|
state: plan.diagnostic.initialState
|
|
102715
102788
|
}
|
|
102716
102789
|
};
|
|
@@ -103005,127 +103078,6 @@ var init_timeback_assessment_publication_util = __esm(() => {
|
|
|
103005
103078
|
MANAGED_ASSET_URL = new RegExp(`https?://[^\\s"')]+/(${ASSESSMENT_ASSET_KEY_PREFIX}v1/sha256/[a-f0-9]{64}\\.[a-z0-9]+)`, "gi");
|
|
103006
103079
|
});
|
|
103007
103080
|
|
|
103008
|
-
// ../api-core/src/utils/timeback-assessment-reconciliation.util.ts
|
|
103009
|
-
function assessmentChildProjectionIsTerminal(result) {
|
|
103010
|
-
const state = { inProgress: result.inProgress ?? "", scoreStatus: result.scoreStatus };
|
|
103011
|
-
return isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
103012
|
-
}
|
|
103013
|
-
async function reconcileReviewChildResults({
|
|
103014
|
-
initial,
|
|
103015
|
-
writeOpen,
|
|
103016
|
-
crossBarrier,
|
|
103017
|
-
readLatest,
|
|
103018
|
-
restoreTerminal
|
|
103019
|
-
}) {
|
|
103020
|
-
if (initial.metadata.purpose !== "review") {
|
|
103021
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
103022
|
-
}
|
|
103023
|
-
if (assessmentChildProjectionIsTerminal(initial.result)) {
|
|
103024
|
-
const settled = { result: initial.result, metadata: initial.metadata };
|
|
103025
|
-
await restoreTerminal(settled);
|
|
103026
|
-
return settled;
|
|
103027
|
-
}
|
|
103028
|
-
await writeOpen();
|
|
103029
|
-
await crossBarrier();
|
|
103030
|
-
const current = await readLatest();
|
|
103031
|
-
if (current.metadata.purpose !== "review") {
|
|
103032
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
103033
|
-
}
|
|
103034
|
-
const reviewed = { result: current.result, metadata: current.metadata };
|
|
103035
|
-
if (assessmentChildProjectionIsTerminal(current.result)) {
|
|
103036
|
-
await restoreTerminal(reviewed);
|
|
103037
|
-
}
|
|
103038
|
-
return reviewed;
|
|
103039
|
-
}
|
|
103040
|
-
var init_timeback_assessment_reconciliation_util = __esm(() => {
|
|
103041
|
-
init_assessment_runtime2();
|
|
103042
|
-
});
|
|
103043
|
-
|
|
103044
|
-
// ../api-core/src/utils/timeback-assessment-replay.util.ts
|
|
103045
|
-
function resolveDiagnosticItemReplay(metadata2, input) {
|
|
103046
|
-
const priorIndex = metadata2.itemSubmissions.findIndex((submission) => submission.submissionId === input.submissionId);
|
|
103047
|
-
if (priorIndex === -1) {
|
|
103048
|
-
return { action: "missing" };
|
|
103049
|
-
}
|
|
103050
|
-
const prior = metadata2.itemSubmissions[priorIndex];
|
|
103051
|
-
const ledgerEntry = metadata2.diagnostic.ledger[priorIndex];
|
|
103052
|
-
if (!prior || !ledgerEntry || prior.itemIdentifier !== input.itemIdentifier || ledgerEntry.routingNodeKey !== input.routingNodeKey || !itemResponseUpdateMatches(metadata2.responses[input.itemIdentifier], input.responses)) {
|
|
103053
|
-
return {
|
|
103054
|
-
action: "conflict",
|
|
103055
|
-
failure: assessmentFlowViolation("This diagnostic submission ID was already used for a different request.", {
|
|
103056
|
-
submissionId: input.submissionId,
|
|
103057
|
-
routingNodeKey: input.routingNodeKey,
|
|
103058
|
-
itemIdentifier: input.itemIdentifier
|
|
103059
|
-
})
|
|
103060
|
-
};
|
|
103061
|
-
}
|
|
103062
|
-
return {
|
|
103063
|
-
action: "replay",
|
|
103064
|
-
submission: prior,
|
|
103065
|
-
ledgerEntry,
|
|
103066
|
-
priorIndex,
|
|
103067
|
-
routing: metadata2.diagnostic.routingSnapshots?.[priorIndex]
|
|
103068
|
-
};
|
|
103069
|
-
}
|
|
103070
|
-
function buildAssessmentItemReplayResult(attemptId, metadata2, submission) {
|
|
103071
|
-
return {
|
|
103072
|
-
attemptId,
|
|
103073
|
-
responseVersion: metadata2.responseVersion,
|
|
103074
|
-
status: "in_progress",
|
|
103075
|
-
responses: metadata2.responses,
|
|
103076
|
-
itemSubmissions: metadata2.itemSubmissions,
|
|
103077
|
-
submission
|
|
103078
|
-
};
|
|
103079
|
-
}
|
|
103080
|
-
function diagnosticReceipt(submission, ledgerEntry) {
|
|
103081
|
-
return {
|
|
103082
|
-
submissionId: submission.submissionId,
|
|
103083
|
-
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
103084
|
-
itemIdentifier: submission.itemIdentifier,
|
|
103085
|
-
submittedAt: submission.submittedAt,
|
|
103086
|
-
responseVersion: submission.responseVersion,
|
|
103087
|
-
answered: submission.answered,
|
|
103088
|
-
score: submission.score,
|
|
103089
|
-
isCorrect: ledgerEntry.isCorrect
|
|
103090
|
-
};
|
|
103091
|
-
}
|
|
103092
|
-
function buildDiagnosticItemReplayResult(attemptId, submission, ledgerEntry, routing) {
|
|
103093
|
-
return {
|
|
103094
|
-
attemptId,
|
|
103095
|
-
responseVersion: submission.responseVersion,
|
|
103096
|
-
status: "in_progress",
|
|
103097
|
-
routing,
|
|
103098
|
-
submission: diagnosticReceipt(submission, ledgerEntry)
|
|
103099
|
-
};
|
|
103100
|
-
}
|
|
103101
|
-
function requireAssessmentMutationAccess(attemptId, integrationStatus, enrollment) {
|
|
103102
|
-
if (!enrollment || integrationStatus === "deactivated") {
|
|
103103
|
-
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
103104
|
-
}
|
|
103105
|
-
return enrollment;
|
|
103106
|
-
}
|
|
103107
|
-
function committedReviewItemResults(metadata2, assessment) {
|
|
103108
|
-
const available = new Set(assessment.items.map((item) => item.identifier));
|
|
103109
|
-
const seen = new Set;
|
|
103110
|
-
return metadata2.itemSubmissions.map((submission) => {
|
|
103111
|
-
if (!available.has(submission.itemIdentifier) || seen.has(submission.itemIdentifier)) {
|
|
103112
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The persisted review item-submission ledger is corrupt.", {
|
|
103113
|
-
itemIdentifier: submission.itemIdentifier
|
|
103114
|
-
}));
|
|
103115
|
-
}
|
|
103116
|
-
seen.add(submission.itemIdentifier);
|
|
103117
|
-
return {
|
|
103118
|
-
itemIdentifier: submission.itemIdentifier,
|
|
103119
|
-
score: submission.score,
|
|
103120
|
-
isCorrect: submission.isCorrect
|
|
103121
|
-
};
|
|
103122
|
-
});
|
|
103123
|
-
}
|
|
103124
|
-
var init_timeback_assessment_replay_util = __esm(() => {
|
|
103125
|
-
init_assessment_runtime2();
|
|
103126
|
-
init_errors2();
|
|
103127
|
-
});
|
|
103128
|
-
|
|
103129
103081
|
// ../api-core/src/utils/timeback-assessment-routing.util.ts
|
|
103130
103082
|
function diagnosticRoutingFallbackReason(artifact, routingRevision) {
|
|
103131
103083
|
if (!artifact) {
|
|
@@ -103275,154 +103227,255 @@ var init_timeback_assessment_rules_util = __esm(() => {
|
|
|
103275
103227
|
init_errors2();
|
|
103276
103228
|
});
|
|
103277
103229
|
|
|
103278
|
-
// ../api-core/src/utils/timeback-assessment-
|
|
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
|
-
|
|
103317
|
-
|
|
103230
|
+
// ../api-core/src/utils/timeback-assessment-replay.util.ts
|
|
103231
|
+
function diagnosticReceipt(submission, ledgerEntry) {
|
|
103232
|
+
return {
|
|
103233
|
+
submissionId: submission.submissionId,
|
|
103234
|
+
routingNodeKey: ledgerEntry.routingNodeKey,
|
|
103235
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103236
|
+
submittedAt: submission.submittedAt,
|
|
103237
|
+
responseVersion: submission.responseVersion,
|
|
103238
|
+
answered: submission.answered,
|
|
103239
|
+
score: submission.score,
|
|
103240
|
+
isCorrect: ledgerEntry.isCorrect
|
|
103241
|
+
};
|
|
103242
|
+
}
|
|
103243
|
+
|
|
103244
|
+
// ../api-core/src/utils/timeback-assessment-transcript.util.ts
|
|
103245
|
+
function parseAssessmentTranscript(value) {
|
|
103246
|
+
const parsed = AssessmentTranscriptSchema.safeParse(value);
|
|
103247
|
+
if (!parsed.success) {
|
|
103248
|
+
throw new ValidationError("Invalid assessment transcript");
|
|
103249
|
+
}
|
|
103250
|
+
return parsed.data;
|
|
103251
|
+
}
|
|
103252
|
+
function assessmentTokenMetadata(metadata2) {
|
|
103253
|
+
const context2 = { ...metadata2, updatedAt: metadata2.startedAt };
|
|
103254
|
+
delete context2.transcript;
|
|
103255
|
+
return context2;
|
|
103256
|
+
}
|
|
103257
|
+
function requireAssessmentTokenContext(context2, scope) {
|
|
103258
|
+
if (!context2 || context2.gameId !== scope.gameId || context2.studentId !== scope.studentId || context2.attemptId !== scope.attemptId) {
|
|
103259
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(scope.attemptId));
|
|
103260
|
+
}
|
|
103261
|
+
return context2;
|
|
103262
|
+
}
|
|
103263
|
+
function requireAssessmentFeedbackContext(tokenContext, scope) {
|
|
103264
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103265
|
+
const metadata2 = context2.metadata;
|
|
103266
|
+
if (assessmentFlowForPurpose(metadata2.purpose) === "attempt-submit" && !(metadata2.purpose === "diagnostic" && metadata2.diagnostic)) {
|
|
103267
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("This assessment accepts a complete attempt, not per-item feedback."));
|
|
103268
|
+
}
|
|
103269
|
+
return context2;
|
|
103270
|
+
}
|
|
103271
|
+
function prepareAssessmentProgress(tokenContext, scope, input) {
|
|
103272
|
+
const context2 = requireAssessmentTokenContext(tokenContext, scope);
|
|
103273
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
103274
|
+
const progress = assessmentProgressAdministration(context2.metadata.purpose, {
|
|
103275
|
+
...input,
|
|
103276
|
+
transcript
|
|
103277
|
+
});
|
|
103278
|
+
if (!progress.ok) {
|
|
103279
|
+
throw AssessmentRuntimeError.from(progress.failure);
|
|
103280
|
+
}
|
|
103281
|
+
return { context: context2, transcript, submissionId: progress.submissionId };
|
|
103282
|
+
}
|
|
103283
|
+
function prepareAssessmentItemTranscript(assessment, input) {
|
|
103284
|
+
validateAssessmentResponseUpdate(assessment, { [input.itemIdentifier]: input.responses });
|
|
103285
|
+
const appended = appendAssessmentAdministration(input.transcript, input);
|
|
103286
|
+
if (!appended.ok) {
|
|
103287
|
+
throw AssessmentRuntimeError.from(appended.failure);
|
|
103288
|
+
}
|
|
103289
|
+
return appended;
|
|
103290
|
+
}
|
|
103291
|
+
function prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal = false) {
|
|
103292
|
+
return prepareAssessmentTranscript({
|
|
103293
|
+
transcript,
|
|
103294
|
+
attemptId: context2.attemptId,
|
|
103295
|
+
contentRevision: context2.metadata.selectedTest.contentRevision,
|
|
103296
|
+
assessment,
|
|
103297
|
+
routed: context2.metadata.purpose === "diagnostic" && context2.metadata.diagnostic !== undefined,
|
|
103298
|
+
allowIncomplete: !terminal && assessmentFlowForPurpose(context2.metadata.purpose) === "attempt-submit"
|
|
103299
|
+
});
|
|
103300
|
+
}
|
|
103301
|
+
function requireTranscriptWriteAccess(attemptId, integrationStatus) {
|
|
103302
|
+
if (integrationStatus === "deactivated") {
|
|
103303
|
+
throw AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
103304
|
+
}
|
|
103305
|
+
}
|
|
103306
|
+
function requireTranscriptVersion(transcript, expectedResponseVersion) {
|
|
103307
|
+
if (expectedResponseVersion !== transcript.administrations.length) {
|
|
103308
|
+
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, transcript.administrations.length));
|
|
103309
|
+
}
|
|
103310
|
+
}
|
|
103311
|
+
function requireAssessmentTranscriptCheckpoint(attempt, integrationStatus, input) {
|
|
103312
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103313
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103314
|
+
if (!isAssessmentAttemptOpen({ ...attempt, inProgress: attempt.inProgress ?? "" })) {
|
|
103315
|
+
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(attempt.sourcedId));
|
|
103316
|
+
}
|
|
103317
|
+
}
|
|
103318
|
+
function assessmentTranscriptRequestHash(input) {
|
|
103319
|
+
return sha256Hex(canonicalJson2({
|
|
103320
|
+
transcript: input.transcript,
|
|
103321
|
+
...input.session ? { session: input.session } : {}
|
|
103322
|
+
}));
|
|
103323
|
+
}
|
|
103324
|
+
function resolveAssessmentTranscriptSubmission(attempt, metadata2, integrationStatus, input, requestHash) {
|
|
103325
|
+
const disposition = classifyAssessmentSubmission({
|
|
103326
|
+
inProgress: attempt.inProgress ?? "",
|
|
103327
|
+
scoreStatus: attempt.scoreStatus,
|
|
103328
|
+
submissionId: metadata2.submissionId
|
|
103329
|
+
}, input.submissionId);
|
|
103330
|
+
if (disposition === "replay" && metadata2.completion?.requestHash === requestHash) {
|
|
103331
|
+
return "replay";
|
|
103332
|
+
}
|
|
103333
|
+
if (disposition === "reject") {
|
|
103334
|
+
throw AssessmentRuntimeError.from(assessmentAlreadySubmitted(attempt.sourcedId));
|
|
103335
|
+
}
|
|
103336
|
+
if (disposition !== "submit") {
|
|
103337
|
+
throw AssessmentRuntimeError.from(assessmentFlowViolation("A different completion was already accepted for this attempt."));
|
|
103338
|
+
}
|
|
103339
|
+
requireTranscriptWriteAccess(attempt.sourcedId, integrationStatus);
|
|
103340
|
+
if (input.session && input.session.runId !== attempt.sourcedId) {
|
|
103341
|
+
throw new ValidationError("Assessment session runId must match the attemptId.");
|
|
103342
|
+
}
|
|
103343
|
+
requireTranscriptVersion(input.transcript, input.expectedResponseVersion);
|
|
103344
|
+
return "submit";
|
|
103345
|
+
}
|
|
103346
|
+
function buildGradedAssessmentTranscript(input) {
|
|
103347
|
+
const { context: context2, prepared, itemResults, assessment, submittedAt, manifest } = input;
|
|
103348
|
+
const completed = completeAssessmentTranscript({
|
|
103349
|
+
transcript: prepared.transcript,
|
|
103350
|
+
scores: itemResults,
|
|
103351
|
+
purpose: context2.metadata.purpose,
|
|
103352
|
+
itemCount: assessment.items.length,
|
|
103353
|
+
submittedAt,
|
|
103354
|
+
manifest,
|
|
103355
|
+
terminal: input.terminal
|
|
103356
|
+
});
|
|
103357
|
+
const common2 = {
|
|
103358
|
+
...context2.metadata,
|
|
103359
|
+
responses: prepared.responses,
|
|
103360
|
+
responseVersion: completed.itemSubmissions.length,
|
|
103361
|
+
itemSubmissions: completed.itemSubmissions,
|
|
103362
|
+
updatedAt: submittedAt,
|
|
103363
|
+
transcript: prepared.transcript
|
|
103364
|
+
};
|
|
103365
|
+
const diagnostic = context2.metadata.purpose === "diagnostic" ? context2.metadata.diagnostic : undefined;
|
|
103366
|
+
const metadata2 = diagnostic ? {
|
|
103367
|
+
...common2,
|
|
103368
|
+
purpose: "diagnostic",
|
|
103369
|
+
diagnostic: { ...diagnostic, ledger: completed.ledger, state: completed.state }
|
|
103370
|
+
} : common2;
|
|
103371
|
+
return {
|
|
103372
|
+
metadata: metadata2,
|
|
103373
|
+
transcript: prepared.transcript,
|
|
103374
|
+
assessment,
|
|
103375
|
+
itemResults,
|
|
103376
|
+
manifest,
|
|
103377
|
+
score: aggregateAssessmentScore(itemResults.map((item) => item.score.earned), itemResults.reduce((sum, item) => sum + item.score.possible, 0)),
|
|
103378
|
+
correctQuestions: countCorrectQuestions(itemResults.map((item) => item.isCorrect))
|
|
103379
|
+
};
|
|
103380
|
+
}
|
|
103381
|
+
function assessmentTranscriptRuntimeError(error88) {
|
|
103382
|
+
return error88 instanceof AssessmentTranscriptError ? AssessmentRuntimeError.from({
|
|
103383
|
+
code: error88.code,
|
|
103384
|
+
message: error88.message,
|
|
103385
|
+
details: { transcriptIndex: error88.index }
|
|
103386
|
+
}) : error88;
|
|
103387
|
+
}
|
|
103388
|
+
function buildAssessmentTranscriptFeedback(attemptId, graded, index2) {
|
|
103389
|
+
const { metadata: metadata2 } = graded;
|
|
103390
|
+
const submission = metadata2.itemSubmissions[index2];
|
|
103391
|
+
const common2 = {
|
|
103392
|
+
attemptId,
|
|
103393
|
+
responseVersion: metadata2.responseVersion,
|
|
103394
|
+
status: "in_progress",
|
|
103395
|
+
transcript: graded.transcript
|
|
103396
|
+
};
|
|
103397
|
+
return metadata2.purpose === "diagnostic" && metadata2.diagnostic ? {
|
|
103398
|
+
...common2,
|
|
103399
|
+
routing: projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, metadata2.diagnostic.state),
|
|
103400
|
+
submission: diagnosticReceipt(submission, metadata2.diagnostic.ledger[index2])
|
|
103401
|
+
} : {
|
|
103402
|
+
...common2,
|
|
103403
|
+
responses: metadata2.responses,
|
|
103404
|
+
itemSubmissions: metadata2.itemSubmissions,
|
|
103405
|
+
submission
|
|
103406
|
+
};
|
|
103407
|
+
}
|
|
103408
|
+
async function resumeAssessmentTranscript(input) {
|
|
103409
|
+
const retained = input.suppliedTranscript ?? input.checkpointTranscript;
|
|
103410
|
+
let transcript = retained ?? await input.recover();
|
|
103411
|
+
while (true) {
|
|
103412
|
+
try {
|
|
103413
|
+
return await input.grade(transcript);
|
|
103414
|
+
} catch (error88) {
|
|
103415
|
+
const index2 = error88 instanceof AssessmentRuntimeError && isRecord(error88.details) ? error88.details.transcriptIndex : undefined;
|
|
103416
|
+
if (retained !== undefined || typeof index2 !== "number" || index2 < 0 || index2 >= transcript.administrations.length) {
|
|
103417
|
+
throw error88;
|
|
103318
103418
|
}
|
|
103319
|
-
|
|
103320
|
-
|
|
103321
|
-
|
|
103419
|
+
transcript = {
|
|
103420
|
+
...transcript,
|
|
103421
|
+
administrations: transcript.administrations.slice(0, index2)
|
|
103422
|
+
};
|
|
103322
103423
|
}
|
|
103323
103424
|
}
|
|
103324
|
-
|
|
103325
|
-
|
|
103326
|
-
|
|
103327
|
-
|
|
103328
|
-
|
|
103329
|
-
|
|
103330
|
-
|
|
103331
|
-
|
|
103332
|
-
|
|
103333
|
-
|
|
103334
|
-
|
|
103335
|
-
|
|
103336
|
-
|
|
103337
|
-
|
|
103338
|
-
|
|
103339
|
-
|
|
103340
|
-
|
|
103341
|
-
|
|
103342
|
-
|
|
103343
|
-
|
|
103344
|
-
|
|
103345
|
-
|
|
103346
|
-
|
|
103347
|
-
|
|
103348
|
-
|
|
103349
|
-
|
|
103350
|
-
|
|
103351
|
-
|
|
103352
|
-
const
|
|
103353
|
-
|
|
103354
|
-
|
|
103355
|
-
|
|
103356
|
-
|
|
103357
|
-
|
|
103358
|
-
|
|
103359
|
-
|
|
103360
|
-
},
|
|
103361
|
-
isCorrect: assessmentItemCorrectness(interactionResults.map((result) => result.isCorrect)),
|
|
103362
|
-
grading: omittedResponseCount > 0 ? PLATFORM_QTI_ADAPTER_CHILD_GRADING : QTI_CHILD_GRADING
|
|
103363
|
-
});
|
|
103364
|
-
}
|
|
103365
|
-
if (item.requests.length === 0) {
|
|
103366
|
-
completeItem();
|
|
103367
|
-
return [];
|
|
103425
|
+
}
|
|
103426
|
+
function assessmentReviewTranscriptOutcomes(input) {
|
|
103427
|
+
const resultByItem = new Map(input.itemResults.map((item) => [item.itemIdentifier, item]));
|
|
103428
|
+
const administered = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
103429
|
+
return input.metadata.review.selections.filter((selection) => administered.has(selection.itemIdentifier)).map((selection) => {
|
|
103430
|
+
const result = resultByItem.get(selection.itemIdentifier);
|
|
103431
|
+
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
103432
|
+
if (!result || !submission) {
|
|
103433
|
+
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
103434
|
+
}
|
|
103435
|
+
return {
|
|
103436
|
+
itemIdentifier: selection.itemIdentifier,
|
|
103437
|
+
standard: selection.standard,
|
|
103438
|
+
answered: submission.answered,
|
|
103439
|
+
score: result.score,
|
|
103440
|
+
isCorrect: result.isCorrect
|
|
103441
|
+
};
|
|
103442
|
+
});
|
|
103443
|
+
}
|
|
103444
|
+
function assessmentReviewTranscriptExposures(input) {
|
|
103445
|
+
const exposures = [];
|
|
103446
|
+
const authoritative = new Set;
|
|
103447
|
+
for (const { result, metadata: metadata2 } of input.attempts) {
|
|
103448
|
+
const state = { ...result, inProgress: result.inProgress ?? "" };
|
|
103449
|
+
const settled = isAssessmentAttemptAwaitingAward(state) || isAssessmentAttemptCompleted(state);
|
|
103450
|
+
if (metadata2.purpose === "review" && (metadata2.transcript !== undefined || settled)) {
|
|
103451
|
+
authoritative.add(result.sourcedId);
|
|
103452
|
+
if (metadata2.review.bankRevision === input.bankRevision) {
|
|
103453
|
+
const accepted = settled ? metadata2.itemSubmissions.map((submission) => ({
|
|
103454
|
+
itemIdentifier: submission.itemIdentifier,
|
|
103455
|
+
deliveredAt: submission.submittedAt
|
|
103456
|
+
})) : metadata2.transcript.administrations.map((entry) => ({
|
|
103457
|
+
itemIdentifier: entry.itemIdentifier,
|
|
103458
|
+
deliveredAt: metadata2.updatedAt
|
|
103459
|
+
}));
|
|
103460
|
+
exposures.push(...accepted);
|
|
103368
103461
|
}
|
|
103369
|
-
|
|
103370
|
-
startedAt ??= Date.now();
|
|
103371
|
-
try {
|
|
103372
|
-
const response = responses[item.itemIdentifier]?.[request.identifier];
|
|
103373
|
-
if (response === undefined) {
|
|
103374
|
-
syntheticIncorrectInteractionCount += 1;
|
|
103375
|
-
interactionResults[index2] = { score: 0, isCorrect: false };
|
|
103376
|
-
} else {
|
|
103377
|
-
qtiRemoteInteractionCount += 1;
|
|
103378
|
-
interactionResults[index2] = await processResponse(item.itemIdentifier, request);
|
|
103379
|
-
}
|
|
103380
|
-
completedInteractions += 1;
|
|
103381
|
-
if (completedInteractions === item.requests.length && !failed) {
|
|
103382
|
-
completeItem();
|
|
103383
|
-
}
|
|
103384
|
-
} catch (error88) {
|
|
103385
|
-
if (!failed) {
|
|
103386
|
-
failed = true;
|
|
103387
|
-
fallbackEvent({
|
|
103388
|
-
"app.assessment.fallback_outcome": "failed",
|
|
103389
|
-
"exception.type": errorType(error88),
|
|
103390
|
-
"app.error.message": errorMessage(error88)
|
|
103391
|
-
});
|
|
103392
|
-
}
|
|
103393
|
-
throw error88;
|
|
103394
|
-
}
|
|
103395
|
-
});
|
|
103396
|
-
});
|
|
103397
|
-
await runWithConcurrency(fallbackTasks, SCORING_CONCURRENCY, (task) => task());
|
|
103462
|
+
}
|
|
103398
103463
|
}
|
|
103399
|
-
|
|
103400
|
-
const
|
|
103401
|
-
if (!
|
|
103402
|
-
|
|
103464
|
+
for (const command of input.children) {
|
|
103465
|
+
const { attempt, administration, context: context2 } = command.payload;
|
|
103466
|
+
if (!authoritative.has(attempt.attemptId) && context2.kind === "review" && context2.bankRevision === input.bankRevision) {
|
|
103467
|
+
exposures.push({
|
|
103468
|
+
itemIdentifier: administration.itemIdentifier,
|
|
103469
|
+
deliveredAt: command.recordedAt
|
|
103470
|
+
});
|
|
103403
103471
|
}
|
|
103404
|
-
|
|
103405
|
-
|
|
103472
|
+
}
|
|
103473
|
+
return exposures;
|
|
103406
103474
|
}
|
|
103407
|
-
var
|
|
103408
|
-
var init_timeback_assessment_scoring_util = __esm(() => {
|
|
103475
|
+
var init_timeback_assessment_transcript_util = __esm(() => {
|
|
103409
103476
|
init_assessment_runtime2();
|
|
103410
|
-
|
|
103477
|
+
init_errors2();
|
|
103411
103478
|
init_timeback_assessment_runtime_util();
|
|
103412
|
-
QTI_CHILD_GRADING = {
|
|
103413
|
-
source: "timeback-qti",
|
|
103414
|
-
graderVersion: "timeback-qti-process-response-v1"
|
|
103415
|
-
};
|
|
103416
|
-
PLATFORM_QTI_ADAPTER_CHILD_GRADING = {
|
|
103417
|
-
source: "platform-qti-adapter",
|
|
103418
|
-
graderVersion: "platform-qti-adapter-v1",
|
|
103419
|
-
qtiGraderVersion: "timeback-qti-process-response-v1"
|
|
103420
|
-
};
|
|
103421
|
-
PLATFORM_CHILD_GRADING = {
|
|
103422
|
-
source: "platform-artifact",
|
|
103423
|
-
artifactVersion: `assessment-artifact-v${ASSESSMENT_ARTIFACT_SCHEMA_VERSION}`,
|
|
103424
|
-
graderVersion: `platform-grader-v${ASSESSMENT_GRADER_PROTOCOL_VERSION}`
|
|
103425
|
-
};
|
|
103426
103479
|
});
|
|
103427
103480
|
|
|
103428
103481
|
// ../api-core/src/utils/timeback-qti-hydration.util.ts
|
|
@@ -103509,20 +103562,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103509
103562
|
init_assessment_artifact_runtime_util();
|
|
103510
103563
|
init_assessment_runtime_lock_util();
|
|
103511
103564
|
init_timeback_assessment_artifacts_util();
|
|
103565
|
+
init_timeback_assessment_child_result_util();
|
|
103512
103566
|
init_timeback_assessment_preparation_util();
|
|
103513
103567
|
init_timeback_assessment_publication_util();
|
|
103514
|
-
init_timeback_assessment_reconciliation_util();
|
|
103515
|
-
init_timeback_assessment_replay_util();
|
|
103516
103568
|
init_timeback_assessment_routing_util();
|
|
103517
103569
|
init_timeback_assessment_rules_util();
|
|
103518
103570
|
init_timeback_assessment_runtime_util();
|
|
103519
103571
|
init_timeback_assessment_scoring_util();
|
|
103572
|
+
init_timeback_assessment_transcript_util();
|
|
103520
103573
|
init_timeback_qti_hydration_util();
|
|
103521
103574
|
await __promiseAll([
|
|
103522
103575
|
init_dist5(),
|
|
103523
103576
|
init_errors8(),
|
|
103524
|
-
init_assessment_artifact_loader_util()
|
|
103525
|
-
init_timeback_assessment_child_result_util()
|
|
103577
|
+
init_assessment_artifact_loader_util()
|
|
103526
103578
|
]);
|
|
103527
103579
|
AssessmentPreparationPrerequisiteError = class AssessmentPreparationPrerequisiteError extends Error {
|
|
103528
103580
|
original;
|
|
@@ -103537,7 +103589,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103537
103589
|
static PREPARATION_IN_FLIGHT_LIMIT = 32;
|
|
103538
103590
|
static ASSESSMENT_CACHE_TTL_MS = 60000;
|
|
103539
103591
|
static EXPORT_CONCURRENCY = 4;
|
|
103540
|
-
static ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS = 3;
|
|
103541
103592
|
static ASSESSMENT_VERSION_CONFIRMATION_DELAY_MS = 25;
|
|
103542
103593
|
static SCORING_CONCURRENCY = 4;
|
|
103543
103594
|
static STALE_AWARD_AGE_MS = 15 * 60 * 1000;
|
|
@@ -103555,6 +103606,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103555
103606
|
maxSize: TimebackAssessmentRuntimeService.ASSESSMENT_CACHE_LIMIT
|
|
103556
103607
|
});
|
|
103557
103608
|
artifactLoader;
|
|
103609
|
+
itemScoreCache = new TimebackCache({
|
|
103610
|
+
defaultTTL: 5 * 60000,
|
|
103611
|
+
maxSize: 512
|
|
103612
|
+
});
|
|
103613
|
+
childLineItemCache = new TimebackCache({
|
|
103614
|
+
defaultTTL: 5 * 60000,
|
|
103615
|
+
maxSize: 512
|
|
103616
|
+
});
|
|
103558
103617
|
preparations = new AssessmentPreparationFlights(TimebackAssessmentRuntimeService.PREPARATION_IN_FLIGHT_LIMIT);
|
|
103559
103618
|
constructor(deps) {
|
|
103560
103619
|
this.deps = deps;
|
|
@@ -103681,6 +103740,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103681
103740
|
version: PLAYCADEMY_ASSESSMENT_RESULT_METADATA_VERSION,
|
|
103682
103741
|
activityId: input.activityId,
|
|
103683
103742
|
courseId: context2.integration.courseId,
|
|
103743
|
+
courseName: context2.enrollment.course.title,
|
|
103684
103744
|
integrationId: context2.integration.id,
|
|
103685
103745
|
enrollmentId: context2.enrollment.id,
|
|
103686
103746
|
selectedTest: {
|
|
@@ -103737,7 +103797,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103737
103797
|
enrollmentId: context2.enrollment.id
|
|
103738
103798
|
};
|
|
103739
103799
|
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);
|
|
103740
|
-
const listing = this.attemptListing(results, attemptScope);
|
|
103800
|
+
const listing = await this.attemptListing(results, attemptScope);
|
|
103741
103801
|
const decision = selectRuntimeAssessment(tests, [
|
|
103742
103802
|
...this.attemptsMatchingRequest(listing.attempts, this.startRequestFingerprint(params.input)).values()
|
|
103743
103803
|
].map((entry) => entry.selection));
|
|
@@ -103760,6 +103820,18 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103760
103820
|
let prepared;
|
|
103761
103821
|
try {
|
|
103762
103822
|
prepared = await this.buildPreparationPlan(params, requestHash, preparation);
|
|
103823
|
+
const assessment = await this.loadPreparedPlanAssessment(prepared.plan);
|
|
103824
|
+
if (!assessment) {
|
|
103825
|
+
throw new Error("Prepared assessment artifact is unavailable");
|
|
103826
|
+
}
|
|
103827
|
+
await this.provisionPreparationPrerequisites(() => this.ensureTranscriptLineItems({
|
|
103828
|
+
version: 1,
|
|
103829
|
+
gameId: prepared.plan.gameId,
|
|
103830
|
+
studentId: prepared.plan.studentId,
|
|
103831
|
+
attemptId: prepared.plan.attemptId,
|
|
103832
|
+
parentLineItemId: prepared.plan.parentLineItemId,
|
|
103833
|
+
metadata: buildPreparedAssessmentMetadata(prepared.plan, new Date().toISOString(), assessment.title)
|
|
103834
|
+
}, assessment));
|
|
103763
103835
|
} catch (error88) {
|
|
103764
103836
|
if (error88 instanceof ValidationError || error88 instanceof AssessmentRuntimeError) {
|
|
103765
103837
|
throw error88;
|
|
@@ -103845,6 +103917,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103845
103917
|
integrationId: context2.integration.id,
|
|
103846
103918
|
enrollmentId: context2.enrollment.id,
|
|
103847
103919
|
courseId: context2.integration.courseId,
|
|
103920
|
+
courseName: context2.enrollment.course.title,
|
|
103848
103921
|
...attempt,
|
|
103849
103922
|
parentLineItemId,
|
|
103850
103923
|
selectedTest: {
|
|
@@ -103885,7 +103958,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103885
103958
|
const selected = selectReviewItems({
|
|
103886
103959
|
request: params.input,
|
|
103887
103960
|
bank: catalog.bank,
|
|
103888
|
-
exposures:
|
|
103961
|
+
exposures: assessmentReviewTranscriptExposures({
|
|
103962
|
+
attempts: listing.attempts.values(),
|
|
103963
|
+
children: listing.reviewChildren.values(),
|
|
103889
103964
|
bankRevision: catalog.bank.bankRevision
|
|
103890
103965
|
})
|
|
103891
103966
|
});
|
|
@@ -103934,6 +104009,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103934
104009
|
integrationId: context2.integration.id,
|
|
103935
104010
|
enrollmentId: context2.enrollment.id,
|
|
103936
104011
|
courseId: context2.integration.courseId,
|
|
104012
|
+
courseName: context2.enrollment.course.title,
|
|
103937
104013
|
...attempt,
|
|
103938
104014
|
parentLineItemId,
|
|
103939
104015
|
selectedTest: {
|
|
@@ -104085,6 +104161,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104085
104161
|
activityId: input.activityId,
|
|
104086
104162
|
purpose: "diagnostic",
|
|
104087
104163
|
courseId: context2.integration.courseId,
|
|
104164
|
+
courseName: context2.enrollment.course.title,
|
|
104088
104165
|
integrationId: context2.integration.id,
|
|
104089
104166
|
enrollmentId: context2.enrollment.id,
|
|
104090
104167
|
selectedTest: {
|
|
@@ -104104,7 +104181,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104104
104181
|
testName: assessment.title,
|
|
104105
104182
|
routingRevision: routing.routingRevision,
|
|
104106
104183
|
ledger: [],
|
|
104107
|
-
routingSnapshots: [],
|
|
104108
104184
|
state: routing.state
|
|
104109
104185
|
}
|
|
104110
104186
|
};
|
|
@@ -104123,7 +104199,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104123
104199
|
})
|
|
104124
104200
|
});
|
|
104125
104201
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104126
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration,
|
|
104202
|
+
return this.diagnosticSnapshot(result, metadata2, assessment, context2.integration, {
|
|
104203
|
+
state: routing.state
|
|
104204
|
+
});
|
|
104127
104205
|
});
|
|
104128
104206
|
}
|
|
104129
104207
|
async startReview({
|
|
@@ -104157,7 +104235,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104157
104235
|
this.listAttempts(attemptScope)
|
|
104158
104236
|
]);
|
|
104159
104237
|
const attempts = listing.attempts;
|
|
104160
|
-
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts,
|
|
104238
|
+
const initialResolution = await this.resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, attempts, context2));
|
|
104161
104239
|
if (initialResolution.kind === "resumed") {
|
|
104162
104240
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104163
104241
|
return initialResolution.snapshot;
|
|
@@ -104176,7 +104254,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104176
104254
|
});
|
|
104177
104255
|
const refreshedListing = await this.listAttempts(attemptScope);
|
|
104178
104256
|
const refreshed = refreshedListing.attempts;
|
|
104179
|
-
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed,
|
|
104257
|
+
const refreshedResolution = await this.resumeAvailableAttempt(tests, refreshed, requestFingerprint, pendingSupersessions, (resumeDecision) => this.resumeReviewSnapshot(resumeDecision, refreshed, context2));
|
|
104180
104258
|
if (refreshedResolution.kind === "resumed") {
|
|
104181
104259
|
await this.persistPendingSupersessionsBestEffort(db2, pendingSupersessions);
|
|
104182
104260
|
return refreshedResolution.snapshot;
|
|
@@ -104189,7 +104267,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104189
104267
|
if (catalog.test.identifier !== decision.test.qtiTestIdentifier) {
|
|
104190
104268
|
catalog = await this.loadReviewBankCatalog(decision.test.qtiTestIdentifier);
|
|
104191
104269
|
}
|
|
104192
|
-
const exposures =
|
|
104270
|
+
const exposures = assessmentReviewTranscriptExposures({
|
|
104271
|
+
attempts: refreshedListing.attempts.values(),
|
|
104272
|
+
children: refreshedListing.reviewChildren.values(),
|
|
104193
104273
|
bankRevision: catalog.bank.bankRevision
|
|
104194
104274
|
});
|
|
104195
104275
|
const selected = selectReviewItems({
|
|
@@ -104228,6 +104308,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104228
104308
|
activityId: input.activityId,
|
|
104229
104309
|
purpose: input.purpose,
|
|
104230
104310
|
courseId: context2.integration.courseId,
|
|
104311
|
+
courseName: context2.enrollment.course.title,
|
|
104231
104312
|
integrationId: context2.integration.id,
|
|
104232
104313
|
enrollmentId: context2.enrollment.id,
|
|
104233
104314
|
selectedTest: {
|
|
@@ -104274,8 +104355,14 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104274
104355
|
});
|
|
104275
104356
|
}
|
|
104276
104357
|
async get(params) {
|
|
104358
|
+
return this.resume({ ...params, input: {} });
|
|
104359
|
+
}
|
|
104360
|
+
async resume({
|
|
104361
|
+
input,
|
|
104362
|
+
...params
|
|
104363
|
+
}) {
|
|
104277
104364
|
const attempt = await this.authorizeAttemptRead(params);
|
|
104278
|
-
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration);
|
|
104365
|
+
return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration, input.transcript);
|
|
104279
104366
|
}
|
|
104280
104367
|
async latest({
|
|
104281
104368
|
gameId,
|
|
@@ -104337,496 +104424,104 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104337
104424
|
}
|
|
104338
104425
|
async save({ input, ...params }) {
|
|
104339
104426
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104340
|
-
try {
|
|
104341
|
-
const warmup = await this.peekAttempt(params);
|
|
104342
|
-
if (this.isOpen(warmup.result) && !this.isPlatformRoutedDiagnosticMetadata(warmup.metadata)) {
|
|
104343
|
-
await this.loadAttemptAssessment(warmup.metadata);
|
|
104344
|
-
}
|
|
104345
|
-
} catch (error88) {
|
|
104346
|
-
addEvent("assessment.save_warmup_failed", {
|
|
104347
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104348
|
-
"exception.type": errorType(error88),
|
|
104349
|
-
"app.error.message": errorMessage(error88)
|
|
104350
|
-
});
|
|
104351
|
-
}
|
|
104352
104427
|
return this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104353
|
-
const attempt = await this.
|
|
104428
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104354
104429
|
db: db2,
|
|
104355
|
-
developerAccessValidated: true
|
|
104430
|
+
developerAccessValidated: true,
|
|
104431
|
+
includeEnrollment: false
|
|
104356
104432
|
});
|
|
104357
|
-
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
|
|
104362
|
-
|
|
104363
|
-
|
|
104364
|
-
|
|
104365
|
-
|
|
104366
|
-
|
|
104367
|
-
|
|
104368
|
-
|
|
104369
|
-
validateAssessmentDraftResponses(assessment, responses);
|
|
104370
|
-
const metadata2 = {
|
|
104371
|
-
...attempt.metadata,
|
|
104372
|
-
responses,
|
|
104373
|
-
responseVersion: attempt.metadata.responseVersion + 1,
|
|
104374
|
-
updatedAt: new Date().toISOString()
|
|
104375
|
-
};
|
|
104376
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
104433
|
+
requireAssessmentTranscriptCheckpoint(attempt.result, attempt.integration.status, input);
|
|
104434
|
+
const graded = await this.gradeTranscript({
|
|
104435
|
+
attemptId: params.attemptId,
|
|
104436
|
+
metadata: attempt.metadata
|
|
104437
|
+
}, input.transcript, false, db2);
|
|
104438
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, buildAssessmentResultCheckpoint({
|
|
104439
|
+
result: attempt.result,
|
|
104440
|
+
metadata: attempt.metadata,
|
|
104441
|
+
transcript: graded.transcript,
|
|
104442
|
+
timestamp: graded.metadata.updatedAt
|
|
104443
|
+
}));
|
|
104444
|
+
this.reconcileTranscriptChildren(this.tokenContext(attempt), graded).catch((error88) => this.recordTranscriptFailure("checkpoint_projection", params.attemptId, error88));
|
|
104377
104445
|
return {
|
|
104378
|
-
attemptId:
|
|
104379
|
-
responseVersion:
|
|
104446
|
+
attemptId: params.attemptId,
|
|
104447
|
+
responseVersion: graded.metadata.responseVersion,
|
|
104380
104448
|
status: "in_progress",
|
|
104381
|
-
responses
|
|
104449
|
+
responses: graded.metadata.responses,
|
|
104450
|
+
transcript: graded.transcript
|
|
104382
104451
|
};
|
|
104383
104452
|
});
|
|
104384
104453
|
}
|
|
104385
|
-
async
|
|
104454
|
+
async recordProgress({
|
|
104386
104455
|
input,
|
|
104387
104456
|
...params
|
|
104388
104457
|
}) {
|
|
104389
104458
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104390
|
-
|
|
104391
|
-
|
|
104392
|
-
|
|
104393
|
-
|
|
104394
|
-
}
|
|
104395
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is not accepted by the hosted conventional item-submit flow.", { routingNodeKey: input.routingNodeKey }));
|
|
104396
|
-
}
|
|
104397
|
-
let preview = await this.prepareItemSubmission(params, input);
|
|
104398
|
-
let preparationAttempts = 1;
|
|
104399
|
-
while (true) {
|
|
104400
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104401
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, {
|
|
104402
|
-
db: db2,
|
|
104403
|
-
developerAccessValidated: true
|
|
104404
|
-
});
|
|
104405
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104406
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104407
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("Platform-routed diagnostic submissions require routingNodeKey.", { flow: "platform-routed-item-submit" }));
|
|
104408
|
-
}
|
|
104409
|
-
const replay = resolveAssessmentItemReplay(attempt.metadata, input);
|
|
104410
|
-
if (replay.action === "conflict") {
|
|
104411
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104412
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
104413
|
-
}
|
|
104414
|
-
if (replay.action === "replay") {
|
|
104415
|
-
const prior = replay.submission;
|
|
104416
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104417
|
-
attempt,
|
|
104418
|
-
metadata: attempt.metadata,
|
|
104419
|
-
submission: prior
|
|
104420
|
-
}) : null;
|
|
104421
|
-
return {
|
|
104422
|
-
action: "complete",
|
|
104423
|
-
response: buildAssessmentItemReplayResult(attempt.result.sourcedId, attempt.metadata, prior),
|
|
104424
|
-
checkpoint: child2,
|
|
104425
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
104426
|
-
result: attempt.result,
|
|
104427
|
-
metadata: attempt.metadata,
|
|
104428
|
-
itemIdentifier: input.itemIdentifier,
|
|
104429
|
-
integration: attempt.integration
|
|
104430
|
-
} : null
|
|
104431
|
-
};
|
|
104432
|
-
}
|
|
104433
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104434
|
-
this.assertInProgress(attempt.result);
|
|
104435
|
-
if (!preview || preview.responseVersion !== attempt.metadata.responseVersion || preview.submissionId !== input.submissionId || preview.itemIdentifier !== input.itemIdentifier) {
|
|
104436
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
104437
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, attempt.metadata.responseVersion, "assessment");
|
|
104438
|
-
}
|
|
104439
|
-
return { action: "prepare" };
|
|
104440
|
-
}
|
|
104441
|
-
const transition = prepareAssessmentItemSubmission({
|
|
104442
|
-
purpose: attempt.metadata.purpose,
|
|
104443
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104444
|
-
responses: attempt.metadata.responses,
|
|
104445
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104446
|
-
assessment: preview.assessment
|
|
104447
|
-
}, input);
|
|
104448
|
-
if (transition.action === "reject") {
|
|
104449
|
-
throw AssessmentRuntimeError.from(transition.failure);
|
|
104450
|
-
}
|
|
104451
|
-
if (transition.action === "replay") {
|
|
104452
|
-
const child2 = attempt.metadata.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104453
|
-
attempt,
|
|
104454
|
-
metadata: attempt.metadata,
|
|
104455
|
-
submission: transition.submission
|
|
104456
|
-
}) : null;
|
|
104457
|
-
return {
|
|
104458
|
-
action: "complete",
|
|
104459
|
-
response: {
|
|
104460
|
-
attemptId: attempt.result.sourcedId,
|
|
104461
|
-
responseVersion: transition.responseVersion,
|
|
104462
|
-
status: "in_progress",
|
|
104463
|
-
responses: transition.responses,
|
|
104464
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104465
|
-
submission: transition.submission
|
|
104466
|
-
},
|
|
104467
|
-
checkpoint: child2,
|
|
104468
|
-
projection: attempt.metadata.purpose === "review" ? {
|
|
104469
|
-
result: attempt.result,
|
|
104470
|
-
metadata: attempt.metadata,
|
|
104471
|
-
itemIdentifier: input.itemIdentifier,
|
|
104472
|
-
integration: attempt.integration
|
|
104473
|
-
} : null
|
|
104474
|
-
};
|
|
104475
|
-
}
|
|
104476
|
-
if (!preview.scoring) {
|
|
104477
|
-
throw new Error("Prepared item submission is missing its score");
|
|
104478
|
-
}
|
|
104479
|
-
const submittedAt = new Date().toISOString();
|
|
104480
|
-
const completed = completeAssessmentItemSubmission(attempt.metadata, input, transition, preview.scoring, submittedAt);
|
|
104481
|
-
const metadata2 = {
|
|
104482
|
-
...attempt.metadata,
|
|
104483
|
-
responses: completed.responses,
|
|
104484
|
-
responseVersion: completed.responseVersion,
|
|
104485
|
-
itemSubmissions: completed.itemSubmissions,
|
|
104486
|
-
updatedAt: submittedAt
|
|
104487
|
-
};
|
|
104488
|
-
const child = metadata2.purpose === "review" ? await this.prepareReviewAssessmentChildResult({
|
|
104489
|
-
attempt,
|
|
104490
|
-
metadata: metadata2,
|
|
104491
|
-
submission: completed.submission
|
|
104492
|
-
}) : null;
|
|
104493
|
-
await this.putResultMetadata(attempt.result, metadata2);
|
|
104494
|
-
return {
|
|
104495
|
-
action: "complete",
|
|
104496
|
-
response: {
|
|
104497
|
-
attemptId: attempt.result.sourcedId,
|
|
104498
|
-
responseVersion: completed.responseVersion,
|
|
104499
|
-
status: "in_progress",
|
|
104500
|
-
responses: completed.responses,
|
|
104501
|
-
itemSubmissions: completed.itemSubmissions,
|
|
104502
|
-
submission: completed.submission
|
|
104503
|
-
},
|
|
104504
|
-
checkpoint: child,
|
|
104505
|
-
projection: metadata2.purpose === "review" ? {
|
|
104506
|
-
result: attempt.result,
|
|
104507
|
-
metadata: metadata2,
|
|
104508
|
-
itemIdentifier: input.itemIdentifier,
|
|
104509
|
-
integration: attempt.integration
|
|
104510
|
-
} : null
|
|
104511
|
-
};
|
|
104512
|
-
});
|
|
104513
|
-
if (committed.action === "prepare") {
|
|
104514
|
-
preparationAttempts += 1;
|
|
104515
|
-
preview = await this.scoreItemSubmission(params, input);
|
|
104516
|
-
} else {
|
|
104517
|
-
if (committed.projection) {
|
|
104518
|
-
this.projectReviewItemResponse(params, committed.projection, committed.checkpoint);
|
|
104519
|
-
}
|
|
104520
|
-
return committed.response;
|
|
104521
|
-
}
|
|
104522
|
-
}
|
|
104523
|
-
}
|
|
104524
|
-
async submitDiagnosticItem(params, input) {
|
|
104525
|
-
let preview = await this.prepareDiagnosticItemSubmission(params, input);
|
|
104526
|
-
let preparationAttempts = 1;
|
|
104527
|
-
while (true) {
|
|
104528
|
-
const committed = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104529
|
-
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params, { db: db2, developerAccessValidated: true });
|
|
104530
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104531
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104532
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("routingNodeKey is accepted only for a platform-routed diagnostic.", { routingNodeKey: input.routingNodeKey }));
|
|
104533
|
-
}
|
|
104534
|
-
const metadata2 = attempt.metadata;
|
|
104535
|
-
const replay = resolveDiagnosticItemReplay(metadata2, input);
|
|
104536
|
-
if (replay.action === "conflict") {
|
|
104537
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104538
|
-
throw AssessmentRuntimeError.from(replay.failure);
|
|
104539
|
-
}
|
|
104540
|
-
if (replay.action === "replay") {
|
|
104541
|
-
const { submission: prior, ledgerEntry, priorIndex } = replay;
|
|
104542
|
-
let routing = replay.routing;
|
|
104543
|
-
if (!routing) {
|
|
104544
|
-
const assessment = preview?.assessment ?? await this.loadAttemptAssessment(metadata2);
|
|
104545
|
-
const loaded2 = await this.loadAttemptDiagnosticRouting(metadata2, assessment, db2);
|
|
104546
|
-
const replayed = replayDiagnosticRouting(loaded2.manifest, metadata2.diagnostic.ledger.slice(0, priorIndex + 1));
|
|
104547
|
-
if (!replayed.ok) {
|
|
104548
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The committed diagnostic routing state cannot be replayed.", { failure: replayed.failure }));
|
|
104549
|
-
}
|
|
104550
|
-
routing = this.diagnosticRoutingSnapshot(metadata2, replayed.state);
|
|
104551
|
-
}
|
|
104552
|
-
const child2 = await this.prepareDiagnosticAssessmentChildResult({
|
|
104553
|
-
attempt,
|
|
104554
|
-
metadata: metadata2,
|
|
104555
|
-
submission: prior,
|
|
104556
|
-
transition: ledgerEntry
|
|
104557
|
-
});
|
|
104558
|
-
return {
|
|
104559
|
-
action: "complete",
|
|
104560
|
-
response: buildDiagnosticItemReplayResult(attempt.result.sourcedId, prior, ledgerEntry, routing),
|
|
104561
|
-
checkpoint: child2,
|
|
104562
|
-
projection: {
|
|
104563
|
-
result: attempt.result,
|
|
104564
|
-
metadata: metadata2,
|
|
104565
|
-
ledgerEntry,
|
|
104566
|
-
integration: attempt.integration
|
|
104567
|
-
}
|
|
104568
|
-
};
|
|
104569
|
-
}
|
|
104570
|
-
this.requireActiveAttempt({ attempt, enrollment }, params.attemptId);
|
|
104571
|
-
this.assertInProgress(attempt.result);
|
|
104572
|
-
if (!preview || preview.responseVersion !== metadata2.responseVersion || preview.submissionId !== input.submissionId || preview.routingNodeKey !== input.routingNodeKey || preview.itemIdentifier !== input.itemIdentifier) {
|
|
104573
|
-
if (preparationAttempts >= TimebackAssessmentRuntimeService.ITEM_SUBMISSION_MAX_PREPARATION_ATTEMPTS) {
|
|
104574
|
-
throw assessmentPreparationContentionError(input.expectedResponseVersion, metadata2.responseVersion, "diagnostic");
|
|
104575
|
-
}
|
|
104576
|
-
return { action: "prepare" };
|
|
104577
|
-
}
|
|
104578
|
-
this.assertResponseVersion(metadata2, input.expectedResponseVersion);
|
|
104579
|
-
const loaded = await this.loadAttemptDiagnosticRouting(metadata2, preview.assessment, db2);
|
|
104580
|
-
const expected = loaded.state.next;
|
|
104581
|
-
if (loaded.state.status !== "in-progress" || !expected || expected.nodeKey !== input.routingNodeKey || expected.itemIdentifier !== input.itemIdentifier) {
|
|
104582
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(expected ? "Submitted diagnostic item is not the canonical next item." : "Diagnostic routing is already ready to complete.", {
|
|
104583
|
-
expectedRoutingNodeKey: expected?.nodeKey ?? null,
|
|
104584
|
-
routingNodeKey: input.routingNodeKey,
|
|
104585
|
-
expectedItemIdentifier: expected?.itemIdentifier ?? null,
|
|
104586
|
-
itemIdentifier: input.itemIdentifier
|
|
104587
|
-
}));
|
|
104588
|
-
}
|
|
104589
|
-
const responses = prepareDiagnosticAssessmentResponses(preview.assessment, metadata2.responses, input);
|
|
104590
|
-
const scoring = preview.scoring;
|
|
104591
|
-
if (!scoring || typeof scoring.isCorrect !== "boolean") {
|
|
104592
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation(`Diagnostic item ${input.itemIdentifier} did not produce determinate binary grading.`, { itemIdentifier: input.itemIdentifier }));
|
|
104593
|
-
}
|
|
104594
|
-
const advanced = advanceDiagnosticRouting(loaded.manifest, metadata2.diagnostic.ledger, {
|
|
104595
|
-
routingNodeKey: input.routingNodeKey,
|
|
104596
|
-
itemIdentifier: input.itemIdentifier,
|
|
104597
|
-
isCorrect: scoring.isCorrect
|
|
104598
|
-
});
|
|
104599
|
-
if (!advanced.ok) {
|
|
104600
|
-
throw AssessmentRuntimeError.from(assessmentFlowViolation("The diagnostic routing transition was rejected.", { failure: advanced.failure }));
|
|
104601
|
-
}
|
|
104602
|
-
const submittedAt = new Date().toISOString();
|
|
104603
|
-
const responseVersion = metadata2.responseVersion + 1;
|
|
104604
|
-
const routingSnapshot = this.diagnosticRoutingSnapshot(metadata2, advanced.state);
|
|
104605
|
-
const routingSnapshots = metadata2.diagnostic.routingSnapshots ? [...metadata2.diagnostic.routingSnapshots, routingSnapshot] : undefined;
|
|
104606
|
-
const submission = {
|
|
104607
|
-
submissionId: input.submissionId,
|
|
104608
|
-
itemIdentifier: input.itemIdentifier,
|
|
104609
|
-
submittedAt,
|
|
104610
|
-
responseVersion,
|
|
104611
|
-
answered: Object.keys(responses[input.itemIdentifier] ?? {}).length > 0,
|
|
104612
|
-
score: scoring.score,
|
|
104613
|
-
isCorrect: scoring.isCorrect,
|
|
104614
|
-
grading: scoring.grading
|
|
104615
|
-
};
|
|
104616
|
-
const nextMetadata = {
|
|
104617
|
-
...metadata2,
|
|
104618
|
-
responses,
|
|
104619
|
-
responseVersion,
|
|
104620
|
-
itemSubmissions: [...metadata2.itemSubmissions, submission],
|
|
104621
|
-
updatedAt: submittedAt,
|
|
104622
|
-
diagnostic: {
|
|
104623
|
-
...metadata2.diagnostic,
|
|
104624
|
-
ledger: [...metadata2.diagnostic.ledger, advanced.ledgerEntry],
|
|
104625
|
-
...routingSnapshots ? { routingSnapshots } : {},
|
|
104626
|
-
state: advanced.state
|
|
104627
|
-
}
|
|
104628
|
-
};
|
|
104629
|
-
const child = await this.prepareDiagnosticAssessmentChildResult({
|
|
104630
|
-
attempt,
|
|
104631
|
-
metadata: nextMetadata,
|
|
104632
|
-
submission,
|
|
104633
|
-
transition: advanced.ledgerEntry
|
|
104634
|
-
});
|
|
104635
|
-
await this.putResultMetadata(attempt.result, nextMetadata);
|
|
104636
|
-
return {
|
|
104637
|
-
action: "complete",
|
|
104638
|
-
response: {
|
|
104639
|
-
attemptId: attempt.result.sourcedId,
|
|
104640
|
-
responseVersion,
|
|
104641
|
-
status: "in_progress",
|
|
104642
|
-
routing: routingSnapshot,
|
|
104643
|
-
submission: diagnosticReceipt(submission, advanced.ledgerEntry)
|
|
104644
|
-
},
|
|
104645
|
-
checkpoint: child,
|
|
104646
|
-
projection: {
|
|
104647
|
-
result: attempt.result,
|
|
104648
|
-
metadata: nextMetadata,
|
|
104649
|
-
ledgerEntry: advanced.ledgerEntry,
|
|
104650
|
-
integration: attempt.integration
|
|
104651
|
-
}
|
|
104652
|
-
};
|
|
104653
|
-
});
|
|
104654
|
-
if (committed.action === "prepare") {
|
|
104655
|
-
preparationAttempts += 1;
|
|
104656
|
-
preview = await this.scoreDiagnosticItemSubmission(params, input);
|
|
104657
|
-
} else {
|
|
104658
|
-
this.projectDiagnosticItemResponse(committed.projection, preview?.assessment, committed.checkpoint);
|
|
104659
|
-
return committed.response;
|
|
104660
|
-
}
|
|
104661
|
-
}
|
|
104459
|
+
const { context: context2, transcript, submissionId } = prepareAssessmentProgress(await this.deps.verifyAssessmentToken(input.assessmentToken), params, input);
|
|
104460
|
+
const graded = await this.gradeTranscript(context2, transcript);
|
|
104461
|
+
await this.reconcileTranscriptChildren(context2, graded, new Set([submissionId]));
|
|
104462
|
+
return { attemptId: params.attemptId };
|
|
104662
104463
|
}
|
|
104663
|
-
async
|
|
104464
|
+
async submitItem({
|
|
104465
|
+
input,
|
|
104466
|
+
...params
|
|
104467
|
+
}) {
|
|
104468
|
+
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104469
|
+
const context2 = requireAssessmentFeedbackContext(await this.deps.verifyAssessmentToken(input.assessmentToken), params);
|
|
104470
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104471
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104472
|
+
const appended = prepareAssessmentItemTranscript(assessment, { ...input, transcript });
|
|
104473
|
+
const graded = await this.gradeTranscript(context2, appended.transcript);
|
|
104474
|
+
const submission = graded.metadata.itemSubmissions[appended.index];
|
|
104475
|
+
this.reconcileTranscriptChildren(context2, graded, new Set([submission.submissionId])).catch((error88) => this.recordTranscriptFailure("child_projection", params.attemptId, error88));
|
|
104476
|
+
return buildAssessmentTranscriptFeedback(params.attemptId, graded, appended.index);
|
|
104477
|
+
}
|
|
104478
|
+
async gradeTranscript(context2, transcript, terminal = false, db2 = this.deps.db) {
|
|
104479
|
+
const startedAt = Date.now();
|
|
104664
104480
|
try {
|
|
104665
|
-
|
|
104666
|
-
|
|
104667
|
-
|
|
104668
|
-
|
|
104669
|
-
|
|
104670
|
-
|
|
104671
|
-
|
|
104672
|
-
|
|
104673
|
-
|
|
104674
|
-
|
|
104675
|
-
|
|
104676
|
-
|
|
104677
|
-
|
|
104678
|
-
|
|
104679
|
-
|
|
104680
|
-
|
|
104681
|
-
|
|
104682
|
-
|
|
104683
|
-
|
|
104684
|
-
|
|
104685
|
-
const artifactIdentity = assessmentArtifactIdentityFromAttempt(attempt.metadata);
|
|
104686
|
-
const scoringArtifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
104687
|
-
const loaded = await this.loadAttemptDiagnosticRouting(attempt.metadata, assessment, this.deps.db, scoringArtifact);
|
|
104688
|
-
const expected = loaded.state.next;
|
|
104689
|
-
let scoring = null;
|
|
104690
|
-
if (loaded.state.status === "in-progress" && expected?.nodeKey === input.routingNodeKey && expected.itemIdentifier === input.itemIdentifier && attempt.metadata.responseVersion === input.expectedResponseVersion) {
|
|
104691
|
-
const responses = prepareDiagnosticAssessmentResponses(assessment, attempt.metadata.responses, input);
|
|
104692
|
-
scoring = await this.scoreItem({
|
|
104481
|
+
const assessment = await this.loadAttemptAssessment(context2.metadata);
|
|
104482
|
+
const prepared = prepareAttemptAssessmentTranscript(context2, assessment, transcript, terminal);
|
|
104483
|
+
const artifactIdentity = assessmentArtifactIdentityFromAttempt(context2.metadata);
|
|
104484
|
+
const artifact = await this.artifactLoader.loadMatchedScoring(assessment, artifactIdentity);
|
|
104485
|
+
const [itemResults, routing] = await Promise.all([
|
|
104486
|
+
scoreCachedAssessmentItems({
|
|
104487
|
+
assessment,
|
|
104488
|
+
responses: prepared.responses,
|
|
104489
|
+
artifactIdentity,
|
|
104490
|
+
purpose: context2.metadata.purpose,
|
|
104491
|
+
itemIdentifiers: new Set(prepared.transcript.administrations.map((entry) => entry.itemIdentifier)),
|
|
104492
|
+
artifact,
|
|
104493
|
+
cache: this.itemScoreCache,
|
|
104494
|
+
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
104495
|
+
onEvent: addEvent
|
|
104496
|
+
}),
|
|
104497
|
+
this.isPlatformRoutedDiagnosticMetadata(context2.metadata) ? this.loadAttemptDiagnosticRouting(context2.metadata, assessment, db2, artifact) : undefined
|
|
104498
|
+
]);
|
|
104499
|
+
const graded = buildGradedAssessmentTranscript({
|
|
104500
|
+
context: context2,
|
|
104693
104501
|
assessment,
|
|
104694
|
-
|
|
104695
|
-
|
|
104696
|
-
|
|
104697
|
-
|
|
104698
|
-
|
|
104502
|
+
prepared,
|
|
104503
|
+
itemResults,
|
|
104504
|
+
submittedAt: new Date().toISOString(),
|
|
104505
|
+
manifest: routing?.manifest,
|
|
104506
|
+
terminal
|
|
104507
|
+
});
|
|
104508
|
+
addEvent("assessment.transcript_validated", {
|
|
104509
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
104510
|
+
"app.assessment.administration_count": graded.metadata.responseVersion,
|
|
104511
|
+
"app.assessment.duration_ms": Date.now() - startedAt
|
|
104699
104512
|
});
|
|
104700
|
-
|
|
104701
|
-
return {
|
|
104702
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104703
|
-
submissionId: input.submissionId,
|
|
104704
|
-
routingNodeKey: input.routingNodeKey,
|
|
104705
|
-
itemIdentifier: input.itemIdentifier,
|
|
104706
|
-
assessment,
|
|
104707
|
-
scoring
|
|
104708
|
-
};
|
|
104709
|
-
}
|
|
104710
|
-
async scoreItems({
|
|
104711
|
-
preparedArtifact,
|
|
104712
|
-
...input
|
|
104713
|
-
}) {
|
|
104714
|
-
const artifact = preparedArtifact === undefined ? await this.artifactLoader.loadMatchedScoring(input.assessment, input.artifactIdentity) : preparedArtifact;
|
|
104715
|
-
return scoreAssessmentItems({
|
|
104716
|
-
...input,
|
|
104717
|
-
artifact,
|
|
104718
|
-
processResponse: (identifier, request) => this.requireClient().qtiApi.assessmentItems.processResponse(identifier, request),
|
|
104719
|
-
onEvent: addEvent
|
|
104720
|
-
});
|
|
104721
|
-
}
|
|
104722
|
-
async scoreItem(input) {
|
|
104723
|
-
const [scoring] = await this.scoreItems({
|
|
104724
|
-
assessment: input.assessment,
|
|
104725
|
-
responses: input.responses,
|
|
104726
|
-
artifactIdentity: input.artifactIdentity,
|
|
104727
|
-
purpose: input.purpose,
|
|
104728
|
-
itemIdentifiers: new Set([input.itemIdentifier]),
|
|
104729
|
-
preparedArtifact: input.preparedArtifact
|
|
104730
|
-
});
|
|
104731
|
-
return scoring;
|
|
104732
|
-
}
|
|
104733
|
-
async prepareItemSubmission(params, input) {
|
|
104734
|
-
try {
|
|
104735
|
-
return await this.scoreItemSubmission(params, input);
|
|
104513
|
+
return graded;
|
|
104736
104514
|
} catch (error88) {
|
|
104737
|
-
|
|
104738
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104739
|
-
"exception.type": errorType(error88),
|
|
104740
|
-
"app.error.message": errorMessage(error88)
|
|
104741
|
-
});
|
|
104742
|
-
return null;
|
|
104515
|
+
throw assessmentTranscriptRuntimeError(error88);
|
|
104743
104516
|
}
|
|
104744
104517
|
}
|
|
104745
|
-
|
|
104746
|
-
|
|
104747
|
-
|
|
104748
|
-
|
|
104749
|
-
|
|
104750
|
-
|
|
104751
|
-
const assessment = await this.loadAttemptAssessment(attempt.metadata);
|
|
104752
|
-
const transition = prepareAssessmentItemSubmission({
|
|
104753
|
-
purpose: attempt.metadata.purpose,
|
|
104754
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104755
|
-
responses: attempt.metadata.responses,
|
|
104756
|
-
itemSubmissions: attempt.metadata.itemSubmissions,
|
|
104757
|
-
assessment
|
|
104758
|
-
}, input);
|
|
104759
|
-
const scoring = transition.action === "commit" ? await this.scoreItem({
|
|
104760
|
-
assessment,
|
|
104761
|
-
responses: transition.responses,
|
|
104762
|
-
itemIdentifier: input.itemIdentifier,
|
|
104763
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
104764
|
-
purpose: attempt.metadata.purpose
|
|
104765
|
-
}) : null;
|
|
104766
|
-
return {
|
|
104767
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104768
|
-
submissionId: input.submissionId,
|
|
104769
|
-
itemIdentifier: input.itemIdentifier,
|
|
104770
|
-
assessment,
|
|
104771
|
-
scoring
|
|
104772
|
-
};
|
|
104773
|
-
}
|
|
104774
|
-
async scoreSubmission(attempt) {
|
|
104775
|
-
const assessment = await this.loadAttemptAssessment(attempt.metadata);
|
|
104776
|
-
validateAssessmentResponses(assessment, attempt.metadata.responses);
|
|
104777
|
-
const itemResults = attempt.metadata.purpose === "review" ? committedReviewItemResults(attempt.metadata, assessment) : await this.scoreItems({
|
|
104778
|
-
assessment,
|
|
104779
|
-
responses: attempt.metadata.responses,
|
|
104780
|
-
artifactIdentity: assessmentArtifactIdentityFromAttempt(attempt.metadata),
|
|
104781
|
-
purpose: attempt.metadata.purpose
|
|
104518
|
+
recordTranscriptFailure(operation, attemptId, error88) {
|
|
104519
|
+
addEvent("assessment.transcript_operation_failed", {
|
|
104520
|
+
"app.assessment.operation": operation,
|
|
104521
|
+
"app.assessment.attempt_id": attemptId,
|
|
104522
|
+
"exception.type": errorType(error88),
|
|
104523
|
+
"app.error.message": errorMessage(error88)
|
|
104782
104524
|
});
|
|
104783
|
-
return {
|
|
104784
|
-
responseVersion: attempt.metadata.responseVersion,
|
|
104785
|
-
assessment,
|
|
104786
|
-
itemResults,
|
|
104787
|
-
score: aggregateAssessmentScore(itemResults.map((result) => result.score.earned), itemResults.reduce((total, result) => total + result.score.possible, 0)),
|
|
104788
|
-
correctQuestions: countCorrectQuestions(itemResults.map((result) => result.isCorrect))
|
|
104789
|
-
};
|
|
104790
|
-
}
|
|
104791
|
-
async peekAttempt({
|
|
104792
|
-
studentId,
|
|
104793
|
-
attemptId
|
|
104794
|
-
}) {
|
|
104795
|
-
const result = await this.requireClient().api.oneroster.assessmentResults.get(attemptId).catch((error88) => {
|
|
104796
|
-
if (isApiError(error88) && error88.statusCode === 404) {
|
|
104797
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
104798
|
-
}
|
|
104799
|
-
throw error88;
|
|
104800
|
-
});
|
|
104801
|
-
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
104802
|
-
if (!metadata2 || result.student.sourcedId !== studentId || result.status !== ONEROSTER_STATUS2.active) {
|
|
104803
|
-
throw this.unauthorizedAttempt(attemptId);
|
|
104804
|
-
}
|
|
104805
|
-
return { result, metadata: metadata2 };
|
|
104806
|
-
}
|
|
104807
|
-
async prepareSubmission(params, input) {
|
|
104808
|
-
try {
|
|
104809
|
-
const attempt = await this.peekAttempt(params);
|
|
104810
|
-
const disposition = classifyAssessmentSubmission({
|
|
104811
|
-
inProgress: attempt.result.inProgress ?? "",
|
|
104812
|
-
scoreStatus: attempt.result.scoreStatus,
|
|
104813
|
-
submissionId: attempt.metadata.submissionId
|
|
104814
|
-
}, input.submissionId);
|
|
104815
|
-
if (disposition !== "submit") {
|
|
104816
|
-
return null;
|
|
104817
|
-
}
|
|
104818
|
-
if (this.isPlatformRoutedDiagnosticMetadata(attempt.metadata)) {
|
|
104819
|
-
return null;
|
|
104820
|
-
}
|
|
104821
|
-
return await this.scoreSubmission(attempt);
|
|
104822
|
-
} catch (error88) {
|
|
104823
|
-
addEvent("assessment.submission_preview_failed", {
|
|
104824
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
104825
|
-
"exception.type": errorType(error88),
|
|
104826
|
-
"app.error.message": errorMessage(error88)
|
|
104827
|
-
});
|
|
104828
|
-
return null;
|
|
104829
|
-
}
|
|
104830
104525
|
}
|
|
104831
104526
|
async submit({
|
|
104832
104527
|
input,
|
|
@@ -104834,121 +104529,83 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104834
104529
|
...params
|
|
104835
104530
|
}) {
|
|
104836
104531
|
await this.deps.validateDeveloperAccess(params.user, params.gameId);
|
|
104837
|
-
const
|
|
104838
|
-
const
|
|
104839
|
-
|
|
104840
|
-
|
|
104841
|
-
|
|
104842
|
-
|
|
104843
|
-
|
|
104844
|
-
|
|
104845
|
-
|
|
104532
|
+
const transcript = parseAssessmentTranscript(input.transcript);
|
|
104533
|
+
const request = { ...input, transcript };
|
|
104534
|
+
const requestHash = await assessmentTranscriptRequestHash(request);
|
|
104535
|
+
const accepted = await this.deps.assessmentRuntimeLock(this.deps.db, [assessmentRuntimeAttemptLockKey(params.attemptId)], async (db2) => {
|
|
104536
|
+
const { attempt } = await this.resolveAttemptAuthorization(params, {
|
|
104537
|
+
db: db2,
|
|
104538
|
+
developerAccessValidated: true,
|
|
104539
|
+
includeEnrollment: false
|
|
104540
|
+
});
|
|
104541
|
+
const disposition = resolveAssessmentTranscriptSubmission(attempt.result, attempt.metadata, attempt.integration.status, request, requestHash);
|
|
104542
|
+
if (disposition === "replay") {
|
|
104846
104543
|
return {
|
|
104847
|
-
response: this.submittedResult(
|
|
104848
|
-
emission: this.replayCompletion(
|
|
104544
|
+
response: this.submittedResult(attempt.result, attempt.metadata),
|
|
104545
|
+
emission: this.replayCompletion(attempt, input.submissionId, params, game2)
|
|
104849
104546
|
};
|
|
104850
104547
|
}
|
|
104851
|
-
const
|
|
104852
|
-
|
|
104853
|
-
|
|
104854
|
-
}
|
|
104855
|
-
|
|
104856
|
-
|
|
104857
|
-
|
|
104858
|
-
|
|
104859
|
-
|
|
104860
|
-
|
|
104861
|
-
attempt: { ...attempt, metadata: attempt.metadata },
|
|
104862
|
-
input,
|
|
104863
|
-
params,
|
|
104864
|
-
game: game2,
|
|
104865
|
-
db: db2
|
|
104866
|
-
});
|
|
104867
|
-
}
|
|
104868
|
-
if (assessmentFlowForPurpose(attempt.metadata.purpose) === "item-submit") {
|
|
104869
|
-
const assessment2 = await this.loadAttemptAssessment(attempt.metadata);
|
|
104870
|
-
const flowFailure = assessmentCompletionFlowFailure(attempt.metadata.purpose, attempt.metadata.itemSubmissions, assessment2.items.length);
|
|
104871
|
-
if (flowFailure) {
|
|
104872
|
-
throw AssessmentRuntimeError.from(flowFailure);
|
|
104873
|
-
}
|
|
104874
|
-
}
|
|
104875
|
-
const scored = preview && preview.responseVersion === attempt.metadata.responseVersion ? preview : await this.scoreSubmission(attempt);
|
|
104876
|
-
const { assessment, itemResults, score, correctQuestions } = scored;
|
|
104877
|
-
const client2 = this.requireClient();
|
|
104548
|
+
const graded = await this.gradeTranscript({
|
|
104549
|
+
attemptId: params.attemptId,
|
|
104550
|
+
metadata: attempt.metadata
|
|
104551
|
+
}, transcript, true, db2);
|
|
104552
|
+
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
104553
|
+
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
104554
|
+
metadata: graded.metadata,
|
|
104555
|
+
itemResults: graded.itemResults
|
|
104556
|
+
}) : undefined;
|
|
104557
|
+
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
104878
104558
|
const timestamp6 = new Date().toISOString();
|
|
104879
|
-
const reviewOutcomes = attempt.metadata.purpose === "review" ? this.reviewItemOutcomes({
|
|
104880
|
-
metadata: attempt.metadata,
|
|
104881
|
-
itemResults
|
|
104882
|
-
}) : null;
|
|
104883
104559
|
const finalization = buildAssessmentResultSubmission({
|
|
104884
104560
|
result: attempt.result,
|
|
104885
|
-
metadata:
|
|
104886
|
-
score,
|
|
104561
|
+
metadata: graded.metadata,
|
|
104562
|
+
score: graded.score,
|
|
104887
104563
|
submissionId: input.submissionId,
|
|
104888
104564
|
timestamp: timestamp6,
|
|
104889
|
-
testName: assessment.title,
|
|
104890
|
-
courseName
|
|
104891
|
-
totalQuestions: itemResults.length,
|
|
104892
|
-
correctQuestions,
|
|
104565
|
+
testName: graded.assessment.title,
|
|
104566
|
+
courseName,
|
|
104567
|
+
totalQuestions: graded.itemResults.length,
|
|
104568
|
+
correctQuestions: graded.correctQuestions,
|
|
104893
104569
|
...input.session ? { session: input.session } : {},
|
|
104894
|
-
...
|
|
104570
|
+
...itemOutcomes ? { itemOutcomes } : {},
|
|
104571
|
+
...diagnostic ? { diagnosticTracks: diagnostic.tracks } : {}
|
|
104895
104572
|
});
|
|
104896
|
-
const metadata2 =
|
|
104897
|
-
|
|
104898
|
-
|
|
104899
|
-
|
|
104900
|
-
|
|
104901
|
-
|
|
104902
|
-
|
|
104903
|
-
|
|
104573
|
+
const metadata2 = {
|
|
104574
|
+
...finalization.metadata,
|
|
104575
|
+
completion: { ...finalization.metadata.completion, requestHash }
|
|
104576
|
+
};
|
|
104577
|
+
const resultUpdate = {
|
|
104578
|
+
...finalization.resultUpdate,
|
|
104579
|
+
metadata: mergeAssessmentRuntimeMetadata(attempt.result.metadata, metadata2)
|
|
104580
|
+
};
|
|
104581
|
+
await this.requireClient().api.oneroster.assessmentResults.upsert(params.attemptId, resultUpdate);
|
|
104582
|
+
const result = { ...attempt.result, ...resultUpdate };
|
|
104583
|
+
const emission = {
|
|
104584
|
+
attempt: { ...attempt, result, metadata: metadata2 },
|
|
104585
|
+
testIdentifier: graded.assessment.identifier,
|
|
104586
|
+
testName: graded.assessment.title,
|
|
104587
|
+
score: graded.score,
|
|
104588
|
+
totalQuestions: graded.itemResults.length,
|
|
104589
|
+
correctQuestions: graded.correctQuestions,
|
|
104590
|
+
submissionId: input.submissionId,
|
|
104591
|
+
gameId: params.gameId,
|
|
104592
|
+
studentId: params.studentId,
|
|
104593
|
+
courseName,
|
|
104594
|
+
game: game2
|
|
104904
104595
|
};
|
|
104905
|
-
addEvent("assessment.awaiting_award_created", {
|
|
104906
|
-
"app.assessment.attempt_id": submittedForEmission.sourcedId,
|
|
104907
|
-
"app.assessment.submission_id": input.submissionId,
|
|
104908
|
-
"app.assessment.purpose": metadata2.purpose,
|
|
104909
|
-
"app.assessment.response_version": metadata2.responseVersion
|
|
104910
|
-
});
|
|
104911
104596
|
return {
|
|
104912
|
-
response:
|
|
104913
|
-
|
|
104914
|
-
|
|
104915
|
-
responseVersion: metadata2.responseVersion,
|
|
104916
|
-
status: "awaiting_award",
|
|
104917
|
-
responses: metadata2.responses,
|
|
104918
|
-
score,
|
|
104919
|
-
...metadata2.purpose === "review" ? {
|
|
104920
|
-
purpose: metadata2.purpose,
|
|
104921
|
-
itemOutcomes: reviewOutcomes
|
|
104922
|
-
} : { purpose: metadata2.purpose }
|
|
104923
|
-
},
|
|
104924
|
-
emission: {
|
|
104925
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
104926
|
-
testIdentifier: assessment.identifier,
|
|
104927
|
-
testName: assessment.title,
|
|
104928
|
-
score,
|
|
104929
|
-
totalQuestions: itemResults.length,
|
|
104930
|
-
correctQuestions,
|
|
104931
|
-
submissionId: input.submissionId,
|
|
104932
|
-
gameId: params.gameId,
|
|
104933
|
-
studentId: params.studentId,
|
|
104934
|
-
courseName: attempt.enrollment.course.title,
|
|
104935
|
-
game: game2
|
|
104936
|
-
}
|
|
104597
|
+
response: this.submittedResult(result, metadata2),
|
|
104598
|
+
emission,
|
|
104599
|
+
repair: { context: this.tokenContext(attempt), graded }
|
|
104937
104600
|
};
|
|
104938
104601
|
});
|
|
104939
|
-
if (
|
|
104940
|
-
|
|
104941
|
-
|
|
104942
|
-
|
|
104943
|
-
|
|
104944
|
-
const { result, metadata: metadata2 } = submission.emission.attempt;
|
|
104945
|
-
await Promise.allSettled([
|
|
104946
|
-
this.emitSessionBestEffort(submission.emission),
|
|
104947
|
-
...diagnosticCompletion ? [this.restoreDiagnosticChildResults(diagnosticCompletion)] : [],
|
|
104948
|
-
...metadata2.purpose === "review" ? [this.restoreAllCompletedReviewChildResponses(result, metadata2)] : []
|
|
104949
|
-
]);
|
|
104602
|
+
if (accepted.repair) {
|
|
104603
|
+
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
104604
|
+
}
|
|
104605
|
+
if (accepted.emission) {
|
|
104606
|
+
this.emitSessionBestEffort(accepted.emission);
|
|
104950
104607
|
}
|
|
104951
|
-
return
|
|
104608
|
+
return accepted.response;
|
|
104952
104609
|
}
|
|
104953
104610
|
async finalize({
|
|
104954
104611
|
input,
|
|
@@ -105092,58 +104749,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105092
104749
|
});
|
|
105093
104750
|
}
|
|
105094
104751
|
}
|
|
105095
|
-
async finalizeDiagnosticSubmission(input) {
|
|
105096
|
-
const { attempt } = input;
|
|
105097
|
-
const routing = await this.loadAttemptDiagnosticManifest(attempt.metadata, input.db);
|
|
105098
|
-
const { tracks, score, correctQuestions } = summarizeDiagnosticSubmission(attempt.metadata, routing.manifest);
|
|
105099
|
-
const timestamp6 = new Date().toISOString();
|
|
105100
|
-
const finalization = buildAssessmentResultSubmission({
|
|
105101
|
-
result: attempt.result,
|
|
105102
|
-
metadata: attempt.metadata,
|
|
105103
|
-
score,
|
|
105104
|
-
submissionId: input.input.submissionId,
|
|
105105
|
-
timestamp: timestamp6,
|
|
105106
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
105107
|
-
courseName: attempt.enrollment.course.title,
|
|
105108
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
105109
|
-
correctQuestions,
|
|
105110
|
-
...input.input.session ? { session: input.input.session } : {},
|
|
105111
|
-
diagnosticTracks: tracks
|
|
105112
|
-
});
|
|
105113
|
-
const metadata2 = finalization.metadata;
|
|
105114
|
-
const submitted = await this.requireClient().api.oneroster.assessmentResults.upsert(attempt.result.sourcedId, finalization.resultUpdate);
|
|
105115
|
-
const submittedForEmission = {
|
|
105116
|
-
...submitted,
|
|
105117
|
-
score: score.normalized * 100,
|
|
105118
|
-
scoreDate: timestamp6,
|
|
105119
|
-
...ASSESSMENT_ATTEMPT_AWAITING_AWARD,
|
|
105120
|
-
metadata: finalization.resultUpdate.metadata
|
|
105121
|
-
};
|
|
105122
|
-
return {
|
|
105123
|
-
response: {
|
|
105124
|
-
purpose: "diagnostic",
|
|
105125
|
-
attemptId: submittedForEmission.sourcedId,
|
|
105126
|
-
submissionId: input.input.submissionId,
|
|
105127
|
-
diagnosticKey: metadata2.diagnostic.diagnosticKey,
|
|
105128
|
-
responseVersion: metadata2.responseVersion,
|
|
105129
|
-
status: "awaiting_award",
|
|
105130
|
-
tracks
|
|
105131
|
-
},
|
|
105132
|
-
emission: {
|
|
105133
|
-
attempt: { ...attempt, result: submittedForEmission, metadata: metadata2 },
|
|
105134
|
-
testIdentifier: attempt.metadata.selectedTest.identifier,
|
|
105135
|
-
testName: attempt.metadata.diagnostic.testName,
|
|
105136
|
-
score,
|
|
105137
|
-
totalQuestions: attempt.metadata.itemSubmissions.length,
|
|
105138
|
-
correctQuestions,
|
|
105139
|
-
submissionId: input.input.submissionId,
|
|
105140
|
-
gameId: input.params.gameId,
|
|
105141
|
-
studentId: input.params.studentId,
|
|
105142
|
-
courseName: attempt.enrollment.course.title,
|
|
105143
|
-
game: input.game
|
|
105144
|
-
}
|
|
105145
|
-
};
|
|
105146
|
-
}
|
|
105147
104752
|
async exportFixture({
|
|
105148
104753
|
gameId,
|
|
105149
104754
|
user
|
|
@@ -105343,14 +104948,32 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105343
104948
|
async listAttempts(scope) {
|
|
105344
104949
|
return this.attemptListing(await this.studentAssessmentResults(scope.studentId), scope);
|
|
105345
104950
|
}
|
|
105346
|
-
attemptListing(results, scope) {
|
|
104951
|
+
async attemptListing(results, scope) {
|
|
105347
104952
|
const attempts = new Map;
|
|
105348
104953
|
const reviewChildren = new Map;
|
|
105349
104954
|
for (const result of results) {
|
|
105350
104955
|
const metadata2 = playcademyAssessmentResultMetadata(result.metadata);
|
|
105351
|
-
|
|
105352
|
-
|
|
105353
|
-
|
|
104956
|
+
if (scope.purpose === "review") {
|
|
104957
|
+
const verified = await verifyAssessmentChildResultCommand(result.metadata);
|
|
104958
|
+
if (verified.ok && verified.command.payload.context.kind === "review") {
|
|
104959
|
+
const { command } = verified;
|
|
104960
|
+
const { attempt, administration } = command.payload;
|
|
104961
|
+
const parent = await reviewQuestionLineItemId(await deterministicUUID([
|
|
104962
|
+
"playcademy:review-bank-line-item:v1",
|
|
104963
|
+
scope.integrationId,
|
|
104964
|
+
scope.activityId,
|
|
104965
|
+
attempt.selectedTest.identifier,
|
|
104966
|
+
verified.command.payload.context.bankRevision
|
|
104967
|
+
].join("\x00")), administration.itemIdentifier);
|
|
104968
|
+
const projection = await classifyAssessmentChildResult({
|
|
104969
|
+
result,
|
|
104970
|
+
command,
|
|
104971
|
+
childLineItemId: parent
|
|
104972
|
+
});
|
|
104973
|
+
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") {
|
|
104974
|
+
reviewChildren.set(result.sourcedId, command);
|
|
104975
|
+
}
|
|
104976
|
+
}
|
|
105354
104977
|
}
|
|
105355
104978
|
if (metadata2 && metadata2.activityId === scope.activityId && metadata2.purpose === scope.purpose && metadata2.courseId === scope.courseId && metadata2.integrationId === scope.integrationId && result.student.sourcedId === scope.studentId) {
|
|
105356
104979
|
const selectedAssessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
@@ -105401,19 +105024,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105401
105024
|
}
|
|
105402
105025
|
return true;
|
|
105403
105026
|
}
|
|
105404
|
-
reviewExposures(results, scope) {
|
|
105405
|
-
const exposures = [];
|
|
105406
|
-
for (const result of results) {
|
|
105407
|
-
const metadata2 = playcademyAssessmentItemResultMetadata(result.metadata);
|
|
105408
|
-
if (metadata2?.bankRevision === scope.bankRevision) {
|
|
105409
|
-
exposures.push({
|
|
105410
|
-
itemIdentifier: metadata2.itemIdentifier,
|
|
105411
|
-
deliveredAt: metadata2.deliveredAt
|
|
105412
|
-
});
|
|
105413
|
-
}
|
|
105414
|
-
}
|
|
105415
|
-
return exposures;
|
|
105416
|
-
}
|
|
105417
105027
|
requireDecision(decision, purpose) {
|
|
105418
105028
|
if (!decision) {
|
|
105419
105029
|
throw AssessmentRuntimeError.from(assessmentNoEligibleTests(purpose));
|
|
@@ -105430,29 +105040,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105430
105040
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105431
105041
|
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105432
105042
|
}
|
|
105433
|
-
async resumeDiagnosticSnapshot(decision, attempts, integration,
|
|
105434
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
105435
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
105436
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105437
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105438
|
-
});
|
|
105439
|
-
}
|
|
105043
|
+
async resumeDiagnosticSnapshot(decision, attempts, integration, _db = this.deps.db) {
|
|
105440
105044
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105441
|
-
|
|
105442
|
-
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105443
|
-
}
|
|
105444
|
-
if (!this.isPlatformRoutedDiagnosticMetadata(resumed.metadata)) {
|
|
105445
|
-
throw new Error("A keyed diagnostic request selected a legacy fixed attempt");
|
|
105446
|
-
}
|
|
105447
|
-
const assessment = await this.loadAttemptAssessment(resumed.metadata);
|
|
105448
|
-
const routing = await this.loadAttemptDiagnosticRouting(resumed.metadata, assessment, db2);
|
|
105449
|
-
await this.restoreDiagnosticChildResults({
|
|
105450
|
-
result: resumed.result,
|
|
105451
|
-
metadata: resumed.metadata,
|
|
105452
|
-
integration,
|
|
105453
|
-
assessment
|
|
105454
|
-
});
|
|
105455
|
-
return this.diagnosticSnapshot(resumed.result, resumed.metadata, assessment, integration, routing.state);
|
|
105045
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, integration);
|
|
105456
105046
|
}
|
|
105457
105047
|
async resumeAvailableAttempt(tests, attempts, requestFingerprint, pendingSupersessions, resume) {
|
|
105458
105048
|
const select3 = () => selectRuntimeAssessment(tests, [...this.attemptsMatchingRequest(attempts, requestFingerprint).values()].map((entry) => entry.selection));
|
|
@@ -105523,42 +105113,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105523
105113
|
});
|
|
105524
105114
|
}
|
|
105525
105115
|
}
|
|
105526
|
-
async resumeReviewSnapshot(decision, attempts,
|
|
105527
|
-
if (decision.additionalResumableAttemptIds.length > 0) {
|
|
105528
|
-
addEvent("assessment.multiple_resumable_attempts", {
|
|
105529
|
-
"app.assessment.attempt_id": decision.attempt.attemptId,
|
|
105530
|
-
"app.assessment.additional_attempt_ids": decision.additionalResumableAttemptIds.join(",")
|
|
105531
|
-
});
|
|
105532
|
-
}
|
|
105116
|
+
async resumeReviewSnapshot(decision, attempts, context2) {
|
|
105533
105117
|
const resumed = attempts.get(decision.attempt.attemptId);
|
|
105534
|
-
|
|
105535
|
-
throw new Error("A standards-review request selected a non-review attempt");
|
|
105536
|
-
}
|
|
105537
|
-
const reviewMetadata = resumed.metadata;
|
|
105538
|
-
let assessment;
|
|
105539
|
-
const current = await reconcileReviewChildResults({
|
|
105540
|
-
initial: resumed,
|
|
105541
|
-
writeOpen: async () => {
|
|
105542
|
-
const source = await this.loadPinnedReviewBankSource(reviewMetadata);
|
|
105543
|
-
assessment = projectReviewAssessment(source.assessment, source.bank, reviewMetadata.review.selections);
|
|
105544
|
-
await this.ensureReviewChildResults({
|
|
105545
|
-
result: resumed.result,
|
|
105546
|
-
metadata: reviewMetadata,
|
|
105547
|
-
integration: context2.integration,
|
|
105548
|
-
source,
|
|
105549
|
-
assessment,
|
|
105550
|
-
existingChildResults,
|
|
105551
|
-
lookupMissingResults: true
|
|
105552
|
-
});
|
|
105553
|
-
},
|
|
105554
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, resumed.result.sourcedId),
|
|
105555
|
-
readLatest: () => this.peekAttempt({
|
|
105556
|
-
studentId: resumed.result.student.sourcedId,
|
|
105557
|
-
attemptId: resumed.result.sourcedId
|
|
105558
|
-
}),
|
|
105559
|
-
restoreTerminal: (latest) => this.restoreAllCompletedReviewChildResponses(latest.result, latest.metadata)
|
|
105560
|
-
});
|
|
105561
|
-
return assessmentChildProjectionIsTerminal(current.result) ? this.snapshotForResult(current.result, current.metadata, context2.integration) : this.snapshot(current.result, current.metadata, assessment, context2.integration);
|
|
105118
|
+
return this.snapshotForResult(resumed.result, resumed.metadata, context2.integration);
|
|
105562
105119
|
}
|
|
105563
105120
|
recordReviewPreparationPhase(phase, startedAt, counts = {}) {
|
|
105564
105121
|
addEvent("assessment.review_preparation_phase", {
|
|
@@ -105966,135 +105523,82 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105966
105523
|
}
|
|
105967
105524
|
return lineItemId;
|
|
105968
105525
|
}
|
|
105969
|
-
|
|
105970
|
-
const { metadata: metadata2 } = attempt;
|
|
105971
|
-
const assessmentKey = metadata2.selectedTest.assessmentKey ?? assessmentKeyFromManagedQtiIdentifier(metadata2.selectedTest.identifier);
|
|
105972
|
-
if (!assessmentKey) {
|
|
105973
|
-
addEvent("assessment.child_result_legacy_attempt_skipped", {
|
|
105974
|
-
"app.assessment.attempt_id": attempt.result.sourcedId
|
|
105975
|
-
});
|
|
105976
|
-
return null;
|
|
105977
|
-
}
|
|
105526
|
+
tokenContext(attempt) {
|
|
105978
105527
|
return {
|
|
105979
|
-
|
|
105528
|
+
version: 1,
|
|
105980
105529
|
gameId: attempt.integration.gameId,
|
|
105981
105530
|
studentId: attempt.result.student.sourcedId,
|
|
105982
|
-
|
|
105983
|
-
|
|
105984
|
-
|
|
105985
|
-
enrollmentId: metadata2.enrollmentId,
|
|
105986
|
-
selectedTest: {
|
|
105987
|
-
assessmentKey,
|
|
105988
|
-
identifier: metadata2.selectedTest.identifier,
|
|
105989
|
-
contentRevision: metadata2.selectedTest.contentRevision
|
|
105990
|
-
}
|
|
105991
|
-
};
|
|
105992
|
-
}
|
|
105993
|
-
reviewChildResultContext(metadata2, submission) {
|
|
105994
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === submission.itemIdentifier);
|
|
105995
|
-
if (!selection) {
|
|
105996
|
-
throw new Error(`Committed review item ${submission.itemIdentifier} is not in its pinned selection`);
|
|
105997
|
-
}
|
|
105998
|
-
return {
|
|
105999
|
-
kind: "review",
|
|
106000
|
-
bankRevision: metadata2.review.bankRevision,
|
|
106001
|
-
standard: selection.standard
|
|
105531
|
+
attemptId: attempt.result.sourcedId,
|
|
105532
|
+
parentLineItemId: attempt.result.assessmentLineItem.sourcedId,
|
|
105533
|
+
metadata: assessmentTokenMetadata(attempt.metadata)
|
|
106002
105534
|
};
|
|
106003
105535
|
}
|
|
106004
|
-
|
|
106005
|
-
|
|
106006
|
-
|
|
106007
|
-
|
|
106008
|
-
|
|
106009
|
-
|
|
105536
|
+
async ensureTranscriptLineItems(context2, assessment) {
|
|
105537
|
+
await runWithConcurrency(assessment.items, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (item) => {
|
|
105538
|
+
const id = await assessmentTranscriptChildLineItemId(context2, item.identifier);
|
|
105539
|
+
if (this.childLineItemCache.get(id)) {
|
|
105540
|
+
return;
|
|
105541
|
+
}
|
|
105542
|
+
const metadata2 = context2.metadata;
|
|
105543
|
+
const common2 = {
|
|
105544
|
+
parentLineItemId: context2.parentLineItemId,
|
|
105545
|
+
integration: { courseId: metadata2.courseId },
|
|
105546
|
+
item
|
|
105547
|
+
};
|
|
105548
|
+
if (metadata2.purpose === "review") {
|
|
105549
|
+
const standard = metadata2.review.selections.find((selection) => selection.itemIdentifier === item.identifier)?.standard;
|
|
105550
|
+
if (!standard) {
|
|
105551
|
+
throw new Error("Review candidate is not in the pinned selection");
|
|
105552
|
+
}
|
|
105553
|
+
await this.ensureReviewQuestionLineItem({
|
|
105554
|
+
...common2,
|
|
105555
|
+
bankRevision: metadata2.review.bankRevision,
|
|
105556
|
+
standards: [standard]
|
|
105557
|
+
});
|
|
105558
|
+
} else if (this.isPlatformRoutedDiagnosticMetadata(metadata2)) {
|
|
105559
|
+
await this.ensureDiagnosticQuestionLineItem({ ...common2, metadata: metadata2 });
|
|
105560
|
+
} else {
|
|
105561
|
+
const expected = {
|
|
105562
|
+
status: "active",
|
|
105563
|
+
parentAssessmentLineItem: { sourcedId: context2.parentLineItemId },
|
|
105564
|
+
course: { sourcedId: metadata2.courseId },
|
|
105565
|
+
resultValueMin: 0,
|
|
105566
|
+
resultValueMax: item.maxScore,
|
|
105567
|
+
metadata: {
|
|
105568
|
+
playcademyAssessmentQuestionDefinition: {
|
|
105569
|
+
version: 1,
|
|
105570
|
+
itemIdentifier: item.identifier,
|
|
105571
|
+
contentRevision: metadata2.selectedTest.contentRevision
|
|
105572
|
+
}
|
|
105573
|
+
}
|
|
105574
|
+
};
|
|
105575
|
+
const store = this.requireClient().api.oneroster.assessmentLineItems;
|
|
105576
|
+
try {
|
|
105577
|
+
await store.create({ sourcedId: id, title: item.title, ...expected });
|
|
105578
|
+
} catch (error88) {
|
|
105579
|
+
if (!isApiError(error88) || error88.statusCode !== 409) {
|
|
105580
|
+
throw error88;
|
|
105581
|
+
}
|
|
105582
|
+
this.assertAssessmentChildLineItem(id, await store.get(id), expected);
|
|
105583
|
+
}
|
|
105584
|
+
}
|
|
105585
|
+
this.childLineItemCache.set(id, true);
|
|
106010
105586
|
});
|
|
106011
105587
|
}
|
|
106012
|
-
|
|
106013
|
-
return
|
|
106014
|
-
|
|
106015
|
-
|
|
106016
|
-
|
|
106017
|
-
|
|
106018
|
-
|
|
106019
|
-
|
|
106020
|
-
|
|
106021
|
-
|
|
106022
|
-
|
|
105588
|
+
async reconcileTranscriptChildren(context2, graded, only) {
|
|
105589
|
+
return reconcileAssessmentTranscriptChildren({
|
|
105590
|
+
context: context2,
|
|
105591
|
+
graded,
|
|
105592
|
+
only,
|
|
105593
|
+
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
105594
|
+
ensureLineItems: (assessment) => this.ensureTranscriptLineItems(context2, assessment),
|
|
105595
|
+
onPersisted: (submissionId) => addEvent("assessment.child_result_persisted", {
|
|
105596
|
+
"app.assessment.attempt_id": context2.attemptId,
|
|
105597
|
+
"app.assessment.submission_id": submissionId,
|
|
105598
|
+
"app.assessment.child_result_outcome": "upserted"
|
|
106023
105599
|
})
|
|
106024
105600
|
});
|
|
106025
105601
|
}
|
|
106026
|
-
async prepareAssessmentChildResult(input) {
|
|
106027
|
-
try {
|
|
106028
|
-
const attemptContext = this.assessmentChildAttempt({
|
|
106029
|
-
...input.attempt,
|
|
106030
|
-
metadata: input.metadata
|
|
106031
|
-
});
|
|
106032
|
-
if (!attemptContext) {
|
|
106033
|
-
return null;
|
|
106034
|
-
}
|
|
106035
|
-
const parentLineItemId = input.attempt.result.assessmentLineItem.sourcedId;
|
|
106036
|
-
const { itemIdentifier } = input.submission;
|
|
106037
|
-
const context2 = input.context();
|
|
106038
|
-
const [command, childLineItemId] = await Promise.all([
|
|
106039
|
-
buildSubmissionChildResultCommand({
|
|
106040
|
-
attempt: attemptContext,
|
|
106041
|
-
submission: input.submission,
|
|
106042
|
-
responses: input.metadata.responses[itemIdentifier],
|
|
106043
|
-
grading: input.submission.grading ?? QTI_CHILD_GRADING,
|
|
106044
|
-
context: context2
|
|
106045
|
-
}),
|
|
106046
|
-
context2.kind === "review" ? reviewQuestionLineItemId(parentLineItemId, itemIdentifier) : diagnosticQuestionLineItemId(parentLineItemId, itemIdentifier)
|
|
106047
|
-
]);
|
|
106048
|
-
return { command, childLineItemId };
|
|
106049
|
-
} catch (error88) {
|
|
106050
|
-
addEvent("assessment.child_result_prepare_failed", {
|
|
106051
|
-
"app.assessment.attempt_id": input.attempt.result.sourcedId,
|
|
106052
|
-
"app.assessment.submission_id": input.submission.submissionId,
|
|
106053
|
-
"app.assessment.qti_item_identifier": input.submission.itemIdentifier,
|
|
106054
|
-
"exception.type": errorType(error88),
|
|
106055
|
-
"app.error.message": errorMessage(error88)
|
|
106056
|
-
});
|
|
106057
|
-
return null;
|
|
106058
|
-
}
|
|
106059
|
-
}
|
|
106060
|
-
async projectPreparedAssessmentChildResult(prepared) {
|
|
106061
|
-
if (!prepared) {
|
|
106062
|
-
return;
|
|
106063
|
-
}
|
|
106064
|
-
const startedAt = Date.now();
|
|
106065
|
-
const identity = {
|
|
106066
|
-
"app.assessment.attempt_id": prepared.command.payload.attempt.attemptId,
|
|
106067
|
-
"app.assessment.submission_id": prepared.command.payload.administration.submissionId
|
|
106068
|
-
};
|
|
106069
|
-
try {
|
|
106070
|
-
const disposition = await persistAssessmentChildResult({
|
|
106071
|
-
assessmentResults: this.requireClient().api.oneroster.assessmentResults,
|
|
106072
|
-
command: prepared.command,
|
|
106073
|
-
childLineItemId: prepared.childLineItemId
|
|
106074
|
-
});
|
|
106075
|
-
if (disposition.outcome === "conflict") {
|
|
106076
|
-
addEvent("assessment.child_result_conflict", {
|
|
106077
|
-
...identity,
|
|
106078
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
106079
|
-
"app.assessment.child_result_conflict_reason": disposition.reason
|
|
106080
|
-
});
|
|
106081
|
-
return;
|
|
106082
|
-
}
|
|
106083
|
-
addEvent("assessment.child_result_persisted", {
|
|
106084
|
-
...identity,
|
|
106085
|
-
"app.assessment.child_result_id": disposition.resultId,
|
|
106086
|
-
"app.assessment.child_result_outcome": disposition.outcome,
|
|
106087
|
-
"app.assessment.duration_ms": Date.now() - startedAt
|
|
106088
|
-
});
|
|
106089
|
-
} catch (error88) {
|
|
106090
|
-
addEvent("assessment.child_result_persist_failed", {
|
|
106091
|
-
...identity,
|
|
106092
|
-
"app.assessment.duration_ms": Date.now() - startedAt,
|
|
106093
|
-
"exception.type": errorType(error88),
|
|
106094
|
-
"app.error.message": errorMessage(error88)
|
|
106095
|
-
});
|
|
106096
|
-
}
|
|
106097
|
-
}
|
|
106098
105602
|
assertAssessmentChildLineItem(lineItemId, stored, expected) {
|
|
106099
105603
|
if (assessmentChildLineItemMatches(stored, expected)) {
|
|
106100
105604
|
return;
|
|
@@ -106152,91 +105656,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106152
105656
|
}
|
|
106153
105657
|
return lineItemId;
|
|
106154
105658
|
}
|
|
106155
|
-
async projectDiagnosticItemResponse(projection, assessment, checkpoint = null) {
|
|
106156
|
-
try {
|
|
106157
|
-
const loadedAssessment = assessment ?? await this.loadAttemptAssessment(projection.metadata);
|
|
106158
|
-
const item = loadedAssessment.items.find((candidate) => candidate.identifier === projection.ledgerEntry.itemIdentifier);
|
|
106159
|
-
if (!item) {
|
|
106160
|
-
throw new Error(`Diagnostic assessment is missing item ${projection.ledgerEntry.itemIdentifier}`);
|
|
106161
|
-
}
|
|
106162
|
-
const childLineItemId = await this.ensureDiagnosticQuestionLineItem({
|
|
106163
|
-
parentLineItemId: projection.result.assessmentLineItem.sourcedId,
|
|
106164
|
-
integration: projection.integration,
|
|
106165
|
-
metadata: projection.metadata,
|
|
106166
|
-
item
|
|
106167
|
-
});
|
|
106168
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
106169
|
-
throw new Error("Prepared diagnostic checkpoint resolved a different line item");
|
|
106170
|
-
}
|
|
106171
|
-
const childResultId = await diagnosticQuestionResultId(projection.result.sourcedId, childLineItemId);
|
|
106172
|
-
const finalized = buildFinalizedDiagnosticChildResult({
|
|
106173
|
-
childLineItemId,
|
|
106174
|
-
student: projection.result.student,
|
|
106175
|
-
parentAttemptId: projection.result.sourcedId,
|
|
106176
|
-
metadata: projection.metadata,
|
|
106177
|
-
ledgerEntry: projection.ledgerEntry
|
|
106178
|
-
});
|
|
106179
|
-
await Promise.all([
|
|
106180
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106181
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate)
|
|
106182
|
-
]);
|
|
106183
|
-
} catch (error88) {
|
|
106184
|
-
addEvent("assessment.diagnostic_child_projection_failed", {
|
|
106185
|
-
"app.assessment.attempt_id": projection.result.sourcedId,
|
|
106186
|
-
"app.assessment.qti_item_identifier": projection.ledgerEntry.itemIdentifier,
|
|
106187
|
-
"exception.type": errorType(error88),
|
|
106188
|
-
"app.error.message": errorMessage(error88)
|
|
106189
|
-
});
|
|
106190
|
-
}
|
|
106191
|
-
}
|
|
106192
|
-
async restoreDiagnosticChildResults(attempt) {
|
|
106193
|
-
const assessment = attempt.assessment ?? await this.loadAttemptAssessment(attempt.metadata);
|
|
106194
|
-
await runWithConcurrency(attempt.metadata.diagnostic.ledger, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (ledgerEntry) => {
|
|
106195
|
-
try {
|
|
106196
|
-
const childLineItemId = await diagnosticQuestionLineItemId(attempt.result.assessmentLineItem.sourcedId, ledgerEntry.itemIdentifier);
|
|
106197
|
-
const childResultId = await diagnosticQuestionResultId(attempt.result.sourcedId, childLineItemId);
|
|
106198
|
-
let childResult;
|
|
106199
|
-
try {
|
|
106200
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
106201
|
-
} catch (error88) {
|
|
106202
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
106203
|
-
throw error88;
|
|
106204
|
-
}
|
|
106205
|
-
}
|
|
106206
|
-
const resolution = resolveDiagnosticChildResult({
|
|
106207
|
-
childResult,
|
|
106208
|
-
childLineItemId,
|
|
106209
|
-
parentResult: attempt.result,
|
|
106210
|
-
parentMetadata: attempt.metadata,
|
|
106211
|
-
ledgerEntry
|
|
106212
|
-
});
|
|
106213
|
-
if (resolution.action === "reuse") {
|
|
106214
|
-
return;
|
|
106215
|
-
}
|
|
106216
|
-
if (resolution.action === "restore") {
|
|
106217
|
-
addEvent("assessment.diagnostic_child_result_restore", {
|
|
106218
|
-
"app.assessment.diagnostic_child_restore_reason": resolution.reason,
|
|
106219
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
106220
|
-
"app.assessment.child_result_id": childResultId,
|
|
106221
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier
|
|
106222
|
-
});
|
|
106223
|
-
}
|
|
106224
|
-
await this.projectDiagnosticItemResponse({
|
|
106225
|
-
result: attempt.result,
|
|
106226
|
-
metadata: attempt.metadata,
|
|
106227
|
-
ledgerEntry,
|
|
106228
|
-
integration: attempt.integration
|
|
106229
|
-
}, assessment);
|
|
106230
|
-
} catch (error88) {
|
|
106231
|
-
addEvent("assessment.diagnostic_child_reconciliation_failed", {
|
|
106232
|
-
"app.assessment.attempt_id": attempt.result.sourcedId,
|
|
106233
|
-
"app.assessment.qti_item_identifier": ledgerEntry.itemIdentifier,
|
|
106234
|
-
"exception.type": errorType(error88),
|
|
106235
|
-
"app.error.message": errorMessage(error88)
|
|
106236
|
-
});
|
|
106237
|
-
}
|
|
106238
|
-
});
|
|
106239
|
-
}
|
|
106240
105659
|
async ensureReviewQuestionLineItem(input) {
|
|
106241
105660
|
const client2 = this.requireClient();
|
|
106242
105661
|
const lineItemId = await reviewQuestionLineItemId(input.parentLineItemId, input.item.identifier);
|
|
@@ -106283,220 +105702,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106283
105702
|
}
|
|
106284
105703
|
return lineItemId;
|
|
106285
105704
|
}
|
|
106286
|
-
async putOpenReviewChildResult(input) {
|
|
106287
|
-
const metadata2 = buildReviewItemResultMetadata({
|
|
106288
|
-
metadata: input.parentMetadata,
|
|
106289
|
-
selection: input.selection,
|
|
106290
|
-
parentAttemptId: input.parentResult.sourcedId
|
|
106291
|
-
});
|
|
106292
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(input.childResultId, buildOpenReviewChildResult({
|
|
106293
|
-
childLineItemId: input.childLineItemId,
|
|
106294
|
-
student: input.parentResult.student,
|
|
106295
|
-
metadata: metadata2
|
|
106296
|
-
}));
|
|
106297
|
-
}
|
|
106298
|
-
async ensureReviewChildResults(input) {
|
|
106299
|
-
const itemByIdentifier = new Map(input.assessment.items.map((item) => [item.identifier, item]));
|
|
106300
|
-
const bankItemByIdentifier = new Map(input.source.bank.items.map((item) => [item.itemIdentifier, item]));
|
|
106301
|
-
const physicalItems = new Set(input.metadata.review.selections.map((selection) => selection.itemIdentifier));
|
|
106302
|
-
if (physicalItems.size !== input.metadata.review.selections.length) {
|
|
106303
|
-
throw new Error("MVP review selection must use each physical item at most once");
|
|
106304
|
-
}
|
|
106305
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
106306
|
-
await runWithConcurrency(input.metadata.review.selections, TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (selection) => {
|
|
106307
|
-
const item = itemByIdentifier.get(selection.itemIdentifier);
|
|
106308
|
-
const indexed = bankItemByIdentifier.get(selection.itemIdentifier);
|
|
106309
|
-
if (!item || !indexed) {
|
|
106310
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
106311
|
-
}
|
|
106312
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
106313
|
-
parentLineItemId: input.result.assessmentLineItem.sourcedId,
|
|
106314
|
-
integration: input.integration,
|
|
106315
|
-
bankRevision: input.metadata.review.bankRevision,
|
|
106316
|
-
standards: [...indexed.standards],
|
|
106317
|
-
item
|
|
106318
|
-
});
|
|
106319
|
-
if (!administeredItems.has(selection.itemIdentifier)) {
|
|
106320
|
-
return;
|
|
106321
|
-
}
|
|
106322
|
-
const childResultId = await reviewQuestionResultId(input.result.sourcedId, childLineItemId);
|
|
106323
|
-
const streamedChildResult = input.existingChildResults.get(childResultId);
|
|
106324
|
-
let childResult = streamedChildResult;
|
|
106325
|
-
if (!childResult && input.lookupMissingResults) {
|
|
106326
|
-
try {
|
|
106327
|
-
childResult = await this.requireClient().api.oneroster.assessmentResults.get(childResultId);
|
|
106328
|
-
} catch (error88) {
|
|
106329
|
-
if (!isApiError(error88) || error88.statusCode !== 404) {
|
|
106330
|
-
throw error88;
|
|
106331
|
-
}
|
|
106332
|
-
}
|
|
106333
|
-
}
|
|
106334
|
-
const resolution = resolveReviewChildResult({
|
|
106335
|
-
childResult,
|
|
106336
|
-
childLineItemId,
|
|
106337
|
-
parentResult: input.result,
|
|
106338
|
-
parentMetadata: input.metadata,
|
|
106339
|
-
selection
|
|
106340
|
-
});
|
|
106341
|
-
if (resolution.action === "reuse") {
|
|
106342
|
-
return;
|
|
106343
|
-
}
|
|
106344
|
-
if (resolution.action === "restore") {
|
|
106345
|
-
addEvent("assessment.review_child_result_restore", {
|
|
106346
|
-
"app.assessment.review_child_restore_reason": resolution.reason,
|
|
106347
|
-
"app.assessment.attempt_id": input.result.sourcedId,
|
|
106348
|
-
"app.assessment.child_result_id": childResultId,
|
|
106349
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier
|
|
106350
|
-
});
|
|
106351
|
-
}
|
|
106352
|
-
await this.putOpenReviewChildResult({
|
|
106353
|
-
childResultId,
|
|
106354
|
-
childLineItemId,
|
|
106355
|
-
parentResult: input.result,
|
|
106356
|
-
parentMetadata: input.metadata,
|
|
106357
|
-
selection
|
|
106358
|
-
});
|
|
106359
|
-
});
|
|
106360
|
-
}
|
|
106361
|
-
async putReviewChildResponse(projection, checkpoint) {
|
|
106362
|
-
const { result, metadata: metadata2, itemIdentifier, integration } = projection;
|
|
106363
|
-
const selection = metadata2.review.selections.find((candidate) => candidate.itemIdentifier === itemIdentifier);
|
|
106364
|
-
if (!selection) {
|
|
106365
|
-
return;
|
|
106366
|
-
}
|
|
106367
|
-
try {
|
|
106368
|
-
const source = await this.loadPinnedReviewBankSource(metadata2);
|
|
106369
|
-
const item = source.assessment.items.find((candidate) => candidate.identifier === selection.itemIdentifier);
|
|
106370
|
-
const indexed = source.bank.items.find((candidate) => candidate.itemIdentifier === selection.itemIdentifier);
|
|
106371
|
-
if (!item || !indexed) {
|
|
106372
|
-
throw new Error(`Review assessment or bank is missing selected item ${selection.itemIdentifier}`);
|
|
106373
|
-
}
|
|
106374
|
-
const childLineItemId = await this.ensureReviewQuestionLineItem({
|
|
106375
|
-
parentLineItemId: result.assessmentLineItem.sourcedId,
|
|
106376
|
-
integration,
|
|
106377
|
-
bankRevision: metadata2.review.bankRevision,
|
|
106378
|
-
standards: [...indexed.standards],
|
|
106379
|
-
item
|
|
106380
|
-
});
|
|
106381
|
-
if (checkpoint && checkpoint.childLineItemId !== childLineItemId) {
|
|
106382
|
-
throw new Error("Prepared review checkpoint resolved a different line item");
|
|
106383
|
-
}
|
|
106384
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
106385
|
-
const childMetadata = buildReviewItemResultMetadata({
|
|
106386
|
-
metadata: metadata2,
|
|
106387
|
-
selection,
|
|
106388
|
-
parentAttemptId: result.sourcedId
|
|
106389
|
-
});
|
|
106390
|
-
await Promise.all([
|
|
106391
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106392
|
-
this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, buildOpenReviewChildResult({
|
|
106393
|
-
childLineItemId,
|
|
106394
|
-
student: result.student,
|
|
106395
|
-
metadata: childMetadata
|
|
106396
|
-
}))
|
|
106397
|
-
]);
|
|
106398
|
-
} catch (error88) {
|
|
106399
|
-
addEvent("assessment.review_child_response_projection_failed", {
|
|
106400
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
106401
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
106402
|
-
"exception.type": errorType(error88),
|
|
106403
|
-
"app.error.message": errorMessage(error88)
|
|
106404
|
-
});
|
|
106405
|
-
}
|
|
106406
|
-
}
|
|
106407
|
-
async projectReviewItemResponse(params, projection, checkpoint) {
|
|
106408
|
-
const terminal = assessmentChildProjectionIsTerminal(projection.result);
|
|
106409
|
-
try {
|
|
106410
|
-
await reconcileReviewChildResults({
|
|
106411
|
-
initial: projection,
|
|
106412
|
-
writeOpen: () => this.putReviewChildResponse(projection, checkpoint),
|
|
106413
|
-
crossBarrier: () => crossAssessmentAttemptLockBarrier(this.deps.assessmentRuntimeLock, this.deps.db, params.attemptId),
|
|
106414
|
-
readLatest: () => this.peekAttempt(params),
|
|
106415
|
-
restoreTerminal: async (latest) => {
|
|
106416
|
-
const repair = this.restoreCompletedReviewChildResponses(latest.result, latest.metadata, new Set([projection.itemIdentifier]));
|
|
106417
|
-
if (terminal) {
|
|
106418
|
-
await Promise.allSettled([
|
|
106419
|
-
this.projectPreparedAssessmentChildResult(checkpoint),
|
|
106420
|
-
repair
|
|
106421
|
-
]);
|
|
106422
|
-
} else {
|
|
106423
|
-
await repair;
|
|
106424
|
-
}
|
|
106425
|
-
}
|
|
106426
|
-
});
|
|
106427
|
-
} catch (error88) {
|
|
106428
|
-
addEvent("assessment.review_child_projection_barrier_failed", {
|
|
106429
|
-
"app.assessment.attempt_id": params.attemptId,
|
|
106430
|
-
"app.assessment.qti_item_identifier": projection.itemIdentifier,
|
|
106431
|
-
"exception.type": errorType(error88),
|
|
106432
|
-
"app.error.message": errorMessage(error88)
|
|
106433
|
-
});
|
|
106434
|
-
}
|
|
106435
|
-
}
|
|
106436
|
-
restoreAllCompletedReviewChildResponses(result, metadata2) {
|
|
106437
|
-
return this.restoreCompletedReviewChildResponses(result, metadata2, new Set(metadata2.itemSubmissions.map((item) => item.itemIdentifier)));
|
|
106438
|
-
}
|
|
106439
|
-
async restoreCompletedReviewChildResponses(result, metadata2, changedItemIdentifiers) {
|
|
106440
|
-
const outcomes = metadata2.completion?.itemOutcomes;
|
|
106441
|
-
const submissionId = metadata2.submissionId;
|
|
106442
|
-
if (!submissionId || !outcomes) {
|
|
106443
|
-
addEvent("assessment.review_child_terminal_restore_unavailable", {
|
|
106444
|
-
"app.assessment.attempt_id": result.sourcedId
|
|
106445
|
-
});
|
|
106446
|
-
return;
|
|
106447
|
-
}
|
|
106448
|
-
const outcomeByItem = new Map(outcomes.map((outcome) => [outcome.itemIdentifier, outcome]));
|
|
106449
|
-
await runWithConcurrency(metadata2.review.selections.filter((selection) => changedItemIdentifiers.has(selection.itemIdentifier)), TimebackAssessmentRuntimeService.SCORING_CONCURRENCY, async (selection) => {
|
|
106450
|
-
try {
|
|
106451
|
-
const outcome = outcomeByItem.get(selection.itemIdentifier);
|
|
106452
|
-
if (!outcome) {
|
|
106453
|
-
throw new Error(`Completed review omitted item ${selection.itemIdentifier}`);
|
|
106454
|
-
}
|
|
106455
|
-
const childLineItemId = await reviewQuestionLineItemId(result.assessmentLineItem.sourcedId, selection.itemIdentifier);
|
|
106456
|
-
const childResultId = await reviewQuestionResultId(result.sourcedId, childLineItemId);
|
|
106457
|
-
const finalized = buildFinalizedReviewChildResult({
|
|
106458
|
-
childLineItemId,
|
|
106459
|
-
student: result.student,
|
|
106460
|
-
parentAttemptId: result.sourcedId,
|
|
106461
|
-
metadata: metadata2,
|
|
106462
|
-
selection,
|
|
106463
|
-
itemResult: {
|
|
106464
|
-
itemIdentifier: outcome.itemIdentifier,
|
|
106465
|
-
score: outcome.score,
|
|
106466
|
-
isCorrect: outcome.isCorrect
|
|
106467
|
-
},
|
|
106468
|
-
submissionId,
|
|
106469
|
-
timestamp: result.scoreDate
|
|
106470
|
-
});
|
|
106471
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(childResultId, finalized.resultUpdate);
|
|
106472
|
-
} catch (error88) {
|
|
106473
|
-
addEvent("assessment.review_child_terminal_restore_failed", {
|
|
106474
|
-
"app.assessment.attempt_id": result.sourcedId,
|
|
106475
|
-
"app.assessment.qti_item_identifier": selection.itemIdentifier,
|
|
106476
|
-
"exception.type": errorType(error88),
|
|
106477
|
-
"app.error.message": errorMessage(error88)
|
|
106478
|
-
});
|
|
106479
|
-
}
|
|
106480
|
-
});
|
|
106481
|
-
}
|
|
106482
|
-
reviewItemOutcomes(input) {
|
|
106483
|
-
const resultByItem = new Map(input.itemResults.map((itemResult) => [itemResult.itemIdentifier, itemResult]));
|
|
106484
|
-
const administeredItems = administeredItemIdentifiers(input.metadata.itemSubmissions);
|
|
106485
|
-
return input.metadata.review.selections.filter((selection) => administeredItems.has(selection.itemIdentifier)).map((selection) => {
|
|
106486
|
-
const itemResult = resultByItem.get(selection.itemIdentifier);
|
|
106487
|
-
const submission = itemAdministration(input.metadata.itemSubmissions, selection.itemIdentifier);
|
|
106488
|
-
if (!itemResult || !submission) {
|
|
106489
|
-
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
106490
|
-
}
|
|
106491
|
-
return {
|
|
106492
|
-
itemIdentifier: selection.itemIdentifier,
|
|
106493
|
-
standard: selection.standard,
|
|
106494
|
-
answered: submission.answered,
|
|
106495
|
-
score: itemResult.score,
|
|
106496
|
-
isCorrect: itemResult.isCorrect
|
|
106497
|
-
};
|
|
106498
|
-
});
|
|
106499
|
-
}
|
|
106500
105705
|
async resolveAttemptAuthorization({ gameId, studentId, attemptId, user }, options = {}) {
|
|
106501
105706
|
if (!options.developerAccessValidated) {
|
|
106502
105707
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
@@ -106525,14 +105730,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106525
105730
|
const enrollment = enrollments.find((candidate) => candidate.id === metadata2.enrollmentId && candidate.course.id === integration.courseId);
|
|
106526
105731
|
return { attempt: { result, metadata: metadata2, integration }, enrollment };
|
|
106527
105732
|
}
|
|
106528
|
-
async authorizeAttempt(params, options = {}) {
|
|
106529
|
-
const authorization = await this.resolveAttemptAuthorization(params, options);
|
|
106530
|
-
return this.requireActiveAttempt(authorization, params.attemptId);
|
|
106531
|
-
}
|
|
106532
|
-
requireActiveAttempt(authorization, attemptId) {
|
|
106533
|
-
const enrollment = requireAssessmentMutationAccess(attemptId, authorization.attempt.integration.status, authorization.enrollment);
|
|
106534
|
-
return { ...authorization.attempt, enrollment };
|
|
106535
|
-
}
|
|
106536
105733
|
async authorizeAttemptRead(params) {
|
|
106537
105734
|
const { attempt, enrollment } = await this.resolveAttemptAuthorization(params);
|
|
106538
105735
|
if ((!enrollment || attempt.integration.status === "deactivated") && !this.isSettled(attempt.result)) {
|
|
@@ -106543,7 +105740,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106543
105740
|
unauthorizedAttempt(attemptId) {
|
|
106544
105741
|
return AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
|
|
106545
105742
|
}
|
|
106546
|
-
async snapshotForResult(result, metadata2, integration) {
|
|
105743
|
+
async snapshotForResult(result, metadata2, integration, suppliedTranscript) {
|
|
106547
105744
|
if (isAssessmentAttemptSuperseded({
|
|
106548
105745
|
inProgress: result.inProgress ?? "",
|
|
106549
105746
|
scoreStatus: result.scoreStatus
|
|
@@ -106579,38 +105776,59 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106579
105776
|
result: buildCompletedAssessmentSubmitResult({ result, metadata: metadata2 })
|
|
106580
105777
|
};
|
|
106581
105778
|
}
|
|
106582
|
-
const
|
|
106583
|
-
|
|
106584
|
-
|
|
106585
|
-
|
|
106586
|
-
|
|
106587
|
-
|
|
106588
|
-
integration,
|
|
106589
|
-
assessment
|
|
106590
|
-
});
|
|
106591
|
-
return this.diagnosticSnapshot(result, metadata2, assessment, integration, routing.state);
|
|
106592
|
-
}
|
|
106593
|
-
return this.snapshot(result, metadata2, assessment, integration);
|
|
106594
|
-
}
|
|
106595
|
-
diagnosticSnapshot(result, metadata2, assessment, integration, state) {
|
|
106596
|
-
return buildDiagnosticAssessmentSnapshot({
|
|
106597
|
-
attemptId: result.sourcedId,
|
|
106598
|
-
metadata: metadata2,
|
|
106599
|
-
assessment,
|
|
106600
|
-
state,
|
|
106601
|
-
activityData: this.activityData(metadata2, assessment, integration)
|
|
105779
|
+
const context2 = this.tokenContext({ result, metadata: metadata2, integration });
|
|
105780
|
+
const graded = await resumeAssessmentTranscript({
|
|
105781
|
+
suppliedTranscript,
|
|
105782
|
+
checkpointTranscript: metadata2.transcript,
|
|
105783
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.studentAssessmentResults(context2.studentId)),
|
|
105784
|
+
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
106602
105785
|
});
|
|
105786
|
+
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
105787
|
+
const common2 = {
|
|
105788
|
+
attemptId: result.sourcedId,
|
|
105789
|
+
assessment: graded.assessment,
|
|
105790
|
+
activityData: this.activityData(metadata2, graded.assessment, integration)
|
|
105791
|
+
};
|
|
105792
|
+
return this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? {
|
|
105793
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105794
|
+
...common2,
|
|
105795
|
+
metadata: graded.metadata,
|
|
105796
|
+
state: graded.metadata.diagnostic.state
|
|
105797
|
+
}),
|
|
105798
|
+
...session2
|
|
105799
|
+
} : {
|
|
105800
|
+
...buildConventionalAssessmentSnapshot({ ...common2, metadata: graded.metadata }),
|
|
105801
|
+
...session2
|
|
105802
|
+
};
|
|
105803
|
+
}
|
|
105804
|
+
async assessmentSession(context2, transcript = emptyAssessmentTranscript(context2.attemptId, context2.metadata.selectedTest.contentRevision)) {
|
|
105805
|
+
return { transcript, assessmentToken: await this.deps.mintAssessmentToken(context2) };
|
|
105806
|
+
}
|
|
105807
|
+
async diagnosticSnapshot(result, metadata2, assessment, integration, options) {
|
|
105808
|
+
return {
|
|
105809
|
+
...buildDiagnosticAssessmentSnapshot({
|
|
105810
|
+
attemptId: result.sourcedId,
|
|
105811
|
+
metadata: metadata2,
|
|
105812
|
+
assessment,
|
|
105813
|
+
state: options.state,
|
|
105814
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105815
|
+
}),
|
|
105816
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105817
|
+
};
|
|
106603
105818
|
}
|
|
106604
105819
|
diagnosticRoutingSnapshot(metadata2, state) {
|
|
106605
105820
|
return projectDiagnosticRoutingSnapshot(metadata2.diagnostic.routingRevision, state);
|
|
106606
105821
|
}
|
|
106607
|
-
snapshot(result, metadata2, assessment, integration) {
|
|
106608
|
-
return
|
|
106609
|
-
|
|
106610
|
-
|
|
106611
|
-
|
|
106612
|
-
|
|
106613
|
-
|
|
105822
|
+
async snapshot(result, metadata2, assessment, integration) {
|
|
105823
|
+
return {
|
|
105824
|
+
...buildConventionalAssessmentSnapshot({
|
|
105825
|
+
attemptId: result.sourcedId,
|
|
105826
|
+
metadata: metadata2,
|
|
105827
|
+
assessment,
|
|
105828
|
+
activityData: this.activityData(metadata2, assessment, integration)
|
|
105829
|
+
}),
|
|
105830
|
+
...await this.assessmentSession(this.tokenContext({ result, metadata: metadata2, integration }))
|
|
105831
|
+
};
|
|
106614
105832
|
}
|
|
106615
105833
|
activityData(metadata2, assessment, integration) {
|
|
106616
105834
|
const course = this.trackableCourse(integration, "assessment.activity_tracking_invalid_course_metadata");
|
|
@@ -106671,16 +105889,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106671
105889
|
isSettled(result) {
|
|
106672
105890
|
return this.isAwaitingAward(result) || this.isCompleted(result);
|
|
106673
105891
|
}
|
|
106674
|
-
assertInProgress(result) {
|
|
106675
|
-
if (!this.isOpen(result)) {
|
|
106676
|
-
throw AssessmentRuntimeError.from(assessmentAttemptNotOpen(result.sourcedId));
|
|
106677
|
-
}
|
|
106678
|
-
}
|
|
106679
|
-
assertResponseVersion(metadata2, expectedResponseVersion) {
|
|
106680
|
-
if (metadata2.responseVersion !== expectedResponseVersion) {
|
|
106681
|
-
throw AssessmentRuntimeError.from(assessmentResponseVersionConflict(expectedResponseVersion, metadata2.responseVersion));
|
|
106682
|
-
}
|
|
106683
|
-
}
|
|
106684
105892
|
scoreFromResult(result, possible, metadata2) {
|
|
106685
105893
|
return metadata2.score ?? assessmentScoreFromPercentage(result.score, possible);
|
|
106686
105894
|
}
|
|
@@ -106748,7 +105956,9 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106748
105956
|
grade: integration.grade
|
|
106749
105957
|
})
|
|
106750
105958
|
});
|
|
106751
|
-
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration,
|
|
105959
|
+
return this.isPlatformRoutedDiagnosticMetadata(metadata2) ? this.diagnosticSnapshot(result, metadata2, assessment, integration, {
|
|
105960
|
+
state: metadata2.diagnostic.state
|
|
105961
|
+
}) : this.snapshot(result, metadata2, assessment, integration);
|
|
106752
105962
|
}
|
|
106753
105963
|
async claimAttemptId(scope, from) {
|
|
106754
105964
|
for (let attemptNumber = from;attemptNumber < from + 20; attemptNumber += 1) {
|
|
@@ -106793,18 +106003,6 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
106793
106003
|
game: game2
|
|
106794
106004
|
};
|
|
106795
106005
|
}
|
|
106796
|
-
async putResultMetadata(result, metadata2) {
|
|
106797
|
-
await this.requireClient().api.oneroster.assessmentResults.upsert(result.sourcedId, {
|
|
106798
|
-
status: ONEROSTER_STATUS2.active,
|
|
106799
|
-
assessmentLineItem: result.assessmentLineItem,
|
|
106800
|
-
student: result.student,
|
|
106801
|
-
score: result.score ?? 0,
|
|
106802
|
-
scoreDate: result.scoreDate,
|
|
106803
|
-
scoreStatus: result.scoreStatus,
|
|
106804
|
-
...result.inProgress === undefined || result.inProgress === null ? {} : { inProgress: result.inProgress },
|
|
106805
|
-
metadata: mergeAssessmentRuntimeMetadata(result.metadata, metadata2)
|
|
106806
|
-
});
|
|
106807
|
-
}
|
|
106808
106006
|
async emitCompletionBestEffort(input) {
|
|
106809
106007
|
const { attempt } = input;
|
|
106810
106008
|
const integration = attempt.integration;
|
|
@@ -110393,6 +109591,8 @@ function createPlatformServices(deps) {
|
|
|
110393
109591
|
mintPlatformServiceToken,
|
|
110394
109592
|
mintAssessmentPreparationReceipt,
|
|
110395
109593
|
verifyAssessmentPreparationReceipt,
|
|
109594
|
+
mintAssessmentToken,
|
|
109595
|
+
verifyAssessmentToken,
|
|
110396
109596
|
validateDeveloperAccessBySlug,
|
|
110397
109597
|
validateDeveloperAccess,
|
|
110398
109598
|
validateGameManagementAccess
|
|
@@ -110467,7 +109667,9 @@ function createPlatformServices(deps) {
|
|
|
110467
109667
|
validateDeveloperAccess,
|
|
110468
109668
|
assessmentRuntimeLock,
|
|
110469
109669
|
mintAssessmentPreparationReceipt,
|
|
110470
|
-
verifyAssessmentPreparationReceipt
|
|
109670
|
+
verifyAssessmentPreparationReceipt,
|
|
109671
|
+
mintAssessmentToken,
|
|
109672
|
+
verifyAssessmentToken
|
|
110471
109673
|
});
|
|
110472
109674
|
return {
|
|
110473
109675
|
assessmentAssets,
|
|
@@ -111217,6 +110419,8 @@ function createServices(ctx) {
|
|
|
111217
110419
|
mintPlatformServiceToken: auth2.mintPlatformServiceToken.bind(auth2),
|
|
111218
110420
|
mintAssessmentPreparationReceipt: auth2.mintAssessmentPreparationReceipt.bind(auth2),
|
|
111219
110421
|
verifyAssessmentPreparationReceipt: auth2.verifyAssessmentPreparationReceipt.bind(auth2),
|
|
110422
|
+
mintAssessmentToken: auth2.mintAssessmentToken.bind(auth2),
|
|
110423
|
+
verifyAssessmentToken: auth2.verifyAssessmentToken.bind(auth2),
|
|
111220
110424
|
...gaming.validators
|
|
111221
110425
|
});
|
|
111222
110426
|
const standalone = createStandaloneServices({ db: db2, auth: auth2, timeback: timeback2 });
|
|
@@ -111745,6 +110949,15 @@ function createSandboxAuthProvider() {
|
|
|
111745
110949
|
});
|
|
111746
110950
|
return { receipt, expiresAt: new Date(expiresAt).toISOString() };
|
|
111747
110951
|
},
|
|
110952
|
+
async mintAssessmentToken(context2) {
|
|
110953
|
+
const token = `sandbox_assessment_${crypto.randomUUID()}`;
|
|
110954
|
+
sandboxAssessmentTokens.set(token, structuredClone(context2));
|
|
110955
|
+
return token;
|
|
110956
|
+
},
|
|
110957
|
+
async verifyAssessmentToken(token) {
|
|
110958
|
+
const context2 = sandboxAssessmentTokens.get(token);
|
|
110959
|
+
return context2 ? structuredClone(context2) : null;
|
|
110960
|
+
},
|
|
111748
110961
|
async verifyAssessmentPreparationReceipt(receipt) {
|
|
111749
110962
|
const prepared = sandboxAssessmentPreparationReceipts.get(receipt);
|
|
111750
110963
|
if (!prepared) {
|
|
@@ -111797,13 +111010,18 @@ function createSandboxAuthProvider() {
|
|
|
111797
111010
|
}
|
|
111798
111011
|
};
|
|
111799
111012
|
}
|
|
111800
|
-
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS;
|
|
111801
|
-
var init_auth_provider = __esm(() => {
|
|
111013
|
+
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, sandboxAssessmentTokens;
|
|
111014
|
+
var init_auth_provider = __esm(async () => {
|
|
111802
111015
|
init_src6();
|
|
111803
111016
|
init_constants();
|
|
111017
|
+
await init_dist5();
|
|
111804
111018
|
sandboxApiKeys = new Map;
|
|
111805
111019
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
111806
111020
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 5 * 60 * 1000;
|
|
111021
|
+
sandboxAssessmentTokens = new TimebackCache({
|
|
111022
|
+
defaultTTL: 2 * 60 * 60 * 1000,
|
|
111023
|
+
maxSize: 1000
|
|
111024
|
+
});
|
|
111807
111025
|
});
|
|
111808
111026
|
|
|
111809
111027
|
// src/infrastructure/api/providers/cache.provider.ts
|
|
@@ -111964,10 +111182,10 @@ var init_storage_provider = __esm(() => {
|
|
|
111964
111182
|
});
|
|
111965
111183
|
|
|
111966
111184
|
// src/infrastructure/api/providers/index.ts
|
|
111967
|
-
var init_providers = __esm(() => {
|
|
111968
|
-
init_auth_provider();
|
|
111185
|
+
var init_providers = __esm(async () => {
|
|
111969
111186
|
init_cache_provider();
|
|
111970
111187
|
init_storage_provider();
|
|
111188
|
+
await init_auth_provider();
|
|
111971
111189
|
});
|
|
111972
111190
|
|
|
111973
111191
|
// src/infrastructure/api/context.ts
|
|
@@ -112037,10 +111255,10 @@ var init_context = __esm(async () => {
|
|
|
112037
111255
|
init_config2();
|
|
112038
111256
|
init_utils7();
|
|
112039
111257
|
init_src6();
|
|
112040
|
-
init_providers();
|
|
112041
111258
|
await __promiseAll([
|
|
112042
111259
|
init_factory(),
|
|
112043
|
-
init_clients()
|
|
111260
|
+
init_clients(),
|
|
111261
|
+
init_providers()
|
|
112044
111262
|
]);
|
|
112045
111263
|
});
|
|
112046
111264
|
|
|
@@ -168436,7 +167654,7 @@ function parseQtiLibraryParams(searchParams) {
|
|
|
168436
167654
|
limit
|
|
168437
167655
|
};
|
|
168438
167656
|
}
|
|
168439
|
-
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;
|
|
167657
|
+
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;
|
|
168440
167658
|
var init_timeback_controller = __esm(() => {
|
|
168441
167659
|
init_esm();
|
|
168442
167660
|
init_src();
|
|
@@ -168738,6 +167956,20 @@ var init_timeback_controller = __esm(() => {
|
|
|
168738
167956
|
user: ctx.user
|
|
168739
167957
|
});
|
|
168740
167958
|
});
|
|
167959
|
+
resumeRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
167960
|
+
const attemptId = ctx.params.attemptId;
|
|
167961
|
+
const body2 = await parseRequestBody(ctx.request, ResumeRuntimeAssessmentRequestSchema);
|
|
167962
|
+
if (!attemptId) {
|
|
167963
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167964
|
+
}
|
|
167965
|
+
return ctx.services.timebackAssessmentRuntime.resume({
|
|
167966
|
+
gameId: body2.gameId,
|
|
167967
|
+
studentId: body2.studentId,
|
|
167968
|
+
attemptId,
|
|
167969
|
+
input: { transcript: body2.transcript },
|
|
167970
|
+
user: ctx.user
|
|
167971
|
+
});
|
|
167972
|
+
});
|
|
168741
167973
|
saveRuntimeAssessment = requireDeveloper(async (ctx) => {
|
|
168742
167974
|
const attemptId = ctx.params.attemptId;
|
|
168743
167975
|
const body2 = await parseRequestBody(ctx.request, SaveRuntimeAssessmentRequestSchema);
|
|
@@ -168750,7 +167982,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
168750
167982
|
attemptId,
|
|
168751
167983
|
input: {
|
|
168752
167984
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168753
|
-
|
|
167985
|
+
transcript: body2.transcript
|
|
167986
|
+
},
|
|
167987
|
+
user: ctx.user
|
|
167988
|
+
});
|
|
167989
|
+
});
|
|
167990
|
+
recordRuntimeAssessmentProgress = requireDeveloper(async (ctx) => {
|
|
167991
|
+
const attemptId = ctx.params.attemptId;
|
|
167992
|
+
const body2 = await parseRequestBody(ctx.request, RecordRuntimeAssessmentProgressRequestSchema);
|
|
167993
|
+
if (!attemptId) {
|
|
167994
|
+
throw ApiError.badRequest("Missing attemptId");
|
|
167995
|
+
}
|
|
167996
|
+
return ctx.services.timebackAssessmentRuntime.recordProgress({
|
|
167997
|
+
gameId: body2.gameId,
|
|
167998
|
+
studentId: body2.studentId,
|
|
167999
|
+
attemptId,
|
|
168000
|
+
input: {
|
|
168001
|
+
assessmentToken: body2.assessmentToken,
|
|
168002
|
+
transcript: body2.transcript,
|
|
168003
|
+
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168004
|
+
itemIdentifier: body2.itemIdentifier
|
|
168754
168005
|
},
|
|
168755
168006
|
user: ctx.user
|
|
168756
168007
|
});
|
|
@@ -168766,6 +168017,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
168766
168017
|
studentId: body2.studentId,
|
|
168767
168018
|
attemptId,
|
|
168768
168019
|
input: {
|
|
168020
|
+
assessmentToken: body2.assessmentToken,
|
|
168021
|
+
transcript: body2.transcript,
|
|
168769
168022
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168770
168023
|
submissionId: body2.submissionId,
|
|
168771
168024
|
itemIdentifier: body2.itemIdentifier,
|
|
@@ -168788,6 +168041,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
168788
168041
|
input: {
|
|
168789
168042
|
expectedResponseVersion: body2.expectedResponseVersion,
|
|
168790
168043
|
submissionId: body2.submissionId,
|
|
168044
|
+
transcript: body2.transcript,
|
|
168791
168045
|
...body2.session ? { session: body2.session } : {}
|
|
168792
168046
|
},
|
|
168793
168047
|
game: {
|
|
@@ -169265,6 +168519,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
169265
168519
|
getLatestRuntimeAssessment,
|
|
169266
168520
|
getRuntimeAssessment,
|
|
169267
168521
|
saveRuntimeAssessment,
|
|
168522
|
+
resumeRuntimeAssessment,
|
|
168523
|
+
recordRuntimeAssessmentProgress,
|
|
169268
168524
|
submitRuntimeAssessmentItem,
|
|
169269
168525
|
submitRuntimeAssessment,
|
|
169270
168526
|
finalizeRuntimeAssessment,
|
|
@@ -170257,6 +169513,8 @@ var init_timeback8 = __esm(async () => {
|
|
|
170257
169513
|
timebackRouter.post("/assessments/start", handle2(timeback2.startRuntimeAssessment));
|
|
170258
169514
|
timebackRouter.get("/assessments/latest", handle2(timeback2.getLatestRuntimeAssessment));
|
|
170259
169515
|
timebackRouter.get("/assessments/:attemptId", handle2(timeback2.getRuntimeAssessment));
|
|
169516
|
+
timebackRouter.post("/assessments/:attemptId/resume", handle2(timeback2.resumeRuntimeAssessment));
|
|
169517
|
+
timebackRouter.post("/assessments/:attemptId/progress", handle2(timeback2.recordRuntimeAssessmentProgress));
|
|
170260
169518
|
timebackRouter.post("/assessments/:attemptId/save", handle2(timeback2.saveRuntimeAssessment));
|
|
170261
169519
|
timebackRouter.post("/assessments/:attemptId/submit-item", handle2(timeback2.submitRuntimeAssessmentItem));
|
|
170262
169520
|
timebackRouter.post("/assessments/:attemptId/submit", handle2(timeback2.submitRuntimeAssessment));
|
|
@@ -170665,13 +169923,13 @@ var init_server = __esm(async () => {
|
|
|
170665
169923
|
init_port();
|
|
170666
169924
|
init_package();
|
|
170667
169925
|
init_config();
|
|
170668
|
-
init_providers();
|
|
170669
169926
|
init_app();
|
|
170670
169927
|
init_database2();
|
|
170671
169928
|
init_options();
|
|
170672
169929
|
await __promiseAll([
|
|
170673
169930
|
init_api3(),
|
|
170674
|
-
init_context()
|
|
169931
|
+
init_context(),
|
|
169932
|
+
init_providers()
|
|
170675
169933
|
]);
|
|
170676
169934
|
version5 = package_default.version;
|
|
170677
169935
|
});
|