@examplary/qti 0.0.1
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/README.md +44 -0
- package/dist/ims/ims-manifest.d.ts +55 -0
- package/dist/ims/ims-manifest.js +126 -0
- package/dist/ims/ims-manifest.js.map +1 -0
- package/dist/ims/ims-package.d.ts +34 -0
- package/dist/ims/ims-package.js +48 -0
- package/dist/ims/ims-package.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/qti/qti-item.d.ts +61 -0
- package/dist/qti/qti-item.js +149 -0
- package/dist/qti/qti-item.js.map +1 -0
- package/dist/qti/qti-test.d.ts +25 -0
- package/dist/qti/qti-test.js +76 -0
- package/dist/qti/qti-test.js.map +1 -0
- package/dist/qti/types.d.ts +3 -0
- package/dist/qti/types.js +2 -0
- package/dist/qti/types.js.map +1 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# QTI utilities
|
|
2
|
+
|
|
3
|
+
This module provides utilities for creating and managing QTI 3.0 assessment packages, including generating IMS manifests and QTI test/item XML structures.
|
|
4
|
+
|
|
5
|
+
Example usage:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { QtiTest, QtiItem, ImsPackage } from "@examplary/qti";
|
|
9
|
+
|
|
10
|
+
// Build a test with one question
|
|
11
|
+
|
|
12
|
+
const test = new QtiTest({
|
|
13
|
+
identifier: "my-test",
|
|
14
|
+
title: "My Test",
|
|
15
|
+
language: "en",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const item = new QtiItem({
|
|
19
|
+
identifier: "item-1",
|
|
20
|
+
title: "Sample Question",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
item.addItemBodyFromHtml("<p>What is 2 + 2?</p>");
|
|
24
|
+
item.addResponseDeclaration({ identifier: "RESPONSE" });
|
|
25
|
+
item.addCorrectResponse("RESPONSE", ["4"]);
|
|
26
|
+
item.addResponseProcessing(ResponseProcessingTemplate.MatchCorrect);
|
|
27
|
+
|
|
28
|
+
item.addToTest(test);
|
|
29
|
+
|
|
30
|
+
// Create a package from the test
|
|
31
|
+
|
|
32
|
+
const pkg = new ImsPackage({
|
|
33
|
+
identifier: "my-package",
|
|
34
|
+
title: "My Package",
|
|
35
|
+
language: "en",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
item.addToPackage(pkg);
|
|
39
|
+
test.addToPackage(pkg);
|
|
40
|
+
|
|
41
|
+
// Get a ZIP of the package
|
|
42
|
+
|
|
43
|
+
const zip = await pkg.generateZip();
|
|
44
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export declare enum ImsManifestResourceType {
|
|
2
|
+
imsqti_test_xmlv3p0 = "imsqti_test_xmlv3p0",
|
|
3
|
+
imsqti_section_xmlv3p0 = "imsqti_section_xmlv3p0",
|
|
4
|
+
imsqti_item_xmlv3p0 = "imsqti_item_xmlv3p0",
|
|
5
|
+
imsqti_outcomes_xmlv3p0 = "imsqti_outcomes_xmlv3p0",
|
|
6
|
+
imsqti_responseprocessing_xmlv3p0 = "imsqti_responseprocessing_xmlv3p0",
|
|
7
|
+
imsqti_stimulus_xmlv3p0 = "imsqti_stimulus_xmlv3p0",
|
|
8
|
+
imsqti_fragment_xmlv3p0 = "imsqti_fragment_xmlv3p0",
|
|
9
|
+
imsqti_rptemplate_xmlv3p0 = "imsqti_rptemplate_xmlv3p0",
|
|
10
|
+
webcontent = "webcontent",
|
|
11
|
+
imslti_xmlv1p1 = "imslti_xmlv1p1",
|
|
12
|
+
imsltia_xmlv1p0 = "imsltia_xmlv1p0",
|
|
13
|
+
css2 = "css2",
|
|
14
|
+
css3 = "css3",
|
|
15
|
+
pls = "pls",
|
|
16
|
+
extension = "extension",
|
|
17
|
+
"associatedcontent/learning-application-resource" = "associatedcontent/learning-application-resource",
|
|
18
|
+
"controlfile" = "controlfile",
|
|
19
|
+
"resourcemetadata/xml" = "resourcemetadata/xml",
|
|
20
|
+
"resourceextmetadata/xml" = "resourceextmetadata/xml",
|
|
21
|
+
"qtiusagedata/xml" = "qtiusagedata/xml"
|
|
22
|
+
}
|
|
23
|
+
export type ImsManifestResource = {
|
|
24
|
+
identifier: string;
|
|
25
|
+
type: ImsManifestResourceType;
|
|
26
|
+
href?: string;
|
|
27
|
+
files: ImsManifestFile[];
|
|
28
|
+
dependencies?: ImsManifestDependency[];
|
|
29
|
+
metadata?: any;
|
|
30
|
+
};
|
|
31
|
+
export type ImsManifestDependency = {
|
|
32
|
+
identifierref: string;
|
|
33
|
+
};
|
|
34
|
+
export type ImsManifestFile = {
|
|
35
|
+
href: string;
|
|
36
|
+
};
|
|
37
|
+
export type ImsManifestOptions = {
|
|
38
|
+
identifier?: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
language?: string;
|
|
41
|
+
toolName?: string;
|
|
42
|
+
toolVersion?: string;
|
|
43
|
+
};
|
|
44
|
+
export declare class ImsManifest {
|
|
45
|
+
private resources;
|
|
46
|
+
identifier: string;
|
|
47
|
+
title: string;
|
|
48
|
+
language: string;
|
|
49
|
+
toolName: string;
|
|
50
|
+
toolVersion: string;
|
|
51
|
+
constructor(options?: ImsManifestOptions);
|
|
52
|
+
addResource(resource: ImsManifestResource): void;
|
|
53
|
+
getResources(): ImsManifestResource[];
|
|
54
|
+
buildXml(): string;
|
|
55
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { create } from "xmlbuilder2/lib/index.js";
|
|
2
|
+
export var ImsManifestResourceType;
|
|
3
|
+
(function (ImsManifestResourceType) {
|
|
4
|
+
// QTI 3 AssessmentTest
|
|
5
|
+
ImsManifestResourceType["imsqti_test_xmlv3p0"] = "imsqti_test_xmlv3p0";
|
|
6
|
+
// QTI 3 AssessmentSection
|
|
7
|
+
ImsManifestResourceType["imsqti_section_xmlv3p0"] = "imsqti_section_xmlv3p0";
|
|
8
|
+
// QTI 3 AssessmentItem
|
|
9
|
+
ImsManifestResourceType["imsqti_item_xmlv3p0"] = "imsqti_item_xmlv3p0";
|
|
10
|
+
// QTI 3 OutcomeDeclaration
|
|
11
|
+
ImsManifestResourceType["imsqti_outcomes_xmlv3p0"] = "imsqti_outcomes_xmlv3p0";
|
|
12
|
+
// QTI 3 ResponseProcessing
|
|
13
|
+
ImsManifestResourceType["imsqti_responseprocessing_xmlv3p0"] = "imsqti_responseprocessing_xmlv3p0";
|
|
14
|
+
// QTI 3 AssessmentStimulus
|
|
15
|
+
ImsManifestResourceType["imsqti_stimulus_xmlv3p0"] = "imsqti_stimulus_xmlv3p0";
|
|
16
|
+
// QTI 3 inclusion (XInclude)
|
|
17
|
+
ImsManifestResourceType["imsqti_fragment_xmlv3p0"] = "imsqti_fragment_xmlv3p0";
|
|
18
|
+
// QTI 3 Response Processing Template
|
|
19
|
+
ImsManifestResourceType["imsqti_rptemplate_xmlv3p0"] = "imsqti_rptemplate_xmlv3p0";
|
|
20
|
+
// Web Content files include any files that are widely supported for delivery over the web.
|
|
21
|
+
// These could include HTML files, images, audio, video, MS Office, PDF, Flash etc.
|
|
22
|
+
ImsManifestResourceType["webcontent"] = "webcontent";
|
|
23
|
+
// 1EdTech LTI 1.1 resource
|
|
24
|
+
ImsManifestResourceType["imslti_xmlv1p1"] = "imslti_xmlv1p1";
|
|
25
|
+
// 1EdTech LTI 1.3 resource
|
|
26
|
+
ImsManifestResourceType["imsltia_xmlv1p0"] = "imsltia_xmlv1p0";
|
|
27
|
+
// CSS 2.0 file
|
|
28
|
+
ImsManifestResourceType["css2"] = "css2";
|
|
29
|
+
// CSS 3.0 file
|
|
30
|
+
ImsManifestResourceType["css3"] = "css3";
|
|
31
|
+
// W3C Pronunciation Lexicon Specification files (for content accessibility)
|
|
32
|
+
ImsManifestResourceType["pls"] = "pls";
|
|
33
|
+
// Proprietary file (enables extension)
|
|
34
|
+
ImsManifestResourceType["extension"] = "extension";
|
|
35
|
+
// A collection of files used exclusively by an individual Learning Application Object.
|
|
36
|
+
// A Learning Application Object is a directory structure used to group together all the files (or file references)
|
|
37
|
+
// that are used to deliver a single instance of one of the following resource types: web content, web link,
|
|
38
|
+
// discussion topic, assessment or intra-package reference.
|
|
39
|
+
ImsManifestResourceType["associatedcontent/learning-application-resource"] = "associatedcontent/learning-application-resource";
|
|
40
|
+
// Schema (XSD). Allows for packaging the XSD files needed to validate the files in the package as part of the package.
|
|
41
|
+
ImsManifestResourceType["controlfile"] = "controlfile";
|
|
42
|
+
// External IMS metadata
|
|
43
|
+
ImsManifestResourceType["resourcemetadata/xml"] = "resourcemetadata/xml";
|
|
44
|
+
// External non-IMS metadata
|
|
45
|
+
ImsManifestResourceType["resourceextmetadata/xml"] = "resourceextmetadata/xml";
|
|
46
|
+
// QTI 3 Usage Data (may need to pass to a CAT engine)
|
|
47
|
+
ImsManifestResourceType["qtiusagedata/xml"] = "qtiusagedata/xml";
|
|
48
|
+
})(ImsManifestResourceType || (ImsManifestResourceType = {}));
|
|
49
|
+
export class ImsManifest {
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this.resources = new Map();
|
|
52
|
+
this.identifier = options?.identifier || "manifest-" + Date.now();
|
|
53
|
+
this.title = options?.title || "QTI 3 Package";
|
|
54
|
+
this.language = options?.language || "en";
|
|
55
|
+
this.toolName = options?.toolName || "Examplary QTI Module";
|
|
56
|
+
this.toolVersion = options?.toolVersion || "1.0.0";
|
|
57
|
+
this.resources = new Map();
|
|
58
|
+
}
|
|
59
|
+
addResource(resource) {
|
|
60
|
+
if (!resource.href && resource.files?.length > 0) {
|
|
61
|
+
resource.href = resource.files[0].href;
|
|
62
|
+
}
|
|
63
|
+
this.resources.set(resource.identifier, resource);
|
|
64
|
+
}
|
|
65
|
+
getResources() {
|
|
66
|
+
return Array.from(this.resources.values());
|
|
67
|
+
}
|
|
68
|
+
buildXml() {
|
|
69
|
+
const manifest = create({ version: "1.0", encoding: "UTF-8" }).ele("manifest", {
|
|
70
|
+
xmlns: "http://www.imsglobal.org/xsd/qti/qtiv3p0/imscp_v1p1",
|
|
71
|
+
"xmlns:lom": "http://ltsc.ieee.org/xsd/LOM",
|
|
72
|
+
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
73
|
+
"xmlns:imsqti": "http://www.imsglobal.org/xsd/imsqti_metadata_v3p0",
|
|
74
|
+
"xsi:schemaLocation": "http://ltsc.ieee.org/xsd/LOM https://purl.imsglobal.org/spec/md/v1p3/schema/xsd/imsmd_loose_v1p3p2.xsd " +
|
|
75
|
+
"http://www.imsglobal.org/xsd/qti/qtiv3p0/imscp_v1p1 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqtiv3p0_imscpv1p2_v1p0.xsd " +
|
|
76
|
+
"http://www.imsglobal.org/xsd/imsqti_metadata_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_metadatav3p0_v1p0.xsd",
|
|
77
|
+
identifier: this.identifier,
|
|
78
|
+
});
|
|
79
|
+
// Define metadata
|
|
80
|
+
const metadata = manifest.ele("metadata");
|
|
81
|
+
metadata.ele("schema").txt("QTI Package");
|
|
82
|
+
metadata.ele("schemaversion").txt("3.0.0");
|
|
83
|
+
const lom = "http://ltsc.ieee.org/xsd/LOM";
|
|
84
|
+
const lomMetadata = metadata.ele(lom, "lom", { xmlns: lom });
|
|
85
|
+
const general = lomMetadata.ele(lom, "general");
|
|
86
|
+
general.ele(lom, "title").ele(lom, "string").txt(this.title);
|
|
87
|
+
general.ele(lom, "language").txt(this.language);
|
|
88
|
+
const metaMeta = lomMetadata.ele(lom, "metaMetadata");
|
|
89
|
+
metaMeta.ele(lom, "metadataschema").txt("LOMv1.0");
|
|
90
|
+
metaMeta.ele(lom, "metadataschema").txt("QTIv3.0");
|
|
91
|
+
metaMeta.ele(lom, "language").txt(this.language);
|
|
92
|
+
const ns = "http://www.imsglobal.org/xsd/imsqti_metadata_v3p0";
|
|
93
|
+
const qtiMetadata = lomMetadata.ele(ns, "qtiMetadata", {
|
|
94
|
+
xmlns: ns,
|
|
95
|
+
});
|
|
96
|
+
qtiMetadata.ele(ns, "toolName").txt(this.toolName);
|
|
97
|
+
qtiMetadata.ele(ns, "toolVersion").txt(this.toolVersion);
|
|
98
|
+
// Empty organizations element
|
|
99
|
+
manifest.ele("organizations");
|
|
100
|
+
// Define resources
|
|
101
|
+
const resources = manifest.ele("resources");
|
|
102
|
+
for (const res of this.resources.values()) {
|
|
103
|
+
const resourceEle = resources.ele("resource", {
|
|
104
|
+
identifier: res.identifier,
|
|
105
|
+
type: res.type,
|
|
106
|
+
href: res.href,
|
|
107
|
+
});
|
|
108
|
+
if (res.metadata) {
|
|
109
|
+
const resMetadata = resourceEle.ele("metadata");
|
|
110
|
+
resMetadata.import(res.metadata);
|
|
111
|
+
}
|
|
112
|
+
for (const file of res.files) {
|
|
113
|
+
resourceEle.ele("file", { href: file.href });
|
|
114
|
+
}
|
|
115
|
+
if (res.dependencies) {
|
|
116
|
+
for (const dep of res.dependencies) {
|
|
117
|
+
resourceEle.ele("dependency", {
|
|
118
|
+
identifierref: dep.identifierref,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return manifest.end({ prettyPrint: true });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=ims-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ims-manifest.js","sourceRoot":"","sources":["../../src/ims/ims-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Zip from "jszip";
|
|
2
|
+
import { ImsManifest, ImsManifestOptions, ImsManifestResource } from "./ims-manifest";
|
|
3
|
+
export type ImsPackageFileData = Uint8Array | ArrayBuffer | string | Blob | NodeJS.ReadableStream;
|
|
4
|
+
export type ImsPackageFileFromData = {
|
|
5
|
+
filename: string;
|
|
6
|
+
data: ImsPackageFileData;
|
|
7
|
+
};
|
|
8
|
+
export type ImsPackageFileFromUrl = {
|
|
9
|
+
filename: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
export type ImsPackageFile = ImsPackageFileFromData | ImsPackageFileFromUrl;
|
|
13
|
+
export type ImsManifestResourceWithoutFiles = Omit<ImsManifestResource, "files">;
|
|
14
|
+
export declare class ImsPackage {
|
|
15
|
+
manifest: ImsManifest;
|
|
16
|
+
zip: Zip;
|
|
17
|
+
/**
|
|
18
|
+
* Create a new IMS Package.
|
|
19
|
+
*/
|
|
20
|
+
constructor(manifestOptions?: ImsManifestOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Add a resource to the IMS Package, along with its associated files.
|
|
23
|
+
*/
|
|
24
|
+
addResource(resource: ImsManifestResourceWithoutFiles, files: ImsPackageFile[]): void;
|
|
25
|
+
/**
|
|
26
|
+
* Add a file directly to the final ZIP, without adding it to the manifest.
|
|
27
|
+
* Can fetch from URL or add from data.
|
|
28
|
+
*/
|
|
29
|
+
addFileDirectly(file: ImsPackageFile): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Output the IMS Package as a ZIP file.
|
|
32
|
+
*/
|
|
33
|
+
generateZip(): Promise<ArrayBuffer>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Zip from "jszip";
|
|
2
|
+
import { ImsManifest, } from "./ims-manifest";
|
|
3
|
+
export class ImsPackage {
|
|
4
|
+
/**
|
|
5
|
+
* Create a new IMS Package.
|
|
6
|
+
*/
|
|
7
|
+
constructor(manifestOptions) {
|
|
8
|
+
this.manifest = new ImsManifest(manifestOptions);
|
|
9
|
+
this.zip = new Zip();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Add a resource to the IMS Package, along with its associated files.
|
|
13
|
+
*/
|
|
14
|
+
addResource(resource, files) {
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
this.addFileDirectly(file);
|
|
17
|
+
}
|
|
18
|
+
this.manifest.addResource({
|
|
19
|
+
...resource,
|
|
20
|
+
files: files.map((file) => ({ href: file.filename })),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Add a file directly to the final ZIP, without adding it to the manifest.
|
|
25
|
+
* Can fetch from URL or add from data.
|
|
26
|
+
*/
|
|
27
|
+
async addFileDirectly(file) {
|
|
28
|
+
if ("url" in file) {
|
|
29
|
+
const response = await fetch(file.url);
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`Failed to fetch resource from ${file.url}: ${response.statusText}`);
|
|
32
|
+
}
|
|
33
|
+
const data = await response.arrayBuffer();
|
|
34
|
+
this.zip.file(file.filename, data);
|
|
35
|
+
}
|
|
36
|
+
if ("data" in file) {
|
|
37
|
+
this.zip.file(file.filename, file.data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Output the IMS Package as a ZIP file.
|
|
42
|
+
*/
|
|
43
|
+
generateZip() {
|
|
44
|
+
this.zip.file("imsmanifest.xml", this.manifest.buildXml());
|
|
45
|
+
return this.zip.generateAsync({ type: "arraybuffer" });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=ims-package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ims-package.js","sourceRoot":"","sources":["../../src/ims/ims-package.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,OAAO,CAAC;AAExB,OAAO,EACL,WAAW,GAGZ,MAAM,gBAAgB,CAAC;AA0BxB,MAAM,OAAO,UAAU;IAIrB;;OAEG;IACH,YAAY,eAAoC;QAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,WAAW,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,WAAW,CACT,QAAyC,EACzC,KAAuB;QAEvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YACxB,GAAG,QAAQ;YACX,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe,CAAC,IAAoB;QACxC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CACb,iCAAiC,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,UAAU,EAAE,CACpE,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;YAE1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED;;OAEG;IACH,WAAW;QACT,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE3D,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;IACzD,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { QtiTest } from "./qti-test";
|
|
2
|
+
import { QtiCardinality, QtiBaseType, QtiAudience } from "./types";
|
|
3
|
+
import { ImsPackage } from "../ims/ims-package";
|
|
4
|
+
export type QtiItemOptions = {
|
|
5
|
+
identifier?: string;
|
|
6
|
+
adaptive?: boolean;
|
|
7
|
+
timeDependent?: boolean;
|
|
8
|
+
title?: string;
|
|
9
|
+
language?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
toolName?: string;
|
|
12
|
+
toolVersion?: string;
|
|
13
|
+
};
|
|
14
|
+
export type PciInteraction = {
|
|
15
|
+
responseIdentifier: string;
|
|
16
|
+
module: string;
|
|
17
|
+
customInteractionTypeIdentifier: string;
|
|
18
|
+
dataExamplarySettings?: string;
|
|
19
|
+
class?: string;
|
|
20
|
+
};
|
|
21
|
+
export type ResponseDeclaration = {
|
|
22
|
+
identifier: string;
|
|
23
|
+
cardinality?: QtiCardinality;
|
|
24
|
+
baseType?: QtiBaseType;
|
|
25
|
+
};
|
|
26
|
+
export type OutcomeDeclaration = {
|
|
27
|
+
identifier: string;
|
|
28
|
+
cardinality: QtiCardinality;
|
|
29
|
+
baseType: QtiBaseType;
|
|
30
|
+
defaultValue?: number;
|
|
31
|
+
view?: QtiAudience;
|
|
32
|
+
};
|
|
33
|
+
export declare enum ResponseProcessingTemplate {
|
|
34
|
+
MatchCorrect = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml",
|
|
35
|
+
MapResponse = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response.xml",
|
|
36
|
+
MapResponsePoint = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response_point.xml"
|
|
37
|
+
}
|
|
38
|
+
export declare class QtiItem {
|
|
39
|
+
identifier: string;
|
|
40
|
+
adaptive?: boolean;
|
|
41
|
+
timeDependent?: boolean;
|
|
42
|
+
title?: string;
|
|
43
|
+
language?: string;
|
|
44
|
+
label?: string;
|
|
45
|
+
toolName?: string;
|
|
46
|
+
toolVersion?: string;
|
|
47
|
+
private item;
|
|
48
|
+
private itemBody?;
|
|
49
|
+
private responseDeclarations;
|
|
50
|
+
constructor(options?: QtiItemOptions);
|
|
51
|
+
buildXml(): string;
|
|
52
|
+
addToPackage(pkg: ImsPackage): void;
|
|
53
|
+
addToTest(test: QtiTest): void;
|
|
54
|
+
private getOrCreateItemBody;
|
|
55
|
+
addItemBodyFromHtml(html: string): void;
|
|
56
|
+
addPciInteraction(interaction: PciInteraction, externalModules?: boolean): void;
|
|
57
|
+
addResponseDeclaration(responseDeclaration?: ResponseDeclaration): void;
|
|
58
|
+
addCorrectResponse(identifier: string, values: string[]): void;
|
|
59
|
+
addResponseProcessing(template: ResponseProcessingTemplate): void;
|
|
60
|
+
addOutcomeDeclaration(outcomeDeclaration?: OutcomeDeclaration): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { create, fragment } from "xmlbuilder2/lib/index.js";
|
|
2
|
+
import { ImsManifestResourceType } from "../ims/ims-manifest";
|
|
3
|
+
export var ResponseProcessingTemplate;
|
|
4
|
+
(function (ResponseProcessingTemplate) {
|
|
5
|
+
ResponseProcessingTemplate["MatchCorrect"] = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml";
|
|
6
|
+
ResponseProcessingTemplate["MapResponse"] = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response.xml";
|
|
7
|
+
ResponseProcessingTemplate["MapResponsePoint"] = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response_point.xml";
|
|
8
|
+
})(ResponseProcessingTemplate || (ResponseProcessingTemplate = {}));
|
|
9
|
+
export class QtiItem {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.responseDeclarations = new Map();
|
|
12
|
+
this.identifier = options?.identifier || "item-" + Date.now();
|
|
13
|
+
this.adaptive = options?.adaptive;
|
|
14
|
+
this.timeDependent = options?.timeDependent;
|
|
15
|
+
this.title = options?.title;
|
|
16
|
+
this.language = options?.language;
|
|
17
|
+
this.label = options?.label;
|
|
18
|
+
this.item = create({ version: "1.0", encoding: "UTF-8" }).ele("qti-assessment-item", {
|
|
19
|
+
xmlns: "http://www.imsglobal.org/xsd/imsqtiasi_v3p0",
|
|
20
|
+
"xmlns:m": "http://www.w3.org/1998/Math/MathML",
|
|
21
|
+
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
22
|
+
"xsi:schemaLocation": "http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd " +
|
|
23
|
+
"http://www.w3.org/1998/Math/MathML https://purl.imsglobal.org/spec/mathml/v3p0/schema/xsd/mathml3.xsd",
|
|
24
|
+
identifier: this.identifier,
|
|
25
|
+
adaptive: this.adaptive?.toString(),
|
|
26
|
+
"time-dependent": this.timeDependent?.toString(),
|
|
27
|
+
label: this.label,
|
|
28
|
+
title: this.title,
|
|
29
|
+
toolName: this.toolName || "Examplary QTI Module",
|
|
30
|
+
toolVersion: this.toolVersion || "1.0.0",
|
|
31
|
+
"xml:lang": this.language,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
buildXml() {
|
|
35
|
+
return this.item.end({ prettyPrint: true });
|
|
36
|
+
}
|
|
37
|
+
addToPackage(pkg) {
|
|
38
|
+
pkg.addResource({
|
|
39
|
+
identifier: this.identifier,
|
|
40
|
+
type: ImsManifestResourceType.imsqti_item_xmlv3p0,
|
|
41
|
+
}, [
|
|
42
|
+
{
|
|
43
|
+
filename: `item-${this.identifier}.xml`,
|
|
44
|
+
data: this.buildXml(),
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
addToTest(test) {
|
|
49
|
+
test.addItemReference(this.identifier, `items/${this.identifier}.xml`);
|
|
50
|
+
}
|
|
51
|
+
getOrCreateItemBody() {
|
|
52
|
+
if (!this.itemBody) {
|
|
53
|
+
this.itemBody = this.item.ele("qti-item-body");
|
|
54
|
+
}
|
|
55
|
+
return this.itemBody;
|
|
56
|
+
}
|
|
57
|
+
addItemBodyFromHtml(html) {
|
|
58
|
+
// TODO: support inline math -> MathML conversion
|
|
59
|
+
// https://www.imsglobal.org/spec/qti/v3p0/impl#h.z1siiqfxmyc5
|
|
60
|
+
// TODO: convert other custom web components to valid HTML5
|
|
61
|
+
// file-attachment
|
|
62
|
+
// page-clipping
|
|
63
|
+
const frag = fragment({
|
|
64
|
+
invalidCharReplacement: "�",
|
|
65
|
+
}, `<div>
|
|
66
|
+
${this.title ? `<h1>${this.title}</h1>` : ""}
|
|
67
|
+
${html}
|
|
68
|
+
</div>`);
|
|
69
|
+
this.getOrCreateItemBody().import(frag);
|
|
70
|
+
}
|
|
71
|
+
addPciInteraction(interaction, externalModules = false) {
|
|
72
|
+
const pci = this.getOrCreateItemBody().ele("qti-portable-custom-interaction", {
|
|
73
|
+
"response-identifier": interaction.responseIdentifier,
|
|
74
|
+
module: interaction.module,
|
|
75
|
+
"custom-interaction-type-identifier": interaction.customInteractionTypeIdentifier,
|
|
76
|
+
"data-examplary-settings": interaction.dataExamplarySettings,
|
|
77
|
+
class: interaction.class,
|
|
78
|
+
});
|
|
79
|
+
if (externalModules) {
|
|
80
|
+
// Use external PCI runtime and HTTP hosted module
|
|
81
|
+
const modules = pci.ele("qti-interaction-modules");
|
|
82
|
+
modules.ele("qti-interaction-module", {
|
|
83
|
+
id: "examplaryPciRuntime",
|
|
84
|
+
"primary-path": "https://unpkg.com/@examplary/pci-runtime@latest/public/dist/runtime",
|
|
85
|
+
});
|
|
86
|
+
modules.ele("qti-interaction-module", {
|
|
87
|
+
id: "single-line-text",
|
|
88
|
+
"primary-path": `https://api.examplary.ai/question-types/${interaction.module}/export/qti3-pci`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
pci.ele("qti-interaction-markup"); // empty markup
|
|
92
|
+
}
|
|
93
|
+
addResponseDeclaration(responseDeclaration = {
|
|
94
|
+
identifier: "RESPONSE",
|
|
95
|
+
cardinality: "single",
|
|
96
|
+
baseType: "string",
|
|
97
|
+
}) {
|
|
98
|
+
if (this.responseDeclarations.has(responseDeclaration.identifier)) {
|
|
99
|
+
throw new Error(`Response declaration with identifier ${responseDeclaration.identifier} already exists.`);
|
|
100
|
+
}
|
|
101
|
+
const element = this.item.ele("qti-response-declaration", {
|
|
102
|
+
identifier: responseDeclaration.identifier,
|
|
103
|
+
cardinality: responseDeclaration.cardinality || "single",
|
|
104
|
+
"base-type": responseDeclaration.baseType || "string",
|
|
105
|
+
});
|
|
106
|
+
this.responseDeclarations.set(responseDeclaration.identifier, {
|
|
107
|
+
element,
|
|
108
|
+
responseDeclaration,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
addCorrectResponse(identifier, values) {
|
|
112
|
+
const responseDecl = this.responseDeclarations.get(identifier);
|
|
113
|
+
if (!responseDecl) {
|
|
114
|
+
throw new Error(`Response declaration with identifier ${identifier} does not exist.`);
|
|
115
|
+
}
|
|
116
|
+
if (values.length > 1 &&
|
|
117
|
+
responseDecl.responseDeclaration.cardinality === "single") {
|
|
118
|
+
throw new Error(`Cannot add multiple correct responses to a single cardinality response declaration (${identifier}).`);
|
|
119
|
+
}
|
|
120
|
+
const correctResponse = responseDecl.element.ele("qti-correct-response");
|
|
121
|
+
for (const value of values) {
|
|
122
|
+
correctResponse.ele("qti-value").txt(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
addResponseProcessing(template) {
|
|
126
|
+
this.item.ele("qti-response-processing", {
|
|
127
|
+
template,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
addOutcomeDeclaration(outcomeDeclaration = {
|
|
131
|
+
identifier: "SCORE",
|
|
132
|
+
cardinality: "single",
|
|
133
|
+
baseType: "float",
|
|
134
|
+
defaultValue: 0,
|
|
135
|
+
}) {
|
|
136
|
+
const outcome = this.item.ele("qti-outcome-declaration", {
|
|
137
|
+
"base-type": outcomeDeclaration.baseType,
|
|
138
|
+
cardinality: outcomeDeclaration.cardinality,
|
|
139
|
+
identifier: outcomeDeclaration.identifier,
|
|
140
|
+
});
|
|
141
|
+
if (outcomeDeclaration.defaultValue !== undefined) {
|
|
142
|
+
outcome
|
|
143
|
+
.ele("qti-default-value")
|
|
144
|
+
.ele("qti-value")
|
|
145
|
+
.txt(outcomeDeclaration.defaultValue?.toString() ?? "0");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=qti-item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qti-item.js","sourceRoot":"","sources":["../../src/qti/qti-item.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ImsPackage } from "../ims/ims-package";
|
|
2
|
+
type QtiTestOptions = {
|
|
3
|
+
identifier?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
language?: string;
|
|
6
|
+
toolName?: string;
|
|
7
|
+
toolVersion?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare class QtiTest {
|
|
10
|
+
identifier: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
toolName?: string;
|
|
14
|
+
toolVersion?: string;
|
|
15
|
+
private test;
|
|
16
|
+
private part;
|
|
17
|
+
private section;
|
|
18
|
+
private outcomeProcessing;
|
|
19
|
+
constructor(options?: QtiTestOptions);
|
|
20
|
+
buildXml(): string;
|
|
21
|
+
addToPackage(pkg: ImsPackage): void;
|
|
22
|
+
addItemReference(itemIdentifier: string, href: string): void;
|
|
23
|
+
private addOutcomeDeclaration;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { create } from "xmlbuilder2/lib/index.js";
|
|
2
|
+
import { ImsManifestResourceType } from "../ims/ims-manifest";
|
|
3
|
+
export class QtiTest {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.identifier = options?.identifier || "test-" + Date.now();
|
|
6
|
+
this.title = options?.title;
|
|
7
|
+
this.language = options?.language;
|
|
8
|
+
this.toolName = options?.toolName || "Examplary QTI Module";
|
|
9
|
+
this.toolVersion = options?.toolVersion || "1.0.0";
|
|
10
|
+
this.test = create({ version: "1.0", encoding: "UTF-8" }).ele("qti-assessment-test", {
|
|
11
|
+
xmlns: "http://www.imsglobal.org/xsd/imsqtiasi_v3p0",
|
|
12
|
+
"xmlns:m": "http://www.w3.org/1998/Math/MathML",
|
|
13
|
+
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
14
|
+
"xsi:schemaLocation": "http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd " +
|
|
15
|
+
"http://www.w3.org/1998/Math/MathML https://purl.imsglobal.org/spec/mathml/v3p0/schema/xsd/mathml3.xsd",
|
|
16
|
+
identifier: this.identifier,
|
|
17
|
+
title: this.title,
|
|
18
|
+
toolName: this.toolName,
|
|
19
|
+
toolVersion: this.toolVersion,
|
|
20
|
+
"xml:lang": this.language,
|
|
21
|
+
});
|
|
22
|
+
this.part = this.test.ele("qti-test-part", {
|
|
23
|
+
identifier: "TEST-PART",
|
|
24
|
+
"navigation-mode": "linear",
|
|
25
|
+
"submission-mode": "simultaneous",
|
|
26
|
+
});
|
|
27
|
+
this.section = this.part.ele("qti-assessment-section", {
|
|
28
|
+
identifier: "TEST-SECTION",
|
|
29
|
+
title: "Section 1",
|
|
30
|
+
visible: "true",
|
|
31
|
+
});
|
|
32
|
+
this.outcomeProcessing = this.test.ele("qti-outcome-processing");
|
|
33
|
+
this.addOutcomeDeclaration("SCORE");
|
|
34
|
+
this.addOutcomeDeclaration("MAX_SCORE");
|
|
35
|
+
}
|
|
36
|
+
buildXml() {
|
|
37
|
+
return this.test.end({ prettyPrint: true });
|
|
38
|
+
}
|
|
39
|
+
addToPackage(pkg) {
|
|
40
|
+
pkg.addResource({
|
|
41
|
+
identifier: this.identifier,
|
|
42
|
+
type: ImsManifestResourceType.imsqti_test_xmlv3p0,
|
|
43
|
+
}, [
|
|
44
|
+
{
|
|
45
|
+
filename: `test-${this.identifier}.xml`,
|
|
46
|
+
data: this.buildXml(),
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
addItemReference(itemIdentifier, href) {
|
|
51
|
+
this.section.ele("qti-assessment-item-ref", {
|
|
52
|
+
identifier: itemIdentifier,
|
|
53
|
+
href: href,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
addOutcomeDeclaration(identifier) {
|
|
57
|
+
this.test
|
|
58
|
+
.ele("qti-outcome-declaration", {
|
|
59
|
+
identifier,
|
|
60
|
+
cardinality: "single",
|
|
61
|
+
baseType: "float",
|
|
62
|
+
})
|
|
63
|
+
.ele("qti-default-value")
|
|
64
|
+
.ele("qti-value")
|
|
65
|
+
.txt("0");
|
|
66
|
+
this.outcomeProcessing
|
|
67
|
+
.ele("qti-set-outcome-value", {
|
|
68
|
+
identifier,
|
|
69
|
+
})
|
|
70
|
+
.ele("qti-sum")
|
|
71
|
+
.ele("qti-test-variables", {
|
|
72
|
+
"variable-identifier": identifier,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=qti-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qti-test.js","sourceRoot":"","sources":["../../src/qti/qti-test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type QtiCardinality = "single" | "multiple" | "ordered" | "record";
|
|
2
|
+
export type QtiBaseType = "boolean" | "directedPair" | "duration" | "file" | "float" | "identifier" | "integer" | "pair" | "point" | "string" | "uri";
|
|
3
|
+
export type QtiAudience = "author" | "candidate" | "proctor" | "scorer" | "testConstructor" | "tutor";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/qti/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@examplary/qti",
|
|
3
|
+
"description": "Utilities to generate QTI 3.0 assessment packages.",
|
|
4
|
+
"packageManager": "yarn@4.5.3",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "yarn build --watch",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"release": "semantic-release -e semantic-release-monorepo",
|
|
15
|
+
"build": "tsc ./src/index.ts --outDir dist --skipLibCheck --declaration --esModuleInterop --moduleResolution bundler --module es2020 -target es2020 --sourceMap"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.9.3",
|
|
19
|
+
"vitest": "^4.0.16"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://developers.examplary.ai/",
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "Examplary AI",
|
|
24
|
+
"email": "hi@examplary.ai"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"jszip": "^3.10.1",
|
|
28
|
+
"xmlbuilder2": "^4.0.3"
|
|
29
|
+
}
|
|
30
|
+
}
|