diff --git a/admin/app/components/administration/certification/flash-algorithm-configuration/form.gjs b/admin/app/components/administration/certification/flash-algorithm-configuration/form.gjs index 46b41586062..e867cef7e0d 100644 --- a/admin/app/components/administration/certification/flash-algorithm-configuration/form.gjs +++ b/admin/app/components/administration/certification/flash-algorithm-configuration/form.gjs @@ -18,10 +18,6 @@ import { t } from 'ember-intl'; }} - - <:label>{{t "pages.administration.certification.flash-algorithm-configuration.form.warmUpLength"}} - - {{t "pages.administration.certification.flash-algorithm-configuration.form.variationPercent"}} - - <:label>{{t - "pages.administration.certification.flash-algorithm-configuration.form.variationPercentUntil" - }} - - - - <:label>{{t "pages.administration.certification.flash-algorithm-configuration.form.doubleMeasuresUntil"}} - - { test('should call the post url with the correct payload', async function (assert) { // given - const flashAlgorithmConfiguration = { warmUpLength: 2 }; + const flashAlgorithmConfiguration = { enablePassageByAllCompetences: true }; const payload = { data: flashAlgorithmConfiguration }; const adapter = this.owner.lookup('adapter:flash-algorithm-configuration'); sinon.stub(adapter, 'ajax'); diff --git a/admin/tests/unit/components/administration/certification/flash-algorithm-configuration-test.js b/admin/tests/unit/components/administration/certification/flash-algorithm-configuration-test.js index f685881217b..48af516bde9 100644 --- a/admin/tests/unit/components/administration/certification/flash-algorithm-configuration-test.js +++ b/admin/tests/unit/components/administration/certification/flash-algorithm-configuration-test.js @@ -28,11 +28,8 @@ module('Unit | Component | authenticated/certifications/flash-algorithm-configur const flashAlgorithmConfiguration = { maximumAssessmentLength: 0, - warmUpLength: 1, challengesBetweenSameCompetence: 2, variationPercent: 3, - variationPercentUntil: 4, - doubleMeasuresUntil: 5, limitToOneQuestionPerTube: true, enablePassageByAllCompetences: true, }; @@ -72,11 +69,8 @@ module('Unit | Component | authenticated/certifications/flash-algorithm-configur const flashAlgorithmConfiguration = { maximumAssessmentLength: 0, - warmUpLength: 1, challengesBetweenSameCompetence: 2, variationPercent: 3, - variationPercentUntil: 4, - doubleMeasuresUntil: 5, limitToOneQuestionPerTube: true, enablePassageByAllCompetences: true, }; diff --git a/admin/translations/en.json b/admin/translations/en.json index 263bad59065..25326f60ccc 100644 --- a/admin/translations/en.json +++ b/admin/translations/en.json @@ -402,13 +402,10 @@ "title": "Configuration de l’algorithme de déroulé du test", "form": { "challengesBetweenSameCompetence": "Nombre de questions entre 2 questions de la même compétence", - "doubleMeasuresUntil": "Nombre de questions pour la double mesure", "enablePassageByAllCompetences": "Forcer le passage par les 16 compétences", "limitToOneQuestionPerTube": "Limiter à une question par sujet", "maximumAssessmentLength": "Nombre de questions", - "variationPercent": "Capage de la capacité (en % )", - "variationPercentUntil": "Nombre de questions pour le capage de la capacité", - "warmUpLength": "Nombre de questions d'entraînement" + "variationPercent": "Capage de la capacité (en % )" } }, "sco-whitelist": { diff --git a/admin/translations/fr.json b/admin/translations/fr.json index ef2ce2dcb78..11ee667d15b 100644 --- a/admin/translations/fr.json +++ b/admin/translations/fr.json @@ -412,13 +412,10 @@ "title": "Configuration de l’algorithme de déroulé du test", "form": { "challengesBetweenSameCompetence": "Nombre de questions entre 2 questions de la même compétence", - "doubleMeasuresUntil": "Nombre de questions pour la double mesure", "enablePassageByAllCompetences": "Forcer le passage par les 16 compétences", "limitToOneQuestionPerTube": "Limiter à une question par sujet", "maximumAssessmentLength": "Nombre de questions", - "variationPercent": "Capage de la capacité (en % )", - "variationPercentUntil": "Nombre de questions pour le capage de la capacité", - "warmUpLength": "Nombre de questions d'entraînement" + "variationPercent": "Capage de la capacité (en % )" } }, "sco-whitelist": { diff --git a/api/db/database-builder/factory/build-flash-algorithm-configuration.js b/api/db/database-builder/factory/build-flash-algorithm-configuration.js index 8e0bcb009b6..ecf31f3e4df 100644 --- a/api/db/database-builder/factory/build-flash-algorithm-configuration.js +++ b/api/db/database-builder/factory/build-flash-algorithm-configuration.js @@ -2,29 +2,19 @@ import { config } from '../../../src/shared/config.js'; import { databaseBuffer } from '../database-buffer.js'; const buildFlashAlgorithmConfiguration = function ({ - warmUpLength = null, - forcedCompetences = [], maximumAssessmentLength = config.v3Certification.numberOfChallengesPerCourse, challengesBetweenSameCompetence = null, - minimumEstimatedSuccessRateRanges = [], limitToOneQuestionPerTube = null, enablePassageByAllCompetences = false, - doubleMeasuresUntil = null, variationPercent = null, - variationPercentUntil = null, createdAt = new Date(), } = {}) { const values = { - warmUpLength, maximumAssessmentLength, challengesBetweenSameCompetence, - forcedCompetences: JSON.stringify(forcedCompetences), - minimumEstimatedSuccessRateRanges: JSON.stringify(minimumEstimatedSuccessRateRanges), limitToOneQuestionPerTube, enablePassageByAllCompetences, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, }; diff --git a/api/db/migrations/20241128152506_remove-unused-columns-in-flash-algorithm-configurations-table.js b/api/db/migrations/20241128152506_remove-unused-columns-in-flash-algorithm-configurations-table.js new file mode 100644 index 00000000000..2b8dc8c8869 --- /dev/null +++ b/api/db/migrations/20241128152506_remove-unused-columns-in-flash-algorithm-configurations-table.js @@ -0,0 +1,29 @@ +const TABLE_NAME = 'flash-algorithm-configurations'; +const WARM_UP_LENGTH_COLUMN_NAME = 'warmUpLength'; +const FORCED_COMPETENCES_COLUMN_NAME = 'forcedCompetences'; +const MINIMUM_ESTIMATED_SUCCESS_RATE_RANGES_COLUMN_NAME = 'minimumEstimatedSuccessRateRanges'; +const DOUBLE_MEASURES_UNTIL_COLUMN_NAME = 'doubleMeasuresUntil'; +const VARIATION_PERCENT_UNTIL_COLUMN_NAME = 'variationPercentUntil'; + +const up = function (knex) { + return knex.schema.table(TABLE_NAME, (table) => { + table.dropColumn(WARM_UP_LENGTH_COLUMN_NAME); + table.dropColumn(FORCED_COMPETENCES_COLUMN_NAME); + table.dropColumn(MINIMUM_ESTIMATED_SUCCESS_RATE_RANGES_COLUMN_NAME); + table.dropColumn(DOUBLE_MEASURES_UNTIL_COLUMN_NAME); + table.dropColumn(VARIATION_PERCENT_UNTIL_COLUMN_NAME); + table.comment(`Configuration parameters used to alter the behaviour of the flash algorithm.`); + }); +}; + +const down = function (knex) { + return knex.schema.table(TABLE_NAME, (table) => { + table.integer(WARM_UP_LENGTH_COLUMN_NAME); + table.jsonb(FORCED_COMPETENCES_COLUMN_NAME); + table.jsonb(MINIMUM_ESTIMATED_SUCCESS_RATE_RANGES_COLUMN_NAME); + table.integer(DOUBLE_MEASURES_UNTIL_COLUMN_NAME); + table.integer(VARIATION_PERCENT_UNTIL_COLUMN_NAME); + }); +}; + +export { down, up }; diff --git a/api/db/seeds/data/team-certification/data-builder.js b/api/db/seeds/data/team-certification/data-builder.js index ac53c0dc25e..761689cbf83 100644 --- a/api/db/seeds/data/team-certification/data-builder.js +++ b/api/db/seeds/data/team-certification/data-builder.js @@ -118,16 +118,11 @@ async function _createScoCertificationCenter({ databaseBuilder }) { function _createV3CertificationConfiguration({ databaseBuilder }) { databaseBuilder.factory.buildFlashAlgorithmConfiguration({ - warmUpLength: null, - forcedCompetences: [], maximumAssessmentLength: 32, challengesBetweenSameCompetence: null, - minimumEstimatedSuccessRateRanges: [], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: true, - doubleMeasuresUntil: null, variationPercent: 0.5, - variationPercentUntil: null, createdAt: new Date('1977-10-19'), }); } diff --git a/api/lib/domain/usecases/get-next-challenge-for-campaign-assessment.js b/api/lib/domain/usecases/get-next-challenge-for-campaign-assessment.js index d38106bedf1..60be75ede08 100644 --- a/api/lib/domain/usecases/get-next-challenge-for-campaign-assessment.js +++ b/api/lib/domain/usecases/get-next-challenge-for-campaign-assessment.js @@ -65,10 +65,7 @@ const _hasAnsweredToAllChallenges = ({ possibleChallenges }) => { const _createDefaultAlgorithmConfiguration = () => { return new FlashAssessmentAlgorithmConfiguration({ - warmUpLength: 0, - forcedCompetences: [], limitToOneQuestionPerTube: false, - minimumEstimatedSuccessRateRanges: [], enablePassageByAllCompetences: false, }); }; diff --git a/api/scripts/certification/next-gen/generate-flash-algorithm-configuration.js b/api/scripts/certification/next-gen/generate-flash-algorithm-configuration.js deleted file mode 100644 index 727662ee2e1..00000000000 --- a/api/scripts/certification/next-gen/generate-flash-algorithm-configuration.js +++ /dev/null @@ -1,48 +0,0 @@ -import * as url from 'node:url'; - -import { disconnect } from '../../../db/knex-database-connection.js'; -import * as flashAlgorithmConfigurationRepository from '../../../src/certification/flash-certification/infrastructure/repositories/flash-algorithm-configuration-repository.js'; -import { FlashAssessmentAlgorithmConfiguration } from '../../../src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js'; -import { logger } from '../../../src/shared/infrastructure/utils/logger.js'; - -const modulePath = url.fileURLToPath(import.meta.url); -const isLaunchedFromCommandLine = process.argv[1] === modulePath; -const __filename = modulePath; - -async function main() { - logger.info(`Script ${__filename} est lancé !`); - - const configurationData = { - warmUpLength: null, - forcedCompetences: [], - maximumAssessmentLength: 32, - challengesBetweenSameCompetence: null, - minimumEstimatedSuccessRateRanges: [], - limitToOneQuestionPerTube: true, - enablePassageByAllCompetences: true, - doubleMeasuresUntil: null, - variationPercent: 0.5, - variationPercentUntil: null, - }; - - const configuration = new FlashAssessmentAlgorithmConfiguration(configurationData); - - await flashAlgorithmConfigurationRepository.save(configuration); - - logger.info('La configuration a été créée.'); -} - -(async () => { - if (isLaunchedFromCommandLine) { - try { - await main(); - } catch (error) { - console.error(error); - process.exitCode = 1; - } finally { - await disconnect(); - } - } -})(); - -export { main }; diff --git a/api/src/certification/flash-certification/application/flash-assessment-configuration-route.js b/api/src/certification/flash-certification/application/flash-assessment-configuration-route.js index 5f6a70bf3df..daf174f9b67 100644 --- a/api/src/certification/flash-certification/application/flash-assessment-configuration-route.js +++ b/api/src/certification/flash-certification/application/flash-assessment-configuration-route.js @@ -35,15 +35,11 @@ const register = async (server) => { ], validate: { payload: Joi.object({ - warmUpLength: Joi.number().integer().min(0).allow(null).optional(), - forcedCompetences: Joi.array().items(Joi.string()).optional(), maximumAssessmentLength: Joi.number().integer().min(0).allow(null).optional(), challengesBetweenSameCompetence: Joi.number().integer().min(0).allow(null).optional(), limitToOneQuestionPerTube: Joi.boolean().optional(), enablePassageByAllCompetences: Joi.boolean().optional(), - doubleMeasuresUntil: Joi.number().min(0).allow(null).optional(), variationPercent: Joi.number().min(0).max(1).allow(null).optional(), - variationPercentUntil: Joi.number().min(0).allow(null).optional(), }), }, handler: flashAssessmentConfigurationController.createFlashAssessmentConfiguration, diff --git a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithm.js b/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithm.js index 1af8f7a45fd..84533ffdad9 100644 --- a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithm.js +++ b/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithm.js @@ -1,6 +1,5 @@ import { config } from '../../../../shared/config.js'; import { FlashAssessmentAlgorithmChallengesBetweenCompetencesRule } from './FlashAssessmentAlgorithmChallengesBetweenCompetencesRule.js'; -import { FlashAssessmentAlgorithmForcedCompetencesRule } from './FlashAssessmentAlgorithmForcedCompetencesRule.js'; import { FlashAssessmentAlgorithmNonAnsweredSkillsRule } from './FlashAssessmentAlgorithmNonAnsweredSkillsRule.js'; import { FlashAssessmentAlgorithmOneQuestionPerTubeRule } from './FlashAssessmentAlgorithmOneQuestionPerTubeRule.js'; import { FlashAssessmentAlgorithmPassageByAllCompetencesRule } from './FlashAssessmentAlgorithmPassageByAllCompetencesRule.js'; @@ -10,14 +9,12 @@ const availableRules = [ FlashAssessmentAlgorithmOneQuestionPerTubeRule, FlashAssessmentAlgorithmNonAnsweredSkillsRule, FlashAssessmentAlgorithmPassageByAllCompetencesRule, - FlashAssessmentAlgorithmForcedCompetencesRule, FlashAssessmentAlgorithmChallengesBetweenCompetencesRule, ]; class FlashAssessmentAlgorithm { /** * Model to interact with the flash algorithm - * @param warmUpLength - define a warmup when the algorithm do not go through the competences * @param configuration - options to configure algorithm challenge selection and behaviour */ constructor({ flashAlgorithmImplementation, configuration = {} } = {}) { @@ -27,8 +24,6 @@ class FlashAssessmentAlgorithm { this.ruleEngine = new FlashAssessmentAlgorithmRuleEngine(availableRules, { limitToOneQuestionPerTube: configuration.limitToOneQuestionPerTube, challengesBetweenSameCompetence: configuration.challengesBetweenSameCompetence, - forcedCompetences: configuration.forcedCompetences, - warmUpLength: configuration.warmUpLength, enablePassageByAllCompetences: configuration.enablePassageByAllCompetences, }); } @@ -59,15 +54,9 @@ class FlashAssessmentAlgorithm { throw new RangeError('No eligible challenges in referential'); } - const minimalSuccessRate = this.#computeMinimalSuccessRate(assessmentAnswers.length); - return this.flashAlgorithmImplementation.getPossibleNextChallenges({ availableChallenges: challengesAfterRulesApplication, capacity, - options: { - challengesBetweenSameCompetence: this._configuration.challengesBetweenSameCompetence, - minimalSuccessRate, - }, }); } @@ -86,22 +75,6 @@ class FlashAssessmentAlgorithm { }); } - #computeMinimalSuccessRate(questionIndex) { - const filterConfiguration = this.#findApplicableSuccessRateConfiguration(questionIndex); - - if (!filterConfiguration) { - return 0; - } - - return filterConfiguration.getMinimalSuccessRate(questionIndex); - } - - #findApplicableSuccessRateConfiguration(questionIndex) { - return this._configuration.minimumEstimatedSuccessRateRanges.find((successRateRange) => - successRateRange.isApplicable(questionIndex), - ); - } - getCapacityAndErrorRate({ allAnswers, challenges, @@ -112,8 +85,6 @@ class FlashAssessmentAlgorithm { challenges, capacity: initialCapacity, variationPercent: this._configuration.variationPercent, - variationPercentUntil: this._configuration.variationPercentUntil, - doubleMeasuresUntil: this._configuration.doubleMeasuresUntil, }); } @@ -127,8 +98,6 @@ class FlashAssessmentAlgorithm { challenges, capacity: initialCapacity, variationPercent: this._configuration.variationPercent, - variationPercentUntil: this._configuration.variationPercentUntil, - doubleMeasuresUntil: this._configuration.doubleMeasuresUntil, }); } diff --git a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule.js b/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule.js deleted file mode 100644 index 87746b646ec..00000000000 --- a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule.js +++ /dev/null @@ -1,44 +0,0 @@ -import lodash from 'lodash'; - -export class FlashAssessmentAlgorithmForcedCompetencesRule { - static isApplicable({ answers, forcedCompetences, warmUpLength }) { - return forcedCompetences.length > 0 && answers.length >= warmUpLength; - } - - static execute({ allChallenges, assessmentAnswers, availableChallenges, warmUpLength, forcedCompetences }) { - const answersAfterWarmup = this._getAnswersAfterWarmup({ assessmentAnswers, warmUpLength }); - - return this._filterAlreadyAnsweredCompetences({ - assessmentAnswers: answersAfterWarmup, - availableChallenges, - allChallenges, - forcedCompetences, - }); - } - - static _getAnswersAfterWarmup({ assessmentAnswers, warmUpLength }) { - return assessmentAnswers.slice(warmUpLength); - } - - static _filterAlreadyAnsweredCompetences({ - assessmentAnswers, - allChallenges, - forcedCompetences, - availableChallenges, - }) { - const answeredCompetenceIds = assessmentAnswers.map( - ({ challengeId }) => lodash.find(allChallenges, { id: challengeId }).competenceId, - ); - - const remainingCompetenceIds = forcedCompetences.filter( - (competenceId) => !answeredCompetenceIds.includes(competenceId), - ); - - const allCompetencesAreAnswered = remainingCompetenceIds.length === 0; - - if (allCompetencesAreAnswered) { - return availableChallenges; - } - return availableChallenges.filter(({ competenceId }) => remainingCompetenceIds.includes(competenceId)); - } -} diff --git a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule.js b/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule.js index 0f990cbbfd4..e1dc26705a0 100644 --- a/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule.js +++ b/api/src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule.js @@ -1,24 +1,18 @@ import lodash from 'lodash'; export class FlashAssessmentAlgorithmPassageByAllCompetencesRule { - static isApplicable({ answers, enablePassageByAllCompetences, warmUpLength }) { - return enablePassageByAllCompetences && answers.length >= warmUpLength; + static isApplicable({ enablePassageByAllCompetences }) { + return enablePassageByAllCompetences; } - static execute({ allChallenges, assessmentAnswers, availableChallenges, warmUpLength }) { - const answersAfterWarmup = this._getAnswersAfterWarmup({ assessmentAnswers, warmUpLength }); - + static execute({ allChallenges, assessmentAnswers, availableChallenges }) { return this._filterAlreadyAnsweredCompetences({ - assessmentAnswers: answersAfterWarmup, + assessmentAnswers, availableChallenges, allChallenges, }); } - static _getAnswersAfterWarmup({ assessmentAnswers, warmUpLength }) { - return assessmentAnswers.slice(warmUpLength); - } - static _filterAlreadyAnsweredCompetences({ assessmentAnswers, allChallenges, availableChallenges }) { const answeredCompetenceIds = assessmentAnswers.map( ({ challengeId }) => lodash.find(allChallenges, { id: challengeId }).competenceId, diff --git a/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js b/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js deleted file mode 100644 index d5d8ae461a4..00000000000 --- a/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js +++ /dev/null @@ -1,42 +0,0 @@ -import { FlashAssessmentSuccessRateHandlerFixedStrategy } from './FlashAssessmentSuccessRateHandlerFixedStrategy.js'; - -class FlashAssessmentSuccessRateHandler { - constructor({ startingChallengeIndex, endingChallengeIndex, strategy }) { - this.startingChallengeIndex = startingChallengeIndex; - this.endingChallengeIndex = endingChallengeIndex; - this._strategy = strategy; - } - - isApplicable(questionIndex) { - return this.startingChallengeIndex <= questionIndex && this.endingChallengeIndex >= questionIndex; - } - - getMinimalSuccessRate(questionIndex) { - return this._strategy.getMinimalSuccessRate(this.startingChallengeIndex, this.endingChallengeIndex, questionIndex); - } - - static create(successRateRange) { - const { startingChallengeIndex, endingChallengeIndex, value } = successRateRange; - return new FlashAssessmentSuccessRateHandler({ - startingChallengeIndex, - endingChallengeIndex, - strategy: new FlashAssessmentSuccessRateHandlerFixedStrategy({ - value, - }), - }); - } - - toDTO() { - return { - startingChallengeIndex: this.startingChallengeIndex, - endingChallengeIndex: this.endingChallengeIndex, - ...this._strategy.toDTO(), - }; - } - - static fromDTO(config) { - return FlashAssessmentSuccessRateHandler.create(config); - } -} - -export { FlashAssessmentSuccessRateHandler }; diff --git a/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandlerFixedStrategy.js b/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandlerFixedStrategy.js deleted file mode 100644 index 3f6f06935fa..00000000000 --- a/api/src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandlerFixedStrategy.js +++ /dev/null @@ -1,16 +0,0 @@ -export class FlashAssessmentSuccessRateHandlerFixedStrategy { - constructor({ value }) { - this.value = value; - } - - getMinimalSuccessRate() { - return this.value; - } - - toDTO() { - return { - type: 'fixed', - value: this.value, - }; - } -} diff --git a/api/src/certification/flash-certification/domain/services/algorithm-methods/flash.js b/api/src/certification/flash-certification/domain/services/algorithm-methods/flash.js index 8e01a8166f5..3417b2d19ff 100644 --- a/api/src/certification/flash-certification/domain/services/algorithm-methods/flash.js +++ b/api/src/certification/flash-certification/domain/services/algorithm-methods/flash.js @@ -24,11 +24,7 @@ export { getReward, }; -function getPossibleNextChallenges({ - availableChallenges, - capacity = DEFAULT_CAPACITY, - options: { minimalSuccessRate = 0 } = {}, -} = {}) { +function getPossibleNextChallenges({ availableChallenges, capacity = DEFAULT_CAPACITY } = {}) { const challengesWithReward = availableChallenges.map((challenge) => { return { challenge, @@ -40,17 +36,10 @@ function getPossibleNextChallenges({ }; }); - return _findBestPossibleChallenges(challengesWithReward, minimalSuccessRate, capacity); + return _findBestPossibleChallenges(challengesWithReward, capacity); } -function getCapacityAndErrorRate({ - allAnswers, - challenges, - capacity = DEFAULT_CAPACITY, - doubleMeasuresUntil = 0, - variationPercent, - variationPercentUntil, -}) { +function getCapacityAndErrorRate({ allAnswers, challenges, capacity = DEFAULT_CAPACITY, variationPercent }) { if (allAnswers.length === 0) { return { capacity, errorRate: DEFAULT_ERROR_RATE }; } @@ -59,22 +48,13 @@ function getCapacityAndErrorRate({ allAnswers, challenges, capacity, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, }); return capacityHistory.at(-1); } -function getCapacityAndErrorRateHistory({ - allAnswers, - challenges, - capacity = DEFAULT_CAPACITY, - doubleMeasuresUntil = 0, - variationPercent, - variationPercentUntil, -}) { +function getCapacityAndErrorRateHistory({ allAnswers, challenges, capacity = DEFAULT_CAPACITY, variationPercent }) { let latestCapacity = capacity; let likelihood = samples.map(() => DEFAULT_PROBABILITY_TO_ANSWER); @@ -86,37 +66,17 @@ function getCapacityAndErrorRateHistory({ while (answerIndex < allAnswers.length) { answer = allAnswers[answerIndex]; - const variationPercentForCurrentAnswer = _defineVariationPercentForCurrentAnswer( + + ({ latestCapacity, likelihood, normalizedPosteriori } = _singleMeasure({ + challenges, + answer, + latestCapacity, + likelihood, + normalizedPosteriori, variationPercent, - variationPercentUntil, - answerIndex, - ); - - if (!_shouldUseDoubleMeasure({ doubleMeasuresUntil, answerIndex, answersLength: allAnswers.length })) { - ({ latestCapacity, likelihood, normalizedPosteriori } = _singleMeasure({ - challenges, - answer, - latestCapacity, - likelihood, - normalizedPosteriori, - variationPercent: variationPercentForCurrentAnswer, - })); - - answerIndex++; - } else { - answer = allAnswers[answerIndex]; - const answer2 = allAnswers[answerIndex + 1]; - ({ latestCapacity, likelihood, normalizedPosteriori } = _doubleMeasure({ - challenges, - answers: [answer, answer2], - latestCapacity, - likelihood, - normalizedPosteriori, - variationPercent: variationPercentForCurrentAnswer, - })); - - answerIndex += 2; - } + })); + + answerIndex++; capacityHistory.push({ answerId: answer.id, @@ -128,19 +88,6 @@ function getCapacityAndErrorRateHistory({ return capacityHistory; } -function _defineVariationPercentForCurrentAnswer(variationPercent, variationPercentUntil, answerIndex) { - if (!variationPercentUntil) { - return variationPercent; - } - - return variationPercentUntil >= answerIndex ? variationPercent : undefined; -} - -function _shouldUseDoubleMeasure({ doubleMeasuresUntil, answerIndex, answersLength }) { - const isLastAnswer = answersLength === answerIndex + 1; - return doubleMeasuresUntil > answerIndex && !isLastAnswer; -} - function _singleMeasure({ challenges, answer, latestCapacity, likelihood, normalizedPosteriori, variationPercent }) { const answeredChallenge = _findChallengeForAnswer(challenges, answer); @@ -154,20 +101,6 @@ function _singleMeasure({ challenges, answer, latestCapacity, likelihood, normal return { latestCapacity, likelihood, normalizedPosteriori }; } -function _doubleMeasure({ challenges, answers, latestCapacity, likelihood, normalizedPosteriori, variationPercent }) { - const answeredChallenge1 = _findChallengeForAnswer(challenges, answers[0]); - const answeredChallenge2 = _findChallengeForAnswer(challenges, answers[1]); - - const normalizedPrior = _computeNormalizedPrior(latestCapacity); - - likelihood = _computeDoubleMeasureLikelihood([answeredChallenge1, answeredChallenge2], answers, likelihood); - - normalizedPosteriori = _computeNormalizedPosteriori(likelihood, normalizedPrior); - - latestCapacity = _computeCapacity(latestCapacity, variationPercent, normalizedPosteriori); - return { latestCapacity, likelihood, normalizedPosteriori }; -} - function _computeNormalizedPrior(gaussianMean) { return _normalizeDistribution( samples.map((sample) => @@ -186,17 +119,6 @@ function _computeLikelihood(answeredChallenge, answer, previousLikelihood) { return previousLikelihood[index] * probability; }); } - -function _computeDoubleMeasureLikelihood(answeredChallenges, answers, previousLikelihood) { - return samples.map((sample, index) => { - let probability1 = _getProbability(sample, answeredChallenges[0].discriminant, answeredChallenges[0].difficulty); - let probability2 = _getProbability(sample, answeredChallenges[1].discriminant, answeredChallenges[1].difficulty); - probability1 = answers[0].isOk() ? probability1 : 1 - probability1; - probability2 = answers[1].isOk() ? probability2 : 1 - probability2; - return (previousLikelihood[index] * (probability1 + probability2)) / 2; - }); -} - function _computeNormalizedPosteriori(likelihood, normalizedGaussian) { const posteriori = samples.map((_, index) => likelihood[index] * normalizedGaussian[index]); @@ -251,8 +173,9 @@ function _limitCapacityVariation(previousCapacity, nextCapacity, variationPercen : Math.max(nextCapacity, previousCapacity - gap); } -function _findBestPossibleChallenges(challengesWithReward, minimumSuccessRate, capacity) { - const hasMinimumSuccessRate = ({ challenge }) => { +function _findBestPossibleChallenges(challengesWithReward, capacity) { + const canChallengeBeSuccessful = ({ challenge }) => { + const minimumSuccessRate = 0; const successProbability = _getProbability(capacity, challenge.discriminant, challenge.difficulty); return successProbability >= minimumSuccessRate; @@ -260,7 +183,7 @@ function _findBestPossibleChallenges(challengesWithReward, minimumSuccessRate, c const orderedChallengesWithReward = orderBy( challengesWithReward, - [hasMinimumSuccessRate, 'reward'], + [canChallengeBeSuccessful, 'reward'], ['desc', 'desc'], ); diff --git a/api/src/certification/flash-certification/infrastructure/serializers/flash-algorithm-configuration-serializer.js b/api/src/certification/flash-certification/infrastructure/serializers/flash-algorithm-configuration-serializer.js index 022c971a7dd..a95abd09832 100644 --- a/api/src/certification/flash-certification/infrastructure/serializers/flash-algorithm-configuration-serializer.js +++ b/api/src/certification/flash-certification/infrastructure/serializers/flash-algorithm-configuration-serializer.js @@ -4,16 +4,11 @@ const { Serializer } = jsonapiSerializer; const serialize = function ({ flashAlgorithmConfiguration }) { const attributes = [ - 'warmUpLength', - 'forcedCompetences', 'maximumAssessmentLength', 'challengesBetweenSameCompetence', - 'minimumEstimatedSuccessRateRanges', 'limitToOneQuestionPerTube', 'enablePassageByAllCompetences', - 'doubleMeasuresUntil', 'variationPercent', - 'variationPercentUntil', ]; return new Serializer('flash-algorithm-configurations', { transform(config) { diff --git a/api/src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js b/api/src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js index bfb5194b577..acff170d980 100644 --- a/api/src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js +++ b/api/src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js @@ -1,89 +1,55 @@ import { config } from '../../../../shared/config.js'; -import { FlashAssessmentSuccessRateHandler } from '../../../flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js'; /** - * @param forcedCompetences - force the algorithm to ask questions on the specified competences * @param maximumAssessmentLength - override the default limit for an assessment length * @param challengesBetweenSameCompetence - define a number of questions before getting another one on the same competence - * @param minimumEstimatedSuccessRateRanges - force a minimal estimated success rate for challenges chosen at specific indexes * @param limitToOneQuestionPerTube - limits questions to one per tube * @param flashImplementation - the flash algorithm implementation * @param enablePassageByAllCompetences - enable or disable the passage through all competences - * @param doubleMeasuresUntil - use the double measure strategy for specified number of challenges * @param variationPercent - maximum variation of estimated level between two answers - * @param variationPercentUntil - maximum variation of estimated level between two answers for a specified number of challenges */ export class FlashAssessmentAlgorithmConfiguration { constructor({ - warmUpLength = 0, - forcedCompetences = [], maximumAssessmentLength = config.v3Certification.numberOfChallengesPerCourse, challengesBetweenSameCompetence = config.v3Certification.challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges = [], limitToOneQuestionPerTube = false, enablePassageByAllCompetences = false, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, } = {}) { - this.warmUpLength = warmUpLength; - this.forcedCompetences = forcedCompetences; this.maximumAssessmentLength = maximumAssessmentLength; this.challengesBetweenSameCompetence = challengesBetweenSameCompetence; - this.minimumEstimatedSuccessRateRanges = minimumEstimatedSuccessRateRanges; this.limitToOneQuestionPerTube = limitToOneQuestionPerTube; this.enablePassageByAllCompetences = enablePassageByAllCompetences; - this.doubleMeasuresUntil = doubleMeasuresUntil; this.variationPercent = variationPercent; - this.variationPercentUntil = variationPercentUntil; this.createdAt = createdAt; } toDTO() { return { - warmUpLength: this.warmUpLength, - forcedCompetences: JSON.stringify(this.forcedCompetences), maximumAssessmentLength: this.maximumAssessmentLength, challengesBetweenSameCompetence: this.challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges: JSON.stringify( - this.minimumEstimatedSuccessRateRanges.map((successRateRange) => successRateRange.toDTO()), - ), limitToOneQuestionPerTube: this.limitToOneQuestionPerTube, enablePassageByAllCompetences: this.enablePassageByAllCompetences, - doubleMeasuresUntil: this.doubleMeasuresUntil, variationPercent: this.variationPercent, - variationPercentUntil: this.variationPercentUntil, createdAt: this.createdAt, }; } static fromDTO({ - warmUpLength, - forcedCompetences, maximumAssessmentLength, challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges, limitToOneQuestionPerTube, enablePassageByAllCompetences, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, }) { return new FlashAssessmentAlgorithmConfiguration({ - warmUpLength, - forcedCompetences, maximumAssessmentLength, challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges: minimumEstimatedSuccessRateRanges - ? minimumEstimatedSuccessRateRanges.map((config) => FlashAssessmentSuccessRateHandler.fromDTO(config)) - : minimumEstimatedSuccessRateRanges, limitToOneQuestionPerTube, enablePassageByAllCompetences, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, }); } diff --git a/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v3_test.js b/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v3_test.js index 21290f6a002..850b6f5a05e 100644 --- a/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v3_test.js +++ b/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v3_test.js @@ -173,8 +173,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -186,8 +184,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -289,8 +285,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -303,8 +297,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -396,8 +388,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -409,8 +399,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -551,8 +539,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -564,8 +550,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -667,8 +651,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -680,8 +662,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -789,8 +769,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -802,8 +780,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -909,8 +885,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -922,8 +896,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -1034,8 +1006,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -1046,8 +1016,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -1158,8 +1126,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -1171,8 +1137,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -1281,8 +1245,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -1294,8 +1256,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { @@ -1399,8 +1359,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: expectedCapacity, @@ -1412,8 +1370,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V3', funct allAnswers: answers, capacity: sinon.match.number, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns([ { diff --git a/api/tests/certification/evaluation/unit/domain/usecases/get-next-challenge_test.js b/api/tests/certification/evaluation/unit/domain/usecases/get-next-challenge_test.js index b1b747a546d..214389b9bea 100644 --- a/api/tests/certification/evaluation/unit/domain/usecases/get-next-challenge_test.js +++ b/api/tests/certification/evaluation/unit/domain/usecases/get-next-challenge_test.js @@ -103,8 +103,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [nextChallengeToAnswer], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -112,7 +110,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [nextChallengeToAnswer], capacity: 0, - options: sinon.match.any, }) .returns([nextChallengeToAnswer]); @@ -195,8 +192,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [nextChallengeToAnswer, accessibleChallenge], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -204,7 +199,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [nextChallengeToAnswer, accessibleChallenge], capacity: 0, - options: sinon.match.any, }) .returns([nextChallengeToAnswer]); @@ -355,8 +349,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [alreadyAnsweredChallenge, outdatedChallenge, nextChallengeToAnswer], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -364,7 +356,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [nextChallengeToAnswer], capacity: 0, - options: sinon.match.any, }) .returns([nextChallengeToAnswer]); @@ -439,8 +430,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [nextChallenge], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -448,7 +437,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [nextChallenge], capacity: 0, - options: sinon.match.any, }) .returns([nextChallenge]); @@ -542,8 +530,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [challengeWithOtherSkill], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -551,7 +537,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [challengeWithOtherSkill], capacity: 0, - options: sinon.match.any, }) .returns([challengeWithOtherSkill]); @@ -660,16 +645,7 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { const competenceId = 'cmp1'; // eslint-disable-next-line mocha/no-setup-in-describe Object.entries({ - forcedCompetences: [competenceId], challengesBetweenSameCompetence: 2, - minimumEstimatedSuccessRateRanges: [ - { - type: 'fixed', - startingChallengeIndex: 0, - endingChallengeIndex: 10, - value: 0.1, - }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: true, variationPercent: 20, @@ -721,8 +697,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { challenges: [nextChallengeToAnswer], capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: configuration.variationPercent, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0 }); @@ -730,7 +704,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () { .withArgs({ availableChallenges: [nextChallengeToAnswer], capacity: 0, - options: sinon.match.any, }) .returns([nextChallengeToAnswer]); diff --git a/api/tests/certification/flash-certification/acceptance/application/flash-assessment-configuration-route_test.js b/api/tests/certification/flash-certification/acceptance/application/flash-assessment-configuration-route_test.js index 1b10ffaef37..506795f5ceb 100644 --- a/api/tests/certification/flash-certification/acceptance/application/flash-assessment-configuration-route_test.js +++ b/api/tests/certification/flash-certification/acceptance/application/flash-assessment-configuration-route_test.js @@ -52,7 +52,7 @@ describe('Acceptance | Application | flash-assessment-configuration-route', func describe('when there is an available configuration', function () { it('should return a 200', async function () { // given - const warmUpLength = 12; + const enablePassageByAllCompetences = true; const superAdmin = databaseBuilder.factory.buildUser.withRole({ role: PIX_ADMIN.ROLES.SUPER_ADMIN, }); @@ -63,7 +63,7 @@ describe('Acceptance | Application | flash-assessment-configuration-route', func databaseBuilder.factory.buildFlashAlgorithmConfiguration({ createdAt: new Date('2021-01-01'), - warmUpLength, + enablePassageByAllCompetences, }); await databaseBuilder.commit(); @@ -83,7 +83,7 @@ describe('Acceptance | Application | flash-assessment-configuration-route', func // then expect(response.statusCode).to.equal(200); - expect(response.result.data.attributes['warm-up-length']).to.equal(warmUpLength); + expect(response.result.data.attributes['enable-passage-by-all-competences']).to.be.true; }); }); }); diff --git a/api/tests/certification/flash-certification/integration/application/flash-assessment-configuration-controller_test.js b/api/tests/certification/flash-certification/integration/application/flash-assessment-configuration-controller_test.js index 586fab9f08f..739f1ae0d21 100644 --- a/api/tests/certification/flash-certification/integration/application/flash-assessment-configuration-controller_test.js +++ b/api/tests/certification/flash-certification/integration/application/flash-assessment-configuration-controller_test.js @@ -9,7 +9,7 @@ describe('Integration | Application | FlashAssessmentConfigurationController', f data: { type: 'flash-algorithm-configurations', attributes: { - 'warm-up-length': 12, + 'enable-passage-by-all-competences': true, }, }, }; @@ -20,7 +20,7 @@ describe('Integration | Application | FlashAssessmentConfigurationController', f }; const flashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 12, + enablePassageByAllCompetences: true, }); usecases.getActiveFlashAssessmentConfiguration.resolves(flashAlgorithmConfiguration); @@ -42,7 +42,7 @@ describe('Integration | Application | FlashAssessmentConfigurationController', f sinon.stub(usecases, 'createFlashAssessmentConfiguration'); const payload = { - warmUpLength: 12, + enablePassageByAllCompetences: true, }; const response = await flashAssessmentConfigurationController.createFlashAssessmentConfiguration( diff --git a/api/tests/certification/flash-certification/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js b/api/tests/certification/flash-certification/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js index a40ad47ecc7..bbc514883b8 100644 --- a/api/tests/certification/flash-certification/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js +++ b/api/tests/certification/flash-certification/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js @@ -6,16 +6,9 @@ describe('Certification | Flash-certification | Integration | Infrastructure | R it('should create a flash algorithm configuration', async function () { // given const flashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, }); @@ -26,33 +19,20 @@ describe('Certification | Flash-certification | Integration | Infrastructure | R // then const createdConfiguration = await knex('flash-algorithm-configurations').first(); expect(createdConfiguration).to.deep.contains({ - warmUpLength: 1, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, }); }); it('should create a flash algorithm configuration without forced competences', async function () { // given const flashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, }); @@ -63,31 +43,20 @@ describe('Certification | Flash-certification | Integration | Infrastructure | R // then const createdConfiguration = await knex('flash-algorithm-configurations').first(); expect(createdConfiguration).to.deep.contains({ - warmUpLength: 1, - forcedCompetences: [], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, }); }); it('should create a flash algorithm configuration without minimum estimated success rate ranges', async function () { // given const flashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - forcedCompetences: ['comp1', 'comp2'], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, }); @@ -98,16 +67,11 @@ describe('Certification | Flash-certification | Integration | Infrastructure | R // then const createdConfiguration = await knex('flash-algorithm-configurations').first(); expect(createdConfiguration).to.deep.contains({ - warmUpLength: 1, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [], maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, }); }); }); diff --git a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule_test.js b/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule_test.js deleted file mode 100644 index 3c5621a59fa..00000000000 --- a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule_test.js +++ /dev/null @@ -1,137 +0,0 @@ -import { FlashAssessmentAlgorithmForcedCompetencesRule } from '../../../../../../src/certification/flash-certification/domain/models/FlashAssessmentAlgorithmForcedCompetencesRule.js'; -import { domainBuilder, expect } from '../../../../../test-helper.js'; - -describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlgorithmForcedCompetencesRule', function () { - describe('#isApplicable', function () { - describe('when there is NO forcedCompetences', function () { - it('should return false', function () { - const configuration = { - forcedCompetences: [], - warmUpLength: 1, - answers: [], - }; - - expect(FlashAssessmentAlgorithmForcedCompetencesRule.isApplicable(configuration)).to.be.false; - }); - }); - - describe('when there is forcedCompetences AND answers length is smaller than warmUpLength', function () { - it('should return false', function () { - const configuration = { - forcedCompetences: ['comp1'], - warmUpLength: 2, - answers: [], - }; - - expect(FlashAssessmentAlgorithmForcedCompetencesRule.isApplicable(configuration)).to.be.false; - }); - }); - - describe('when there is forcedCompetence AND answers length is bigger or equal than warmUpLength', function () { - it('should return true', function () { - const configuration = { - forcedCompetences: ['comp1'], - warmUpLength: 0, - answers: [], - }; - - expect(FlashAssessmentAlgorithmForcedCompetencesRule.isApplicable(configuration)).to.be.true; - }); - }); - }); - - describe('#execute', function () { - it('should return the challenges with forced competences ONLY', function () { - const unansweredCompetenceUnansweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge1', - competenceId: 'competenceId1', - }); - - const unansweredCompetenceUnansweredForcedChallenge = domainBuilder.buildChallenge({ - id: 'challenge1forced', - competenceId: 'comp1', - }); - - const answeredCompetenceUnansweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge2', - competenceId: 'competenceId2', - }); - - const answeredCompetenceAnsweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge3', - competenceId: 'competenceId2', - }); - - const allChallenges = [ - unansweredCompetenceUnansweredChallenge, - unansweredCompetenceUnansweredForcedChallenge, - answeredCompetenceUnansweredChallenge, - answeredCompetenceAnsweredChallenge, - ]; - - const assessmentAnswers = [ - domainBuilder.buildAnswer({ - challengeId: answeredCompetenceAnsweredChallenge.id, - }), - ]; - - expect( - FlashAssessmentAlgorithmForcedCompetencesRule.execute({ - allChallenges, - assessmentAnswers, - availableChallenges: allChallenges, - warmUpLength: 0, - forcedCompetences: ['comp1'], - }), - ).to.deep.equal([unansweredCompetenceUnansweredForcedChallenge]); - }); - - it('should all the challenges if they are all answered', function () { - const unansweredCompetenceUnansweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge1', - competenceId: 'competenceId1', - }); - - const answeredCompetenceUnansweredForcedChallenge = domainBuilder.buildChallenge({ - id: 'challenge1forced', - competenceId: 'comp1', - }); - - const answeredCompetenceUnansweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge2', - competenceId: 'competenceId2', - }); - - const answeredCompetenceAnsweredChallenge = domainBuilder.buildChallenge({ - id: 'challenge3', - competenceId: 'competenceId2', - }); - - const allChallenges = [ - unansweredCompetenceUnansweredChallenge, - answeredCompetenceUnansweredForcedChallenge, - answeredCompetenceUnansweredChallenge, - answeredCompetenceAnsweredChallenge, - ]; - - const assessmentAnswers = [ - domainBuilder.buildAnswer({ - challengeId: answeredCompetenceAnsweredChallenge.id, - }), - domainBuilder.buildAnswer({ - challengeId: answeredCompetenceUnansweredForcedChallenge.id, - }), - ]; - - expect( - FlashAssessmentAlgorithmForcedCompetencesRule.execute({ - allChallenges, - assessmentAnswers, - availableChallenges: allChallenges, - warmUpLength: 0, - forcedCompetences: ['comp1'], - }), - ).to.deep.equal(allChallenges); - }); - }); -}); diff --git a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule_test.js b/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule_test.js index 4fc5ed5642f..a0c96f1037a 100644 --- a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule_test.js +++ b/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentAlgorithmPassageByAllCompetencesRule_test.js @@ -7,7 +7,6 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg it('should return false', function () { const configuration = { enablePassageByAllCompetences: false, - warmUpLength: 1, answers: [], }; @@ -15,24 +14,10 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg }); }); - describe('when enablePassageByAllCompetences is true AND answers length is smaller than warmUpLength', function () { - it('should return false', function () { - const configuration = { - enablePassageByAllCompetences: true, - warmUpLength: 2, - answers: [], - }; - - expect(FlashAssessmentAlgorithmPassageByAllCompetencesRule.isApplicable(configuration)).to.be.false; - }); - }); - - describe('when the enablePassageByAllCompetences is true AND answers length is bigger or equal than warmUpLength', function () { + describe('when enablePassageByAllCompetences is true', function () { it('should return true', function () { const configuration = { enablePassageByAllCompetences: true, - warmUpLength: 0, - answers: [], }; expect(FlashAssessmentAlgorithmPassageByAllCompetencesRule.isApplicable(configuration)).to.be.true; @@ -74,7 +59,6 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg allChallenges, assessmentAnswers, availableChallenges: allChallenges, - warmUpLength: 0, }), ).to.deep.equal([unansweredCompetenceUnansweredChallenge]); }); diff --git a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentSuccessRateHandler_test.js b/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentSuccessRateHandler_test.js deleted file mode 100644 index fcd16a56a2a..00000000000 --- a/api/tests/certification/flash-certification/unit/domain/models/FlashAssessmentSuccessRateHandler_test.js +++ /dev/null @@ -1,83 +0,0 @@ -import { FlashAssessmentSuccessRateHandler } from '../../../../../../src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js'; -import { expect } from '../../../../../test-helper.js'; - -describe('Unit | Domain | Models | FlashAssessmentAlgorithmSuccessRateHandler', function () { - describe('#isApplicable', function () { - let flashAssessmentSuccessRateHandler; - - beforeEach(function () { - const fixedConfig = { - startingChallengeIndex: 0, - endingChallengeIndex: 7, - value: 0.8, - }; - - flashAssessmentSuccessRateHandler = FlashAssessmentSuccessRateHandler.create(fixedConfig); - }); - - describe('when currentIndex is inside the application range', function () { - it('should return true', function () { - const currentIndex = 5; - - const isApplicable = flashAssessmentSuccessRateHandler.isApplicable(currentIndex); - - expect(isApplicable).to.be.true; - }); - }); - - describe('when filter is outside the application range', function () { - it('should return false', function () { - const currentIndex = 8; - - const isApplicable = flashAssessmentSuccessRateHandler.isApplicable(currentIndex); - - expect(isApplicable).to.be.false; - }); - }); - }); - - describe('#getMinimalSuccessRate', function () { - describe('when strategy is fixed', function () { - let flashAssessmentSuccessRateHandler; - const configSuccessRate = 0.8; - - beforeEach(function () { - const fixedConfig = { - startingChallengeIndex: 0, - endingChallengeIndex: 7, - value: configSuccessRate, - }; - - flashAssessmentSuccessRateHandler = FlashAssessmentSuccessRateHandler.create(fixedConfig); - }); - - it('should return the fixed value', function () { - const questionIndex = 5; - const successRate = flashAssessmentSuccessRateHandler.getMinimalSuccessRate(questionIndex); - - expect(successRate).to.equal(configSuccessRate); - }); - }); - }); - - describe('#create', function () { - describe('when type is fixed', function () { - it('should return the fixed value', function () { - const configSuccessRate = 0.8; - const fixedConfig = { - type: 'fixed', - startingChallengeIndex: 0, - endingChallengeIndex: 7, - value: configSuccessRate, - }; - - const flashAssessmentSuccessRateHandler = FlashAssessmentSuccessRateHandler.create(fixedConfig); - - const questionIndex = 5; - const successRate = flashAssessmentSuccessRateHandler.getMinimalSuccessRate(questionIndex); - - expect(successRate).to.equal(configSuccessRate); - }); - }); - }); -}); diff --git a/api/tests/certification/flash-certification/unit/domain/services/algorithm-methods/flash_test.js b/api/tests/certification/flash-certification/unit/domain/services/algorithm-methods/flash_test.js index 8967b492788..2678f9a2ca6 100644 --- a/api/tests/certification/flash-certification/unit/domain/services/algorithm-methods/flash_test.js +++ b/api/tests/certification/flash-certification/unit/domain/services/algorithm-methods/flash_test.js @@ -91,670 +91,490 @@ describe('Integration | Domain | Algorithm-methods | Flash', function () { }); describe('#getCapacityAndErrorRate', function () { - context('when single measure', function () { - it('should return 0 when there is no answers', function () { - // given - const allAnswers = []; + it('should return 0 when there is no answers', function () { + // given + const allAnswers = []; - // when - const result = flash.getCapacityAndErrorRate({ allAnswers }); + // when + const result = flash.getCapacityAndErrorRate({ allAnswers }); - // then - expect(result).to.deep.equal({ - capacity: 0, - errorRate: 5, - }); + // then + expect(result).to.deep.equal({ + capacity: 0, + errorRate: 5, }); + }); - it('should return the correct capacity when there is one answer', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - ]; + it('should return the correct capacity when there is one answer', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + ]; - const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; + const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; - // when - const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); + // when + const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); - // then - expect(capacity).to.be.closeTo(0.859419960298745, 0.00000000001); - expect(errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); - }); + // then + expect(capacity).to.be.closeTo(0.859419960298745, 0.00000000001); + expect(errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); + }); - it('should return the correct capacity when there is two answers', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswers', - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeSecondAnswers', - discriminant: 2.25422414740233, - difficulty: 0.823376599163319, - }), - ]; + it('should return the correct capacity when there is two answers', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + id: 'ChallengeFirstAnswers', + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + domainBuilder.buildChallenge({ + id: 'ChallengeSecondAnswers', + discriminant: 2.25422414740233, + difficulty: 0.823376599163319, + }), + ]; - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - ]; + const allAnswers = [ + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), + ]; - // when - const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); + // when + const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); - // then - expect(capacity).to.be.closeTo(1.802340122865396, 0.00000000001); - expect(errorRate).to.be.closeTo(0.8549014053951466, 0.00000000001); - }); + // then + expect(capacity).to.be.closeTo(1.802340122865396, 0.00000000001); + expect(errorRate).to.be.closeTo(0.8549014053951466, 0.00000000001); + }); - it('should return the correct capacity when there is three answers', function () { - // given - const challenges = [ + it('should return the correct capacity when there is three answers', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + id: 'ChallengeFirstAnswers', + discriminant: 1.06665273005823, + difficulty: -0.030736508016524, + }), + domainBuilder.buildChallenge({ + id: 'ChallengeSecondAnswers', + discriminant: 1.50948587856458, + difficulty: 1.62670103354638, + }), + domainBuilder.buildChallenge({ + id: 'ChallengeThirdAnswers', + discriminant: 0.950709518595358, + difficulty: 1.90647729810166, + }), + ]; + + const allAnswers = [ + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), + ]; + + // when + const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); + + // then + expect(capacity).to.be.closeTo(2.851063556136754, 0.00000000001); + expect(errorRate).to.be.closeTo(0.9271693210547304, 0.00000000001); + }); + + context('when the user answers a lot of challenges', function () { + it('should return the correct capacity and errorRate', function () { + const listSkills = { + url5: domainBuilder.buildSkill({ id: 'url5' }), + web3: domainBuilder.buildSkill({ id: 'web3' }), + sourceinfo5: domainBuilder.buildSkill({ id: 'sourceinfo5' }), + installogiciel2: domainBuilder.buildSkill({ id: 'installogiciel2' }), + fichier4: domainBuilder.buildSkill({ id: 'fichier4' }), + sauvegarde5: domainBuilder.buildSkill({ id: 'sauvegarde5' }), + langbalise6: domainBuilder.buildSkill({ id: 'langbalise6' }), + pratiquesinternet4: domainBuilder.buildSkill({ id: 'pratiquesinternet4' }), + langbalise7: domainBuilder.buildSkill({ id: 'langbalise7' }), + }; + + const listChallenges = [ + domainBuilder.buildChallenge({ + id: 'recA', + skill: listSkills.url5, + difficulty: -0.917927344545694, + discriminant: 1.02282430250024, + }), domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswers', - discriminant: 1.06665273005823, + id: 'recB', + skill: listSkills.web3, + difficulty: 0.301604780272093, + discriminant: 0.815896135600247, + }), + domainBuilder.buildChallenge({ + id: 'recC', + skill: listSkills.sourceinfo5, + difficulty: -1.69218011589622, + discriminant: 1.38594509996278, + }), + domainBuilder.buildChallenge({ + id: 'recD', + skill: listSkills.installogiciel2, + difficulty: -5.4464574841729, + discriminant: 0.427255285029657, + }), + domainBuilder.buildChallenge({ + id: 'recE', + skill: listSkills.fichier4, + difficulty: -1.5526216455839, + discriminant: 1.21015304225808, + }), + domainBuilder.buildChallenge({ + id: 'recF', + skill: listSkills.fichier4, + difficulty: -1.36561917255237, + discriminant: 1.09320650236677, + }), + domainBuilder.buildChallenge({ + id: 'recG', + skill: listSkills.fichier4, + difficulty: -4.20230915443229, + discriminant: 0.562929008226957, + }), + domainBuilder.buildChallenge({ + id: 'recH', + skill: listSkills.fichier4, + difficulty: 0.262904155422314, + discriminant: 0.901542609459213, + }), + domainBuilder.buildChallenge({ + id: 'recI', + skill: listSkills.fichier4, + difficulty: -0.754355900389256, + discriminant: 0.834990152043718, + }), + domainBuilder.buildChallenge({ + id: 'recJ', + skill: listSkills.sauvegarde5, + difficulty: 3.174339929941, + discriminant: 0.827526706077148, + }), + domainBuilder.buildChallenge({ + id: 'recK', + skill: listSkills.sauvegarde5, + difficulty: -1.16967416012961, + discriminant: 1.17433370794629, + }), + domainBuilder.buildChallenge({ + id: 'recL', + skill: listSkills.sauvegarde5, difficulty: -0.030736508016524, + discriminant: 1.06665273005823, }), domainBuilder.buildChallenge({ - id: 'ChallengeSecondAnswers', - discriminant: 1.50948587856458, + id: 'recM', + skill: listSkills.sauvegarde5, + difficulty: -2.37249657419562, + discriminant: 0.656224379307742, + }), + domainBuilder.buildChallenge({ + id: 'recN', + skill: listSkills.langbalise6, difficulty: 1.62670103354638, + discriminant: 1.50948587856458, }), domainBuilder.buildChallenge({ - id: 'ChallengeThirdAnswers', - discriminant: 0.950709518595358, + id: 'recO', + skill: listSkills.langbalise6, + difficulty: 2.811956480867, + discriminant: 1.04445171700575, + }), + domainBuilder.buildChallenge({ + id: 'recP', + skill: listSkills.langbalise6, + difficulty: 0.026713944730478, + discriminant: 0.703441785686095, + }), + domainBuilder.buildChallenge({ + id: 'recQ', + skill: listSkills.pratiquesinternet4, + difficulty: -1.83253533603, + discriminant: 0.711777117426424, + }), + domainBuilder.buildChallenge({ + id: 'recR', + skill: listSkills.pratiquesinternet4, + difficulty: 0.251708600387063, + discriminant: 0.369707224301943, + }), + domainBuilder.buildChallenge({ + id: 'recS', + skill: listSkills.pratiquesinternet4, difficulty: 1.90647729810166, + discriminant: 0.950709518595358, + }), + domainBuilder.buildChallenge({ + id: 'recT', + skill: listSkills.langbalise6, + difficulty: -1.82670103354638, + discriminant: 2.50948587856458, }), ]; - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), + const answers = [ + domainBuilder.buildAnswer({ challengeId: 'recL', result: AnswerStatus.KO }), + domainBuilder.buildAnswer({ challengeId: 'recC', result: AnswerStatus.OK }), + domainBuilder.buildAnswer({ challengeId: 'recT', result: AnswerStatus.KO }), + domainBuilder.buildAnswer({ challengeId: 'recE', result: AnswerStatus.OK }), + domainBuilder.buildAnswer({ challengeId: 'recA', result: AnswerStatus.OK }), + domainBuilder.buildAnswer({ challengeId: 'recQ', result: AnswerStatus.OK }), + ]; + const expectedCapacity = [ + 0, -0.6086049191210775, -0.6653800198379971, -1.7794873733366134, -1.8036203882448785, -1.557864373635504, + -1.3925555729766932, ]; - // when - const { capacity, errorRate } = flash.getCapacityAndErrorRate({ allAnswers, challenges }); + const expectedErrorRate = [ + 5, 1.0659524854635638, 0.9249688328247474, 0.6682031829777919, 0.6193894938423906, 0.5934694499356048, + 0.5880298028515323, + ]; - // then - expect(capacity).to.be.closeTo(2.851063556136754, 0.00000000001); - expect(errorRate).to.be.closeTo(0.9271693210547304, 0.00000000001); + const allAnswers = []; + let result; + + for (let i = 0; i < answers.length; i++) { + result = flash.getCapacityAndErrorRate({ challenges: listChallenges, allAnswers }); + + // then + expect(result.capacity).to.be.closeTo(expectedCapacity[i], 0.000000001); + expect(result.errorRate).to.be.closeTo(expectedErrorRate[i], 0.000000001); + + allAnswers.push(answers[i]); + } }); + }); - context('when the user answers a lot of challenges', function () { - it('should return the correct capacity and errorRate', function () { - const listSkills = { - url5: domainBuilder.buildSkill({ id: 'url5' }), - web3: domainBuilder.buildSkill({ id: 'web3' }), - sourceinfo5: domainBuilder.buildSkill({ id: 'sourceinfo5' }), - installogiciel2: domainBuilder.buildSkill({ id: 'installogiciel2' }), - fichier4: domainBuilder.buildSkill({ id: 'fichier4' }), - sauvegarde5: domainBuilder.buildSkill({ id: 'sauvegarde5' }), - langbalise6: domainBuilder.buildSkill({ id: 'langbalise6' }), - pratiquesinternet4: domainBuilder.buildSkill({ id: 'pratiquesinternet4' }), - langbalise7: domainBuilder.buildSkill({ id: 'langbalise7' }), - }; - - const listChallenges = [ - domainBuilder.buildChallenge({ - id: 'recA', - skill: listSkills.url5, - difficulty: -0.917927344545694, - discriminant: 1.02282430250024, - }), - domainBuilder.buildChallenge({ - id: 'recB', - skill: listSkills.web3, - difficulty: 0.301604780272093, - discriminant: 0.815896135600247, - }), - domainBuilder.buildChallenge({ - id: 'recC', - skill: listSkills.sourceinfo5, - difficulty: -1.69218011589622, - discriminant: 1.38594509996278, - }), - domainBuilder.buildChallenge({ - id: 'recD', - skill: listSkills.installogiciel2, - difficulty: -5.4464574841729, - discriminant: 0.427255285029657, - }), - domainBuilder.buildChallenge({ - id: 'recE', - skill: listSkills.fichier4, - difficulty: -1.5526216455839, - discriminant: 1.21015304225808, - }), - domainBuilder.buildChallenge({ - id: 'recF', - skill: listSkills.fichier4, - difficulty: -1.36561917255237, - discriminant: 1.09320650236677, - }), - domainBuilder.buildChallenge({ - id: 'recG', - skill: listSkills.fichier4, - difficulty: -4.20230915443229, - discriminant: 0.562929008226957, - }), - domainBuilder.buildChallenge({ - id: 'recH', - skill: listSkills.fichier4, - difficulty: 0.262904155422314, - discriminant: 0.901542609459213, - }), - domainBuilder.buildChallenge({ - id: 'recI', - skill: listSkills.fichier4, - difficulty: -0.754355900389256, - discriminant: 0.834990152043718, - }), - domainBuilder.buildChallenge({ - id: 'recJ', - skill: listSkills.sauvegarde5, - difficulty: 3.174339929941, - discriminant: 0.827526706077148, - }), - domainBuilder.buildChallenge({ - id: 'recK', - skill: listSkills.sauvegarde5, - difficulty: -1.16967416012961, - discriminant: 1.17433370794629, - }), - domainBuilder.buildChallenge({ - id: 'recL', - skill: listSkills.sauvegarde5, - difficulty: -0.030736508016524, - discriminant: 1.06665273005823, - }), - domainBuilder.buildChallenge({ - id: 'recM', - skill: listSkills.sauvegarde5, - difficulty: -2.37249657419562, - discriminant: 0.656224379307742, - }), - domainBuilder.buildChallenge({ - id: 'recN', - skill: listSkills.langbalise6, - difficulty: 1.62670103354638, - discriminant: 1.50948587856458, - }), - domainBuilder.buildChallenge({ - id: 'recO', - skill: listSkills.langbalise6, - difficulty: 2.811956480867, - discriminant: 1.04445171700575, - }), - domainBuilder.buildChallenge({ - id: 'recP', - skill: listSkills.langbalise6, - difficulty: 0.026713944730478, - discriminant: 0.703441785686095, - }), - domainBuilder.buildChallenge({ - id: 'recQ', - skill: listSkills.pratiquesinternet4, - difficulty: -1.83253533603, - discriminant: 0.711777117426424, - }), - domainBuilder.buildChallenge({ - id: 'recR', - skill: listSkills.pratiquesinternet4, - difficulty: 0.251708600387063, - discriminant: 0.369707224301943, - }), - domainBuilder.buildChallenge({ - id: 'recS', - skill: listSkills.pratiquesinternet4, - difficulty: 1.90647729810166, - discriminant: 0.950709518595358, - }), + context('when limiting the estimated level variation to a given number of challenges', function () { + context('when giving a right answer', function () { + it('should return the limited capacity for the correct number of challenges', function () { + // given + const challenges = [ domainBuilder.buildChallenge({ - id: 'recT', - skill: listSkills.langbalise6, - difficulty: -1.82670103354638, - discriminant: 2.50948587856458, + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, }), ]; - const answers = [ - domainBuilder.buildAnswer({ challengeId: 'recL', result: AnswerStatus.KO }), - domainBuilder.buildAnswer({ challengeId: 'recC', result: AnswerStatus.OK }), - domainBuilder.buildAnswer({ challengeId: 'recT', result: AnswerStatus.KO }), - domainBuilder.buildAnswer({ challengeId: 'recE', result: AnswerStatus.OK }), - domainBuilder.buildAnswer({ challengeId: 'recA', result: AnswerStatus.OK }), - domainBuilder.buildAnswer({ challengeId: 'recQ', result: AnswerStatus.OK }), - ]; - const expectedCapacity = [ - 0, -0.6086049191210775, -0.6653800198379971, -1.7794873733366134, -1.8036203882448785, -1.557864373635504, - -1.3925555729766932, - ]; - - const expectedErrorRate = [ - 5, 1.0659524854635638, 0.9249688328247474, 0.6682031829777919, 0.6193894938423906, 0.5934694499356048, - 0.5880298028515323, - ]; + const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; - const allAnswers = []; - let result; + const variationPercent = 0.5; - for (let i = 0; i < answers.length; i++) { - result = flash.getCapacityAndErrorRate({ challenges: listChallenges, allAnswers }); - - // then - expect(result.capacity).to.be.closeTo(expectedCapacity[i], 0.000000001); - expect(result.errorRate).to.be.closeTo(expectedErrorRate[i], 0.000000001); - - allAnswers.push(answers[i]); - } - }); - }); - - context('when limiting the estimated level variation to a given number of challenges', function () { - context('when giving a right answer', function () { - it('should return the limited capacity for the correct number of challenges', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - ]; - - const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; - - const variationPercent = 0.5; - const variationPercentUntil = 1; - - // when - const { capacity } = flash.getCapacityAndErrorRate({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - expect(capacity).to.be.closeTo(0.5, 0.00000000001); - }); - }); - - context('when giving a wrong answer', function () { - it('should return the limited capacity', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - ]; - - const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[0].id })]; - - const variationPercent = 0.5; - const variationPercentUntil = 1; - - // when - const { capacity } = flash.getCapacityAndErrorRate({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - expect(capacity).to.be.closeTo(-0.5, 0.00000000001); + // when + const { capacity } = flash.getCapacityAndErrorRate({ + allAnswers, + challenges, + variationPercent, }); - }); - }); - }); - - context('when double measure', function () { - it('should return the correct capacity when there is three answers', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswers', - discriminant: 1.06665273005823, - difficulty: -0.03, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeSecondAnswers', - discriminant: 1.06665273005823, - difficulty: -0.06, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeThirdAnswers', - discriminant: 0.950709518595358, - difficulty: 1.90647729810166, - }), - ]; - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), - ]; - - // when - const { capacity } = flash.getCapacityAndErrorRate({ - allAnswers, - challenges, - doubleMeasuresUntil: 2, + // then + expect(capacity).to.be.closeTo(0.5, 0.00000000001); }); - - // then - expect(capacity).to.be.closeTo(1.663469355503838, 0.00000000001); }); - context('when double measure is active with an odd number of challenges', function () { - it('should return the correct capacity when there is three answers', function () { + context('when giving a wrong answer', function () { + it('should return the limited capacity', function () { // given const challenges = [ domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswers', - discriminant: 1.06665273005823, - difficulty: -0.03, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeSecondAnswers', - discriminant: 1.06665273005823, - difficulty: -0.06, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeThirdAnswers', - discriminant: 0.950709518595358, - difficulty: 1.90647729810166, + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, }), ]; - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), - ]; + const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[0].id })]; + + const variationPercent = 0.5; // when const { capacity } = flash.getCapacityAndErrorRate({ allAnswers, challenges, - doubleMeasuresUntil: 4, + variationPercent, }); // then - expect(capacity).to.be.closeTo(1.663469355503838, 0.00000000001); + expect(capacity).to.be.closeTo(-0.5, 0.00000000001); }); }); }); }); describe('#getCapacityAndErrorRateHistory', function () { - context('when single measure', function () { - it('should return 0 when there is no answers', function () { - // given - const allAnswers = []; + it('should return 0 when there is no answers', function () { + // given + const allAnswers = []; - // when - const result = flash.getCapacityAndErrorRateHistory({ allAnswers }); + // when + const result = flash.getCapacityAndErrorRateHistory({ allAnswers }); - // then - expect(result).to.deep.equal([]); - }); + // then + expect(result).to.deep.equal([]); + }); - it('should return the correct capacity when there is one answer', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - ]; + it('should return the correct capacity when there is one answer', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + ]; - const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; + const allAnswers = [domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id })]; - // when - const [{ capacity, errorRate }] = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); + // when + const [{ capacity, errorRate }] = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); - // then - expect(capacity).to.be.closeTo(0.859419960298745, 0.00000000001); - expect(errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); - }); + // then + expect(capacity).to.be.closeTo(0.859419960298745, 0.00000000001); + expect(errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); + }); - it('should return the correct capacity when there are two answers', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswers', - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - domainBuilder.buildChallenge({ - id: 'ChallengeSecondAnswers', - discriminant: 2.25422414740233, - difficulty: 0.823376599163319, - }), - ]; + it('should return the correct capacity when there are two answers', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + id: 'ChallengeFirstAnswers', + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + domainBuilder.buildChallenge({ + id: 'ChallengeSecondAnswers', + discriminant: 2.25422414740233, + difficulty: 0.823376599163319, + }), + ]; - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - ]; + const allAnswers = [ + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), + ]; - // when - const results = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); + // when + const results = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); - // then - expect(results[0].capacity).to.be.closeTo(0.859419960298745, 0.00000000001); - expect(results[0].errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); - expect(results[1].capacity).to.be.closeTo(1.802340122865396, 0.00000000001); - expect(results[1].errorRate).to.be.closeTo(0.8549014053951466, 0.00000000001); - }); + // then + expect(results[0].capacity).to.be.closeTo(0.859419960298745, 0.00000000001); + expect(results[0].errorRate).to.be.closeTo(0.9327454634914153, 0.00000000001); + expect(results[1].capacity).to.be.closeTo(1.802340122865396, 0.00000000001); + expect(results[1].errorRate).to.be.closeTo(0.8549014053951466, 0.00000000001); + }); - it('should return the answer id when there is at least one answer', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - id: 'ChallengeFirstAnswer', - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - ]; + it('should return the answer id when there is at least one answer', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + id: 'ChallengeFirstAnswer', + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + ]; - const answerId = 1969; + const answerId = 1969; - const allAnswers = [ - domainBuilder.buildAnswer({ id: answerId, result: AnswerStatus.OK, challengeId: challenges[0].id }), - ]; + const allAnswers = [ + domainBuilder.buildAnswer({ id: answerId, result: AnswerStatus.OK, challengeId: challenges[0].id }), + ]; - // when - const results = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); + // when + const results = flash.getCapacityAndErrorRateHistory({ allAnswers, challenges }); - // then - expect(results[0].answerId).to.equal(answerId); - }); + // then + expect(results[0].answerId).to.equal(answerId); + }); - describe('when limiting the capacity variation', function () { - describe('when limiting to a given number of challenges', function () { - describe('when giving right answers', function () { - it('should return the limited capacities for the correct number of challenges', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.056, - difficulty: 0.6973893, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.5689203, - difficulty: 1.36973893, - }), - ]; - - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), - ]; - - const variationPercent = 0.5; - const variationPercentUntil = 1; - - // when - const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); - expect(capacities).to.deep.equal([0.5, 0.75, 1.7005749291039245]); - }); - }); + describe('when limiting the capacity variation', function () { + describe('when giving right answers', function () { + it('should return the unlimited capacities for all challenges', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + + domainBuilder.buildChallenge({ + discriminant: 2.056, + difficulty: 0.6973893, + }), + + domainBuilder.buildChallenge({ + discriminant: 2.5689203, + difficulty: 1.36973893, + }), + ]; - describe('when giving wrong answers', function () { - it('should return the limited capacities for the correct number of challenges', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.056, - difficulty: 0.6973893, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.5689203, - difficulty: 1.36973893, - }), - ]; - - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[2].id }), - ]; - - const variationPercent = 0.5; - const variationPercentUntil = 1; - - // when - const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); - expect(capacities).to.deep.equal([-0.5, -0.75, -1.5100020666768261]); - }); + const allAnswers = [ + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), + ]; + + const variationPercent = 0.5; + + // when + const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ + allAnswers, + challenges, + variationPercent, }); + + // then + const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); + expect(capacities).to.deep.equal([0.5, 0.75, 1.125]); }); + }); - describe('when not limiting to a number of challenges', function () { - describe('when giving right answers', function () { - it('should return the unlimited capacities for all challenges', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.056, - difficulty: 0.6973893, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.5689203, - difficulty: 1.36973893, - }), - ]; - - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.OK, challengeId: challenges[2].id }), - ]; - - const variationPercent = 0.5; - const variationPercentUntil = null; - - // when - const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); - expect(capacities).to.deep.equal([0.5, 0.75, 1.125]); - }); - }); + describe('when giving wrong answers', function () { + it('should return the unlimited capacities for all challenges', function () { + // given + const challenges = [ + domainBuilder.buildChallenge({ + discriminant: 1.86350005965093, + difficulty: 0.194712138508747, + }), + + domainBuilder.buildChallenge({ + discriminant: 2.056, + difficulty: 0.6973893, + }), - describe('when giving wrong answers', function () { - it('should return the unlimited capacities for all challenges', function () { - // given - const challenges = [ - domainBuilder.buildChallenge({ - discriminant: 1.86350005965093, - difficulty: 0.194712138508747, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.056, - difficulty: 0.6973893, - }), - - domainBuilder.buildChallenge({ - discriminant: 2.5689203, - difficulty: 1.36973893, - }), - ]; - - const allAnswers = [ - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[0].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[1].id }), - domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[2].id }), - ]; - - const variationPercent = 0.5; - const variationPercentUntil = null; - - // when - const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ - allAnswers, - challenges, - variationPercent, - variationPercentUntil, - }); - - // then - const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); - expect(capacities).to.deep.equal([-0.5, -0.75, -1.125]); - }); + domainBuilder.buildChallenge({ + discriminant: 2.5689203, + difficulty: 1.36973893, + }), + ]; + + const allAnswers = [ + domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[0].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[1].id }), + domainBuilder.buildAnswer({ result: AnswerStatus.KO, challengeId: challenges[2].id }), + ]; + + const variationPercent = 0.5; + + // when + const capacityAndErrorRateHistory = flash.getCapacityAndErrorRateHistory({ + allAnswers, + challenges, + variationPercent, }); + + // then + const capacities = capacityAndErrorRateHistory.map(({ capacity }) => capacity); + expect(capacities).to.deep.equal([-0.5, -0.75, -1.125]); }); }); }); diff --git a/api/tests/certification/flash-certification/unit/domain/usecases/update-active-flash-assessment-configuration_test.js b/api/tests/certification/flash-certification/unit/domain/usecases/update-active-flash-assessment-configuration_test.js index 725c7a01f70..8c38626ce7c 100644 --- a/api/tests/certification/flash-certification/unit/domain/usecases/update-active-flash-assessment-configuration_test.js +++ b/api/tests/certification/flash-certification/unit/domain/usecases/update-active-flash-assessment-configuration_test.js @@ -14,11 +14,11 @@ describe('Unit | Domain | UseCases | create-flash-assessment-configuration', fun }; const configuration = { - warmUpLength: 12, + enablePassageByAllCompetences: true, }; const previousConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 10, + enablePassageByAllCompetences: false, variationPercent: 0.5, }); diff --git a/api/tests/certification/flash-certification/unit/infrastructure/serializers/flash-algorithm-configuration-serializer_test.js b/api/tests/certification/flash-certification/unit/infrastructure/serializers/flash-algorithm-configuration-serializer_test.js index 3dde211dca9..0fbbeac3108 100644 --- a/api/tests/certification/flash-certification/unit/infrastructure/serializers/flash-algorithm-configuration-serializer_test.js +++ b/api/tests/certification/flash-certification/unit/infrastructure/serializers/flash-algorithm-configuration-serializer_test.js @@ -11,14 +11,9 @@ describe('Unit | Certification | flash-certification | Serializer | flash-algori id: '0', type: 'flash-algorithm-configurations', attributes: { - 'warm-up-length': 1, - 'forced-competences': 2, 'maximum-assessment-length': 3, 'challenges-between-same-competence': 4, - 'double-measures-until': 5, 'variation-percent': 6, - 'variation-percent-until': 7, - 'minimum-estimated-success-rate-ranges': [], 'limit-to-one-question-per-tube': false, 'enable-passage-by-all-competences': true, }, @@ -26,14 +21,9 @@ describe('Unit | Certification | flash-certification | Serializer | flash-algori }; const flashAlgorithmConfiguration = new FlashAssessmentAlgorithmConfiguration({ - warmUpLength: 1, - forcedCompetences: 2, maximumAssessmentLength: 3, challengesBetweenSameCompetence: 4, - doubleMeasuresUntil: 5, variationPercent: 6, - variationPercentUntil: 7, - minimumEstimatedSuccessRateRanges: [], limitToOneQuestionPerTube: false, enablePassageByAllCompetences: true, }); diff --git a/api/tests/certification/scoring/integration/application/services/scoring-degradation-service_test.js b/api/tests/certification/scoring/integration/application/services/scoring-degradation-service_test.js index 9238ea5946f..b27f341bdec 100644 --- a/api/tests/certification/scoring/integration/application/services/scoring-degradation-service_test.js +++ b/api/tests/certification/scoring/integration/application/services/scoring-degradation-service_test.js @@ -9,16 +9,11 @@ describe('Integration | Domain | services | scoringDegradationService', function const allChallenges = _buildChallenges(); const allAnswers = _buildAnswers(); const flashAssessmentAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ - warmUpLength: 0, - forcedCompetences: [], maximumAssessmentLength: 4, challengesBetweenSameCompetence: 0, - minimumEstimatedSuccessRateRanges: [], limitToOneQuestionPerTube: false, enablePassageByAllCompetences: false, - doubleMeasuresUntil: null, variationPercent: null, - variationPercentUntil: null, createdAt: new Date('2020-01-01T00:00:00Z'), }); diff --git a/api/tests/certification/shared/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js b/api/tests/certification/shared/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js index 8a048071d8b..87cb2c7ce03 100644 --- a/api/tests/certification/shared/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js +++ b/api/tests/certification/shared/integration/infrastructure/repositories/flash-algorithm-configuration-repository_test.js @@ -11,24 +11,15 @@ describe('Certification | Shared | Integration | Infrastructure | Repository | F it('should return a flash algorithm configuration', async function () { // given const flashAlgorithmConfiguration = databaseBuilder.factory.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, }); const expectedFlashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ ...flashAlgorithmConfiguration, - forcedCompetences: JSON.parse(flashAlgorithmConfiguration.forcedCompetences), - minimumEstimatedSuccessRateRanges: JSON.parse(flashAlgorithmConfiguration.minimumEstimatedSuccessRateRanges), }); await databaseBuilder.commit(); @@ -45,32 +36,18 @@ describe('Certification | Shared | Integration | Infrastructure | Repository | F it('should return the latest', async function () { // given const latestFlashAlgorithmConfiguration = databaseBuilder.factory.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, createdAt: new Date('2021-01-01'), }); databaseBuilder.factory.buildFlashAlgorithmConfiguration({ - warmUpLength: 1, maximumAssessmentLength: 2, challengesBetweenSameCompetence: 3, variationPercent: 4, - variationPercentUntil: 3, - doubleMeasuresUntil: 5, - forcedCompetences: ['comp1', 'comp2'], - minimumEstimatedSuccessRateRanges: [ - { type: 'fixed', startingChallengeIndex: 0, endingChallengeIndex: 7, value: 0.8 }, - ], limitToOneQuestionPerTube: true, enablePassageByAllCompetences: false, createdAt: new Date('2020-01-01'), @@ -78,10 +55,6 @@ describe('Certification | Shared | Integration | Infrastructure | Repository | F const expectedFlashAlgorithmConfiguration = domainBuilder.buildFlashAlgorithmConfiguration({ ...latestFlashAlgorithmConfiguration, - forcedCompetences: JSON.parse(latestFlashAlgorithmConfiguration.forcedCompetences), - minimumEstimatedSuccessRateRanges: JSON.parse( - latestFlashAlgorithmConfiguration.minimumEstimatedSuccessRateRanges, - ), }); await databaseBuilder.commit(); diff --git a/api/tests/certification/shared/unit/domain/models/FlashAssessmentAlgorithm_test.js b/api/tests/certification/shared/unit/domain/models/FlashAssessmentAlgorithm_test.js index 6e86211ddeb..db7ffa7ed36 100644 --- a/api/tests/certification/shared/unit/domain/models/FlashAssessmentAlgorithm_test.js +++ b/api/tests/certification/shared/unit/domain/models/FlashAssessmentAlgorithm_test.js @@ -1,13 +1,9 @@ import { FlashAssessmentAlgorithm } from '../../../../../../src/certification/flash-certification/domain/models/FlashAssessmentAlgorithm.js'; -import { FlashAssessmentSuccessRateHandler } from '../../../../../../src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js'; import { FlashAssessmentAlgorithmConfiguration } from '../../../../../../src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js'; import { config } from '../../../../../../src/shared/config.js'; import { catchErrSync, domainBuilder, expect, sinon } from '../../../../../test-helper.js'; const baseFlashAssessmentAlgorithmConfig = { - warmUpLength: 0, - forcedCompetences: [], - minimumEstimatedSuccessRateRanges: [], limitToOneQuestionPerTube: false, enablePassageByAllCompetences: false, }; @@ -15,11 +11,6 @@ const baseFlashAssessmentAlgorithmConfig = { describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlgorithm', function () { let flashAlgorithmImplementation; - const baseGetNextChallengeOptions = { - challengesBetweenSameCompetence: 2, - minimalSuccessRate: 0, - }; - beforeEach(function () { flashAlgorithmImplementation = { getPossibleNextChallenges: sinon.stub(), @@ -160,7 +151,6 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg .withArgs({ availableChallenges: expectedChallenges, capacity: computedCapacity, - options: baseGetNextChallengeOptions, }) .returns(expectedChallenges); @@ -237,7 +227,6 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg .withArgs({ availableChallenges: expectedChallenges, capacity: computedCapacity, - options: baseGetNextChallengeOptions, }) .returns(expectedChallenges); @@ -247,78 +236,6 @@ describe('Unit | Domain | Models | FlashAssessmentAlgorithm | FlashAssessmentAlg }); }); }); - - context('when specifying a minimal success rate', function () { - context('when a fixed minimal success rate has been set', function () { - it('should choose a challenge that has the required success rate first', function () { - // Given - const easyDifficulty = -3; - const hardDifficulty = 3; - const discriminant = 1; - const initialCapacity = 3; - - const easyChallenge = domainBuilder.buildChallenge({ - id: 'easyChallenge', - skill: domainBuilder.buildSkill({ - id: 'skillEasy', - }), - competenceId: 'compEasy', - discriminant, - difficulty: easyDifficulty, - }); - - const hardChallenge = domainBuilder.buildChallenge({ - id: 'hardChallenge', - skill: domainBuilder.buildSkill({ - id: 'skillHard', - }), - competenceId: 'compHard', - discriminant, - difficulty: hardDifficulty, - }); - - const challenges = [hardChallenge, easyChallenge]; - const assessmentAnswers = []; - - // when - const algorithm = new FlashAssessmentAlgorithm({ - flashAlgorithmImplementation, - configuration: _getAlgorithmConfig({ - limitToOneQuestionPerTube: true, - minimumEstimatedSuccessRateRanges: [ - FlashAssessmentSuccessRateHandler.create({ - startingChallengeIndex: 0, - endingChallengeIndex: 1, - value: 0.8, - }), - ], - }), - }); - - flashAlgorithmImplementation.getCapacityAndErrorRate.returns({ - capacity: 0, - }); - flashAlgorithmImplementation.getPossibleNextChallenges - .withArgs({ - availableChallenges: challenges, - capacity: 0, - options: { - ...baseGetNextChallengeOptions, - minimalSuccessRate: 0.8, - }, - }) - .returns([easyChallenge, hardChallenge]); - - const nextChallenges = algorithm.getPossibleNextChallenges({ - assessmentAnswers, - challenges, - initialCapacity, - }); - - expect(nextChallenges).to.deep.equal([easyChallenge, hardChallenge]); - }); - }); - }); }); }); @@ -331,7 +248,5 @@ const _getAlgorithmConfig = (options) => { const _getCapacityAndErrorRateParams = (params) => ({ variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, ...params, }); diff --git a/api/tests/integration/scripts/certification/next-gen/generate-flash-algorithm-configuration_test.js b/api/tests/integration/scripts/certification/next-gen/generate-flash-algorithm-configuration_test.js deleted file mode 100644 index af04ff93fa8..00000000000 --- a/api/tests/integration/scripts/certification/next-gen/generate-flash-algorithm-configuration_test.js +++ /dev/null @@ -1,29 +0,0 @@ -import _ from 'lodash'; - -import { main } from '../../../../../scripts/certification/next-gen/generate-flash-algorithm-configuration.js'; -import { expect, knex } from '../../../../test-helper.js'; - -describe('Integration | Scripts | Certification | generate-flash-algorithm-configuration', function () { - const TABLE_NAME = 'flash-algorithm-configurations'; - - it('should create a flash algorithm configuration', async function () { - // when - await main(); - - // then - const expectedFlashAlgorithmConfiguration = { - warmUpLength: null, - forcedCompetences: [], - maximumAssessmentLength: 32, - challengesBetweenSameCompetence: null, - minimumEstimatedSuccessRateRanges: [], - limitToOneQuestionPerTube: true, - enablePassageByAllCompetences: true, - doubleMeasuresUntil: null, - variationPercent: 0.5, - variationPercentUntil: null, - }; - const flashAlgorithmConfiguration = await knex(TABLE_NAME).first(); - expect(_.omit(flashAlgorithmConfiguration, ['id', 'createdAt'])).to.deep.equal(expectedFlashAlgorithmConfiguration); - }); -}); diff --git a/api/tests/tooling/domain-builder/factory/build-flash-algorithm-configuration.js b/api/tests/tooling/domain-builder/factory/build-flash-algorithm-configuration.js index caef8f0c00e..b4e95856513 100644 --- a/api/tests/tooling/domain-builder/factory/build-flash-algorithm-configuration.js +++ b/api/tests/tooling/domain-builder/factory/build-flash-algorithm-configuration.js @@ -1,32 +1,19 @@ -import { FlashAssessmentSuccessRateHandler } from '../../../../src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js'; import { FlashAssessmentAlgorithmConfiguration } from '../../../../src/certification/shared/domain/models/FlashAssessmentAlgorithmConfiguration.js'; export const buildFlashAlgorithmConfiguration = ({ - warmUpLength, - forcedCompetences, maximumAssessmentLength, challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges = [], limitToOneQuestionPerTube, enablePassageByAllCompetences, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, } = {}) => { return new FlashAssessmentAlgorithmConfiguration({ - warmUpLength, - forcedCompetences, maximumAssessmentLength, challengesBetweenSameCompetence, - minimumEstimatedSuccessRateRanges: minimumEstimatedSuccessRateRanges.map((successRateConfig) => - FlashAssessmentSuccessRateHandler.create(successRateConfig), - ), limitToOneQuestionPerTube, enablePassageByAllCompetences, - doubleMeasuresUntil, variationPercent, - variationPercentUntil, createdAt, }); }; diff --git a/api/tests/tooling/domain-builder/factory/build-flash-assessment-algorithm-success-rate-handler.js b/api/tests/tooling/domain-builder/factory/build-flash-assessment-algorithm-success-rate-handler.js deleted file mode 100644 index 21f35e0f351..00000000000 --- a/api/tests/tooling/domain-builder/factory/build-flash-assessment-algorithm-success-rate-handler.js +++ /dev/null @@ -1,9 +0,0 @@ -import { FlashAssessmentSuccessRateHandler } from '../../../../src/certification/flash-certification/domain/models/FlashAssessmentSuccessRateHandler.js'; - -export const buildFlashAssessmentAlgorithmSuccessRateHandlerFixed = ({ - startingChallengeIndex, - endingChallengeIndex, - value, -}) => { - return FlashAssessmentSuccessRateHandler.create({ startingChallengeIndex, endingChallengeIndex, value }); -}; diff --git a/api/tests/tooling/domain-builder/factory/index.js b/api/tests/tooling/domain-builder/factory/index.js index a11679e3edd..a969c5c1113 100644 --- a/api/tests/tooling/domain-builder/factory/index.js +++ b/api/tests/tooling/domain-builder/factory/index.js @@ -93,7 +93,6 @@ import * as buildDataProtectionOfficer from './build-data-protection-officer.js' import { buildFeedback } from './build-feedback.js'; import { buildFinalizedSession } from './build-finalized-session.js'; import { buildFlashAlgorithmConfiguration } from './build-flash-algorithm-configuration.js'; -import { buildFlashAssessmentAlgorithmSuccessRateHandlerFixed } from './build-flash-assessment-algorithm-success-rate-handler.js'; import { buildFramework } from './build-framework.js'; import { buildHabilitation } from './build-habilitation.js'; import { buildHint } from './build-hint.js'; @@ -359,7 +358,6 @@ export { buildFinalizedSession, buildFlashAlgorithmConfiguration, buildFlashAssessmentAlgorithm, - buildFlashAssessmentAlgorithmSuccessRateHandlerFixed, buildFramework, buildHint, buildJuryCertification, diff --git a/api/tests/unit/domain/usecases/get-next-challenge-for-campaign-assessment_test.js b/api/tests/unit/domain/usecases/get-next-challenge-for-campaign-assessment_test.js index 20fb6d355af..829184f85f2 100644 --- a/api/tests/unit/domain/usecases/get-next-challenge-for-campaign-assessment_test.js +++ b/api/tests/unit/domain/usecases/get-next-challenge-for-campaign-assessment_test.js @@ -152,8 +152,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge-for-campaign-assessment allAnswers, capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - variationPercentUntil: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0, @@ -164,7 +162,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge-for-campaign-assessment .withArgs({ availableChallenges: [secondChallenge], capacity: 0, - options: sinon.match.object, }) .returns([secondChallenge]); @@ -265,7 +262,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge-for-campaign-assessment allAnswers, capacity: config.v3Certification.defaultCandidateCapacity, variationPercent: undefined, - doubleMeasuresUntil: undefined, }) .returns({ capacity: 0, @@ -276,7 +272,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge-for-campaign-assessment .withArgs({ challenges, capacity: 0, - options: sinon.match.object, }) .returns({ hasAssessmentEnded: false,