@octo-cyber/quiz-engine 0.5.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/controllers/attempt.controller.d.ts +3 -0
- package/dist/controllers/attempt.controller.d.ts.map +1 -0
- package/dist/controllers/attempt.controller.js +83 -0
- package/dist/controllers/attempt.controller.js.map +1 -0
- package/dist/controllers/category.controller.d.ts +3 -0
- package/dist/controllers/category.controller.d.ts.map +1 -0
- package/dist/controllers/category.controller.js +38 -0
- package/dist/controllers/category.controller.js.map +1 -0
- package/dist/controllers/exam.controller.d.ts +3 -0
- package/dist/controllers/exam.controller.d.ts.map +1 -0
- package/dist/controllers/exam.controller.js +48 -0
- package/dist/controllers/exam.controller.js.map +1 -0
- package/dist/controllers/index.d.ts +7 -0
- package/dist/controllers/index.d.ts.map +1 -0
- package/dist/controllers/index.js +16 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/controllers/paper.controller.d.ts +3 -0
- package/dist/controllers/paper.controller.d.ts.map +1 -0
- package/dist/controllers/paper.controller.js +70 -0
- package/dist/controllers/paper.controller.js.map +1 -0
- package/dist/controllers/question.controller.d.ts +3 -0
- package/dist/controllers/question.controller.d.ts.map +1 -0
- package/dist/controllers/question.controller.js +59 -0
- package/dist/controllers/question.controller.js.map +1 -0
- package/dist/controllers/statistics.controller.d.ts +3 -0
- package/dist/controllers/statistics.controller.d.ts.map +1 -0
- package/dist/controllers/statistics.controller.js +37 -0
- package/dist/controllers/statistics.controller.js.map +1 -0
- package/dist/entities/index.d.ts +24 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +43 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/quiz-answer.entity.d.ts +19 -0
- package/dist/entities/quiz-answer.entity.d.ts.map +1 -0
- package/dist/entities/quiz-answer.entity.js +81 -0
- package/dist/entities/quiz-answer.entity.js.map +1 -0
- package/dist/entities/quiz-attempt.entity.d.ts +17 -0
- package/dist/entities/quiz-attempt.entity.d.ts.map +1 -0
- package/dist/entities/quiz-attempt.entity.js +80 -0
- package/dist/entities/quiz-attempt.entity.js.map +1 -0
- package/dist/entities/quiz-category.entity.d.ts +10 -0
- package/dist/entities/quiz-category.entity.d.ts.map +1 -0
- package/dist/entities/quiz-category.entity.js +55 -0
- package/dist/entities/quiz-category.entity.js.map +1 -0
- package/dist/entities/quiz-exam.entity.d.ts +25 -0
- package/dist/entities/quiz-exam.entity.d.ts.map +1 -0
- package/dist/entities/quiz-exam.entity.js +99 -0
- package/dist/entities/quiz-exam.entity.js.map +1 -0
- package/dist/entities/quiz-paper-question.entity.d.ts +12 -0
- package/dist/entities/quiz-paper-question.entity.d.ts.map +1 -0
- package/dist/entities/quiz-paper-question.entity.js +58 -0
- package/dist/entities/quiz-paper-question.entity.js.map +1 -0
- package/dist/entities/quiz-paper.entity.d.ts +18 -0
- package/dist/entities/quiz-paper.entity.d.ts.map +1 -0
- package/dist/entities/quiz-paper.entity.js +75 -0
- package/dist/entities/quiz-paper.entity.js.map +1 -0
- package/dist/entities/quiz-question.entity.d.ts +28 -0
- package/dist/entities/quiz-question.entity.d.ts.map +1 -0
- package/dist/entities/quiz-question.entity.js +107 -0
- package/dist/entities/quiz-question.entity.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/quiz-engine.module.d.ts +8 -0
- package/dist/quiz-engine.module.d.ts.map +1 -0
- package/dist/quiz-engine.module.js +45 -0
- package/dist/quiz-engine.module.js.map +1 -0
- package/dist/schemas/attempt.schema.d.ts +47 -0
- package/dist/schemas/attempt.schema.d.ts.map +1 -0
- package/dist/schemas/attempt.schema.js +19 -0
- package/dist/schemas/attempt.schema.js.map +1 -0
- package/dist/schemas/category.schema.d.ts +36 -0
- package/dist/schemas/category.schema.d.ts.map +1 -0
- package/dist/schemas/category.schema.js +12 -0
- package/dist/schemas/category.schema.js.map +1 -0
- package/dist/schemas/exam.schema.d.ts +70 -0
- package/dist/schemas/exam.schema.d.ts.map +1 -0
- package/dist/schemas/exam.schema.js +20 -0
- package/dist/schemas/exam.schema.js.map +1 -0
- package/dist/schemas/paper.schema.d.ts +71 -0
- package/dist/schemas/paper.schema.d.ts.map +1 -0
- package/dist/schemas/paper.schema.js +26 -0
- package/dist/schemas/paper.schema.js.map +1 -0
- package/dist/schemas/question.schema.d.ts +147 -0
- package/dist/schemas/question.schema.d.ts.map +1 -0
- package/dist/schemas/question.schema.js +32 -0
- package/dist/schemas/question.schema.js.map +1 -0
- package/dist/services/attempt.service.d.ts +33 -0
- package/dist/services/attempt.service.d.ts.map +1 -0
- package/dist/services/attempt.service.js +197 -0
- package/dist/services/attempt.service.js.map +1 -0
- package/dist/services/category.service.d.ts +14 -0
- package/dist/services/category.service.d.ts.map +1 -0
- package/dist/services/category.service.js +74 -0
- package/dist/services/category.service.js.map +1 -0
- package/dist/services/exam.service.d.ts +17 -0
- package/dist/services/exam.service.d.ts.map +1 -0
- package/dist/services/exam.service.js +92 -0
- package/dist/services/exam.service.js.map +1 -0
- package/dist/services/grade.service.d.ts +16 -0
- package/dist/services/grade.service.d.ts.map +1 -0
- package/dist/services/grade.service.js +75 -0
- package/dist/services/grade.service.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +18 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/paper.service.d.ts +32 -0
- package/dist/services/paper.service.d.ts.map +1 -0
- package/dist/services/paper.service.js +157 -0
- package/dist/services/paper.service.js.map +1 -0
- package/dist/services/question.service.d.ts +30 -0
- package/dist/services/question.service.d.ts.map +1 -0
- package/dist/services/question.service.js +155 -0
- package/dist/services/question.service.js.map +1 -0
- package/dist/services/statistics.service.d.ts +43 -0
- package/dist/services/statistics.service.d.ts.map +1 -0
- package/dist/services/statistics.service.js +134 -0
- package/dist/services/statistics.service.js.map +1 -0
- package/package.json +85 -0
- package/web/index.ts +51 -0
- package/web/manifest.ts +36 -0
- package/web/messages/en-US.json +143 -0
- package/web/messages/zh-CN.json +143 -0
- package/web/pages/ExamRoomPage.tsx +289 -0
- package/web/pages/ExamsPage.tsx +248 -0
- package/web/pages/PapersPage.tsx +202 -0
- package/web/pages/QuestionBankPage.tsx +263 -0
- package/web/pages/StatisticsPage.tsx +178 -0
- package/web/services/attempt-service.ts +53 -0
- package/web/services/category-service.ts +26 -0
- package/web/services/exam-service.ts +31 -0
- package/web/services/paper-service.ts +50 -0
- package/web/services/question-service.ts +36 -0
- package/web/services/statistics-service.ts +28 -0
- package/web/stores/quiz-store.ts +31 -0
- package/web/types/quiz.ts +166 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.StatisticsService = void 0;
|
|
10
|
+
const core_1 = require("@octo-cyber/core");
|
|
11
|
+
const quiz_attempt_entity_js_1 = require("../entities/quiz-attempt.entity.js");
|
|
12
|
+
const quiz_answer_entity_js_1 = require("../entities/quiz-answer.entity.js");
|
|
13
|
+
const quiz_exam_entity_js_1 = require("../entities/quiz-exam.entity.js");
|
|
14
|
+
const quiz_paper_entity_js_1 = require("../entities/quiz-paper.entity.js");
|
|
15
|
+
let StatisticsService = class StatisticsService {
|
|
16
|
+
attemptRepo;
|
|
17
|
+
answerRepo;
|
|
18
|
+
examRepo;
|
|
19
|
+
paperRepo;
|
|
20
|
+
initialized = false;
|
|
21
|
+
initialize() {
|
|
22
|
+
const db = core_1.Container.get(core_1.DatabaseService);
|
|
23
|
+
this.attemptRepo = db.getRepository(quiz_attempt_entity_js_1.QuizAttempt);
|
|
24
|
+
this.answerRepo = db.getRepository(quiz_answer_entity_js_1.QuizAnswer);
|
|
25
|
+
this.examRepo = db.getRepository(quiz_exam_entity_js_1.QuizExam);
|
|
26
|
+
this.paperRepo = db.getRepository(quiz_paper_entity_js_1.QuizPaper);
|
|
27
|
+
this.initialized = true;
|
|
28
|
+
}
|
|
29
|
+
ensureInitialized() {
|
|
30
|
+
if (!this.initialized) {
|
|
31
|
+
try {
|
|
32
|
+
this.initialize();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new core_1.AppError('StatisticsService not initialized', 500);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async getExamStats(examId) {
|
|
40
|
+
this.ensureInitialized();
|
|
41
|
+
const exam = await this.examRepo.findOne({ where: { id: examId } });
|
|
42
|
+
if (!exam)
|
|
43
|
+
throw core_1.AppError.notFound('Exam not found');
|
|
44
|
+
const paper = await this.paperRepo.findOne({ where: { id: exam.paperId } });
|
|
45
|
+
const passingScore = paper?.passingScore ?? 60;
|
|
46
|
+
const attempts = await this.attemptRepo.find({ where: { examId } });
|
|
47
|
+
const graded = attempts.filter((a) => a.status === 'GRADED');
|
|
48
|
+
const submitted = attempts.filter((a) => a.status === 'SUBMITTED' || a.status === 'GRADED');
|
|
49
|
+
const scores = graded.map((a) => a.totalScore);
|
|
50
|
+
const averageScore = scores.length > 0
|
|
51
|
+
? scores.reduce((s, v) => s + v, 0) / scores.length
|
|
52
|
+
: 0;
|
|
53
|
+
const highestScore = scores.length > 0 ? Math.max(...scores) : 0;
|
|
54
|
+
const lowestScore = scores.length > 0 ? Math.min(...scores) : 0;
|
|
55
|
+
const passCount = graded.filter((a) => a.isPassed || a.totalScore >= passingScore).length;
|
|
56
|
+
const passRate = graded.length > 0 ? passCount / graded.length : 0;
|
|
57
|
+
const distribution = this.buildScoreDistribution(scores, paper?.totalScore ?? 100);
|
|
58
|
+
return {
|
|
59
|
+
examId,
|
|
60
|
+
title: exam.title,
|
|
61
|
+
totalAttempts: attempts.length,
|
|
62
|
+
submittedAttempts: submitted.length,
|
|
63
|
+
gradedAttempts: graded.length,
|
|
64
|
+
averageScore: Math.round(averageScore * 10) / 10,
|
|
65
|
+
highestScore,
|
|
66
|
+
lowestScore,
|
|
67
|
+
passRate: Math.round(passRate * 100) / 100,
|
|
68
|
+
scoreDistribution: distribution,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async getUserStats(userId) {
|
|
72
|
+
this.ensureInitialized();
|
|
73
|
+
const attempts = await this.attemptRepo.find({
|
|
74
|
+
where: { userId },
|
|
75
|
+
order: { createdAt: 'DESC' },
|
|
76
|
+
});
|
|
77
|
+
const graded = attempts.filter((a) => a.status === 'GRADED');
|
|
78
|
+
const scores = graded.map((a) => a.totalScore);
|
|
79
|
+
const averageScore = scores.length > 0
|
|
80
|
+
? scores.reduce((s, v) => s + v, 0) / scores.length
|
|
81
|
+
: 0;
|
|
82
|
+
const highestScore = scores.length > 0 ? Math.max(...scores) : 0;
|
|
83
|
+
const passCount = graded.filter((a) => a.isPassed).length;
|
|
84
|
+
return {
|
|
85
|
+
userId,
|
|
86
|
+
totalAttempts: attempts.length,
|
|
87
|
+
averageScore: Math.round(averageScore * 10) / 10,
|
|
88
|
+
highestScore,
|
|
89
|
+
passCount,
|
|
90
|
+
passRate: graded.length > 0 ? Math.round((passCount / graded.length) * 100) / 100 : 0,
|
|
91
|
+
recentAttempts: attempts.slice(0, 10),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async getRanking(examId, limit = 20) {
|
|
95
|
+
this.ensureInitialized();
|
|
96
|
+
const attempts = await this.attemptRepo.find({
|
|
97
|
+
where: { examId, status: 'GRADED' },
|
|
98
|
+
order: { totalScore: 'DESC' },
|
|
99
|
+
});
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const ranked = [];
|
|
102
|
+
let rank = 1;
|
|
103
|
+
for (const a of attempts) {
|
|
104
|
+
if (!seen.has(a.userId)) {
|
|
105
|
+
seen.add(a.userId);
|
|
106
|
+
ranked.push({ userId: a.userId, score: a.totalScore, rank: rank++ });
|
|
107
|
+
}
|
|
108
|
+
if (ranked.length >= limit)
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
return ranked;
|
|
112
|
+
}
|
|
113
|
+
buildScoreDistribution(scores, totalScore) {
|
|
114
|
+
const buckets = [
|
|
115
|
+
{ label: '0-59', min: 0, max: 59 },
|
|
116
|
+
{ label: '60-69', min: 60, max: 69 },
|
|
117
|
+
{ label: '70-79', min: 70, max: 79 },
|
|
118
|
+
{ label: '80-89', min: 80, max: 89 },
|
|
119
|
+
{ label: '90-100', min: 90, max: 100 },
|
|
120
|
+
];
|
|
121
|
+
return buckets.map(({ label, min, max }) => ({
|
|
122
|
+
range: label,
|
|
123
|
+
count: scores.filter((s) => {
|
|
124
|
+
const pct = totalScore > 0 ? (s / totalScore) * 100 : 0;
|
|
125
|
+
return pct >= min && pct <= max;
|
|
126
|
+
}).length,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
exports.StatisticsService = StatisticsService;
|
|
131
|
+
exports.StatisticsService = StatisticsService = __decorate([
|
|
132
|
+
(0, core_1.Service)()
|
|
133
|
+
], StatisticsService);
|
|
134
|
+
//# sourceMappingURL=statistics.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statistics.service.js","sourceRoot":"","sources":["../../src/services/statistics.service.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octo-cyber/quiz-engine",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Octo quiz engine module — question bank, smart paper generation, online exam, auto grading, statistics",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./web": {
|
|
14
|
+
"types": "./web/index.ts",
|
|
15
|
+
"import": "./web/index.ts",
|
|
16
|
+
"default": "./web/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"./web/pages/*": {
|
|
19
|
+
"types": "./web/pages/*.tsx",
|
|
20
|
+
"import": "./web/pages/*.tsx",
|
|
21
|
+
"default": "./web/pages/*.tsx"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"zod": "^3.24.0",
|
|
26
|
+
"@octo-cyber/core": "^0.5.4",
|
|
27
|
+
"@octo-cyber/auth": "^0.5.6"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": "^19.0.0",
|
|
31
|
+
"react-dom": "^19.0.0",
|
|
32
|
+
"next-intl": "^4.0.0",
|
|
33
|
+
"lucide-react": ">=0.400.0",
|
|
34
|
+
"sonner": "^2.0.0",
|
|
35
|
+
"zustand": "^5.0.0",
|
|
36
|
+
"@octo-cyber/ui": "^0.5.3",
|
|
37
|
+
"@octo-cyber/layout": "^0.5.2"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@octo-cyber/ui": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"@octo-cyber/layout": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"react": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"react-dom": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"next-intl": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"lucide-react": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"sonner": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"zustand": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/react": "^19",
|
|
67
|
+
"@types/react-dom": "^19",
|
|
68
|
+
"typescript": "^5.8.0",
|
|
69
|
+
"vitest": "^3.0.0"
|
|
70
|
+
},
|
|
71
|
+
"files": [
|
|
72
|
+
"dist",
|
|
73
|
+
"web"
|
|
74
|
+
],
|
|
75
|
+
"publishConfig": {
|
|
76
|
+
"registry": "https://registry.npmjs.org/"
|
|
77
|
+
},
|
|
78
|
+
"license": "MIT",
|
|
79
|
+
"scripts": {
|
|
80
|
+
"build": "tsc -p tsconfig.build.json",
|
|
81
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
82
|
+
"test": "vitest run",
|
|
83
|
+
"typecheck": "tsc --noEmit"
|
|
84
|
+
}
|
|
85
|
+
}
|
package/web/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octo-cyber/quiz-engine — Frontend entry point.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Manifest
|
|
6
|
+
export { quizEngineFrontendManifest } from './manifest'
|
|
7
|
+
|
|
8
|
+
// Pages
|
|
9
|
+
export { default as QuestionBankPage } from './pages/QuestionBankPage'
|
|
10
|
+
export { default as PapersPage } from './pages/PapersPage'
|
|
11
|
+
export { default as ExamsPage } from './pages/ExamsPage'
|
|
12
|
+
export { default as ExamRoomPage } from './pages/ExamRoomPage'
|
|
13
|
+
export { default as StatisticsPage } from './pages/StatisticsPage'
|
|
14
|
+
|
|
15
|
+
// Services
|
|
16
|
+
export { categoryApi } from './services/category-service'
|
|
17
|
+
export { questionApi } from './services/question-service'
|
|
18
|
+
export { paperApi } from './services/paper-service'
|
|
19
|
+
export { examApi } from './services/exam-service'
|
|
20
|
+
export { attemptApi } from './services/attempt-service'
|
|
21
|
+
export { statisticsApi } from './services/statistics-service'
|
|
22
|
+
|
|
23
|
+
// Store
|
|
24
|
+
export { useQuizStore } from './stores/quiz-store'
|
|
25
|
+
|
|
26
|
+
// Types
|
|
27
|
+
export type {
|
|
28
|
+
QuizCategory,
|
|
29
|
+
QuizQuestion,
|
|
30
|
+
QuestionType,
|
|
31
|
+
QuizOption,
|
|
32
|
+
QuizPaper,
|
|
33
|
+
PaperStatus,
|
|
34
|
+
QuizPaperQuestion,
|
|
35
|
+
PaperWithQuestions,
|
|
36
|
+
QuizExam,
|
|
37
|
+
ExamStatus,
|
|
38
|
+
QuizAttempt,
|
|
39
|
+
AttemptStatus,
|
|
40
|
+
QuizAnswer,
|
|
41
|
+
AttemptDetail,
|
|
42
|
+
PaginatedResponse,
|
|
43
|
+
ExamStats,
|
|
44
|
+
UserStats,
|
|
45
|
+
RankingEntry,
|
|
46
|
+
SmartGenerateRule,
|
|
47
|
+
} from './types/quiz'
|
|
48
|
+
|
|
49
|
+
// i18n messages
|
|
50
|
+
export { default as quizEngineMessagesZhCN } from './messages/zh-CN.json'
|
|
51
|
+
export { default as quizEngineMessagesEnUS } from './messages/en-US.json'
|
package/web/manifest.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { IFrontendManifest } from '@octo-cyber/core';
|
|
2
|
+
|
|
3
|
+
export const quizEngineFrontendManifest: IFrontendManifest = {
|
|
4
|
+
moduleId: 'quiz-engine',
|
|
5
|
+
icon: 'GraduationCap',
|
|
6
|
+
color: 'violet',
|
|
7
|
+
position: 'main',
|
|
8
|
+
titleKey: 'quizEngine.title',
|
|
9
|
+
descriptionKey: 'quizEngine.description',
|
|
10
|
+
pages: [
|
|
11
|
+
{
|
|
12
|
+
path: '/quiz/questions',
|
|
13
|
+
titleKey: 'quizEngine.pages.questionBank',
|
|
14
|
+
icon: 'BookOpen',
|
|
15
|
+
component: '@octo-cyber/quiz-engine/web/pages/QuestionBankPage',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
path: '/quiz/papers',
|
|
19
|
+
titleKey: 'quizEngine.pages.papers',
|
|
20
|
+
icon: 'FileText',
|
|
21
|
+
component: '@octo-cyber/quiz-engine/web/pages/PapersPage',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: '/quiz/exams',
|
|
25
|
+
titleKey: 'quizEngine.pages.exams',
|
|
26
|
+
icon: 'ClipboardList',
|
|
27
|
+
component: '@octo-cyber/quiz-engine/web/pages/ExamsPage',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
path: '/quiz/stats',
|
|
31
|
+
titleKey: 'quizEngine.pages.statistics',
|
|
32
|
+
icon: 'BarChart2',
|
|
33
|
+
component: '@octo-cyber/quiz-engine/web/pages/StatisticsPage',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"quizEngine": {
|
|
3
|
+
"title": "Online Exam",
|
|
4
|
+
"description": "Question bank, paper builder, online exams and result statistics",
|
|
5
|
+
"pages": {
|
|
6
|
+
"questionBank": "Question Bank",
|
|
7
|
+
"papers": "Papers",
|
|
8
|
+
"exams": "Exams",
|
|
9
|
+
"statistics": "Statistics"
|
|
10
|
+
},
|
|
11
|
+
"question": {
|
|
12
|
+
"title": "Question Bank",
|
|
13
|
+
"description": "Manage questions: single choice, multiple choice, true/false, fill-blank, essay",
|
|
14
|
+
"create": "New Question",
|
|
15
|
+
"createTitle": "New Question",
|
|
16
|
+
"searchPlaceholder": "Search question content...",
|
|
17
|
+
"filterType": "Type",
|
|
18
|
+
"filterCategory": "Category",
|
|
19
|
+
"filterDifficulty": "Difficulty",
|
|
20
|
+
"noData": "No questions yet",
|
|
21
|
+
"createSuccess": "Question created",
|
|
22
|
+
"createFailed": "Failed to create question",
|
|
23
|
+
"deleteSuccess": "Question deleted",
|
|
24
|
+
"deleteFailed": "Failed to delete question",
|
|
25
|
+
"contentPlaceholder": "Enter question content (Markdown supported)...",
|
|
26
|
+
"types": {
|
|
27
|
+
"single_choice": "Single",
|
|
28
|
+
"multiple_choice": "Multiple",
|
|
29
|
+
"true_false": "T/F",
|
|
30
|
+
"fill_blank": "Fill",
|
|
31
|
+
"essay": "Essay"
|
|
32
|
+
},
|
|
33
|
+
"col": {
|
|
34
|
+
"type": "Type",
|
|
35
|
+
"content": "Content",
|
|
36
|
+
"difficulty": "Difficulty",
|
|
37
|
+
"score": "Score",
|
|
38
|
+
"useCount": "Used"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"paper": {
|
|
42
|
+
"title": "Papers",
|
|
43
|
+
"description": "Create and manage exam papers",
|
|
44
|
+
"create": "New Paper",
|
|
45
|
+
"createTitle": "New Paper",
|
|
46
|
+
"titlePlaceholder": "Enter paper title...",
|
|
47
|
+
"noData": "No papers yet",
|
|
48
|
+
"createSuccess": "Paper created",
|
|
49
|
+
"createFailed": "Failed to create paper",
|
|
50
|
+
"deleteSuccess": "Paper deleted",
|
|
51
|
+
"deleteFailed": "Failed to delete paper",
|
|
52
|
+
"publishSuccess": "Paper published",
|
|
53
|
+
"publishFailed": "Failed to publish paper",
|
|
54
|
+
"publish": "Publish",
|
|
55
|
+
"titleRequired": "Please enter a title",
|
|
56
|
+
"totalScore": "Total",
|
|
57
|
+
"passingScore": "Pass",
|
|
58
|
+
"timeLimit": "Time",
|
|
59
|
+
"statuses": {
|
|
60
|
+
"draft": "Draft",
|
|
61
|
+
"published": "Published",
|
|
62
|
+
"archived": "Archived"
|
|
63
|
+
},
|
|
64
|
+
"col": {
|
|
65
|
+
"title": "Title",
|
|
66
|
+
"description": "Description"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"exam": {
|
|
70
|
+
"title": "Exams",
|
|
71
|
+
"description": "Create exam sessions with time windows and rules",
|
|
72
|
+
"create": "New Exam",
|
|
73
|
+
"createTitle": "New Exam",
|
|
74
|
+
"noData": "No exams yet",
|
|
75
|
+
"createSuccess": "Exam created",
|
|
76
|
+
"createFailed": "Failed to create exam",
|
|
77
|
+
"deleteSuccess": "Exam deleted",
|
|
78
|
+
"deleteFailed": "Failed to delete exam",
|
|
79
|
+
"statusUpdated": "Status updated",
|
|
80
|
+
"updateFailed": "Update failed",
|
|
81
|
+
"formRequired": "Please fill required fields",
|
|
82
|
+
"selectPaper": "Select a paper",
|
|
83
|
+
"maxAttempts": "Max attempts",
|
|
84
|
+
"shuffleQuestions": "Shuffle questions",
|
|
85
|
+
"showAnswerAfterSubmit": "Show answers after submit",
|
|
86
|
+
"startTime": "Start time",
|
|
87
|
+
"endTime": "End time",
|
|
88
|
+
"schedule": "Schedule",
|
|
89
|
+
"start": "Start",
|
|
90
|
+
"end": "End",
|
|
91
|
+
"statuses": {
|
|
92
|
+
"draft": "Draft",
|
|
93
|
+
"scheduled": "Scheduled",
|
|
94
|
+
"ongoing": "Ongoing",
|
|
95
|
+
"ended": "Ended",
|
|
96
|
+
"cancelled": "Cancelled"
|
|
97
|
+
},
|
|
98
|
+
"col": {
|
|
99
|
+
"title": "Title",
|
|
100
|
+
"paper": "Paper",
|
|
101
|
+
"description": "Description"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"examRoom": {
|
|
105
|
+
"startFailed": "Failed to start exam, please try again",
|
|
106
|
+
"loadFailed": "Failed to load exam",
|
|
107
|
+
"submitted": "Exam submitted",
|
|
108
|
+
"yourScore": "Your score",
|
|
109
|
+
"passed": "Passed",
|
|
110
|
+
"failed": "Failed",
|
|
111
|
+
"question": "Q",
|
|
112
|
+
"score": "Score",
|
|
113
|
+
"answered": "Answered {count}/{total}",
|
|
114
|
+
"submit": "Submit",
|
|
115
|
+
"submitSuccess": "Submitted! Score: {score}",
|
|
116
|
+
"submitFailed": "Submit failed, please try again",
|
|
117
|
+
"confirmSubmitTitle": "Confirm Submit",
|
|
118
|
+
"confirmSubmitDesc": "You answered {answered}/{total} questions. Submit now?",
|
|
119
|
+
"trueLabel": "True",
|
|
120
|
+
"falseLabel": "False",
|
|
121
|
+
"fillBlankPlaceholder": "Fill in your answer...",
|
|
122
|
+
"essayPlaceholder": "Write your answer..."
|
|
123
|
+
},
|
|
124
|
+
"statistics": {
|
|
125
|
+
"title": "Statistics",
|
|
126
|
+
"description": "View exam statistics, score distribution and ranking",
|
|
127
|
+
"selectExam": "Select exam",
|
|
128
|
+
"selectExamHint": "Select an exam to view statistics",
|
|
129
|
+
"totalAttempts": "Participants",
|
|
130
|
+
"gradedAttempts": "Graded",
|
|
131
|
+
"averageScore": "Average",
|
|
132
|
+
"passRate": "Pass rate",
|
|
133
|
+
"highestScore": "Highest",
|
|
134
|
+
"lowestScore": "Lowest",
|
|
135
|
+
"scoreDistribution": "Score distribution",
|
|
136
|
+
"ranking": "Ranking",
|
|
137
|
+
"noRanking": "No ranking data",
|
|
138
|
+
"rank": "Rank",
|
|
139
|
+
"userId": "User",
|
|
140
|
+
"score": "Score"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"quizEngine": {
|
|
3
|
+
"title": "在线考试",
|
|
4
|
+
"description": "题库管理、试卷组卷、在线考试与成绩统计",
|
|
5
|
+
"pages": {
|
|
6
|
+
"questionBank": "题库",
|
|
7
|
+
"papers": "试卷",
|
|
8
|
+
"exams": "考试",
|
|
9
|
+
"statistics": "成绩统计"
|
|
10
|
+
},
|
|
11
|
+
"question": {
|
|
12
|
+
"title": "题库管理",
|
|
13
|
+
"description": "管理题目,支持单选、多选、判断、填空、简答",
|
|
14
|
+
"create": "新建题目",
|
|
15
|
+
"createTitle": "新建题目",
|
|
16
|
+
"searchPlaceholder": "搜索题目内容...",
|
|
17
|
+
"filterType": "题型",
|
|
18
|
+
"filterCategory": "分类",
|
|
19
|
+
"filterDifficulty": "难度",
|
|
20
|
+
"noData": "暂无题目",
|
|
21
|
+
"createSuccess": "题目创建成功",
|
|
22
|
+
"createFailed": "题目创建失败",
|
|
23
|
+
"deleteSuccess": "题目已删除",
|
|
24
|
+
"deleteFailed": "题目删除失败",
|
|
25
|
+
"contentPlaceholder": "输入题目内容,支持 Markdown...",
|
|
26
|
+
"types": {
|
|
27
|
+
"single_choice": "单选",
|
|
28
|
+
"multiple_choice": "多选",
|
|
29
|
+
"true_false": "判断",
|
|
30
|
+
"fill_blank": "填空",
|
|
31
|
+
"essay": "简答"
|
|
32
|
+
},
|
|
33
|
+
"col": {
|
|
34
|
+
"type": "题型",
|
|
35
|
+
"content": "题目内容",
|
|
36
|
+
"difficulty": "难度",
|
|
37
|
+
"score": "分值",
|
|
38
|
+
"useCount": "使用次数"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"paper": {
|
|
42
|
+
"title": "试卷管理",
|
|
43
|
+
"description": "创建和管理试卷,支持智能组卷",
|
|
44
|
+
"create": "新建试卷",
|
|
45
|
+
"createTitle": "新建试卷",
|
|
46
|
+
"titlePlaceholder": "输入试卷标题...",
|
|
47
|
+
"noData": "暂无试卷",
|
|
48
|
+
"createSuccess": "试卷创建成功",
|
|
49
|
+
"createFailed": "试卷创建失败",
|
|
50
|
+
"deleteSuccess": "试卷已删除",
|
|
51
|
+
"deleteFailed": "试卷删除失败",
|
|
52
|
+
"publishSuccess": "试卷发布成功",
|
|
53
|
+
"publishFailed": "试卷发布失败",
|
|
54
|
+
"publish": "发布",
|
|
55
|
+
"titleRequired": "请输入试卷标题",
|
|
56
|
+
"totalScore": "总分",
|
|
57
|
+
"passingScore": "及格分",
|
|
58
|
+
"timeLimit": "时限",
|
|
59
|
+
"statuses": {
|
|
60
|
+
"draft": "草稿",
|
|
61
|
+
"published": "已发布",
|
|
62
|
+
"archived": "已归档"
|
|
63
|
+
},
|
|
64
|
+
"col": {
|
|
65
|
+
"title": "试卷名称",
|
|
66
|
+
"description": "描述"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"exam": {
|
|
70
|
+
"title": "考试管理",
|
|
71
|
+
"description": "创建考试活动,设置时间和规则",
|
|
72
|
+
"create": "新建考试",
|
|
73
|
+
"createTitle": "新建考试",
|
|
74
|
+
"noData": "暂无考试",
|
|
75
|
+
"createSuccess": "考试创建成功",
|
|
76
|
+
"createFailed": "考试创建失败",
|
|
77
|
+
"deleteSuccess": "考试已删除",
|
|
78
|
+
"deleteFailed": "考试删除失败",
|
|
79
|
+
"statusUpdated": "状态已更新",
|
|
80
|
+
"updateFailed": "更新失败",
|
|
81
|
+
"formRequired": "请填写必填项",
|
|
82
|
+
"selectPaper": "选择试卷",
|
|
83
|
+
"maxAttempts": "最大次数",
|
|
84
|
+
"shuffleQuestions": "随机题目顺序",
|
|
85
|
+
"showAnswerAfterSubmit": "提交后展示答案",
|
|
86
|
+
"startTime": "开始时间",
|
|
87
|
+
"endTime": "结束时间",
|
|
88
|
+
"schedule": "发布",
|
|
89
|
+
"start": "开始",
|
|
90
|
+
"end": "结束",
|
|
91
|
+
"statuses": {
|
|
92
|
+
"draft": "草稿",
|
|
93
|
+
"scheduled": "已发布",
|
|
94
|
+
"ongoing": "进行中",
|
|
95
|
+
"ended": "已结束",
|
|
96
|
+
"cancelled": "已取消"
|
|
97
|
+
},
|
|
98
|
+
"col": {
|
|
99
|
+
"title": "考试名称",
|
|
100
|
+
"paper": "试卷",
|
|
101
|
+
"description": "描述"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"examRoom": {
|
|
105
|
+
"startFailed": "开始考试失败,请重试",
|
|
106
|
+
"loadFailed": "加载考试信息失败",
|
|
107
|
+
"submitted": "答卷已提交",
|
|
108
|
+
"yourScore": "你的得分",
|
|
109
|
+
"passed": "通过",
|
|
110
|
+
"failed": "未通过",
|
|
111
|
+
"question": "第",
|
|
112
|
+
"score": "分值",
|
|
113
|
+
"answered": "已答 {count}/{total} 题",
|
|
114
|
+
"submit": "提交答卷",
|
|
115
|
+
"submitSuccess": "提交成功,得分: {score}",
|
|
116
|
+
"submitFailed": "提交失败,请重试",
|
|
117
|
+
"confirmSubmitTitle": "确认提交",
|
|
118
|
+
"confirmSubmitDesc": "你已完成 {answered}/{total} 题,确定提交吗?",
|
|
119
|
+
"trueLabel": "正确",
|
|
120
|
+
"falseLabel": "错误",
|
|
121
|
+
"fillBlankPlaceholder": "填写你的答案...",
|
|
122
|
+
"essayPlaceholder": "写下你的回答..."
|
|
123
|
+
},
|
|
124
|
+
"statistics": {
|
|
125
|
+
"title": "成绩统计",
|
|
126
|
+
"description": "查看考试统计、成绩分布和排名",
|
|
127
|
+
"selectExam": "选择考试",
|
|
128
|
+
"selectExamHint": "请选择考试查看统计",
|
|
129
|
+
"totalAttempts": "参与人数",
|
|
130
|
+
"gradedAttempts": "已阅卷",
|
|
131
|
+
"averageScore": "平均分",
|
|
132
|
+
"passRate": "通过率",
|
|
133
|
+
"highestScore": "最高分",
|
|
134
|
+
"lowestScore": "最低分",
|
|
135
|
+
"scoreDistribution": "成绩分布",
|
|
136
|
+
"ranking": "排行榜",
|
|
137
|
+
"noRanking": "暂无排名数据",
|
|
138
|
+
"rank": "排名",
|
|
139
|
+
"userId": "用户",
|
|
140
|
+
"score": "得分"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|