Skip to content

Commit

Permalink
[BUGFIX] Les erreurs de runtime sur le downgrade de capacite ne remon…
Browse files Browse the repository at this point in the history
…te pas dans le monitoring (PIX-13660).

 #9821
  • Loading branch information
pix-service-auto-merge authored Aug 14, 2024
2 parents 93159bd + 161ff8c commit 8edd300
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 415 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const getNextChallengeForCampaignAssessment = async function ({
challenges,
});

if (_hasAnsweredToAllChallenges({ possibleChallenges })) {
throw new AssessmentEndedError();
}

return pickChallengeService.chooseNextChallenge(assessment.id)({ possibleChallenges });
} else {
const inputValues = await algorithmDataFetcherService.fetchForCampaigns(...arguments);
Expand All @@ -55,6 +59,10 @@ const getNextChallengeForCampaignAssessment = async function ({
}
};

const _hasAnsweredToAllChallenges = ({ possibleChallenges }) => {
return possibleChallenges.length === 0;
};

const _createDefaultAlgorithmConfiguration = () => {
return new FlashAssessmentAlgorithmConfiguration({
warmUpLength: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { Readable } from 'node:stream';

import _ from 'lodash';

import { parseCsv } from '../../../../scripts/helpers/csvHelpers.js';
import { pickChallengeService } from '../../../evaluation/domain/services/pick-challenge-service.js';
import { HttpErrors } from '../../../shared/application/http-errors.js';
import { random } from '../../../shared/infrastructure/utils/random.js';
import { extractLocaleFromRequest } from '../../../shared/infrastructure/utils/request-response-utils.js';
import { pickAnswerStatusService } from '../../shared/domain/services/pick-answer-status-service.js';
Expand Down Expand Up @@ -97,40 +95,6 @@ async function simulateFlashAssessmentScenario(
return h.response(generatedResponse).type('text/event-stream; charset=utf-8');
}

async function importScenarios(
request,
h,
dependencies = { parseCsv, pickChallengeService, scenarioSimulatorBatchSerializer, extractLocaleFromRequest },
) {
const parsedCsvData = await dependencies.parseCsv(request.payload.path);

if (!_isValidAnswerStatusArray(parsedCsvData)) {
return new HttpErrors.BadRequestError("Each CSV cell must be one of 'ok', 'ko' or 'aband'");
}

const locale = dependencies.extractLocaleFromRequest(request);

const results = (
await Promise.all(
parsedCsvData.map(async (answerStatusArray, index) => {
const pickAnswerStatus = pickAnswerStatusService.pickAnswerStatusFromArray(answerStatusArray);
const pickChallenge = dependencies.pickChallengeService.chooseNextChallenge(index);

return usecases.simulateFlashDeterministicAssessmentScenario({
pickAnswerStatus,
pickChallenge,
locale,
});
}),
)
).map((simulationReport, index) => ({
index,
simulationReport,
}));

return dependencies.scenarioSimulatorBatchSerializer.serialize(results);
}

function _getPickAnswerStatusMethod(pickAnswerStatusService, payload) {
const { type, probabilities, length, capacity, answerStatusArray } = payload;

Expand All @@ -146,10 +110,6 @@ function _getPickAnswerStatusMethod(pickAnswerStatusService, payload) {
}
}

function _isValidAnswerStatusArray(answerStatusArray) {
return answerStatusArray.every((row) => row.every((cell) => ['ok', 'ko', 'aband'].includes(cell)));
}

function _generateAnswerStatusArray(random, probabilities, length) {
return random.weightedRandoms(probabilities, length);
}
Expand All @@ -164,4 +124,4 @@ function _minimumEstimatedSuccessRateRangesToDomain(successRateRanges) {
});
}

export const scenarioSimulatorController = { simulateFlashAssessmentScenario, importScenarios };
export const scenarioSimulatorController = { simulateFlashAssessmentScenario };
Original file line number Diff line number Diff line change
Expand Up @@ -82,29 +82,6 @@ const register = async (server) => {
],
},
},
{
method: 'POST',
path: '/api/scenario-simulator/csv-import',
config: {
pre: [
{
method: securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
assign: 'hasAuthorizationToAccessAdminScope',
},
],
handler: scenarioSimulatorController.importScenarios,
payload: {
maxBytes: 20715200,
output: 'file',
parse: 'gunzip',
},
tags: ['api'],
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Elle permet de générer la liste de challenges passés avec le nouvel algorithme ainsi que le niveau estimé, pour une liste de réponses données via un import de fichier CSV',
],
},
},
]);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,20 @@ export class AssessmentSimulator {
const result = [];

let stepIndex = 0;
let hasNextAnswer;

// eslint-disable-next-line no-constant-condition
while (true) {
try {
const simulatorStepResult = this.getStrategy(stepIndex).run({ challengesAnswers, stepIndex });
do {
hasNextAnswer = false;

if (!simulatorStepResult) {
break;
}
const simulatorStepResult = this.getStrategy(stepIndex).run({ challengesAnswers, stepIndex });
if (simulatorStepResult) {
stepIndex = simulatorStepResult.nextStepIndex;
challengesAnswers.push(...simulatorStepResult.challengeAnswers);
result.push(...simulatorStepResult.results);
} catch (error) {
logger.error(error);
break;

hasNextAnswer = true;
}
}
} while (hasNextAnswer);

logger.trace({ result }, 'AssessmentSimulator result');
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ export class AssessmentSimulatorSingleMeasureStrategy {

const nextChallenge = this.pickChallenge({ possibleChallenges });

if (!nextChallenge) {
return null;
}

const answerStatus = this.pickAnswerStatus({
answerIndex: stepIndex,
nextChallenge,
});

const noMoreAnswerRemaining = !answerStatus;

if (noMoreAnswerRemaining) {
if (!answerStatus) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { config } from '../../../../shared/config.js';
import { AssessmentEndedError } from '../../../../shared/domain/errors.js';
import { FlashAssessmentAlgorithmChallengesBetweenCompetencesRule } from './FlashAssessmentAlgorithmChallengesBetweenCompetencesRule.js';
import { FlashAssessmentAlgorithmForcedCompetencesRule } from './FlashAssessmentAlgorithmForcedCompetencesRule.js';
import { FlashAssessmentAlgorithmNonAnsweredSkillsRule } from './FlashAssessmentAlgorithmNonAnsweredSkillsRule.js';
Expand Down Expand Up @@ -40,8 +39,13 @@ class FlashAssessmentAlgorithm {
initialCapacity = config.v3Certification.defaultCandidateCapacity,
answersForComputingCapacity,
}) {
if (assessmentAnswers.length >= this._configuration.maximumAssessmentLength) {
throw new AssessmentEndedError();
const maximumAssessmentLength = this._configuration.maximumAssessmentLength;
if (assessmentAnswers?.length > maximumAssessmentLength) {
throw new RangeError('User answered more questions than allowed');
}

if (this.#hasAnsweredToAllChallenges({ assessmentAnswers, maximumAssessmentLength })) {
return [];
}

const { capacity } = this.getCapacityAndErrorRate({
Expand All @@ -50,13 +54,13 @@ class FlashAssessmentAlgorithm {
initialCapacity,
});

const challengesAfterRulesApplication = this._applyChallengeSelectionRules(assessmentAnswers, challenges);
const challengesAfterRulesApplication = this.#applyChallengeSelectionRules(assessmentAnswers, challenges);

if (challengesAfterRulesApplication?.length === 0) {
throw new AssessmentEndedError();
throw new RangeError('No eligible challenges in referential');
}

const minimalSuccessRate = this._computeMinimalSuccessRate(assessmentAnswers.length);
const minimalSuccessRate = this.#computeMinimalSuccessRate(assessmentAnswers.length);

return this.flashAlgorithmImplementation.getPossibleNextChallenges({
availableChallenges: challengesAfterRulesApplication,
Expand All @@ -68,15 +72,23 @@ class FlashAssessmentAlgorithm {
});
}

_applyChallengeSelectionRules(assessmentAnswers, challenges) {
#hasAnsweredToAllChallenges({ assessmentAnswers, maximumAssessmentLength }) {
if (assessmentAnswers && assessmentAnswers.length === maximumAssessmentLength) {
return true;
}

return false;
}

#applyChallengeSelectionRules(assessmentAnswers, challenges) {
return this.ruleEngine.execute({
assessmentAnswers,
allChallenges: challenges,
});
}

_computeMinimalSuccessRate(questionIndex) {
const filterConfiguration = this._findApplicableSuccessRateConfiguration(questionIndex);
#computeMinimalSuccessRate(questionIndex) {
const filterConfiguration = this.#findApplicableSuccessRateConfiguration(questionIndex);

if (!filterConfiguration) {
return 0;
Expand All @@ -85,7 +97,7 @@ class FlashAssessmentAlgorithm {
return filterConfiguration.getMinimalSuccessRate(questionIndex);
}

_findApplicableSuccessRateConfiguration(questionIndex) {
#findApplicableSuccessRateConfiguration(questionIndex) {
return this._configuration.minimumEstimatedSuccessRateRanges.find((successRateRange) =>
successRateRange.isApplicable(questionIndex),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @typedef {import('./index.js').FlashAlgorithmService} FlashAlgorithmService
*/

import { AssessmentEndedError } from '../../../../shared/domain/errors.js';
import { CertificationChallenge, FlashAssessmentAlgorithm } from '../../../../shared/domain/models/index.js';

/**
Expand Down Expand Up @@ -81,6 +82,10 @@ const getNextChallengeForV3Certification = async function ({
challenges: challengesWithoutSkillsWithAValidatedLiveAlert,
});

if (_hasAnsweredToAllChallenges({ possibleChallenges })) {
throw new AssessmentEndedError();
}

const challenge = pickChallengeService.chooseNextChallenge()({ possibleChallenges });

const certificationChallenge = new CertificationChallenge({
Expand All @@ -100,6 +105,10 @@ const getNextChallengeForV3Certification = async function ({
return challenge;
};

const _hasAnsweredToAllChallenges = ({ possibleChallenges }) => {
return possibleChallenges.length === 0;
};

const _excludeChallengesWithASkillWithAValidatedLiveAlert = ({ validatedLiveAlertChallengeIds, challenges }) => {
const validatedLiveAlertChallenges = challenges.filter((challenge) => {
return validatedLiveAlertChallengeIds.includes(challenge.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import { cpfReceiptsStorage } from '../../infrastructure/storage/cpf-receipts-st
* @typedef {certificationOfficerRepository} CertificationOfficerRepository
* @typedef {certificationChallengeRepository} CertificationChallengeRepository
* @typedef {challengeRepository} ChallengeRepository
* @typedef {competenceMarkRepository} CompetenceMarkRepository
* @typedef {finalizedSessionRepository} FinalizedSessionRepository
* @typedef {juryCertificationRepository} JuryCertificationRepository
* @typedef {jurySessionRepository} JurySessionRepository
Expand Down
Loading

0 comments on commit 8edd300

Please sign in to comment.