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

[TECH] 📦 Déplacement du learning content repository vers src #11331

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions api/lib/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,13 @@ import * as frameworkRepository from '../../infrastructure/repositories/framewor
import { repositories } from '../../infrastructure/repositories/index.js';
import { certificationCompletedJobRepository } from '../../infrastructure/repositories/jobs/certification-completed-job-repository.js';
// Not used in lib
import * as learningContentRepository from '../../infrastructure/repositories/learning-content-repository.js';
import * as organizationMemberIdentityRepository from '../../infrastructure/repositories/organization-member-identity-repository.js';
import * as targetProfileShareRepository from '../../infrastructure/repositories/target-profile-share-repository.js';
// Not used in lib
import * as targetProfileTrainingRepository from '../../infrastructure/repositories/target-profile-training-repository.js';
import * as thematicRepository from '../../infrastructure/repositories/thematic-repository.js';
// Not used in lib
import * as stageCollectionRepository from '../../infrastructure/repositories/user-campaign-results/stage-collection-repository.js';
import * as learningContentConversionService from '../services/learning-content/learning-content-conversion-service.js';
import * as userReconciliationService from '../services/user-reconciliation-service.js';
import * as organizationCreationValidator from '../validators/organization-creation-validator.js';
import * as organizationValidator from '../validators/organization-with-tags-and-target-profiles-script.js';
Expand Down Expand Up @@ -275,8 +273,6 @@ const dependencies = {
jurySessionRepository,
knowledgeElementRepository,
languageService,
learningContentConversionService,
learningContentRepository,
localeService,
mailService,
membershipRepository,
Expand Down
144 changes: 0 additions & 144 deletions api/lib/infrastructure/repositories/learning-content-repository.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';

import * as learningContentRepository from '../../../../../lib/infrastructure/repositories/learning-content-repository.js';
import * as learningContentRepository from '../../../../learning-content/infrastructure/repositories/learning-content-repository.js';
import {
MAX_CHALLENGES_PER_AREA_FOR_CERTIFICATION_PLUS,
MAX_CHALLENGES_PER_COMPETENCE_FOR_CERTIFICATION,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,144 @@
import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js';
import { child, SCOPES } from '../../../shared/infrastructure/utils/logger.js';

const logger = child('learningcontent:repository', { event: SCOPES.LEARNING_CONTENT });

export class LearningContentRepository {
/** @type {string} */
#tableName;

/** @type {number} */
#chunkSize;

/**
* @param {{
* tableName: string
* chunkSize?: number
* }} config
*/
constructor({ tableName, chunkSize = 1000 }) {
this.#tableName = tableName;
this.#chunkSize = chunkSize;
import _ from 'lodash';

import { knex } from '../../../../db/knex-database-connection.js';
import * as campaignRepository from '../../../prescription/campaign/infrastructure/repositories/campaign-repository.js';
import { NoSkillsInCampaignError, NotFoundError } from '../../../shared/domain/errors.js';
import { CampaignLearningContent } from '../../../shared/domain/models/CampaignLearningContent.js';
import { LearningContent } from '../../../shared/domain/models/LearningContent.js';
import * as areaRepository from '../../../shared/infrastructure/repositories/area-repository.js';
import * as competenceRepository from '../../../shared/infrastructure/repositories/competence-repository.js';
import * as skillRepository from '../../../shared/infrastructure/repositories/skill-repository.js';
import * as tubeRepository from '../../../shared/infrastructure/repositories/tube-repository.js';
import * as learningContentConversionService from '../../domain/services/learning-content-conversion-service.js';
import * as frameworkRepository from './framework-repository.js';
import * as thematicRepository from './thematic-repository.js';

async function findByCampaignId(campaignId, locale) {
const skills = await campaignRepository.findSkills({ campaignId });

const frameworks = await _getLearningContentBySkillIds(skills, locale);

return new CampaignLearningContent(frameworks);
}

async function findByTargetProfileId(targetProfileId, locale) {
const cappedTubesDTO = await knex('target-profile_tubes')
.select({
id: 'tubeId',
level: 'level',
})
.where({ targetProfileId });

if (cappedTubesDTO.length === 0) {
throw new NotFoundError("Le profil cible n'existe pas");
}

/**
* @param {object[]} objects
*/
async saveMany(objects) {
if (!objects) return;
logger.debug(`saving ${objects.length} items in ${this.#tableName}`);
const dtos = objects.map(this.toDto);
const knex = DomainTransaction.getConnection();
for (const chunk of chunks(dtos, this.#chunkSize)) {
await knex.insert(chunk).into(this.#tableName).onConflict('id').merge();
}
const frameworks = await _getLearningContentByCappedTubes(cappedTubesDTO, locale);
return new LearningContent(frameworks);
}

async function findByFrameworkNames({ frameworkNames, locale }) {
const baseFrameworks = [];
for (const frameworkName of frameworkNames) {
baseFrameworks.push(await frameworkRepository.getByName(frameworkName));
}

/**
* @param {object} object
*/
async save(object) {
logger.debug(`saving one item in ${this.#tableName}`);
const dto = this.toDto(object);
const knex = DomainTransaction.getConnection();
await knex.insert(dto).into(this.#tableName).onConflict('id').merge();
const frameworks = await _getLearningContentByFrameworks(baseFrameworks, locale);
return new LearningContent(frameworks);
}

async function _getLearningContentBySkillIds(skills, locale) {
if (_.isEmpty(skills)) {
throw new NoSkillsInCampaignError();
}
const tubeIds = _.uniq(skills.map((skill) => skill.tubeId));
const tubes = await tubeRepository.findByRecordIds(tubeIds, locale);

tubes.forEach((tube) => {
tube.skills = skills.filter((skill) => {
return skill.tubeId === tube.id;
});
});

return _getLearningContentByTubes(tubes, locale);
}

/**
* Maps an object to a DTO before insertion.
* @param {object} _object
* @returns {object}
*/
toDto(_object) {
// must be overridden
async function _getLearningContentByCappedTubes(cappedTubesDTO, locale) {
const skills = await learningContentConversionService.findActiveSkillsForCappedTubes(cappedTubesDTO);

const tubes = await tubeRepository.findByRecordIds(
cappedTubesDTO.map((dto) => dto.id),
locale,
);

tubes.forEach((tube) => {
tube.skills = skills.filter((skill) => {
return skill.tubeId === tube.id;
});
});

return _getLearningContentByTubes(tubes, locale);
}

async function _getLearningContentByTubes(tubes, locale) {
const thematicIds = _.uniq(tubes.map((tube) => tube.thematicId));
const thematics = await thematicRepository.findByRecordIds(thematicIds, locale);
thematics.forEach((thematic) => {
thematic.tubes = tubes.filter((tube) => tube.thematicId === thematic.id);
});

const competenceIds = _.uniq(tubes.map((tube) => tube.competenceId));
const competences = await competenceRepository.findByRecordIds({ competenceIds, locale });

competences.forEach((competence) => {
competence.tubes = tubes.filter((tube) => {
return tube.competenceId === competence.id;
});
competence.thematics = thematics.filter((thematic) => {
return thematic.competenceId === competence.id;
});
});

const allAreaIds = _.map(competences, (competence) => competence.areaId);
const uniqAreaIds = _.uniq(allAreaIds, 'id');
const areas = await areaRepository.findByRecordIds({ areaIds: uniqAreaIds, locale });
for (const area of areas) {
area.competences = competences.filter((competence) => {
return competence.areaId === area.id;
});
}

const frameworkIds = _.uniq(areas.map((area) => area.frameworkId));
const frameworks = await frameworkRepository.findByRecordIds(frameworkIds);
for (const framework of frameworks) {
framework.areas = areas.filter((area) => {
return area.frameworkId === framework.id;
});
}

return frameworks;
}

function chunks(items, size) {
const chunks = [];
for (let i = 0; i < items.length; i += size) {
chunks.push(items.slice(i, i + size));
async function _getLearningContentByFrameworks(frameworks, locale) {
for (const framework of frameworks) {
framework.areas = await areaRepository.findByFrameworkId({ frameworkId: framework.id, locale });
for (const area of framework.areas) {
area.competences = await competenceRepository.findByAreaId({ areaId: area.id, locale });
for (const competence of area.competences) {
competence.thematics = await thematicRepository.findByCompetenceIds([competence.id], locale);
for (const thematic of competence.thematics) {
const tubes = await tubeRepository.findActiveByRecordIds(thematic.tubeIds, locale);
thematic.tubes = tubes;
competence.tubes.push(...tubes);
for (const tube of thematic.tubes) {
tube.skills = await skillRepository.findActiveByTubeId(tube.id);
}
}
}
}
}
return chunks;

return frameworks;
}

export { findByCampaignId, findByFrameworkNames, findByTargetProfileId };
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { fileURLToPath } from 'node:url';

import * as badgeAcquisitionRepository from '../../../../../lib/infrastructure/repositories/badge-acquisition-repository.js';
import * as badgeForCalculationRepository from '../../../../../lib/infrastructure/repositories/badge-for-calculation-repository.js';
import * as learningContentRepository from '../../../../../lib/infrastructure/repositories/learning-content-repository.js';
import * as stageCollectionRepository from '../../../../../lib/infrastructure/repositories/user-campaign-results/stage-collection-repository.js';
import * as tutorialRepository from '../../../../devcomp/infrastructure/repositories/tutorial-repository.js';
import * as compareStagesAndAcquiredStages from '../../../../evaluation/domain/services/stages/stage-and-stage-acquisition-comparison-service.js';
Expand All @@ -13,6 +12,7 @@ import * as stageAcquisitionRepository from '../../../../evaluation/infrastructu
import * as stageRepository from '../../../../evaluation/infrastructure/repositories/stage-repository.js';
import * as authenticationMethodRepository from '../../../../identity-access-management/infrastructure/repositories/authentication-method.repository.js';
import * as userRepository from '../../../../identity-access-management/infrastructure/repositories/user.repository.js';
import * as learningContentRepository from '../../../../learning-content/infrastructure/repositories/learning-content-repository.js';
import { config } from '../../../../shared/config.js';
import * as areaRepository from '../../../../shared/infrastructure/repositories/area-repository.js';
import * as assessmentRepository from '../../../../shared/infrastructure/repositories/assessment-repository.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { knex } from '../../../../../db/knex-database-connection.js';
import * as learningContentRepository from '../../../../../lib/infrastructure/repositories/learning-content-repository.js';
import * as learningContentRepository from '../../../../learning-content/infrastructure/repositories/learning-content-repository.js';
import { NotFoundError } from '../../../../shared/domain/errors.js';
import * as knowledgeElementRepository from '../../../../shared/infrastructure/repositories/knowledge-element-repository.js';
import { CampaignAssessmentParticipationResult } from '../../domain/models/CampaignAssessmentParticipationResult.js';
Expand Down
Loading