From e82f5f768c37daeb352899831fc8854554bed924 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Tue, 19 Nov 2024 14:31:24 +0100 Subject: [PATCH 1/6] refactor(api): remove doubleMeasuresUntil parameter from simulator --- .../scenario-simulator-controller.js | 2 - .../application/scenario-simulator-route.js | 1 - .../domain/models/AssessmentSimulator.js | 2 +- ...ssessmentSimulatorDoubleMeasureStrategy.js | 99 --------- .../simulate-flash-assessment-scenario.js | 15 +- ...mentSimulatorDoubleMeasureStrategy_test.js | 193 ------------------ .../domain/models/AssessmentSimulator_test.js | 14 +- ...simulate-flash-assessment-scenario_test.js | 27 --- 8 files changed, 7 insertions(+), 346 deletions(-) delete mode 100644 api/src/certification/flash-certification/domain/models/AssessmentSimulatorDoubleMeasureStrategy.js delete mode 100644 api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulatorDoubleMeasureStrategy_test.js diff --git a/api/src/certification/flash-certification/application/scenario-simulator-controller.js b/api/src/certification/flash-certification/application/scenario-simulator-controller.js index efd008ed8d3..9815a1c2176 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-controller.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-controller.js @@ -29,7 +29,6 @@ async function simulateFlashAssessmentScenario( challengesBetweenSameCompetence, limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges: minimumEstimatedSuccessRateRangesDto, - doubleMeasuresUntil, variationPercent, variationPercentUntil, capacity, @@ -59,7 +58,6 @@ async function simulateFlashAssessmentScenario( challengesBetweenSameCompetence, limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges, - doubleMeasuresUntil, variationPercent, variationPercentUntil, }, diff --git a/api/src/certification/flash-certification/application/scenario-simulator-route.js b/api/src/certification/flash-certification/application/scenario-simulator-route.js index 4e830212a7d..e41ca1c58df 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-route.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-route.js @@ -17,7 +17,6 @@ const _baseScenarioParametersValidator = Joi.object().keys({ challengesBetweenSameCompetence: Joi.number().min(0), limitToOneQuestionPerTube: Joi.boolean(), minimumEstimatedSuccessRateRanges: Joi.array().items(_successRatesConfigurationValidator), - doubleMeasuresUntil: Joi.number().min(0), variationPercent: Joi.number().min(0).max(1), variationPercentUntil: Joi.number().min(0), }); diff --git a/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js b/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js index 5a7d4cb3b50..2ad8a0e350f 100644 --- a/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js +++ b/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js @@ -14,7 +14,7 @@ export class AssessmentSimulator { do { hasNextAnswer = false; - const simulatorStepResult = this.getStrategy(stepIndex).run({ challengesAnswers, stepIndex }); + const simulatorStepResult = this.getStrategy().run({ challengesAnswers, stepIndex }); if (simulatorStepResult) { stepIndex = simulatorStepResult.nextStepIndex; challengesAnswers.push(...simulatorStepResult.challengeAnswers); diff --git a/api/src/certification/flash-certification/domain/models/AssessmentSimulatorDoubleMeasureStrategy.js b/api/src/certification/flash-certification/domain/models/AssessmentSimulatorDoubleMeasureStrategy.js deleted file mode 100644 index 141f73ff613..00000000000 --- a/api/src/certification/flash-certification/domain/models/AssessmentSimulatorDoubleMeasureStrategy.js +++ /dev/null @@ -1,99 +0,0 @@ -import { Answer } from '../../../../evaluation/domain/models/Answer.js'; - -const NUMBER_OF_MEASURES = 2; -export class AssessmentSimulatorDoubleMeasureStrategy { - constructor({ algorithm, challenges, pickChallenge, pickAnswerStatus, initialCapacity, doubleMeasuresUntil }) { - this.algorithm = algorithm; - this.challenges = challenges; - this.pickAnswerStatus = pickAnswerStatus; - this.pickChallenge = pickChallenge; - this.initialCapacity = initialCapacity; - this.doubleMeasuresUntil = doubleMeasuresUntil; - } - - run({ challengesAnswers, stepIndex }) { - const results = []; - const newAnswers = []; - - const { capacity: capacityBefore } = this.algorithm.getCapacityAndErrorRate({ - allAnswers: challengesAnswers, - challenges: this.challenges, - initialCapacity: this.initialCapacity, - doubleMeasuresUntil: this.doubleMeasuresUntil, - }); - - for (let index = 0; index < NUMBER_OF_MEASURES; index++) { - const possibleChallenges = this.algorithm.getPossibleNextChallenges({ - assessmentAnswers: [...challengesAnswers, ...newAnswers], - challenges: this.challenges, - initialCapacity: this.initialCapacity, - answersForComputingCapacity: challengesAnswers, - }); - - const availableChallenges = possibleChallenges.filter(({ id }) => { - return newAnswers.every(({ challengeId }) => challengeId !== id); - }); - const { hasAssessmentEnded, nextChallenge, answer } = this._getNextChallengeAndAnswer({ - possibleChallenges: availableChallenges, - stepIndex: stepIndex + index, - }); - - if (hasAssessmentEnded) { - return null; - } - - const reward = this.algorithm.getReward({ - capacity: capacityBefore, - difficulty: nextChallenge.difficulty, - discriminant: nextChallenge.discriminant, - }); - - results.push({ - challenge: nextChallenge, - answerStatus: answer.result.status, - reward, - }); - - newAnswers.push(answer); - } - - const { capacity } = this.algorithm.getCapacityAndErrorRate({ - allAnswers: [...challengesAnswers, ...newAnswers], - challenges: this.challenges, - initialCapacity: this.initialCapacity, - doubleMeasuresUntil: this.doubleMeasuresUntil, - }); - - return { - results: results.map((result) => ({ ...result, capacity })), - challengeAnswers: newAnswers, - nextStepIndex: stepIndex + NUMBER_OF_MEASURES, - }; - } - - _getNextChallengeAndAnswer({ possibleChallenges, stepIndex }) { - const nextChallenge = this.pickChallenge({ possibleChallenges }); - - const answerStatus = this.pickAnswerStatus({ - answerIndex: stepIndex, - nextChallenge, - }); - - const noMoreAnswerRemaining = !answerStatus; - - if (noMoreAnswerRemaining) { - return { - hasAssessmentEnded: true, - }; - } - - return { - nextChallenge, - answer: new Answer({ - result: answerStatus, - challengeId: nextChallenge.id, - }), - hasAssessmentEnded: false, - }; - } -} diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index 1b23dacd5ff..3344535468b 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -1,6 +1,5 @@ import { FlashAssessmentAlgorithmConfiguration } from '../../../shared/domain/models/FlashAssessmentAlgorithmConfiguration.js'; import { AssessmentSimulator } from '../models/AssessmentSimulator.js'; -import { AssessmentSimulatorDoubleMeasureStrategy } from '../models/AssessmentSimulatorDoubleMeasureStrategy.js'; import { AssessmentSimulatorSingleMeasureStrategy } from '../models/AssessmentSimulatorSingleMeasureStrategy.js'; import { FlashAssessmentAlgorithm } from '../models/FlashAssessmentAlgorithm.js'; @@ -13,7 +12,6 @@ export async function simulateFlashAssessmentScenario({ challengesBetweenSameCompetence = 0, limitToOneQuestionPerTube = true, minimumEstimatedSuccessRateRanges = [], - doubleMeasuresUntil = 0, variationPercent, variationPercentUntil, challengeRepository, @@ -31,7 +29,7 @@ export async function simulateFlashAssessmentScenario({ enablePassageByAllCompetences: enablePassageByAllCompetencesValueInProduction, variationPercent, variationPercentUntil, - doubleMeasuresUntil, + doubleMeasuresUntil: 0, challengesBetweenSameCompetence, maximumAssessmentLength: stopAtChallenge, }), @@ -45,16 +43,7 @@ export async function simulateFlashAssessmentScenario({ initialCapacity, }); - const doubleMeasureStrategy = new AssessmentSimulatorDoubleMeasureStrategy({ - algorithm: flashAssessmentAlgorithm, - challenges, - pickChallenge, - pickAnswerStatus, - initialCapacity, - }); - - const getStrategy = (questionIndex) => - questionIndex >= doubleMeasuresUntil ? singleMeasureStrategy : doubleMeasureStrategy; + const getStrategy = () => singleMeasureStrategy; const simulator = new AssessmentSimulator({ getStrategy, diff --git a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulatorDoubleMeasureStrategy_test.js b/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulatorDoubleMeasureStrategy_test.js deleted file mode 100644 index d0ff83aacd0..00000000000 --- a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulatorDoubleMeasureStrategy_test.js +++ /dev/null @@ -1,193 +0,0 @@ -import { AssessmentSimulatorDoubleMeasureStrategy } from '../../../../../../src/certification/flash-certification/domain/models/AssessmentSimulatorDoubleMeasureStrategy.js'; -import { Answer, AnswerStatus } from '../../../../../../src/shared/domain/models/index.js'; -import { domainBuilder, expect, sinon } from '../../../../../test-helper.js'; - -describe('Unit | Domain | Models | AssessmentSimulatorDoubleMeasureStrategy', function () { - describe('#run', function () { - context('when there is no available answer', function () { - it('should return null', function () { - // given - const challenge1 = domainBuilder.buildChallenge({ id: 'rec1' }); - const challenge2 = domainBuilder.buildChallenge({ id: 'rec2' }); - const allChallenges = [challenge1, challenge2]; - const initialCapacity = 0; - const doubleMeasuresUntil = 2; - const algorithm = { - getPossibleNextChallenges: sinon.stub(), - getCapacityAndErrorRate: sinon.stub(), - getReward: sinon.stub(), - }; - const pickChallenge = sinon.stub(); - const pickAnswerStatus = sinon.stub(); - - algorithm.getCapacityAndErrorRate - .withArgs({ - allAnswers: [], - challenges: allChallenges, - initialCapacity, - doubleMeasuresUntil, - }) - .returns({ - capacity: initialCapacity, - }); - - algorithm.getPossibleNextChallenges - .withArgs({ - assessmentAnswers: [], - challenges: allChallenges, - initialCapacity, - answersForComputingCapacity: [], - }) - .returns([challenge2, challenge1]); - - pickChallenge.withArgs({ possibleChallenges: [challenge2, challenge1] }).returns(challenge2); - pickAnswerStatus.withArgs({ nextChallenge: challenge2, answerIndex: 0 }).returns(undefined); - - // when - const strategy = new AssessmentSimulatorDoubleMeasureStrategy({ - algorithm, - challenges: allChallenges, - pickChallenge, - pickAnswerStatus, - initialCapacity, - doubleMeasuresUntil, - }); - const result = strategy.run({ challengesAnswers: [], stepIndex: 0 }); - - // then - expect(result).to.be.null; - }); - }); - - context('when there are two available answers', function () { - it('should return the result for both challenges', function () { - // given - const stepIndex = 0; - const challenge1 = domainBuilder.buildChallenge({ id: 'rec1', difficulty: 1, discriminant: 0.5 }); - const challenge2 = domainBuilder.buildChallenge({ id: 'rec2', difficulty: 2, discriminant: 1.5 }); - const challenge1Reward = 2; - const challenge2Reward = 3; - const allChallenges = [challenge1, challenge2]; - const answerStatusForSimulator1 = AnswerStatus.OK; - const answerStatusForSimulator2 = AnswerStatus.OK; - const newAnswer1 = new Answer({ challengeId: challenge1.id, result: answerStatusForSimulator1 }); - const newAnswer2 = new Answer({ challengeId: challenge2.id, result: answerStatusForSimulator2 }); - const capacityBeforeAnswering = -0.5; - const expectedCapacity = 0.4; - const initialCapacity = 0; - const algorithm = { - getPossibleNextChallenges: sinon.stub(), - getCapacityAndErrorRate: sinon.stub(), - getReward: sinon.stub(), - }; - const pickChallenge = sinon.stub(); - const pickAnswerStatus = sinon.stub(); - - algorithm.getPossibleNextChallenges - .withArgs({ - assessmentAnswers: [], - challenges: allChallenges, - initialCapacity, - answersForComputingCapacity: [], - }) - .returns([challenge2, challenge1]) - .withArgs({ - assessmentAnswers: [newAnswer2], - challenges: allChallenges, - initialCapacity, - answersForComputingCapacity: [], - }) - .returns([challenge1]); - - algorithm.getCapacityAndErrorRate - .withArgs({ - allAnswers: [], - challenges: allChallenges, - initialCapacity, - doubleMeasuresUntil: 2, - }) - .returns({ - capacity: capacityBeforeAnswering, - }) - .withArgs({ - allAnswers: [sinon.match(newAnswer2), sinon.match(newAnswer1)], - challenges: allChallenges, - initialCapacity, - doubleMeasuresUntil: 2, - }) - .returns({ - capacity: expectedCapacity, - }); - - algorithm.getReward - .withArgs({ - capacity: capacityBeforeAnswering, - difficulty: challenge1.difficulty, - discriminant: challenge1.discriminant, - }) - .returns(challenge1Reward) - .withArgs({ - capacity: capacityBeforeAnswering, - difficulty: challenge2.difficulty, - discriminant: challenge2.discriminant, - }) - .returns(challenge2Reward); - - pickChallenge - .withArgs({ possibleChallenges: [challenge2, challenge1] }) - .returns(challenge2) - .withArgs({ possibleChallenges: [challenge1] }) - .returns(challenge1); - - pickAnswerStatus - .withArgs({ nextChallenge: challenge2, answerIndex: 0 }) - .returns(answerStatusForSimulator1) - .withArgs({ nextChallenge: challenge1, answerIndex: 1 }) - .returns(answerStatusForSimulator2); - - // when - const expectedResult = { - results: [ - { - challenge: challenge2, - capacity: expectedCapacity, - answerStatus: answerStatusForSimulator2.status, - reward: challenge2Reward, - }, - { - challenge: challenge1, - capacity: expectedCapacity, - answerStatus: answerStatusForSimulator1.status, - reward: challenge1Reward, - }, - ], - challengeAnswers: [ - new Answer({ - result: answerStatusForSimulator1, - challengeId: challenge2.id, - }), - new Answer({ - result: answerStatusForSimulator2, - challengeId: challenge1.id, - }), - ], - nextStepIndex: stepIndex + 2, - }; - - const strategy = new AssessmentSimulatorDoubleMeasureStrategy({ - algorithm, - challenges: allChallenges, - pickChallenge, - pickAnswerStatus, - initialCapacity, - doubleMeasuresUntil: 2, - }); - - const result = strategy.run({ challengesAnswers: [], stepIndex }); - - // then - expect(result).to.deep.equal(expectedResult); - }); - }); - }); -}); diff --git a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js b/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js index d5a3beb2277..47a10059bae 100644 --- a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js +++ b/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js @@ -11,13 +11,10 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { const expectedEstimatedLevels = [0.4, 0.2]; const expectedErrorRates = [0.2, 0.1]; const expectedRewards = [5, 3]; - const strategy1 = { - run: sinon.stub(), - }; - - const strategy2 = { + const strategy = { run: sinon.stub(), }; + const getStrategy = () => strategy; const firstRunAnswer = new Answer({ result: answersForSimulator[0], @@ -28,7 +25,7 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { challengeId: secondChallenge.id, }); - strategy1.run + strategy.run .withArgs({ challengesAnswers: [], stepIndex: 0, @@ -45,8 +42,7 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { ], challengeAnswers: [firstRunAnswer], nextStepIndex: 1, - }); - strategy2.run + }) .withArgs({ challengesAnswers: [firstRunAnswer], stepIndex: 1, @@ -65,8 +61,6 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { nextStepIndex: 2, }); - const getStrategy = (stepIndex) => (stepIndex === 0 ? strategy1 : strategy2); - // when const expectedResult = [ { diff --git a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js index 835a4efbd31..ad249529e9f 100644 --- a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js +++ b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js @@ -186,33 +186,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { }); }); }); - - context('when doing a double measure', function () { - it('should return an array of estimated level, challenge, reward and error rate for each answer', async function () { - // given - const { challengeRepository, pickChallenge, pickAnswerStatus, flashAlgorithmService } = prepareStubs({ - doubleMeasuresUntil: 2, - }); - - // when - const result = await simulateFlashAssessmentScenario({ - stopAtChallenge: 3, - challengeRepository, - locale, - pickChallenge, - pickAnswerStatus, - flashAlgorithmService, - doubleMeasuresUntil: 2, - }); - - // then - expect(result).to.have.lengthOf(3); - result.forEach((answer) => { - expect(answer.challenge).not.to.be.undefined; - expect(answer.capacity).not.to.be.undefined; - }); - }); - }); }); context('when there are not enough flash challenges left', function () { From 7c577ed1c17d0800e328393e6da2333c71936578 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Tue, 19 Nov 2024 16:23:16 +0100 Subject: [PATCH 2/6] refactor(api): force single measure strategy --- .../domain/models/AssessmentSimulator.js | 6 +++--- .../domain/usecases/simulate-flash-assessment-scenario.js | 4 +--- .../scoring/domain/services/scoring-degradation-service.js | 4 +--- .../unit/domain/models/AssessmentSimulator_test.js | 3 +-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js b/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js index 2ad8a0e350f..82af75f54e6 100644 --- a/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js +++ b/api/src/certification/flash-certification/domain/models/AssessmentSimulator.js @@ -1,8 +1,8 @@ import { logger } from '../../../../shared/infrastructure/utils/logger.js'; export class AssessmentSimulator { - constructor({ getStrategy }) { - this.getStrategy = getStrategy; + constructor({ strategy }) { + this.strategy = strategy; } run({ challengesAnswers } = { challengesAnswers: [] }) { @@ -14,7 +14,7 @@ export class AssessmentSimulator { do { hasNextAnswer = false; - const simulatorStepResult = this.getStrategy().run({ challengesAnswers, stepIndex }); + const simulatorStepResult = this.strategy.run({ challengesAnswers, stepIndex }); if (simulatorStepResult) { stepIndex = simulatorStepResult.nextStepIndex; challengesAnswers.push(...simulatorStepResult.challengeAnswers); diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index 3344535468b..3504c574e8f 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -43,10 +43,8 @@ export async function simulateFlashAssessmentScenario({ initialCapacity, }); - const getStrategy = () => singleMeasureStrategy; - const simulator = new AssessmentSimulator({ - getStrategy, + strategy: singleMeasureStrategy, }); return simulator.run(); diff --git a/api/src/certification/scoring/domain/services/scoring-degradation-service.js b/api/src/certification/scoring/domain/services/scoring-degradation-service.js index 4625303739a..dfcca93b54b 100644 --- a/api/src/certification/scoring/domain/services/scoring-degradation-service.js +++ b/api/src/certification/scoring/domain/services/scoring-degradation-service.js @@ -30,10 +30,8 @@ export const downgradeCapacity = ({ initialCapacity: capacity, }); - const getStrategy = () => singleMeasureStrategy; - const simulator = new AssessmentSimulator({ - getStrategy, + strategy: singleMeasureStrategy, }); const result = simulator.run({ challengesAnswers: allAnswers }); diff --git a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js b/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js index 47a10059bae..8c143b64d5d 100644 --- a/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js +++ b/api/tests/certification/flash-certification/unit/domain/models/AssessmentSimulator_test.js @@ -14,7 +14,6 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { const strategy = { run: sinon.stub(), }; - const getStrategy = () => strategy; const firstRunAnswer = new Answer({ result: answersForSimulator[0], @@ -80,7 +79,7 @@ describe('Unit | Domain | Models | AssessmentSimulator', function () { ]; const result = new AssessmentSimulator({ - getStrategy, + strategy, }).run(); // then From 10b3ec63bb955374037a5455b3d34e55fbb5caf5 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Tue, 19 Nov 2024 15:13:17 +0100 Subject: [PATCH 3/6] refactor(api): remove variationPercentUntil parameter --- .../application/scenario-simulator-controller.js | 2 -- .../application/scenario-simulator-route.js | 1 - .../domain/usecases/simulate-flash-assessment-scenario.js | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/api/src/certification/flash-certification/application/scenario-simulator-controller.js b/api/src/certification/flash-certification/application/scenario-simulator-controller.js index 9815a1c2176..6626f5d0f0e 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-controller.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-controller.js @@ -30,7 +30,6 @@ async function simulateFlashAssessmentScenario( limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges: minimumEstimatedSuccessRateRangesDto, variationPercent, - variationPercentUntil, capacity, } = request.payload; @@ -59,7 +58,6 @@ async function simulateFlashAssessmentScenario( limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges, variationPercent, - variationPercentUntil, }, _.isUndefined, ); diff --git a/api/src/certification/flash-certification/application/scenario-simulator-route.js b/api/src/certification/flash-certification/application/scenario-simulator-route.js index e41ca1c58df..ecf9e86ff2d 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-route.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-route.js @@ -18,7 +18,6 @@ const _baseScenarioParametersValidator = Joi.object().keys({ limitToOneQuestionPerTube: Joi.boolean(), minimumEstimatedSuccessRateRanges: Joi.array().items(_successRatesConfigurationValidator), variationPercent: Joi.number().min(0).max(1), - variationPercentUntil: Joi.number().min(0), }); const register = async (server) => { diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index 3504c574e8f..16989e0cb0a 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -13,7 +13,6 @@ export async function simulateFlashAssessmentScenario({ limitToOneQuestionPerTube = true, minimumEstimatedSuccessRateRanges = [], variationPercent, - variationPercentUntil, challengeRepository, flashAlgorithmService, }) { @@ -28,7 +27,7 @@ export async function simulateFlashAssessmentScenario({ minimumEstimatedSuccessRateRanges, enablePassageByAllCompetences: enablePassageByAllCompetencesValueInProduction, variationPercent, - variationPercentUntil, + variationPercentUntil: undefined, doubleMeasuresUntil: 0, challengesBetweenSameCompetence, maximumAssessmentLength: stopAtChallenge, From fcd815086356f8c46b76ae92d1b26d7a55745915 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Wed, 20 Nov 2024 09:11:16 +0100 Subject: [PATCH 4/6] refactor(api): use current flash algo config in simulators --- .../simulate-flash-assessment-scenario.js | 5 +- ...simulate-flash-assessment-scenario_test.js | 48 ++++++++++++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index 16989e0cb0a..f8e965821ed 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -15,17 +15,18 @@ export async function simulateFlashAssessmentScenario({ variationPercent, challengeRepository, flashAlgorithmService, + sharedFlashAlgorithmConfigurationRepository, }) { const challenges = await challengeRepository.findActiveFlashCompatible({ locale }); - const enablePassageByAllCompetencesValueInProduction = true; + const configurationUsedInProduction = await sharedFlashAlgorithmConfigurationRepository.getMostRecent(); const flashAssessmentAlgorithm = new FlashAssessmentAlgorithm({ flashAlgorithmImplementation: flashAlgorithmService, configuration: new FlashAssessmentAlgorithmConfiguration({ limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges, - enablePassageByAllCompetences: enablePassageByAllCompetencesValueInProduction, + enablePassageByAllCompetences: configurationUsedInProduction.enablePassageByAllCompetences, variationPercent, variationPercentUntil: undefined, doubleMeasuresUntil: 0, diff --git a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js index ad249529e9f..ef5da78b2b1 100644 --- a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js +++ b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js @@ -16,7 +16,13 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { context('when no initial capacity is provided', function () { it('should return an array of capacity, challenge, reward and error rate for each answer', async function () { // given - const { challengeRepository, pickChallenge, pickAnswerStatus, flashAlgorithmService } = prepareStubs(); + const { + challengeRepository, + sharedFlashAlgorithmConfigurationRepository, + pickChallenge, + pickAnswerStatus, + flashAlgorithmService, + } = prepareStubs(); // when const result = await simulateFlashAssessmentScenario({ @@ -26,6 +32,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { pickChallenge, pickAnswerStatus, flashAlgorithmService, + sharedFlashAlgorithmConfigurationRepository, }); // then @@ -45,10 +52,16 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // given const initialCapacity = 7; - const { challengeRepository, firstChallenge, pickChallenge, pickAnswerStatus, flashAlgorithmService } = - prepareStubs({ - initialCapacity, - }); + const { + challengeRepository, + sharedFlashAlgorithmConfigurationRepository, + firstChallenge, + pickChallenge, + pickAnswerStatus, + flashAlgorithmService, + } = prepareStubs({ + initialCapacity, + }); flashAlgorithmService.getReward .withArgs({ @@ -62,6 +75,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { const result = await simulateFlashAssessmentScenario({ stopAtChallenge: 3, challengeRepository, + sharedFlashAlgorithmConfigurationRepository, locale, pickChallenge, pickAnswerStatus, @@ -89,6 +103,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { const { challengeRepository, + sharedFlashAlgorithmConfigurationRepository, pickChallenge, pickAnswerStatus, flashAlgorithmService: baseFlashAlgorithmService, @@ -128,6 +143,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { const result = await simulateFlashAssessmentScenario({ stopAtChallenge: 3, challengeRepository, + sharedFlashAlgorithmConfigurationRepository, locale, pickChallenge, pickAnswerStatus, @@ -159,7 +175,13 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { }), ]; - const { challengeRepository, pickChallenge, pickAnswerStatus, flashAlgorithmService } = prepareStubs({ + const { + challengeRepository, + sharedFlashAlgorithmConfigurationRepository, + pickChallenge, + pickAnswerStatus, + flashAlgorithmService, + } = prepareStubs({ minimalSuccessRate: 0.8, }); @@ -167,6 +189,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { const result = await simulateFlashAssessmentScenario({ stopAtChallenge: 3, challengeRepository, + sharedFlashAlgorithmConfigurationRepository, locale, pickChallenge, pickAnswerStatus, @@ -196,6 +219,10 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { const challengeRepository = { findActiveFlashCompatible: sinon.stub(), }; + const sharedFlashAlgorithmConfigurationRepository = { + getMostRecent: sinon.stub(), + }; + sharedFlashAlgorithmConfigurationRepository.getMostRecent.resolves({ enablePassageByAllCompetences: true }); challengeRepository.findActiveFlashCompatible.resolves([challenge]); const pickChallenge = sinon.stub(); @@ -238,6 +265,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // when const error = await catchErr(simulateFlashAssessmentScenario)({ challengeRepository, + sharedFlashAlgorithmConfigurationRepository, locale, pickChallenge, pickAnswerStatus, @@ -287,6 +315,11 @@ function prepareStubs({ const challengeRepository = { findActiveFlashCompatible: sinon.stub(), }; + + const sharedFlashAlgorithmConfigurationRepository = { + getMostRecent: sinon.stub(), + }; + const pickChallenge = sinon.stub(); const pickAnswerStatus = sinon.stub(); const flashAlgorithmService = { @@ -304,6 +337,8 @@ function prepareStubs({ ), ); + sharedFlashAlgorithmConfigurationRepository.getMostRecent.resolves({ enablePassageByAllCompetences: true }); + flashAlgorithmService.getCapacityAndErrorRate .withArgs({ allAnswers: [], @@ -418,6 +453,7 @@ function prepareStubs({ pickChallenge, pickAnswerStatus, challengeRepository, + sharedFlashAlgorithmConfigurationRepository, flashAlgorithmService, firstChallenge, allChallenges, From 99e2c1a68156c0bfc21d7b07308be4141ebff4fb Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Wed, 20 Nov 2024 09:20:19 +0100 Subject: [PATCH 5/6] refactor(api): remove stopAtChallenge parameter --- .../scenario-simulator-controller.js | 2 -- .../application/scenario-simulator-route.js | 1 - .../simulate-flash-assessment-scenario.js | 3 +-- .../scenario-simulator-controller_test.js | 18 ++++++------------ .../simulate-flash-assessment-scenario_test.js | 9 ++++----- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/api/src/certification/flash-certification/application/scenario-simulator-controller.js b/api/src/certification/flash-certification/application/scenario-simulator-controller.js index 6626f5d0f0e..ff7d4c3867a 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-controller.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-controller.js @@ -22,7 +22,6 @@ async function simulateFlashAssessmentScenario( }, ) { const { - stopAtChallenge, initialCapacity, numberOfIterations = 1, challengePickProbability, @@ -52,7 +51,6 @@ async function simulateFlashAssessmentScenario( pickAnswerStatus, pickChallenge, locale, - stopAtChallenge, initialCapacity, challengesBetweenSameCompetence, limitToOneQuestionPerTube, diff --git a/api/src/certification/flash-certification/application/scenario-simulator-route.js b/api/src/certification/flash-certification/application/scenario-simulator-route.js index ecf9e86ff2d..0a306262a53 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-route.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-route.js @@ -11,7 +11,6 @@ const _successRatesConfigurationValidator = Joi.object({ const _baseScenarioParametersValidator = Joi.object().keys({ initialCapacity: Joi.number().integer().min(-8).max(8), - stopAtChallenge: Joi.number().integer().min(0), numberOfIterations: Joi.number().integer().min(0), challengePickProbability: Joi.number().min(0).max(100), challengesBetweenSameCompetence: Joi.number().min(0), diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index f8e965821ed..d113dfb9116 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -7,7 +7,6 @@ export async function simulateFlashAssessmentScenario({ locale, pickChallenge, pickAnswerStatus, - stopAtChallenge, initialCapacity, challengesBetweenSameCompetence = 0, limitToOneQuestionPerTube = true, @@ -31,7 +30,7 @@ export async function simulateFlashAssessmentScenario({ variationPercentUntil: undefined, doubleMeasuresUntil: 0, challengesBetweenSameCompetence, - maximumAssessmentLength: stopAtChallenge, + maximumAssessmentLength: configurationUsedInProduction.maximumAssessmentLength, }), }); diff --git a/api/tests/acceptance/application/scenario-simulator/scenario-simulator-controller_test.js b/api/tests/acceptance/application/scenario-simulator/scenario-simulator-controller_test.js index 53db3317e61..b4815805084 100644 --- a/api/tests/acceptance/application/scenario-simulator/scenario-simulator-controller_test.js +++ b/api/tests/acceptance/application/scenario-simulator/scenario-simulator-controller_test.js @@ -15,25 +15,23 @@ const { describe('Acceptance | Controller | scenario-simulator-controller', function () { let server; let adminAuthorization; - let validCapacityPayload; - let stopAtChallenge; + let validPayload; beforeEach(async function () { const { id: adminId } = databaseBuilder.factory.buildUser.withRole({ role: SUPER_ADMIN, }); - stopAtChallenge = databaseBuilder.factory.buildFlashAlgorithmConfiguration({ + databaseBuilder.factory.buildFlashAlgorithmConfiguration({ maximumAssessmentLength: 2, createdAt: new Date('2022-02-01'), - }).maximumAssessmentLength; + }); adminAuthorization = generateValidRequestAuthorizationHeader(adminId); await databaseBuilder.commit(); - validCapacityPayload = { + validPayload = { capacity: 4.5, - stopAtChallenge, }; const learningContent = { @@ -151,10 +149,6 @@ describe('Acceptance | Controller | scenario-simulator-controller', function () describe('when a number of challenges to pass is specified', function () { it('should return a payload with the same number of simulation scenario results', async function () { // given - const validPayload = { - ...validCapacityPayload, - stopAtChallenge, - }; options.headers.authorization = adminAuthorization; options.payload = validPayload; @@ -179,7 +173,7 @@ describe('Acceptance | Controller | scenario-simulator-controller', function () it('should return a payload with simulation the capacity scenario results', async function () { // given options.headers.authorization = adminAuthorization; - options.payload = validCapacityPayload; + options.payload = validPayload; // when const response = await server.inject(options); @@ -209,7 +203,7 @@ describe('Acceptance | Controller | scenario-simulator-controller', function () const { id: userId } = databaseBuilder.factory.buildUser(); options.headers.authorization = generateValidRequestAuthorizationHeader(userId); await databaseBuilder.commit(); - options.payload = validCapacityPayload; + options.payload = validPayload; // when const response = await server.inject(options); diff --git a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js index ef5da78b2b1..b1122875f79 100644 --- a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js +++ b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js @@ -26,7 +26,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // when const result = await simulateFlashAssessmentScenario({ - stopAtChallenge: 3, challengeRepository, locale, pickChallenge, @@ -73,7 +72,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // when const result = await simulateFlashAssessmentScenario({ - stopAtChallenge: 3, challengeRepository, sharedFlashAlgorithmConfigurationRepository, locale, @@ -141,7 +139,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // when const result = await simulateFlashAssessmentScenario({ - stopAtChallenge: 3, challengeRepository, sharedFlashAlgorithmConfigurationRepository, locale, @@ -187,7 +184,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { // when const result = await simulateFlashAssessmentScenario({ - stopAtChallenge: 3, challengeRepository, sharedFlashAlgorithmConfigurationRepository, locale, @@ -337,7 +333,10 @@ function prepareStubs({ ), ); - sharedFlashAlgorithmConfigurationRepository.getMostRecent.resolves({ enablePassageByAllCompetences: true }); + sharedFlashAlgorithmConfigurationRepository.getMostRecent.resolves({ + enablePassageByAllCompetences: true, + maximumAssessmentLength: 3, + }); flashAlgorithmService.getCapacityAndErrorRate .withArgs({ From 639fde042b6c21718d9a593c4abaf65c7b5db215 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Wed, 20 Nov 2024 10:55:31 +0100 Subject: [PATCH 6/6] refactor(api): remove limitToOneQuestionPerTube parameter --- .../scenario-simulator-controller.js | 2 - .../application/scenario-simulator-route.js | 1 - .../simulate-flash-assessment-scenario.js | 3 +- .../scenario-simulator-controller_test.js | 59 ------------------- ...simulate-flash-assessment-scenario_test.js | 13 +--- 5 files changed, 3 insertions(+), 75 deletions(-) diff --git a/api/src/certification/flash-certification/application/scenario-simulator-controller.js b/api/src/certification/flash-certification/application/scenario-simulator-controller.js index ff7d4c3867a..48db1af75b3 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-controller.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-controller.js @@ -26,7 +26,6 @@ async function simulateFlashAssessmentScenario( numberOfIterations = 1, challengePickProbability, challengesBetweenSameCompetence, - limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges: minimumEstimatedSuccessRateRangesDto, variationPercent, capacity, @@ -53,7 +52,6 @@ async function simulateFlashAssessmentScenario( locale, initialCapacity, challengesBetweenSameCompetence, - limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges, variationPercent, }, diff --git a/api/src/certification/flash-certification/application/scenario-simulator-route.js b/api/src/certification/flash-certification/application/scenario-simulator-route.js index 0a306262a53..5bcb1d6674c 100644 --- a/api/src/certification/flash-certification/application/scenario-simulator-route.js +++ b/api/src/certification/flash-certification/application/scenario-simulator-route.js @@ -14,7 +14,6 @@ const _baseScenarioParametersValidator = Joi.object().keys({ numberOfIterations: Joi.number().integer().min(0), challengePickProbability: Joi.number().min(0).max(100), challengesBetweenSameCompetence: Joi.number().min(0), - limitToOneQuestionPerTube: Joi.boolean(), minimumEstimatedSuccessRateRanges: Joi.array().items(_successRatesConfigurationValidator), variationPercent: Joi.number().min(0).max(1), }); diff --git a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js index d113dfb9116..a187a8ddb4d 100644 --- a/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js +++ b/api/src/certification/flash-certification/domain/usecases/simulate-flash-assessment-scenario.js @@ -9,7 +9,6 @@ export async function simulateFlashAssessmentScenario({ pickAnswerStatus, initialCapacity, challengesBetweenSameCompetence = 0, - limitToOneQuestionPerTube = true, minimumEstimatedSuccessRateRanges = [], variationPercent, challengeRepository, @@ -23,7 +22,7 @@ export async function simulateFlashAssessmentScenario({ const flashAssessmentAlgorithm = new FlashAssessmentAlgorithm({ flashAlgorithmImplementation: flashAlgorithmService, configuration: new FlashAssessmentAlgorithmConfiguration({ - limitToOneQuestionPerTube, + limitToOneQuestionPerTube: configurationUsedInProduction.limitToOneQuestionPerTube, minimumEstimatedSuccessRateRanges, enablePassageByAllCompetences: configurationUsedInProduction.enablePassageByAllCompetences, variationPercent, diff --git a/api/tests/certification/flash-certification/integration/application/scenario-simulator-controller_test.js b/api/tests/certification/flash-certification/integration/application/scenario-simulator-controller_test.js index 9d8c45c63dc..943550d1190 100644 --- a/api/tests/certification/flash-certification/integration/application/scenario-simulator-controller_test.js +++ b/api/tests/certification/flash-certification/integration/application/scenario-simulator-controller_test.js @@ -175,65 +175,6 @@ describe('Integration | Application | scenario-simulator-controller', function ( }); }); - context('When configuring the limit of challenges per tube', function () { - it('should call simulateFlashAssessmentScenario usecase with correct arguments', async function () { - // given - const limitToOneQuestionPerTube = true; - - const pickChallengeImplementation = sinon.stub(); - pickChallengeService.chooseNextChallenge.withArgs().returns(pickChallengeImplementation); - const pickAnswerStatusForCapacityImplementation = sinon.stub(); - pickAnswerStatusService.pickAnswerStatusForCapacity - .withArgs(6) - .returns(pickAnswerStatusForCapacityImplementation); - - usecases.simulateFlashAssessmentScenario - .withArgs({ - pickAnswerStatus: pickAnswerStatusForCapacityImplementation, - locale: 'en', - pickChallenge: pickChallengeImplementation, - initialCapacity, - limitToOneQuestionPerTube, - }) - .resolves(simulationResults); - securityPreHandlers.checkAdminMemberHasRoleSuperAdmin.returns(() => true); - - // when - const response = await httpTestServer.request( - 'POST', - '/api/scenario-simulator', - { - initialCapacity, - capacity: 6, - limitToOneQuestionPerTube, - }, - null, - { 'accept-language': 'en' }, - ); - - // then - expect(response.statusCode).to.equal(200); - const parsedResult = parseJsonStream(response); - expect(parsedResult).to.deep.equal([ - { - index: 0, - simulationReport: [ - { - challengeId: challenge1.id, - errorRate: errorRate1, - capacity: capacity1, - minimumCapability: 0.6190392084062237, - answerStatus: 'ok', - reward: reward1, - difficulty: challenge1.difficulty, - discriminant: challenge1.discriminant, - }, - ], - }, - ]); - }); - }); - context('When configuring the minimum success rates', function () { context('When providing valid parameters', function () { it('should call simulateFlashAssessmentScenario usecase with correct arguments', async function () { diff --git a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js index b1122875f79..34572de9219 100644 --- a/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js +++ b/api/tests/unit/domain/usecases/simulate-flash-assessment-scenario_test.js @@ -79,7 +79,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { pickAnswerStatus, initialCapacity, flashAlgorithmService, - limitToOneQuestionPerTube: false, }); // then @@ -97,8 +96,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { context('when we don‘t limit the number of challenges per tube', function () { it('should return an array of estimated level, challenge, reward and error rate for each answer', async function () { // given - const limitToOneQuestionPerTube = false; - const { challengeRepository, sharedFlashAlgorithmConfigurationRepository, @@ -107,9 +104,7 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { flashAlgorithmService: baseFlashAlgorithmService, getNextChallengesOptionsMatcher, allChallenges, - } = prepareStubs({ - limitToOneQuestionPerTube, - }); + } = prepareStubs(); const flashAlgorithmService = { ...baseFlashAlgorithmService, @@ -144,7 +139,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { locale, pickChallenge, pickAnswerStatus, - limitToOneQuestionPerTube, flashAlgorithmService, }); @@ -163,7 +157,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { context('when we set a minimum estimated success rate range', function () { it('should return an array of estimated level, challenge, reward and error rate for each answer', async function () { // given - const limitToOneQuestionPerTube = false; const minimumEstimatedSuccessRateRanges = [ domainBuilder.buildFlashAssessmentAlgorithmSuccessRateHandlerFixed({ startingChallengeIndex: 0, @@ -191,7 +184,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { pickAnswerStatus, minimumEstimatedSuccessRateRanges, flashAlgorithmService, - limitToOneQuestionPerTube, }); // then @@ -210,7 +202,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { context('when there are not enough flash challenges left', function () { it('should stop simulating', async function () { // given - const limitToOneQuestionPerTube = false; const challenge = domainBuilder.buildChallenge({ id: 1 }); const challengeRepository = { findActiveFlashCompatible: sinon.stub(), @@ -266,7 +257,6 @@ describe('Unit | UseCase | simulate-flash-assessment-scenario', function () { pickChallenge, pickAnswerStatus, flashAlgorithmService, - limitToOneQuestionPerTube, }); // then @@ -336,6 +326,7 @@ function prepareStubs({ sharedFlashAlgorithmConfigurationRepository.getMostRecent.resolves({ enablePassageByAllCompetences: true, maximumAssessmentLength: 3, + limitToOneQuestionPerTube: false, }); flashAlgorithmService.getCapacityAndErrorRate