@playcademy/sandbox 0.10.0 → 0.10.1-beta.2
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 +86 -31
- package/dist/server.js +86 -31
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1128,7 +1128,7 @@ var package_default;
|
|
|
1128
1128
|
var init_package = __esm(() => {
|
|
1129
1129
|
package_default = {
|
|
1130
1130
|
name: "@playcademy/sandbox",
|
|
1131
|
-
version: "0.10.
|
|
1131
|
+
version: "0.10.1-beta.2",
|
|
1132
1132
|
description: "Local development server for Playcademy game development",
|
|
1133
1133
|
type: "module",
|
|
1134
1134
|
exports: {
|
|
@@ -9562,6 +9562,21 @@ function assessmentReviewBankShortage(fulfillment) {
|
|
|
9562
9562
|
function assessmentFlowForPurpose(purpose) {
|
|
9563
9563
|
return purpose === "review" ? "item-submit" : "attempt-submit";
|
|
9564
9564
|
}
|
|
9565
|
+
function assessmentItemOutcomes(submissions, standardForItem) {
|
|
9566
|
+
return submissions.map((submission) => {
|
|
9567
|
+
const standard = standardForItem(submission.itemIdentifier);
|
|
9568
|
+
if (!standard) {
|
|
9569
|
+
throw new Error(`Missing outcome standard for item ${submission.itemIdentifier}`);
|
|
9570
|
+
}
|
|
9571
|
+
return {
|
|
9572
|
+
itemIdentifier: submission.itemIdentifier,
|
|
9573
|
+
standard: { ...standard },
|
|
9574
|
+
answered: submission.answered,
|
|
9575
|
+
score: { ...submission.score },
|
|
9576
|
+
isCorrect: submission.isCorrect
|
|
9577
|
+
};
|
|
9578
|
+
});
|
|
9579
|
+
}
|
|
9565
9580
|
function isObject(value) {
|
|
9566
9581
|
return typeof value === "object" && value !== null;
|
|
9567
9582
|
}
|
|
@@ -10730,7 +10745,7 @@ function completeAssessmentTranscript(input) {
|
|
|
10730
10745
|
}
|
|
10731
10746
|
if (input.terminal) {
|
|
10732
10747
|
const count = itemSubmissions.length;
|
|
10733
|
-
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount;
|
|
10748
|
+
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount && (input.purpose !== "mastery" || count > 0);
|
|
10734
10749
|
const complete = state ? state.status === "ready-to-complete" && count > 0 : conventionalComplete;
|
|
10735
10750
|
if (!complete) {
|
|
10736
10751
|
throw new AssessmentTranscriptError("A complete administered transcript is required for submission", count);
|
|
@@ -10738,12 +10753,6 @@ function completeAssessmentTranscript(input) {
|
|
|
10738
10753
|
}
|
|
10739
10754
|
return { itemSubmissions, ledger, state };
|
|
10740
10755
|
}
|
|
10741
|
-
function administeredItemIdentifiers(itemSubmissions) {
|
|
10742
|
-
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10743
|
-
}
|
|
10744
|
-
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
10745
|
-
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
10746
|
-
}
|
|
10747
10756
|
function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
|
|
10748
10757
|
if (candidates.length === 0) {
|
|
10749
10758
|
return { outcome: "none" };
|
|
@@ -10993,6 +11002,32 @@ function scorePlatformAssessmentItem(item, responses) {
|
|
|
10993
11002
|
isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
|
|
10994
11003
|
};
|
|
10995
11004
|
}
|
|
11005
|
+
function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
|
|
11006
|
+
if (!review.requireBothQuestions) {
|
|
11007
|
+
return;
|
|
11008
|
+
}
|
|
11009
|
+
const administered = new Set(administeredItemIdentifiers2);
|
|
11010
|
+
const failures = [];
|
|
11011
|
+
for (const standard of review.standards) {
|
|
11012
|
+
const key = assessmentStandardRefKey(standard);
|
|
11013
|
+
const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
|
|
11014
|
+
const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
|
|
11015
|
+
if (selected.size < 2 || administeredCount === 1) {
|
|
11016
|
+
failures.push({
|
|
11017
|
+
standard,
|
|
11018
|
+
reason: selected.size < 2 ? "selection_shortage" : "not_administered",
|
|
11019
|
+
requiredCount: 2,
|
|
11020
|
+
selectedCount: selected.size,
|
|
11021
|
+
administeredCount
|
|
11022
|
+
});
|
|
11023
|
+
}
|
|
11024
|
+
}
|
|
11025
|
+
if (failures.length > 0) {
|
|
11026
|
+
try {
|
|
11027
|
+
report({ code: "REVIEW_REQUIRED_PAIR_UNAVAILABLE", failures });
|
|
11028
|
+
} catch {}
|
|
11029
|
+
}
|
|
11030
|
+
}
|
|
10996
11031
|
function reviewStandardFieldsWithinLimits(input) {
|
|
10997
11032
|
return input.framework.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength && input.identifier.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength;
|
|
10998
11033
|
}
|
|
@@ -11045,7 +11080,11 @@ function normalizeReviewRequest(request) {
|
|
|
11045
11080
|
if (standards.length === 0) {
|
|
11046
11081
|
throw new Error("At least one review standard is required");
|
|
11047
11082
|
}
|
|
11048
|
-
return {
|
|
11083
|
+
return {
|
|
11084
|
+
standards,
|
|
11085
|
+
candidateItemsPerStandard,
|
|
11086
|
+
...request.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11087
|
+
};
|
|
11049
11088
|
}
|
|
11050
11089
|
function reviewRequestFingerprint(request, policy) {
|
|
11051
11090
|
const normalized = normalizeReviewRequest(request);
|
|
@@ -11055,6 +11094,7 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
11055
11094
|
version: policy.version
|
|
11056
11095
|
},
|
|
11057
11096
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
11097
|
+
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
11058
11098
|
standardKeys: normalized.standards.map(assessmentStandardRefKey)
|
|
11059
11099
|
});
|
|
11060
11100
|
}
|
|
@@ -11570,7 +11610,7 @@ function isReviewFulfillment(value) {
|
|
|
11570
11610
|
const shortagesValid = value.shortages.every((shortage) => isRecord(shortage) && isAssessmentStandardRef(shortage.standard) && Number.isInteger(shortage.requested) && Number.isInteger(shortage.selected) && Number.isInteger(shortage.eligible));
|
|
11571
11611
|
return shortagesValid && (value.status === "complete" && value.shortages.length === 0 || value.status === "partial" && value.shortages.length > 0);
|
|
11572
11612
|
}
|
|
11573
|
-
function
|
|
11613
|
+
function isAssessmentItemOutcome(value) {
|
|
11574
11614
|
return isRecord(value) && typeof value.itemIdentifier === "string" && value.itemIdentifier.length > 0 && isAssessmentStandardRef(value.standard) && typeof value.answered === "boolean" && isAssessmentScore(value.score) && (value.isCorrect === null || typeof value.isCorrect === "boolean");
|
|
11575
11615
|
}
|
|
11576
11616
|
function isAssessmentItemSubmission(value) {
|
|
@@ -11637,7 +11677,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
|
|
|
11637
11677
|
});
|
|
11638
11678
|
}
|
|
11639
11679
|
function isReviewAttemptMetadata(value) {
|
|
11640
|
-
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11680
|
+
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean") && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11641
11681
|
}
|
|
11642
11682
|
function isOptionalInteger(value) {
|
|
11643
11683
|
return value === undefined || Number.isInteger(value);
|
|
@@ -11661,7 +11701,7 @@ function isCompletionMetadata(value, purpose, routedDiagnostic) {
|
|
|
11661
11701
|
if (!isRecord(value) || typeof value.testName !== "string" || value.courseName !== undefined && typeof value.courseName !== "string" || !Number.isInteger(value.correctQuestions) || value.session !== undefined && !isAssessmentSessionTiming(value.session) || value.totalQuestions !== undefined && (!Number.isInteger(value.totalQuestions) || Number(value.totalQuestions) < 0)) {
|
|
11662
11702
|
return false;
|
|
11663
11703
|
}
|
|
11664
|
-
const itemOutcomesValid = value.itemOutcomes === undefined || purpose === "review" && Array.isArray(value.itemOutcomes) && value.itemOutcomes.every(
|
|
11704
|
+
const itemOutcomesValid = value.itemOutcomes === undefined || purpose === "review" && Array.isArray(value.itemOutcomes) && value.itemOutcomes.every(isAssessmentItemOutcome);
|
|
11665
11705
|
const diagnosticTracksValid = value.diagnosticTracks === undefined || routedDiagnostic && Array.isArray(value.diagnosticTracks) && value.diagnosticTracks.every(isDiagnosticTrackResult);
|
|
11666
11706
|
return itemOutcomesValid && diagnosticTracksValid && (!routedDiagnostic || value.itemOutcomes === undefined && Array.isArray(value.diagnosticTracks) && value.diagnosticTracks.length > 0);
|
|
11667
11707
|
}
|
|
@@ -12335,6 +12375,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12335
12375
|
}),
|
|
12336
12376
|
StartAssessmentBaseSchema.extend({
|
|
12337
12377
|
purpose: exports_external.literal("review"),
|
|
12378
|
+
requireBothQuestions: exports_external.boolean().optional(),
|
|
12338
12379
|
standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12339
12380
|
candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
|
|
12340
12381
|
}),
|
|
@@ -100305,6 +100346,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100305
100346
|
purpose: metadata2.purpose,
|
|
100306
100347
|
standards: [...metadata2.review.standards],
|
|
100307
100348
|
candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
|
|
100349
|
+
...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
100308
100350
|
selections: [...metadata2.review.selections],
|
|
100309
100351
|
fulfillment: metadata2.review.fulfillment
|
|
100310
100352
|
};
|
|
@@ -100388,6 +100430,17 @@ function buildAssessmentSubmitResult(input) {
|
|
|
100388
100430
|
const itemOutcomes = input.metadata.completion?.itemOutcomes;
|
|
100389
100431
|
return itemOutcomes ? { ...base, purpose: input.metadata.purpose, itemOutcomes } : null;
|
|
100390
100432
|
}
|
|
100433
|
+
if (input.metadata.purpose === "mastery") {
|
|
100434
|
+
const { itemSubmissions, mastery, completion } = input.metadata;
|
|
100435
|
+
if (itemSubmissions.length === 0 || completion?.totalQuestions !== undefined && itemSubmissions.length !== completion.totalQuestions) {
|
|
100436
|
+
return null;
|
|
100437
|
+
}
|
|
100438
|
+
return {
|
|
100439
|
+
...base,
|
|
100440
|
+
purpose: "mastery",
|
|
100441
|
+
itemOutcomes: assessmentItemOutcomes(itemSubmissions, () => mastery.standard)
|
|
100442
|
+
};
|
|
100443
|
+
}
|
|
100391
100444
|
return { ...base, purpose: input.metadata.purpose };
|
|
100392
100445
|
}
|
|
100393
100446
|
function assessmentAwardFromResult(result, metadata2) {
|
|
@@ -102765,11 +102818,13 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102765
102818
|
review: {
|
|
102766
102819
|
requestFingerprint: reviewRequestFingerprint({
|
|
102767
102820
|
standards: plan.review.standards,
|
|
102768
|
-
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102821
|
+
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102822
|
+
requireBothQuestions: plan.review.requireBothQuestions
|
|
102769
102823
|
}, DEFAULT_REVIEW_SELECTION_POLICY),
|
|
102770
102824
|
bankRevision: plan.review.bankRevision,
|
|
102771
102825
|
standards: plan.review.standards,
|
|
102772
102826
|
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102827
|
+
...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
102773
102828
|
selections: plan.review.selections,
|
|
102774
102829
|
fulfillment: plan.review.fulfillment
|
|
102775
102830
|
}
|
|
@@ -103394,22 +103449,11 @@ async function resumeAssessmentTranscript(input) {
|
|
|
103394
103449
|
}
|
|
103395
103450
|
}
|
|
103396
103451
|
function assessmentReviewTranscriptOutcomes(input) {
|
|
103397
|
-
const
|
|
103398
|
-
|
|
103399
|
-
|
|
103400
|
-
|
|
103401
|
-
|
|
103402
|
-
if (!result || !submission) {
|
|
103403
|
-
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
103404
|
-
}
|
|
103405
|
-
return {
|
|
103406
|
-
itemIdentifier: selection.itemIdentifier,
|
|
103407
|
-
standard: selection.standard,
|
|
103408
|
-
answered: submission.answered,
|
|
103409
|
-
score: result.score,
|
|
103410
|
-
isCorrect: result.isCorrect
|
|
103411
|
-
};
|
|
103412
|
-
});
|
|
103452
|
+
const standards = new Map(input.metadata.review.selections.map((selection) => [
|
|
103453
|
+
selection.itemIdentifier,
|
|
103454
|
+
selection.standard
|
|
103455
|
+
]));
|
|
103456
|
+
return assessmentItemOutcomes(input.metadata.itemSubmissions, (identifier) => standards.get(identifier));
|
|
103413
103457
|
}
|
|
103414
103458
|
function assessmentReviewTranscriptExposures(input) {
|
|
103415
103459
|
const exposures = [];
|
|
@@ -103963,6 +104007,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103963
104007
|
bankRevision: catalog.bank.bankRevision,
|
|
103964
104008
|
standards: [...normalized.standards],
|
|
103965
104009
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
104010
|
+
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
103966
104011
|
selections: [...selected.selections],
|
|
103967
104012
|
fulfillment: selected.fulfillment
|
|
103968
104013
|
}
|
|
@@ -104234,6 +104279,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104234
104279
|
bankRevision: source.bank.bankRevision,
|
|
104235
104280
|
standards: [...normalizedRequest.standards],
|
|
104236
104281
|
candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
|
|
104282
|
+
...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104237
104283
|
selections: [...selected.selections],
|
|
104238
104284
|
fulfillment: selected.fulfillment
|
|
104239
104285
|
}
|
|
@@ -104461,8 +104507,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104461
104507
|
}, transcript, true, db2);
|
|
104462
104508
|
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
104463
104509
|
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
104464
|
-
metadata: graded.metadata
|
|
104465
|
-
itemResults: graded.itemResults
|
|
104510
|
+
metadata: graded.metadata
|
|
104466
104511
|
}) : undefined;
|
|
104467
104512
|
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
104468
104513
|
const timestamp6 = new Date().toISOString();
|
|
@@ -104510,6 +104555,16 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104510
104555
|
};
|
|
104511
104556
|
});
|
|
104512
104557
|
if (accepted.repair) {
|
|
104558
|
+
const { metadata: metadata2 } = accepted.repair.graded;
|
|
104559
|
+
if (metadata2.purpose === "review") {
|
|
104560
|
+
reportReviewRequiredPairUnavailable(metadata2.review, metadata2.itemSubmissions.map((item) => item.itemIdentifier), (error88) => addEvent("assessment.review_required_pair_unavailable", {
|
|
104561
|
+
"app.assessment.attempt_id": params.attemptId,
|
|
104562
|
+
"app.assessment.submission_id": input.submissionId,
|
|
104563
|
+
"app.error.code": error88.code,
|
|
104564
|
+
"app.assessment.review.required_pair_failure_count": error88.failures.length,
|
|
104565
|
+
"app.assessment.review.required_pair_failures": error88.failures.map((failure) => JSON.stringify(failure))
|
|
104566
|
+
}));
|
|
104567
|
+
}
|
|
104513
104568
|
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
104514
104569
|
}
|
|
104515
104570
|
if (accepted.emission) {
|
package/dist/server.js
CHANGED
|
@@ -1127,7 +1127,7 @@ var package_default;
|
|
|
1127
1127
|
var init_package = __esm(() => {
|
|
1128
1128
|
package_default = {
|
|
1129
1129
|
name: "@playcademy/sandbox",
|
|
1130
|
-
version: "0.10.
|
|
1130
|
+
version: "0.10.1-beta.2",
|
|
1131
1131
|
description: "Local development server for Playcademy game development",
|
|
1132
1132
|
type: "module",
|
|
1133
1133
|
exports: {
|
|
@@ -9561,6 +9561,21 @@ function assessmentReviewBankShortage(fulfillment) {
|
|
|
9561
9561
|
function assessmentFlowForPurpose(purpose) {
|
|
9562
9562
|
return purpose === "review" ? "item-submit" : "attempt-submit";
|
|
9563
9563
|
}
|
|
9564
|
+
function assessmentItemOutcomes(submissions, standardForItem) {
|
|
9565
|
+
return submissions.map((submission) => {
|
|
9566
|
+
const standard = standardForItem(submission.itemIdentifier);
|
|
9567
|
+
if (!standard) {
|
|
9568
|
+
throw new Error(`Missing outcome standard for item ${submission.itemIdentifier}`);
|
|
9569
|
+
}
|
|
9570
|
+
return {
|
|
9571
|
+
itemIdentifier: submission.itemIdentifier,
|
|
9572
|
+
standard: { ...standard },
|
|
9573
|
+
answered: submission.answered,
|
|
9574
|
+
score: { ...submission.score },
|
|
9575
|
+
isCorrect: submission.isCorrect
|
|
9576
|
+
};
|
|
9577
|
+
});
|
|
9578
|
+
}
|
|
9564
9579
|
function isObject(value) {
|
|
9565
9580
|
return typeof value === "object" && value !== null;
|
|
9566
9581
|
}
|
|
@@ -10729,7 +10744,7 @@ function completeAssessmentTranscript(input) {
|
|
|
10729
10744
|
}
|
|
10730
10745
|
if (input.terminal) {
|
|
10731
10746
|
const count = itemSubmissions.length;
|
|
10732
|
-
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount;
|
|
10747
|
+
const conventionalComplete = assessmentFlowForPurpose(input.purpose) === "item-submit" ? count > 0 : count === input.itemCount && (input.purpose !== "mastery" || count > 0);
|
|
10733
10748
|
const complete = state ? state.status === "ready-to-complete" && count > 0 : conventionalComplete;
|
|
10734
10749
|
if (!complete) {
|
|
10735
10750
|
throw new AssessmentTranscriptError("A complete administered transcript is required for submission", count);
|
|
@@ -10737,12 +10752,6 @@ function completeAssessmentTranscript(input) {
|
|
|
10737
10752
|
}
|
|
10738
10753
|
return { itemSubmissions, ledger, state };
|
|
10739
10754
|
}
|
|
10740
|
-
function administeredItemIdentifiers(itemSubmissions) {
|
|
10741
|
-
return new Set(itemSubmissions.map((submission) => submission.itemIdentifier));
|
|
10742
|
-
}
|
|
10743
|
-
function itemAdministration(itemSubmissions, itemIdentifier) {
|
|
10744
|
-
return itemSubmissions.find((submission) => submission.itemIdentifier === itemIdentifier);
|
|
10745
|
-
}
|
|
10746
10755
|
function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
|
|
10747
10756
|
if (candidates.length === 0) {
|
|
10748
10757
|
return { outcome: "none" };
|
|
@@ -10992,6 +11001,32 @@ function scorePlatformAssessmentItem(item, responses) {
|
|
|
10992
11001
|
isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
|
|
10993
11002
|
};
|
|
10994
11003
|
}
|
|
11004
|
+
function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
|
|
11005
|
+
if (!review.requireBothQuestions) {
|
|
11006
|
+
return;
|
|
11007
|
+
}
|
|
11008
|
+
const administered = new Set(administeredItemIdentifiers2);
|
|
11009
|
+
const failures = [];
|
|
11010
|
+
for (const standard of review.standards) {
|
|
11011
|
+
const key = assessmentStandardRefKey(standard);
|
|
11012
|
+
const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
|
|
11013
|
+
const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
|
|
11014
|
+
if (selected.size < 2 || administeredCount === 1) {
|
|
11015
|
+
failures.push({
|
|
11016
|
+
standard,
|
|
11017
|
+
reason: selected.size < 2 ? "selection_shortage" : "not_administered",
|
|
11018
|
+
requiredCount: 2,
|
|
11019
|
+
selectedCount: selected.size,
|
|
11020
|
+
administeredCount
|
|
11021
|
+
});
|
|
11022
|
+
}
|
|
11023
|
+
}
|
|
11024
|
+
if (failures.length > 0) {
|
|
11025
|
+
try {
|
|
11026
|
+
report({ code: "REVIEW_REQUIRED_PAIR_UNAVAILABLE", failures });
|
|
11027
|
+
} catch {}
|
|
11028
|
+
}
|
|
11029
|
+
}
|
|
10995
11030
|
function reviewStandardFieldsWithinLimits(input) {
|
|
10996
11031
|
return input.framework.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength && input.identifier.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength;
|
|
10997
11032
|
}
|
|
@@ -11044,7 +11079,11 @@ function normalizeReviewRequest(request) {
|
|
|
11044
11079
|
if (standards.length === 0) {
|
|
11045
11080
|
throw new Error("At least one review standard is required");
|
|
11046
11081
|
}
|
|
11047
|
-
return {
|
|
11082
|
+
return {
|
|
11083
|
+
standards,
|
|
11084
|
+
candidateItemsPerStandard,
|
|
11085
|
+
...request.requireBothQuestions ? { requireBothQuestions: true } : {}
|
|
11086
|
+
};
|
|
11048
11087
|
}
|
|
11049
11088
|
function reviewRequestFingerprint(request, policy) {
|
|
11050
11089
|
const normalized = normalizeReviewRequest(request);
|
|
@@ -11054,6 +11093,7 @@ function reviewRequestFingerprint(request, policy) {
|
|
|
11054
11093
|
version: policy.version
|
|
11055
11094
|
},
|
|
11056
11095
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
11096
|
+
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
11057
11097
|
standardKeys: normalized.standards.map(assessmentStandardRefKey)
|
|
11058
11098
|
});
|
|
11059
11099
|
}
|
|
@@ -11569,7 +11609,7 @@ function isReviewFulfillment(value) {
|
|
|
11569
11609
|
const shortagesValid = value.shortages.every((shortage) => isRecord(shortage) && isAssessmentStandardRef(shortage.standard) && Number.isInteger(shortage.requested) && Number.isInteger(shortage.selected) && Number.isInteger(shortage.eligible));
|
|
11570
11610
|
return shortagesValid && (value.status === "complete" && value.shortages.length === 0 || value.status === "partial" && value.shortages.length > 0);
|
|
11571
11611
|
}
|
|
11572
|
-
function
|
|
11612
|
+
function isAssessmentItemOutcome(value) {
|
|
11573
11613
|
return isRecord(value) && typeof value.itemIdentifier === "string" && value.itemIdentifier.length > 0 && isAssessmentStandardRef(value.standard) && typeof value.answered === "boolean" && isAssessmentScore(value.score) && (value.isCorrect === null || typeof value.isCorrect === "boolean");
|
|
11574
11614
|
}
|
|
11575
11615
|
function isAssessmentItemSubmission(value) {
|
|
@@ -11636,7 +11676,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
|
|
|
11636
11676
|
});
|
|
11637
11677
|
}
|
|
11638
11678
|
function isReviewAttemptMetadata(value) {
|
|
11639
|
-
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11679
|
+
return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean") && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
|
|
11640
11680
|
}
|
|
11641
11681
|
function isOptionalInteger(value) {
|
|
11642
11682
|
return value === undefined || Number.isInteger(value);
|
|
@@ -11660,7 +11700,7 @@ function isCompletionMetadata(value, purpose, routedDiagnostic) {
|
|
|
11660
11700
|
if (!isRecord(value) || typeof value.testName !== "string" || value.courseName !== undefined && typeof value.courseName !== "string" || !Number.isInteger(value.correctQuestions) || value.session !== undefined && !isAssessmentSessionTiming(value.session) || value.totalQuestions !== undefined && (!Number.isInteger(value.totalQuestions) || Number(value.totalQuestions) < 0)) {
|
|
11661
11701
|
return false;
|
|
11662
11702
|
}
|
|
11663
|
-
const itemOutcomesValid = value.itemOutcomes === undefined || purpose === "review" && Array.isArray(value.itemOutcomes) && value.itemOutcomes.every(
|
|
11703
|
+
const itemOutcomesValid = value.itemOutcomes === undefined || purpose === "review" && Array.isArray(value.itemOutcomes) && value.itemOutcomes.every(isAssessmentItemOutcome);
|
|
11664
11704
|
const diagnosticTracksValid = value.diagnosticTracks === undefined || routedDiagnostic && Array.isArray(value.diagnosticTracks) && value.diagnosticTracks.every(isDiagnosticTrackResult);
|
|
11665
11705
|
return itemOutcomesValid && diagnosticTracksValid && (!routedDiagnostic || value.itemOutcomes === undefined && Array.isArray(value.diagnosticTracks) && value.diagnosticTracks.length > 0);
|
|
11666
11706
|
}
|
|
@@ -12334,6 +12374,7 @@ var init_assessment_runtime2 = __esm(() => {
|
|
|
12334
12374
|
}),
|
|
12335
12375
|
StartAssessmentBaseSchema.extend({
|
|
12336
12376
|
purpose: exports_external.literal("review"),
|
|
12377
|
+
requireBothQuestions: exports_external.boolean().optional(),
|
|
12337
12378
|
standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
|
|
12338
12379
|
candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
|
|
12339
12380
|
}),
|
|
@@ -100304,6 +100345,7 @@ function buildConventionalAssessmentSnapshot(input) {
|
|
|
100304
100345
|
purpose: metadata2.purpose,
|
|
100305
100346
|
standards: [...metadata2.review.standards],
|
|
100306
100347
|
candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
|
|
100348
|
+
...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
100307
100349
|
selections: [...metadata2.review.selections],
|
|
100308
100350
|
fulfillment: metadata2.review.fulfillment
|
|
100309
100351
|
};
|
|
@@ -100387,6 +100429,17 @@ function buildAssessmentSubmitResult(input) {
|
|
|
100387
100429
|
const itemOutcomes = input.metadata.completion?.itemOutcomes;
|
|
100388
100430
|
return itemOutcomes ? { ...base, purpose: input.metadata.purpose, itemOutcomes } : null;
|
|
100389
100431
|
}
|
|
100432
|
+
if (input.metadata.purpose === "mastery") {
|
|
100433
|
+
const { itemSubmissions, mastery, completion } = input.metadata;
|
|
100434
|
+
if (itemSubmissions.length === 0 || completion?.totalQuestions !== undefined && itemSubmissions.length !== completion.totalQuestions) {
|
|
100435
|
+
return null;
|
|
100436
|
+
}
|
|
100437
|
+
return {
|
|
100438
|
+
...base,
|
|
100439
|
+
purpose: "mastery",
|
|
100440
|
+
itemOutcomes: assessmentItemOutcomes(itemSubmissions, () => mastery.standard)
|
|
100441
|
+
};
|
|
100442
|
+
}
|
|
100390
100443
|
return { ...base, purpose: input.metadata.purpose };
|
|
100391
100444
|
}
|
|
100392
100445
|
function assessmentAwardFromResult(result, metadata2) {
|
|
@@ -102764,11 +102817,13 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
|
|
|
102764
102817
|
review: {
|
|
102765
102818
|
requestFingerprint: reviewRequestFingerprint({
|
|
102766
102819
|
standards: plan.review.standards,
|
|
102767
|
-
candidateItemsPerStandard: plan.review.candidateItemsPerStandard
|
|
102820
|
+
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102821
|
+
requireBothQuestions: plan.review.requireBothQuestions
|
|
102768
102822
|
}, DEFAULT_REVIEW_SELECTION_POLICY),
|
|
102769
102823
|
bankRevision: plan.review.bankRevision,
|
|
102770
102824
|
standards: plan.review.standards,
|
|
102771
102825
|
candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
|
|
102826
|
+
...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
102772
102827
|
selections: plan.review.selections,
|
|
102773
102828
|
fulfillment: plan.review.fulfillment
|
|
102774
102829
|
}
|
|
@@ -103393,22 +103448,11 @@ async function resumeAssessmentTranscript(input) {
|
|
|
103393
103448
|
}
|
|
103394
103449
|
}
|
|
103395
103450
|
function assessmentReviewTranscriptOutcomes(input) {
|
|
103396
|
-
const
|
|
103397
|
-
|
|
103398
|
-
|
|
103399
|
-
|
|
103400
|
-
|
|
103401
|
-
if (!result || !submission) {
|
|
103402
|
-
throw new Error(`Scoring omitted selected review item ${selection.itemIdentifier}`);
|
|
103403
|
-
}
|
|
103404
|
-
return {
|
|
103405
|
-
itemIdentifier: selection.itemIdentifier,
|
|
103406
|
-
standard: selection.standard,
|
|
103407
|
-
answered: submission.answered,
|
|
103408
|
-
score: result.score,
|
|
103409
|
-
isCorrect: result.isCorrect
|
|
103410
|
-
};
|
|
103411
|
-
});
|
|
103451
|
+
const standards = new Map(input.metadata.review.selections.map((selection) => [
|
|
103452
|
+
selection.itemIdentifier,
|
|
103453
|
+
selection.standard
|
|
103454
|
+
]));
|
|
103455
|
+
return assessmentItemOutcomes(input.metadata.itemSubmissions, (identifier) => standards.get(identifier));
|
|
103412
103456
|
}
|
|
103413
103457
|
function assessmentReviewTranscriptExposures(input) {
|
|
103414
103458
|
const exposures = [];
|
|
@@ -103962,6 +104006,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103962
104006
|
bankRevision: catalog.bank.bankRevision,
|
|
103963
104007
|
standards: [...normalized.standards],
|
|
103964
104008
|
candidateItemsPerStandard: normalized.candidateItemsPerStandard,
|
|
104009
|
+
...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
103965
104010
|
selections: [...selected.selections],
|
|
103966
104011
|
fulfillment: selected.fulfillment
|
|
103967
104012
|
}
|
|
@@ -104233,6 +104278,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104233
104278
|
bankRevision: source.bank.bankRevision,
|
|
104234
104279
|
standards: [...normalizedRequest.standards],
|
|
104235
104280
|
candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
|
|
104281
|
+
...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
|
|
104236
104282
|
selections: [...selected.selections],
|
|
104237
104283
|
fulfillment: selected.fulfillment
|
|
104238
104284
|
}
|
|
@@ -104460,8 +104506,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104460
104506
|
}, transcript, true, db2);
|
|
104461
104507
|
const diagnostic = this.isPlatformRoutedDiagnosticMetadata(graded.metadata) ? summarizeDiagnosticSubmission(graded.metadata, graded.manifest) : null;
|
|
104462
104508
|
const itemOutcomes = graded.metadata.purpose === "review" ? assessmentReviewTranscriptOutcomes({
|
|
104463
|
-
metadata: graded.metadata
|
|
104464
|
-
itemResults: graded.itemResults
|
|
104509
|
+
metadata: graded.metadata
|
|
104465
104510
|
}) : undefined;
|
|
104466
104511
|
const courseName = attempt.metadata.courseName ?? "Game Course";
|
|
104467
104512
|
const timestamp6 = new Date().toISOString();
|
|
@@ -104509,6 +104554,16 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104509
104554
|
};
|
|
104510
104555
|
});
|
|
104511
104556
|
if (accepted.repair) {
|
|
104557
|
+
const { metadata: metadata2 } = accepted.repair.graded;
|
|
104558
|
+
if (metadata2.purpose === "review") {
|
|
104559
|
+
reportReviewRequiredPairUnavailable(metadata2.review, metadata2.itemSubmissions.map((item) => item.itemIdentifier), (error88) => addEvent("assessment.review_required_pair_unavailable", {
|
|
104560
|
+
"app.assessment.attempt_id": params.attemptId,
|
|
104561
|
+
"app.assessment.submission_id": input.submissionId,
|
|
104562
|
+
"app.error.code": error88.code,
|
|
104563
|
+
"app.assessment.review.required_pair_failure_count": error88.failures.length,
|
|
104564
|
+
"app.assessment.review.required_pair_failures": error88.failures.map((failure) => JSON.stringify(failure))
|
|
104565
|
+
}));
|
|
104566
|
+
}
|
|
104512
104567
|
this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
|
|
104513
104568
|
}
|
|
104514
104569
|
if (accepted.emission) {
|