@programisto/edrm-exams 0.1.4 → 0.1.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.
- package/README.md +135 -0
- package/dist/bin/www.d.ts +2 -0
- package/dist/bin/www.js +9 -0
- package/dist/modules/edrm-exams/lib/openai/correctQuestion.txt +10 -0
- package/dist/modules/edrm-exams/lib/openai/createQuestion.txt +68 -0
- package/dist/modules/edrm-exams/lib/openai.d.ts +36 -0
- package/dist/modules/edrm-exams/lib/openai.js +82 -0
- package/dist/modules/edrm-exams/listeners/correct.listener.d.ts +2 -0
- package/dist/modules/edrm-exams/listeners/correct.listener.js +85 -0
- package/dist/modules/edrm-exams/models/candidate.models.d.ts +13 -0
- package/dist/modules/edrm-exams/models/candidate.models.js +59 -0
- package/dist/modules/edrm-exams/models/company.model.d.ts +8 -0
- package/dist/modules/edrm-exams/models/company.model.js +34 -0
- package/dist/modules/edrm-exams/models/test-category.models.d.ts +7 -0
- package/dist/modules/edrm-exams/models/test-category.models.js +29 -0
- package/dist/modules/edrm-exams/models/test-question.model.d.ts +25 -0
- package/dist/modules/edrm-exams/models/test-question.model.js +70 -0
- package/dist/modules/edrm-exams/models/test-result.model.d.ts +26 -0
- package/dist/modules/edrm-exams/models/test-result.model.js +70 -0
- package/dist/modules/edrm-exams/models/test.model.d.ts +52 -0
- package/dist/modules/edrm-exams/models/test.model.js +123 -0
- package/dist/modules/edrm-exams/models/user.model.d.ts +18 -0
- package/dist/modules/edrm-exams/models/user.model.js +64 -0
- package/dist/modules/edrm-exams/routes/company.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/company.router.js +108 -0
- package/dist/modules/edrm-exams/routes/exams-candidate.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/exams-candidate.router.js +299 -0
- package/dist/modules/edrm-exams/routes/exams.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/exams.router.js +1012 -0
- package/dist/modules/edrm-exams/routes/result.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/result.router.js +314 -0
- package/dist/modules/edrm-exams/routes/user.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/user.router.js +96 -0
- package/package.json +71 -8
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.
|
package/dist/bin/www.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
const coreAppPath = 'endurance-core/dist/lib/app.js';
|
|
9
|
+
fs.existsSync(localAppPath) ? await import(localAppPath) : await import(coreAppPath);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Tu est un correcteur de question de concours.
|
|
2
|
+
Tu doit donner un score à cette question : ${instruction} dont la réponse donnée par le candidat est : ${response}.
|
|
3
|
+
Il s'agissait d'une question de type ${questionType} (et si la question est de type MCQ alors les réponses possibles étaient les suivantes : ${possibleResponses})
|
|
4
|
+
le score maximum pour cette question est ${maxScore}
|
|
5
|
+
Tu doit donner un score entre 0 et ${maxScore} et un commentaire de correction.
|
|
6
|
+
Tu dois répondre au format JSON : la reponse envoyer doit correspondre exactement à ce model json :
|
|
7
|
+
{
|
|
8
|
+
"score": score,
|
|
9
|
+
"comment": commentaire
|
|
10
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Tu es un assistant qui crée des questions pour un test.
|
|
2
|
+
créer une question en fonction de du métier : ${job}, du niveau de seniorité : ${seniority}, de la categorie de question : ${category} et le niveau d'expertise de cette categorie : ${expertiseLevel} (allant de 1 à 10, de 1 à 3 = 'beginner', de 3 à 6 = 'intermediate', de 6 à 10 = 'advanced').
|
|
3
|
+
- la question doit être de type : ${questionType}.
|
|
4
|
+
- la question doit être dans la langue suivante : Français
|
|
5
|
+
- voici le model pour une question :
|
|
6
|
+
const questionModel = new mongoose.Schema(
|
|
7
|
+
{
|
|
8
|
+
instruction:
|
|
9
|
+
{
|
|
10
|
+
type: String,
|
|
11
|
+
required: true
|
|
12
|
+
},
|
|
13
|
+
questionType:
|
|
14
|
+
{
|
|
15
|
+
type: String,
|
|
16
|
+
enum:
|
|
17
|
+
["MCQ", "free question", "exercice"],
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
maxScore:
|
|
21
|
+
{
|
|
22
|
+
type: Number,
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
possibleResponses: // only on MCQ (multiple choice responses)
|
|
26
|
+
{
|
|
27
|
+
type:
|
|
28
|
+
[{
|
|
29
|
+
possibleResponse:
|
|
30
|
+
{
|
|
31
|
+
type: String,
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
valid:
|
|
35
|
+
{
|
|
36
|
+
type: Boolean,
|
|
37
|
+
required: true
|
|
38
|
+
}
|
|
39
|
+
}],
|
|
40
|
+
},
|
|
41
|
+
time: // in secondes
|
|
42
|
+
{
|
|
43
|
+
type: Number,
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
textType: //type d'input demandé à l'utilisateur
|
|
47
|
+
{
|
|
48
|
+
type: String,
|
|
49
|
+
enum:
|
|
50
|
+
["text", "code"],
|
|
51
|
+
required: false
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
- toutes les instruction et les réponses doivent toujours être compatible avec LaTeX pour être afficher dans des balises <MathJax>
|
|
55
|
+
voici un exemple : Voici une formule mathématique : \\( \\frac{a}{b} = c \\)
|
|
56
|
+
- voici des containtes pour chaque type de question :
|
|
57
|
+
- MCQ
|
|
58
|
+
(il ne peut y avoir qu'une seule réponse juste sur quatre au total) le temps pour ces questions vont de 30 secondes à 1 minute
|
|
59
|
+
- free question ( ne rien mettre dans possibleResponses)
|
|
60
|
+
le temps pour ces questions vont de 30 secondes à 5 minutes en fonction de la complexité de la réponse attendue (à toi de juger)
|
|
61
|
+
- exercice ( ne rien mettre dans possibleResponses )
|
|
62
|
+
le temps pour ces questions vont de 3 à 20 minutes en fonction de la complexité de la réponse attendue (à toi de juger)
|
|
63
|
+
- les instruction et la difficulté doivent être en rapport avec targetJob et seniorityLevel de ${test}
|
|
64
|
+
- le temps pour répondre à la question dans time doit laisser le temps aux utilisateur d'y répondre, sans non plus lui laisser trop de temps pour qu'il triche (surtout sur les QCM).
|
|
65
|
+
- le format de la réponses doit être en json avec seulement les elements présent dans le model ci dessus et pas besoin d'ajouter d"_id".
|
|
66
|
+
- si la réponse attendue à une question de type free question ou exercice est un bout de code, alors remplit textType en "code", sinon laisse "text"
|
|
67
|
+
|
|
68
|
+
Pour ne pas faire de doublon, voici la liste des questions déjà présentes dans le test : ${otherQuestions}
|
|
@@ -0,0 +1,36 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,82 @@
|
|
|
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}`).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
|
+
function removeQuotes(str) {
|
|
78
|
+
if (str.startsWith('"') && str.endsWith('"')) {
|
|
79
|
+
return str.substring(1, str.length - 1);
|
|
80
|
+
}
|
|
81
|
+
return str;
|
|
82
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { enduranceListener, enduranceEventTypes } from '@programisto/endurance-core';
|
|
2
|
+
import TestQuestion from '../models/test-question.model.js';
|
|
3
|
+
import { generateLiveMessage } from '../lib/openai.js';
|
|
4
|
+
import TestResult, { TestState } from '../models/test-result.model.js';
|
|
5
|
+
async function correctTest(options) {
|
|
6
|
+
console.log('Correcting test', { options });
|
|
7
|
+
if (!options.testId)
|
|
8
|
+
throw new Error('TestId is required');
|
|
9
|
+
if (!options.responses)
|
|
10
|
+
throw new Error('Responses are required');
|
|
11
|
+
if (!options.state)
|
|
12
|
+
throw new Error('State is required');
|
|
13
|
+
try {
|
|
14
|
+
// Récupérer le résultat de test
|
|
15
|
+
const result = await TestResult.findById(options._id);
|
|
16
|
+
if (!result) {
|
|
17
|
+
throw new Error('Test result not found');
|
|
18
|
+
}
|
|
19
|
+
let finalscore = 0;
|
|
20
|
+
// Pour chaque réponse enregistrée en base, on cherche la correction correspondante
|
|
21
|
+
for (const dbResponse of result.responses) {
|
|
22
|
+
const correction = options.responses.find(r => r.questionId.toString() === dbResponse.questionId.toString());
|
|
23
|
+
if (!correction)
|
|
24
|
+
continue;
|
|
25
|
+
const question = await TestQuestion.findById(dbResponse.questionId);
|
|
26
|
+
if (!question) {
|
|
27
|
+
console.error('Question not found', { questionId: dbResponse.questionId });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const scoreResponse = await generateLiveMessage('correctQuestion', {
|
|
31
|
+
question: {
|
|
32
|
+
_id: question._id.toString(),
|
|
33
|
+
instruction: question.instruction,
|
|
34
|
+
possibleResponses: question.possibleResponses,
|
|
35
|
+
questionType: question.questionType,
|
|
36
|
+
maxScore: question.maxScore
|
|
37
|
+
},
|
|
38
|
+
result: {
|
|
39
|
+
responses: [{
|
|
40
|
+
questionId: dbResponse.questionId.toString(),
|
|
41
|
+
response: dbResponse.response
|
|
42
|
+
}]
|
|
43
|
+
}
|
|
44
|
+
}, true);
|
|
45
|
+
console.log('Correction result:', { scoreResponse });
|
|
46
|
+
const parsedResult = JSON.parse(scoreResponse);
|
|
47
|
+
finalscore += parsedResult.score;
|
|
48
|
+
dbResponse.score = parsedResult.score;
|
|
49
|
+
dbResponse.comment = parsedResult.comment || '';
|
|
50
|
+
}
|
|
51
|
+
// Mettre à jour le score final et l'état
|
|
52
|
+
result.score = finalscore;
|
|
53
|
+
result.state = TestState.Finish;
|
|
54
|
+
// Forcer la sauvegarde des sous-documents responses
|
|
55
|
+
result.markModified('responses');
|
|
56
|
+
// Sauvegarder les modifications avec findByIdAndUpdate pour éviter les conflits de version
|
|
57
|
+
await TestResult.findByIdAndUpdate(result._id, {
|
|
58
|
+
$set: {
|
|
59
|
+
responses: result.responses,
|
|
60
|
+
score: result.score,
|
|
61
|
+
state: result.state
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
console.log('Test correction completed and saved', { finalScore: finalscore });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err instanceof Error) {
|
|
68
|
+
console.error(`Error correcting test: ${err.message}`, { err });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.error('Unknown error occurred during test correction', { err });
|
|
72
|
+
}
|
|
73
|
+
throw err; // Propager l'erreur pour la gestion en amont
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
enduranceListener.createListener(enduranceEventTypes.CORRECT_TEST, (args) => {
|
|
77
|
+
if (typeof args === 'object' && args !== null) {
|
|
78
|
+
const options = args;
|
|
79
|
+
correctTest(options);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.error('Invalid data type received in correct listener', { args });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
export default enduranceListener;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EnduranceSchema } from '@programisto/endurance-core';
|
|
2
|
+
declare class Candidate extends EnduranceSchema {
|
|
3
|
+
firstName: string;
|
|
4
|
+
lastName: string;
|
|
5
|
+
email: string;
|
|
6
|
+
magicLinkToken?: string;
|
|
7
|
+
magicLinkExpiresAt?: Date;
|
|
8
|
+
authToken?: string;
|
|
9
|
+
authTokenExpiresAt?: Date;
|
|
10
|
+
static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
11
|
+
}
|
|
12
|
+
declare const CandidateModel: import("@typegoose/typegoose").ReturnModelType<typeof Candidate, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
13
|
+
export default CandidateModel;
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
let Candidate = class Candidate extends EnduranceSchema {
|
|
12
|
+
firstName;
|
|
13
|
+
lastName;
|
|
14
|
+
email;
|
|
15
|
+
magicLinkToken;
|
|
16
|
+
magicLinkExpiresAt;
|
|
17
|
+
authToken;
|
|
18
|
+
authTokenExpiresAt;
|
|
19
|
+
static getModel() {
|
|
20
|
+
return CandidateModel;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
__decorate([
|
|
24
|
+
EnduranceModelType.prop({ required: true }),
|
|
25
|
+
__metadata("design:type", String)
|
|
26
|
+
], Candidate.prototype, "firstName", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
EnduranceModelType.prop({ required: true }),
|
|
29
|
+
__metadata("design:type", String)
|
|
30
|
+
], Candidate.prototype, "lastName", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
EnduranceModelType.prop({ required: true, unique: true }),
|
|
33
|
+
__metadata("design:type", String)
|
|
34
|
+
], Candidate.prototype, "email", void 0);
|
|
35
|
+
__decorate([
|
|
36
|
+
EnduranceModelType.prop({ required: false, type: String }),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], Candidate.prototype, "magicLinkToken", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
EnduranceModelType.prop({ required: false, type: Date }),
|
|
41
|
+
__metadata("design:type", Date)
|
|
42
|
+
], Candidate.prototype, "magicLinkExpiresAt", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
EnduranceModelType.prop({ required: false, type: String }),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], Candidate.prototype, "authToken", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
EnduranceModelType.prop({ required: false, type: Date }),
|
|
49
|
+
__metadata("design:type", Date)
|
|
50
|
+
], Candidate.prototype, "authTokenExpiresAt", void 0);
|
|
51
|
+
Candidate = __decorate([
|
|
52
|
+
EnduranceModelType.modelOptions({
|
|
53
|
+
options: {
|
|
54
|
+
allowMixed: EnduranceModelType.Severity.ALLOW
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
], Candidate);
|
|
58
|
+
const CandidateModel = EnduranceModelType.getModelForClass(Candidate);
|
|
59
|
+
export default CandidateModel;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EnduranceSchema } from '@programisto/endurance-core';
|
|
2
|
+
declare class Company extends EnduranceSchema {
|
|
3
|
+
name: string;
|
|
4
|
+
logo: string;
|
|
5
|
+
static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof Company, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
6
|
+
}
|
|
7
|
+
declare const CompanyModel: import("@typegoose/typegoose").ReturnModelType<typeof Company, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
8
|
+
export default CompanyModel;
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
let Company = class Company extends EnduranceSchema {
|
|
12
|
+
name;
|
|
13
|
+
logo;
|
|
14
|
+
static getModel() {
|
|
15
|
+
return CompanyModel;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
__decorate([
|
|
19
|
+
EnduranceModelType.prop({ required: true }),
|
|
20
|
+
__metadata("design:type", String)
|
|
21
|
+
], Company.prototype, "name", void 0);
|
|
22
|
+
__decorate([
|
|
23
|
+
EnduranceModelType.prop({ required: true }),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], Company.prototype, "logo", void 0);
|
|
26
|
+
Company = __decorate([
|
|
27
|
+
EnduranceModelType.modelOptions({
|
|
28
|
+
options: {
|
|
29
|
+
allowMixed: EnduranceModelType.Severity.ALLOW
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
], Company);
|
|
33
|
+
const CompanyModel = EnduranceModelType.getModelForClass(Company);
|
|
34
|
+
export default CompanyModel;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { EnduranceSchema } from '@programisto/endurance-core';
|
|
2
|
+
declare class TestCategory extends EnduranceSchema {
|
|
3
|
+
name: string;
|
|
4
|
+
static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof TestCategory, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
5
|
+
}
|
|
6
|
+
declare const TestCategoryModel: import("@typegoose/typegoose").ReturnModelType<typeof TestCategory, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
7
|
+
export default TestCategoryModel;
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
let TestCategory = class TestCategory extends EnduranceSchema {
|
|
12
|
+
name;
|
|
13
|
+
static getModel() {
|
|
14
|
+
return TestCategoryModel;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
__decorate([
|
|
18
|
+
EnduranceModelType.prop({ required: true }),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], TestCategory.prototype, "name", void 0);
|
|
21
|
+
TestCategory = __decorate([
|
|
22
|
+
EnduranceModelType.modelOptions({
|
|
23
|
+
options: {
|
|
24
|
+
allowMixed: EnduranceModelType.Severity.ALLOW
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
], TestCategory);
|
|
28
|
+
const TestCategoryModel = EnduranceModelType.getModelForClass(TestCategory);
|
|
29
|
+
export default TestCategoryModel;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EnduranceSchema } from '@programisto/endurance-core';
|
|
2
|
+
declare enum QuestionType {
|
|
3
|
+
MCQ = "MCQ",
|
|
4
|
+
FreeQuestion = "free question",
|
|
5
|
+
Exercise = "exercice"
|
|
6
|
+
}
|
|
7
|
+
interface PossibleResponse {
|
|
8
|
+
possibleResponse: string;
|
|
9
|
+
valid: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare enum TextType {
|
|
12
|
+
Text = "text",
|
|
13
|
+
Code = "code"
|
|
14
|
+
}
|
|
15
|
+
declare class TestQuestion extends EnduranceSchema {
|
|
16
|
+
questionType: QuestionType;
|
|
17
|
+
instruction: string;
|
|
18
|
+
maxScore: number;
|
|
19
|
+
possibleResponses: PossibleResponse[];
|
|
20
|
+
time: number;
|
|
21
|
+
textType: TextType;
|
|
22
|
+
static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof TestQuestion, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
23
|
+
}
|
|
24
|
+
declare const TestQuestionModel: import("@typegoose/typegoose").ReturnModelType<typeof TestQuestion, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
25
|
+
export default TestQuestionModel;
|