@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.
@@ -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, UserResponseSchema as U, type ValidationResult as V, AssessmentSubmissionSchema as a, GradedResponseSchema as b, GradedAssessmentSchema as c, ValidationResultSchema as d, type UserResponse as e, type GradedResponse as f, type MisconceptionReport as g };
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, UserResponseSchema as U, type ValidationResult as V, AssessmentSubmissionSchema as a, GradedResponseSchema as b, GradedAssessmentSchema as c, ValidationResultSchema as d, type UserResponse as e, type GradedResponse as f, type MisconceptionReport as g };
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 };
@@ -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 CourseDomainSchema = zod.z.enum([
127
- // AI & Data Science
128
- "ai_data_science",
129
- "ai_ml_cv",
130
- "ai_generative",
131
- "ai_advanced",
132
- "ai",
133
- // Prompt Engineering (general AI)
134
- // Web Development
135
- "web_development",
136
- // Mobile Development
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().uuid(),
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
- questions.push({
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
  }