-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCollectionQuestions.js
More file actions
476 lines (419 loc) · 19.3 KB
/
CollectionQuestions.js
File metadata and controls
476 lines (419 loc) · 19.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
const fs = require('fs');
const writeFile = require('fs').promises.writeFile;
const chalk = require('chalk');
const path = require('path');
const GiftParser = require('./GiftParser');
const { match } = require('assert');
const dataFolderPath = path.join(__dirname, 'data', 'gift');
const tempStoragePath = path.join(__dirname, 'data', 'temp_selected_questions.json');
class CollectionQuestions {
constructor(nomFichier) {
this.nomFichier = nomFichier;
}
/**
* Charge et parse les questions d'un fichier GIFT.
* @param {string} data - Contenu du fichier en texte brut.
* @param {string} collectionPath - Chemin du fichier pour des logs ou analyses supplémentaires.
* @param {boolean} check - Indique si le format des questions doit être vérifié.
* @returns {Array} - Liste des questions parsées.
*/
chargeExamQuestions = function (data, collectionPath, check) {
const parser = new GiftParser();
parser.parse(data, collectionPath);
const parsedQuestions = parser.parse(data, collectionPath);
if (check == true) {parser.checkFormat(collectionPath);}
this.questions = parsedQuestions;
return parsedQuestions;
}
/**
* Charge toutes les questions des fichiers GIFT dans le dossier `data/gift`.
* @param {boolean} check - Indique si le format des questions doit être vérifié.
* @returns {Array} - Liste combinée de toutes les questions parsées.
*/
chargeAllFolderQuestions(check) {
try {
const files = fs.readdirSync(dataFolderPath);
const allQuestions = [];
files.forEach((file) => {
try {
const filePath = path.join(dataFolderPath, file);
const data = fs.readFileSync(filePath, 'utf8'); // Synchronous version of readFile
const fileQuestions = this.chargeExamQuestions(data, file, check);
allQuestions.push(...fileQuestions); // Add questions to the list
} catch (err) {
console.log(`Erreur de lecture du fichier ${file}: ${err.message}`);
}
});
return allQuestions;
} catch (err) {
console.log(`Erreur lors du traitement du dossier : ${err.message}`);
return [];
}
}
/**
* Affiche les informations détaillées d'une liste de questions.
* @param {Array} questions - Liste des questions à afficher.
*/
logQuestions = function (questions) {
const transformType = (typeDeQuestion) => {
switch (typeDeQuestion) {
case 'qcm1':
return 'Choix multiples';
case 'vrai_faux':
return 'Vrai ou faux';
case 'numerique':
return 'Numérique';
case 'mot_manquant':
return 'Mot manquant';
default:
return typeDeQuestion.charAt(0).toUpperCase() + typeDeQuestion.slice(1);
}
};
questions.forEach((question, index) => {
console.log(`\n`);
console.log(chalk.bold(`ID : `) + chalk.gray(String(question.id)));
console.log(chalk.bold(`Titre : `) + chalk.gray(String(question.titre)));
// console.log(chalk.bold(`formatGift : `) + chalk.gray(transformType(question.formatGift) || 'No format gift available'));
console.log(chalk.bold(`typeDeQuestion : `) + chalk.gray(transformType(question.typeDeQuestion)));
console.log(chalk.bold(`Enoncé : `) + chalk.gray(question.texte));
if (Array.isArray(question.bonnesReponses) && question.bonnesReponses.length > 0) {
console.log(chalk.bold("Bonnes réponses :"));
question.bonnesReponses.forEach(ans => {
console.log(chalk.gray(`\t✔️ ${ans}`));
});
}
if (Array.isArray(question.reponses) && question.reponses.length > 0) {
console.log(chalk.bold("Choix de réponse :"));
question.reponses.forEach(indivreponses => {
// Check if the indivreponse is in bonnesReponses
const isCorrect = question.bonnesReponses.includes(indivreponses);
if (isCorrect) {
console.log(chalk.grey(`\t✔️ ${indivreponses}`)); // Correct answer with check mark
} else {
console.log(chalk.grey(`\t❌ ${indivreponses}`)); // Incorrect answer with cross
}
});
}
});
}
/**
* Recherche des questions contenant une clé dans leur titre ou énoncé.
* @param {Array} questions - Liste des questions à filtrer.
* @param {string} searchKey - Mot-clé de recherche.
* @returns {Array} - Liste des questions correspondant à la recherche.
*/
search(questions, searchKey) {
return questions.filter(q =>
(q.titre && q.titre.toLowerCase().includes(searchKey.toLowerCase())) ||
(q.texte && q.texte.toLowerCase().includes(searchKey.toLowerCase()))
);
}
/**
* Compte le nombre de questions dans un fichier de collection.
* @param {string} collectionName - Nom de la collection (sans extension).
* @returns {number} - Nombre de questions dans la collection.
*/
compterQuestions(collectionName) {
const collectionPath = path.join(dataFolderPath, `${collectionName}.gift`);
try {
const data = fs.readFileSync(collectionPath, 'utf8'); // Synchronous file read
const collectionQuestions = this.chargeExamQuestions(data, collectionPath, false);
return collectionQuestions.length;
} catch (err) {
console.error('Erreur de lecture du fichier :', err);
return 0;
}
}
/**
* Vérifie si une question avec l'ID donné existe dans une liste de questions.
* @param {Array} questions - Liste des questions à examiner.
* @param {string} id - ID de la question à rechercher.
* @returns {boolean} - `true` si la question est trouvée, sinon `false`.
*/
contientQuestions(questions, id) {
return questions.some((question) => question?.id === id);
}
/**
* Sélectionne des questions par ID et les stocke dans un fichier temporaire.
* @param {Array} questions - Liste des questions disponibles.
* @param {string} id - ID de la question à sélectionner.
*/
selectQuestionsFromId = function (questions, id) {
const filteredQuestions = questions.filter(question => question?.id === id);
if (filteredQuestions.length === 0) {
console.error(`Aucune question trouvée avec l'ID : ${id}`);
return;
}
console.log('Question sélectionnée :');
this.logQuestions(filteredQuestions);
// Vérifier si le fichier existe
fs.exists(tempStoragePath, (exists) => {
if (exists) {
fs.readFile(tempStoragePath, 'utf8', (err, data) => {
if (err) {
console.error('Erreur de lecture du fichier :', err);
return;
}
let existingSelectedQuestions = [];
try {
existingSelectedQuestions = JSON.parse(data); // Parser les données existantes
} catch (e) {
console.error('Erreur de parsing JSON :', e);
}
// Vérifier si l'ID est déjà présent
const existingIds = new Set(existingSelectedQuestions.map(q => q.id));
const newQuestions = filteredQuestions.filter(q => !existingIds.has(q.id));
if (newQuestions.length === 0) {
console.error(`La question avec l'ID ${id} est déjà dans le fichier ${tempStoragePath}.`);
return;
}
// Ajouter uniquement les nouvelles questions
existingSelectedQuestions.push(...newQuestions);
// Réécrire le fichier avec les nouvelles questions
writeFile(tempStoragePath, JSON.stringify(existingSelectedQuestions, null, 2), 'utf8')
.then(() => {
console.log(`Question ajoutée dans : ${tempStoragePath}`);
})
.catch((error) => {
console.error('Erreur lors de l\'écriture dans le fichier :', error);
});
});
} else {
// Si le fichier n'existe pas, créer un nouveau fichier et y écrire les questions
writeFile(tempStoragePath, JSON.stringify(filteredQuestions, null, 2), 'utf8')
.then(() => {
console.log(`Résultats enregistrés dans : ${tempStoragePath}`);
})
.catch((error) => {
console.error('Erreur lors de l\'écriture dans le fichier :', error);
});
}
});
};
/**
* Ajoute des questions à un fichier GIFT depuis un fichier temporaire.
* @param {string} collectionPath - Chemin du fichier GIFT de destination.
*/
ajouterQuestions(collectionPath) {
fs.readFile(collectionPath, 'utf8', (err, existingData) => {
if (err) {
console.error('Erreur de lecture du fichier :', err);
return;
}
const collectionQuestions = this.chargeExamQuestions(existingData, collectionPath, false);
// Read the temporary storage
fs.readFile(tempStoragePath, 'utf8', (err, data) => {
if (err && err.code !== 'ENOENT') {
console.error('Erreur de lecture du fichier :', err);
return;
}
let selectedQuestions = [];
if (data) {
try {
selectedQuestions = JSON.parse(data);
} catch (e) {
console.error('Erreur de parsing JSON :', e);
return;
}
}
if (!Array.isArray(selectedQuestions) || selectedQuestions.length === 0) {
console.error('Aucune question sélectionnée dans le fichier temporaire.');
return;
}
// Filter out questions already in the collection
const newQuestions = selectedQuestions.filter(newQ =>
!this.contientQuestions(collectionQuestions, newQ.id)
);
if (newQuestions.length === 0) {
console.log('Toutes les questions sélectionnées existent déjà dans la collection.');
return;
}
this.logQuestions(newQuestions);
// Convert new questions to GIFT format
const contenuGIFT = newQuestions.map(q => q.formatGift.startsWith('::') ? q.formatGift : `::${q.formatGift}`).join('\n\n');
// Conditionally add newlines based on whether the file is empty
const prefix = existingData.trim() === '' ? '' : '\n\n';
// Append the new questions to the end of the existing collection file
fs.appendFileSync(collectionPath, prefix + contenuGIFT, 'utf-8');
console.log(`Nouvelles questions ajoutées au fichier GIFT : ${collectionPath}`);
});
});
}
/**
* Retire des questions spécifiques d'une collection en utilisant un fichier temporaire.
* @param {string} collectionPath - Chemin du fichier GIFT de destination.
*/
removeQuestions(collectionPath) {
// Read the collection file
fs.readFile(collectionPath, 'utf8', (err, existingData) => {
if (err) {
console.error('Erreur de lecture du fichier de collection :', err);
return;
}
const collectionQuestions = this.chargeExamQuestions(existingData, collectionPath, false);
// Read the temporary storage
fs.readFile(tempStoragePath, 'utf8', (err, tempData) => {
if (err) {
if (err.code === 'ENOENT') {
console.error('Le fichier temporaire n\'existe pas.');
} else {
console.error('Erreur de lecture du fichier temporaire :', err);
}
return;
}
let selectedQuestions = [];
if (tempData.trim()) {
try {
selectedQuestions = JSON.parse(tempData);
} catch (e) {
console.error('Erreur de parsing JSON du fichier temporaire :', e);
return;
}
}
if (!Array.isArray(selectedQuestions) || selectedQuestions.length === 0) {
console.error('Aucune question sélectionnée dans le fichier temporaire.');
return;
}
// Filter out questions in the temp file from the collection
const updatedCollection = collectionQuestions.filter(
(question) => !selectedQuestions.some(tempQ => tempQ.id === question.id)
);
if (updatedCollection.length === collectionQuestions.length) {
console.log('Aucune question correspondante trouvée à supprimer.');
return;
}
// Write back the updated collection
const updatedData = JSON.stringify(updatedCollection, null, 2);
fs.writeFile(collectionPath, updatedData, 'utf8', (err) => {
if (err) {
console.error('Erreur d\'écriture dans le fichier de collection :', err);
return;
}
console.log(`${collectionQuestions.length - updatedCollection.length} question(s) supprimée(s) du fichier : ${collectionPath}`);
});
});
});
}
/**
* Crée une nouvelle collection (fichier GIFT) et y ajoute des questions.
* @param {string} collectionName - Nom de la nouvelle collection (sans extension).
*/
createCollection(collectionName) {
const filenames = fs.readdirSync(dataFolderPath);
// Vérification que le fichier n'existe pas déjà
if (filenames.includes(`${collectionName}.gift`)) {
console.log(`L'examen ${collectionName}.gift existe déjà. Vous pouvez le retrouver ici :\n` +
`${process.cwd()}/${dataFolderPath}${collectionName}.gift`);
return;
}
// Création du fichier si nécessaire
const collectionPath = `${dataFolderPath}/${collectionName}.gift`;
fs.writeFile(collectionPath, "", "utf8", (err) => {
if (err) {
console.error(err);
} else {
console.log(`L'examen ${collectionName}.gift a bien été créé. Vous pouvez le trouver ici :\n` +
`${process.cwd()}/${dataFolderPath}${collectionName}.gift`);
}
});
this.ajouterQuestions(collectionPath);
}
/**
* Vérifie la qualité des données d'examen.
* @param {Array} questions - Liste des questions à analyser.
*/
verifyQuality(questions) {
if (!Array.isArray(questions) || questions.length === 0) {
console.error(chalk.red('La liste de questions est vide ou invalide.'));
return;
}
// Vérification du nombre de questions
const questionCount = questions.length;
if (questionCount < 15 || questionCount > 20) {
console.warn(chalk.red(`Nombre de questions invalide (${questionCount}).`));
console.log(`L'examen doit contenir entre 15 et 20 questions.`)
} else {
console.log(chalk.green(`Nombre de questions valide (${questionCount}).`));
}
// Vérification des doublons (basée sur l'ID des questions)
const idSet = new Set();
const duplicateIds = [];
questions.forEach((question) => {
if (idSet.has(question.id)) {
duplicateIds.push(question.id);
} else {
idSet.add(question.id);
}
});
if (duplicateIds.length > 0) {
console.error(chalk.red(`Des doublons ont été détectés pour les IDs suivants : ${duplicateIds.join(', ')}`));
} else {
console.log(chalk.green('Aucun doublon détecté.'));
}
// Résumé
if (duplicateIds.length === 0 && questionCount >= 15 && questionCount <= 20) {
console.log(chalk.green('✔ L’examen est valide.'));
} else {
console.warn(chalk.red('✘ L’examen contient des erreurs.'));
}
}
/**
* Compte le nombre de questions de chaque type dans un fichier de collection.
* @param {string} collectionName - Nom de la collection (sans extension).
* @returns {Object} - Nombre de questions par type
*/
genererStats(collectionName) {
const collectionPath = path.join(dataFolderPath, `${collectionName}.gift`);
try {
const data = fs.readFileSync(collectionPath, 'utf8'); // Synchronous file read
const questions = this.chargeExamQuestions(data, collectionPath, false);
let stats = {
vrai_faux : 0,
ouverte : 0,
texte : 0,
match : 0,
numerique : 0,
mot_manquant : 0,
qcml : 0,
qcm2 : 0,
inconnu : 0
};
for (let index in questions){
switch (questions[index].typeDeQuestion){
case 'vrai_faux' :
stats.vrai_faux ++
break
case 'ouverte' :
stats.ouverte ++
break
case 'texte' :
stats.texte ++
break
case 'match' :
stats.match ++
break
case "numerique" :
stats.numerique ++
break
case "mot_manquant" :
stats.mot_manquant ++
break
case "qcml" :
stats.qcml ++
break
case "qcm2" :
stats.qcm2 ++
break
default :
stats.inconnu ++
break
}
}
return stats;
} catch (err) {
console.error('Erreur de lecture du fichier :', err);
return 0;
}
}
}
module.exports = CollectionQuestions;