@programisto/edrm-exams 0.3.7 → 0.3.9
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/README.md +1 -1
- package/dist/bin/www.js +1 -1
- package/dist/modules/edrm-exams/listeners/correct.listener.d.ts +1 -1
- package/dist/modules/edrm-exams/listeners/correct.listener.js +1 -1
- package/dist/modules/edrm-exams/models/candidate.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/candidate.model.js +1 -1
- package/dist/modules/edrm-exams/models/company.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/company.model.js +1 -1
- package/dist/modules/edrm-exams/models/contact.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/contact.model.js +1 -1
- package/dist/modules/edrm-exams/models/test-category.models.d.ts +1 -1
- package/dist/modules/edrm-exams/models/test-category.models.js +1 -1
- package/dist/modules/edrm-exams/models/test-job.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/test-job.model.js +1 -1
- package/dist/modules/edrm-exams/models/test-question.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/test-question.model.js +1 -1
- package/dist/modules/edrm-exams/models/test-result.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/test-result.model.js +1 -1
- package/dist/modules/edrm-exams/models/test.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/test.model.js +1 -1
- package/dist/modules/edrm-exams/models/user.model.d.ts +1 -1
- package/dist/modules/edrm-exams/models/user.model.js +1 -1
- package/dist/modules/edrm-exams/routes/company.router.d.ts +1 -1
- package/dist/modules/edrm-exams/routes/company.router.js +136 -7
- package/dist/modules/edrm-exams/routes/exams-candidate.router.d.ts +1 -1
- package/dist/modules/edrm-exams/routes/exams-candidate.router.js +227 -8
- package/dist/modules/edrm-exams/routes/exams.router.d.ts +1 -1
- package/dist/modules/edrm-exams/routes/exams.router.js +1022 -40
- package/dist/modules/edrm-exams/routes/result.router.d.ts +1 -1
- package/dist/modules/edrm-exams/routes/result.router.js +153 -6
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnduranceRouter, EnduranceAuthMiddleware, enduranceEmitter as emitter, enduranceEventTypes as eventTypes } from '@programisto/endurance
|
|
1
|
+
import { EnduranceRouter, EnduranceAuthMiddleware, enduranceEmitter as emitter, enduranceEventTypes as eventTypes } from '@programisto/endurance';
|
|
2
2
|
import Test from '../models/test.model.js';
|
|
3
3
|
import TestQuestion from '../models/test-question.model.js';
|
|
4
4
|
import TestResult from '../models/test-result.model.js';
|
|
@@ -84,7 +84,31 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
84
84
|
requireAuth: false,
|
|
85
85
|
permissions: []
|
|
86
86
|
};
|
|
87
|
-
|
|
87
|
+
/**
|
|
88
|
+
* @swagger
|
|
89
|
+
* /exams/categories:
|
|
90
|
+
* post:
|
|
91
|
+
* summary: Créer une catégorie
|
|
92
|
+
* description: Crée une catégorie de test avec son nom.
|
|
93
|
+
* tags: [Examens]
|
|
94
|
+
* requestBody:
|
|
95
|
+
* required: true
|
|
96
|
+
* content:
|
|
97
|
+
* application/json:
|
|
98
|
+
* schema:
|
|
99
|
+
* type: object
|
|
100
|
+
* required: [name]
|
|
101
|
+
* properties:
|
|
102
|
+
* name:
|
|
103
|
+
* type: string
|
|
104
|
+
* responses:
|
|
105
|
+
* 201:
|
|
106
|
+
* description: Catégorie créée
|
|
107
|
+
* 400:
|
|
108
|
+
* description: Paramètres manquants
|
|
109
|
+
* 500:
|
|
110
|
+
* description: Erreur interne
|
|
111
|
+
*/
|
|
88
112
|
this.post('/categories', authenticatedOptions, async (req, res) => {
|
|
89
113
|
const { name } = req.body;
|
|
90
114
|
if (!name) {
|
|
@@ -100,7 +124,19 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
100
124
|
res.status(500).json({ message: 'Internal server error' });
|
|
101
125
|
}
|
|
102
126
|
});
|
|
103
|
-
|
|
127
|
+
/**
|
|
128
|
+
* @swagger
|
|
129
|
+
* /exams/categories:
|
|
130
|
+
* get:
|
|
131
|
+
* summary: Lister les catégories
|
|
132
|
+
* description: Retourne toutes les catégories de test.
|
|
133
|
+
* tags: [Examens]
|
|
134
|
+
* responses:
|
|
135
|
+
* 200:
|
|
136
|
+
* description: Liste des catégories
|
|
137
|
+
* 500:
|
|
138
|
+
* description: Erreur interne
|
|
139
|
+
*/
|
|
104
140
|
this.get('/categories', authenticatedOptions, async (req, res) => {
|
|
105
141
|
try {
|
|
106
142
|
const categories = await TestCategory.find();
|
|
@@ -111,7 +147,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
111
147
|
res.status(500).json({ message: 'Internal server error' });
|
|
112
148
|
}
|
|
113
149
|
});
|
|
114
|
-
|
|
150
|
+
/**
|
|
151
|
+
* @swagger
|
|
152
|
+
* /exams/categorie/{id}:
|
|
153
|
+
* get:
|
|
154
|
+
* summary: Détail d'une catégorie
|
|
155
|
+
* description: Récupère une catégorie par son identifiant.
|
|
156
|
+
* tags: [Examens]
|
|
157
|
+
* parameters:
|
|
158
|
+
* - in: path
|
|
159
|
+
* name: id
|
|
160
|
+
* required: true
|
|
161
|
+
* schema:
|
|
162
|
+
* type: string
|
|
163
|
+
* responses:
|
|
164
|
+
* 200:
|
|
165
|
+
* description: Catégorie trouvée
|
|
166
|
+
* 404:
|
|
167
|
+
* description: Catégorie non trouvée
|
|
168
|
+
* 500:
|
|
169
|
+
* description: Erreur interne
|
|
170
|
+
*/
|
|
115
171
|
this.get('/categorie/:id', authenticatedOptions, async (req, res) => {
|
|
116
172
|
const { id } = req.params;
|
|
117
173
|
try {
|
|
@@ -126,7 +182,31 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
126
182
|
res.status(500).json({ message: 'Internal server error' });
|
|
127
183
|
}
|
|
128
184
|
});
|
|
129
|
-
|
|
185
|
+
/**
|
|
186
|
+
* @swagger
|
|
187
|
+
* /exams/jobs:
|
|
188
|
+
* post:
|
|
189
|
+
* summary: Créer un job cible
|
|
190
|
+
* description: Crée un job cible pour les tests.
|
|
191
|
+
* tags: [Examens]
|
|
192
|
+
* requestBody:
|
|
193
|
+
* required: true
|
|
194
|
+
* content:
|
|
195
|
+
* application/json:
|
|
196
|
+
* schema:
|
|
197
|
+
* type: object
|
|
198
|
+
* required: [name]
|
|
199
|
+
* properties:
|
|
200
|
+
* name:
|
|
201
|
+
* type: string
|
|
202
|
+
* responses:
|
|
203
|
+
* 201:
|
|
204
|
+
* description: Job créé
|
|
205
|
+
* 400:
|
|
206
|
+
* description: Paramètres manquants
|
|
207
|
+
* 500:
|
|
208
|
+
* description: Erreur interne
|
|
209
|
+
*/
|
|
130
210
|
this.post('/jobs', authenticatedOptions, async (req, res) => {
|
|
131
211
|
const { name } = req.body;
|
|
132
212
|
if (!name) {
|
|
@@ -142,7 +222,19 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
142
222
|
res.status(500).json({ message: 'Internal server error' });
|
|
143
223
|
}
|
|
144
224
|
});
|
|
145
|
-
|
|
225
|
+
/**
|
|
226
|
+
* @swagger
|
|
227
|
+
* /exams/jobs:
|
|
228
|
+
* get:
|
|
229
|
+
* summary: Lister les jobs cibles
|
|
230
|
+
* description: Retourne l'ensemble des jobs disponibles pour les tests.
|
|
231
|
+
* tags: [Examens]
|
|
232
|
+
* responses:
|
|
233
|
+
* 200:
|
|
234
|
+
* description: Liste des jobs
|
|
235
|
+
* 500:
|
|
236
|
+
* description: Erreur interne
|
|
237
|
+
*/
|
|
146
238
|
this.get('/jobs', authenticatedOptions, async (req, res) => {
|
|
147
239
|
try {
|
|
148
240
|
const jobs = await TestJob.find();
|
|
@@ -153,7 +245,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
153
245
|
res.status(500).json({ message: 'Internal server error' });
|
|
154
246
|
}
|
|
155
247
|
});
|
|
156
|
-
|
|
248
|
+
/**
|
|
249
|
+
* @swagger
|
|
250
|
+
* /exams/jobs/{id}:
|
|
251
|
+
* get:
|
|
252
|
+
* summary: Détail d'un job
|
|
253
|
+
* description: Récupère un job cible par ID.
|
|
254
|
+
* tags: [Examens]
|
|
255
|
+
* parameters:
|
|
256
|
+
* - in: path
|
|
257
|
+
* name: id
|
|
258
|
+
* required: true
|
|
259
|
+
* schema:
|
|
260
|
+
* type: string
|
|
261
|
+
* responses:
|
|
262
|
+
* 200:
|
|
263
|
+
* description: Job trouvé
|
|
264
|
+
* 404:
|
|
265
|
+
* description: Job non trouvé
|
|
266
|
+
* 500:
|
|
267
|
+
* description: Erreur interne
|
|
268
|
+
*/
|
|
157
269
|
this.get('/jobs/:id', authenticatedOptions, async (req, res) => {
|
|
158
270
|
const { id } = req.params;
|
|
159
271
|
try {
|
|
@@ -168,7 +280,19 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
168
280
|
res.status(500).json({ message: 'Internal server error' });
|
|
169
281
|
}
|
|
170
282
|
});
|
|
171
|
-
|
|
283
|
+
/**
|
|
284
|
+
* @swagger
|
|
285
|
+
* /exams/migrate-targetjobs:
|
|
286
|
+
* post:
|
|
287
|
+
* summary: Migrer les tests (targetJob)
|
|
288
|
+
* description: Convertit les tests utilisant l'ancien format de targetJob vers les références TestJob.
|
|
289
|
+
* tags: [Examens]
|
|
290
|
+
* responses:
|
|
291
|
+
* 200:
|
|
292
|
+
* description: Migration exécutée
|
|
293
|
+
* 500:
|
|
294
|
+
* description: Erreur interne
|
|
295
|
+
*/
|
|
172
296
|
this.post('/migrate-targetjobs', authenticatedOptions, async (req, res) => {
|
|
173
297
|
try {
|
|
174
298
|
const tests = await Test.find();
|
|
@@ -198,7 +322,48 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
198
322
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
199
323
|
}
|
|
200
324
|
});
|
|
201
|
-
|
|
325
|
+
/**
|
|
326
|
+
* @swagger
|
|
327
|
+
* /exams/test:
|
|
328
|
+
* post:
|
|
329
|
+
* summary: Créer un test
|
|
330
|
+
* description: Crée un test avec titre, description, job cible, séniorité et catégories.
|
|
331
|
+
* tags: [Examens]
|
|
332
|
+
* requestBody:
|
|
333
|
+
* required: true
|
|
334
|
+
* content:
|
|
335
|
+
* application/json:
|
|
336
|
+
* schema:
|
|
337
|
+
* type: object
|
|
338
|
+
* required: [title, targetJob, seniorityLevel]
|
|
339
|
+
* properties:
|
|
340
|
+
* title:
|
|
341
|
+
* type: string
|
|
342
|
+
* description:
|
|
343
|
+
* type: string
|
|
344
|
+
* targetJob:
|
|
345
|
+
* type: string
|
|
346
|
+
* seniorityLevel:
|
|
347
|
+
* type: string
|
|
348
|
+
* categories:
|
|
349
|
+
* type: array
|
|
350
|
+
* items:
|
|
351
|
+
* type: object
|
|
352
|
+
* properties:
|
|
353
|
+
* name:
|
|
354
|
+
* type: string
|
|
355
|
+
* expertiseLevel:
|
|
356
|
+
* type: string
|
|
357
|
+
* state:
|
|
358
|
+
* type: string
|
|
359
|
+
* responses:
|
|
360
|
+
* 201:
|
|
361
|
+
* description: Test créé
|
|
362
|
+
* 400:
|
|
363
|
+
* description: Paramètres manquants
|
|
364
|
+
* 500:
|
|
365
|
+
* description: Erreur interne
|
|
366
|
+
*/
|
|
202
367
|
this.post('/test', authenticatedOptions, async (req, res) => {
|
|
203
368
|
const { title, description, targetJob, seniorityLevel, categories, state = 'draft' } = req.body;
|
|
204
369
|
const user = req.user;
|
|
@@ -249,7 +414,35 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
249
414
|
res.status(500).json({ message: 'Internal server error' });
|
|
250
415
|
}
|
|
251
416
|
});
|
|
252
|
-
|
|
417
|
+
/**
|
|
418
|
+
* @swagger
|
|
419
|
+
* /exams/test/{id}:
|
|
420
|
+
* put:
|
|
421
|
+
* summary: Modifier un test
|
|
422
|
+
* description: Met à jour les métadonnées, catégories ou questions d'un test.
|
|
423
|
+
* tags: [Examens]
|
|
424
|
+
* parameters:
|
|
425
|
+
* - in: path
|
|
426
|
+
* name: id
|
|
427
|
+
* required: true
|
|
428
|
+
* schema:
|
|
429
|
+
* type: string
|
|
430
|
+
* requestBody:
|
|
431
|
+
* required: true
|
|
432
|
+
* content:
|
|
433
|
+
* application/json:
|
|
434
|
+
* schema:
|
|
435
|
+
* type: object
|
|
436
|
+
* responses:
|
|
437
|
+
* 200:
|
|
438
|
+
* description: Test modifié
|
|
439
|
+
* 404:
|
|
440
|
+
* description: Test non trouvé
|
|
441
|
+
* 400:
|
|
442
|
+
* description: Données invalides
|
|
443
|
+
* 500:
|
|
444
|
+
* description: Erreur interne
|
|
445
|
+
*/
|
|
253
446
|
this.put('/test/:id', authenticatedOptions, async (req, res) => {
|
|
254
447
|
const { id } = req.params;
|
|
255
448
|
const { title, description, targetJob, seniorityLevel, categories, state, questions } = req.body;
|
|
@@ -318,7 +511,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
318
511
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
319
512
|
}
|
|
320
513
|
});
|
|
321
|
-
|
|
514
|
+
/**
|
|
515
|
+
* @swagger
|
|
516
|
+
* /exams/test/{id}:
|
|
517
|
+
* delete:
|
|
518
|
+
* summary: Supprimer un test
|
|
519
|
+
* description: Supprime un test ainsi que ses questions et résultats associés.
|
|
520
|
+
* tags: [Examens]
|
|
521
|
+
* parameters:
|
|
522
|
+
* - in: path
|
|
523
|
+
* name: id
|
|
524
|
+
* required: true
|
|
525
|
+
* schema:
|
|
526
|
+
* type: string
|
|
527
|
+
* responses:
|
|
528
|
+
* 200:
|
|
529
|
+
* description: Test supprimé
|
|
530
|
+
* 404:
|
|
531
|
+
* description: Test non trouvé
|
|
532
|
+
* 500:
|
|
533
|
+
* description: Erreur interne
|
|
534
|
+
*/
|
|
322
535
|
this.delete('/test/:id', authenticatedOptions, async (req, res) => {
|
|
323
536
|
const { id } = req.params;
|
|
324
537
|
try {
|
|
@@ -338,7 +551,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
338
551
|
res.status(500).json({ message: 'Internal server error' });
|
|
339
552
|
}
|
|
340
553
|
});
|
|
341
|
-
|
|
554
|
+
/**
|
|
555
|
+
* @swagger
|
|
556
|
+
* /exams/test/{id}:
|
|
557
|
+
* get:
|
|
558
|
+
* summary: Détail d'un test
|
|
559
|
+
* description: Retourne un test avec ses questions et nom du job cible.
|
|
560
|
+
* tags: [Examens]
|
|
561
|
+
* parameters:
|
|
562
|
+
* - in: path
|
|
563
|
+
* name: id
|
|
564
|
+
* required: true
|
|
565
|
+
* schema:
|
|
566
|
+
* type: string
|
|
567
|
+
* responses:
|
|
568
|
+
* 200:
|
|
569
|
+
* description: Test trouvé
|
|
570
|
+
* 404:
|
|
571
|
+
* description: Test non trouvé
|
|
572
|
+
* 500:
|
|
573
|
+
* description: Erreur interne
|
|
574
|
+
*/
|
|
342
575
|
this.get('/test/:id', authenticatedOptions, async (req, res) => {
|
|
343
576
|
const { id } = req.params;
|
|
344
577
|
try {
|
|
@@ -367,7 +600,59 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
367
600
|
res.status(500).json({ message: 'Internal server error' });
|
|
368
601
|
}
|
|
369
602
|
});
|
|
370
|
-
|
|
603
|
+
/**
|
|
604
|
+
* @swagger
|
|
605
|
+
* /exams:
|
|
606
|
+
* get:
|
|
607
|
+
* summary: Lister les tests
|
|
608
|
+
* description: Liste paginée des tests avec filtres, recherche et tri.
|
|
609
|
+
* tags: [Examens]
|
|
610
|
+
* parameters:
|
|
611
|
+
* - in: query
|
|
612
|
+
* name: page
|
|
613
|
+
* schema:
|
|
614
|
+
* type: integer
|
|
615
|
+
* default: 1
|
|
616
|
+
* - in: query
|
|
617
|
+
* name: limit
|
|
618
|
+
* schema:
|
|
619
|
+
* type: integer
|
|
620
|
+
* default: 10
|
|
621
|
+
* - in: query
|
|
622
|
+
* name: search
|
|
623
|
+
* schema:
|
|
624
|
+
* type: string
|
|
625
|
+
* - in: query
|
|
626
|
+
* name: targetJob
|
|
627
|
+
* schema:
|
|
628
|
+
* type: string
|
|
629
|
+
* default: all
|
|
630
|
+
* - in: query
|
|
631
|
+
* name: seniorityLevel
|
|
632
|
+
* schema:
|
|
633
|
+
* type: string
|
|
634
|
+
* default: all
|
|
635
|
+
* - in: query
|
|
636
|
+
* name: state
|
|
637
|
+
* schema:
|
|
638
|
+
* type: string
|
|
639
|
+
* default: all
|
|
640
|
+
* - in: query
|
|
641
|
+
* name: sortBy
|
|
642
|
+
* schema:
|
|
643
|
+
* type: string
|
|
644
|
+
* default: updatedAt
|
|
645
|
+
* - in: query
|
|
646
|
+
* name: sortOrder
|
|
647
|
+
* schema:
|
|
648
|
+
* type: string
|
|
649
|
+
* default: desc
|
|
650
|
+
* responses:
|
|
651
|
+
* 200:
|
|
652
|
+
* description: Tests paginés
|
|
653
|
+
* 500:
|
|
654
|
+
* description: Erreur interne
|
|
655
|
+
*/
|
|
371
656
|
this.get('/', authenticatedOptions, async (req, res) => {
|
|
372
657
|
try {
|
|
373
658
|
const page = parseInt(req.query.page) || 1;
|
|
@@ -462,7 +747,36 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
462
747
|
res.status(500).json({ message: 'Internal server error' });
|
|
463
748
|
}
|
|
464
749
|
});
|
|
465
|
-
|
|
750
|
+
/**
|
|
751
|
+
* @swagger
|
|
752
|
+
* /exams/test/removeCategory/{testId}:
|
|
753
|
+
* delete:
|
|
754
|
+
* summary: Retirer une catégorie d'un test
|
|
755
|
+
* description: Supprime une catégorie d'un test par son nom.
|
|
756
|
+
* tags: [Examens]
|
|
757
|
+
* parameters:
|
|
758
|
+
* - in: path
|
|
759
|
+
* name: testId
|
|
760
|
+
* required: true
|
|
761
|
+
* schema:
|
|
762
|
+
* type: string
|
|
763
|
+
* requestBody:
|
|
764
|
+
* required: true
|
|
765
|
+
* content:
|
|
766
|
+
* application/json:
|
|
767
|
+
* schema:
|
|
768
|
+
* type: object
|
|
769
|
+
* properties:
|
|
770
|
+
* categoryName:
|
|
771
|
+
* type: string
|
|
772
|
+
* responses:
|
|
773
|
+
* 200:
|
|
774
|
+
* description: Catégorie supprimée
|
|
775
|
+
* 404:
|
|
776
|
+
* description: Test ou catégorie non trouvé
|
|
777
|
+
* 500:
|
|
778
|
+
* description: Erreur interne
|
|
779
|
+
*/
|
|
466
780
|
this.delete('/test/removeCategory/:testId', authenticatedOptions, async (req, res) => {
|
|
467
781
|
const { testId } = req.params;
|
|
468
782
|
const { categoryName } = req.body;
|
|
@@ -480,7 +794,38 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
480
794
|
res.status(500).json({ message: 'Internal server error' });
|
|
481
795
|
}
|
|
482
796
|
});
|
|
483
|
-
|
|
797
|
+
/**
|
|
798
|
+
* @swagger
|
|
799
|
+
* /exams/test/addCategory/{testId}:
|
|
800
|
+
* put:
|
|
801
|
+
* summary: Ajouter une catégorie à un test
|
|
802
|
+
* description: Ajoute une catégorie (créée si besoin) à un test avec niveau d'expertise.
|
|
803
|
+
* tags: [Examens]
|
|
804
|
+
* parameters:
|
|
805
|
+
* - in: path
|
|
806
|
+
* name: testId
|
|
807
|
+
* required: true
|
|
808
|
+
* schema:
|
|
809
|
+
* type: string
|
|
810
|
+
* requestBody:
|
|
811
|
+
* required: true
|
|
812
|
+
* content:
|
|
813
|
+
* application/json:
|
|
814
|
+
* schema:
|
|
815
|
+
* type: object
|
|
816
|
+
* properties:
|
|
817
|
+
* categoryName:
|
|
818
|
+
* type: string
|
|
819
|
+
* expertiseLevel:
|
|
820
|
+
* type: string
|
|
821
|
+
* responses:
|
|
822
|
+
* 200:
|
|
823
|
+
* description: Catégorie ajoutée
|
|
824
|
+
* 404:
|
|
825
|
+
* description: Test non trouvé
|
|
826
|
+
* 500:
|
|
827
|
+
* description: Erreur interne
|
|
828
|
+
*/
|
|
484
829
|
this.put('/test/addCategory/:testId', authenticatedOptions, async (req, res) => {
|
|
485
830
|
const { testId } = req.params;
|
|
486
831
|
const { categoryName, expertiseLevel } = req.body;
|
|
@@ -507,7 +852,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
507
852
|
res.status(500).json({ message: 'Internal server error' });
|
|
508
853
|
}
|
|
509
854
|
});
|
|
510
|
-
|
|
855
|
+
/**
|
|
856
|
+
* @swagger
|
|
857
|
+
* /exams/test/question/{questionId}:
|
|
858
|
+
* get:
|
|
859
|
+
* summary: Détail d'une question
|
|
860
|
+
* description: Retourne une question de test par son identifiant.
|
|
861
|
+
* tags: [Examens]
|
|
862
|
+
* parameters:
|
|
863
|
+
* - in: path
|
|
864
|
+
* name: questionId
|
|
865
|
+
* required: true
|
|
866
|
+
* schema:
|
|
867
|
+
* type: string
|
|
868
|
+
* responses:
|
|
869
|
+
* 200:
|
|
870
|
+
* description: Question trouvée
|
|
871
|
+
* 404:
|
|
872
|
+
* description: Question non trouvée
|
|
873
|
+
* 500:
|
|
874
|
+
* description: Erreur interne
|
|
875
|
+
*/
|
|
511
876
|
this.get('/test/question/:questionId', authenticatedOptions, async (req, res) => {
|
|
512
877
|
const { questionId } = req.params;
|
|
513
878
|
const question = await TestQuestion.findById(questionId);
|
|
@@ -516,7 +881,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
516
881
|
}
|
|
517
882
|
res.status(200).json({ data: question });
|
|
518
883
|
});
|
|
519
|
-
|
|
884
|
+
/**
|
|
885
|
+
* @swagger
|
|
886
|
+
* /exams/test/questions/{testId}:
|
|
887
|
+
* get:
|
|
888
|
+
* summary: Lister les questions d'un test
|
|
889
|
+
* description: Retourne toutes les questions d'un test.
|
|
890
|
+
* tags: [Examens]
|
|
891
|
+
* parameters:
|
|
892
|
+
* - in: path
|
|
893
|
+
* name: testId
|
|
894
|
+
* required: true
|
|
895
|
+
* schema:
|
|
896
|
+
* type: string
|
|
897
|
+
* responses:
|
|
898
|
+
* 200:
|
|
899
|
+
* description: Questions retournées
|
|
900
|
+
* 404:
|
|
901
|
+
* description: Test non trouvé
|
|
902
|
+
* 500:
|
|
903
|
+
* description: Erreur interne
|
|
904
|
+
*/
|
|
520
905
|
this.get('/test/questions/:testId', authenticatedOptions, async (req, res) => {
|
|
521
906
|
const { testId } = req.params;
|
|
522
907
|
try {
|
|
@@ -538,7 +923,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
538
923
|
res.status(500).json({ message: 'Internal server error' });
|
|
539
924
|
}
|
|
540
925
|
});
|
|
541
|
-
|
|
926
|
+
/**
|
|
927
|
+
* @swagger
|
|
928
|
+
* /exams/test/question/{testId}/{questionId}:
|
|
929
|
+
* delete:
|
|
930
|
+
* summary: Supprimer une question d'un test
|
|
931
|
+
* description: Supprime une question spécifique et réordonne les questions restantes.
|
|
932
|
+
* tags: [Examens]
|
|
933
|
+
* parameters:
|
|
934
|
+
* - in: path
|
|
935
|
+
* name: testId
|
|
936
|
+
* required: true
|
|
937
|
+
* schema:
|
|
938
|
+
* type: string
|
|
939
|
+
* - in: path
|
|
940
|
+
* name: questionId
|
|
941
|
+
* required: true
|
|
942
|
+
* schema:
|
|
943
|
+
* type: string
|
|
944
|
+
* responses:
|
|
945
|
+
* 200:
|
|
946
|
+
* description: Question supprimée
|
|
947
|
+
* 404:
|
|
948
|
+
* description: Test ou question non trouvés
|
|
949
|
+
* 500:
|
|
950
|
+
* description: Erreur interne
|
|
951
|
+
*/
|
|
542
952
|
this.delete('/test/question/:testId/:questionId', authenticatedOptions, async (req, res) => {
|
|
543
953
|
const { testId, questionId } = req.params;
|
|
544
954
|
const question = await TestQuestion.findByIdAndDelete(questionId);
|
|
@@ -558,7 +968,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
558
968
|
await test.save();
|
|
559
969
|
res.status(200).json({ message: 'question deleted with sucess' });
|
|
560
970
|
});
|
|
561
|
-
|
|
971
|
+
/**
|
|
972
|
+
* @swagger
|
|
973
|
+
* /exams/test/questions/{testId}:
|
|
974
|
+
* delete:
|
|
975
|
+
* summary: Supprimer toutes les questions d'un test
|
|
976
|
+
* description: Supprime toutes les questions associées à un test.
|
|
977
|
+
* tags: [Examens]
|
|
978
|
+
* parameters:
|
|
979
|
+
* - in: path
|
|
980
|
+
* name: testId
|
|
981
|
+
* required: true
|
|
982
|
+
* schema:
|
|
983
|
+
* type: string
|
|
984
|
+
* responses:
|
|
985
|
+
* 200:
|
|
986
|
+
* description: Questions supprimées
|
|
987
|
+
* 404:
|
|
988
|
+
* description: Test non trouvé
|
|
989
|
+
* 500:
|
|
990
|
+
* description: Erreur interne
|
|
991
|
+
*/
|
|
562
992
|
this.delete('/test/questions/:testId', authenticatedOptions, async (req, res) => {
|
|
563
993
|
const { testId } = req.params;
|
|
564
994
|
const test = await Test.findById(testId);
|
|
@@ -572,7 +1002,33 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
572
1002
|
await test.save();
|
|
573
1003
|
res.status(200).json({ message: 'questions deleted with sucess' });
|
|
574
1004
|
});
|
|
575
|
-
|
|
1005
|
+
/**
|
|
1006
|
+
* @swagger
|
|
1007
|
+
* /exams/test/modifyQuestion/{id}:
|
|
1008
|
+
* put:
|
|
1009
|
+
* summary: Modifier une question
|
|
1010
|
+
* description: Met à jour les champs d'une question (texte, score, temps, réponses possibles).
|
|
1011
|
+
* tags: [Examens]
|
|
1012
|
+
* parameters:
|
|
1013
|
+
* - in: path
|
|
1014
|
+
* name: id
|
|
1015
|
+
* required: true
|
|
1016
|
+
* schema:
|
|
1017
|
+
* type: string
|
|
1018
|
+
* requestBody:
|
|
1019
|
+
* required: true
|
|
1020
|
+
* content:
|
|
1021
|
+
* application/json:
|
|
1022
|
+
* schema:
|
|
1023
|
+
* type: object
|
|
1024
|
+
* responses:
|
|
1025
|
+
* 200:
|
|
1026
|
+
* description: Question modifiée
|
|
1027
|
+
* 404:
|
|
1028
|
+
* description: Question non trouvée
|
|
1029
|
+
* 500:
|
|
1030
|
+
* description: Erreur interne
|
|
1031
|
+
*/
|
|
576
1032
|
this.put('/test/modifyQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
577
1033
|
const { id } = req.params;
|
|
578
1034
|
const { instruction, maxScore, time, possibleResponses, textType } = req.body;
|
|
@@ -604,7 +1060,42 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
604
1060
|
res.status(500).json({ message: 'Internal server error' });
|
|
605
1061
|
}
|
|
606
1062
|
});
|
|
607
|
-
|
|
1063
|
+
/**
|
|
1064
|
+
* @swagger
|
|
1065
|
+
* /exams/test/addCustomQuestion/{id}:
|
|
1066
|
+
* put:
|
|
1067
|
+
* summary: Ajouter une question personnalisée
|
|
1068
|
+
* description: Ajoute une question manuelle à un test.
|
|
1069
|
+
* tags: [Examens]
|
|
1070
|
+
* parameters:
|
|
1071
|
+
* - in: path
|
|
1072
|
+
* name: id
|
|
1073
|
+
* required: true
|
|
1074
|
+
* schema:
|
|
1075
|
+
* type: string
|
|
1076
|
+
* requestBody:
|
|
1077
|
+
* required: true
|
|
1078
|
+
* content:
|
|
1079
|
+
* application/json:
|
|
1080
|
+
* schema:
|
|
1081
|
+
* type: object
|
|
1082
|
+
* properties:
|
|
1083
|
+
* questionType:
|
|
1084
|
+
* type: string
|
|
1085
|
+
* instruction:
|
|
1086
|
+
* type: string
|
|
1087
|
+
* maxScore:
|
|
1088
|
+
* type: number
|
|
1089
|
+
* time:
|
|
1090
|
+
* type: number
|
|
1091
|
+
* responses:
|
|
1092
|
+
* 200:
|
|
1093
|
+
* description: Question ajoutée
|
|
1094
|
+
* 404:
|
|
1095
|
+
* description: Test non trouvé
|
|
1096
|
+
* 500:
|
|
1097
|
+
* description: Erreur interne
|
|
1098
|
+
*/
|
|
608
1099
|
this.put('/test/addCustomQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
609
1100
|
const { id } = req.params;
|
|
610
1101
|
const { questionType, instruction, maxScore, time } = req.body;
|
|
@@ -629,7 +1120,40 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
629
1120
|
res.status(500).json({ message: 'Internal server error' });
|
|
630
1121
|
}
|
|
631
1122
|
});
|
|
632
|
-
|
|
1123
|
+
/**
|
|
1124
|
+
* @swagger
|
|
1125
|
+
* /exams/test/addQuestion/{id}:
|
|
1126
|
+
* put:
|
|
1127
|
+
* summary: Générer et ajouter une question
|
|
1128
|
+
* description: Génère une question via assistant et l'ajoute au test.
|
|
1129
|
+
* tags: [Examens]
|
|
1130
|
+
* parameters:
|
|
1131
|
+
* - in: path
|
|
1132
|
+
* name: id
|
|
1133
|
+
* required: true
|
|
1134
|
+
* schema:
|
|
1135
|
+
* type: string
|
|
1136
|
+
* requestBody:
|
|
1137
|
+
* required: true
|
|
1138
|
+
* content:
|
|
1139
|
+
* application/json:
|
|
1140
|
+
* schema:
|
|
1141
|
+
* type: object
|
|
1142
|
+
* properties:
|
|
1143
|
+
* questionType:
|
|
1144
|
+
* type: string
|
|
1145
|
+
* category:
|
|
1146
|
+
* type: string
|
|
1147
|
+
* expertiseLevel:
|
|
1148
|
+
* type: string
|
|
1149
|
+
* responses:
|
|
1150
|
+
* 200:
|
|
1151
|
+
* description: Question générée et ajoutée
|
|
1152
|
+
* 404:
|
|
1153
|
+
* description: Test non trouvé
|
|
1154
|
+
* 500:
|
|
1155
|
+
* description: Erreur interne
|
|
1156
|
+
*/
|
|
633
1157
|
this.put('/test/addQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
634
1158
|
const { id } = req.params;
|
|
635
1159
|
const { questionType, category, expertiseLevel } = req.body;
|
|
@@ -660,7 +1184,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
660
1184
|
res.status(500).json({ message: 'Internal server error' });
|
|
661
1185
|
}
|
|
662
1186
|
});
|
|
663
|
-
|
|
1187
|
+
/**
|
|
1188
|
+
* @swagger
|
|
1189
|
+
* /exams/test/shuffle/{testId}:
|
|
1190
|
+
* get:
|
|
1191
|
+
* summary: Mélanger les questions d'un test
|
|
1192
|
+
* description: Mélange l'ordre des questions d'un test.
|
|
1193
|
+
* tags: [Examens]
|
|
1194
|
+
* parameters:
|
|
1195
|
+
* - in: path
|
|
1196
|
+
* name: testId
|
|
1197
|
+
* required: true
|
|
1198
|
+
* schema:
|
|
1199
|
+
* type: string
|
|
1200
|
+
* responses:
|
|
1201
|
+
* 200:
|
|
1202
|
+
* description: Questions mélangées
|
|
1203
|
+
* 404:
|
|
1204
|
+
* description: Test non trouvé
|
|
1205
|
+
* 500:
|
|
1206
|
+
* description: Erreur interne
|
|
1207
|
+
*/
|
|
664
1208
|
this.get('/test/shuffle/:testId', authenticatedOptions, async (req, res) => {
|
|
665
1209
|
const { testId } = req.params;
|
|
666
1210
|
try {
|
|
@@ -680,7 +1224,36 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
680
1224
|
res.status(500).json({ message: 'Internal server error' });
|
|
681
1225
|
}
|
|
682
1226
|
});
|
|
683
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* @swagger
|
|
1229
|
+
* /exams/test/addInvitationText/{id}:
|
|
1230
|
+
* put:
|
|
1231
|
+
* summary: Ajouter un texte d'invitation
|
|
1232
|
+
* description: Ajoute ou met à jour le texte d'invitation utilisé pour un test.
|
|
1233
|
+
* tags: [Examens]
|
|
1234
|
+
* parameters:
|
|
1235
|
+
* - in: path
|
|
1236
|
+
* name: id
|
|
1237
|
+
* required: true
|
|
1238
|
+
* schema:
|
|
1239
|
+
* type: string
|
|
1240
|
+
* requestBody:
|
|
1241
|
+
* required: true
|
|
1242
|
+
* content:
|
|
1243
|
+
* application/json:
|
|
1244
|
+
* schema:
|
|
1245
|
+
* type: object
|
|
1246
|
+
* properties:
|
|
1247
|
+
* invitationText:
|
|
1248
|
+
* type: string
|
|
1249
|
+
* responses:
|
|
1250
|
+
* 200:
|
|
1251
|
+
* description: Texte mis à jour
|
|
1252
|
+
* 404:
|
|
1253
|
+
* description: Test non trouvé
|
|
1254
|
+
* 500:
|
|
1255
|
+
* description: Erreur interne
|
|
1256
|
+
*/
|
|
684
1257
|
this.put('/test/addInvitationText/:id', authenticatedOptions, async (req, res) => {
|
|
685
1258
|
const { id } = req.params;
|
|
686
1259
|
const { invitationText } = req.body;
|
|
@@ -701,7 +1274,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
701
1274
|
res.status(500).json({ message: 'Internal server error' });
|
|
702
1275
|
}
|
|
703
1276
|
});
|
|
704
|
-
|
|
1277
|
+
/**
|
|
1278
|
+
* @swagger
|
|
1279
|
+
* /exams/result/{id}:
|
|
1280
|
+
* get:
|
|
1281
|
+
* summary: Détail d'un résultat
|
|
1282
|
+
* description: Retourne un TestResult par identifiant.
|
|
1283
|
+
* tags: [Résultats]
|
|
1284
|
+
* parameters:
|
|
1285
|
+
* - in: path
|
|
1286
|
+
* name: id
|
|
1287
|
+
* required: true
|
|
1288
|
+
* schema:
|
|
1289
|
+
* type: string
|
|
1290
|
+
* responses:
|
|
1291
|
+
* 200:
|
|
1292
|
+
* description: Résultat trouvé
|
|
1293
|
+
* 404:
|
|
1294
|
+
* description: Résultat non trouvé
|
|
1295
|
+
* 500:
|
|
1296
|
+
* description: Erreur interne
|
|
1297
|
+
*/
|
|
705
1298
|
this.get('/result/:id', authenticatedOptions, async (req, res) => {
|
|
706
1299
|
const { id } = req.params;
|
|
707
1300
|
try {
|
|
@@ -716,7 +1309,21 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
716
1309
|
res.status(500).json({ message: 'Internal server error' });
|
|
717
1310
|
}
|
|
718
1311
|
});
|
|
719
|
-
|
|
1312
|
+
/**
|
|
1313
|
+
* @swagger
|
|
1314
|
+
* /exams/results/:
|
|
1315
|
+
* get:
|
|
1316
|
+
* summary: Lister les résultats
|
|
1317
|
+
* description: Retourne tous les résultats existants.
|
|
1318
|
+
* tags: [Résultats]
|
|
1319
|
+
* responses:
|
|
1320
|
+
* 200:
|
|
1321
|
+
* description: Liste des résultats
|
|
1322
|
+
* 404:
|
|
1323
|
+
* description: Aucun résultat
|
|
1324
|
+
* 500:
|
|
1325
|
+
* description: Erreur interne
|
|
1326
|
+
*/
|
|
720
1327
|
this.get('/results/', authenticatedOptions, async (req, res) => {
|
|
721
1328
|
try {
|
|
722
1329
|
const results = await TestResult.find();
|
|
@@ -730,7 +1337,35 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
730
1337
|
res.status(500).json({ message: 'Internal server error' });
|
|
731
1338
|
}
|
|
732
1339
|
});
|
|
733
|
-
|
|
1340
|
+
/**
|
|
1341
|
+
* @swagger
|
|
1342
|
+
* /exams/invite:
|
|
1343
|
+
* post:
|
|
1344
|
+
* summary: Inviter un candidat à un test
|
|
1345
|
+
* description: Crée un TestResult et envoie un email d'invitation au candidat.
|
|
1346
|
+
* tags: [Résultats]
|
|
1347
|
+
* requestBody:
|
|
1348
|
+
* required: true
|
|
1349
|
+
* content:
|
|
1350
|
+
* application/json:
|
|
1351
|
+
* schema:
|
|
1352
|
+
* type: object
|
|
1353
|
+
* required: [candidateId, testId]
|
|
1354
|
+
* properties:
|
|
1355
|
+
* candidateId:
|
|
1356
|
+
* type: string
|
|
1357
|
+
* testId:
|
|
1358
|
+
* type: string
|
|
1359
|
+
* responses:
|
|
1360
|
+
* 201:
|
|
1361
|
+
* description: Invitation créée
|
|
1362
|
+
* 404:
|
|
1363
|
+
* description: Test ou candidat non trouvé
|
|
1364
|
+
* 400:
|
|
1365
|
+
* description: Paramètres manquants
|
|
1366
|
+
* 500:
|
|
1367
|
+
* description: Erreur interne
|
|
1368
|
+
*/
|
|
734
1369
|
this.post('/invite', authenticatedOptions, async (req, res) => {
|
|
735
1370
|
const { candidateId, testId } = req.body;
|
|
736
1371
|
if (!candidateId || !testId) {
|
|
@@ -786,7 +1421,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
786
1421
|
res.status(500).json({ message: 'Internal server error' });
|
|
787
1422
|
}
|
|
788
1423
|
});
|
|
789
|
-
|
|
1424
|
+
/**
|
|
1425
|
+
* @swagger
|
|
1426
|
+
* /exams/result/getNextQuestion/{id}/{idCurrentQuestion}:
|
|
1427
|
+
* get:
|
|
1428
|
+
* summary: Obtenir la question suivante
|
|
1429
|
+
* description: Retourne la prochaine question pour un TestResult ou null s'il n'y en a plus.
|
|
1430
|
+
* tags: [Résultats]
|
|
1431
|
+
* parameters:
|
|
1432
|
+
* - in: path
|
|
1433
|
+
* name: id
|
|
1434
|
+
* required: true
|
|
1435
|
+
* schema:
|
|
1436
|
+
* type: string
|
|
1437
|
+
* - in: path
|
|
1438
|
+
* name: idCurrentQuestion
|
|
1439
|
+
* required: true
|
|
1440
|
+
* schema:
|
|
1441
|
+
* type: string
|
|
1442
|
+
* responses:
|
|
1443
|
+
* 200:
|
|
1444
|
+
* description: Prochaine question ou null
|
|
1445
|
+
* 404:
|
|
1446
|
+
* description: Résultat ou test non trouvé
|
|
1447
|
+
* 500:
|
|
1448
|
+
* description: Erreur interne
|
|
1449
|
+
*/
|
|
790
1450
|
this.get('/result/getNextQuestion/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
791
1451
|
const { id, idCurrentQuestion } = req.params;
|
|
792
1452
|
try {
|
|
@@ -812,7 +1472,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
812
1472
|
res.status(500).json({ message: 'Internal server error' });
|
|
813
1473
|
}
|
|
814
1474
|
});
|
|
815
|
-
|
|
1475
|
+
/**
|
|
1476
|
+
* @swagger
|
|
1477
|
+
* /exams/result/isLastQuestion/{id}/{idCurrentQuestion}:
|
|
1478
|
+
* get:
|
|
1479
|
+
* summary: Vérifier la dernière question
|
|
1480
|
+
* description: Indique si la question courante est la dernière du test.
|
|
1481
|
+
* tags: [Résultats]
|
|
1482
|
+
* parameters:
|
|
1483
|
+
* - in: path
|
|
1484
|
+
* name: id
|
|
1485
|
+
* required: true
|
|
1486
|
+
* schema:
|
|
1487
|
+
* type: string
|
|
1488
|
+
* - in: path
|
|
1489
|
+
* name: idCurrentQuestion
|
|
1490
|
+
* required: true
|
|
1491
|
+
* schema:
|
|
1492
|
+
* type: string
|
|
1493
|
+
* responses:
|
|
1494
|
+
* 200:
|
|
1495
|
+
* description: Booléen retourné
|
|
1496
|
+
* 404:
|
|
1497
|
+
* description: Résultat ou test non trouvé
|
|
1498
|
+
* 500:
|
|
1499
|
+
* description: Erreur interne
|
|
1500
|
+
*/
|
|
816
1501
|
this.get('/result/isLastQuestion/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
817
1502
|
const { id, idCurrentQuestion } = req.params;
|
|
818
1503
|
try {
|
|
@@ -837,7 +1522,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
837
1522
|
res.status(500).json({ message: 'Internal server error' });
|
|
838
1523
|
}
|
|
839
1524
|
});
|
|
840
|
-
|
|
1525
|
+
/**
|
|
1526
|
+
* @swagger
|
|
1527
|
+
* /exams/result/question/{questionId}:
|
|
1528
|
+
* get:
|
|
1529
|
+
* summary: Obtenir une question (résultat)
|
|
1530
|
+
* description: Retourne une question via son identifiant pour un résultat.
|
|
1531
|
+
* tags: [Résultats]
|
|
1532
|
+
* parameters:
|
|
1533
|
+
* - in: path
|
|
1534
|
+
* name: questionId
|
|
1535
|
+
* required: true
|
|
1536
|
+
* schema:
|
|
1537
|
+
* type: string
|
|
1538
|
+
* responses:
|
|
1539
|
+
* 200:
|
|
1540
|
+
* description: Question trouvée
|
|
1541
|
+
* 404:
|
|
1542
|
+
* description: Question non trouvée
|
|
1543
|
+
* 500:
|
|
1544
|
+
* description: Erreur interne
|
|
1545
|
+
*/
|
|
841
1546
|
this.get('/result/question/:questionId', authenticatedOptions, async (req, res) => {
|
|
842
1547
|
const { questionId } = req.params;
|
|
843
1548
|
try {
|
|
@@ -852,7 +1557,41 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
852
1557
|
res.status(500).json({ message: 'Internal server error' });
|
|
853
1558
|
}
|
|
854
1559
|
});
|
|
855
|
-
|
|
1560
|
+
/**
|
|
1561
|
+
* @swagger
|
|
1562
|
+
* /exams/result/sendResponse/{id}/{idCurrentQuestion}:
|
|
1563
|
+
* put:
|
|
1564
|
+
* summary: Envoyer une réponse
|
|
1565
|
+
* description: Enregistre la réponse d'un candidat pour une question d'un test.
|
|
1566
|
+
* tags: [Résultats]
|
|
1567
|
+
* parameters:
|
|
1568
|
+
* - in: path
|
|
1569
|
+
* name: id
|
|
1570
|
+
* required: true
|
|
1571
|
+
* schema:
|
|
1572
|
+
* type: string
|
|
1573
|
+
* - in: path
|
|
1574
|
+
* name: idCurrentQuestion
|
|
1575
|
+
* required: true
|
|
1576
|
+
* schema:
|
|
1577
|
+
* type: string
|
|
1578
|
+
* requestBody:
|
|
1579
|
+
* required: true
|
|
1580
|
+
* content:
|
|
1581
|
+
* application/json:
|
|
1582
|
+
* schema:
|
|
1583
|
+
* type: object
|
|
1584
|
+
* properties:
|
|
1585
|
+
* candidateResponse:
|
|
1586
|
+
* type: string
|
|
1587
|
+
* responses:
|
|
1588
|
+
* 200:
|
|
1589
|
+
* description: Réponse enregistrée
|
|
1590
|
+
* 404:
|
|
1591
|
+
* description: Test ou résultat non trouvé
|
|
1592
|
+
* 500:
|
|
1593
|
+
* description: Erreur interne
|
|
1594
|
+
*/
|
|
856
1595
|
this.put('/result/sendResponse/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
857
1596
|
const { id, idCurrentQuestion } = req.params;
|
|
858
1597
|
const { candidateResponse } = req.body;
|
|
@@ -889,7 +1628,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
889
1628
|
res.status(500).json({ message: 'Internal server error' });
|
|
890
1629
|
}
|
|
891
1630
|
});
|
|
892
|
-
|
|
1631
|
+
/**
|
|
1632
|
+
* @swagger
|
|
1633
|
+
* /exams/result/correct/{id}:
|
|
1634
|
+
* post:
|
|
1635
|
+
* summary: Lancer la correction d'un test
|
|
1636
|
+
* description: Déclenche la correction d'un TestResult.
|
|
1637
|
+
* tags: [Résultats]
|
|
1638
|
+
* parameters:
|
|
1639
|
+
* - in: path
|
|
1640
|
+
* name: id
|
|
1641
|
+
* required: true
|
|
1642
|
+
* schema:
|
|
1643
|
+
* type: string
|
|
1644
|
+
* responses:
|
|
1645
|
+
* 200:
|
|
1646
|
+
* description: Correction lancée
|
|
1647
|
+
* 404:
|
|
1648
|
+
* description: Résultat non trouvé
|
|
1649
|
+
* 500:
|
|
1650
|
+
* description: Erreur interne
|
|
1651
|
+
*/
|
|
893
1652
|
this.post('/result/correct/:id', authenticatedOptions, async (req, res) => {
|
|
894
1653
|
const { id } = req.params;
|
|
895
1654
|
try {
|
|
@@ -905,7 +1664,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
905
1664
|
res.status(500).json({ message: 'Internal server error' });
|
|
906
1665
|
}
|
|
907
1666
|
});
|
|
908
|
-
|
|
1667
|
+
/**
|
|
1668
|
+
* @swagger
|
|
1669
|
+
* /exams/result/calculateScore/{id}:
|
|
1670
|
+
* put:
|
|
1671
|
+
* summary: Calculer le score
|
|
1672
|
+
* description: Calcule et enregistre le score final d'un TestResult.
|
|
1673
|
+
* tags: [Résultats]
|
|
1674
|
+
* parameters:
|
|
1675
|
+
* - in: path
|
|
1676
|
+
* name: id
|
|
1677
|
+
* required: true
|
|
1678
|
+
* schema:
|
|
1679
|
+
* type: string
|
|
1680
|
+
* responses:
|
|
1681
|
+
* 200:
|
|
1682
|
+
* description: Score calculé
|
|
1683
|
+
* 404:
|
|
1684
|
+
* description: Résultat non trouvé
|
|
1685
|
+
* 500:
|
|
1686
|
+
* description: Erreur interne
|
|
1687
|
+
*/
|
|
909
1688
|
this.put('/result/calculateScore/:id', authenticatedOptions, async (req, res) => {
|
|
910
1689
|
const { id } = req.params;
|
|
911
1690
|
try {
|
|
@@ -948,7 +1727,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
948
1727
|
res.status(500).json({ message: 'Internal server error' });
|
|
949
1728
|
}
|
|
950
1729
|
});
|
|
951
|
-
|
|
1730
|
+
/**
|
|
1731
|
+
* @swagger
|
|
1732
|
+
* /exams/maxscore/{resultId}:
|
|
1733
|
+
* get:
|
|
1734
|
+
* summary: Obtenir le score maximum
|
|
1735
|
+
* description: Calcule le score maximal possible pour un résultat.
|
|
1736
|
+
* tags: [Résultats]
|
|
1737
|
+
* parameters:
|
|
1738
|
+
* - in: path
|
|
1739
|
+
* name: resultId
|
|
1740
|
+
* required: true
|
|
1741
|
+
* schema:
|
|
1742
|
+
* type: string
|
|
1743
|
+
* responses:
|
|
1744
|
+
* 200:
|
|
1745
|
+
* description: Score maximum retourné
|
|
1746
|
+
* 404:
|
|
1747
|
+
* description: Résultat ou test non trouvé
|
|
1748
|
+
* 500:
|
|
1749
|
+
* description: Erreur interne
|
|
1750
|
+
*/
|
|
952
1751
|
this.get('/maxscore/:resultId', authenticatedOptions, async (req, res) => {
|
|
953
1752
|
const { resultId } = req.params;
|
|
954
1753
|
try {
|
|
@@ -974,7 +1773,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
974
1773
|
res.status(500).json({ message: 'Internal server error' });
|
|
975
1774
|
}
|
|
976
1775
|
});
|
|
977
|
-
|
|
1776
|
+
/**
|
|
1777
|
+
* @swagger
|
|
1778
|
+
* /exams/result/score/{id}:
|
|
1779
|
+
* get:
|
|
1780
|
+
* summary: Obtenir le score d'un résultat
|
|
1781
|
+
* description: Retourne le score calculé d'un TestResult.
|
|
1782
|
+
* tags: [Résultats]
|
|
1783
|
+
* parameters:
|
|
1784
|
+
* - in: path
|
|
1785
|
+
* name: id
|
|
1786
|
+
* required: true
|
|
1787
|
+
* schema:
|
|
1788
|
+
* type: string
|
|
1789
|
+
* responses:
|
|
1790
|
+
* 200:
|
|
1791
|
+
* description: Score retourné
|
|
1792
|
+
* 404:
|
|
1793
|
+
* description: Résultat non trouvé
|
|
1794
|
+
* 500:
|
|
1795
|
+
* description: Erreur interne
|
|
1796
|
+
*/
|
|
978
1797
|
this.get('/result/score/:id', authenticatedOptions, async (req, res) => {
|
|
979
1798
|
const { id } = req.params;
|
|
980
1799
|
try {
|
|
@@ -989,7 +1808,43 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
989
1808
|
res.status(500).json({ message: 'Internal server error' });
|
|
990
1809
|
}
|
|
991
1810
|
});
|
|
992
|
-
|
|
1811
|
+
/**
|
|
1812
|
+
* @swagger
|
|
1813
|
+
* /exams/test/generateQuestions/{id}:
|
|
1814
|
+
* put:
|
|
1815
|
+
* summary: Générer des questions pour un test
|
|
1816
|
+
* description: Génère plusieurs questions (IA) pour un test donné, éventuellement filtrées par catégorie et type.
|
|
1817
|
+
* tags: [Examens]
|
|
1818
|
+
* parameters:
|
|
1819
|
+
* - in: path
|
|
1820
|
+
* name: id
|
|
1821
|
+
* required: true
|
|
1822
|
+
* schema:
|
|
1823
|
+
* type: string
|
|
1824
|
+
* requestBody:
|
|
1825
|
+
* required: true
|
|
1826
|
+
* content:
|
|
1827
|
+
* application/json:
|
|
1828
|
+
* schema:
|
|
1829
|
+
* type: object
|
|
1830
|
+
* required: [numberOfQuestions]
|
|
1831
|
+
* properties:
|
|
1832
|
+
* numberOfQuestions:
|
|
1833
|
+
* type: integer
|
|
1834
|
+
* category:
|
|
1835
|
+
* type: string
|
|
1836
|
+
* questionType:
|
|
1837
|
+
* type: string
|
|
1838
|
+
* responses:
|
|
1839
|
+
* 200:
|
|
1840
|
+
* description: Questions générées
|
|
1841
|
+
* 400:
|
|
1842
|
+
* description: Paramètres invalides
|
|
1843
|
+
* 404:
|
|
1844
|
+
* description: Test non trouvé
|
|
1845
|
+
* 500:
|
|
1846
|
+
* description: Erreur interne
|
|
1847
|
+
*/
|
|
993
1848
|
this.put('/test/generateQuestions/:id', authenticatedOptions, async (req, res) => {
|
|
994
1849
|
const { id } = req.params;
|
|
995
1850
|
const { numberOfQuestions, category, questionType } = req.body;
|
|
@@ -1069,7 +1924,56 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1069
1924
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1070
1925
|
}
|
|
1071
1926
|
});
|
|
1072
|
-
|
|
1927
|
+
/**
|
|
1928
|
+
* @swagger
|
|
1929
|
+
* /exams/test/{testId}/candidates:
|
|
1930
|
+
* get:
|
|
1931
|
+
* summary: Lister les candidats invités
|
|
1932
|
+
* description: Liste paginée des candidats invités à un test avec filtres et tri.
|
|
1933
|
+
* tags: [Résultats]
|
|
1934
|
+
* parameters:
|
|
1935
|
+
* - in: path
|
|
1936
|
+
* name: testId
|
|
1937
|
+
* required: true
|
|
1938
|
+
* schema:
|
|
1939
|
+
* type: string
|
|
1940
|
+
* - in: query
|
|
1941
|
+
* name: page
|
|
1942
|
+
* schema:
|
|
1943
|
+
* type: integer
|
|
1944
|
+
* default: 1
|
|
1945
|
+
* - in: query
|
|
1946
|
+
* name: limit
|
|
1947
|
+
* schema:
|
|
1948
|
+
* type: integer
|
|
1949
|
+
* default: 10
|
|
1950
|
+
* - in: query
|
|
1951
|
+
* name: search
|
|
1952
|
+
* schema:
|
|
1953
|
+
* type: string
|
|
1954
|
+
* - in: query
|
|
1955
|
+
* name: state
|
|
1956
|
+
* schema:
|
|
1957
|
+
* type: string
|
|
1958
|
+
* default: all
|
|
1959
|
+
* - in: query
|
|
1960
|
+
* name: sortBy
|
|
1961
|
+
* schema:
|
|
1962
|
+
* type: string
|
|
1963
|
+
* default: invitationDate
|
|
1964
|
+
* - in: query
|
|
1965
|
+
* name: sortOrder
|
|
1966
|
+
* schema:
|
|
1967
|
+
* type: string
|
|
1968
|
+
* default: desc
|
|
1969
|
+
* responses:
|
|
1970
|
+
* 200:
|
|
1971
|
+
* description: Candidats paginés
|
|
1972
|
+
* 404:
|
|
1973
|
+
* description: Test non trouvé
|
|
1974
|
+
* 500:
|
|
1975
|
+
* description: Erreur interne
|
|
1976
|
+
*/
|
|
1073
1977
|
this.get('/test/:testId/candidates', authenticatedOptions, async (req, res) => {
|
|
1074
1978
|
const { testId } = req.params;
|
|
1075
1979
|
const page = parseInt(req.query.page) || 1;
|
|
@@ -1231,7 +2135,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1231
2135
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1232
2136
|
}
|
|
1233
2137
|
});
|
|
1234
|
-
|
|
2138
|
+
/**
|
|
2139
|
+
* @swagger
|
|
2140
|
+
* /exams/reinvite/{resultId}:
|
|
2141
|
+
* post:
|
|
2142
|
+
* summary: Renvoyer une invitation
|
|
2143
|
+
* description: Récupère un TestResult et renvoie l'email d'invitation.
|
|
2144
|
+
* tags: [Résultats]
|
|
2145
|
+
* parameters:
|
|
2146
|
+
* - in: path
|
|
2147
|
+
* name: resultId
|
|
2148
|
+
* required: true
|
|
2149
|
+
* schema:
|
|
2150
|
+
* type: string
|
|
2151
|
+
* responses:
|
|
2152
|
+
* 200:
|
|
2153
|
+
* description: Invitation renvoyée
|
|
2154
|
+
* 404:
|
|
2155
|
+
* description: Ressource non trouvée
|
|
2156
|
+
* 500:
|
|
2157
|
+
* description: Erreur interne
|
|
2158
|
+
*/
|
|
1235
2159
|
this.post('/reinvite/:resultId', authenticatedOptions, async (req, res) => {
|
|
1236
2160
|
const { resultId } = req.params;
|
|
1237
2161
|
try {
|
|
@@ -1280,7 +2204,45 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1280
2204
|
res.status(500).json({ message: 'Internal server error' });
|
|
1281
2205
|
}
|
|
1282
2206
|
});
|
|
1283
|
-
|
|
2207
|
+
/**
|
|
2208
|
+
* @swagger
|
|
2209
|
+
* /exams/result/{testResultId}/response/{questionId}:
|
|
2210
|
+
* put:
|
|
2211
|
+
* summary: Correction manuelle d'une réponse
|
|
2212
|
+
* description: Met à jour le score/commentaire d'une réponse et recalcule le score global.
|
|
2213
|
+
* tags: [Résultats]
|
|
2214
|
+
* parameters:
|
|
2215
|
+
* - in: path
|
|
2216
|
+
* name: testResultId
|
|
2217
|
+
* required: true
|
|
2218
|
+
* schema:
|
|
2219
|
+
* type: string
|
|
2220
|
+
* - in: path
|
|
2221
|
+
* name: questionId
|
|
2222
|
+
* required: true
|
|
2223
|
+
* schema:
|
|
2224
|
+
* type: string
|
|
2225
|
+
* requestBody:
|
|
2226
|
+
* required: true
|
|
2227
|
+
* content:
|
|
2228
|
+
* application/json:
|
|
2229
|
+
* schema:
|
|
2230
|
+
* type: object
|
|
2231
|
+
* properties:
|
|
2232
|
+
* score:
|
|
2233
|
+
* type: number
|
|
2234
|
+
* comment:
|
|
2235
|
+
* type: string
|
|
2236
|
+
* responses:
|
|
2237
|
+
* 200:
|
|
2238
|
+
* description: Correction enregistrée
|
|
2239
|
+
* 404:
|
|
2240
|
+
* description: Ressource non trouvée
|
|
2241
|
+
* 400:
|
|
2242
|
+
* description: Score invalide
|
|
2243
|
+
* 500:
|
|
2244
|
+
* description: Erreur interne
|
|
2245
|
+
*/
|
|
1284
2246
|
this.put('/result/:testResultId/response/:questionId', authenticatedOptions, async (req, res) => {
|
|
1285
2247
|
try {
|
|
1286
2248
|
const { testResultId, questionId } = req.params;
|
|
@@ -1323,7 +2285,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1323
2285
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1324
2286
|
}
|
|
1325
2287
|
});
|
|
1326
|
-
|
|
2288
|
+
/**
|
|
2289
|
+
* @swagger
|
|
2290
|
+
* /exams/result/{id}:
|
|
2291
|
+
* delete:
|
|
2292
|
+
* summary: Supprimer un résultat
|
|
2293
|
+
* description: Supprime un TestResult.
|
|
2294
|
+
* tags: [Résultats]
|
|
2295
|
+
* parameters:
|
|
2296
|
+
* - in: path
|
|
2297
|
+
* name: id
|
|
2298
|
+
* required: true
|
|
2299
|
+
* schema:
|
|
2300
|
+
* type: string
|
|
2301
|
+
* responses:
|
|
2302
|
+
* 200:
|
|
2303
|
+
* description: Résultat supprimé
|
|
2304
|
+
* 404:
|
|
2305
|
+
* description: Résultat non trouvé
|
|
2306
|
+
* 500:
|
|
2307
|
+
* description: Erreur interne
|
|
2308
|
+
*/
|
|
1327
2309
|
this.delete('/result/:id', authenticatedOptions, async (req, res) => {
|
|
1328
2310
|
const { id } = req.params;
|
|
1329
2311
|
try {
|