@programisto/edrm-exams 0.3.8 → 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.
@@ -84,7 +84,31 @@ class ExamsRouter extends EnduranceRouter {
84
84
  requireAuth: false,
85
85
  permissions: []
86
86
  };
87
- // Créer une catégorie
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
- // Lister toutes les catégories
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
- // Obtenir une catégorie par son ID
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
- // Créer un job type
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
- // Lister tous les jobs
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
- // Obtenir un job par son ID
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
- // Migrer tous les tests avec l'ancien format targetJob
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
- // Créer un test
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
- // Modifier un test
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
- // Supprimer un test
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
- // Obtenir un test par son ID
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
- // Lister tous les tests
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
- // Supprimer une catégorie d'un test
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
- // Ajouter une catégorie à un test
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
- // Obtenir une question par son ID
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
- // Obtenir toutes les questions d'un test
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
- // Supprimer une question d'un test
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
- // Supprimer toutes les questions d'un test
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
- // Modifier une question
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
- // Ajouter une question à un test
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
- // Ajouter une question à un test
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
- // Mélanger les questions d'un test
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
- // Ajouter un texte d'invitation à un test
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
- // Obtenir un résultat par son ID
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
- // Lister tous les résultats
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
- // Créer un résultat
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
- // Obtenir la question suivante
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
- // Vérifier si c'est la dernière question
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
- // Obtenir une question
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
- // Envoyer une réponse
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
- // Corriger un test
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
- // Calculer le score
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
- // Obtenir le score maximum
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
- // Obtenir le score d'un résultat
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
- // Générer plusieurs questions pour un test
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
- // Lister tous les candidats invités à un test
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
- // Renvoyer l'email d'invitation à un candidat
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
- // Correction manuelle d'une réponse à une question d'un testResult
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
- // Supprimer un test result
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 {