@programisto/edrm-exams 0.3.8 → 0.3.10
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/modules/edrm-exams/models/test.model.d.ts +3 -0
- package/dist/modules/edrm-exams/models/test.model.js +7 -0
- package/dist/modules/edrm-exams/routes/company.router.js +135 -6
- package/dist/modules/edrm-exams/routes/exams-candidate.router.js +226 -7
- package/dist/modules/edrm-exams/routes/exams.router.js +1072 -41
- package/dist/modules/edrm-exams/routes/result.router.js +152 -5
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -239,7 +404,8 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
239
404
|
targetJob: targetJobId,
|
|
240
405
|
seniorityLevel,
|
|
241
406
|
state,
|
|
242
|
-
categories: processedCategories
|
|
407
|
+
categories: processedCategories,
|
|
408
|
+
...(req.entity?._id && { entityId: req.entity._id })
|
|
243
409
|
});
|
|
244
410
|
await newTest.save();
|
|
245
411
|
res.status(201).json({ message: 'test created with sucess', data: newTest });
|
|
@@ -249,7 +415,35 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
249
415
|
res.status(500).json({ message: 'Internal server error' });
|
|
250
416
|
}
|
|
251
417
|
});
|
|
252
|
-
|
|
418
|
+
/**
|
|
419
|
+
* @swagger
|
|
420
|
+
* /exams/test/{id}:
|
|
421
|
+
* put:
|
|
422
|
+
* summary: Modifier un test
|
|
423
|
+
* description: Met à jour les métadonnées, catégories ou questions d'un test.
|
|
424
|
+
* tags: [Examens]
|
|
425
|
+
* parameters:
|
|
426
|
+
* - in: path
|
|
427
|
+
* name: id
|
|
428
|
+
* required: true
|
|
429
|
+
* schema:
|
|
430
|
+
* type: string
|
|
431
|
+
* requestBody:
|
|
432
|
+
* required: true
|
|
433
|
+
* content:
|
|
434
|
+
* application/json:
|
|
435
|
+
* schema:
|
|
436
|
+
* type: object
|
|
437
|
+
* responses:
|
|
438
|
+
* 200:
|
|
439
|
+
* description: Test modifié
|
|
440
|
+
* 404:
|
|
441
|
+
* description: Test non trouvé
|
|
442
|
+
* 400:
|
|
443
|
+
* description: Données invalides
|
|
444
|
+
* 500:
|
|
445
|
+
* description: Erreur interne
|
|
446
|
+
*/
|
|
253
447
|
this.put('/test/:id', authenticatedOptions, async (req, res) => {
|
|
254
448
|
const { id } = req.params;
|
|
255
449
|
const { title, description, targetJob, seniorityLevel, categories, state, questions } = req.body;
|
|
@@ -258,6 +452,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
258
452
|
if (!test) {
|
|
259
453
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
260
454
|
}
|
|
455
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
456
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
457
|
+
}
|
|
261
458
|
if (title)
|
|
262
459
|
test.title = title;
|
|
263
460
|
if (description)
|
|
@@ -318,7 +515,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
318
515
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
319
516
|
}
|
|
320
517
|
});
|
|
321
|
-
|
|
518
|
+
/**
|
|
519
|
+
* @swagger
|
|
520
|
+
* /exams/test/{id}:
|
|
521
|
+
* delete:
|
|
522
|
+
* summary: Supprimer un test
|
|
523
|
+
* description: Supprime un test ainsi que ses questions et résultats associés.
|
|
524
|
+
* tags: [Examens]
|
|
525
|
+
* parameters:
|
|
526
|
+
* - in: path
|
|
527
|
+
* name: id
|
|
528
|
+
* required: true
|
|
529
|
+
* schema:
|
|
530
|
+
* type: string
|
|
531
|
+
* responses:
|
|
532
|
+
* 200:
|
|
533
|
+
* description: Test supprimé
|
|
534
|
+
* 404:
|
|
535
|
+
* description: Test non trouvé
|
|
536
|
+
* 500:
|
|
537
|
+
* description: Erreur interne
|
|
538
|
+
*/
|
|
322
539
|
this.delete('/test/:id', authenticatedOptions, async (req, res) => {
|
|
323
540
|
const { id } = req.params;
|
|
324
541
|
try {
|
|
@@ -326,6 +543,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
326
543
|
if (!test) {
|
|
327
544
|
return res.status(404).json({ message: 'Test not found' });
|
|
328
545
|
}
|
|
546
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
547
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
548
|
+
}
|
|
329
549
|
for (let i = 0; i < test.questions.length; i++) {
|
|
330
550
|
await TestQuestion.findByIdAndDelete(test.questions[i].questionId);
|
|
331
551
|
}
|
|
@@ -338,7 +558,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
338
558
|
res.status(500).json({ message: 'Internal server error' });
|
|
339
559
|
}
|
|
340
560
|
});
|
|
341
|
-
|
|
561
|
+
/**
|
|
562
|
+
* @swagger
|
|
563
|
+
* /exams/test/{id}:
|
|
564
|
+
* get:
|
|
565
|
+
* summary: Détail d'un test
|
|
566
|
+
* description: Retourne un test avec ses questions et nom du job cible.
|
|
567
|
+
* tags: [Examens]
|
|
568
|
+
* parameters:
|
|
569
|
+
* - in: path
|
|
570
|
+
* name: id
|
|
571
|
+
* required: true
|
|
572
|
+
* schema:
|
|
573
|
+
* type: string
|
|
574
|
+
* responses:
|
|
575
|
+
* 200:
|
|
576
|
+
* description: Test trouvé
|
|
577
|
+
* 404:
|
|
578
|
+
* description: Test non trouvé
|
|
579
|
+
* 500:
|
|
580
|
+
* description: Erreur interne
|
|
581
|
+
*/
|
|
342
582
|
this.get('/test/:id', authenticatedOptions, async (req, res) => {
|
|
343
583
|
const { id } = req.params;
|
|
344
584
|
try {
|
|
@@ -346,6 +586,10 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
346
586
|
if (!test) {
|
|
347
587
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
348
588
|
}
|
|
589
|
+
// Vérifier que le test appartient à l'entité courante (multi-entités)
|
|
590
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
591
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
592
|
+
}
|
|
349
593
|
// Migration automatique si nécessaire
|
|
350
594
|
await migrateTestIfNeeded(test);
|
|
351
595
|
const questions = [];
|
|
@@ -367,7 +611,59 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
367
611
|
res.status(500).json({ message: 'Internal server error' });
|
|
368
612
|
}
|
|
369
613
|
});
|
|
370
|
-
|
|
614
|
+
/**
|
|
615
|
+
* @swagger
|
|
616
|
+
* /exams:
|
|
617
|
+
* get:
|
|
618
|
+
* summary: Lister les tests
|
|
619
|
+
* description: Liste paginée des tests avec filtres, recherche et tri.
|
|
620
|
+
* tags: [Examens]
|
|
621
|
+
* parameters:
|
|
622
|
+
* - in: query
|
|
623
|
+
* name: page
|
|
624
|
+
* schema:
|
|
625
|
+
* type: integer
|
|
626
|
+
* default: 1
|
|
627
|
+
* - in: query
|
|
628
|
+
* name: limit
|
|
629
|
+
* schema:
|
|
630
|
+
* type: integer
|
|
631
|
+
* default: 10
|
|
632
|
+
* - in: query
|
|
633
|
+
* name: search
|
|
634
|
+
* schema:
|
|
635
|
+
* type: string
|
|
636
|
+
* - in: query
|
|
637
|
+
* name: targetJob
|
|
638
|
+
* schema:
|
|
639
|
+
* type: string
|
|
640
|
+
* default: all
|
|
641
|
+
* - in: query
|
|
642
|
+
* name: seniorityLevel
|
|
643
|
+
* schema:
|
|
644
|
+
* type: string
|
|
645
|
+
* default: all
|
|
646
|
+
* - in: query
|
|
647
|
+
* name: state
|
|
648
|
+
* schema:
|
|
649
|
+
* type: string
|
|
650
|
+
* default: all
|
|
651
|
+
* - in: query
|
|
652
|
+
* name: sortBy
|
|
653
|
+
* schema:
|
|
654
|
+
* type: string
|
|
655
|
+
* default: updatedAt
|
|
656
|
+
* - in: query
|
|
657
|
+
* name: sortOrder
|
|
658
|
+
* schema:
|
|
659
|
+
* type: string
|
|
660
|
+
* default: desc
|
|
661
|
+
* responses:
|
|
662
|
+
* 200:
|
|
663
|
+
* description: Tests paginés
|
|
664
|
+
* 500:
|
|
665
|
+
* description: Erreur interne
|
|
666
|
+
*/
|
|
371
667
|
this.get('/', authenticatedOptions, async (req, res) => {
|
|
372
668
|
try {
|
|
373
669
|
const page = parseInt(req.query.page) || 1;
|
|
@@ -381,6 +677,10 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
381
677
|
const sortOrder = req.query.sortOrder || 'desc';
|
|
382
678
|
// Construction de la requête de recherche
|
|
383
679
|
const query = {};
|
|
680
|
+
// Filtre par entité (contexte multi-entités)
|
|
681
|
+
if (req.entity?._id) {
|
|
682
|
+
query.entityId = req.entity._id;
|
|
683
|
+
}
|
|
384
684
|
// Filtres
|
|
385
685
|
if (targetJob !== 'all') {
|
|
386
686
|
// Si on filtre par targetJob, on cherche d'abord le TestJob correspondant
|
|
@@ -462,7 +762,36 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
462
762
|
res.status(500).json({ message: 'Internal server error' });
|
|
463
763
|
}
|
|
464
764
|
});
|
|
465
|
-
|
|
765
|
+
/**
|
|
766
|
+
* @swagger
|
|
767
|
+
* /exams/test/removeCategory/{testId}:
|
|
768
|
+
* delete:
|
|
769
|
+
* summary: Retirer une catégorie d'un test
|
|
770
|
+
* description: Supprime une catégorie d'un test par son nom.
|
|
771
|
+
* tags: [Examens]
|
|
772
|
+
* parameters:
|
|
773
|
+
* - in: path
|
|
774
|
+
* name: testId
|
|
775
|
+
* required: true
|
|
776
|
+
* schema:
|
|
777
|
+
* type: string
|
|
778
|
+
* requestBody:
|
|
779
|
+
* required: true
|
|
780
|
+
* content:
|
|
781
|
+
* application/json:
|
|
782
|
+
* schema:
|
|
783
|
+
* type: object
|
|
784
|
+
* properties:
|
|
785
|
+
* categoryName:
|
|
786
|
+
* type: string
|
|
787
|
+
* responses:
|
|
788
|
+
* 200:
|
|
789
|
+
* description: Catégorie supprimée
|
|
790
|
+
* 404:
|
|
791
|
+
* description: Test ou catégorie non trouvé
|
|
792
|
+
* 500:
|
|
793
|
+
* description: Erreur interne
|
|
794
|
+
*/
|
|
466
795
|
this.delete('/test/removeCategory/:testId', authenticatedOptions, async (req, res) => {
|
|
467
796
|
const { testId } = req.params;
|
|
468
797
|
const { categoryName } = req.body;
|
|
@@ -470,9 +799,13 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
470
799
|
const category = await TestCategory.findOne({ name: categoryName });
|
|
471
800
|
if (!category)
|
|
472
801
|
return res.status(404).json({ message: 'Category not found' });
|
|
473
|
-
|
|
802
|
+
let test = await Test.findById(testId);
|
|
474
803
|
if (!test)
|
|
475
804
|
return res.status(404).json({ message: 'Test not found' });
|
|
805
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
806
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
807
|
+
}
|
|
808
|
+
test = await Test.findByIdAndUpdate(testId, { $pull: { categories: { categoryId: category._id } } }, { new: true });
|
|
476
809
|
res.status(200).json({ message: 'Category removed', test });
|
|
477
810
|
}
|
|
478
811
|
catch (err) {
|
|
@@ -480,7 +813,38 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
480
813
|
res.status(500).json({ message: 'Internal server error' });
|
|
481
814
|
}
|
|
482
815
|
});
|
|
483
|
-
|
|
816
|
+
/**
|
|
817
|
+
* @swagger
|
|
818
|
+
* /exams/test/addCategory/{testId}:
|
|
819
|
+
* put:
|
|
820
|
+
* summary: Ajouter une catégorie à un test
|
|
821
|
+
* description: Ajoute une catégorie (créée si besoin) à un test avec niveau d'expertise.
|
|
822
|
+
* tags: [Examens]
|
|
823
|
+
* parameters:
|
|
824
|
+
* - in: path
|
|
825
|
+
* name: testId
|
|
826
|
+
* required: true
|
|
827
|
+
* schema:
|
|
828
|
+
* type: string
|
|
829
|
+
* requestBody:
|
|
830
|
+
* required: true
|
|
831
|
+
* content:
|
|
832
|
+
* application/json:
|
|
833
|
+
* schema:
|
|
834
|
+
* type: object
|
|
835
|
+
* properties:
|
|
836
|
+
* categoryName:
|
|
837
|
+
* type: string
|
|
838
|
+
* expertiseLevel:
|
|
839
|
+
* type: string
|
|
840
|
+
* responses:
|
|
841
|
+
* 200:
|
|
842
|
+
* description: Catégorie ajoutée
|
|
843
|
+
* 404:
|
|
844
|
+
* description: Test non trouvé
|
|
845
|
+
* 500:
|
|
846
|
+
* description: Erreur interne
|
|
847
|
+
*/
|
|
484
848
|
this.put('/test/addCategory/:testId', authenticatedOptions, async (req, res) => {
|
|
485
849
|
const { testId } = req.params;
|
|
486
850
|
const { categoryName, expertiseLevel } = req.body;
|
|
@@ -494,6 +858,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
494
858
|
if (!test) {
|
|
495
859
|
return res.status(404).json({ message: 'Test not found' });
|
|
496
860
|
}
|
|
861
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
862
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
863
|
+
}
|
|
497
864
|
const categoryExists = test.categories.some(cat => cat.categoryId.equals(category._id));
|
|
498
865
|
if (categoryExists) {
|
|
499
866
|
return res.status(200).json({ message: 'Category already exists in the test' });
|
|
@@ -507,7 +874,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
507
874
|
res.status(500).json({ message: 'Internal server error' });
|
|
508
875
|
}
|
|
509
876
|
});
|
|
510
|
-
|
|
877
|
+
/**
|
|
878
|
+
* @swagger
|
|
879
|
+
* /exams/test/question/{questionId}:
|
|
880
|
+
* get:
|
|
881
|
+
* summary: Détail d'une question
|
|
882
|
+
* description: Retourne une question de test par son identifiant.
|
|
883
|
+
* tags: [Examens]
|
|
884
|
+
* parameters:
|
|
885
|
+
* - in: path
|
|
886
|
+
* name: questionId
|
|
887
|
+
* required: true
|
|
888
|
+
* schema:
|
|
889
|
+
* type: string
|
|
890
|
+
* responses:
|
|
891
|
+
* 200:
|
|
892
|
+
* description: Question trouvée
|
|
893
|
+
* 404:
|
|
894
|
+
* description: Question non trouvée
|
|
895
|
+
* 500:
|
|
896
|
+
* description: Erreur interne
|
|
897
|
+
*/
|
|
511
898
|
this.get('/test/question/:questionId', authenticatedOptions, async (req, res) => {
|
|
512
899
|
const { questionId } = req.params;
|
|
513
900
|
const question = await TestQuestion.findById(questionId);
|
|
@@ -516,7 +903,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
516
903
|
}
|
|
517
904
|
res.status(200).json({ data: question });
|
|
518
905
|
});
|
|
519
|
-
|
|
906
|
+
/**
|
|
907
|
+
* @swagger
|
|
908
|
+
* /exams/test/questions/{testId}:
|
|
909
|
+
* get:
|
|
910
|
+
* summary: Lister les questions d'un test
|
|
911
|
+
* description: Retourne toutes les questions d'un test.
|
|
912
|
+
* tags: [Examens]
|
|
913
|
+
* parameters:
|
|
914
|
+
* - in: path
|
|
915
|
+
* name: testId
|
|
916
|
+
* required: true
|
|
917
|
+
* schema:
|
|
918
|
+
* type: string
|
|
919
|
+
* responses:
|
|
920
|
+
* 200:
|
|
921
|
+
* description: Questions retournées
|
|
922
|
+
* 404:
|
|
923
|
+
* description: Test non trouvé
|
|
924
|
+
* 500:
|
|
925
|
+
* description: Erreur interne
|
|
926
|
+
*/
|
|
520
927
|
this.get('/test/questions/:testId', authenticatedOptions, async (req, res) => {
|
|
521
928
|
const { testId } = req.params;
|
|
522
929
|
try {
|
|
@@ -524,6 +931,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
524
931
|
if (!test) {
|
|
525
932
|
return res.status(404).json({ message: 'Test not found' });
|
|
526
933
|
}
|
|
934
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
935
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
936
|
+
}
|
|
527
937
|
const questions = [];
|
|
528
938
|
for (const questionId of test.questions) {
|
|
529
939
|
const question = await TestQuestion.findById(questionId);
|
|
@@ -538,7 +948,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
538
948
|
res.status(500).json({ message: 'Internal server error' });
|
|
539
949
|
}
|
|
540
950
|
});
|
|
541
|
-
|
|
951
|
+
/**
|
|
952
|
+
* @swagger
|
|
953
|
+
* /exams/test/question/{testId}/{questionId}:
|
|
954
|
+
* delete:
|
|
955
|
+
* summary: Supprimer une question d'un test
|
|
956
|
+
* description: Supprime une question spécifique et réordonne les questions restantes.
|
|
957
|
+
* tags: [Examens]
|
|
958
|
+
* parameters:
|
|
959
|
+
* - in: path
|
|
960
|
+
* name: testId
|
|
961
|
+
* required: true
|
|
962
|
+
* schema:
|
|
963
|
+
* type: string
|
|
964
|
+
* - in: path
|
|
965
|
+
* name: questionId
|
|
966
|
+
* required: true
|
|
967
|
+
* schema:
|
|
968
|
+
* type: string
|
|
969
|
+
* responses:
|
|
970
|
+
* 200:
|
|
971
|
+
* description: Question supprimée
|
|
972
|
+
* 404:
|
|
973
|
+
* description: Test ou question non trouvés
|
|
974
|
+
* 500:
|
|
975
|
+
* description: Erreur interne
|
|
976
|
+
*/
|
|
542
977
|
this.delete('/test/question/:testId/:questionId', authenticatedOptions, async (req, res) => {
|
|
543
978
|
const { testId, questionId } = req.params;
|
|
544
979
|
const question = await TestQuestion.findByIdAndDelete(questionId);
|
|
@@ -549,6 +984,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
549
984
|
if (!test) {
|
|
550
985
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
551
986
|
}
|
|
987
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
988
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
989
|
+
}
|
|
552
990
|
// Supprimer la question du tableau questions en filtrant par questionId
|
|
553
991
|
test.questions = test.questions.filter(q => q.questionId.toString() !== questionId);
|
|
554
992
|
// Recalculer les ordres pour que ça se suive
|
|
@@ -558,13 +996,36 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
558
996
|
await test.save();
|
|
559
997
|
res.status(200).json({ message: 'question deleted with sucess' });
|
|
560
998
|
});
|
|
561
|
-
|
|
999
|
+
/**
|
|
1000
|
+
* @swagger
|
|
1001
|
+
* /exams/test/questions/{testId}:
|
|
1002
|
+
* delete:
|
|
1003
|
+
* summary: Supprimer toutes les questions d'un test
|
|
1004
|
+
* description: Supprime toutes les questions associées à un test.
|
|
1005
|
+
* tags: [Examens]
|
|
1006
|
+
* parameters:
|
|
1007
|
+
* - in: path
|
|
1008
|
+
* name: testId
|
|
1009
|
+
* required: true
|
|
1010
|
+
* schema:
|
|
1011
|
+
* type: string
|
|
1012
|
+
* responses:
|
|
1013
|
+
* 200:
|
|
1014
|
+
* description: Questions supprimées
|
|
1015
|
+
* 404:
|
|
1016
|
+
* description: Test non trouvé
|
|
1017
|
+
* 500:
|
|
1018
|
+
* description: Erreur interne
|
|
1019
|
+
*/
|
|
562
1020
|
this.delete('/test/questions/:testId', authenticatedOptions, async (req, res) => {
|
|
563
1021
|
const { testId } = req.params;
|
|
564
1022
|
const test = await Test.findById(testId);
|
|
565
1023
|
if (!test) {
|
|
566
1024
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
567
1025
|
}
|
|
1026
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1027
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1028
|
+
}
|
|
568
1029
|
for (const questionId of test.questions) {
|
|
569
1030
|
await TestQuestion.findByIdAndDelete(questionId);
|
|
570
1031
|
}
|
|
@@ -572,7 +1033,33 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
572
1033
|
await test.save();
|
|
573
1034
|
res.status(200).json({ message: 'questions deleted with sucess' });
|
|
574
1035
|
});
|
|
575
|
-
|
|
1036
|
+
/**
|
|
1037
|
+
* @swagger
|
|
1038
|
+
* /exams/test/modifyQuestion/{id}:
|
|
1039
|
+
* put:
|
|
1040
|
+
* summary: Modifier une question
|
|
1041
|
+
* description: Met à jour les champs d'une question (texte, score, temps, réponses possibles).
|
|
1042
|
+
* tags: [Examens]
|
|
1043
|
+
* parameters:
|
|
1044
|
+
* - in: path
|
|
1045
|
+
* name: id
|
|
1046
|
+
* required: true
|
|
1047
|
+
* schema:
|
|
1048
|
+
* type: string
|
|
1049
|
+
* requestBody:
|
|
1050
|
+
* required: true
|
|
1051
|
+
* content:
|
|
1052
|
+
* application/json:
|
|
1053
|
+
* schema:
|
|
1054
|
+
* type: object
|
|
1055
|
+
* responses:
|
|
1056
|
+
* 200:
|
|
1057
|
+
* description: Question modifiée
|
|
1058
|
+
* 404:
|
|
1059
|
+
* description: Question non trouvée
|
|
1060
|
+
* 500:
|
|
1061
|
+
* description: Erreur interne
|
|
1062
|
+
*/
|
|
576
1063
|
this.put('/test/modifyQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
577
1064
|
const { id } = req.params;
|
|
578
1065
|
const { instruction, maxScore, time, possibleResponses, textType } = req.body;
|
|
@@ -604,7 +1091,42 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
604
1091
|
res.status(500).json({ message: 'Internal server error' });
|
|
605
1092
|
}
|
|
606
1093
|
});
|
|
607
|
-
|
|
1094
|
+
/**
|
|
1095
|
+
* @swagger
|
|
1096
|
+
* /exams/test/addCustomQuestion/{id}:
|
|
1097
|
+
* put:
|
|
1098
|
+
* summary: Ajouter une question personnalisée
|
|
1099
|
+
* description: Ajoute une question manuelle à un test.
|
|
1100
|
+
* tags: [Examens]
|
|
1101
|
+
* parameters:
|
|
1102
|
+
* - in: path
|
|
1103
|
+
* name: id
|
|
1104
|
+
* required: true
|
|
1105
|
+
* schema:
|
|
1106
|
+
* type: string
|
|
1107
|
+
* requestBody:
|
|
1108
|
+
* required: true
|
|
1109
|
+
* content:
|
|
1110
|
+
* application/json:
|
|
1111
|
+
* schema:
|
|
1112
|
+
* type: object
|
|
1113
|
+
* properties:
|
|
1114
|
+
* questionType:
|
|
1115
|
+
* type: string
|
|
1116
|
+
* instruction:
|
|
1117
|
+
* type: string
|
|
1118
|
+
* maxScore:
|
|
1119
|
+
* type: number
|
|
1120
|
+
* time:
|
|
1121
|
+
* type: number
|
|
1122
|
+
* responses:
|
|
1123
|
+
* 200:
|
|
1124
|
+
* description: Question ajoutée
|
|
1125
|
+
* 404:
|
|
1126
|
+
* description: Test non trouvé
|
|
1127
|
+
* 500:
|
|
1128
|
+
* description: Erreur interne
|
|
1129
|
+
*/
|
|
608
1130
|
this.put('/test/addCustomQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
609
1131
|
const { id } = req.params;
|
|
610
1132
|
const { questionType, instruction, maxScore, time } = req.body;
|
|
@@ -613,6 +1135,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
613
1135
|
if (!test) {
|
|
614
1136
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
615
1137
|
}
|
|
1138
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1139
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1140
|
+
}
|
|
616
1141
|
const question = new TestQuestion({
|
|
617
1142
|
questionType,
|
|
618
1143
|
instruction,
|
|
@@ -629,7 +1154,40 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
629
1154
|
res.status(500).json({ message: 'Internal server error' });
|
|
630
1155
|
}
|
|
631
1156
|
});
|
|
632
|
-
|
|
1157
|
+
/**
|
|
1158
|
+
* @swagger
|
|
1159
|
+
* /exams/test/addQuestion/{id}:
|
|
1160
|
+
* put:
|
|
1161
|
+
* summary: Générer et ajouter une question
|
|
1162
|
+
* description: Génère une question via assistant et l'ajoute au test.
|
|
1163
|
+
* tags: [Examens]
|
|
1164
|
+
* parameters:
|
|
1165
|
+
* - in: path
|
|
1166
|
+
* name: id
|
|
1167
|
+
* required: true
|
|
1168
|
+
* schema:
|
|
1169
|
+
* type: string
|
|
1170
|
+
* requestBody:
|
|
1171
|
+
* required: true
|
|
1172
|
+
* content:
|
|
1173
|
+
* application/json:
|
|
1174
|
+
* schema:
|
|
1175
|
+
* type: object
|
|
1176
|
+
* properties:
|
|
1177
|
+
* questionType:
|
|
1178
|
+
* type: string
|
|
1179
|
+
* category:
|
|
1180
|
+
* type: string
|
|
1181
|
+
* expertiseLevel:
|
|
1182
|
+
* type: string
|
|
1183
|
+
* responses:
|
|
1184
|
+
* 200:
|
|
1185
|
+
* description: Question générée et ajoutée
|
|
1186
|
+
* 404:
|
|
1187
|
+
* description: Test non trouvé
|
|
1188
|
+
* 500:
|
|
1189
|
+
* description: Erreur interne
|
|
1190
|
+
*/
|
|
633
1191
|
this.put('/test/addQuestion/:id', authenticatedOptions, async (req, res) => {
|
|
634
1192
|
const { id } = req.params;
|
|
635
1193
|
const { questionType, category, expertiseLevel } = req.body;
|
|
@@ -638,6 +1196,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
638
1196
|
if (!test) {
|
|
639
1197
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
640
1198
|
}
|
|
1199
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1200
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1201
|
+
}
|
|
641
1202
|
const otherQuestionsIds = test.questions.map(question => question.questionId);
|
|
642
1203
|
const otherQuestions = await TestQuestion.find({ _id: { $in: otherQuestionsIds } });
|
|
643
1204
|
const jobName = await getJobName(test.targetJob);
|
|
@@ -660,7 +1221,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
660
1221
|
res.status(500).json({ message: 'Internal server error' });
|
|
661
1222
|
}
|
|
662
1223
|
});
|
|
663
|
-
|
|
1224
|
+
/**
|
|
1225
|
+
* @swagger
|
|
1226
|
+
* /exams/test/shuffle/{testId}:
|
|
1227
|
+
* get:
|
|
1228
|
+
* summary: Mélanger les questions d'un test
|
|
1229
|
+
* description: Mélange l'ordre des questions d'un test.
|
|
1230
|
+
* tags: [Examens]
|
|
1231
|
+
* parameters:
|
|
1232
|
+
* - in: path
|
|
1233
|
+
* name: testId
|
|
1234
|
+
* required: true
|
|
1235
|
+
* schema:
|
|
1236
|
+
* type: string
|
|
1237
|
+
* responses:
|
|
1238
|
+
* 200:
|
|
1239
|
+
* description: Questions mélangées
|
|
1240
|
+
* 404:
|
|
1241
|
+
* description: Test non trouvé
|
|
1242
|
+
* 500:
|
|
1243
|
+
* description: Erreur interne
|
|
1244
|
+
*/
|
|
664
1245
|
this.get('/test/shuffle/:testId', authenticatedOptions, async (req, res) => {
|
|
665
1246
|
const { testId } = req.params;
|
|
666
1247
|
try {
|
|
@@ -668,6 +1249,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
668
1249
|
if (!test) {
|
|
669
1250
|
return res.status(404).json({ message: 'Test not found' });
|
|
670
1251
|
}
|
|
1252
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1253
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
1254
|
+
}
|
|
671
1255
|
for (let i = test.questions.length - 1; i > 0; i--) {
|
|
672
1256
|
const j = Math.floor(Math.random() * (i + 1));
|
|
673
1257
|
[test.questions[i], test.questions[j]] = [test.questions[j], test.questions[i]];
|
|
@@ -680,7 +1264,36 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
680
1264
|
res.status(500).json({ message: 'Internal server error' });
|
|
681
1265
|
}
|
|
682
1266
|
});
|
|
683
|
-
|
|
1267
|
+
/**
|
|
1268
|
+
* @swagger
|
|
1269
|
+
* /exams/test/addInvitationText/{id}:
|
|
1270
|
+
* put:
|
|
1271
|
+
* summary: Ajouter un texte d'invitation
|
|
1272
|
+
* description: Ajoute ou met à jour le texte d'invitation utilisé pour un test.
|
|
1273
|
+
* tags: [Examens]
|
|
1274
|
+
* parameters:
|
|
1275
|
+
* - in: path
|
|
1276
|
+
* name: id
|
|
1277
|
+
* required: true
|
|
1278
|
+
* schema:
|
|
1279
|
+
* type: string
|
|
1280
|
+
* requestBody:
|
|
1281
|
+
* required: true
|
|
1282
|
+
* content:
|
|
1283
|
+
* application/json:
|
|
1284
|
+
* schema:
|
|
1285
|
+
* type: object
|
|
1286
|
+
* properties:
|
|
1287
|
+
* invitationText:
|
|
1288
|
+
* type: string
|
|
1289
|
+
* responses:
|
|
1290
|
+
* 200:
|
|
1291
|
+
* description: Texte mis à jour
|
|
1292
|
+
* 404:
|
|
1293
|
+
* description: Test non trouvé
|
|
1294
|
+
* 500:
|
|
1295
|
+
* description: Erreur interne
|
|
1296
|
+
*/
|
|
684
1297
|
this.put('/test/addInvitationText/:id', authenticatedOptions, async (req, res) => {
|
|
685
1298
|
const { id } = req.params;
|
|
686
1299
|
const { invitationText } = req.body;
|
|
@@ -689,6 +1302,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
689
1302
|
if (!test) {
|
|
690
1303
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
691
1304
|
}
|
|
1305
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1306
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1307
|
+
}
|
|
692
1308
|
test.invitationText = invitationText;
|
|
693
1309
|
await test.save();
|
|
694
1310
|
res.status(200).json({
|
|
@@ -701,7 +1317,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
701
1317
|
res.status(500).json({ message: 'Internal server error' });
|
|
702
1318
|
}
|
|
703
1319
|
});
|
|
704
|
-
|
|
1320
|
+
/**
|
|
1321
|
+
* @swagger
|
|
1322
|
+
* /exams/result/{id}:
|
|
1323
|
+
* get:
|
|
1324
|
+
* summary: Détail d'un résultat
|
|
1325
|
+
* description: Retourne un TestResult par identifiant.
|
|
1326
|
+
* tags: [Résultats]
|
|
1327
|
+
* parameters:
|
|
1328
|
+
* - in: path
|
|
1329
|
+
* name: id
|
|
1330
|
+
* required: true
|
|
1331
|
+
* schema:
|
|
1332
|
+
* type: string
|
|
1333
|
+
* responses:
|
|
1334
|
+
* 200:
|
|
1335
|
+
* description: Résultat trouvé
|
|
1336
|
+
* 404:
|
|
1337
|
+
* description: Résultat non trouvé
|
|
1338
|
+
* 500:
|
|
1339
|
+
* description: Erreur interne
|
|
1340
|
+
*/
|
|
705
1341
|
this.get('/result/:id', authenticatedOptions, async (req, res) => {
|
|
706
1342
|
const { id } = req.params;
|
|
707
1343
|
try {
|
|
@@ -716,7 +1352,21 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
716
1352
|
res.status(500).json({ message: 'Internal server error' });
|
|
717
1353
|
}
|
|
718
1354
|
});
|
|
719
|
-
|
|
1355
|
+
/**
|
|
1356
|
+
* @swagger
|
|
1357
|
+
* /exams/results/:
|
|
1358
|
+
* get:
|
|
1359
|
+
* summary: Lister les résultats
|
|
1360
|
+
* description: Retourne tous les résultats existants.
|
|
1361
|
+
* tags: [Résultats]
|
|
1362
|
+
* responses:
|
|
1363
|
+
* 200:
|
|
1364
|
+
* description: Liste des résultats
|
|
1365
|
+
* 404:
|
|
1366
|
+
* description: Aucun résultat
|
|
1367
|
+
* 500:
|
|
1368
|
+
* description: Erreur interne
|
|
1369
|
+
*/
|
|
720
1370
|
this.get('/results/', authenticatedOptions, async (req, res) => {
|
|
721
1371
|
try {
|
|
722
1372
|
const results = await TestResult.find();
|
|
@@ -730,7 +1380,35 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
730
1380
|
res.status(500).json({ message: 'Internal server error' });
|
|
731
1381
|
}
|
|
732
1382
|
});
|
|
733
|
-
|
|
1383
|
+
/**
|
|
1384
|
+
* @swagger
|
|
1385
|
+
* /exams/invite:
|
|
1386
|
+
* post:
|
|
1387
|
+
* summary: Inviter un candidat à un test
|
|
1388
|
+
* description: Crée un TestResult et envoie un email d'invitation au candidat.
|
|
1389
|
+
* tags: [Résultats]
|
|
1390
|
+
* requestBody:
|
|
1391
|
+
* required: true
|
|
1392
|
+
* content:
|
|
1393
|
+
* application/json:
|
|
1394
|
+
* schema:
|
|
1395
|
+
* type: object
|
|
1396
|
+
* required: [candidateId, testId]
|
|
1397
|
+
* properties:
|
|
1398
|
+
* candidateId:
|
|
1399
|
+
* type: string
|
|
1400
|
+
* testId:
|
|
1401
|
+
* type: string
|
|
1402
|
+
* responses:
|
|
1403
|
+
* 201:
|
|
1404
|
+
* description: Invitation créée
|
|
1405
|
+
* 404:
|
|
1406
|
+
* description: Test ou candidat non trouvé
|
|
1407
|
+
* 400:
|
|
1408
|
+
* description: Paramètres manquants
|
|
1409
|
+
* 500:
|
|
1410
|
+
* description: Erreur interne
|
|
1411
|
+
*/
|
|
734
1412
|
this.post('/invite', authenticatedOptions, async (req, res) => {
|
|
735
1413
|
const { candidateId, testId } = req.body;
|
|
736
1414
|
if (!candidateId || !testId) {
|
|
@@ -786,7 +1464,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
786
1464
|
res.status(500).json({ message: 'Internal server error' });
|
|
787
1465
|
}
|
|
788
1466
|
});
|
|
789
|
-
|
|
1467
|
+
/**
|
|
1468
|
+
* @swagger
|
|
1469
|
+
* /exams/result/getNextQuestion/{id}/{idCurrentQuestion}:
|
|
1470
|
+
* get:
|
|
1471
|
+
* summary: Obtenir la question suivante
|
|
1472
|
+
* description: Retourne la prochaine question pour un TestResult ou null s'il n'y en a plus.
|
|
1473
|
+
* tags: [Résultats]
|
|
1474
|
+
* parameters:
|
|
1475
|
+
* - in: path
|
|
1476
|
+
* name: id
|
|
1477
|
+
* required: true
|
|
1478
|
+
* schema:
|
|
1479
|
+
* type: string
|
|
1480
|
+
* - in: path
|
|
1481
|
+
* name: idCurrentQuestion
|
|
1482
|
+
* required: true
|
|
1483
|
+
* schema:
|
|
1484
|
+
* type: string
|
|
1485
|
+
* responses:
|
|
1486
|
+
* 200:
|
|
1487
|
+
* description: Prochaine question ou null
|
|
1488
|
+
* 404:
|
|
1489
|
+
* description: Résultat ou test non trouvé
|
|
1490
|
+
* 500:
|
|
1491
|
+
* description: Erreur interne
|
|
1492
|
+
*/
|
|
790
1493
|
this.get('/result/getNextQuestion/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
791
1494
|
const { id, idCurrentQuestion } = req.params;
|
|
792
1495
|
try {
|
|
@@ -812,7 +1515,32 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
812
1515
|
res.status(500).json({ message: 'Internal server error' });
|
|
813
1516
|
}
|
|
814
1517
|
});
|
|
815
|
-
|
|
1518
|
+
/**
|
|
1519
|
+
* @swagger
|
|
1520
|
+
* /exams/result/isLastQuestion/{id}/{idCurrentQuestion}:
|
|
1521
|
+
* get:
|
|
1522
|
+
* summary: Vérifier la dernière question
|
|
1523
|
+
* description: Indique si la question courante est la dernière du test.
|
|
1524
|
+
* tags: [Résultats]
|
|
1525
|
+
* parameters:
|
|
1526
|
+
* - in: path
|
|
1527
|
+
* name: id
|
|
1528
|
+
* required: true
|
|
1529
|
+
* schema:
|
|
1530
|
+
* type: string
|
|
1531
|
+
* - in: path
|
|
1532
|
+
* name: idCurrentQuestion
|
|
1533
|
+
* required: true
|
|
1534
|
+
* schema:
|
|
1535
|
+
* type: string
|
|
1536
|
+
* responses:
|
|
1537
|
+
* 200:
|
|
1538
|
+
* description: Booléen retourné
|
|
1539
|
+
* 404:
|
|
1540
|
+
* description: Résultat ou test non trouvé
|
|
1541
|
+
* 500:
|
|
1542
|
+
* description: Erreur interne
|
|
1543
|
+
*/
|
|
816
1544
|
this.get('/result/isLastQuestion/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
817
1545
|
const { id, idCurrentQuestion } = req.params;
|
|
818
1546
|
try {
|
|
@@ -837,7 +1565,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
837
1565
|
res.status(500).json({ message: 'Internal server error' });
|
|
838
1566
|
}
|
|
839
1567
|
});
|
|
840
|
-
|
|
1568
|
+
/**
|
|
1569
|
+
* @swagger
|
|
1570
|
+
* /exams/result/question/{questionId}:
|
|
1571
|
+
* get:
|
|
1572
|
+
* summary: Obtenir une question (résultat)
|
|
1573
|
+
* description: Retourne une question via son identifiant pour un résultat.
|
|
1574
|
+
* tags: [Résultats]
|
|
1575
|
+
* parameters:
|
|
1576
|
+
* - in: path
|
|
1577
|
+
* name: questionId
|
|
1578
|
+
* required: true
|
|
1579
|
+
* schema:
|
|
1580
|
+
* type: string
|
|
1581
|
+
* responses:
|
|
1582
|
+
* 200:
|
|
1583
|
+
* description: Question trouvée
|
|
1584
|
+
* 404:
|
|
1585
|
+
* description: Question non trouvée
|
|
1586
|
+
* 500:
|
|
1587
|
+
* description: Erreur interne
|
|
1588
|
+
*/
|
|
841
1589
|
this.get('/result/question/:questionId', authenticatedOptions, async (req, res) => {
|
|
842
1590
|
const { questionId } = req.params;
|
|
843
1591
|
try {
|
|
@@ -852,7 +1600,41 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
852
1600
|
res.status(500).json({ message: 'Internal server error' });
|
|
853
1601
|
}
|
|
854
1602
|
});
|
|
855
|
-
|
|
1603
|
+
/**
|
|
1604
|
+
* @swagger
|
|
1605
|
+
* /exams/result/sendResponse/{id}/{idCurrentQuestion}:
|
|
1606
|
+
* put:
|
|
1607
|
+
* summary: Envoyer une réponse
|
|
1608
|
+
* description: Enregistre la réponse d'un candidat pour une question d'un test.
|
|
1609
|
+
* tags: [Résultats]
|
|
1610
|
+
* parameters:
|
|
1611
|
+
* - in: path
|
|
1612
|
+
* name: id
|
|
1613
|
+
* required: true
|
|
1614
|
+
* schema:
|
|
1615
|
+
* type: string
|
|
1616
|
+
* - in: path
|
|
1617
|
+
* name: idCurrentQuestion
|
|
1618
|
+
* required: true
|
|
1619
|
+
* schema:
|
|
1620
|
+
* type: string
|
|
1621
|
+
* requestBody:
|
|
1622
|
+
* required: true
|
|
1623
|
+
* content:
|
|
1624
|
+
* application/json:
|
|
1625
|
+
* schema:
|
|
1626
|
+
* type: object
|
|
1627
|
+
* properties:
|
|
1628
|
+
* candidateResponse:
|
|
1629
|
+
* type: string
|
|
1630
|
+
* responses:
|
|
1631
|
+
* 200:
|
|
1632
|
+
* description: Réponse enregistrée
|
|
1633
|
+
* 404:
|
|
1634
|
+
* description: Test ou résultat non trouvé
|
|
1635
|
+
* 500:
|
|
1636
|
+
* description: Erreur interne
|
|
1637
|
+
*/
|
|
856
1638
|
this.put('/result/sendResponse/:id/:idCurrentQuestion', authenticatedOptions, async (req, res) => {
|
|
857
1639
|
const { id, idCurrentQuestion } = req.params;
|
|
858
1640
|
const { candidateResponse } = req.body;
|
|
@@ -889,7 +1671,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
889
1671
|
res.status(500).json({ message: 'Internal server error' });
|
|
890
1672
|
}
|
|
891
1673
|
});
|
|
892
|
-
|
|
1674
|
+
/**
|
|
1675
|
+
* @swagger
|
|
1676
|
+
* /exams/result/correct/{id}:
|
|
1677
|
+
* post:
|
|
1678
|
+
* summary: Lancer la correction d'un test
|
|
1679
|
+
* description: Déclenche la correction d'un TestResult.
|
|
1680
|
+
* tags: [Résultats]
|
|
1681
|
+
* parameters:
|
|
1682
|
+
* - in: path
|
|
1683
|
+
* name: id
|
|
1684
|
+
* required: true
|
|
1685
|
+
* schema:
|
|
1686
|
+
* type: string
|
|
1687
|
+
* responses:
|
|
1688
|
+
* 200:
|
|
1689
|
+
* description: Correction lancée
|
|
1690
|
+
* 404:
|
|
1691
|
+
* description: Résultat non trouvé
|
|
1692
|
+
* 500:
|
|
1693
|
+
* description: Erreur interne
|
|
1694
|
+
*/
|
|
893
1695
|
this.post('/result/correct/:id', authenticatedOptions, async (req, res) => {
|
|
894
1696
|
const { id } = req.params;
|
|
895
1697
|
try {
|
|
@@ -905,7 +1707,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
905
1707
|
res.status(500).json({ message: 'Internal server error' });
|
|
906
1708
|
}
|
|
907
1709
|
});
|
|
908
|
-
|
|
1710
|
+
/**
|
|
1711
|
+
* @swagger
|
|
1712
|
+
* /exams/result/calculateScore/{id}:
|
|
1713
|
+
* put:
|
|
1714
|
+
* summary: Calculer le score
|
|
1715
|
+
* description: Calcule et enregistre le score final d'un TestResult.
|
|
1716
|
+
* tags: [Résultats]
|
|
1717
|
+
* parameters:
|
|
1718
|
+
* - in: path
|
|
1719
|
+
* name: id
|
|
1720
|
+
* required: true
|
|
1721
|
+
* schema:
|
|
1722
|
+
* type: string
|
|
1723
|
+
* responses:
|
|
1724
|
+
* 200:
|
|
1725
|
+
* description: Score calculé
|
|
1726
|
+
* 404:
|
|
1727
|
+
* description: Résultat non trouvé
|
|
1728
|
+
* 500:
|
|
1729
|
+
* description: Erreur interne
|
|
1730
|
+
*/
|
|
909
1731
|
this.put('/result/calculateScore/:id', authenticatedOptions, async (req, res) => {
|
|
910
1732
|
const { id } = req.params;
|
|
911
1733
|
try {
|
|
@@ -948,7 +1770,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
948
1770
|
res.status(500).json({ message: 'Internal server error' });
|
|
949
1771
|
}
|
|
950
1772
|
});
|
|
951
|
-
|
|
1773
|
+
/**
|
|
1774
|
+
* @swagger
|
|
1775
|
+
* /exams/maxscore/{resultId}:
|
|
1776
|
+
* get:
|
|
1777
|
+
* summary: Obtenir le score maximum
|
|
1778
|
+
* description: Calcule le score maximal possible pour un résultat.
|
|
1779
|
+
* tags: [Résultats]
|
|
1780
|
+
* parameters:
|
|
1781
|
+
* - in: path
|
|
1782
|
+
* name: resultId
|
|
1783
|
+
* required: true
|
|
1784
|
+
* schema:
|
|
1785
|
+
* type: string
|
|
1786
|
+
* responses:
|
|
1787
|
+
* 200:
|
|
1788
|
+
* description: Score maximum retourné
|
|
1789
|
+
* 404:
|
|
1790
|
+
* description: Résultat ou test non trouvé
|
|
1791
|
+
* 500:
|
|
1792
|
+
* description: Erreur interne
|
|
1793
|
+
*/
|
|
952
1794
|
this.get('/maxscore/:resultId', authenticatedOptions, async (req, res) => {
|
|
953
1795
|
const { resultId } = req.params;
|
|
954
1796
|
try {
|
|
@@ -974,7 +1816,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
974
1816
|
res.status(500).json({ message: 'Internal server error' });
|
|
975
1817
|
}
|
|
976
1818
|
});
|
|
977
|
-
|
|
1819
|
+
/**
|
|
1820
|
+
* @swagger
|
|
1821
|
+
* /exams/result/score/{id}:
|
|
1822
|
+
* get:
|
|
1823
|
+
* summary: Obtenir le score d'un résultat
|
|
1824
|
+
* description: Retourne le score calculé d'un TestResult.
|
|
1825
|
+
* tags: [Résultats]
|
|
1826
|
+
* parameters:
|
|
1827
|
+
* - in: path
|
|
1828
|
+
* name: id
|
|
1829
|
+
* required: true
|
|
1830
|
+
* schema:
|
|
1831
|
+
* type: string
|
|
1832
|
+
* responses:
|
|
1833
|
+
* 200:
|
|
1834
|
+
* description: Score retourné
|
|
1835
|
+
* 404:
|
|
1836
|
+
* description: Résultat non trouvé
|
|
1837
|
+
* 500:
|
|
1838
|
+
* description: Erreur interne
|
|
1839
|
+
*/
|
|
978
1840
|
this.get('/result/score/:id', authenticatedOptions, async (req, res) => {
|
|
979
1841
|
const { id } = req.params;
|
|
980
1842
|
try {
|
|
@@ -989,7 +1851,43 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
989
1851
|
res.status(500).json({ message: 'Internal server error' });
|
|
990
1852
|
}
|
|
991
1853
|
});
|
|
992
|
-
|
|
1854
|
+
/**
|
|
1855
|
+
* @swagger
|
|
1856
|
+
* /exams/test/generateQuestions/{id}:
|
|
1857
|
+
* put:
|
|
1858
|
+
* summary: Générer des questions pour un test
|
|
1859
|
+
* description: Génère plusieurs questions (IA) pour un test donné, éventuellement filtrées par catégorie et type.
|
|
1860
|
+
* tags: [Examens]
|
|
1861
|
+
* parameters:
|
|
1862
|
+
* - in: path
|
|
1863
|
+
* name: id
|
|
1864
|
+
* required: true
|
|
1865
|
+
* schema:
|
|
1866
|
+
* type: string
|
|
1867
|
+
* requestBody:
|
|
1868
|
+
* required: true
|
|
1869
|
+
* content:
|
|
1870
|
+
* application/json:
|
|
1871
|
+
* schema:
|
|
1872
|
+
* type: object
|
|
1873
|
+
* required: [numberOfQuestions]
|
|
1874
|
+
* properties:
|
|
1875
|
+
* numberOfQuestions:
|
|
1876
|
+
* type: integer
|
|
1877
|
+
* category:
|
|
1878
|
+
* type: string
|
|
1879
|
+
* questionType:
|
|
1880
|
+
* type: string
|
|
1881
|
+
* responses:
|
|
1882
|
+
* 200:
|
|
1883
|
+
* description: Questions générées
|
|
1884
|
+
* 400:
|
|
1885
|
+
* description: Paramètres invalides
|
|
1886
|
+
* 404:
|
|
1887
|
+
* description: Test non trouvé
|
|
1888
|
+
* 500:
|
|
1889
|
+
* description: Erreur interne
|
|
1890
|
+
*/
|
|
993
1891
|
this.put('/test/generateQuestions/:id', authenticatedOptions, async (req, res) => {
|
|
994
1892
|
const { id } = req.params;
|
|
995
1893
|
const { numberOfQuestions, category, questionType } = req.body;
|
|
@@ -1001,6 +1899,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1001
1899
|
if (!test) {
|
|
1002
1900
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1003
1901
|
}
|
|
1902
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
1903
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1904
|
+
}
|
|
1004
1905
|
let categoriesToUse = [];
|
|
1005
1906
|
if (category && category !== 'ALL') {
|
|
1006
1907
|
const categoryInfo = test.categories.find(cat => cat.categoryId.toString() === category);
|
|
@@ -1069,7 +1970,56 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1069
1970
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1070
1971
|
}
|
|
1071
1972
|
});
|
|
1072
|
-
|
|
1973
|
+
/**
|
|
1974
|
+
* @swagger
|
|
1975
|
+
* /exams/test/{testId}/candidates:
|
|
1976
|
+
* get:
|
|
1977
|
+
* summary: Lister les candidats invités
|
|
1978
|
+
* description: Liste paginée des candidats invités à un test avec filtres et tri.
|
|
1979
|
+
* tags: [Résultats]
|
|
1980
|
+
* parameters:
|
|
1981
|
+
* - in: path
|
|
1982
|
+
* name: testId
|
|
1983
|
+
* required: true
|
|
1984
|
+
* schema:
|
|
1985
|
+
* type: string
|
|
1986
|
+
* - in: query
|
|
1987
|
+
* name: page
|
|
1988
|
+
* schema:
|
|
1989
|
+
* type: integer
|
|
1990
|
+
* default: 1
|
|
1991
|
+
* - in: query
|
|
1992
|
+
* name: limit
|
|
1993
|
+
* schema:
|
|
1994
|
+
* type: integer
|
|
1995
|
+
* default: 10
|
|
1996
|
+
* - in: query
|
|
1997
|
+
* name: search
|
|
1998
|
+
* schema:
|
|
1999
|
+
* type: string
|
|
2000
|
+
* - in: query
|
|
2001
|
+
* name: state
|
|
2002
|
+
* schema:
|
|
2003
|
+
* type: string
|
|
2004
|
+
* default: all
|
|
2005
|
+
* - in: query
|
|
2006
|
+
* name: sortBy
|
|
2007
|
+
* schema:
|
|
2008
|
+
* type: string
|
|
2009
|
+
* default: invitationDate
|
|
2010
|
+
* - in: query
|
|
2011
|
+
* name: sortOrder
|
|
2012
|
+
* schema:
|
|
2013
|
+
* type: string
|
|
2014
|
+
* default: desc
|
|
2015
|
+
* responses:
|
|
2016
|
+
* 200:
|
|
2017
|
+
* description: Candidats paginés
|
|
2018
|
+
* 404:
|
|
2019
|
+
* description: Test non trouvé
|
|
2020
|
+
* 500:
|
|
2021
|
+
* description: Erreur interne
|
|
2022
|
+
*/
|
|
1073
2023
|
this.get('/test/:testId/candidates', authenticatedOptions, async (req, res) => {
|
|
1074
2024
|
const { testId } = req.params;
|
|
1075
2025
|
const page = parseInt(req.query.page) || 1;
|
|
@@ -1084,6 +2034,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1084
2034
|
if (!test) {
|
|
1085
2035
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1086
2036
|
}
|
|
2037
|
+
if (req.entity?._id && test.entityId && !test.entityId.equals(req.entity._id)) {
|
|
2038
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
2039
|
+
}
|
|
1087
2040
|
// Construction de la requête
|
|
1088
2041
|
const query = { testId };
|
|
1089
2042
|
if (state !== 'all') {
|
|
@@ -1231,7 +2184,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1231
2184
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1232
2185
|
}
|
|
1233
2186
|
});
|
|
1234
|
-
|
|
2187
|
+
/**
|
|
2188
|
+
* @swagger
|
|
2189
|
+
* /exams/reinvite/{resultId}:
|
|
2190
|
+
* post:
|
|
2191
|
+
* summary: Renvoyer une invitation
|
|
2192
|
+
* description: Récupère un TestResult et renvoie l'email d'invitation.
|
|
2193
|
+
* tags: [Résultats]
|
|
2194
|
+
* parameters:
|
|
2195
|
+
* - in: path
|
|
2196
|
+
* name: resultId
|
|
2197
|
+
* required: true
|
|
2198
|
+
* schema:
|
|
2199
|
+
* type: string
|
|
2200
|
+
* responses:
|
|
2201
|
+
* 200:
|
|
2202
|
+
* description: Invitation renvoyée
|
|
2203
|
+
* 404:
|
|
2204
|
+
* description: Ressource non trouvée
|
|
2205
|
+
* 500:
|
|
2206
|
+
* description: Erreur interne
|
|
2207
|
+
*/
|
|
1235
2208
|
this.post('/reinvite/:resultId', authenticatedOptions, async (req, res) => {
|
|
1236
2209
|
const { resultId } = req.params;
|
|
1237
2210
|
try {
|
|
@@ -1280,7 +2253,45 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1280
2253
|
res.status(500).json({ message: 'Internal server error' });
|
|
1281
2254
|
}
|
|
1282
2255
|
});
|
|
1283
|
-
|
|
2256
|
+
/**
|
|
2257
|
+
* @swagger
|
|
2258
|
+
* /exams/result/{testResultId}/response/{questionId}:
|
|
2259
|
+
* put:
|
|
2260
|
+
* summary: Correction manuelle d'une réponse
|
|
2261
|
+
* description: Met à jour le score/commentaire d'une réponse et recalcule le score global.
|
|
2262
|
+
* tags: [Résultats]
|
|
2263
|
+
* parameters:
|
|
2264
|
+
* - in: path
|
|
2265
|
+
* name: testResultId
|
|
2266
|
+
* required: true
|
|
2267
|
+
* schema:
|
|
2268
|
+
* type: string
|
|
2269
|
+
* - in: path
|
|
2270
|
+
* name: questionId
|
|
2271
|
+
* required: true
|
|
2272
|
+
* schema:
|
|
2273
|
+
* type: string
|
|
2274
|
+
* requestBody:
|
|
2275
|
+
* required: true
|
|
2276
|
+
* content:
|
|
2277
|
+
* application/json:
|
|
2278
|
+
* schema:
|
|
2279
|
+
* type: object
|
|
2280
|
+
* properties:
|
|
2281
|
+
* score:
|
|
2282
|
+
* type: number
|
|
2283
|
+
* comment:
|
|
2284
|
+
* type: string
|
|
2285
|
+
* responses:
|
|
2286
|
+
* 200:
|
|
2287
|
+
* description: Correction enregistrée
|
|
2288
|
+
* 404:
|
|
2289
|
+
* description: Ressource non trouvée
|
|
2290
|
+
* 400:
|
|
2291
|
+
* description: Score invalide
|
|
2292
|
+
* 500:
|
|
2293
|
+
* description: Erreur interne
|
|
2294
|
+
*/
|
|
1284
2295
|
this.put('/result/:testResultId/response/:questionId', authenticatedOptions, async (req, res) => {
|
|
1285
2296
|
try {
|
|
1286
2297
|
const { testResultId, questionId } = req.params;
|
|
@@ -1323,7 +2334,27 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1323
2334
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
1324
2335
|
}
|
|
1325
2336
|
});
|
|
1326
|
-
|
|
2337
|
+
/**
|
|
2338
|
+
* @swagger
|
|
2339
|
+
* /exams/result/{id}:
|
|
2340
|
+
* delete:
|
|
2341
|
+
* summary: Supprimer un résultat
|
|
2342
|
+
* description: Supprime un TestResult.
|
|
2343
|
+
* tags: [Résultats]
|
|
2344
|
+
* parameters:
|
|
2345
|
+
* - in: path
|
|
2346
|
+
* name: id
|
|
2347
|
+
* required: true
|
|
2348
|
+
* schema:
|
|
2349
|
+
* type: string
|
|
2350
|
+
* responses:
|
|
2351
|
+
* 200:
|
|
2352
|
+
* description: Résultat supprimé
|
|
2353
|
+
* 404:
|
|
2354
|
+
* description: Résultat non trouvé
|
|
2355
|
+
* 500:
|
|
2356
|
+
* description: Erreur interne
|
|
2357
|
+
*/
|
|
1327
2358
|
this.delete('/result/:id', authenticatedOptions, async (req, res) => {
|
|
1328
2359
|
const { id } = req.params;
|
|
1329
2360
|
try {
|