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

Pix 15758/using campaign participation ids to get snapshots #11332

Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import _ from 'lodash';

import { knex } from '../../../../../db/knex-database-connection.js';
import { CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING } from '../../../../../src/shared/infrastructure/constants.js';
import * as knowledgeElementRepository from '../../../../shared/infrastructure/repositories/knowledge-element-repository.js';
import { CampaignAnalysis } from '../../../campaign/domain/read-models/CampaignAnalysis.js';
import * as knowledgeElementSnapshotRepository from '../../../campaign/infrastructure/repositories/knowledge-element-snapshot-repository.js';
import { CampaignParticipationStatuses } from '../../../shared/domain/constants.js';

const { SHARED } = CampaignParticipationStatuses;

const getCampaignAnalysis = async function (campaignId, campaignLearningContent, tutorials) {
const userIdsAndSharedDates = await _getSharedParticipationsWithUserIdsAndDates(campaignId);
const userIdsAndSharedDatesChunks = _.chunk(userIdsAndSharedDates, CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING);
const participantCount = userIdsAndSharedDates.length;
const campaignParticipationIds = await _getSharedParticipationsId(campaignId);
const campaignParticipationIdsChunks = _.chunk(campaignParticipationIds, CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING);
const participantCount = campaignParticipationIds.length;

const campaignAnalysis = new CampaignAnalysis({
campaignId,
Expand All @@ -20,10 +19,12 @@ const getCampaignAnalysis = async function (campaignId, campaignLearningContent,
participantCount,
});

for (const userIdsAndSharedDates of userIdsAndSharedDatesChunks) {
const knowledgeElementsByTube = await knowledgeElementRepository.findValidatedGroupedByTubesWithinCampaign(
Object.fromEntries(userIdsAndSharedDates),
campaignLearningContent,
for (const campaignParticipationIdChunk of campaignParticipationIdsChunks) {
const knowledgeElementsByParticipation =
await knowledgeElementSnapshotRepository.findByCampaignParticipationIds(campaignParticipationIdChunk);

const knowledgeElementsByTube = campaignLearningContent.getValidatedKnowledgeElementsGroupedByTube(
Object.values(knowledgeElementsByParticipation).flat(),
);
campaignAnalysis.addToTubeRecommendations({ knowledgeElementsByTube });
}
Expand All @@ -45,9 +46,10 @@ const getCampaignParticipationAnalysis = async function (
participantCount: 1,
});

const knowledgeElementsByTube = await knowledgeElementRepository.findValidatedGroupedByTubesWithinCampaign(
{ [campaignParticipation.userId]: campaignParticipation.sharedAt },
campaignLearningContent,
const snapshot = await knowledgeElementSnapshotRepository.findByCampaignParticipationIds([campaignParticipation.id]);

const knowledgeElementsByTube = campaignLearningContent.getValidatedKnowledgeElementsGroupedByTube(
snapshot[campaignParticipation.id],
);
campaignAnalysis.addToTubeRecommendations({ knowledgeElementsByTube });

Expand All @@ -57,15 +59,10 @@ const getCampaignParticipationAnalysis = async function (

export { getCampaignAnalysis, getCampaignParticipationAnalysis };

async function _getSharedParticipationsWithUserIdsAndDates(campaignId) {
async function _getSharedParticipationsId(campaignId) {
const results = await knex('campaign-participations')
.select('userId', 'sharedAt')
.pluck('id')
.where({ campaignId, status: SHARED, isImproved: false, deletedAt: null });

const userIdsAndDates = [];
for (const result of results) {
userIdsAndDates.push([result.userId, result.sharedAt]);
}

return userIdsAndDates;
return results;
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ async function _setSkillsCount(result) {
if (result.assessmentState !== Assessment.states.COMPLETED) {
const operativeSkillIds = await campaignRepository.findSkillIds({ campaignId: result.campaignId });

const knowledgeElementsByUser = await knowledgeElementRepository.findSnapshotForUsers({
[result.userId]: result.sharedAt,
const knowledgeElementsByUser = await knowledgeElementRepository.findAssessedByUserIdAndLimitDateQuery({
userId: result.userId,
limitDate: result.sharedAt,
});
const knowledgeElements = knowledgeElementsByUser[result.userId];

targetedSkillsCount = operativeSkillIds.length;
testedSkillsCount = _getTestedSkillsCount(operativeSkillIds, knowledgeElements);
testedSkillsCount = _getTestedSkillsCount(operativeSkillIds, knowledgeElementsByUser);
}

return { targetedSkillsCount, testedSkillsCount };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ async function _fetchCampaignAssessmentParticipationResultAttributesFromCampaign
}

async function _buildCampaignAssessmentParticipationResults(result, campaignLearningContent) {
const knowledgeElementsGroupedByUser = await knowledgeElementRepository.findSnapshotForUsers({
[result.userId]: result.sharedAt,
const knowledgeElementsGroupedByUser = await knowledgeElementRepository.findAssessedByUserIdAndLimitDateQuery({
userId: result.userId,
limitDate: result.sharedAt,
});
const knowledgeElements = Object.values(knowledgeElementsGroupedByUser).flat();
const validatedTargetedKnowledgeElementsCountByCompetenceId =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@ import * as assessmentRepository from '../../../../shared/infrastructure/reposit
import * as competenceRepository from '../../../../shared/infrastructure/repositories/competence-repository.js';
import * as knowledgeElementRepository from '../../../../shared/infrastructure/repositories/knowledge-element-repository.js';
import * as campaignRepository from '../../../campaign/infrastructure/repositories/campaign-repository.js';
import * as knowledgeElementSnapshotRepository from '../../../campaign/infrastructure/repositories/knowledge-element-snapshot-repository.js';
import * as campaignParticipationRepository from './campaign-participation-repository.js';

const campaignParticipationResultRepository = {
async getByParticipationId(campaignParticipationId) {
const campaignParticipation = await campaignParticipationRepository.get(campaignParticipationId);

const [skillIds, competences, assessment, snapshots] = await Promise.all([
const [skillIds, competences, assessment, knowledgeElements] = await Promise.all([
campaignRepository.findSkillIds({ campaignId: campaignParticipation.campaignId }),
competenceRepository.list(),
assessmentRepository.get(campaignParticipation.lastAssessment.id),
knowledgeElementRepository.findSnapshotForUsers({
[campaignParticipation.userId]: campaignParticipation.sharedAt,
}),
getKnowledgeElements(campaignParticipation),
]);
const allAreas = await areaRepository.list();

Expand All @@ -25,10 +24,21 @@ const campaignParticipationResultRepository = {
assessment,
competences,
skillIds,
knowledgeElements: snapshots[campaignParticipation.userId],
knowledgeElements,
allAreas,
});
},
};

async function getKnowledgeElements(campaignParticipation) {
const snapshot = await knowledgeElementSnapshotRepository.findByCampaignParticipationIds([campaignParticipation.id]);
if (snapshot[campaignParticipation.id]) {
return snapshot[campaignParticipation.id];
}

return knowledgeElementRepository.findUniqByUserId({
userId: campaignParticipation.userId,
});
}

export { campaignParticipationResultRepository };
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ const findProfile = async function ({ campaignId, campaignParticipationId, local
const allAreas = await areaRepository.list({ locale });

const { sharedAt, userId } = profile;

const placementProfile = await placementProfileService.getPlacementProfileWithSnapshotting({
userId,
limitDate: sharedAt,
allowExcessPixAndLevels: false,
competences,
campaignParticipationId,
});

return new CampaignProfile({ ...profile, placementProfile, allAreas });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,9 @@ import { knex } from '../../../../../db/knex-database-connection.js';
import * as campaignRepository from '../../../../../src/prescription/campaign/infrastructure/repositories/campaign-repository.js';
import * as placementProfileService from '../../../../shared/domain/services/placement-profile-service.js';
import * as competenceRepository from '../../../../shared/infrastructure/repositories/competence-repository.js';
import * as knowledgeElementSnapshotRepository from '../../../campaign/infrastructure/repositories/knowledge-element-snapshot-repository.js';
import { ParticipantResultsShared } from '../../domain/models/ParticipantResultsShared.js';

async function _fetchKnowledgeElements(campaignParticipationId) {
const { snapshot: knowledgeElements } = await knex('campaign-participations')
.select('snapshot')
.join('knowledge-element-snapshots', function () {
this.on('knowledge-element-snapshots.userId', '=', 'campaign-participations.userId').andOn(
'knowledge-element-snapshots.snappedAt',
'=',
'campaign-participations.sharedAt',
);
})
.where('campaign-participations.id', campaignParticipationId)
.first();
return knowledgeElements;
}

function _fetchUserIdAndSharedAt(campaignParticipationId) {
return knex('campaign-participations')
.select('userId', 'sharedAt')
Expand All @@ -35,7 +21,9 @@ const participantResultsSharedRepository = {
const skillIds = await campaignRepository.findSkillIdsByCampaignParticipationId({
campaignParticipationId,
});
const knowledgeElements = await _fetchKnowledgeElements(campaignParticipationId);
const knowledgeElements = await knowledgeElementSnapshotRepository.findByCampaignParticipationIds([
campaignParticipationId,
]);
const { userId, sharedAt } = await _fetchUserIdAndSharedAt(campaignParticipationId);
const competences = await competenceRepository.listPixCompetencesOnly();

Expand All @@ -44,11 +32,12 @@ const participantResultsSharedRepository = {
limitDate: sharedAt,
allowExcessPixAndLevels: false,
competences,
campaignParticipationId,
});

return new ParticipantResultsShared({
campaignParticipationId,
knowledgeElements,
knowledgeElements: knowledgeElements[campaignParticipationId],
skillIds,
placementProfile,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import _ from 'lodash';

import { knex } from '../../../../../db/knex-database-connection.js';
import { CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING } from '../../../../shared/infrastructure/constants.js';
import * as knowledgeElementRepository from '../../../../shared/infrastructure/repositories/knowledge-element-repository.js';
import { CampaignParticipationStatuses } from '../../../shared/domain/constants.js';
import { CampaignCollectiveResult } from '../../domain/read-models/CampaignCollectiveResult.js';
import { getLatestParticipationSharedForOneLearner } from './helpers/get-latest-participation-shared-for-one-learner.js';

import * as knowledgeElementSnapshotRepository from './knowledge-element-snapshot-repository.js';
const { SHARED } = CampaignParticipationStatuses;

const getCampaignCollectiveResult = async function (campaignId, campaignLearningContent) {
Expand All @@ -20,10 +18,9 @@ const getCampaignCollectiveResult = async function (campaignId, campaignLearning
let participantCount = 0;
for (const userIdsAndSharedDates of userIdsAndSharedDatesChunks) {
participantCount += userIdsAndSharedDates.length;
const knowledgeElementsGroupedByUser = await knowledgeElementRepository.findSnapshotForUsers(
Object.fromEntries(userIdsAndSharedDates),
);
const knowledgeElements = Object.values(knowledgeElementsGroupedByUser).flat();
const knowledgeElementsGroupedByCampaignParticipationId =
await knowledgeElementSnapshotRepository.findByCampaignParticipationIds(userIdsAndSharedDates);
const knowledgeElements = Object.values(knowledgeElementsGroupedByCampaignParticipationId).flat();
const validatedTargetedKnowledgeElementsCountByCompetenceId =
campaignLearningContent.countValidatedTargetedKnowledgeElementsByCompetence(knowledgeElements);
campaignCollectiveResult.addValidatedSkillCountToCompetences(validatedTargetedKnowledgeElementsCountByCompetenceId);
Expand All @@ -37,16 +34,11 @@ export { getCampaignCollectiveResult };

async function _getChunksSharedParticipationsWithUserIdsAndDates(campaignId) {
const results = await knex
.from('campaign-participations as cp')
.select([
getLatestParticipationSharedForOneLearner(knex, 'sharedAt', campaignId),
'userId',
'organizationLearnerId',
])
.from('campaign-participations')
.max('id')
.where({ campaignId, status: SHARED, deletedAt: null })
.groupBy('userId', 'organizationLearnerId');

const userIdsAndDates = results.map((result) => [result.userId, result.sharedAt]);

return _.chunk(userIdsAndDates, CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING);
const ids = results.map(({ max }) => max);
return _.chunk(ids, CHUNK_SIZE_CAMPAIGN_RESULT_PROCESSING);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,49 @@ const findByUserIdsAndSnappedAtDates = async function (userIdsAndSnappedAtDates

/**
* @function
* @name findMultipleUsersFromUserIdsAndSnappedAtDates
* @name findCampaignParticipationKnowledgeElementSnapshots
*
* @param {Array<FindMultipleSnapshotsPayload>} userIdsAndSnappedAtDates
* @param {number[]} campaignParticipationIds
* @returns {Promise<Array<CampaignParticipationKnowledgeElementSnapshots>>}
*/
const findMultipleUsersFromUserIdsAndSnappedAtDates = async function (userIdsAndSnappedAtDates) {
const params = userIdsAndSnappedAtDates.map((userIdAndDate) => {
return [userIdAndDate.userId, userIdAndDate.sharedAt];
});
const findCampaignParticipationKnowledgeElementSnapshots = async function (campaignParticipationIds) {
const knowledgeElements = await findByCampaignParticipationIds(campaignParticipationIds);
const knowledgeElementsList = [];
campaignParticipationIds.map((campaignParticipationId) =>
knowledgeElementsList.push(
new CampaignParticipationKnowledgeElementSnapshots({
knowledgeElements: knowledgeElements[campaignParticipationId],
campaignParticipationId: campaignParticipationId,
}),
),
);
return knowledgeElementsList;
};

/**
*
* @param {number[]} campaignParticipationIds
* @returns {Object.<number, KnowledgeElement[]>}
*/
const findByCampaignParticipationIds = async function (campaignParticipationIds) {
const results = await knex
.select('userId', 'snapshot', 'snappedAt', 'campaignParticipationId')
.select('campaignParticipationId', 'snapshot')
.from('knowledge-element-snapshots')
.whereIn(['knowledge-element-snapshots.userId', 'snappedAt'], params);
.whereIn('campaignParticipationId', campaignParticipationIds);

return results.map((result) => {
const mappedKnowledgeElements = _toKnowledgeElementCollection({ snapshot: result.snapshot });

return new CampaignParticipationKnowledgeElementSnapshots({
userId: result.userId,
snappedAt: result.snappedAt,
knowledgeElements: mappedKnowledgeElements,
campaignParticipationId: result.campaignParticipationId,
});
});
return Object.fromEntries(
results.map(({ campaignParticipationId, snapshot }) => [
campaignParticipationId,
snapshot.map(({ createdAt, ...data }) => {
return new KnowledgeElement({ ...data, createdAt: new Date(createdAt) });
}),
]),
);
};

export { findByUserIdsAndSnappedAtDates, findMultipleUsersFromUserIdsAndSnappedAtDates, save };
export {
findByCampaignParticipationIds,
findByUserIdsAndSnappedAtDates,
findCampaignParticipationKnowledgeElementSnapshots,
save,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
class CampaignParticipationKnowledgeElementSnapshots {
constructor({ userId, snappedAt, knowledgeElements, campaignParticipationId } = {}) {
this.userId = userId;
this.snappedAt = snappedAt;
constructor({ knowledgeElements, campaignParticipationId } = {}) {
this.knowledgeElements = knowledgeElements;
this.campaignParticipationId = campaignParticipationId;
}
Expand Down
14 changes: 9 additions & 5 deletions api/src/shared/domain/services/placement-profile-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,15 @@ async function getPlacementProfilesWithSnapshotting({ userIdsAndDates, competenc
});
}

async function getPlacementProfileWithSnapshotting({ userId, limitDate, competences, allowExcessPixAndLevels = true }) {
const snapshots = await knowledgeElementRepository.findSnapshotForUsers({
[userId]: limitDate,
});
const knowledgeElements = snapshots[userId];
async function getPlacementProfileWithSnapshotting({
userId,
limitDate,
competences,
allowExcessPixAndLevels = true,
campaignParticipationId,
}) {
const snapshots = await knowledgeElementSnapshotRepository.findByCampaignParticipationIds([campaignParticipationId]);
const knowledgeElements = snapshots[campaignParticipationId];
const knowledgeElementsByCompetence = _.groupBy(knowledgeElements, 'competenceId');

const userCompetences = _createUserCompetencesV2({
Expand Down
Loading