@thanh01.pmt/interactive-quiz-kit 1.0.13 → 1.0.16
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/HEADLESS.md +126 -213
- package/README.md +83 -95
- package/package.json +1 -1
package/HEADLESS.md
CHANGED
|
@@ -1,170 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Headless Mode: Using `interactive-quiz-kit` Logic Without the UI
|
|
2
2
|
|
|
3
|
-
**`interactive-quiz-kit`**
|
|
3
|
+
**`interactive-quiz-kit`** is designed with a modular architecture, allowing you to use its entire core logic independently of the React UI components. This "headless" mode is ideal for use cases such as:
|
|
4
4
|
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Building a backend to manage and generate quizzes.
|
|
6
|
+
* Integrating quiz logic into a mobile application (React Native, Flutter, etc.).
|
|
7
|
+
* Running automated scripts for content creation or data processing.
|
|
8
|
+
* Integrating with JavaScript frameworks other than React.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
This document will guide you on how to import and use the library's main headless features.
|
|
11
11
|
|
|
12
|
-
## I.
|
|
12
|
+
## I. Overview of Headless Components & Entry Points
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
The non-UI components of the library are accessible through specific entry points for optimized, modular usage.
|
|
15
15
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `schemas/` | Các file JSON Schema để xác thực cấu trúc dữ liệu của quiz. |
|
|
16
|
+
| Entry Point / File | Main Function |
|
|
17
|
+
| :----------------------------------------------- | :------------------------------------------------------------------------------ |
|
|
18
|
+
| `@thanh01.pmt/interactive-quiz-kit` | **(Core)** Contains all types, core services, and utilities. |
|
|
19
|
+
| `@thanh01.pmt/interactive-quiz-kit/ai` | **(AI)** Contains flows for generating quiz content using Artificial Intelligence. |
|
|
20
|
+
| `src/services/TopicDataService.ts` | **(Service)** Manages curriculum data imported from TSV files. |
|
|
21
|
+
| `src/services/SCORMService.ts` & related files | **(Service)** Provides tools for SCORM packaging and communication. |
|
|
23
22
|
|
|
24
|
-
### 1.
|
|
23
|
+
### 1. What is Headless Mode and Why is it Important?
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
Imagine the library is a complete car. The UI (`react-ui/`) is the body, interior, steering wheel, and dashboard—everything you see and interact with. "Headless mode" means you can take the entire **engine, chassis, drivetrain, and control unit (ECU)** and fit it into a different body—like a truck, a boat, or even an industrial machine.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
In other words, **headless mode allows you to use all the logic, business processes, and data handling capabilities of the library without the bundled React UI.**
|
|
29
28
|
|
|
30
|
-
**
|
|
29
|
+
**Why is this important?**
|
|
31
30
|
|
|
32
|
-
* **
|
|
33
|
-
* **
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* **
|
|
31
|
+
* **Maximum Flexibility:** You are not tied to React. You can build your UI with Vue, Svelte, Angular, or even native mobile apps (iOS, Android) and simply call the library's core logic.
|
|
32
|
+
* **Automation & Backend Integration:** You can run processes on a server, for example:
|
|
33
|
+
* A nightly cron job that uses the AI module to automatically generate hundreds of new questions and save them to a database.
|
|
34
|
+
* An API endpoint to manage, edit, and publish quizzes.
|
|
35
|
+
* **Logic Reusability:** Ensures that business logic (e.g., how a question is graded) is consistent everywhere, from the web and mobile to internal scripts.
|
|
37
36
|
|
|
38
37
|
---
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
## II. Core Headless Services
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
### 1. Types - The Foundation
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* **Vai trò:** Là "Hiến pháp" và "Bộ luật" của thư viện. Chúng định nghĩa cấu trúc dữ liệu và các quy tắc mà mọi thành phần khác phải tuân theo.
|
|
47
|
-
* **Mô tả chi tiết:**
|
|
48
|
-
* **`types.ts`:** Đây là file quan trọng nhất. Nó chứa các `interface` TypeScript như `QuizConfig`, `QuizQuestion`, `QuizResult`. Đây là "nguồn chân lý duy nhất" (single source of truth) cho hình dạng của dữ liệu. Bất kỳ hàm nào bạn viết để tương tác với quiz đều sẽ nhận và trả về các kiểu dữ liệu được định nghĩa ở đây.
|
|
49
|
-
* **`schemas/`:** Chứa các file JSON Schema. Trong môi trường headless, chúng cực kỳ hữu ích để **xác thực (validate)** dữ liệu. Ví dụ, khi một hệ thống khác gửi cho bạn một file JSON `quiz.json`, bạn có thể dùng schema này để kiểm tra xem file đó có hợp lệ hay không trước khi xử lý.
|
|
50
|
-
* **Tầm quan trọng:** **Nền tảng (Fundamental)**. Không thể sử dụng thư viện nếu không hiểu các kiểu dữ liệu này.
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
#### **B. `services/QuizEditorService.ts` - Người Thợ Xây**
|
|
55
|
-
|
|
56
|
-
* **Vai trò:** Công cụ để **tạo, sửa, xóa, và sắp xếp** các câu hỏi bên trong một đối tượng `QuizConfig`. Đây là service dành cho khâu *biên soạn (authoring)*.
|
|
57
|
-
* **Mô tả chi tiết:**
|
|
58
|
-
* Đây là một lớp (class) bạn khởi tạo với một đối tượng `QuizConfig` ban đầu. Nó sẽ làm việc trên một bản sao sâu (deep copy), đảm bảo không làm thay đổi đối tượng gốc một cách không mong muốn (nguyên tắc immutability).
|
|
59
|
-
* **`createNewQuestionTemplate(type)` (Static Method):** Tạo ra một "khuôn mẫu" câu hỏi rỗng với các giá trị mặc định. Rất hữu ích khi bạn muốn thêm một câu hỏi mới.
|
|
60
|
-
* **`addQuestion(question)`:** Thêm một câu hỏi vào danh sách.
|
|
61
|
-
* **`updateQuestion(updatedQuestion)`:** Cập nhật một câu hỏi đã có dựa trên `id` của nó.
|
|
62
|
-
* **`deleteQuestionByIndex(index)`:** Xóa một câu hỏi dựa trên vị trí của nó.
|
|
63
|
-
* **`moveQuestion(fromIndex, toIndex)`:** Thay đổi thứ tự các câu hỏi.
|
|
64
|
-
* **Tầm quan trọng:** **Cốt lõi (Core)** cho bất kỳ tác vụ nào liên quan đến việc quản lý nội dung quiz.
|
|
65
|
-
|
|
66
|
-
* **Ví dụ sử dụng:**
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { QuizEditorService, emptyQuiz } from 'interactive-quiz-kit/services';
|
|
70
|
-
|
|
71
|
-
// Bắt đầu với một quiz rỗng
|
|
72
|
-
let myQuiz = emptyQuiz;
|
|
73
|
-
const editor = new QuizEditorService(myQuiz);
|
|
74
|
-
|
|
75
|
-
// Thêm một câu hỏi True/False
|
|
76
|
-
const tfQuestion = QuizEditorService.createNewQuestionTemplate('true_false');
|
|
77
|
-
tfQuestion.prompt = "Trái Đất có phải là một hình cầu hoàn hảo không?";
|
|
78
|
-
if (tfQuestion.questionType === 'true_false') tfQuestion.correctAnswer = false;
|
|
79
|
-
|
|
80
|
-
myQuiz = editor.addQuestion(tfQuestion);
|
|
81
|
-
|
|
82
|
-
// Cập nhật lại tiêu đề quiz
|
|
83
|
-
myQuiz.title = "Quiz đã được cập nhật bằng code";
|
|
84
|
-
|
|
85
|
-
console.log(myQuiz);
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
#### **C. `services/QuizEngine.ts` - Nhạc trưởng Điều hành**
|
|
91
|
-
|
|
92
|
-
* **Vai trò:** Quản lý toàn bộ vòng đời của một **phiên làm bài quiz thực tế**. Đây là service dành cho khâu *thực thi (runtime)*.
|
|
93
|
-
* **Mô tả chi tiết:**
|
|
94
|
-
* Khởi tạo với một `QuizConfig` và một bộ `callbacks`. Thiết kế dựa trên callback này làm cho nó cực kỳ linh hoạt để tích hợp vào bất kỳ hệ thống nào.
|
|
95
|
-
* Nó quản lý trạng thái nội bộ: câu hỏi hiện tại, câu trả lời của người dùng, thời gian còn lại.
|
|
96
|
-
* Cung cấp các phương thức công khai (public methods) như `nextQuestion()`, `previousQuestion()`, `submitAnswer()`, và `calculateResults()` để điều khiển luồng làm bài.
|
|
97
|
-
* Logic chấm điểm phức tạp cho 12 loại câu hỏi được đóng gói hoàn toàn bên trong `evaluateQuestion`.
|
|
98
|
-
* **Tầm quan trọng:** **Cốt lõi (Core)** cho bất kỳ ứng dụng nào muốn cho người dùng *làm* bài quiz.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
#### **D. `ai/flows/` - Bộ não Sáng tạo**
|
|
103
|
-
|
|
104
|
-
* **Vai trò:** Cung cấp các hàm `async` để giao tiếp với AI và tự động tạo ra nội dung quiz.
|
|
105
|
-
* **Mô tả chi tiết:**
|
|
106
|
-
* Mỗi file là một "flow" có thể gọi được. Chúng nhận đầu vào có cấu trúc (nhờ `zod`) và trả về kết quả cũng có cấu trúc.
|
|
107
|
-
* **`generate...Question`:** Các flow tạo câu hỏi đơn lẻ.
|
|
108
|
-
* **`generateQuizPlan` & `generateQuestionsFromQuizPlan`:** Một quy trình 2 giai đoạn mạnh mẽ cho phép tạo ra một bài quiz hoàn chỉnh, cân bằng về chủ đề và độ khó, thay vì chỉ là một mớ câu hỏi ngẫu nhiên.
|
|
109
|
-
* **Tầm quan trọng:** **Tính năng nâng cao (Advanced Feature)**. Lý tưởng cho các hệ thống muốn tự động hóa việc tạo nội dung.
|
|
110
|
-
|
|
111
|
-
* **Ví dụ sử dụng (trong một script backend):**
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { generateQuizPlan, generateQuestionsFromQuizPlan } from 'interactive-quiz-kit/ai';
|
|
115
|
-
|
|
116
|
-
async function generateFullQuiz() {
|
|
117
|
-
const planInput = {
|
|
118
|
-
totalQuestions: 5,
|
|
119
|
-
topics: [{ topic: 'Lịch sử Việt Nam', ratio: 100 }],
|
|
120
|
-
bloomLevels: [{ level: 'remembering', ratio: 60 }, { level: 'understanding', ratio: 40 }],
|
|
121
|
-
selectedQuestionTypes: ['multiple_choice', 'true_false']
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const plan = await generateQuizPlan(planInput);
|
|
125
|
-
|
|
126
|
-
const quizResult = await generateQuestionsFromQuizPlan({ quizPlan: plan.quizPlan });
|
|
127
|
-
|
|
128
|
-
console.log(`${quizResult.generatedQuestions.length} câu hỏi đã được tạo.`);
|
|
129
|
-
// => Lưu các câu hỏi này vào DB
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
#### **E. `services/` (các file liên quan đến SCORM) & `utils/` - Hộp Dụng cụ & Giao tiếp**
|
|
136
|
-
|
|
137
|
-
* **Vai trò:** Cung cấp các công cụ chuyên biệt để hoàn thành các tác vụ cụ thể.
|
|
138
|
-
* **Mô tả chi tiết:**
|
|
139
|
-
* **`SCORMService.ts`:** Một lớp độc lập để nói chuyện với LMS. Nó không biết gì về quiz, chỉ biết về các lệnh SCORM. `QuizEngine` sẽ sử dụng nó.
|
|
140
|
-
* **`generateSCORMManifest`, `generateLauncherHTML`:** Các hàm này nhận dữ liệu và trả về các chuỗi văn bản (XML, HTML). Chúng không có tác dụng phụ (side-effect) và hoàn toàn "thuần khiết" (pure), rất phù hợp cho môi trường headless.
|
|
141
|
-
* **`idGenerators.ts`:** Cung cấp hàm `generateUniqueId`, một tiện ích nhỏ nhưng cần thiết khi bạn tạo dữ liệu mới.
|
|
142
|
-
* **Tầm quan trọng:** **Hỗ trợ (Supporting)**. Cần thiết cho các chức năng cụ thể như SCORM hoặc khi cần tạo dữ liệu mới.
|
|
143
|
-
|
|
144
|
-
Bằng cách kết hợp các thành phần này, bạn có thể xây dựng một hệ thống hoàn chỉnh ở phía backend để tạo, quản lý quiz, và thậm chí mô phỏng các phiên làm bài để kiểm thử mà không cần viết một dòng code giao diện nào.
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## II. Các Kiểu Dữ liệu (Types) - Nền tảng của Thư viện
|
|
149
|
-
|
|
150
|
-
Trước khi bắt đầu, bạn cần làm quen với các kiểu dữ liệu chính. Mọi tương tác với thư viện đều xoay quanh chúng.
|
|
43
|
+
Before you start, familiarize yourself with the main data types. All interactions with the library revolve around them.
|
|
151
44
|
|
|
152
45
|
```typescript
|
|
153
|
-
import { QuizConfig,
|
|
46
|
+
import { QuizConfig, TrueFalseQuestion } from '@thanh01.pmt/interactive-quiz-kit';
|
|
154
47
|
|
|
155
|
-
//
|
|
48
|
+
// Create a question
|
|
156
49
|
const myQuestion: TrueFalseQuestion = {
|
|
157
50
|
id: 'tf-001',
|
|
158
51
|
questionType: 'true_false',
|
|
159
|
-
prompt: '
|
|
52
|
+
prompt: 'The sun rises in the west.',
|
|
160
53
|
correctAnswer: false,
|
|
161
54
|
points: 10
|
|
162
55
|
};
|
|
163
56
|
|
|
164
|
-
//
|
|
57
|
+
// Create a complete quiz configuration
|
|
165
58
|
const myQuizConfig: QuizConfig = {
|
|
166
59
|
id: 'my-first-headless-quiz',
|
|
167
|
-
title: '
|
|
60
|
+
title: 'Fun Science Quiz',
|
|
168
61
|
questions: [myQuestion],
|
|
169
62
|
settings: {
|
|
170
63
|
shuffleQuestions: true,
|
|
@@ -173,87 +66,68 @@ const myQuizConfig: QuizConfig = {
|
|
|
173
66
|
};
|
|
174
67
|
```
|
|
175
68
|
|
|
176
|
-
**
|
|
177
|
-
|
|
178
|
-
* `QuizConfig`: Toàn bộ cấu hình quiz.
|
|
179
|
-
* `QuizQuestion`: Union type của tất cả các loại câu hỏi.
|
|
180
|
-
* Các kiểu câu hỏi cụ thể: `MultipleChoiceQuestion`, `FillInTheBlanksQuestion`, `BlocklyProgrammingQuestion`, v.v.
|
|
181
|
-
* `QuizResult`: Cấu trúc dữ liệu kết quả trả về.
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## III. Sử dụng Dịch vụ Lõi
|
|
69
|
+
**Most important types:** `QuizConfig`, `QuizQuestion`, `QuizResultType`, and specific question types like `MultipleChoiceQuestion`.
|
|
186
70
|
|
|
187
|
-
###
|
|
71
|
+
### 2. `QuizEditorService`: The Quiz Builder
|
|
188
72
|
|
|
189
|
-
|
|
73
|
+
This service allows you to perform CRUD (Create, Read, Update, Delete) operations on a `QuizConfig` object.
|
|
190
74
|
|
|
191
75
|
```typescript
|
|
192
|
-
import { QuizEditorService, emptyQuiz } from 'interactive-quiz-kit
|
|
193
|
-
import type {
|
|
76
|
+
import { QuizEditorService, emptyQuiz } from '@thanh01.pmt/interactive-quiz-kit';
|
|
77
|
+
import type { TrueFalseQuestion } from '@thanh01.pmt/interactive-quiz-kit';
|
|
194
78
|
|
|
195
|
-
//
|
|
79
|
+
// Start with an empty quiz
|
|
196
80
|
const editor = new QuizEditorService(emptyQuiz);
|
|
197
81
|
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
// Cập nhật thông tin cho câu hỏi
|
|
202
|
-
mcqTemplate.prompt = "Hành tinh nào gần mặt trời nhất?";
|
|
203
|
-
if (mcqTemplate.questionType === 'multiple_choice') {
|
|
204
|
-
mcqTemplate.options = [
|
|
205
|
-
{ id: 'opt1', text: 'Trái Đất' },
|
|
206
|
-
{ id: 'opt2', text: 'Sao Hỏa' },
|
|
207
|
-
{ id: 'opt3', text: 'Sao Thủy' },
|
|
208
|
-
];
|
|
209
|
-
mcqTemplate.correctAnswerId = 'opt3';
|
|
210
|
-
}
|
|
82
|
+
// Create a new True/False question template
|
|
83
|
+
const tfQuestion = QuizEditorService.createNewQuestionTemplate('true_false') as TrueFalseQuestion;
|
|
211
84
|
|
|
212
|
-
//
|
|
213
|
-
|
|
85
|
+
// Update the question's details
|
|
86
|
+
tfQuestion.prompt = "Is the Earth a perfect sphere?";
|
|
87
|
+
tfQuestion.correctAnswer = false;
|
|
214
88
|
|
|
215
|
-
//
|
|
216
|
-
|
|
89
|
+
// Add the question to the quiz
|
|
90
|
+
editor.addQuestion(tfQuestion);
|
|
217
91
|
|
|
218
|
-
//
|
|
92
|
+
// Get the final QuizConfig object
|
|
219
93
|
const finalQuizConfig = editor.getQuiz();
|
|
220
94
|
|
|
221
95
|
console.log(JSON.stringify(finalQuizConfig, null, 2));
|
|
222
96
|
```
|
|
223
97
|
|
|
224
|
-
###
|
|
98
|
+
### 3. `QuizEngine`: The Runtime Conductor
|
|
225
99
|
|
|
226
|
-
|
|
100
|
+
This service is the brain that processes the logic when a user is taking a quiz.
|
|
227
101
|
|
|
228
102
|
```typescript
|
|
229
|
-
import { QuizEngine } from 'interactive-quiz-kit
|
|
230
|
-
import type { QuizConfig,
|
|
103
|
+
import { QuizEngine } from '@thanh01.pmt/interactive-quiz-kit';
|
|
104
|
+
import type { QuizConfig, QuizResultType } from '@thanh01.pmt/interactive-quiz-kit';
|
|
231
105
|
|
|
232
|
-
//
|
|
106
|
+
// Assume you have a myQuizConfig object
|
|
233
107
|
const myQuizConfig: QuizConfig = /* ... */;
|
|
234
108
|
|
|
235
|
-
console.log("
|
|
109
|
+
console.log("Starting quiz...");
|
|
236
110
|
|
|
237
111
|
const engine = new QuizEngine({
|
|
238
112
|
config: myQuizConfig,
|
|
239
113
|
callbacks: {
|
|
240
114
|
onQuestionChange: (question, qNum, total) => {
|
|
241
|
-
console.log(`\n---
|
|
115
|
+
console.log(`\n--- Question ${qNum}/${total} ---`);
|
|
242
116
|
console.log(question?.prompt);
|
|
243
117
|
},
|
|
244
|
-
onQuizFinish: (result:
|
|
245
|
-
console.log("\n---
|
|
246
|
-
console.log(
|
|
247
|
-
console.log(`
|
|
248
|
-
console.log(`
|
|
118
|
+
onQuizFinish: (result: QuizResultType) => {
|
|
119
|
+
console.log("\n--- RESULTS ---");
|
|
120
|
+
console.log(`Score: ${result.score}/${result.maxScore}`);
|
|
121
|
+
console.log(`Percentage: ${result.percentage.toFixed(2)}%`);
|
|
122
|
+
console.log(`Status: ${result.passed ? 'Passed' : 'Failed'}`);
|
|
249
123
|
}
|
|
250
124
|
}
|
|
251
125
|
});
|
|
252
126
|
|
|
253
|
-
//
|
|
127
|
+
// Simulate the quiz-taking process
|
|
254
128
|
const question1 = engine.getCurrentQuestion();
|
|
255
129
|
if (question1) {
|
|
256
|
-
//
|
|
130
|
+
// Assuming question 1 is True/False
|
|
257
131
|
engine.submitAnswer(question1.id, false);
|
|
258
132
|
}
|
|
259
133
|
|
|
@@ -261,73 +135,112 @@ engine.nextQuestion();
|
|
|
261
135
|
|
|
262
136
|
const question2 = engine.getCurrentQuestion();
|
|
263
137
|
if (question2) {
|
|
264
|
-
//
|
|
265
|
-
engine.submitAnswer(question2.id, '
|
|
138
|
+
// Assuming question 2 is Multiple Choice
|
|
139
|
+
engine.submitAnswer(question2.id, 'id_of_the_correct_answer');
|
|
266
140
|
}
|
|
267
141
|
|
|
268
|
-
//
|
|
142
|
+
// Finish the quiz
|
|
269
143
|
engine.calculateResults();
|
|
270
144
|
```
|
|
271
145
|
|
|
146
|
+
### 4. `TopicDataService`: The Curriculum Manager
|
|
147
|
+
|
|
148
|
+
This service manages learning objective data imported from TSV files, powering the "Practice with AI" feature.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { TopicDataService } from '@thanh01.pmt/interactive-quiz-kit';
|
|
152
|
+
import type { LearningObjective } from '@thanh01.pmt/interactive-quiz-kit';
|
|
153
|
+
// In a browser environment:
|
|
154
|
+
// import tsvContent from './my_curriculum.tsv?raw';
|
|
155
|
+
|
|
156
|
+
// const { data, errors } = TopicDataService.parseTSV(tsvContent);
|
|
157
|
+
// if (errors.length === 0) {
|
|
158
|
+
// TopicDataService.saveData(data);
|
|
159
|
+
// }
|
|
160
|
+
|
|
161
|
+
// const subjects = TopicDataService.getSubjects();
|
|
162
|
+
// console.log('Available Subjects:', subjects);
|
|
163
|
+
```
|
|
164
|
+
|
|
272
165
|
---
|
|
273
166
|
|
|
274
|
-
##
|
|
167
|
+
## III. AI-Powered Content Generation
|
|
275
168
|
|
|
276
|
-
|
|
169
|
+
You can use the AI flows to automatically generate quiz content. These functions are `async`.
|
|
277
170
|
|
|
278
|
-
*
|
|
171
|
+
*Note: To use these, you need a configured Google Genkit environment and the necessary environment variables (e.g., `GOOGLE_API_KEY`).*
|
|
279
172
|
|
|
280
173
|
```typescript
|
|
281
|
-
import { generateMCQQuestion,
|
|
174
|
+
import { generateMCQQuestion, generatePracticeQuiz } from '@thanh01.pmt/interactive-quiz-kit/ai';
|
|
175
|
+
import type { LearningObjective } from '@thanh01.pmt/interactive-quiz-kit';
|
|
282
176
|
|
|
283
177
|
async function createAIQuestion() {
|
|
284
178
|
try {
|
|
285
|
-
console.log('
|
|
179
|
+
console.log('Generating a Multiple Choice question about the Solar System...');
|
|
286
180
|
const result = await generateMCQQuestion({
|
|
287
|
-
topic: '
|
|
288
|
-
difficulty: 'easy'
|
|
289
|
-
|
|
181
|
+
topic: 'The Solar System',
|
|
182
|
+
difficulty: 'easy',
|
|
183
|
+
language: 'English'
|
|
184
|
+
}, 'YOUR_API_KEY');
|
|
290
185
|
|
|
291
186
|
if (result.question) {
|
|
292
|
-
console.log('
|
|
293
|
-
console.log(JSON.stringify(result.question, null, 2));
|
|
294
|
-
} else {
|
|
295
|
-
console.log('AI không thể tạo câu hỏi.');
|
|
187
|
+
console.log('Generated Question:', JSON.stringify(result.question, null, 2));
|
|
296
188
|
}
|
|
297
|
-
|
|
298
189
|
} catch (error) {
|
|
299
|
-
console.error('
|
|
190
|
+
console.error('Error generating AI question:', error);
|
|
300
191
|
}
|
|
301
192
|
}
|
|
302
193
|
|
|
194
|
+
async function createAIPracticeQuiz() {
|
|
195
|
+
try {
|
|
196
|
+
// Assume 'selectedLOs' is an array of LearningObjective objects
|
|
197
|
+
const selectedLOs: LearningObjective[] = /* ... from TopicDataService ... */;
|
|
198
|
+
const result = await generatePracticeQuiz({
|
|
199
|
+
learningObjectives: selectedLOs,
|
|
200
|
+
quizDifficulty: 'Medium',
|
|
201
|
+
language: 'English'
|
|
202
|
+
}, 'YOUR_API_KEY');
|
|
203
|
+
|
|
204
|
+
console.log(`Generated ${result.generatedQuestions?.length || 0} practice questions.`);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error('Error generating practice quiz:', error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
303
210
|
// createAIQuestion();
|
|
304
211
|
```
|
|
305
212
|
|
|
306
213
|
---
|
|
307
214
|
|
|
308
|
-
##
|
|
215
|
+
## IV. Packaging and Publishing
|
|
309
216
|
|
|
310
|
-
|
|
217
|
+
These functions help create the components of a SCORM package. They return string content that you can write to files.
|
|
311
218
|
|
|
312
219
|
```typescript
|
|
313
|
-
import { generateSCORMManifest, generateLauncherHTML } from 'interactive-quiz-kit
|
|
314
|
-
import type { QuizConfig } from 'interactive-quiz-kit';
|
|
315
|
-
// import * as fs from 'fs'; //
|
|
220
|
+
import { generateSCORMManifest, generateLauncherHTML } from '@thanh01.pmt/interactive-quiz-kit';
|
|
221
|
+
import type { QuizConfig } from '@thanh01.pmt/interactive-quiz-kit';
|
|
222
|
+
// import * as fs from 'fs'; // For use in a Node.js environment
|
|
316
223
|
|
|
317
|
-
//
|
|
224
|
+
// Assume you have a myQuizConfig object
|
|
318
225
|
const myQuizConfig: QuizConfig = /* ... */;
|
|
319
226
|
|
|
320
|
-
//
|
|
227
|
+
// Generate manifest content
|
|
321
228
|
const manifestXML = generateSCORMManifest(myQuizConfig, "1.2");
|
|
322
229
|
console.log('--- imsmanifest.xml ---');
|
|
323
230
|
console.log(manifestXML);
|
|
324
231
|
// fs.writeFileSync('dist/imsmanifest.xml', manifestXML);
|
|
325
232
|
|
|
326
|
-
//
|
|
327
|
-
const launcherHTML = generateLauncherHTML(
|
|
233
|
+
// Generate HTML launcher content
|
|
234
|
+
const launcherHTML = generateLauncherHTML(
|
|
235
|
+
myQuizConfig,
|
|
236
|
+
'player.js', // Path to the JS bundle inside the ZIP
|
|
237
|
+
'quiz_data.json', // Path to the quiz data inside the ZIP
|
|
238
|
+
'blockly-styles.css', // Path to blockly css
|
|
239
|
+
'styles.css' // Path to main css
|
|
240
|
+
);
|
|
328
241
|
console.log('\n--- index.html ---');
|
|
329
242
|
console.log(launcherHTML);
|
|
330
243
|
// fs.writeFileSync('dist/index.html', launcherHTML);
|
|
331
244
|
```
|
|
332
245
|
|
|
333
|
-
**
|
|
246
|
+
**Note:** The `exportQuizAsSCORMZip` function is designed to run in the browser as it interacts with the DOM to trigger a file download. To package a ZIP on the backend, you would use a library like `jszip` and follow the steps outlined in `services/scormPackaging.ts`.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Interactive Quiz Kit
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@thanh01.pmt/interactive-quiz-kit)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
6
|
**Interactive Quiz Kit** is a comprehensive TypeScript library built with React, designed to effortlessly create, manage, play, and distribute interactive quizzes. It provides a robust core logic (`QuizEngine`), reusable React UI components, and support for a wide variety of question types.
|
|
@@ -9,43 +9,51 @@ The library is architected for easy extension and integration, featuring a power
|
|
|
9
9
|
|
|
10
10
|
## ✨ Features
|
|
11
11
|
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
* **Robust Core Engine**: Handles state management, navigation, automatic grading, and time tracking.
|
|
13
|
+
* **Rich Question Support**: Includes 12+ question types, from Multiple Choice to complex ones like Blockly/Scratch Programming and Hotspot.
|
|
14
|
+
* **Modular React UI Kit**: A full suite of components to build quiz players and authoring tools, organized into logical entry points.
|
|
15
|
+
* **Headless Mode**: Use all core logic, services, and AI flows independently of the React UI. Ideal for backend integrations, custom frontends, or automation scripts.
|
|
16
|
+
* **Advanced AI Capabilities**:
|
|
17
|
+
* **Single Question Generation**: Create individual questions based on topic, context, and difficulty.
|
|
18
|
+
* **Full Quiz Generation**: Generate entire, well-structured quizzes using a two-stage planning process.
|
|
19
|
+
* **AI-Powered Practice Mode**: Generate personalized 10-question quizzes on-the-fly from a user-provided curriculum.
|
|
20
|
+
* **Flexible Content Import**: Import curriculum data and learning objectives from TSV files to power the AI Practice Mode.
|
|
21
|
+
* **SCORM & Webhook Integration**:
|
|
22
|
+
* Package quizzes as SCORM 1.2 compliant ZIP files.
|
|
23
|
+
* Send detailed quiz results to a specified webhook URL.
|
|
24
24
|
|
|
25
25
|
## 🚀 Installation
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install @
|
|
28
|
+
npm install @thanh01.pmt/interactive-quiz-kit
|
|
29
29
|
# or
|
|
30
|
-
yarn add @
|
|
30
|
+
yarn add @thanh01.pmt/interactive-quiz-kit
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
**Peer Dependencies:** This library requires `react` and `react-dom` as peer dependencies. Ensure they are installed in your project.
|
|
34
34
|
|
|
35
35
|
## 📚 Usage
|
|
36
36
|
|
|
37
|
-
This library
|
|
37
|
+
This library is modular, allowing you to import only the parts you need.
|
|
38
38
|
|
|
39
|
-
###
|
|
39
|
+
### Understanding the Entry Points
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
The package is split into several entry points for optimized usage:
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
* `@thanh01.pmt/interactive-quiz-kit`: The core "headless" library. Contains all types, services (`QuizEngine`, `QuizEditorService`), and utilities. Use this for backend logic or custom UIs.
|
|
44
|
+
* `@thanh01.pmt/interactive-quiz-kit/react-ui`: General-purpose UI components like `<QuizPlayer>` and `<QuizResult>`.
|
|
45
|
+
* `@thanh01.pmt/interactive-quiz-kit/authoring`: The complete UI suite for creating and editing quizzes, centered around the `<QuizAuthoringTool>` component.
|
|
46
|
+
* `@thanh01.pmt/interactive-quiz-kit/ai`: The headless functions for AI-powered content generation.
|
|
47
|
+
* `@thanh01.pmt/interactive-quiz-kit/player`: A dedicated entry point for the player bundle, useful for SCORM packaging.
|
|
48
|
+
|
|
49
|
+
### 1. Displaying a Quiz (Player Mode)
|
|
50
|
+
|
|
51
|
+
Use the `<QuizPlayer>` component for the test-taker's experience.
|
|
44
52
|
|
|
45
53
|
```tsx
|
|
46
|
-
import React
|
|
47
|
-
import { QuizPlayer } from '@
|
|
48
|
-
import type { QuizConfig,
|
|
54
|
+
import React from 'react';
|
|
55
|
+
import { QuizPlayer } from '@thanh01.pmt/interactive-quiz-kit/react-ui';
|
|
56
|
+
import type { QuizConfig, QuizResultType } from '@thanh01.pmt/interactive-quiz-kit';
|
|
49
57
|
|
|
50
58
|
// 1. Define or load your quiz configuration
|
|
51
59
|
const myQuiz: QuizConfig = {
|
|
@@ -59,7 +67,7 @@ const myQuiz: QuizConfig = {
|
|
|
59
67
|
};
|
|
60
68
|
|
|
61
69
|
const MyQuizPage = () => {
|
|
62
|
-
const handleQuizComplete = (result:
|
|
70
|
+
const handleQuizComplete = (result: QuizResultType) => {
|
|
63
71
|
console.log("Quiz Complete!", result);
|
|
64
72
|
// Send result to your backend, or display a summary
|
|
65
73
|
};
|
|
@@ -75,14 +83,15 @@ const MyQuizPage = () => {
|
|
|
75
83
|
};
|
|
76
84
|
```
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
### 2. Creating Quizzes (Authoring Mode)
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
Use the `<QuizAuthoringTool>` for a complete content creation interface.
|
|
81
89
|
|
|
82
90
|
```tsx
|
|
83
91
|
import React from 'react';
|
|
84
|
-
import { QuizAuthoringTool
|
|
85
|
-
import
|
|
92
|
+
import { QuizAuthoringTool } from '@thanh01.pmt/interactive-quiz-kit/authoring';
|
|
93
|
+
import { emptyQuiz } from '@thanh01.pmt/interactive-quiz-kit';
|
|
94
|
+
import type { QuizConfig } from '@thanh01.pmt/interactive-quiz-kit';
|
|
86
95
|
|
|
87
96
|
const MyAuthoringPage = () => {
|
|
88
97
|
const handleSave = (quiz: QuizConfig) => {
|
|
@@ -100,80 +109,59 @@ const MyAuthoringPage = () => {
|
|
|
100
109
|
};
|
|
101
110
|
```
|
|
102
111
|
|
|
103
|
-
###
|
|
112
|
+
### 3. Using the "Practice with AI" Feature
|
|
104
113
|
|
|
105
|
-
|
|
114
|
+
The library includes a complete application flow for AI-powered practice sessions.
|
|
106
115
|
|
|
107
|
-
|
|
116
|
+
```tsx
|
|
117
|
+
import React from 'react';
|
|
118
|
+
// AppController is a conceptual component you would build
|
|
119
|
+
// using the blocks provided by the library.
|
|
120
|
+
import { AppController } from './components/AppController';
|
|
121
|
+
|
|
122
|
+
// This component would render the main menu, allowing users to switch
|
|
123
|
+
// between Authoring, Practice Mode, and Managing Topics.
|
|
124
|
+
const FullApplication = () => {
|
|
125
|
+
return <AppController />;
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Headless Mode (Core Logic Only)
|
|
130
|
+
|
|
131
|
+
Use the library's services in any JavaScript/TypeScript environment.
|
|
108
132
|
|
|
109
|
-
|
|
133
|
+
> For detailed examples and a full component list, see [**HEADLESS.md**](./HEADLESS.md).
|
|
110
134
|
|
|
111
|
-
|
|
135
|
+
#### Example: Creating a Quiz Programmatically
|
|
112
136
|
|
|
113
137
|
```typescript
|
|
114
|
-
import { QuizEditorService, emptyQuiz } from '@
|
|
115
|
-
import type { MultipleChoiceQuestion } from '@
|
|
138
|
+
import { QuizEditorService, emptyQuiz } from '@thanh01.pmt/interactive-quiz-kit';
|
|
139
|
+
import type { MultipleChoiceQuestion } from '@thanh01.pmt/interactive-quiz-kit';
|
|
116
140
|
|
|
117
141
|
// Start with an empty quiz config
|
|
118
142
|
const editor = new QuizEditorService(emptyQuiz);
|
|
119
143
|
|
|
120
|
-
// Create a question
|
|
144
|
+
// Create and populate a question
|
|
121
145
|
const mcqTemplate = QuizEditorService.createNewQuestionTemplate('multiple_choice') as MultipleChoiceQuestion;
|
|
122
|
-
|
|
123
|
-
// Populate the question details
|
|
124
146
|
mcqTemplate.prompt = "What is the capital of France?";
|
|
125
147
|
mcqTemplate.options = [
|
|
126
148
|
{ id: 'opt1', text: 'Berlin' },
|
|
127
149
|
{ id: 'opt2', text: 'Paris' },
|
|
128
|
-
{ id: 'opt3', text: 'Madrid' },
|
|
129
150
|
];
|
|
130
151
|
mcqTemplate.correctAnswerId = 'opt2';
|
|
131
152
|
|
|
132
|
-
// Add the question to the quiz
|
|
133
153
|
editor.addQuestion(mcqTemplate);
|
|
134
|
-
|
|
135
|
-
// Get the final, updated quiz configuration
|
|
136
154
|
const finalQuizConfig = editor.getQuiz();
|
|
137
155
|
|
|
138
|
-
// Now you can save `finalQuizConfig` to your database
|
|
139
156
|
console.log(finalQuizConfig);
|
|
140
157
|
```
|
|
141
158
|
|
|
142
|
-
#### Example: Running a Quiz Session with `QuizEngine`
|
|
143
|
-
|
|
144
|
-
This is useful for backend-driven quizzes or for running simulations.
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { QuizEngine } from '@thanh01pmt/interactive-quiz-kit';
|
|
148
|
-
import type { QuizConfig, QuizResult } from '@thanh01pmt/interactive-quiz-kit';
|
|
149
|
-
|
|
150
|
-
const myQuizConfig: QuizConfig = /* ... load your quiz config ... */;
|
|
151
|
-
|
|
152
|
-
const engine = new QuizEngine({
|
|
153
|
-
config: myQuizConfig,
|
|
154
|
-
callbacks: {
|
|
155
|
-
onQuizFinish: (result: QuizResult) => {
|
|
156
|
-
console.log(`Quiz finished! Final score: ${result.score}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Simulate answering the first question
|
|
162
|
-
const firstQuestion = engine.getCurrentQuestion();
|
|
163
|
-
if (firstQuestion) {
|
|
164
|
-
engine.submitAnswer(firstQuestion.id, 'id_of_the_user_answer');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Finalize the quiz
|
|
168
|
-
engine.calculateResults();
|
|
169
|
-
```
|
|
170
|
-
|
|
171
159
|
#### Example: Generating a Question with AI
|
|
172
160
|
|
|
173
|
-
|
|
161
|
+
*Requires Genkit environment setup.*
|
|
174
162
|
|
|
175
163
|
```typescript
|
|
176
|
-
import { generateTrueFalseQuestion } from '@
|
|
164
|
+
import { generateTrueFalseQuestion } from '@thanh01.pmt/interactive-quiz-kit/ai';
|
|
177
165
|
|
|
178
166
|
async function createNewQuestion() {
|
|
179
167
|
try {
|
|
@@ -184,7 +172,6 @@ async function createNewQuestion() {
|
|
|
184
172
|
|
|
185
173
|
if (question) {
|
|
186
174
|
console.log("AI-generated question:", question);
|
|
187
|
-
// Save to your database
|
|
188
175
|
}
|
|
189
176
|
} catch (error) {
|
|
190
177
|
console.error("Failed to generate question:", error);
|
|
@@ -195,35 +182,36 @@ async function createNewQuestion() {
|
|
|
195
182
|
## Supported Question Types
|
|
196
183
|
|
|
197
184
|
The library supports a wide array of 12+ question types, including:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
185
|
+
|
|
186
|
+
* `multiple_choice`
|
|
187
|
+
* `multiple_response`
|
|
188
|
+
* `true_false`
|
|
189
|
+
* `short_answer`
|
|
190
|
+
* `numeric`
|
|
191
|
+
* `fill_in_the_blanks`
|
|
192
|
+
* `sequence`
|
|
193
|
+
* `matching`
|
|
194
|
+
* `drag_and_drop`
|
|
195
|
+
* `hotspot`
|
|
196
|
+
* `blockly_programming`
|
|
197
|
+
* `scratch_programming`
|
|
210
198
|
|
|
211
199
|
## Known Issues
|
|
212
200
|
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
201
|
+
* **Asset Integration for Programming Questions**: Using `Blockly` or `Scratch` questions requires manually copying assets (`js`, `css`, `media`) from their respective `node_modules` folders into your project's `public` directory for the UI components to render correctly.
|
|
202
|
+
* **SCORM Packaging**: The SCORM export function relies on fetching the player assets (`player.js`, `styles.css`) from your `public` folder at the time of packaging. Ensure these files are present and accessible.
|
|
203
|
+
* **AI Generation Limits**: AI generation is not yet supported for visually complex types like `Drag and Drop`, `Hotspot`, `Blockly`, or `Scratch`.
|
|
216
204
|
|
|
217
205
|
## 🤝 Contributing
|
|
218
206
|
|
|
219
207
|
Contributions are welcome! Please feel free to submit a pull request or open an issue if you have ideas for improvements or find a bug.
|
|
220
208
|
|
|
221
|
-
1.
|
|
222
|
-
2.
|
|
223
|
-
3.
|
|
224
|
-
4.
|
|
225
|
-
5.
|
|
209
|
+
1. Fork the repository.
|
|
210
|
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`).
|
|
211
|
+
3. Commit your changes (`git commit -m 'feat: Add some AmazingFeature'`).
|
|
212
|
+
4. Push to the branch (`git push origin feature/AmazingFeature`).
|
|
213
|
+
5. Open a Pull Request.
|
|
226
214
|
|
|
227
215
|
## 📄 License
|
|
228
216
|
|
|
229
|
-
This project is licensed under the MIT License
|
|
217
|
+
This project is licensed under the MIT License.
|
package/package.json
CHANGED