@programisto/edrm-storage 0.3.1

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.
Files changed (68) hide show
  1. package/README.md +135 -0
  2. package/dist/bin/www.d.ts +2 -0
  3. package/dist/bin/www.js +13 -0
  4. package/dist/modules/edrm-exams/lib/openai/correctQuestion.txt +9 -0
  5. package/dist/modules/edrm-exams/lib/openai/createQuestion.txt +6 -0
  6. package/dist/modules/edrm-exams/lib/openai.d.ts +37 -0
  7. package/dist/modules/edrm-exams/lib/openai.js +135 -0
  8. package/dist/modules/edrm-exams/listeners/correct.listener.d.ts +2 -0
  9. package/dist/modules/edrm-exams/listeners/correct.listener.js +167 -0
  10. package/dist/modules/edrm-exams/models/candidate.model.d.ts +21 -0
  11. package/dist/modules/edrm-exams/models/candidate.model.js +75 -0
  12. package/dist/modules/edrm-exams/models/candidate.models.d.ts +21 -0
  13. package/dist/modules/edrm-exams/models/candidate.models.js +75 -0
  14. package/dist/modules/edrm-exams/models/company.model.d.ts +8 -0
  15. package/dist/modules/edrm-exams/models/company.model.js +34 -0
  16. package/dist/modules/edrm-exams/models/contact.model.d.ts +14 -0
  17. package/dist/modules/edrm-exams/models/contact.model.js +60 -0
  18. package/dist/modules/edrm-exams/models/test-category.models.d.ts +7 -0
  19. package/dist/modules/edrm-exams/models/test-category.models.js +29 -0
  20. package/dist/modules/edrm-exams/models/test-job.model.d.ts +7 -0
  21. package/dist/modules/edrm-exams/models/test-job.model.js +29 -0
  22. package/dist/modules/edrm-exams/models/test-question.model.d.ts +25 -0
  23. package/dist/modules/edrm-exams/models/test-question.model.js +70 -0
  24. package/dist/modules/edrm-exams/models/test-result.model.d.ts +26 -0
  25. package/dist/modules/edrm-exams/models/test-result.model.js +70 -0
  26. package/dist/modules/edrm-exams/models/test.model.d.ts +47 -0
  27. package/dist/modules/edrm-exams/models/test.model.js +133 -0
  28. package/dist/modules/edrm-exams/models/user.model.d.ts +18 -0
  29. package/dist/modules/edrm-exams/models/user.model.js +73 -0
  30. package/dist/modules/edrm-exams/routes/company.router.d.ts +7 -0
  31. package/dist/modules/edrm-exams/routes/company.router.js +108 -0
  32. package/dist/modules/edrm-exams/routes/exams-candidate.router.d.ts +7 -0
  33. package/dist/modules/edrm-exams/routes/exams-candidate.router.js +448 -0
  34. package/dist/modules/edrm-exams/routes/exams.router.d.ts +8 -0
  35. package/dist/modules/edrm-exams/routes/exams.router.js +1343 -0
  36. package/dist/modules/edrm-exams/routes/result.router.d.ts +7 -0
  37. package/dist/modules/edrm-exams/routes/result.router.js +370 -0
  38. package/dist/modules/edrm-exams/routes/user.router.d.ts +7 -0
  39. package/dist/modules/edrm-exams/routes/user.router.js +96 -0
  40. package/dist/modules/edrm-storage/config/edrm-storage.config.d.ts +29 -0
  41. package/dist/modules/edrm-storage/config/edrm-storage.config.js +31 -0
  42. package/dist/modules/edrm-storage/config/environment.example.d.ts +54 -0
  43. package/dist/modules/edrm-storage/config/environment.example.js +130 -0
  44. package/dist/modules/edrm-storage/examples/usage.example.d.ts +52 -0
  45. package/dist/modules/edrm-storage/examples/usage.example.js +156 -0
  46. package/dist/modules/edrm-storage/index.d.ts +5 -0
  47. package/dist/modules/edrm-storage/index.js +8 -0
  48. package/dist/modules/edrm-storage/integration/edrm-storage-integration.d.ts +53 -0
  49. package/dist/modules/edrm-storage/integration/edrm-storage-integration.js +132 -0
  50. package/dist/modules/edrm-storage/interfaces/storage-provider.interface.d.ts +35 -0
  51. package/dist/modules/edrm-storage/interfaces/storage-provider.interface.js +1 -0
  52. package/dist/modules/edrm-storage/migrations/edrm-storage.migration.d.ts +6 -0
  53. package/dist/modules/edrm-storage/migrations/edrm-storage.migration.js +151 -0
  54. package/dist/modules/edrm-storage/models/file.model.d.ts +78 -0
  55. package/dist/modules/edrm-storage/models/file.model.js +190 -0
  56. package/dist/modules/edrm-storage/providers/s3-storage.provider.d.ts +18 -0
  57. package/dist/modules/edrm-storage/providers/s3-storage.provider.js +95 -0
  58. package/dist/modules/edrm-storage/routes/edrm-storage.router.d.ts +8 -0
  59. package/dist/modules/edrm-storage/routes/edrm-storage.router.js +155 -0
  60. package/dist/modules/edrm-storage/scripts/quick-start.d.ts +7 -0
  61. package/dist/modules/edrm-storage/scripts/quick-start.js +114 -0
  62. package/dist/modules/edrm-storage/services/edrm-storage.service.d.ts +29 -0
  63. package/dist/modules/edrm-storage/services/edrm-storage.service.js +188 -0
  64. package/dist/modules/edrm-storage/tests/edrm-storage.service.test.d.ts +1 -0
  65. package/dist/modules/edrm-storage/tests/edrm-storage.service.test.js +143 -0
  66. package/dist/modules/edrm-storage/tests/integration.test.d.ts +1 -0
  67. package/dist/modules/edrm-storage/tests/integration.test.js +141 -0
  68. package/package.json +81 -0
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # Endurance Template
2
+
3
+ ## Overview
4
+
5
+ The Endurance Framework is a highly modular and scalable Node.js project template built on Express.js. It is designed to dynamically load and manage independent modules, making it extremely easy to develop, extend, and maintain web applications. The goal is to create a library of ready-made modules (e.g., login, user management, etc.) that can be easily integrated into any project.
6
+
7
+ ## Features
8
+
9
+ - **Dynamic Module Loading**: Easily add new modules with their own models and routes, and they will be automatically loaded and exposed by the application.
10
+ - **Express.js**: Fast, unopinionated, minimalist web framework for Node.js.
11
+ - **Modular Structure**: Each module is independent, promoting separation of concerns and better maintainability.
12
+ - **Modules marketplace**: Uses npm packages starting with "EDRM-" to quickly add features to your API.
13
+ - **Lib assets**: Include everything your need to start creating a robust API : events management, CRON, swagger, API versioning, webhooks etc.
14
+
15
+ ## Getting Started
16
+
17
+ ### Prerequisites
18
+
19
+ - Node.js (v20.x)
20
+ - MongoDB (optional for session management and data management)
21
+
22
+ ### Installation
23
+
24
+ 1. Install our CLI:
25
+
26
+ ```sh
27
+ npm install -g endurance
28
+ ```
29
+
30
+ 2. Create a project folder and create a new project:
31
+
32
+ ```sh
33
+ mkdir newproject
34
+ cd newproject
35
+
36
+ endurance new
37
+ ```
38
+
39
+ ### Usage
40
+
41
+ 1. **Start the application**:
42
+
43
+ For development:
44
+
45
+ ```sh
46
+ npm start
47
+ ```
48
+
49
+ For production:
50
+
51
+ ```sh
52
+ npm run prod
53
+ ```
54
+
55
+ 2. **Add a new module**:
56
+
57
+ To add a new module, create a new folder under the `modules` directory. Each module should contain its own models and routes.
58
+
59
+ Example structure for a new module:
60
+
61
+ ```
62
+ modules/
63
+ your-module/
64
+ models/
65
+ YourModel.js
66
+ routes/
67
+ yourModule.router.js
68
+ ```
69
+
70
+ 3. **Dynamic Module Loading**:
71
+
72
+ The application will automatically load and expose the routes and models from any new module added to the `modules` directory. There is no need for additional configuration.
73
+
74
+ ### Example
75
+
76
+ Here is an example of how to add a simple "login" module:
77
+
78
+ 1. **Create the module structure**:
79
+
80
+ ```
81
+ modules/
82
+ login/
83
+ models/
84
+ User.js
85
+ routes/
86
+ login.router.js
87
+ ```
88
+
89
+ 2. **Define the model (`User.js`)**:
90
+
91
+ ```javascript
92
+ const mongoose = require('mongoose');
93
+
94
+ const UserSchema = new mongoose.Schema({
95
+ username: { type: String, required: true },
96
+ password: { type: String, required: true }
97
+ });
98
+
99
+ module.exports = mongoose.model('User', UserSchema);
100
+ ```
101
+
102
+ 3. **Define the route (`login.router.js`)**:
103
+
104
+ ```javascript
105
+ const router = require('endurance-core/lib/router')();
106
+
107
+ router.post('/login', (req, res) => {
108
+ // Your login logic here
109
+ res.send('Login route');
110
+ });
111
+
112
+ module.exports = router;
113
+ ```
114
+
115
+ ### Testing
116
+
117
+ Run tests using Mocha and Supertest:
118
+
119
+ ```sh
120
+ npm test
121
+ ```
122
+
123
+ ### Roadmap
124
+
125
+ - **Library of Modules**: Develop a library of ready-made modules (e.g., login, user management) for easy integration.
126
+ - **Enhanced Documentation**: Provide detailed documentation and examples for each module.
127
+ - **Community Contributions**: Encourage community contributions to expand the module library.
128
+
129
+ ## Contributing
130
+
131
+ We welcome contributions! Please read our [Contributing Guidelines](CONTRIBUTING.md) for more details.
132
+
133
+ ## License
134
+
135
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const localAppPath = path.join(__dirname, '../lib/app.js');
8
+ if (fs.existsSync(localAppPath)) {
9
+ await import(localAppPath);
10
+ }
11
+ else {
12
+ await import('@programisto/endurance-core');
13
+ }
@@ -0,0 +1,9 @@
1
+ Question : ${instruction}
2
+ Type de question : ${questionType}
3
+ - si MCQ les réponses possibles étaient : ${possibleResponses}
4
+
5
+ Réponse du candidat à corriger :
6
+ --------------------------------
7
+ ${response}
8
+ --------------------------------
9
+ Score à donner : De 0 point (tout faux ou réponse vide) à ${maxScore} points (réponse correcte), avec un commentaire de correction
@@ -0,0 +1,6 @@
1
+ Métier ciblé : ${job}
2
+ Catégorie de la question : ${category} niveau ${expertiseLevel}
3
+ Type de question : ${questionType}
4
+ Format : json
5
+
6
+ Voici la liste des questions déjà présentes dans le test (pour éviter les doublons) : ${otherQuestions}
@@ -0,0 +1,37 @@
1
+ interface CreateQuestionParams {
2
+ job: string;
3
+ seniority: string;
4
+ questionType: string;
5
+ category: string;
6
+ expertiseLevel: string;
7
+ otherQuestions: string;
8
+ }
9
+ interface CorrectQuestionParams {
10
+ question: {
11
+ _id: string;
12
+ instruction: string;
13
+ maxScore: number;
14
+ possibleResponses: Array<{
15
+ possibleResponse: string;
16
+ valid: boolean;
17
+ }>;
18
+ questionType: string;
19
+ };
20
+ result: {
21
+ responses: Array<{
22
+ questionId: string;
23
+ response: string;
24
+ }>;
25
+ };
26
+ }
27
+ interface ContextBuilder {
28
+ createQuestion: (params: CreateQuestionParams) => Promise<Record<string, string>>;
29
+ correctQuestion: (params: CorrectQuestionParams) => Promise<{
30
+ instruction: string;
31
+ response: string;
32
+ maxScore: number;
33
+ }>;
34
+ }
35
+ export declare function generateLiveMessage(messageType: keyof ContextBuilder, params: CreateQuestionParams | CorrectQuestionParams, json?: boolean): Promise<string>;
36
+ export declare function generateLiveMessageAssistant(assistantId: string, messageType: keyof ContextBuilder, params: CreateQuestionParams | CorrectQuestionParams, json?: boolean): Promise<string>;
37
+ export {};
@@ -0,0 +1,135 @@
1
+ import OpenAI from 'openai';
2
+ import { fileURLToPath } from 'url';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const openai = new OpenAI({
8
+ apiKey: process.env.OPENAI_API_KEY
9
+ });
10
+ const contextBuilder = {
11
+ async createQuestion({ job, seniority, questionType, category, expertiseLevel, otherQuestions }) {
12
+ const context = {
13
+ job,
14
+ seniority,
15
+ questionType,
16
+ category,
17
+ expertiseLevel,
18
+ otherQuestions
19
+ };
20
+ return context;
21
+ },
22
+ async correctQuestion({ question, result }) {
23
+ let response = '';
24
+ for (let i = 0; i < result.responses.length; i++) {
25
+ if (result.responses[i].questionId === question._id) {
26
+ response = result.responses[i].response;
27
+ break;
28
+ }
29
+ }
30
+ const instruction = question.instruction;
31
+ const maxScore = question.maxScore;
32
+ const questionType = question.questionType;
33
+ const possibleResponses = question.possibleResponses.map((response, index) => `réponse ${index + 1} = "${response.possibleResponse}" (${response.valid ? 'correcte' : 'incorrecte'})`).join('\n');
34
+ const context = {
35
+ instruction,
36
+ response,
37
+ maxScore,
38
+ questionType,
39
+ possibleResponses
40
+ };
41
+ return context;
42
+ }
43
+ };
44
+ export async function generateLiveMessage(messageType, params, json) {
45
+ const MAX_RETRY = 2;
46
+ let retryCount = 0;
47
+ const context = await contextBuilder[messageType](params);
48
+ const text = fs.readFileSync(path.join(__dirname, 'openai', `${messageType}.txt`), 'utf8');
49
+ const message = text.replace(/\${(.*?)}/g, (_, v) => context[v]);
50
+ while (retryCount <= MAX_RETRY) {
51
+ try {
52
+ const openAIParams = {
53
+ model: 'gpt-4-1106-preview',
54
+ temperature: 0.7,
55
+ messages: [{ role: 'system', content: message }]
56
+ };
57
+ if (json) {
58
+ openAIParams.response_format = { type: 'json_object' };
59
+ }
60
+ const result = await openai.chat.completions.create(openAIParams);
61
+ const content = result.choices[0].message.content;
62
+ if (!content) {
63
+ throw new Error('No content in response');
64
+ }
65
+ return removeQuotes(content);
66
+ }
67
+ catch (error) {
68
+ retryCount++;
69
+ console.log(error);
70
+ if (retryCount > MAX_RETRY) {
71
+ return 'Brain freezed, I cannot generate a live message right now.';
72
+ }
73
+ }
74
+ }
75
+ return 'Brain freezed, I cannot generate a live message right now.';
76
+ }
77
+ export async function generateLiveMessageAssistant(assistantId, messageType, params, json) {
78
+ const MAX_RETRY = 2;
79
+ let retryCount = 0;
80
+ // Construire le contexte pour le message
81
+ const context = await contextBuilder[messageType](params);
82
+ const text = fs.readFileSync(path.join(__dirname, 'openai', `${messageType}.txt`), 'utf8');
83
+ const message = text.replace(/\${(.*?)}/g, (_, v) => context[v]);
84
+ while (retryCount <= MAX_RETRY) {
85
+ try {
86
+ // Créer un thread avec l'assistant
87
+ const thread = await openai.beta.threads.create();
88
+ // Ajouter le message avec le contexte
89
+ await openai.beta.threads.messages.create(thread.id, {
90
+ role: 'user',
91
+ content: message
92
+ });
93
+ // Exécuter l'assistant
94
+ const run = await openai.beta.threads.runs.create(thread.id, {
95
+ assistant_id: assistantId
96
+ });
97
+ // Attendre que l'exécution soit terminée
98
+ let runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);
99
+ while (runStatus.status === 'in_progress' || runStatus.status === 'queued') {
100
+ await new Promise(resolve => setTimeout(resolve, 1000));
101
+ runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);
102
+ }
103
+ if (runStatus.status === 'failed') {
104
+ throw new Error('Assistant execution failed');
105
+ }
106
+ // Récupérer les messages de réponse
107
+ const messages = await openai.beta.threads.messages.list(thread.id);
108
+ const lastMessage = messages.data[0]; // Le premier message est le plus récent
109
+ if (!lastMessage || !lastMessage.content || lastMessage.content.length === 0) {
110
+ throw new Error('No content in response');
111
+ }
112
+ const content = lastMessage.content[0];
113
+ if (content.type === 'text') {
114
+ return removeQuotes(content.text.value);
115
+ }
116
+ else {
117
+ throw new Error('Unexpected content type');
118
+ }
119
+ }
120
+ catch (error) {
121
+ retryCount++;
122
+ console.log(error);
123
+ if (retryCount > MAX_RETRY) {
124
+ return 'Brain freezed, I cannot generate a live message right now.';
125
+ }
126
+ }
127
+ }
128
+ return 'Brain freezed, I cannot generate a live message right now.';
129
+ }
130
+ function removeQuotes(str) {
131
+ if (str.startsWith('"') && str.endsWith('"')) {
132
+ return str.substring(1, str.length - 1);
133
+ }
134
+ return str;
135
+ }
@@ -0,0 +1,2 @@
1
+ import { enduranceListener } from '@programisto/endurance-core';
2
+ export default enduranceListener;
@@ -0,0 +1,167 @@
1
+ import { enduranceListener, enduranceEventTypes, enduranceEmitter } from '@programisto/endurance-core';
2
+ import TestQuestion from '../models/test-question.model.js';
3
+ import { generateLiveMessageAssistant } from '../lib/openai.js';
4
+ import TestResult, { TestState } from '../models/test-result.model.js';
5
+ import CandidateModel from '../models/candidate.model.js';
6
+ import ContactModel from '../models/contact.model.js';
7
+ import TestModel from '../models/test.model.js';
8
+ async function sendDiscordNotification(message) {
9
+ const discordWebhook = process.env.TEST_CORRECTION_DISCORD_WEBHOOKS;
10
+ if (discordWebhook) {
11
+ try {
12
+ await fetch(discordWebhook, {
13
+ method: 'POST',
14
+ headers: {
15
+ 'Content-Type': 'application/json',
16
+ },
17
+ body: JSON.stringify({
18
+ content: message,
19
+ }),
20
+ });
21
+ }
22
+ catch (error) {
23
+ console.error('Error sending Discord notification:', error);
24
+ }
25
+ }
26
+ }
27
+ async function correctTest(options) {
28
+ if (!options.testId)
29
+ throw new Error('TestId is required');
30
+ if (!options.responses)
31
+ throw new Error('Responses are required');
32
+ if (!options.state)
33
+ throw new Error('State is required');
34
+ try {
35
+ // Récupérer le résultat de test
36
+ const result = await TestResult.findById(options._id);
37
+ if (!result) {
38
+ throw new Error('Test result not found');
39
+ }
40
+ let finalscore = 0;
41
+ let maxScore = 0;
42
+ // Pour chaque réponse enregistrée en base, on cherche la correction correspondante
43
+ for (const dbResponse of result.responses) {
44
+ const correction = options.responses.find(r => r.questionId.toString() === dbResponse.questionId.toString());
45
+ if (!correction)
46
+ continue;
47
+ const question = await TestQuestion.findById(dbResponse.questionId);
48
+ if (!question) {
49
+ console.error('Question not found', { questionId: dbResponse.questionId });
50
+ continue;
51
+ }
52
+ maxScore += question.maxScore;
53
+ const scoreResponse = await generateLiveMessageAssistant(process.env.OPENAI_ASSISTANT_ID_CORRECT_QUESTION || '', 'correctQuestion', {
54
+ question: {
55
+ _id: question._id.toString(),
56
+ instruction: question.instruction,
57
+ possibleResponses: question.possibleResponses,
58
+ questionType: question.questionType,
59
+ maxScore: question.maxScore
60
+ },
61
+ result: {
62
+ responses: [{
63
+ questionId: dbResponse.questionId.toString(),
64
+ response: dbResponse.response
65
+ }]
66
+ }
67
+ }, true);
68
+ console.log('Correction result:', { scoreResponse });
69
+ const parsedResult = JSON.parse(scoreResponse);
70
+ // Valider le score retourné par l'IA
71
+ let validScore = 0;
72
+ if (parsedResult.score !== undefined && parsedResult.score !== null) {
73
+ const score = parseFloat(parsedResult.score.toString());
74
+ if (!isNaN(score) && isFinite(score) && score >= 0) {
75
+ validScore = score;
76
+ }
77
+ else {
78
+ console.warn('Invalid score returned by AI:', parsedResult.score);
79
+ }
80
+ }
81
+ finalscore += validScore;
82
+ dbResponse.score = validScore;
83
+ dbResponse.comment = parsedResult.comment || '';
84
+ }
85
+ // S'assurer que finalscore est un nombre valide
86
+ if (isNaN(finalscore) || !isFinite(finalscore)) {
87
+ console.warn('Invalid finalscore calculated, setting to 0:', finalscore);
88
+ finalscore = 0;
89
+ }
90
+ // S'assurer que maxScore est un nombre valide
91
+ if (isNaN(maxScore) || !isFinite(maxScore)) {
92
+ console.warn('Invalid maxScore calculated, setting to 0:', maxScore);
93
+ maxScore = 0;
94
+ }
95
+ // Mettre à jour le score final et l'état
96
+ result.score = finalscore;
97
+ result.state = TestState.Finish;
98
+ // Forcer la sauvegarde des sous-documents responses
99
+ result.markModified('responses');
100
+ // Calculer le pourcentage de score en évitant la division par zéro
101
+ let scorePercentage = 0;
102
+ if (maxScore > 0) {
103
+ scorePercentage = Math.ceil((finalscore / maxScore) * 100);
104
+ }
105
+ else if (finalscore > 0) {
106
+ // Si maxScore est 0 mais qu'il y a un score, on met 100%
107
+ scorePercentage = 100;
108
+ }
109
+ // S'assurer que le score est un nombre valide
110
+ if (isNaN(scorePercentage) || !isFinite(scorePercentage)) {
111
+ scorePercentage = 0;
112
+ }
113
+ // Sauvegarder les modifications avec findByIdAndUpdate pour éviter les conflits de version
114
+ await TestResult.findByIdAndUpdate(result._id, {
115
+ $set: {
116
+ responses: result.responses,
117
+ score: finalscore,
118
+ state: result.state
119
+ }
120
+ });
121
+ const test = await TestModel.findById(result.testId);
122
+ const candidate = await CandidateModel.findById(result.candidateId);
123
+ if (candidate) {
124
+ const contact = await ContactModel.findById(candidate.contact);
125
+ if (contact) {
126
+ const testLink = (process.env.TEST_INVITATION_LINK || '') + contact.email;
127
+ enduranceEmitter.emit(enduranceEventTypes.SEND_EMAIL, {
128
+ template: 'test-result',
129
+ to: contact.email,
130
+ data: {
131
+ firstname: contact.firstname,
132
+ lastname: contact.lastname,
133
+ score: scorePercentage,
134
+ testName: test?.title || '',
135
+ testLink
136
+ }
137
+ });
138
+ // Envoyer une notification Discord
139
+ const discordMessage = `📊 **Nouveau résultat de test**\n` +
140
+ `**Candidat:** ${contact.firstname} ${contact.lastname}\n` +
141
+ `**Test:** ${test?.title || 'Test inconnu'}\n` +
142
+ `**Score:** ${scorePercentage}%\n` +
143
+ `**Score brut:** ${finalscore}/${maxScore}`;
144
+ await sendDiscordNotification(discordMessage);
145
+ }
146
+ }
147
+ }
148
+ catch (err) {
149
+ if (err instanceof Error) {
150
+ console.error(`Error correcting test: ${err.message}`, { err });
151
+ }
152
+ else {
153
+ console.error('Unknown error occurred during test correction', { err });
154
+ }
155
+ throw err; // Propager l'erreur pour la gestion en amont
156
+ }
157
+ }
158
+ enduranceListener.createListener(enduranceEventTypes.CORRECT_TEST, (args) => {
159
+ if (typeof args === 'object' && args !== null) {
160
+ const options = args;
161
+ correctTest(options);
162
+ }
163
+ else {
164
+ console.error('Invalid data type received in correct listener', { args });
165
+ }
166
+ });
167
+ export default enduranceListener;
@@ -0,0 +1,21 @@
1
+ import { EnduranceSchema } from '@programisto/endurance-core';
2
+ import { Types } from 'mongoose';
3
+ export declare enum ExperienceLevel {
4
+ JUNIOR = "JUNIOR",
5
+ INTERMEDIATE = "INTERMEDIATE",
6
+ SENIOR = "SENIOR",
7
+ EXPERT = "EXPERT"
8
+ }
9
+ declare class Candidate extends EnduranceSchema {
10
+ contact: Types.ObjectId;
11
+ experienceLevel: string;
12
+ yearsOfExperience: number;
13
+ skills: string[];
14
+ magicLinkToken?: string;
15
+ magicLinkExpiresAt?: Date;
16
+ authToken?: string;
17
+ authTokenExpiresAt?: Date;
18
+ static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
19
+ }
20
+ declare const CandidateModel: import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
21
+ export default CandidateModel;
@@ -0,0 +1,75 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { EnduranceSchema, EnduranceModelType } from '@programisto/endurance-core';
11
+ import { Types } from 'mongoose';
12
+ // Enum pour les niveaux d'expérience
13
+ /* eslint-disable no-unused-vars */
14
+ export var ExperienceLevel;
15
+ (function (ExperienceLevel) {
16
+ ExperienceLevel["JUNIOR"] = "JUNIOR";
17
+ ExperienceLevel["INTERMEDIATE"] = "INTERMEDIATE";
18
+ ExperienceLevel["SENIOR"] = "SENIOR";
19
+ ExperienceLevel["EXPERT"] = "EXPERT";
20
+ })(ExperienceLevel || (ExperienceLevel = {}));
21
+ /* eslint-enable no-unused-vars */
22
+ let Candidate = class Candidate extends EnduranceSchema {
23
+ contact;
24
+ experienceLevel;
25
+ yearsOfExperience;
26
+ skills;
27
+ magicLinkToken;
28
+ magicLinkExpiresAt;
29
+ authToken;
30
+ authTokenExpiresAt;
31
+ static getModel() {
32
+ return CandidateModel;
33
+ }
34
+ };
35
+ __decorate([
36
+ EnduranceModelType.prop({ required: true, ref: 'Contact' }),
37
+ __metadata("design:type", Types.ObjectId)
38
+ ], Candidate.prototype, "contact", void 0);
39
+ __decorate([
40
+ EnduranceModelType.prop({ required: false, enum: ExperienceLevel, default: ExperienceLevel.JUNIOR }),
41
+ __metadata("design:type", String)
42
+ ], Candidate.prototype, "experienceLevel", void 0);
43
+ __decorate([
44
+ EnduranceModelType.prop({ required: false, type: Number, default: 0 }),
45
+ __metadata("design:type", Number)
46
+ ], Candidate.prototype, "yearsOfExperience", void 0);
47
+ __decorate([
48
+ EnduranceModelType.prop({ type: [String], required: true }),
49
+ __metadata("design:type", Array)
50
+ ], Candidate.prototype, "skills", void 0);
51
+ __decorate([
52
+ EnduranceModelType.prop({ required: false, type: String }),
53
+ __metadata("design:type", String)
54
+ ], Candidate.prototype, "magicLinkToken", void 0);
55
+ __decorate([
56
+ EnduranceModelType.prop({ required: false, type: Date }),
57
+ __metadata("design:type", Date)
58
+ ], Candidate.prototype, "magicLinkExpiresAt", void 0);
59
+ __decorate([
60
+ EnduranceModelType.prop({ required: false, type: String }),
61
+ __metadata("design:type", String)
62
+ ], Candidate.prototype, "authToken", void 0);
63
+ __decorate([
64
+ EnduranceModelType.prop({ required: false, type: Date }),
65
+ __metadata("design:type", Date)
66
+ ], Candidate.prototype, "authTokenExpiresAt", void 0);
67
+ Candidate = __decorate([
68
+ EnduranceModelType.modelOptions({
69
+ options: {
70
+ allowMixed: EnduranceModelType.Severity.ALLOW
71
+ }
72
+ })
73
+ ], Candidate);
74
+ const CandidateModel = EnduranceModelType.getModelForClass(Candidate);
75
+ export default CandidateModel;
@@ -0,0 +1,21 @@
1
+ import { EnduranceSchema } from '@programisto/endurance-core';
2
+ import { Types } from 'mongoose';
3
+ export declare enum ExperienceLevel {
4
+ JUNIOR = "JUNIOR",
5
+ INTERMEDIATE = "INTERMEDIATE",
6
+ SENIOR = "SENIOR",
7
+ EXPERT = "EXPERT"
8
+ }
9
+ declare class Candidate extends EnduranceSchema {
10
+ contact: Types.ObjectId;
11
+ experienceLevel: string;
12
+ yearsOfExperience: number;
13
+ skills: string[];
14
+ magicLinkToken?: string;
15
+ magicLinkExpiresAt?: Date;
16
+ authToken?: string;
17
+ authTokenExpiresAt?: Date;
18
+ static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
19
+ }
20
+ declare const CandidateModel: import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
21
+ export default CandidateModel;