Skip to content

Commit

Permalink
[TECH] Ajouter les nouvelles tables Quests, Attestations et ProfileRe…
Browse files Browse the repository at this point in the history
…wards(PIX-13818). (#9890)

Co-authored-by: Quentin Lebouc <[email protected]>
  • Loading branch information
Jeyffrey and Libouk authored Sep 11, 2024
1 parent 82acb0c commit 7ee95cf
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 4 deletions.
20 changes: 20 additions & 0 deletions api/db/database-builder/factory/build-attestation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { databaseBuffer } from '../database-buffer.js';

const buildAttestation = function ({
id = databaseBuffer.getNextId(),
createdAt = new Date(),
templateName = '6eme-pdf',
} = {}) {
const values = {
id,
createdAt,
templateName,
};

return databaseBuffer.pushInsertable({
tableName: 'attestations',
values,
});
};

export { buildAttestation };
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ const buildOrganizationLearnerWithUser = function ({
birthCityCode = 'PERPICODE',
birthCountryCode = '100',
birthProvinceCode = '66',
MEFCode = '45612312345',
MEFCode = '10010012110',
status = 'ST',
studentNumber = null,
nationalStudentId = null,
division = '3eme',
division = '6eme',
organizationId,
user,
updatedAt = new Date('2021-01-01'), // for BEGINNING_OF_THE_2020_SCHOOL_YEAR, can outdate very fast! ;)
Expand Down
4 changes: 2 additions & 2 deletions api/db/database-builder/factory/build-organization-learner.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ const buildOrganizationLearner = function ({
birthCityCode = 'PERPICODE',
birthCountryCode = '100',
birthProvinceCode = '66',
MEFCode = '45612312345',
MEFCode = '10010012110',
status = 'ST',
nationalStudentId = null,
division = '3eme',
division = '6eme',
studentNumber = null,
email = '[email protected]',
educationalTeam = 'Enseignants blonds',
Expand Down
31 changes: 31 additions & 0 deletions api/db/database-builder/factory/build-profile-reward.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import isUndefined from 'lodash/isUndefined.js';

import { databaseBuffer } from '../database-buffer.js';
import { buildAttestation } from './build-attestation.js';
import { buildUser } from './build-user.js';

const buildProfileReward = function ({
id = databaseBuffer.getNextId(),
createdAt = new Date(),
rewardType = 'attestations',
rewardId,
userId,
} = {}) {
userId = isUndefined(userId) ? buildUser().id : userId;
rewardId = isUndefined(rewardId) && rewardType === 'attestations' ? buildAttestation().id : rewardId;

const values = {
id,
createdAt,
rewardType,
rewardId,
userId,
};

return databaseBuffer.pushInsertable({
tableName: 'profile-rewards',
values,
});
};

export { buildProfileReward };
33 changes: 33 additions & 0 deletions api/db/database-builder/factory/build-quest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import isUndefined from 'lodash/isUndefined.js';

import { databaseBuffer } from '../database-buffer.js';
import { buildAttestation } from './build-attestation.js';

const buildQuest = function ({
id = databaseBuffer.getNextId(),
createdAt = new Date(),
rewardType = 'attestations',
rewardId,
eligibilityRequirements,
successRequirements,
} = {}) {
rewardId = isUndefined(rewardId) && rewardType === 'attestations' ? buildAttestation().id : rewardId;
eligibilityRequirements = JSON.stringify(eligibilityRequirements);
successRequirements = JSON.stringify(successRequirements);

const values = {
id,
createdAt,
rewardType,
rewardId,
eligibilityRequirements,
successRequirements,
};

return databaseBuffer.pushInsertable({
tableName: 'quests',
values,
});
};

export { buildQuest };
27 changes: 27 additions & 0 deletions api/db/migrations/20240820095209_add-quests-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Make sure you properly test your migration, especially DDL (Data Definition Language)
// ! If the target table is large, and the migration take more than 20 minutes, the deployment will fail !

// You can design and test your migration to avoid this by following this guide
// https://1024pix.atlassian.net/wiki/spaces/DEV/pages/2153512965/Cr+er+une+migration

// If your migrations target `answers` or `knowledge-elements`
// contact @team-captains, because automatic migrations are not active on `pix-datawarehouse-production`
// this may prevent data replication to succeed the day after your migration is deployed on `pix-api-production`
const TABLE_NAME = 'quests';

const up = async function (knex) {
await knex.schema.createTable(TABLE_NAME, function (table) {
table.increments('id').primary();
table.dateTime('createdAt').notNullable().defaultTo(knex.fn.now());
table.string('rewardType').notNullable();
table.bigInteger('rewardId').notNullable();
table.jsonb('eligibilityRequirements').notNullable();
table.jsonb('successRequirements').notNullable();
});
};

const down = async function (knex) {
return knex.schema.dropTable(TABLE_NAME);
};

export { down, up };
24 changes: 24 additions & 0 deletions api/db/migrations/20240820101115_add-attestations-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Make sure you properly test your migration, especially DDL (Data Definition Language)
// ! If the target table is large, and the migration take more than 20 minutes, the deployment will fail !

// You can design and test your migration to avoid this by following this guide
// https://1024pix.atlassian.net/wiki/spaces/DEV/pages/2153512965/Cr+er+une+migration

// If your migrations target `answers` or `knowledge-elements`
// contact @team-captains, because automatic migrations are not active on `pix-datawarehouse-production`
// this may prevent data replication to succeed the day after your migration is deployed on `pix-api-production`
const TABLE_NAME = 'attestations';

const up = async function (knex) {
await knex.schema.createTable(TABLE_NAME, function (table) {
table.increments('id').primary();
table.dateTime('createdAt').notNullable().defaultTo(knex.fn.now());
table.string('templateName').notNullable();
});
};

const down = async function (knex) {
return knex.schema.dropTable(TABLE_NAME);
};

export { down, up };
26 changes: 26 additions & 0 deletions api/db/migrations/20240820101213_add-profile-rewards-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Make sure you properly test your migration, especially DDL (Data Definition Language)
// ! If the target table is large, and the migration take more than 20 minutes, the deployment will fail !

// You can design and test your migration to avoid this by following this guide
// https://1024pix.atlassian.net/wiki/spaces/DEV/pages/2153512965/Cr+er+une+migration

// If your migrations target `answers` or `knowledge-elements`
// contact @team-captains, because automatic migrations are not active on `pix-datawarehouse-production`
// this may prevent data replication to succeed the day after your migration is deployed on `pix-api-production`
const TABLE_NAME = 'profile-rewards';

const up = async function (knex) {
await knex.schema.createTable(TABLE_NAME, function (table) {
table.increments('id').primary();
table.dateTime('createdAt').notNullable().defaultTo(knex.fn.now());
table.bigInteger('userId').index().references('users.id');
table.string('rewardType').notNullable();
table.string('rewardId').notNullable();
});
};

const down = async function (knex) {
return knex.schema.dropTable(TABLE_NAME);
};

export { down, up };
97 changes: 97 additions & 0 deletions api/db/seeds/data/team-prescription/build-quests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { TARGET_PROFILE_BADGES_STAGES_ID } from './constants.js';

async function createAttestationQuest(databasebuilder) {
const campaigns = await retrieveCampaigns(databasebuilder);
const evaluatedSkills = await retrieveEvaluatedSkills(databasebuilder, campaigns);
const successfulUsers = await retrieveSuccessfulUsers(databasebuilder, campaigns);

const { id: rewardId } = await databasebuilder.factory.buildAttestation({
templateName: '6eme-pdf',
});

const questEligibilityRequirements = [
{
type: 'organization',
data: {
type: 'SCO',
},
},
{
type: 'organization',
data: {
isManagingStudents: true,
tagNames: ['AEFE'],
},
comparison: 'one-of',
},
{
type: 'organization-learner',
data: {
MEFCode: '10010012110',
},
},
{
type: 'campaign-participation',
data: {
targetProfileIds: [TARGET_PROFILE_BADGES_STAGES_ID],
},
},
];
const questSuccessRequirements = [
{
type: 'skill',
data: {
ids: evaluatedSkills.map((skill) => skill.skillId),
threshold: 50,
},
},
];

await databasebuilder.factory.buildQuest({
rewardType: 'attestation',
rewardId,
eligibilityRequirements: questEligibilityRequirements,
successRequirements: questSuccessRequirements,
});

await Promise.all(
successfulUsers.map(({ userId }) => {
return databasebuilder.factory.buildProfileReward({
userId,
rewardType: 'attestation',
rewardId,
});
}),
);
}

export function buildQuests(databaseBuilder) {
return createAttestationQuest(databaseBuilder);
}

async function retrieveCampaigns(databaseBuilder) {
return databaseBuilder.knex('campaigns').select('id').where({
targetProfileId: TARGET_PROFILE_BADGES_STAGES_ID,
});
}

async function retrieveEvaluatedSkills(databaseBuilder, campaigns) {
return databaseBuilder
.knex('campaign_skills')
.distinct('skillId')
.whereIn(
'campaignId',
campaigns.map((campaign) => campaign.id),
);
}

async function retrieveSuccessfulUsers(databaseBuilder, campaigns) {
return databaseBuilder
.knex('campaign-participations')
.distinct('userId')
.whereIn(
'campaignId',
campaigns.map((campaign) => campaign.id),
)
.andWhere('masteryRate', '>=', 0.5);
}
2 changes: 2 additions & 0 deletions api/db/seeds/data/team-prescription/data-builder.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { buildCampaigns } from './build-campaigns.js';
import { buildOrganizationLearners } from './build-learners.js';
import { buildPlaceLots } from './build-place-lots.js';
import { buildQuests } from './build-quests.js';
import { buildTargetProfiles } from './build-target-profiles.js';

async function teamPrescriptionDataBuilder({ databaseBuilder }) {
await buildTargetProfiles(databaseBuilder);
await buildCampaigns(databaseBuilder);
await buildOrganizationLearners(databaseBuilder);
await buildPlaceLots(databaseBuilder);
await buildQuests(databaseBuilder);
}

export { teamPrescriptionDataBuilder };

0 comments on commit 7ee95cf

Please sign in to comment.