@telebort/question-banks 2.3.0 → 2.4.0
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/{assessment-DnV2240e.d.cts → assessment-BhkjdWy1.d.cts} +38 -5
- package/dist/{assessment-DnV2240e.d.ts → assessment-BhkjdWy1.d.ts} +38 -5
- package/dist/cli/index.cjs +238 -79
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +238 -79
- package/dist/cli/index.js.map +1 -1
- package/dist/{course-DyNRNTFp.d.cts → course-DD94UGiI.d.cts} +178 -0
- package/dist/{course-DyNRNTFp.d.ts → course-DD94UGiI.d.ts} +178 -0
- package/dist/{index-BPJQRPuK.d.ts → index-B1r6pY_k.d.ts} +1 -1
- package/dist/{index-BBoylVZx.d.cts → index-jb328lpu.d.cts} +1 -1
- package/dist/index.cjs +221 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -6
- package/dist/index.d.ts +33 -6
- package/dist/index.js +220 -67
- package/dist/index.js.map +1 -1
- package/dist/premium/index.d.cts +3 -3
- package/dist/premium/index.d.ts +3 -3
- package/dist/schemas/index.cjs +85 -65
- package/dist/schemas/index.cjs.map +1 -1
- package/dist/schemas/index.d.cts +2 -2
- package/dist/schemas/index.d.ts +2 -2
- package/dist/schemas/index.js +85 -66
- package/dist/schemas/index.js.map +1 -1
- package/dist/validation/index.cjs +84 -65
- package/dist/validation/index.cjs.map +1 -1
- package/dist/validation/index.d.cts +1 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +84 -65
- package/dist/validation/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Defines a summative assessment (quiz) that covers multiple lessons.
|
|
5
|
+
* Stored in CDN course exports and used by the assessment module to build quizzes.
|
|
6
|
+
*/
|
|
7
|
+
declare const AssessmentBlueprintSchema: z.ZodObject<{
|
|
8
|
+
assessmentId: z.ZodString;
|
|
9
|
+
title: z.ZodString;
|
|
10
|
+
description: z.ZodString;
|
|
11
|
+
lessonRange: z.ZodEffects<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, [number, number], [number, number]>;
|
|
12
|
+
questionCount: z.ZodNumber;
|
|
13
|
+
passingScore: z.ZodNumber;
|
|
14
|
+
timeLimitMinutes: z.ZodNullable<z.ZodNumber>;
|
|
15
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
assessmentId: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
lessonRange: [number, number];
|
|
21
|
+
questionCount: number;
|
|
22
|
+
passingScore: number;
|
|
23
|
+
timeLimitMinutes: number | null;
|
|
24
|
+
tags?: string[] | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
assessmentId: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
lessonRange: [number, number];
|
|
30
|
+
questionCount: number;
|
|
31
|
+
passingScore: number;
|
|
32
|
+
timeLimitMinutes: number | null;
|
|
33
|
+
tags?: string[] | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
type AssessmentBlueprint = z.infer<typeof AssessmentBlueprintSchema>;
|
|
3
36
|
/**
|
|
4
37
|
* A single user response to a question
|
|
5
38
|
*/
|
|
@@ -234,8 +267,8 @@ declare const GradedAssessmentSchema: z.ZodObject<{
|
|
|
234
267
|
}, "strip", z.ZodTypeAny, {
|
|
235
268
|
courseId: string;
|
|
236
269
|
totalQuestions: number;
|
|
237
|
-
submittedAt: string;
|
|
238
270
|
assessmentId: string;
|
|
271
|
+
submittedAt: string;
|
|
239
272
|
responses: {
|
|
240
273
|
questionId: string;
|
|
241
274
|
isCorrect: boolean;
|
|
@@ -269,8 +302,8 @@ declare const GradedAssessmentSchema: z.ZodObject<{
|
|
|
269
302
|
}, {
|
|
270
303
|
courseId: string;
|
|
271
304
|
totalQuestions: number;
|
|
272
|
-
submittedAt: string;
|
|
273
305
|
assessmentId: string;
|
|
306
|
+
submittedAt: string;
|
|
274
307
|
responses: {
|
|
275
308
|
questionId: string;
|
|
276
309
|
isCorrect: boolean;
|
|
@@ -327,8 +360,8 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
327
360
|
answeredCount: z.ZodNumber;
|
|
328
361
|
}, "strip", z.ZodTypeAny, {
|
|
329
362
|
valid: boolean;
|
|
330
|
-
isComplete: boolean;
|
|
331
363
|
questionCount: number;
|
|
364
|
+
isComplete: boolean;
|
|
332
365
|
answeredCount: number;
|
|
333
366
|
errors?: {
|
|
334
367
|
path: (string | number)[];
|
|
@@ -337,8 +370,8 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
337
370
|
}[] | undefined;
|
|
338
371
|
}, {
|
|
339
372
|
valid: boolean;
|
|
340
|
-
isComplete: boolean;
|
|
341
373
|
questionCount: number;
|
|
374
|
+
isComplete: boolean;
|
|
342
375
|
answeredCount: number;
|
|
343
376
|
errors?: {
|
|
344
377
|
path: (string | number)[];
|
|
@@ -348,4 +381,4 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
348
381
|
}>;
|
|
349
382
|
type ValidationResult = z.infer<typeof ValidationResultSchema>;
|
|
350
383
|
|
|
351
|
-
export { type AssessmentSubmission as A, type GradedAssessment as G, MisconceptionReportSchema as M,
|
|
384
|
+
export { type AssessmentSubmission as A, type GradedAssessment as G, MisconceptionReportSchema as M, type UserResponse as U, type ValidationResult as V, type AssessmentBlueprint as a, AssessmentBlueprintSchema as b, UserResponseSchema as c, AssessmentSubmissionSchema as d, GradedResponseSchema as e, GradedAssessmentSchema as f, ValidationResultSchema as g, type GradedResponse as h, type MisconceptionReport as i };
|
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Defines a summative assessment (quiz) that covers multiple lessons.
|
|
5
|
+
* Stored in CDN course exports and used by the assessment module to build quizzes.
|
|
6
|
+
*/
|
|
7
|
+
declare const AssessmentBlueprintSchema: z.ZodObject<{
|
|
8
|
+
assessmentId: z.ZodString;
|
|
9
|
+
title: z.ZodString;
|
|
10
|
+
description: z.ZodString;
|
|
11
|
+
lessonRange: z.ZodEffects<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, [number, number], [number, number]>;
|
|
12
|
+
questionCount: z.ZodNumber;
|
|
13
|
+
passingScore: z.ZodNumber;
|
|
14
|
+
timeLimitMinutes: z.ZodNullable<z.ZodNumber>;
|
|
15
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
assessmentId: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
lessonRange: [number, number];
|
|
21
|
+
questionCount: number;
|
|
22
|
+
passingScore: number;
|
|
23
|
+
timeLimitMinutes: number | null;
|
|
24
|
+
tags?: string[] | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
assessmentId: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
lessonRange: [number, number];
|
|
30
|
+
questionCount: number;
|
|
31
|
+
passingScore: number;
|
|
32
|
+
timeLimitMinutes: number | null;
|
|
33
|
+
tags?: string[] | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
type AssessmentBlueprint = z.infer<typeof AssessmentBlueprintSchema>;
|
|
3
36
|
/**
|
|
4
37
|
* A single user response to a question
|
|
5
38
|
*/
|
|
@@ -234,8 +267,8 @@ declare const GradedAssessmentSchema: z.ZodObject<{
|
|
|
234
267
|
}, "strip", z.ZodTypeAny, {
|
|
235
268
|
courseId: string;
|
|
236
269
|
totalQuestions: number;
|
|
237
|
-
submittedAt: string;
|
|
238
270
|
assessmentId: string;
|
|
271
|
+
submittedAt: string;
|
|
239
272
|
responses: {
|
|
240
273
|
questionId: string;
|
|
241
274
|
isCorrect: boolean;
|
|
@@ -269,8 +302,8 @@ declare const GradedAssessmentSchema: z.ZodObject<{
|
|
|
269
302
|
}, {
|
|
270
303
|
courseId: string;
|
|
271
304
|
totalQuestions: number;
|
|
272
|
-
submittedAt: string;
|
|
273
305
|
assessmentId: string;
|
|
306
|
+
submittedAt: string;
|
|
274
307
|
responses: {
|
|
275
308
|
questionId: string;
|
|
276
309
|
isCorrect: boolean;
|
|
@@ -327,8 +360,8 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
327
360
|
answeredCount: z.ZodNumber;
|
|
328
361
|
}, "strip", z.ZodTypeAny, {
|
|
329
362
|
valid: boolean;
|
|
330
|
-
isComplete: boolean;
|
|
331
363
|
questionCount: number;
|
|
364
|
+
isComplete: boolean;
|
|
332
365
|
answeredCount: number;
|
|
333
366
|
errors?: {
|
|
334
367
|
path: (string | number)[];
|
|
@@ -337,8 +370,8 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
337
370
|
}[] | undefined;
|
|
338
371
|
}, {
|
|
339
372
|
valid: boolean;
|
|
340
|
-
isComplete: boolean;
|
|
341
373
|
questionCount: number;
|
|
374
|
+
isComplete: boolean;
|
|
342
375
|
answeredCount: number;
|
|
343
376
|
errors?: {
|
|
344
377
|
path: (string | number)[];
|
|
@@ -348,4 +381,4 @@ declare const ValidationResultSchema: z.ZodObject<{
|
|
|
348
381
|
}>;
|
|
349
382
|
type ValidationResult = z.infer<typeof ValidationResultSchema>;
|
|
350
383
|
|
|
351
|
-
export { type AssessmentSubmission as A, type GradedAssessment as G, MisconceptionReportSchema as M,
|
|
384
|
+
export { type AssessmentSubmission as A, type GradedAssessment as G, MisconceptionReportSchema as M, type UserResponse as U, type ValidationResult as V, type AssessmentBlueprint as a, AssessmentBlueprintSchema as b, UserResponseSchema as c, AssessmentSubmissionSchema as d, GradedResponseSchema as e, GradedAssessmentSchema as f, ValidationResultSchema as g, type GradedResponse as h, type MisconceptionReport as i };
|
package/dist/cli/index.cjs
CHANGED
|
@@ -81,6 +81,10 @@ var QuestionMetadataSchema = zod.z.object({
|
|
|
81
81
|
// seconds
|
|
82
82
|
bloomsTaxonomy: BloomsTaxonomySchema,
|
|
83
83
|
tags: zod.z.array(zod.z.string()),
|
|
84
|
+
conceptTags: zod.z.array(zod.z.string()).optional(),
|
|
85
|
+
// mastery-relevant concept tags
|
|
86
|
+
metaTags: zod.z.array(zod.z.string()).optional(),
|
|
87
|
+
// Bloom's types, course codes, internal
|
|
84
88
|
source: zod.z.string().default("exit-ticket"),
|
|
85
89
|
version: zod.z.string().default("1.1"),
|
|
86
90
|
createdDate: zod.z.string().optional(),
|
|
@@ -123,70 +127,17 @@ QuestionSchema.omit({
|
|
|
123
127
|
})
|
|
124
128
|
).length(4)
|
|
125
129
|
});
|
|
126
|
-
var
|
|
127
|
-
|
|
128
|
-
"
|
|
129
|
-
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"mobile_development",
|
|
138
|
-
"mobile",
|
|
139
|
-
// Block-based Programming
|
|
140
|
-
"block_based",
|
|
141
|
-
"python_programming",
|
|
142
|
-
"design",
|
|
143
|
-
"artificial_intelligence",
|
|
144
|
-
// Block-based AI courses (BBAI)
|
|
145
|
-
// Computer Science
|
|
146
|
-
"cs_foundations",
|
|
147
|
-
// CS1-CS4 courses
|
|
148
|
-
// Foundation
|
|
149
|
-
"foundation",
|
|
150
|
-
"creative_computing",
|
|
151
|
-
// Utility / Tools
|
|
152
|
-
"tools"
|
|
153
|
-
// IDE Setup, Git, Vibe Coding
|
|
154
|
-
]);
|
|
155
|
-
var CourseTierSchema = zod.z.enum([
|
|
156
|
-
"beginner",
|
|
157
|
-
// Block-based courses (BBW, BBP, BBD, BBAI)
|
|
158
|
-
"foundation",
|
|
159
|
-
"intermediate",
|
|
160
|
-
"advanced"
|
|
161
|
-
]);
|
|
162
|
-
var LessonSchema = zod.z.object({
|
|
163
|
-
lessonId: zod.z.string(),
|
|
164
|
-
// e.g., "ai-1-lesson-1"
|
|
165
|
-
lessonNumber: zod.z.number().int().positive(),
|
|
166
|
-
lessonTitle: zod.z.string(),
|
|
167
|
-
lessonSlug: zod.z.string().optional(),
|
|
168
|
-
totalQuestions: zod.z.number().int().positive().default(5),
|
|
169
|
-
questions: zod.z.array(QuestionSchema)
|
|
170
|
-
});
|
|
171
|
-
var CourseSchema = zod.z.object({
|
|
172
|
-
courseId: zod.z.string(),
|
|
173
|
-
// e.g., "ai-1"
|
|
174
|
-
courseName: zod.z.string(),
|
|
175
|
-
// e.g., "AI-1 Data Analysis and Data Science"
|
|
176
|
-
courseCode: zod.z.string(),
|
|
177
|
-
// e.g., "AI1"
|
|
178
|
-
domain: CourseDomainSchema,
|
|
179
|
-
tier: CourseTierSchema,
|
|
180
|
-
difficulty: zod.z.number().int().min(1).max(5),
|
|
181
|
-
totalLessons: zod.z.number().int().positive(),
|
|
182
|
-
totalQuestions: zod.z.number().int().positive(),
|
|
183
|
-
sourceFile: zod.z.string().optional(),
|
|
184
|
-
lessons: zod.z.array(LessonSchema)
|
|
185
|
-
});
|
|
186
|
-
CourseSchema.omit({ lessons: true }).extend({
|
|
187
|
-
lessons: zod.z.array(
|
|
188
|
-
LessonSchema.omit({ questions: true })
|
|
189
|
-
).optional()
|
|
130
|
+
var AssessmentBlueprintSchema = zod.z.object({
|
|
131
|
+
assessmentId: zod.z.string(),
|
|
132
|
+
// e.g., "cs1-quiz-1"
|
|
133
|
+
title: zod.z.string(),
|
|
134
|
+
// e.g., "Quiz 1"
|
|
135
|
+
description: zod.z.string(),
|
|
136
|
+
lessonRange: zod.z.tuple([zod.z.number().int().positive(), zod.z.number().int().positive()]).refine(([start2, end2]) => start2 <= end2, { message: "lessonRange start must be <= end" }),
|
|
137
|
+
questionCount: zod.z.number().int().positive(),
|
|
138
|
+
passingScore: zod.z.number().min(0).max(100),
|
|
139
|
+
timeLimitMinutes: zod.z.number().int().positive().nullable(),
|
|
140
|
+
tags: zod.z.array(zod.z.string()).optional()
|
|
190
141
|
});
|
|
191
142
|
var UserResponseSchema = zod.z.object({
|
|
192
143
|
questionId: zod.z.string(),
|
|
@@ -227,7 +178,7 @@ var MisconceptionReportSchema = zod.z.object({
|
|
|
227
178
|
description: zod.z.string().optional()
|
|
228
179
|
});
|
|
229
180
|
zod.z.object({
|
|
230
|
-
assessmentId: zod.z.string()
|
|
181
|
+
assessmentId: zod.z.string(),
|
|
231
182
|
userId: zod.z.string().optional(),
|
|
232
183
|
courseId: zod.z.string(),
|
|
233
184
|
lessonId: zod.z.string().optional(),
|
|
@@ -263,6 +214,74 @@ zod.z.object({
|
|
|
263
214
|
questionCount: zod.z.number().int(),
|
|
264
215
|
answeredCount: zod.z.number().int()
|
|
265
216
|
});
|
|
217
|
+
|
|
218
|
+
// src/schemas/course.ts
|
|
219
|
+
var CourseDomainSchema = zod.z.enum([
|
|
220
|
+
// AI & Data Science
|
|
221
|
+
"ai_data_science",
|
|
222
|
+
"ai_ml_cv",
|
|
223
|
+
"ai_generative",
|
|
224
|
+
"ai_advanced",
|
|
225
|
+
"ai",
|
|
226
|
+
// Prompt Engineering (general AI)
|
|
227
|
+
// Web Development
|
|
228
|
+
"web_development",
|
|
229
|
+
// Mobile Development
|
|
230
|
+
"mobile_development",
|
|
231
|
+
"mobile",
|
|
232
|
+
// Block-based Programming
|
|
233
|
+
"block_based",
|
|
234
|
+
"python_programming",
|
|
235
|
+
"design",
|
|
236
|
+
"artificial_intelligence",
|
|
237
|
+
// Block-based AI courses (BBAI)
|
|
238
|
+
// Computer Science
|
|
239
|
+
"cs_foundations",
|
|
240
|
+
// CS1-CS4 courses
|
|
241
|
+
// Foundation
|
|
242
|
+
"foundation",
|
|
243
|
+
"creative_computing",
|
|
244
|
+
// Utility / Tools
|
|
245
|
+
"tools"
|
|
246
|
+
// IDE Setup, Git, Vibe Coding
|
|
247
|
+
]);
|
|
248
|
+
var CourseTierSchema = zod.z.enum([
|
|
249
|
+
"beginner",
|
|
250
|
+
// Block-based courses (BBW, BBP, BBD, BBAI)
|
|
251
|
+
"foundation",
|
|
252
|
+
"intermediate",
|
|
253
|
+
"advanced"
|
|
254
|
+
]);
|
|
255
|
+
var LessonSchema = zod.z.object({
|
|
256
|
+
lessonId: zod.z.string(),
|
|
257
|
+
// e.g., "ai-1-lesson-1"
|
|
258
|
+
lessonNumber: zod.z.number().int().positive(),
|
|
259
|
+
lessonTitle: zod.z.string(),
|
|
260
|
+
lessonSlug: zod.z.string().optional(),
|
|
261
|
+
totalQuestions: zod.z.number().int().positive().default(5),
|
|
262
|
+
questions: zod.z.array(QuestionSchema)
|
|
263
|
+
});
|
|
264
|
+
var CourseSchema = zod.z.object({
|
|
265
|
+
courseId: zod.z.string(),
|
|
266
|
+
// e.g., "ai-1"
|
|
267
|
+
courseName: zod.z.string(),
|
|
268
|
+
// e.g., "AI-1 Data Analysis and Data Science"
|
|
269
|
+
courseCode: zod.z.string(),
|
|
270
|
+
// e.g., "AI1"
|
|
271
|
+
domain: CourseDomainSchema,
|
|
272
|
+
tier: CourseTierSchema,
|
|
273
|
+
difficulty: zod.z.number().int().min(1).max(5),
|
|
274
|
+
totalLessons: zod.z.number().int().positive(),
|
|
275
|
+
totalQuestions: zod.z.number().int().positive(),
|
|
276
|
+
sourceFile: zod.z.string().optional(),
|
|
277
|
+
lessons: zod.z.array(LessonSchema),
|
|
278
|
+
assessments: zod.z.array(AssessmentBlueprintSchema).optional()
|
|
279
|
+
});
|
|
280
|
+
CourseSchema.omit({ lessons: true }).extend({
|
|
281
|
+
lessons: zod.z.array(
|
|
282
|
+
LessonSchema.omit({ questions: true })
|
|
283
|
+
).optional()
|
|
284
|
+
});
|
|
266
285
|
var CertificationProjectSchema = zod.z.object({
|
|
267
286
|
projectNumber: zod.z.number().int().positive(),
|
|
268
287
|
projectName: zod.z.string(),
|
|
@@ -550,6 +569,138 @@ ${green(bold("Valid!"))} File passed schema validation.
|
|
|
550
569
|
}
|
|
551
570
|
}
|
|
552
571
|
|
|
572
|
+
// src/core/assessment.ts
|
|
573
|
+
var SeededRandom = class {
|
|
574
|
+
constructor(seed) {
|
|
575
|
+
this.state = seed;
|
|
576
|
+
}
|
|
577
|
+
next() {
|
|
578
|
+
this.state = (this.state * 1103515245 + 12345) % 2147483648;
|
|
579
|
+
return this.state / 2147483648;
|
|
580
|
+
}
|
|
581
|
+
/** Fisher-Yates shuffle */
|
|
582
|
+
shuffle(array) {
|
|
583
|
+
const result = [...array];
|
|
584
|
+
for (let i3 = result.length - 1; i3 > 0; i3--) {
|
|
585
|
+
const j2 = Math.floor(this.next() * (i3 + 1));
|
|
586
|
+
const temp = result[i3];
|
|
587
|
+
result[i3] = result[j2];
|
|
588
|
+
result[j2] = temp;
|
|
589
|
+
}
|
|
590
|
+
return result;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
function hashString(str) {
|
|
594
|
+
let hash = 0;
|
|
595
|
+
for (let i3 = 0; i3 < str.length; i3++) {
|
|
596
|
+
const char = str.charCodeAt(i3);
|
|
597
|
+
hash = (hash << 5) - hash + char;
|
|
598
|
+
hash = hash & hash;
|
|
599
|
+
}
|
|
600
|
+
return Math.abs(hash);
|
|
601
|
+
}
|
|
602
|
+
function createAssessmentModule(getCourse) {
|
|
603
|
+
return {
|
|
604
|
+
async getAssessments(courseId) {
|
|
605
|
+
const course = await getCourse(courseId);
|
|
606
|
+
return course?.assessments ?? [];
|
|
607
|
+
},
|
|
608
|
+
async buildAssessmentQuiz(courseId, assessmentId, seed) {
|
|
609
|
+
const course = await getCourse(courseId);
|
|
610
|
+
if (!course) throw new Error(`Course not found: ${courseId}`);
|
|
611
|
+
const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
|
|
612
|
+
if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
|
|
613
|
+
const [rangeStart, rangeEnd] = blueprint.lessonRange;
|
|
614
|
+
const pool = [];
|
|
615
|
+
for (const lesson of course.lessons) {
|
|
616
|
+
if (lesson.lessonNumber >= rangeStart && lesson.lessonNumber <= rangeEnd) {
|
|
617
|
+
pool.push(...lesson.questions);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (pool.length === 0) {
|
|
621
|
+
throw new Error(`No questions found for lessons ${rangeStart}-${rangeEnd} in ${courseId}`);
|
|
622
|
+
}
|
|
623
|
+
const seedValue = seed ? hashString(seed) : Date.now();
|
|
624
|
+
const rng = new SeededRandom(seedValue);
|
|
625
|
+
const shuffled = rng.shuffle(pool);
|
|
626
|
+
return shuffled.slice(0, Math.min(blueprint.questionCount, shuffled.length));
|
|
627
|
+
},
|
|
628
|
+
async gradeAssessment(courseId, assessmentId, responses, passingScore) {
|
|
629
|
+
const course = await getCourse(courseId);
|
|
630
|
+
if (!course) throw new Error(`Course not found: ${courseId}`);
|
|
631
|
+
const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
|
|
632
|
+
if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
|
|
633
|
+
const threshold = passingScore ?? blueprint.passingScore;
|
|
634
|
+
const questionMap = /* @__PURE__ */ new Map();
|
|
635
|
+
for (const lesson of course.lessons) {
|
|
636
|
+
for (const q of lesson.questions) {
|
|
637
|
+
questionMap.set(q.questionId, q);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const gradedResponses = [];
|
|
641
|
+
let correctCount = 0;
|
|
642
|
+
for (const response of responses) {
|
|
643
|
+
const question = questionMap.get(response.questionId);
|
|
644
|
+
if (!question) continue;
|
|
645
|
+
const selectedOption = question.options.find((o2) => o2.key === response.selectedAnswer);
|
|
646
|
+
const isCorrect = selectedOption?.isCorrect ?? false;
|
|
647
|
+
if (isCorrect) correctCount++;
|
|
648
|
+
gradedResponses.push({
|
|
649
|
+
questionId: response.questionId,
|
|
650
|
+
selectedAnswer: response.selectedAnswer,
|
|
651
|
+
correctAnswer: question.correctAnswer,
|
|
652
|
+
isCorrect,
|
|
653
|
+
misconceptionId: isCorrect ? null : selectedOption?.misconceptionId ?? null,
|
|
654
|
+
feedback: selectedOption?.feedback,
|
|
655
|
+
timeSpent: response.timeSpent
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
const totalQuestions = gradedResponses.length;
|
|
659
|
+
const score = totalQuestions > 0 ? Math.round(correctCount / totalQuestions * 100) : 0;
|
|
660
|
+
const misconceptionCounts = /* @__PURE__ */ new Map();
|
|
661
|
+
for (const gr of gradedResponses) {
|
|
662
|
+
if (gr.misconceptionId) {
|
|
663
|
+
const existing = misconceptionCounts.get(gr.misconceptionId);
|
|
664
|
+
if (existing) {
|
|
665
|
+
existing.count++;
|
|
666
|
+
existing.questionIds.push(gr.questionId);
|
|
667
|
+
} else {
|
|
668
|
+
misconceptionCounts.set(gr.misconceptionId, {
|
|
669
|
+
count: 1,
|
|
670
|
+
questionIds: [gr.questionId]
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
const misconceptions = Array.from(misconceptionCounts.entries()).map(([id, data]) => ({
|
|
676
|
+
misconceptionId: id,
|
|
677
|
+
count: data.count,
|
|
678
|
+
percentage: Math.round(data.count / totalQuestions * 100),
|
|
679
|
+
questionIds: data.questionIds
|
|
680
|
+
})).sort((a2, b) => b.count - a2.count);
|
|
681
|
+
if (totalQuestions === 0) {
|
|
682
|
+
throw new Error(`No valid questions matched for assessment ${assessmentId}. All ${responses.length} response(s) referenced unknown questionIds.`);
|
|
683
|
+
}
|
|
684
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
685
|
+
const generatedId = `grade-${courseId}-${assessmentId}-${hashString(now).toString(36)}`;
|
|
686
|
+
return {
|
|
687
|
+
assessmentId: generatedId,
|
|
688
|
+
courseId,
|
|
689
|
+
totalQuestions,
|
|
690
|
+
correctCount,
|
|
691
|
+
incorrectCount: totalQuestions - correctCount,
|
|
692
|
+
score,
|
|
693
|
+
passed: score >= threshold,
|
|
694
|
+
responses: gradedResponses,
|
|
695
|
+
misconceptions,
|
|
696
|
+
topMisconceptions: misconceptions.slice(0, 3).map((m3) => m3.misconceptionId),
|
|
697
|
+
submittedAt: now,
|
|
698
|
+
gradedAt: now
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
553
704
|
// src/knowledge/index.ts
|
|
554
705
|
function createLogger(config) {
|
|
555
706
|
return {
|
|
@@ -23291,6 +23442,7 @@ function createExitTicketsSDK(config) {
|
|
|
23291
23442
|
};
|
|
23292
23443
|
const malaysiaMath = createMalaysiaMathModule(malaysiaMathConfig);
|
|
23293
23444
|
const malaysiaMathTaxonomy = createMalaysiaMathTaxonomyModule(malaysiaMathConfig);
|
|
23445
|
+
const assessment = createAssessmentModule(data.getCourse);
|
|
23294
23446
|
return {
|
|
23295
23447
|
data,
|
|
23296
23448
|
query,
|
|
@@ -23301,7 +23453,8 @@ function createExitTicketsSDK(config) {
|
|
|
23301
23453
|
knowledge,
|
|
23302
23454
|
taxonomy,
|
|
23303
23455
|
malaysiaMath,
|
|
23304
|
-
malaysiaMathTaxonomy
|
|
23456
|
+
malaysiaMathTaxonomy,
|
|
23457
|
+
assessment
|
|
23305
23458
|
};
|
|
23306
23459
|
}
|
|
23307
23460
|
|
|
@@ -23470,26 +23623,32 @@ async function loadCourseData(filePath) {
|
|
|
23470
23623
|
}
|
|
23471
23624
|
function extractQuestions(courseData) {
|
|
23472
23625
|
const questions = [];
|
|
23626
|
+
const pushQuestion = (q) => {
|
|
23627
|
+
questions.push({
|
|
23628
|
+
questionId: q.questionId || q.globalId || `${courseData.courseId || "unknown"}-unknown`,
|
|
23629
|
+
prompt: q.prompt || "",
|
|
23630
|
+
options: (q.options || []).map((o2) => ({
|
|
23631
|
+
key: o2.key || "",
|
|
23632
|
+
text: o2.text || "",
|
|
23633
|
+
isCorrect: o2.isCorrect || false
|
|
23634
|
+
})),
|
|
23635
|
+
hasCodeBlock: q.hasCodeBlock || false,
|
|
23636
|
+
codeLanguage: q.codeLanguage || null,
|
|
23637
|
+
codeContent: q.codeContent || null
|
|
23638
|
+
});
|
|
23639
|
+
};
|
|
23473
23640
|
if (courseData.lessons) {
|
|
23474
23641
|
for (const lesson of courseData.lessons) {
|
|
23475
23642
|
if (lesson.questions) {
|
|
23476
23643
|
for (const q of lesson.questions) {
|
|
23477
|
-
|
|
23478
|
-
questionId: q.questionId || `${courseData.courseId}-unknown`,
|
|
23479
|
-
prompt: q.prompt || "",
|
|
23480
|
-
options: (q.options || []).map((o2) => ({
|
|
23481
|
-
key: o2.key || "",
|
|
23482
|
-
text: o2.text || "",
|
|
23483
|
-
isCorrect: o2.isCorrect || false
|
|
23484
|
-
})),
|
|
23485
|
-
// CS-specific fields for enhanced analysis
|
|
23486
|
-
hasCodeBlock: q.hasCodeBlock || false,
|
|
23487
|
-
codeLanguage: q.codeLanguage || null,
|
|
23488
|
-
codeContent: q.codeContent || null
|
|
23489
|
-
});
|
|
23644
|
+
pushQuestion(q);
|
|
23490
23645
|
}
|
|
23491
23646
|
}
|
|
23492
23647
|
}
|
|
23648
|
+
} else if (courseData.questions && Array.isArray(courseData.questions)) {
|
|
23649
|
+
for (const q of courseData.questions) {
|
|
23650
|
+
pushQuestion(q);
|
|
23651
|
+
}
|
|
23493
23652
|
}
|
|
23494
23653
|
return questions;
|
|
23495
23654
|
}
|