@programisto/edrm-exams 0.2.5 → 0.2.6
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.
|
@@ -871,6 +871,8 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
871
871
|
const skip = (page - 1) * limit;
|
|
872
872
|
const search = req.query.search || '';
|
|
873
873
|
const state = req.query.state || 'all';
|
|
874
|
+
const sortBy = req.query.sortBy || 'invitationDate';
|
|
875
|
+
const sortOrder = req.query.sortOrder || 'desc';
|
|
874
876
|
try {
|
|
875
877
|
const test = await Test.findById(testId);
|
|
876
878
|
if (!test) {
|
|
@@ -881,30 +883,84 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
881
883
|
if (state !== 'all') {
|
|
882
884
|
query.state = state;
|
|
883
885
|
}
|
|
884
|
-
// Recherche sur les candidats
|
|
886
|
+
// Recherche sur les candidats via leurs contacts
|
|
885
887
|
if (search) {
|
|
886
|
-
|
|
888
|
+
// D'abord, rechercher dans les contacts
|
|
889
|
+
const contacts = await ContactModel.find({
|
|
887
890
|
$or: [
|
|
888
|
-
{
|
|
889
|
-
{
|
|
891
|
+
{ firstname: { $regex: search, $options: 'i' } },
|
|
892
|
+
{ lastname: { $regex: search, $options: 'i' } },
|
|
890
893
|
{ email: { $regex: search, $options: 'i' } }
|
|
891
894
|
]
|
|
892
895
|
});
|
|
896
|
+
// Ensuite, récupérer les candidats qui ont ces contacts
|
|
897
|
+
const contactIds = contacts.map(c => c._id);
|
|
898
|
+
const candidates = await Candidate.find({
|
|
899
|
+
contact: { $in: contactIds }
|
|
900
|
+
});
|
|
893
901
|
const candidateIds = candidates.map(c => c._id);
|
|
894
902
|
query.candidateId = { $in: candidateIds };
|
|
895
903
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
904
|
+
// Déterminer l'ordre de tri
|
|
905
|
+
const sortDirection = sortOrder === 'asc' ? 1 : -1;
|
|
906
|
+
// Si on trie par lastName, on récupère tous les résultats puis on trie après
|
|
907
|
+
// Sinon on peut trier directement dans la requête MongoDB
|
|
908
|
+
let results, total;
|
|
909
|
+
if (sortBy === 'lastName') {
|
|
910
|
+
// Récupérer tous les résultats sans pagination pour pouvoir trier par lastName
|
|
911
|
+
const allResults = await TestResult.find(query).exec();
|
|
912
|
+
total = allResults.length;
|
|
913
|
+
// Récupérer les données des candidats pour le tri
|
|
914
|
+
const candidateIds = allResults.map(result => result.candidateId);
|
|
915
|
+
const candidates = await Candidate.find({ _id: { $in: candidateIds } });
|
|
916
|
+
const candidatesMap = new Map(candidates.map(c => [c._id.toString(), c]));
|
|
917
|
+
// Combiner les résultats avec les données des candidats et trier
|
|
918
|
+
const resultsWithCandidates = await Promise.all(allResults.map(async (result) => {
|
|
919
|
+
const candidate = candidatesMap.get(result.candidateId.toString());
|
|
920
|
+
if (!candidate) {
|
|
921
|
+
return {
|
|
922
|
+
...result.toObject(),
|
|
923
|
+
candidate: null,
|
|
924
|
+
lastName: ''
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
const contact = await ContactModel.findById(candidate.contact);
|
|
928
|
+
return {
|
|
929
|
+
...result.toObject(),
|
|
930
|
+
candidate: contact
|
|
931
|
+
? {
|
|
932
|
+
firstName: contact.firstname,
|
|
933
|
+
lastName: contact.lastname,
|
|
934
|
+
email: contact.email
|
|
935
|
+
}
|
|
936
|
+
: null,
|
|
937
|
+
lastName: contact ? contact.lastname : ''
|
|
938
|
+
};
|
|
939
|
+
}));
|
|
940
|
+
// Trier par lastName
|
|
941
|
+
resultsWithCandidates.sort((a, b) => {
|
|
942
|
+
const lastNameA = (a.lastName || '').toLowerCase();
|
|
943
|
+
const lastNameB = (b.lastName || '').toLowerCase();
|
|
944
|
+
return sortDirection === 1
|
|
945
|
+
? lastNameA.localeCompare(lastNameB)
|
|
946
|
+
: lastNameB.localeCompare(lastNameA);
|
|
947
|
+
});
|
|
948
|
+
// Appliquer la pagination
|
|
949
|
+
results = resultsWithCandidates.slice(skip, skip + limit);
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
// Tri direct dans MongoDB pour invitationDate
|
|
953
|
+
const sortObject = {};
|
|
954
|
+
sortObject[sortBy] = sortDirection;
|
|
955
|
+
[results, total] = await Promise.all([
|
|
956
|
+
TestResult.find(query)
|
|
957
|
+
.sort(sortObject)
|
|
958
|
+
.skip(skip)
|
|
959
|
+
.limit(limit)
|
|
960
|
+
.exec(),
|
|
961
|
+
TestResult.countDocuments(query)
|
|
962
|
+
]);
|
|
963
|
+
}
|
|
908
964
|
// Calculer le maxScore du test
|
|
909
965
|
let maxScore = 0;
|
|
910
966
|
if (test.questions && test.questions.length > 0) {
|
|
@@ -912,30 +968,45 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
912
968
|
const questions = await TestQuestion.find({ _id: { $in: questionIds } }).lean();
|
|
913
969
|
maxScore = questions.reduce((sum, q) => sum + (q.maxScore || 0), 0);
|
|
914
970
|
}
|
|
915
|
-
//
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
971
|
+
// Si on a déjà traité les candidats pour le tri par lastName, on utilise directement les résultats
|
|
972
|
+
let resultsWithCandidates;
|
|
973
|
+
if (sortBy === 'lastName') {
|
|
974
|
+
// Les résultats sont déjà traités avec les données des candidats
|
|
975
|
+
resultsWithCandidates = results.map(result => ({
|
|
976
|
+
...result,
|
|
977
|
+
maxScore
|
|
978
|
+
}));
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
// Récupérer les données des candidats
|
|
982
|
+
const candidateIds = results.map(result => result.candidateId);
|
|
983
|
+
const candidates = await Candidate.find({ _id: { $in: candidateIds } });
|
|
984
|
+
const candidatesMap = new Map(candidates.map(c => [c._id.toString(), c]));
|
|
985
|
+
// Combiner les résultats avec les données des candidats
|
|
986
|
+
resultsWithCandidates = await Promise.all(results.map(async (result) => {
|
|
987
|
+
const candidate = candidatesMap.get(result.candidateId.toString());
|
|
988
|
+
if (!candidate) {
|
|
989
|
+
return {
|
|
990
|
+
...result.toObject(),
|
|
991
|
+
candidate: null,
|
|
992
|
+
maxScore
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
// Récupérer le contact pour obtenir les informations personnelles
|
|
996
|
+
const contact = await ContactModel.findById(candidate.contact);
|
|
919
997
|
return {
|
|
920
998
|
...result.toObject(),
|
|
921
|
-
candidate:
|
|
999
|
+
candidate: contact
|
|
1000
|
+
? {
|
|
1001
|
+
firstName: contact.firstname,
|
|
1002
|
+
lastName: contact.lastname,
|
|
1003
|
+
email: contact.email
|
|
1004
|
+
}
|
|
1005
|
+
: null,
|
|
922
1006
|
maxScore
|
|
923
1007
|
};
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
const contact = await ContactModel.findById(candidate.contact);
|
|
927
|
-
return {
|
|
928
|
-
...result.toObject(),
|
|
929
|
-
candidate: contact
|
|
930
|
-
? {
|
|
931
|
-
firstName: contact.firstname,
|
|
932
|
-
lastName: contact.lastname,
|
|
933
|
-
email: contact.email
|
|
934
|
-
}
|
|
935
|
-
: null,
|
|
936
|
-
maxScore
|
|
937
|
-
};
|
|
938
|
-
}));
|
|
1008
|
+
}));
|
|
1009
|
+
}
|
|
939
1010
|
const totalPages = Math.ceil(total / limit);
|
|
940
1011
|
return res.json({
|
|
941
1012
|
data: resultsWithCandidates,
|