@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
package/dist/cli/index.js
CHANGED
|
@@ -78,6 +78,10 @@ var QuestionMetadataSchema = z.object({
|
|
|
78
78
|
// seconds
|
|
79
79
|
bloomsTaxonomy: BloomsTaxonomySchema,
|
|
80
80
|
tags: z.array(z.string()),
|
|
81
|
+
conceptTags: z.array(z.string()).optional(),
|
|
82
|
+
// mastery-relevant concept tags
|
|
83
|
+
metaTags: z.array(z.string()).optional(),
|
|
84
|
+
// Bloom's types, course codes, internal
|
|
81
85
|
source: z.string().default("exit-ticket"),
|
|
82
86
|
version: z.string().default("1.1"),
|
|
83
87
|
createdDate: z.string().optional(),
|
|
@@ -120,70 +124,17 @@ QuestionSchema.omit({
|
|
|
120
124
|
})
|
|
121
125
|
).length(4)
|
|
122
126
|
});
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
"
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"mobile_development",
|
|
135
|
-
"mobile",
|
|
136
|
-
// Block-based Programming
|
|
137
|
-
"block_based",
|
|
138
|
-
"python_programming",
|
|
139
|
-
"design",
|
|
140
|
-
"artificial_intelligence",
|
|
141
|
-
// Block-based AI courses (BBAI)
|
|
142
|
-
// Computer Science
|
|
143
|
-
"cs_foundations",
|
|
144
|
-
// CS1-CS4 courses
|
|
145
|
-
// Foundation
|
|
146
|
-
"foundation",
|
|
147
|
-
"creative_computing",
|
|
148
|
-
// Utility / Tools
|
|
149
|
-
"tools"
|
|
150
|
-
// IDE Setup, Git, Vibe Coding
|
|
151
|
-
]);
|
|
152
|
-
var CourseTierSchema = z.enum([
|
|
153
|
-
"beginner",
|
|
154
|
-
// Block-based courses (BBW, BBP, BBD, BBAI)
|
|
155
|
-
"foundation",
|
|
156
|
-
"intermediate",
|
|
157
|
-
"advanced"
|
|
158
|
-
]);
|
|
159
|
-
var LessonSchema = z.object({
|
|
160
|
-
lessonId: z.string(),
|
|
161
|
-
// e.g., "ai-1-lesson-1"
|
|
162
|
-
lessonNumber: z.number().int().positive(),
|
|
163
|
-
lessonTitle: z.string(),
|
|
164
|
-
lessonSlug: z.string().optional(),
|
|
165
|
-
totalQuestions: z.number().int().positive().default(5),
|
|
166
|
-
questions: z.array(QuestionSchema)
|
|
167
|
-
});
|
|
168
|
-
var CourseSchema = z.object({
|
|
169
|
-
courseId: z.string(),
|
|
170
|
-
// e.g., "ai-1"
|
|
171
|
-
courseName: z.string(),
|
|
172
|
-
// e.g., "AI-1 Data Analysis and Data Science"
|
|
173
|
-
courseCode: z.string(),
|
|
174
|
-
// e.g., "AI1"
|
|
175
|
-
domain: CourseDomainSchema,
|
|
176
|
-
tier: CourseTierSchema,
|
|
177
|
-
difficulty: z.number().int().min(1).max(5),
|
|
178
|
-
totalLessons: z.number().int().positive(),
|
|
179
|
-
totalQuestions: z.number().int().positive(),
|
|
180
|
-
sourceFile: z.string().optional(),
|
|
181
|
-
lessons: z.array(LessonSchema)
|
|
182
|
-
});
|
|
183
|
-
CourseSchema.omit({ lessons: true }).extend({
|
|
184
|
-
lessons: z.array(
|
|
185
|
-
LessonSchema.omit({ questions: true })
|
|
186
|
-
).optional()
|
|
127
|
+
var AssessmentBlueprintSchema = z.object({
|
|
128
|
+
assessmentId: z.string(),
|
|
129
|
+
// e.g., "cs1-quiz-1"
|
|
130
|
+
title: z.string(),
|
|
131
|
+
// e.g., "Quiz 1"
|
|
132
|
+
description: z.string(),
|
|
133
|
+
lessonRange: z.tuple([z.number().int().positive(), z.number().int().positive()]).refine(([start2, end2]) => start2 <= end2, { message: "lessonRange start must be <= end" }),
|
|
134
|
+
questionCount: z.number().int().positive(),
|
|
135
|
+
passingScore: z.number().min(0).max(100),
|
|
136
|
+
timeLimitMinutes: z.number().int().positive().nullable(),
|
|
137
|
+
tags: z.array(z.string()).optional()
|
|
187
138
|
});
|
|
188
139
|
var UserResponseSchema = z.object({
|
|
189
140
|
questionId: z.string(),
|
|
@@ -224,7 +175,7 @@ var MisconceptionReportSchema = z.object({
|
|
|
224
175
|
description: z.string().optional()
|
|
225
176
|
});
|
|
226
177
|
z.object({
|
|
227
|
-
assessmentId: z.string()
|
|
178
|
+
assessmentId: z.string(),
|
|
228
179
|
userId: z.string().optional(),
|
|
229
180
|
courseId: z.string(),
|
|
230
181
|
lessonId: z.string().optional(),
|
|
@@ -260,6 +211,74 @@ z.object({
|
|
|
260
211
|
questionCount: z.number().int(),
|
|
261
212
|
answeredCount: z.number().int()
|
|
262
213
|
});
|
|
214
|
+
|
|
215
|
+
// src/schemas/course.ts
|
|
216
|
+
var CourseDomainSchema = z.enum([
|
|
217
|
+
// AI & Data Science
|
|
218
|
+
"ai_data_science",
|
|
219
|
+
"ai_ml_cv",
|
|
220
|
+
"ai_generative",
|
|
221
|
+
"ai_advanced",
|
|
222
|
+
"ai",
|
|
223
|
+
// Prompt Engineering (general AI)
|
|
224
|
+
// Web Development
|
|
225
|
+
"web_development",
|
|
226
|
+
// Mobile Development
|
|
227
|
+
"mobile_development",
|
|
228
|
+
"mobile",
|
|
229
|
+
// Block-based Programming
|
|
230
|
+
"block_based",
|
|
231
|
+
"python_programming",
|
|
232
|
+
"design",
|
|
233
|
+
"artificial_intelligence",
|
|
234
|
+
// Block-based AI courses (BBAI)
|
|
235
|
+
// Computer Science
|
|
236
|
+
"cs_foundations",
|
|
237
|
+
// CS1-CS4 courses
|
|
238
|
+
// Foundation
|
|
239
|
+
"foundation",
|
|
240
|
+
"creative_computing",
|
|
241
|
+
// Utility / Tools
|
|
242
|
+
"tools"
|
|
243
|
+
// IDE Setup, Git, Vibe Coding
|
|
244
|
+
]);
|
|
245
|
+
var CourseTierSchema = z.enum([
|
|
246
|
+
"beginner",
|
|
247
|
+
// Block-based courses (BBW, BBP, BBD, BBAI)
|
|
248
|
+
"foundation",
|
|
249
|
+
"intermediate",
|
|
250
|
+
"advanced"
|
|
251
|
+
]);
|
|
252
|
+
var LessonSchema = z.object({
|
|
253
|
+
lessonId: z.string(),
|
|
254
|
+
// e.g., "ai-1-lesson-1"
|
|
255
|
+
lessonNumber: z.number().int().positive(),
|
|
256
|
+
lessonTitle: z.string(),
|
|
257
|
+
lessonSlug: z.string().optional(),
|
|
258
|
+
totalQuestions: z.number().int().positive().default(5),
|
|
259
|
+
questions: z.array(QuestionSchema)
|
|
260
|
+
});
|
|
261
|
+
var CourseSchema = z.object({
|
|
262
|
+
courseId: z.string(),
|
|
263
|
+
// e.g., "ai-1"
|
|
264
|
+
courseName: z.string(),
|
|
265
|
+
// e.g., "AI-1 Data Analysis and Data Science"
|
|
266
|
+
courseCode: z.string(),
|
|
267
|
+
// e.g., "AI1"
|
|
268
|
+
domain: CourseDomainSchema,
|
|
269
|
+
tier: CourseTierSchema,
|
|
270
|
+
difficulty: z.number().int().min(1).max(5),
|
|
271
|
+
totalLessons: z.number().int().positive(),
|
|
272
|
+
totalQuestions: z.number().int().positive(),
|
|
273
|
+
sourceFile: z.string().optional(),
|
|
274
|
+
lessons: z.array(LessonSchema),
|
|
275
|
+
assessments: z.array(AssessmentBlueprintSchema).optional()
|
|
276
|
+
});
|
|
277
|
+
CourseSchema.omit({ lessons: true }).extend({
|
|
278
|
+
lessons: z.array(
|
|
279
|
+
LessonSchema.omit({ questions: true })
|
|
280
|
+
).optional()
|
|
281
|
+
});
|
|
263
282
|
var CertificationProjectSchema = z.object({
|
|
264
283
|
projectNumber: z.number().int().positive(),
|
|
265
284
|
projectName: z.string(),
|
|
@@ -547,6 +566,138 @@ ${green(bold("Valid!"))} File passed schema validation.
|
|
|
547
566
|
}
|
|
548
567
|
}
|
|
549
568
|
|
|
569
|
+
// src/core/assessment.ts
|
|
570
|
+
var SeededRandom = class {
|
|
571
|
+
constructor(seed) {
|
|
572
|
+
this.state = seed;
|
|
573
|
+
}
|
|
574
|
+
next() {
|
|
575
|
+
this.state = (this.state * 1103515245 + 12345) % 2147483648;
|
|
576
|
+
return this.state / 2147483648;
|
|
577
|
+
}
|
|
578
|
+
/** Fisher-Yates shuffle */
|
|
579
|
+
shuffle(array) {
|
|
580
|
+
const result = [...array];
|
|
581
|
+
for (let i3 = result.length - 1; i3 > 0; i3--) {
|
|
582
|
+
const j2 = Math.floor(this.next() * (i3 + 1));
|
|
583
|
+
const temp = result[i3];
|
|
584
|
+
result[i3] = result[j2];
|
|
585
|
+
result[j2] = temp;
|
|
586
|
+
}
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
function hashString(str) {
|
|
591
|
+
let hash = 0;
|
|
592
|
+
for (let i3 = 0; i3 < str.length; i3++) {
|
|
593
|
+
const char = str.charCodeAt(i3);
|
|
594
|
+
hash = (hash << 5) - hash + char;
|
|
595
|
+
hash = hash & hash;
|
|
596
|
+
}
|
|
597
|
+
return Math.abs(hash);
|
|
598
|
+
}
|
|
599
|
+
function createAssessmentModule(getCourse) {
|
|
600
|
+
return {
|
|
601
|
+
async getAssessments(courseId) {
|
|
602
|
+
const course = await getCourse(courseId);
|
|
603
|
+
return course?.assessments ?? [];
|
|
604
|
+
},
|
|
605
|
+
async buildAssessmentQuiz(courseId, assessmentId, seed) {
|
|
606
|
+
const course = await getCourse(courseId);
|
|
607
|
+
if (!course) throw new Error(`Course not found: ${courseId}`);
|
|
608
|
+
const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
|
|
609
|
+
if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
|
|
610
|
+
const [rangeStart, rangeEnd] = blueprint.lessonRange;
|
|
611
|
+
const pool = [];
|
|
612
|
+
for (const lesson of course.lessons) {
|
|
613
|
+
if (lesson.lessonNumber >= rangeStart && lesson.lessonNumber <= rangeEnd) {
|
|
614
|
+
pool.push(...lesson.questions);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (pool.length === 0) {
|
|
618
|
+
throw new Error(`No questions found for lessons ${rangeStart}-${rangeEnd} in ${courseId}`);
|
|
619
|
+
}
|
|
620
|
+
const seedValue = seed ? hashString(seed) : Date.now();
|
|
621
|
+
const rng = new SeededRandom(seedValue);
|
|
622
|
+
const shuffled = rng.shuffle(pool);
|
|
623
|
+
return shuffled.slice(0, Math.min(blueprint.questionCount, shuffled.length));
|
|
624
|
+
},
|
|
625
|
+
async gradeAssessment(courseId, assessmentId, responses, passingScore) {
|
|
626
|
+
const course = await getCourse(courseId);
|
|
627
|
+
if (!course) throw new Error(`Course not found: ${courseId}`);
|
|
628
|
+
const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
|
|
629
|
+
if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
|
|
630
|
+
const threshold = passingScore ?? blueprint.passingScore;
|
|
631
|
+
const questionMap = /* @__PURE__ */ new Map();
|
|
632
|
+
for (const lesson of course.lessons) {
|
|
633
|
+
for (const q of lesson.questions) {
|
|
634
|
+
questionMap.set(q.questionId, q);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const gradedResponses = [];
|
|
638
|
+
let correctCount = 0;
|
|
639
|
+
for (const response of responses) {
|
|
640
|
+
const question = questionMap.get(response.questionId);
|
|
641
|
+
if (!question) continue;
|
|
642
|
+
const selectedOption = question.options.find((o2) => o2.key === response.selectedAnswer);
|
|
643
|
+
const isCorrect = selectedOption?.isCorrect ?? false;
|
|
644
|
+
if (isCorrect) correctCount++;
|
|
645
|
+
gradedResponses.push({
|
|
646
|
+
questionId: response.questionId,
|
|
647
|
+
selectedAnswer: response.selectedAnswer,
|
|
648
|
+
correctAnswer: question.correctAnswer,
|
|
649
|
+
isCorrect,
|
|
650
|
+
misconceptionId: isCorrect ? null : selectedOption?.misconceptionId ?? null,
|
|
651
|
+
feedback: selectedOption?.feedback,
|
|
652
|
+
timeSpent: response.timeSpent
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const totalQuestions = gradedResponses.length;
|
|
656
|
+
const score = totalQuestions > 0 ? Math.round(correctCount / totalQuestions * 100) : 0;
|
|
657
|
+
const misconceptionCounts = /* @__PURE__ */ new Map();
|
|
658
|
+
for (const gr of gradedResponses) {
|
|
659
|
+
if (gr.misconceptionId) {
|
|
660
|
+
const existing = misconceptionCounts.get(gr.misconceptionId);
|
|
661
|
+
if (existing) {
|
|
662
|
+
existing.count++;
|
|
663
|
+
existing.questionIds.push(gr.questionId);
|
|
664
|
+
} else {
|
|
665
|
+
misconceptionCounts.set(gr.misconceptionId, {
|
|
666
|
+
count: 1,
|
|
667
|
+
questionIds: [gr.questionId]
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
const misconceptions = Array.from(misconceptionCounts.entries()).map(([id, data]) => ({
|
|
673
|
+
misconceptionId: id,
|
|
674
|
+
count: data.count,
|
|
675
|
+
percentage: Math.round(data.count / totalQuestions * 100),
|
|
676
|
+
questionIds: data.questionIds
|
|
677
|
+
})).sort((a2, b) => b.count - a2.count);
|
|
678
|
+
if (totalQuestions === 0) {
|
|
679
|
+
throw new Error(`No valid questions matched for assessment ${assessmentId}. All ${responses.length} response(s) referenced unknown questionIds.`);
|
|
680
|
+
}
|
|
681
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
682
|
+
const generatedId = `grade-${courseId}-${assessmentId}-${hashString(now).toString(36)}`;
|
|
683
|
+
return {
|
|
684
|
+
assessmentId: generatedId,
|
|
685
|
+
courseId,
|
|
686
|
+
totalQuestions,
|
|
687
|
+
correctCount,
|
|
688
|
+
incorrectCount: totalQuestions - correctCount,
|
|
689
|
+
score,
|
|
690
|
+
passed: score >= threshold,
|
|
691
|
+
responses: gradedResponses,
|
|
692
|
+
misconceptions,
|
|
693
|
+
topMisconceptions: misconceptions.slice(0, 3).map((m3) => m3.misconceptionId),
|
|
694
|
+
submittedAt: now,
|
|
695
|
+
gradedAt: now
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
550
701
|
// src/knowledge/index.ts
|
|
551
702
|
function createLogger(config) {
|
|
552
703
|
return {
|
|
@@ -23288,6 +23439,7 @@ function createExitTicketsSDK(config) {
|
|
|
23288
23439
|
};
|
|
23289
23440
|
const malaysiaMath = createMalaysiaMathModule(malaysiaMathConfig);
|
|
23290
23441
|
const malaysiaMathTaxonomy = createMalaysiaMathTaxonomyModule(malaysiaMathConfig);
|
|
23442
|
+
const assessment = createAssessmentModule(data.getCourse);
|
|
23291
23443
|
return {
|
|
23292
23444
|
data,
|
|
23293
23445
|
query,
|
|
@@ -23298,7 +23450,8 @@ function createExitTicketsSDK(config) {
|
|
|
23298
23450
|
knowledge,
|
|
23299
23451
|
taxonomy,
|
|
23300
23452
|
malaysiaMath,
|
|
23301
|
-
malaysiaMathTaxonomy
|
|
23453
|
+
malaysiaMathTaxonomy,
|
|
23454
|
+
assessment
|
|
23302
23455
|
};
|
|
23303
23456
|
}
|
|
23304
23457
|
|
|
@@ -23467,26 +23620,32 @@ async function loadCourseData(filePath) {
|
|
|
23467
23620
|
}
|
|
23468
23621
|
function extractQuestions(courseData) {
|
|
23469
23622
|
const questions = [];
|
|
23623
|
+
const pushQuestion = (q) => {
|
|
23624
|
+
questions.push({
|
|
23625
|
+
questionId: q.questionId || q.globalId || `${courseData.courseId || "unknown"}-unknown`,
|
|
23626
|
+
prompt: q.prompt || "",
|
|
23627
|
+
options: (q.options || []).map((o2) => ({
|
|
23628
|
+
key: o2.key || "",
|
|
23629
|
+
text: o2.text || "",
|
|
23630
|
+
isCorrect: o2.isCorrect || false
|
|
23631
|
+
})),
|
|
23632
|
+
hasCodeBlock: q.hasCodeBlock || false,
|
|
23633
|
+
codeLanguage: q.codeLanguage || null,
|
|
23634
|
+
codeContent: q.codeContent || null
|
|
23635
|
+
});
|
|
23636
|
+
};
|
|
23470
23637
|
if (courseData.lessons) {
|
|
23471
23638
|
for (const lesson of courseData.lessons) {
|
|
23472
23639
|
if (lesson.questions) {
|
|
23473
23640
|
for (const q of lesson.questions) {
|
|
23474
|
-
|
|
23475
|
-
questionId: q.questionId || `${courseData.courseId}-unknown`,
|
|
23476
|
-
prompt: q.prompt || "",
|
|
23477
|
-
options: (q.options || []).map((o2) => ({
|
|
23478
|
-
key: o2.key || "",
|
|
23479
|
-
text: o2.text || "",
|
|
23480
|
-
isCorrect: o2.isCorrect || false
|
|
23481
|
-
})),
|
|
23482
|
-
// CS-specific fields for enhanced analysis
|
|
23483
|
-
hasCodeBlock: q.hasCodeBlock || false,
|
|
23484
|
-
codeLanguage: q.codeLanguage || null,
|
|
23485
|
-
codeContent: q.codeContent || null
|
|
23486
|
-
});
|
|
23641
|
+
pushQuestion(q);
|
|
23487
23642
|
}
|
|
23488
23643
|
}
|
|
23489
23644
|
}
|
|
23645
|
+
} else if (courseData.questions && Array.isArray(courseData.questions)) {
|
|
23646
|
+
for (const q of courseData.questions) {
|
|
23647
|
+
pushQuestion(q);
|
|
23648
|
+
}
|
|
23490
23649
|
}
|
|
23491
23650
|
return questions;
|
|
23492
23651
|
}
|