Skip to content

Commit

Permalink
[FEATURE] Afficher la carte d'une mission avec un statut 'En cours' (P…
Browse files Browse the repository at this point in the history
  • Loading branch information
pix-service-auto-merge authored Aug 7, 2024
2 parents e625cf8 + c51dce8 commit 49a0588
Show file tree
Hide file tree
Showing 23 changed files with 256 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as organizationLearnerSerializer from '../infrastructure/serializers/or

const getById = async function (request) {
const organizationLearnerId = request.params.id;
const organizationLearner = await usecases.getOrganizationLearnerWithCompletedMissionIds({ organizationLearnerId });
const organizationLearner = await usecases.getOrganizationLearnerWithMissionIdsByState({ organizationLearnerId });
return organizationLearnerSerializer.serialize(organizationLearner);
};

Expand Down
3 changes: 2 additions & 1 deletion api/src/school/domain/models/OrganizationLearner.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
class OrganizationLearner {
constructor({ id, lastName, firstName, division, organizationId, completedMissionIds } = {}) {
constructor({ id, lastName, firstName, division, organizationId, completedMissionIds, startedMissionIds } = {}) {
this.id = id;
this.lastName = lastName;
this.firstName = firstName;
this.division = division;
this.organizationId = organizationId;
this.completedMissionIds = completedMissionIds;
this.startedMissionIds = startedMissionIds;
}
}

Expand Down
3 changes: 2 additions & 1 deletion api/src/school/domain/read-models/OrganizationLearnerDTO.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
class OrganizationLearnerDTO {
constructor({ id, displayName, firstName, division, organizationId, completedMissionIds } = {}) {
constructor({ id, displayName, firstName, division, organizationId, startedMissionIds, completedMissionIds } = {}) {
this.id = id;
this.displayName = displayName;
this.firstName = firstName;
this.division = division;
this.organizationId = organizationId;
this.startedMissionIds = startedMissionIds;
this.completedMissionIds = completedMissionIds;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const getOrganizationLearnerWithCompletedMissionIds = async function ({
const getOrganizationLearnerWithMissionIdsByState = async function ({
organizationLearnerId,
missionAssessmentRepository,
organizationLearnerRepository,
} = {}) {
const learner = await organizationLearnerRepository.getById({ organizationLearnerId });
learner.completedMissionIds = await missionAssessmentRepository.getAllCompletedMissionIds(organizationLearnerId);
const { started, completed } = await missionAssessmentRepository.getMissionIdsByState(organizationLearnerId);
learner.startedMissionIds = started;
learner.completedMissionIds = completed;
return learner;
};

export { getOrganizationLearnerWithCompletedMissionIds };
export { getOrganizationLearnerWithMissionIdsByState };
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,34 @@ const getStatusesForLearners = async function (missionId, organizationLearners,
return decoratedMissionLearners;
};

const getAllCompletedMissionIds = async function (organizationLearnerId) {
const raw = await knex('mission-assessments')
.select('mission-assessments.missionId')
const getMissionIdsByState = async function (organizationLearnerId) {
const missionAssessments = await knex('mission-assessments')
.select('mission-assessments.missionId', 'assessments.state', 'mission-assessments.createdAt')
.join('assessments', 'assessments.id', 'mission-assessments.assessmentId')
.where({ organizationLearnerId })
.andWhere({ state: Assessment.states.COMPLETED });
.join(
knex
.select('organizationLearnerId as learnerId', 'missionId')
.max('createdAt', { as: 'date' })
.from('mission-assessments')
.groupBy('organizationLearnerId', 'missionId')
.as('max_dates'),
function () {
this.on('mission-assessments.organizationLearnerId', '=', 'max_dates.learnerId')
.andOn('mission-assessments.createdAt', '=', 'max_dates.date')
.andOn('mission-assessments.missionId', '=', 'max_dates.missionId');
},
)
.where({ organizationLearnerId });

return raw.map((element) => element.missionId);
const missionIdsGroupByState = {};
missionAssessments.forEach((missionAssessment) => {
if (missionIdsGroupByState[missionAssessment.state]) {
missionIdsGroupByState[missionAssessment.state].push(missionAssessment.missionId);
} else {
missionIdsGroupByState[missionAssessment.state] = [missionAssessment.missionId];
}
});
return missionIdsGroupByState;
};

export { getAllCompletedMissionIds, getByAssessmentId, getCurrent, getStatusesForLearners, save };
export { getByAssessmentId, getCurrent, getMissionIdsByState, getStatusesForLearners, save };
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Serializer } from 'jsonapi-serializer';

const serialize = function (organizationLearner) {
return new Serializer('organizationLearner', {
attributes: ['firstName', 'displayName', 'division', 'organizationId', 'completedMissionIds'],
attributes: ['firstName', 'displayName', 'division', 'organizationId', 'completedMissionIds', 'startedMissionIds'],
transform: function (organizationLearner) {
return {
...organizationLearner,
completedMissionIds: organizationLearner.completedMissionIds.map((id) => id.toString()),
completedMissionIds: organizationLearner.completedMissionIds?.map((id) => id.toString()),
startedMissionIds: organizationLearner.startedMissionIds?.map((id) => id.toString()),
};
},
}).serialize(organizationLearner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Assessment } from '../../../../../src/shared/domain/models/index.js';
import { databaseBuilder, expect } from '../../../../test-helper.js';

describe('Integration | Usecase | get-organization-learner-with-completed-mission-ids', function () {
describe('#getOrganizationLearnerWithCompletedMissionIds', function () {
it('should return organization learner with completed mission ids', async function () {
describe('#getOrganizationLearnerWithMissionIdsByState', function () {
it('should return organization learner with mission ids by state', async function () {
const organizationLearner =
databaseBuilder.factory.prescription.organizationLearners.buildOndeOrganizationLearner();
const completedAssessmentId = databaseBuilder.factory.buildPix1dAssessment({
Expand All @@ -28,7 +28,7 @@ describe('Integration | Usecase | get-organization-learner-with-completed-missio
});
await databaseBuilder.commit();

const result = await usecases.getOrganizationLearnerWithCompletedMissionIds({
const result = await usecases.getOrganizationLearnerWithMissionIdsByState({
organizationLearnerId: organizationLearner.id,
});

Expand All @@ -37,6 +37,7 @@ describe('Integration | Usecase | get-organization-learner-with-completed-missio
...organizationLearner,
division: organizationLearner.attributes['Libellé classe'],
completedMissionIds: [completedMissionId],
startedMissionIds: [startedMissionId],
}),
);
});
Expand Down Expand Up @@ -69,7 +70,7 @@ describe('Integration | Usecase | get-organization-learner-with-completed-missio
});
await databaseBuilder.commit();

const result = await usecases.getOrganizationLearnerWithCompletedMissionIds({
const result = await usecases.getOrganizationLearnerWithMissionIdsByState({
organizationLearnerId: organizationLearner.id,
});
expect(result).to.deep.equal(
Expand All @@ -81,20 +82,19 @@ describe('Integration | Usecase | get-organization-learner-with-completed-missio
);
});

it('should return organization learner even without completed missions', async function () {
it('should return organization learner even without missions', async function () {
const organizationLearner =
databaseBuilder.factory.prescription.organizationLearners.buildOndeOrganizationLearner();

await databaseBuilder.commit();

const result = await usecases.getOrganizationLearnerWithCompletedMissionIds({
const result = await usecases.getOrganizationLearnerWithMissionIdsByState({
organizationLearnerId: organizationLearner.id,
});
expect(result).to.deep.equal(
new OrganizationLearner({
...organizationLearner,
division: organizationLearner.attributes['Libellé classe'],
completedMissionIds: [],
}),
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ describe('Integration | Repository | mission-assessment-repository', function ()
});
});

describe('#getAllCompletedMissionIds', function () {
it('should return a list of completed mission ids for a given organization learner', async function () {
describe('#getMissionIdsByState', function () {
it('should return mission ids for all mission assessment for given organization learner', async function () {
const organizationLearner = databaseBuilder.factory.buildOrganizationLearner();
const completedAssessmentId = databaseBuilder.factory.buildPix1dAssessment({
state: Assessment.states.COMPLETED,
Expand All @@ -87,11 +87,13 @@ describe('Integration | Repository | mission-assessment-repository', function ()
missionId: completedMissionId,
organizationLearnerId: organizationLearner.id,
assessmentId: completedAssessmentId,
createdAt: new Date('2022-07-07'),
});
databaseBuilder.factory.buildMissionAssessment({
missionId: otherCompletedMissionId,
organizationLearnerId: organizationLearner.id,
assessmentId: otherCompletedAssessmentId,
createdAt: new Date('2024-09-08'),
});
databaseBuilder.factory.buildMissionAssessment({
missionId: startedMissionId,
Expand All @@ -100,16 +102,52 @@ describe('Integration | Repository | mission-assessment-repository', function ()
});
await databaseBuilder.commit();

const result = await missionAssessmentRepository.getAllCompletedMissionIds(organizationLearner.id);
expect(result).to.deep.equal([completedMissionId, otherCompletedMissionId]);
const result = await missionAssessmentRepository.getMissionIdsByState(organizationLearner.id);
expect(result).to.deep.equal({
completed: [completedMissionId, otherCompletedMissionId],
started: [startedMissionId],
});
});

it('should return organization learner even without completed missions', async function () {
it('should return organization learner even without missions', async function () {
const organizationLearner = databaseBuilder.factory.buildOrganizationLearner();
await databaseBuilder.commit();

const result = await missionAssessmentRepository.getAllCompletedMissionIds(organizationLearner.id);
expect(result).to.deep.equal([]);
const result = await missionAssessmentRepository.getMissionIdsByState(organizationLearner.id);
expect(result).to.deep.equal({});
});

it('should group on last assessment state', async function () {
const missionId = 789;

const organizationLearner = databaseBuilder.factory.buildOrganizationLearner();
const completedAssessmentId = databaseBuilder.factory.buildPix1dAssessment({
state: Assessment.states.COMPLETED,
createdAt: new Date('2024-06-21'),
}).id;
const startedAssessmentId = databaseBuilder.factory.buildPix1dAssessment({
state: Assessment.states.STARTED,
createdAt: new Date('2024-07-21'),
}).id;

databaseBuilder.factory.buildMissionAssessment({
missionId: missionId,
organizationLearnerId: organizationLearner.id,
assessmentId: completedAssessmentId,
createdAt: new Date('2024-06-21'),
});
databaseBuilder.factory.buildMissionAssessment({
missionId: missionId,
organizationLearnerId: organizationLearner.id,
assessmentId: startedAssessmentId,
createdAt: new Date('2024-07-21'),
});
await databaseBuilder.commit();

const result = await missionAssessmentRepository.getMissionIdsByState(organizationLearner.id);
expect(result).to.deep.equal({
started: [missionId],
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { expect, hFake, sinon } from '../../../test-helper.js';
describe('Unit | Controller | organization-learner-controller', function () {
describe('#getById', function () {
it('should return the serialized organization-learner with completed mission ids', async function () {
sinon.stub(usecases, 'getOrganizationLearnerWithCompletedMissionIds');
usecases.getOrganizationLearnerWithCompletedMissionIds.resolves(
sinon.stub(usecases, 'getOrganizationLearnerWithMissionIdsByState');
usecases.getOrganizationLearnerWithMissionIdsByState.resolves(
new OrganizationLearnerDTO({
id: '4356',
completedMissionIds: ['rec12344', 'rec435'],
firstName: 'Edward',
division: 'CM2',
organizationId: '345',
completedMissionIds: ['rec12344', 'rec435'],
startedMissionIds: undefined,
}),
);
const id = 4356;
Expand All @@ -27,6 +28,7 @@ describe('Unit | Controller | organization-learner-controller', function () {
data: {
attributes: {
'completed-mission-ids': ['rec12344', 'rec435'],
'started-mission-ids': undefined,
'first-name': 'Edward',
'display-name': undefined,
division: 'CM2',
Expand All @@ -37,7 +39,7 @@ describe('Unit | Controller | organization-learner-controller', function () {
},
};

expect(usecases.getOrganizationLearnerWithCompletedMissionIds).to.have.been.calledWith({
expect(usecases.getOrganizationLearnerWithMissionIdsByState).to.have.been.calledWith({
organizationLearnerId: id,
});
expect(response).to.deep.equal(expectedOrganizationLearner);
Expand Down
2 changes: 2 additions & 0 deletions api/tests/school/unit/application/school-controller_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('Unit | Controller | school-controller', function () {
firstName: 'Bob',
lastName: 'Dylan',
completedMissionIds: [],
startedMissionIds: [],
}),
],
}),
Expand All @@ -51,6 +52,7 @@ describe('Unit | Controller | school-controller', function () {
displayName: 'Bob',
organizationId: 1,
completedMissionIds: [],
startedMissionIds: [],
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('Unit | Serializer | JSONAPI | organization-learner', function () {
lastName: 'Miche',
division: 'CM3',
completedMissionIds: [1],
startedMissionIds: [2],
});

const expectedJSON = {
Expand All @@ -23,6 +24,7 @@ describe('Unit | Serializer | JSONAPI | organization-learner', function () {
division: 'CM3',
'organization-id': 'orga-1',
'completed-mission-ids': ['1'],
'started-mission-ids': ['2'],
},
},
};
Expand Down
4 changes: 2 additions & 2 deletions junior/app/components/mission-card/completed-mission-card.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import Background from './mission-card-completed-background';
<template>
<div class="mission-card__container">
{{#if @missionLabelStatus}}
<div class="status">
<div class="status status--completed">
<p>{{@missionLabelStatus}}</p>
</div>
{{/if}}

<div class="mission-icon--completed">
<img src="images/mission/icon/finished-icon.svg" alt="mission-icon" />
<img src="/images/mission/icon/finished-icon.svg" alt="" />
</div>
<Background @areaCode={{@areaCode}} />
<div class="completed-mission-card-bottom">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<svg
class={{if @class @class ""}}
width="233"
height="231"
viewBox="0 0 233 231"
height="210"
viewBox="0 0 233 210"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
Expand Down
9 changes: 6 additions & 3 deletions junior/app/components/mission-card/mission-card.gjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import MissionCardBackGround from './mission-card-background';

<template>
<div class="mission-card__container">
{{#if @missionLabelStatus}}
<div class="status">

<p>{{@missionLabelStatus}}</p>
</div>
{{/if}}
{{#if @displayStartedIcon}}
<div class="started-icon">
<img src="/images/mission/icon/started-icon.svg" alt="" />
</div>
{{/if}}

<div class="mission-icon">
<img src="/images/mission/icon/area-code-{{@areaCode}}.svg" alt="mission-icon" />
<img src="/images/mission/icon/area-code-{{@areaCode}}.svg" alt="" />
</div>

<MissionCardBackGround @class="area-code-{{@areaCode}}" @areaCode={{@areaCode}} />
Expand Down
Loading

0 comments on commit 49a0588

Please sign in to comment.