@conform-ed/qti-react 0.0.18 → 0.0.19
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/headless.d.ts +17 -6
- package/dist/headless.js +1743 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +18 -0
- package/dist/item-score.d.ts +17 -0
- package/package.json +4 -4
- package/src/headless.ts +38 -6
- package/src/index.ts +2 -0
- package/src/item-score.ts +40 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const qtiReactPackageName = "@conform-ed/qti-react";
|
|
2
2
|
export { v0ContentModel, v0InteractionKinds, isAllowedFlowElement, isInteractionKind, sanitizeAttributes, type ContentModel, type V0InteractionKind, } from "./content-model";
|
|
3
3
|
export { foldString, mapResponse, matchCorrect, mapResponsePoint, scoreResponse } from "./response-processing";
|
|
4
|
+
export { effectiveItemScore, type EffectiveItemScore } from "./item-score";
|
|
4
5
|
export { assessmentItemViewFromNormalized, assessmentTestViewFromNormalized, stimulusContentFromNormalized, } from "./normalized-item";
|
|
5
6
|
export { referenceInteractionKinds, reportItemCapability, type ItemCapabilityOptions } from "./item-capability";
|
|
6
7
|
export { formatPoint, parseCoords, parsePoint, pointInShape, type Point, type QtiShape } from "./graphic";
|
package/dist/index.js
CHANGED
|
@@ -387,6 +387,23 @@ function scoreResponse(declaration, response, normalize) {
|
|
|
387
387
|
correct
|
|
388
388
|
};
|
|
389
389
|
}
|
|
390
|
+
// src/item-score.ts
|
|
391
|
+
function numericOutcome(value) {
|
|
392
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
393
|
+
}
|
|
394
|
+
function effectiveItemScore(scores, outcomes) {
|
|
395
|
+
const scoreOutcome = numericOutcome(outcomes["SCORE"]);
|
|
396
|
+
const maxOutcome = numericOutcome(outcomes["MAXSCORE"]);
|
|
397
|
+
const summedMax = scores.reduce((total, score) => total + score.maxScore, 0);
|
|
398
|
+
if (scoreOutcome !== null) {
|
|
399
|
+
return { raw: scoreOutcome, max: maxOutcome ?? summedMax, fromOutcomes: true };
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
raw: scores.reduce((total, score) => total + score.score, 0),
|
|
403
|
+
max: maxOutcome ?? summedMax,
|
|
404
|
+
fromOutcomes: false
|
|
405
|
+
};
|
|
406
|
+
}
|
|
390
407
|
// src/normalized-item.ts
|
|
391
408
|
function isRecord(value) {
|
|
392
409
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -6836,6 +6853,7 @@ export {
|
|
|
6836
6853
|
executeTemplateProcessing,
|
|
6837
6854
|
executeResponseProcessing,
|
|
6838
6855
|
endAttemptInteraction,
|
|
6856
|
+
effectiveItemScore,
|
|
6839
6857
|
drawingInteraction,
|
|
6840
6858
|
defineInteraction,
|
|
6841
6859
|
createTestSessionStore,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ScoreResult } from "./types";
|
|
2
|
+
export interface EffectiveItemScore {
|
|
3
|
+
readonly raw: number;
|
|
4
|
+
readonly max: number;
|
|
5
|
+
/** True when SCORE came from the RP outcomes of record rather than per-variable scoring. */
|
|
6
|
+
readonly fromOutcomes: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The item score of record (QTI): a numeric SCORE outcome from response processing is
|
|
10
|
+
* authoritative — PCI/RP-scored items (e.g. math-entry) have no per-variable
|
|
11
|
+
* correctResponse basis, so their standard scores read 0. Summed per-variable standard
|
|
12
|
+
* scoring is the fallback for items without RP. MAXSCORE follows the same precedence.
|
|
13
|
+
*
|
|
14
|
+
* Pure and framework-light: client and server (authoritative finalize) share it so the
|
|
15
|
+
* grade of record is derived identically on both sides.
|
|
16
|
+
*/
|
|
17
|
+
export declare function effectiveItemScore(scores: readonly ScoreResult[], outcomes: Readonly<Record<string, unknown>>): EffectiveItemScore;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conform-ed/qti-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"dist"
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"xspattern": "^3.1.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@conform-ed/contracts": "0.0.
|
|
34
|
-
"@conform-ed/qti-xml": "0.0.
|
|
33
|
+
"@conform-ed/contracts": "0.0.19",
|
|
34
|
+
"@conform-ed/qti-xml": "0.0.19",
|
|
35
35
|
"@types/react": "^19.2.17",
|
|
36
36
|
"@types/react-dom": "^19",
|
|
37
|
-
"happy-dom": "^20.10.
|
|
37
|
+
"happy-dom": "^20.10.3",
|
|
38
38
|
"react": "^19.2.7",
|
|
39
39
|
"react-dom": "^19"
|
|
40
40
|
},
|
package/src/headless.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Headless (React-free) surface of @conform-ed/qti-react: the normalize → view adapters
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Headless (React-free) surface of @conform-ed/qti-react: the normalize → view adapters,
|
|
3
|
+
* the capability gate, and the **scoring engine** (Response Processing interpreter, the
|
|
4
|
+
* standard per-response scoring templates, the item-score aggregator, and the test-level
|
|
5
|
+
* outcome-processing controller). Importable on a server (e.g. a QTI ingest pipeline or an
|
|
6
|
+
* authoritative grade finalize) without pulling React. Exposed at
|
|
7
|
+
* `@conform-ed/qti-react/headless`; everything here is also re-exported from the package
|
|
8
|
+
* root for React consumers.
|
|
6
9
|
*
|
|
7
|
-
* Keep this entry free of React-coupled imports —
|
|
8
|
-
* ./item-capability
|
|
10
|
+
* Keep this entry free of React-coupled imports — every module re-exported here
|
|
11
|
+
* (./normalized-item, ./item-capability, ./response-processing, ./rp, ./item-score,
|
|
12
|
+
* ./store, ./test, ./response-validity) is verified framework-light.
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
15
|
export {
|
|
@@ -14,6 +18,33 @@ export {
|
|
|
14
18
|
stimulusContentFromNormalized,
|
|
15
19
|
} from "./normalized-item";
|
|
16
20
|
export { referenceInteractionKinds, reportItemCapability, type ItemCapabilityOptions } from "./item-capability";
|
|
21
|
+
|
|
22
|
+
// The scoring engine, headless. Running these server-side re-derives the grade of record
|
|
23
|
+
// from the learner's raw responses + the (server-held) answer keys — see emergent ADR-0019.
|
|
24
|
+
export { foldString, mapResponse, matchCorrect, mapResponsePoint, scoreResponse } from "./response-processing";
|
|
25
|
+
export { effectiveItemScore, type EffectiveItemScore } from "./item-score";
|
|
26
|
+
export {
|
|
27
|
+
applyCorrectResponseOverrides,
|
|
28
|
+
collectRpIssues,
|
|
29
|
+
collectTemplateIssues,
|
|
30
|
+
executeResponseProcessing,
|
|
31
|
+
executeTemplateProcessing,
|
|
32
|
+
mulberry32,
|
|
33
|
+
resolveTemplate,
|
|
34
|
+
} from "./rp";
|
|
35
|
+
export type {
|
|
36
|
+
CustomOperatorImplementation,
|
|
37
|
+
OutcomeDeclarationView,
|
|
38
|
+
OutcomeValue,
|
|
39
|
+
ResponseNormalization,
|
|
40
|
+
ResponseProcessingContext,
|
|
41
|
+
ResponseProcessingResult,
|
|
42
|
+
ResponseProcessingView,
|
|
43
|
+
TemplateDeclarationView,
|
|
44
|
+
} from "./rp";
|
|
45
|
+
export { createAttemptStore, type AttemptSnapshot, type AttemptStore, type AttemptStoreOptions } from "./store";
|
|
46
|
+
export { createTestController, type TestController, type TestSessionState } from "./test";
|
|
47
|
+
|
|
17
48
|
export type { CapabilityIssue, CapabilityIssueType, CapabilityReport } from "./capability";
|
|
18
49
|
export type {
|
|
19
50
|
AssessmentItemView,
|
|
@@ -24,3 +55,4 @@ export type {
|
|
|
24
55
|
XmlContentNode,
|
|
25
56
|
} from "./runtime";
|
|
26
57
|
export type { AssessmentTestView } from "./test";
|
|
58
|
+
export type { Cardinality, ResponseDeclarationView, ResponseValue, ScoreResult } from "./types";
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ export {
|
|
|
14
14
|
|
|
15
15
|
export { foldString, mapResponse, matchCorrect, mapResponsePoint, scoreResponse } from "./response-processing";
|
|
16
16
|
|
|
17
|
+
export { effectiveItemScore, type EffectiveItemScore } from "./item-score";
|
|
18
|
+
|
|
17
19
|
export {
|
|
18
20
|
assessmentItemViewFromNormalized,
|
|
19
21
|
assessmentTestViewFromNormalized,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ScoreResult } from "./types";
|
|
2
|
+
|
|
3
|
+
export interface EffectiveItemScore {
|
|
4
|
+
readonly raw: number;
|
|
5
|
+
readonly max: number;
|
|
6
|
+
/** True when SCORE came from the RP outcomes of record rather than per-variable scoring. */
|
|
7
|
+
readonly fromOutcomes: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function numericOutcome(value: unknown): number | null {
|
|
11
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The item score of record (QTI): a numeric SCORE outcome from response processing is
|
|
16
|
+
* authoritative — PCI/RP-scored items (e.g. math-entry) have no per-variable
|
|
17
|
+
* correctResponse basis, so their standard scores read 0. Summed per-variable standard
|
|
18
|
+
* scoring is the fallback for items without RP. MAXSCORE follows the same precedence.
|
|
19
|
+
*
|
|
20
|
+
* Pure and framework-light: client and server (authoritative finalize) share it so the
|
|
21
|
+
* grade of record is derived identically on both sides.
|
|
22
|
+
*/
|
|
23
|
+
export function effectiveItemScore(
|
|
24
|
+
scores: readonly ScoreResult[],
|
|
25
|
+
outcomes: Readonly<Record<string, unknown>>,
|
|
26
|
+
): EffectiveItemScore {
|
|
27
|
+
const scoreOutcome = numericOutcome(outcomes["SCORE"]);
|
|
28
|
+
const maxOutcome = numericOutcome(outcomes["MAXSCORE"]);
|
|
29
|
+
const summedMax = scores.reduce((total, score) => total + score.maxScore, 0);
|
|
30
|
+
|
|
31
|
+
if (scoreOutcome !== null) {
|
|
32
|
+
return { raw: scoreOutcome, max: maxOutcome ?? summedMax, fromOutcomes: true };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
raw: scores.reduce((total, score) => total + score.score, 0),
|
|
37
|
+
max: maxOutcome ?? summedMax,
|
|
38
|
+
fromOutcomes: false,
|
|
39
|
+
};
|
|
40
|
+
}
|