Skip to content

Commit

Permalink
[BUGFIX] Gérer une épreuve qui est périmée en cours de test de certif…
Browse files Browse the repository at this point in the history
…ication (PIX-14819).

 #10403
  • Loading branch information
pix-service-auto-merge authored Oct 24, 2024
2 parents 7a30393 + fb8c1ba commit 4bc4441
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ const getNextChallenge = async function ({
}) {
const certificationCourse = await certificationCourseRepository.get({ id: assessment.certificationCourseId });

const alreadyAnsweredChallengeIds = await _getAlreadyAnsweredChallengeIds({
assessmentId: assessment.id,
answerRepository,
});
const allAnswers = await answerRepository.findByAssessment(assessment.id);
const alreadyAnsweredChallengeIds = allAnswers.map(({ challengeId }) => challengeId);

const validatedLiveAlertChallengeIds = await _getValidatedLiveAlertChallengeIds({
assessmentId: assessment.id,
Expand All @@ -65,11 +63,10 @@ const getNextChallenge = async function ({
return challengeRepository.get(lastNonAnsweredCertificationChallenge.challengeId);
}

const [allAnswers, challenges] = await Promise.all([
answerRepository.findByAssessment(assessment.id),
challengeRepository.findActiveFlashCompatible({ locale }),
]);
const activeFlashCompatibleChallenges = await challengeRepository.findActiveFlashCompatible({ locale });

const alreadyAnsweredChallenges = await challengeRepository.getMany(alreadyAnsweredChallengeIds, locale);
const challenges = [...new Set([...alreadyAnsweredChallenges, ...activeFlashCompatibleChallenges])];
const algorithmConfiguration = await flashAlgorithmConfigurationRepository.getMostRecentBeforeDate(
certificationCourse.getStartDate(),
);
Expand Down Expand Up @@ -140,13 +137,6 @@ const _excludeChallengesWithASkillWithAValidatedLiveAlert = ({ validatedLiveAler
return challengesWithoutSkillsWithAValidatedLiveAlert;
};

const _getAlreadyAnsweredChallengeIds = async ({ assessmentId, answerRepository }) => {
const answers = await answerRepository.findByAssessment(assessmentId);
const alreadyAnsweredChallengeIds = answers.map(({ challengeId }) => challengeId);

return alreadyAnsweredChallengeIds;
};

const _getValidatedLiveAlertChallengeIds = async ({ assessmentId, certificationChallengeLiveAlertRepository }) => {
return certificationChallengeLiveAlertRepository.getLiveAlertValidatedChallengeIdsByAssessmentId({ assessmentId });
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { logger } from '../../../../shared/infrastructure/utils/logger.js';

export class FlashAssessmentAlgorithmChallengesBetweenCompetencesRule {
static isApplicable({ challengesBetweenSameCompetence }) {
return challengesBetweenSameCompetence > 0;
Expand Down Expand Up @@ -33,6 +35,10 @@ export class FlashAssessmentAlgorithmChallengesBetweenCompetencesRule {
}

static _findChallengeForAnswer(challenges, answer) {
return challenges.find((challenge) => challenge.id === answer.challengeId);
const challengeAssociatedToAnswer = challenges.find((challenge) => challenge.id === answer.challengeId);
if (!challengeAssociatedToAnswer) {
logger.warn({ answer }, 'Cannot find a challenge associated to answer.challengeId');
}
return challengeAssociatedToAnswer;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { logger } from '../../../../shared/infrastructure/utils/logger.js';

export class FlashAssessmentAlgorithmNonAnsweredSkillsRule {
static isApplicable({ limitToOneQuestionPerTube }) {
return !limitToOneQuestionPerTube;
Expand All @@ -17,6 +19,10 @@ export class FlashAssessmentAlgorithmNonAnsweredSkillsRule {
}

static _findChallengeForAnswer(challenges, answer) {
return challenges.find((challenge) => challenge.id === answer.challengeId);
const challengeAssociatedToAnswer = challenges.find((challenge) => challenge.id === answer.challengeId);
if (!challengeAssociatedToAnswer) {
logger.warn({ answer }, 'Cannot find a challenge associated to answer.challengeId');
}
return challengeAssociatedToAnswer;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import lodash from 'lodash';

import { logger } from '../../../../../shared/infrastructure/utils/logger.js';

const { orderBy, range, sortBy, sortedUniqBy } = lodash;

const DEFAULT_CAPACITY = 0;
Expand Down Expand Up @@ -300,7 +302,11 @@ function getChallengePriorityForInferrence(challenge) {
}

function _findChallengeForAnswer(challenges, answer) {
return challenges.find((challenge) => challenge.id === answer.challengeId);
const challengeAssociatedToAnswer = challenges.find((challenge) => challenge.id === answer.challengeId);
if (!challengeAssociatedToAnswer) {
logger.warn({ answer }, 'Cannot find a challenge associated to answer.challengeId');
}
return challengeAssociatedToAnswer;
}

function _sumPixScoreAndScoreByCompetence(challenges) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getNextChallenge } from '../../../../../../src/certification/evaluation/domain/usecases/get-next-challenge.js';
import { AlgorithmEngineVersion } from '../../../../../../src/certification/shared/domain/models/AlgorithmEngineVersion.js';
import { SESSIONS_VERSIONS } from '../../../../../../src/certification/shared/domain/models/SessionVersion.js';
import { config } from '../../../../../../src/shared/config.js';
import { AssessmentEndedError } from '../../../../../../src/shared/domain/errors.js';
Expand Down Expand Up @@ -29,6 +30,7 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
};
challengeRepository = {
get: sinon.stub(),
getMany: sinon.stub(),
findActiveFlashCompatible: sinon.stub(),
};
certificationCourseRepository = {
Expand Down Expand Up @@ -90,8 +92,8 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
.resolves(null);
challengeRepository.get.resolves();

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
challengeRepository.findActiveFlashCompatible.withArgs({ locale }).resolves([nextChallengeToAnswer]);
challengeRepository.getMany.withArgs([], locale).resolves([]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
Expand Down Expand Up @@ -180,8 +182,8 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
.withArgs(assessment.certificationCourseId, [])
.resolves(null);

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
challengeRepository.findActiveFlashCompatible.withArgs({ locale }).resolves(allChallenges);
challengeRepository.getMany.withArgs([], locale).resolves([]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
Expand Down Expand Up @@ -292,6 +294,102 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
});
});

context('when some answered challenges are not valid anymore', function () {
it('saves next challenge', async function () {
// given
const nextChallengeToAnswer = domainBuilder.buildChallenge({
id: 'nextChallengeToAnswer',
blindnessCompatibility: 'KO',
status: 'validé',
skill: domainBuilder.buildSkill({ id: 'nottAnsweredSkill' }),
});
const alreadyAnsweredChallenge = domainBuilder.buildChallenge({
id: 'alreadyAnsweredChallenge',
status: 'validé',
});
const outdatedChallenge = domainBuilder.buildChallenge({ id: 'outdatedChallenge', status: 'périmé' });
const v3CertificationCourse = domainBuilder.buildCertificationCourse({
version: AlgorithmEngineVersion.V3,
});
const locale = 'fr-FR';

flashAlgorithmConfigurationRepository.getMostRecentBeforeDate
.withArgs(v3CertificationCourse.getStartDate())
.resolves(flashAlgorithmConfiguration);

const answerStillValid = domainBuilder.buildAnswer({ challengeId: alreadyAnsweredChallenge.id });
const answerWithOutdatedChallenge = domainBuilder.buildAnswer({ challengeId: outdatedChallenge.id });
answerRepository.findByAssessment
.withArgs(assessment.id)
.resolves([answerStillValid, answerWithOutdatedChallenge]);

certificationChallengeLiveAlertRepository.getLiveAlertValidatedChallengeIdsByAssessmentId
.withArgs({ assessmentId: assessment.id })
.resolves([]);

certificationCourseRepository.get
.withArgs({ id: assessment.certificationCourseId })
.resolves(v3CertificationCourse);
certificationChallengeRepository.getNextChallengeByCourseIdForV3
.withArgs(assessment.certificationCourseId, [])
.resolves(null);
challengeRepository.get.resolves();

challengeRepository.findActiveFlashCompatible
.withArgs({ locale })
.resolves([alreadyAnsweredChallenge, nextChallengeToAnswer]);

challengeRepository.getMany
.withArgs([alreadyAnsweredChallenge.id, outdatedChallenge.id], locale)
.resolves([alreadyAnsweredChallenge, outdatedChallenge]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
allAnswers: [answerStillValid, answerWithOutdatedChallenge],
challenges: [alreadyAnsweredChallenge, outdatedChallenge, nextChallengeToAnswer],
capacity: config.v3Certification.defaultCandidateCapacity,
variationPercent: undefined,
variationPercentUntil: undefined,
doubleMeasuresUntil: undefined,
})
.returns({ capacity: 0 });

flashAlgorithmService.getPossibleNextChallenges
.withArgs({
availableChallenges: [nextChallengeToAnswer],
capacity: 0,
options: sinon.match.any,
})
.returns([nextChallengeToAnswer]);

const chooseNextChallengeImpl = sinon.stub();
chooseNextChallengeImpl
.withArgs({
possibleChallenges: [nextChallengeToAnswer],
})
.returns(nextChallengeToAnswer);
pickChallengeService.chooseNextChallenge.withArgs().returns(chooseNextChallengeImpl);

// when
const challenge = await getNextChallenge({
answerRepository,
assessment,
certificationChallengeRepository,
certificationChallengeLiveAlertRepository,
certificationCourseRepository,
challengeRepository,
flashAlgorithmConfigurationRepository,
flashAlgorithmService,
locale,
pickChallengeService,
certificationCandidateRepository,
});

// then
expect(challenge).to.equal(nextChallengeToAnswer);
});
});

context('when there are challenges with validated live alerts', function () {
it('should save the returned next challenge', async function () {
// given
Expand Down Expand Up @@ -322,6 +420,7 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
challengeRepository.findActiveFlashCompatible.withArgs({ locale }).resolves([nextChallenge, lastSeenChallenge]);
challengeRepository.getMany.withArgs([], locale).resolves([]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
Expand All @@ -342,7 +441,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
})
.returns([nextChallenge]);

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
certificationChallengeLiveAlertRepository.getLiveAlertValidatedChallengeIdsByAssessmentId
.withArgs({ assessmentId: assessment.id })
.resolves([nonAnsweredCertificationChallenge.challengeId]);
Expand Down Expand Up @@ -420,6 +518,7 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
challengeRepository.findActiveFlashCompatible
.withArgs()
.resolves([challengeWithLiveAlert, challengeWithOtherSkill, challengeWithLiveAlertedSkill]);
challengeRepository.getMany.withArgs([], locale).resolves([]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
Expand All @@ -440,7 +539,6 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
})
.returns([challengeWithOtherSkill]);

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
certificationChallengeLiveAlertRepository.getLiveAlertValidatedChallengeIdsByAssessmentId
.withArgs({ assessmentId: assessment.id })
.resolves([nonAnsweredCertificationChallenge.challengeId]);
Expand Down Expand Up @@ -515,8 +613,8 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
.resolves(null);
challengeRepository.get.resolves();

answerRepository.findByAssessment.withArgs(assessment.id).resolves([answer]);
challengeRepository.findActiveFlashCompatible.withArgs({ locale }).resolves([answeredChallenge]);
challengeRepository.getMany.withArgs([answeredChallenge.id], locale).resolves([answeredChallenge]);

// when
const error = await catchErr(getNextChallenge)({
Expand Down Expand Up @@ -594,8 +692,8 @@ describe('Unit | Domain | Use Cases | get-next-challenge', function () {
.resolves(null);
challengeRepository.get.resolves();

answerRepository.findByAssessment.withArgs(assessment.id).resolves([]);
challengeRepository.findActiveFlashCompatible.withArgs({ locale }).resolves([nextChallengeToAnswer]);
challengeRepository.getMany.withArgs([], locale).resolves([]);

flashAlgorithmService.getCapacityAndErrorRate
.withArgs({
Expand Down

0 comments on commit 4bc4441

Please sign in to comment.