Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE][BACK] Exposer les meta données pour la reconciliation des imports à format (PIX-13234) #9414

Merged
merged 11 commits into from
Jul 8, 2024
128 changes: 66 additions & 62 deletions api/db/seeds/data/common/tooling/campaign-tooling.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,74 +305,78 @@ async function createProfilesCollectionCampaign({
multipleSendings,
assessmentMethod,
});
const userAndLearnerIds = await _createOrRetrieveUsersAndLearners(
databaseBuilder,
realOrganizationId,
configCampaign.participantCount,
);
const profileDistribution = [
...Array(configCampaign.profileDistribution.blank || 0).fill('BLANK'),
...Array(configCampaign.profileDistribution.beginner || 0).fill('BEGINNER'),
...Array(configCampaign.profileDistribution.intermediate || 0).fill('INTERMEDIATE'),
...Array(configCampaign.profileDistribution.advanced || 0).fill('ADVANCED'),
...Array(configCampaign.profileDistribution.perfect || 0).fill('PERFECT'),
];
if (profileDistribution.length < configCampaign.participantCount)
profileDistribution.push(...Array(configCampaign.participantCount - profileDistribution.length).fill('BEGINNER'));

for (const { userId, organizationLearnerId } of userAndLearnerIds) {
const answersAndKnowledgeElementsForProfile = await _getProfile(profileDistribution.shift());

const campaignParticipationId = databaseBuilder.factory.buildCampaignParticipation({
campaignId: realCampaignId,
userId,
organizationLearnerId,
sharedAt,
masteryRate: null,
pixScore: _.floor(_.sumBy(answersAndKnowledgeElementsForProfile, ({ keData }) => keData.earnedPix)),
status: CampaignParticipationStatuses.SHARED,
isImproved: false,
}).id;
const assessmentId = databaseBuilder.factory.buildAssessment({
userId,
type: Assessment.types.CAMPAIGN,
state: Assessment.states.COMPLETED,
isImproving: false,
lastQuestionDate: new Date(),
lastQuestionState: Assessment.statesOfLastQuestion.ASKED,
competenceId: null,
campaignParticipationId,
}).id;
const keDataForSnapshot = [];
for (const { answerData, keData } of answersAndKnowledgeElementsForProfile) {
const answerId = databaseBuilder.factory.buildAnswer({
assessmentId,
answerData,
if (configCampaign) {
const userAndLearnerIds = await _createOrRetrieveUsersAndLearners(
databaseBuilder,
realOrganizationId,
configCampaign.participantCount,
);
const profileDistribution = [
...Array(configCampaign.profileDistribution.blank || 0).fill('BLANK'),
...Array(configCampaign.profileDistribution.beginner || 0).fill('BEGINNER'),
...Array(configCampaign.profileDistribution.intermediate || 0).fill('INTERMEDIATE'),
...Array(configCampaign.profileDistribution.advanced || 0).fill('ADVANCED'),
...Array(configCampaign.profileDistribution.perfect || 0).fill('PERFECT'),
];
if (profileDistribution.length < configCampaign.participantCount)
profileDistribution.push(...Array(configCampaign.participantCount - profileDistribution.length).fill('BEGINNER'));

for (const { userId, organizationLearnerId } of userAndLearnerIds) {
const answersAndKnowledgeElementsForProfile = await _getProfile(profileDistribution.shift());

const campaignParticipationId = databaseBuilder.factory.buildCampaignParticipation({
campaignId: realCampaignId,
userId,
organizationLearnerId,
sharedAt,
masteryRate: null,
pixScore: _.floor(_.sumBy(answersAndKnowledgeElementsForProfile, ({ keData }) => keData.earnedPix)),
status: CampaignParticipationStatuses.SHARED,
isImproved: false,
}).id;
keDataForSnapshot.push(
databaseBuilder.factory.buildKnowledgeElement({
const assessmentId = databaseBuilder.factory.buildAssessment({
userId,
type: Assessment.types.CAMPAIGN,
state: Assessment.states.COMPLETED,
isImproving: false,
lastQuestionDate: new Date(),
lastQuestionState: Assessment.statesOfLastQuestion.ASKED,
competenceId: null,
campaignParticipationId,
}).id;
const keDataForSnapshot = [];
for (const { answerData, keData } of answersAndKnowledgeElementsForProfile) {
const answerId = databaseBuilder.factory.buildAnswer({
assessmentId,
answerId,
userId,
...keData,
createdAt: dayjs().subtract(1, 'day'),
}),
);
}
databaseBuilder.factory.buildKnowledgeElementSnapshot({
userId,
snappedAt: sharedAt,
snapshot: JSON.stringify(keDataForSnapshot),
});
answerData,
}).id;
keDataForSnapshot.push(
databaseBuilder.factory.buildKnowledgeElement({
assessmentId,
answerId,
userId,
...keData,
createdAt: dayjs().subtract(1, 'day'),
}),
);
}
databaseBuilder.factory.buildKnowledgeElementSnapshot({
userId,
snappedAt: sharedAt,
snapshot: JSON.stringify(keDataForSnapshot),
});

await databaseBuilder.commit();
const placementProfile = await getPlacementProfile({ userId, limitDate: sharedAt });
await databaseBuilder.commit();
const placementProfile = await getPlacementProfile({ userId, limitDate: sharedAt });

await databaseBuilder
.knex('campaign-participations')
.where('id', campaignParticipationId)
.update('isCertifiable', placementProfile.isCertifiable());
await databaseBuilder
.knex('campaign-participations')
.where('id', campaignParticipationId)
.update('isCertifiable', placementProfile.isCertifiable());
}
}

await databaseBuilder.commit();
return { campaignId: realCampaignId };
}
Expand Down
39 changes: 27 additions & 12 deletions api/db/seeds/data/team-prescription/build-campaigns.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import dayjs from 'dayjs';
import { CampaignParticipationStatuses } from '../../../../src/prescription/shared/domain/constants.js';
import { Assessment } from '../../../../src/shared/domain/models/Assessment.js';
import {
PRO_MANAGING_ORGANIZATION_ID,
PRO_ORGANIZATION_ID,
SCO_MANAGING_ORGANIZATION_ID,
SUP_MANAGING_ORGANIZATION_ID,
Expand Down Expand Up @@ -117,7 +118,32 @@ async function _createSupCampaigns(databaseBuilder) {
});
}

async function _createProGenericCampaigns(databaseBuilder) {
await createProfilesCollectionCampaign({
databaseBuilder,
organizationId: PRO_MANAGING_ORGANIZATION_ID,
ownerId: USER_ID_ADMIN_ORGANIZATION,
name: 'Campagne de collecte de profil PRO',
multipleSendings: true,
code: 'PROGENCOL',
type: 'PROFILES_COLLECTION',
title: null,
});
}

async function _createProCampaigns(databaseBuilder) {
await createProfilesCollectionCampaign({
databaseBuilder,
organizationId: PRO_ORGANIZATION_ID,
ownerId: USER_ID_ADMIN_ORGANIZATION,
name: 'Campagne de collecte de profil PRO',
multipleSendings: true,
code: 'PROCOLMUL',
type: 'PROFILES_COLLECTION',
title: null,
configCampaign: { participantCount: 3, profileDistribution: { beginner: 1, perfect: 1, blank: 1 } },
});

await createAssessmentCampaign({
databaseBuilder,
targetProfileId: TARGET_PROFILE_NO_BADGES_NO_STAGES_ID,
Expand Down Expand Up @@ -233,22 +259,11 @@ async function _createProCampaigns(databaseBuilder) {
competenceId: null,
campaignParticipationId: thirdCampaignParticipationId,
});

await createProfilesCollectionCampaign({
databaseBuilder,
organizationId: PRO_ORGANIZATION_ID,
ownerId: USER_ID_ADMIN_ORGANIZATION,
name: 'Campagne de collecte de profil PRO',
multipleSendings: true,
code: 'PROCOLMUL',
type: 'PROFILES_COLLECTION',
title: null,
configCampaign: { participantCount: 3, profileDistribution: { beginner: 1, perfect: 1, blank: 1 } },
});
}

export async function buildCampaigns(databaseBuilder) {
await _createProCampaigns(databaseBuilder);
await _createSupCampaigns(databaseBuilder);
await _createProGenericCampaigns(databaseBuilder);
return _createScoCampaigns(databaseBuilder);
}
2 changes: 0 additions & 2 deletions api/lib/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ import * as userRepository from '../../../src/identity-access-management/infrast
import { userToCreateRepository } from '../../../src/identity-access-management/infrastructure/repositories/user-to-create.repository.js';
import { organizationForAdminRepository } from '../../../src/organizational-entities/infrastructure/repositories/organization-for-admin.repository.js';
import * as campaignManagementRepository from '../../../src/prescription/campaign/infrastructure/repositories/campaign-management-repository.js';
import * as campaignToJoinRepository from '../../../src/prescription/campaign/infrastructure/repositories/campaign-to-join-repository.js';
import * as divisionRepository from '../../../src/prescription/campaign/infrastructure/repositories/division-repository.js';
import * as campaignAssessmentParticipationRepository from '../../../src/prescription/campaign-participation/infrastructure/repositories/campaign-assessment-participation-repository.js';
import * as campaignAssessmentParticipationResultRepository from '../../../src/prescription/campaign-participation/infrastructure/repositories/campaign-assessment-participation-result-repository.js';
Expand Down Expand Up @@ -223,7 +222,6 @@ const dependencies = {
campaignParticipationResultRepository,
campaignProfileRepository,
campaignRepository,
campaignToJoinRepository,
centerRepository,
certifiableProfileForLearningContentRepository,
certificateRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const authenticateAnonymousUser = async function ({
userToCreateRepository,
tokenService,
}) {
const campaign = await campaignToJoinRepository.getByCode(campaignCode);
const campaign = await campaignToJoinRepository.getByCode({ code: campaignCode });
if (!campaign.isSimplifiedAccess) {
throw new UserCantBeCreatedError();
}
Expand Down
4 changes: 2 additions & 2 deletions api/src/identity-access-management/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as campaignParticipationRepository from '../../../../lib/infrastructure
import * as campaignRepository from '../../../../lib/infrastructure/repositories/campaign-repository.js';
import * as organizationLearnerRepository from '../../../../lib/infrastructure/repositories/organization-learner-repository.js';
import * as userRecommendedTrainingRepository from '../../../devcomp/infrastructure/repositories/user-recommended-training-repository.js';
import * as campaignToJoinRepository from '../../../prescription/campaign/infrastructure/repositories/campaign-to-join-repository.js';
import { repositories as campaignRepositories } from '../../../prescription/campaign/infrastructure/repositories/index.js';
import { config } from '../../../shared/config.js';
import { cryptoService } from '../../../shared/domain/services/crypto-service.js';
import { tokenService } from '../../../shared/domain/services/token-service.js';
Expand Down Expand Up @@ -42,8 +42,8 @@ const repositories = {
authenticationMethodRepository,
campaignParticipationRepository,
campaignRepository,
campaignToJoinRepository,
emailValidationDemandRepository,
campaignToJoinRepository: campaignRepositories.campaignToJoinRepository,
oidcProviderRepository,
organizationLearnerRepository,
resetPasswordDemandRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class OrganizationFeaturesDTO {
this.features = features;
}

get hasLeanersImportFeature() {
get hasLearnersImportFeature() {
return this.features.some((feature) => feature.name === ORGANIZATION_FEATURE.LEARNER_IMPORT.key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class CampaignToStartParticipation {
idPixLabel,
archivedAt,
type,
isRestricted,
isManagingStudents,
hasLearnersImportFeature,
multipleSendings,
assessmentMethod,
skillCount,
Expand All @@ -17,12 +18,12 @@ class CampaignToStartParticipation {
this.type = type;
this.idPixLabel = idPixLabel;
this.archivedAt = archivedAt;
this.isRestricted = isRestricted;
this.multipleSendings = multipleSendings;
this.assessmentMethod = assessmentMethod;
this.skillCount = skillCount;
this.organizationId = organizationId;
this.deletedAt = deletedAt;
this.isRestricted = isManagingStudents || hasLearnersImportFeature;
}

get isAssessment() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { importNamedExportsFromDirectory } from '../../../../shared/infrastructu
import * as campaignAnalysisRepository from '../../infrastructure/repositories/campaign-analysis-repository.js';
import * as campaignAssessmentParticipationRepository from '../../infrastructure/repositories/campaign-assessment-participation-repository.js';
import * as campaignAssessmentParticipationResultRepository from '../../infrastructure/repositories/campaign-assessment-participation-result-repository.js';
import * as campaignParticipantRepository from '../../infrastructure/repositories/campaign-participant-repository.js';
import * as campaignParticipationRepository from '../../infrastructure/repositories/campaign-participation-repository.js';
import * as campaignProfileRepository from '../../infrastructure/repositories/campaign-profile-repository.js';
import { repositories as campaignRepositories } from '../../infrastructure/repositories/index.js';
import * as participationsForCampaignManagementRepository from '../../infrastructure/repositories/participations-for-campaign-management-repository.js';
import * as participationsForUserManagementRepository from '../../infrastructure/repositories/participations-for-user-management-repository.js';

Expand All @@ -28,7 +28,7 @@ const dependencies = {
campaignAnalysisRepository,
campaignAssessmentParticipationRepository,
campaignAssessmentParticipationResultRepository,
campaignParticipantRepository,
campaignParticipantRepository: campaignRepositories.campaignParticipantRepository,
campaignProfileRepository,
campaignRepository,
competenceEvaluationRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const startCampaignParticipation = async function ({
isReset: campaignParticipation.isReset,
});

const campaignParticipationId = await campaignParticipantRepository.save(campaignParticipant, domainTransaction);
const campaignParticipationId = await campaignParticipantRepository.save({ campaignParticipant, domainTransaction });

const createdCampaignParticipation = await campaignParticipationRepository.get(
campaignParticipationId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { CampaignParticipant } from '../../domain/models/CampaignParticipant.js'
import { CampaignToStartParticipation } from '../../domain/models/CampaignToStartParticipation.js';
import { PreviousCampaignParticipation } from '../../domain/models/PreviousCampaignParticipation.js';

async function save(campaignParticipant, domainTransaction) {
async function save({ campaignParticipant, domainTransaction }) {
const newlyCreatedOrganizationLearnerId = await _createNewOrganizationLearner(
campaignParticipant.organizationLearner,
domainTransaction.knexTransaction,
Expand All @@ -35,10 +35,14 @@ async function save(campaignParticipant, domainTransaction) {
return campaignParticipationId;
}

async function get({ userId, campaignId, domainTransaction }) {
async function get({ userId, campaignId, domainTransaction, organizationFeatureAPI }) {
const userIdentity = await _getUserIdentityForTrainee(userId, domainTransaction);

const campaignToStartParticipation = await _getCampaignToStart(campaignId, domainTransaction);
const campaignToStartParticipation = await _getCampaignToStart({
campaignId,
domainTransaction,
organizationFeatureAPI,
});

const organizationLearner = await _getOrganizationLearner(campaignId, userId, domainTransaction);

Expand Down Expand Up @@ -148,7 +152,7 @@ async function _getUserIdentityForTrainee(userId, domainTransaction) {
return new UserIdentity(userIdentity);
}

async function _getCampaignToStart(campaignId, domainTransaction) {
async function _getCampaignToStart({ campaignId, domainTransaction, organizationFeatureAPI }) {
const campaignAttributes = await domainTransaction
.knexTransaction('campaigns')
.join('organizations', 'organizations.id', 'organizationId')
Expand All @@ -158,7 +162,7 @@ async function _getCampaignToStart(campaignId, domainTransaction) {
'idPixLabel',
'campaigns.archivedAt',
'campaigns.deletedAt',
'isManagingStudents AS isRestricted',
'isManagingStudents',
'multipleSendings',
'assessmentMethod',
'organizationId',
Expand All @@ -171,7 +175,15 @@ async function _getCampaignToStart(campaignId, domainTransaction) {
}
const skillIds = await campaignRepository.findSkillIds({ campaignId, domainTransaction });

return new CampaignToStartParticipation({ ...campaignAttributes, skillCount: skillIds.length });
const { hasLearnersImportFeature } = await organizationFeatureAPI.getAllFeaturesFromOrganization(
campaignAttributes.organizationId,
);

return new CampaignToStartParticipation({
...campaignAttributes,
hasLearnersImportFeature,
skillCount: skillIds.length,
});
}

async function _getOrganizationLearner(campaignId, userId, domainTransaction) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as organizationFeatureAPI from '../../../../organizational-entities/application/api/organization-features-api.js';
import { injectDependencies } from '../../../../shared/infrastructure/utils/dependency-injection.js';
import * as campaignParticipantRepository from './campaign-participant-repository.js';

const repositoriesWithoutInjectedDependencies = {
campaignParticipantRepository,
};

const dependencies = {
organizationFeatureAPI,
};

const repositories = injectDependencies(repositoriesWithoutInjectedDependencies, dependencies);

export { repositories };
Loading