Skip to content

Commit 0111923

Browse files
author
Guillaume
committed
feat(api): add sixth grade attestation script
1 parent 70c5490 commit 0111923

File tree

2 files changed

+451
-0
lines changed

2 files changed

+451
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import dayjs from 'dayjs';
2+
3+
import { CampaignParticipationStatuses } from '../../prescription/shared/domain/constants.js';
4+
import { usecases } from '../../quest/domain/usecases/index.js';
5+
import { isoDateParser } from '../../shared/application/scripts/parsers.js';
6+
import { Script } from '../../shared/application/scripts/script.js';
7+
import { ScriptRunner } from '../../shared/application/scripts/script-runner.js';
8+
import { DomainTransaction } from '../../shared/domain/DomainTransaction.js';
9+
10+
export const PRODUCTION_SIXTH_GRADE_TARGET_PROFILE_IDS = [
11+
1000000, 1000001, 1000002, 1000003, 1000004, 1000005, 1000006, 1000007, 1100001, 1100002, 1100003, 1100004, 1100005,
12+
];
13+
14+
const options = {
15+
start: {
16+
type: 'string',
17+
describe: 'Date de début de la période à traiter, jour inclus, format "YYYY-MM-DD", (ex: "2024-01-20")',
18+
demandOption: false,
19+
requiresArg: true,
20+
coerce: isoDateParser(),
21+
},
22+
end: {
23+
type: 'string',
24+
describe: 'Date de fin de la période à traiter, jour inclus, format "YYYY-MM-DD", (ex: "2024-02-27")',
25+
demandOption: true,
26+
requiresArg: true,
27+
coerce: isoDateParser(),
28+
},
29+
limit: {
30+
type: 'number',
31+
describe: 'Nombre de jours maximum entre les deux dates, par défaut 7',
32+
demandOption: false,
33+
requiresArg: false,
34+
default: 7,
35+
coerce: Number,
36+
},
37+
};
38+
39+
/**
40+
* Fetch the userIDs of the users who have already completed a campaign linked to the specified target profiles.
41+
*
42+
* @param {Date} startDate
43+
* @param {Date} endDate
44+
*
45+
* @returns {Promise<[number]>}
46+
*/
47+
export const fetchUserIds = async (startDate, endDate) => {
48+
endDate.setDate(endDate.getDate() + 1);
49+
const knexConnection = DomainTransaction.getConnection();
50+
const users = await knexConnection('campaign-participations')
51+
.select('campaign-participations.userId')
52+
.join('campaigns', 'campaign-participations.campaignId', 'campaigns.id')
53+
.join('target-profiles', 'campaigns.targetProfileId', 'target-profiles.id')
54+
.where('campaign-participations.createdAt', '>=', startDate)
55+
.where('campaign-participations.createdAt', '<=', endDate)
56+
.where('campaign-participations.status', '<>', CampaignParticipationStatuses.STARTED)
57+
.whereIn('campaigns.targetProfileId', PRODUCTION_SIXTH_GRADE_TARGET_PROFILE_IDS);
58+
59+
return users.map(({ userId }) => userId);
60+
};
61+
62+
/**
63+
* Check if the end date is before the start date.
64+
*
65+
* @param {Date} startDate
66+
* @param {Date} endDate
67+
*/
68+
const checkEndDateBeforeStartDate = (startDate, endDate) => {
69+
if (endDate < startDate) {
70+
throw new Error('The end date must be greater than the start date');
71+
}
72+
};
73+
74+
/**
75+
* Check if the difference between the two dates is less than the limit in days.
76+
*
77+
* @param {Date} startDate
78+
* @param {Date} endDate
79+
* @param {number} limitInDays
80+
*
81+
* @throws {Error}
82+
*/
83+
const checkDifferenceBetweenDates = (startDate, endDate, limitInDays) => {
84+
if (dayjs(endDate).diff(startDate, 'day') >= limitInDays) {
85+
throw new Error('The difference between the two dates must be less than 7 days');
86+
}
87+
};
88+
89+
/**
90+
* Script to reward sixth-grade students who have already completed a campaign linked to a specific target profile.
91+
*/
92+
export class SixthGradeAttestationRewardScript extends Script {
93+
constructor() {
94+
super({
95+
description:
96+
'This script process attestations rewards for users who have already completed a campaign linked to specific target profiles.',
97+
permanent: false,
98+
options,
99+
});
100+
}
101+
102+
/**
103+
* Handles the core logic of the script.
104+
*
105+
* @param {{start: Date, end: Date, limit: number}} options
106+
* @param {{info: function}} logger
107+
* @param {function} rewardUser
108+
* @returns {Promise<void>}
109+
*/
110+
async handle({ options, logger, rewardUser = usecases.rewardUser }) {
111+
checkEndDateBeforeStartDate(options.start, options.end);
112+
checkDifferenceBetweenDates(options.start, options.end, options.limit);
113+
114+
const users = await fetchUserIds(options.start, options.end);
115+
116+
if (users.length === 0) {
117+
logger.info('No user found');
118+
return;
119+
}
120+
121+
logger.info(`${users.length} users found`);
122+
123+
for (const userId of users) {
124+
logger.info(`Processing user ${userId}`);
125+
await rewardUser({
126+
userId,
127+
});
128+
}
129+
}
130+
}
131+
132+
await ScriptRunner.execute(import.meta.url, SixthGradeAttestationRewardScript);

0 commit comments

Comments
 (0)