Skip to content

Commit

Permalink
[FEATURE] Ajouter une propriété "evolution" dans la réponse de l'API …
Browse files Browse the repository at this point in the history
…pour les résultats d'une campagne Évaluation (PIX-14806)

 #10343
  • Loading branch information
pix-service-auto-merge authored Oct 21, 2024
2 parents 8788cde + 728fb0c commit d8c5118
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import _ from 'lodash';

class CampaignAssessmentParticipationResultMinimal {
#previousMasteryRate;
constructor({
campaignParticipationId,
firstName,
lastName,
participantExternalId,
masteryRate,
previousMasteryRate,
reachedStage,
totalStage,
prescriberTitle,
Expand All @@ -19,14 +21,22 @@ class CampaignAssessmentParticipationResultMinimal {
this.lastName = lastName;
this.participantExternalId = participantExternalId;
this.masteryRate = !_.isNil(masteryRate) ? Number(masteryRate) : null;
this.masteryRate = !_.isNil(masteryRate) ? Number(masteryRate) : null;
this.#previousMasteryRate = !_.isNil(previousMasteryRate) ? Number(previousMasteryRate) : null;
this.reachedStage = reachedStage;
this.totalStage = totalStage;
this.prescriberTitle = prescriberTitle;
this.prescriberDescription = prescriberDescription;
//TODO REMOVE WHEN https://1024pix.atlassian.net/browse/PIX-6849 IS DONE
this.badges = _.uniqBy(badges, 'id');
this.sharedResultCount = sharedResultCount;
this.evolution = this.#computeEvolution();
}

#computeEvolution() {
if ([this.#previousMasteryRate, this.masteryRate].includes(null)) return null;
if (this.masteryRate > this.#previousMasteryRate) return 'increase';
if (this.masteryRate < this.#previousMasteryRate) return 'decrease';
if (this.masteryRate === this.#previousMasteryRate) return 'stable';
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import { CampaignAssessmentParticipationResultMinimal } from '../../domain/read-

const { SHARED } = CampaignParticipationStatuses;

async function findPaginatedByCampaignId({ page = {}, campaignId, filters = {} }) {
const stageCollection = await stageCollectionRepository.findStageCollection({ campaignId });
async function findPaginatedByCampaignId({
page = {},
campaignId,
filters = {},
dependencies = { stageCollectionRepository },
}) {
const stageCollection = await dependencies.stageCollectionRepository.findStageCollection({ campaignId });

const { results, pagination } = await _getResultListPaginated(campaignId, stageCollection, filters, page);
const participations = await _buildCampaignAssessmentParticipationResultList(results, stageCollection);
Expand Down Expand Up @@ -47,6 +52,16 @@ function _getParticipations(qb, campaignId, stageCollection, filters) {
.where('campaign-participations.status', SHARED)
.whereNull('campaign-participations.deletedAt')
.as('sharedResultCount'),
knex('campaign-participations')
.select('masteryRate')
.whereRaw('"organizationLearnerId" = "view-active-organization-learners".id')
.where('campaign-participations.campaignId', campaignId)
.where('campaign-participations.status', SHARED)
.whereNull('campaign-participations.deletedAt')
.orderBy('sharedAt', 'desc')
.offset(1)
.limit(1)
.as('previousMasteryRate'),
)
.distinctOn('campaign-participations.organizationLearnerId')
.from('campaign-participations')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const serialize = function ({ participations, pagination }) {
'lastName',
'participantExternalId',
'masteryRate',
'evolution',
'reachedStage',
'totalStage',
'prescriberTitle',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import * as campaignAssessmentParticipationResultListRepository from '../../../../../../src/prescription/campaign/infrastructure/repositories/campaign-assessment-participation-result-list-repository.js';
import { CampaignParticipationStatuses } from '../../../../../../src/prescription/shared/domain/constants.js';
import {
CampaignParticipationStatuses,
CampaignTypes,
} from '../../../../../../src/prescription/shared/domain/constants.js';
import { databaseBuilder, expect, learningContentBuilder, mockLearningContent } from '../../../../../test-helper.js';

const { STARTED } = CampaignParticipationStatuses;
Expand Down Expand Up @@ -385,6 +388,168 @@ describe('Integration | Repository | Campaign Assessment Participation Result Li
});
});

context('evolution', function () {
let userId, organizationLearnerId;
let organizationId;
beforeEach(async function () {
userId = databaseBuilder.factory.buildUser({}).id;

organizationId = databaseBuilder.factory.buildOrganization().id;

organizationLearnerId = databaseBuilder.factory.prescription.organizationLearners.buildOrganizationLearner({
firstName: 'Sarah',
lastName: 'Croche',
organizationId,
userId,
}).id;

campaign = databaseBuilder.factory.buildCampaign({
type: CampaignTypes.ASSESSMENT,
organizationId,
multipleSendings: true,
});

databaseBuilder.factory.buildCampaignParticipation({
campaignId: campaign.id,
userId,
organizationLearnerId,
masteryRate: 0.33,
isImproved: false,
createdAt: new Date('2024-01-05'),
sharedAt: new Date('2024-01-06'),
status: CampaignParticipationStatuses.SHARED,
});

await databaseBuilder.commit();
});

it('should compute correct evolution for participations', async function () {
// given
databaseBuilder.factory.buildCampaignParticipation({
campaignId: campaign.id,
userId,
organizationLearnerId,
masteryRate: 0.66,
isImproved: true,
createdAt: new Date('2024-01-01'),
sharedAt: new Date('2024-01-02'),
status: CampaignParticipationStatuses.SHARED,
});

databaseBuilder.factory.buildCampaignParticipation({
campaignId: campaign.id,
userId,
organizationLearnerId,
masteryRate: 0,
isImproved: true,
createdAt: new Date('2024-01-03'),
sharedAt: new Date('2024-01-04'),
status: CampaignParticipationStatuses.SHARED,
});

await databaseBuilder.commit();

// when
const { participations } = await campaignAssessmentParticipationResultListRepository.findPaginatedByCampaignId({
campaignId: campaign.id,
});

// then
expect(participations[0].evolution).to.equal('increase');
});

it('should return evolution null when previous participation is deleted', async function () {
// given
databaseBuilder.factory.buildCampaignParticipation({
campaignId: campaign.id,
userId,
organizationLearnerId,
masteryRate: 0,
isImproved: true,
createdAt: new Date('2024-01-03'),
sharedAt: new Date('2024-01-04'),
deletedAt: new Date('2024-01-05'),
status: CampaignParticipationStatuses.SHARED,
});

await databaseBuilder.commit();

// when
const { participations } = await campaignAssessmentParticipationResultListRepository.findPaginatedByCampaignId({
campaignId: campaign.id,
});

// then
expect(participations[0].evolution).to.equal(null);
});

it('should return evolution null when participation does not belong to learner', async function () {
// given
const otherUserId = databaseBuilder.factory.buildUser({}).id;

const otherOrganizationLearnerId =
databaseBuilder.factory.prescription.organizationLearners.buildOrganizationLearner({
firstname: 'John',
lastname: 'Doe',
userId: otherUserId,
organizationId,
}).id;

databaseBuilder.factory.buildCampaignParticipation({
campaignId: campaign.id,
userId: otherUserId,
organizationLearnerId: otherOrganizationLearnerId,
masteryRate: 0,
isImproved: false,
createdAt: new Date('2024-01-03'),
sharedAt: new Date('2024-01-04'),
status: CampaignParticipationStatuses.SHARED,
});

await databaseBuilder.commit();

// when
const { participations } = await campaignAssessmentParticipationResultListRepository.findPaginatedByCampaignId({
campaignId: campaign.id,
});

// then
expect(participations).lengthOf(2);
expect(participations[0].evolution).to.equal(null);
expect(participations[1].evolution).to.equal(null);
});

it('should return evolution null when learner has 2 participations to different campaigns', async function () {
// given
const otherCampaign = databaseBuilder.factory.buildCampaign({
type: CampaignTypes.ASSESSMENT,
organizationId,
multipleSendings: true,
});

databaseBuilder.factory.buildCampaignParticipation({
campaignId: otherCampaign.id,
userId,
organizationLearnerId,
masteryRate: 0.66,
isImproved: false,
createdAt: new Date('2024-01-03'),
sharedAt: new Date('2024-01-04'),
status: CampaignParticipationStatuses.SHARED,
});

await databaseBuilder.commit();

// when
const { participations } = await campaignAssessmentParticipationResultListRepository.findPaginatedByCampaignId({
campaignId: campaign.id,
});

// then
expect(participations[0].evolution).to.equal(null);
});
});

context('order', function () {
it('should return participants data summary ordered by last name then first name asc (including organization learner data)', async function () {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('Unit | Domain | Read-Models | CampaignResults | CampaignAssessmentPart
lastName: 'Aguilar',
badges: [],
masteryRate: 0.45,
evolution: null,
participantExternalId: 'Alba67',
reachedStage: 2,
totalStage: 6,
Expand Down Expand Up @@ -83,6 +84,73 @@ describe('Unit | Domain | Read-Models | CampaignResults | CampaignAssessmentPart
});
});

describe('evolution', function () {
context('when the previousMasteryRate is null', function () {
it('should return null for the evolution', function () {
// when
const campaignAssessmentParticipationResultMinimal = new CampaignAssessmentParticipationResultMinimal({
previousMasteryRate: null,
masteryRate: 0.18,
});

// then
expect(campaignAssessmentParticipationResultMinimal.evolution).to.equal(null);
});
});

context('when the masteryRate is null', function () {
it('should return null for the evolution', function () {
// when
const campaignAssessmentParticipationResultMinimal = new CampaignAssessmentParticipationResultMinimal({
previousMasteryRate: 0.18,
masteryRate: null,
});

// then
expect(campaignAssessmentParticipationResultMinimal.evolution).to.equal(null);
});
});

context('when the masteryRate is superior to the previousMasteryRate', function () {
it('should return increase for the evolution', function () {
// when
const campaignAssessmentParticipationResultMinimal = new CampaignAssessmentParticipationResultMinimal({
masteryRate: 0.8,
previousMasteryRate: 0.5,
});

// then
expect(campaignAssessmentParticipationResultMinimal.evolution).to.equal('increase');
});
});

context('when the masteryRate is inferior to the previousMasteryRate', function () {
it('should return decrease for the evolution', function () {
// when
const campaignAssessmentParticipationResultMinimal = new CampaignAssessmentParticipationResultMinimal({
masteryRate: 0.5,
previousMasteryRate: 0.8,
});

// then
expect(campaignAssessmentParticipationResultMinimal.evolution).to.equal('decrease');
});
});

context('when the masteryRate is equal to the previousMasteryRate', function () {
it('should return stable for the evolution', function () {
// when
const campaignAssessmentParticipationResultMinimal = new CampaignAssessmentParticipationResultMinimal({
masteryRate: 0.8,
previousMasteryRate: 0.8,
});

// then
expect(campaignAssessmentParticipationResultMinimal.evolution).to.equal('stable');
});
});
});

describe('badges', function () {
it('keeps only once each badge', function () {
// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('Unit | Serializer | JSONAPI | campaign-assessment-result-minimal-seria
lastName: 'McClane',
participantExternalId: 'Cop',
masteryRate: 1,
evolution: 'toto',
reachedStage: 2,
totalStage: 6,
prescriberTitle: 'titre prescripteur 1',
Expand All @@ -24,6 +25,7 @@ describe('Unit | Serializer | JSONAPI | campaign-assessment-result-minimal-seria
lastName: 'Gruber',
participantExternalId: 'Thief',
masteryRate: 0.99,
evolution: null,
reachedStage: null,
totalStage: null,
prescriberTitle: null,
Expand Down Expand Up @@ -51,6 +53,7 @@ describe('Unit | Serializer | JSONAPI | campaign-assessment-result-minimal-seria
'last-name': 'McClane',
'participant-external-id': 'Cop',
'mastery-rate': 1,
evolution: 'toto',
'reached-stage': 2,
'total-stage': 6,
'prescriber-title': 'titre prescripteur 1',
Expand All @@ -66,6 +69,7 @@ describe('Unit | Serializer | JSONAPI | campaign-assessment-result-minimal-seria
'last-name': 'Gruber',
'participant-external-id': 'Thief',
'mastery-rate': 0.99,
evolution: null,
'reached-stage': null,
'total-stage': null,
'prescriber-title': null,
Expand Down

0 comments on commit d8c5118

Please sign in to comment.