From 18cae9ae76ba8c4e9f92790cc86316fca6f53cb8 Mon Sep 17 00:00:00 2001 From: Mara Martini Date: Thu, 7 Dec 2023 11:23:42 +0100 Subject: [PATCH] Refactoring --- .vscode/settings.json | 14 +++ bots/attendance/attendanceV2.js | 184 +----------------------------- bots/attendance/newweek.js | 44 +++++++ bots/attendance/newweek.test.js | 60 ++++++++++ bots/attendance/presences.js | 162 ++++++++++++++++++++++++++ bots/attendance/presences.test.js | 36 ++++++ package-lock.json | 8 +- package.json | 3 +- 8 files changed, 330 insertions(+), 181 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 bots/attendance/newweek.js create mode 100644 bots/attendance/newweek.test.js create mode 100644 bots/attendance/presences.js create mode 100644 bots/attendance/presences.test.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6853037c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "cSpell.words": [ + "CDRN", + "Jeudi", + "Lundi", + "Mercredi", + "mockdate", + "NEWWEEK", + "Semaine", + "Vendredi", + "vvxx", + "xxvv" + ] +} \ No newline at end of file diff --git a/bots/attendance/attendanceV2.js b/bots/attendance/attendanceV2.js index 51fb00a9..4b2954cd 100644 --- a/bots/attendance/attendanceV2.js +++ b/bots/attendance/attendanceV2.js @@ -1,5 +1,7 @@ const { App } = require('@slack/bolt'); require('dotenv').config(); +const { newWeek } = require('./newweek'); +const { presences } = require('./presences'); const app = new App({ token: process.env.SLACK_BOT_TOKEN, @@ -8,188 +10,12 @@ const app = new App({ app.start(process.env.PORT || 3000); -const userAttendances = []; -let emojiResponse = []; -const weekResponse = []; -const days = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi']; -const presenceValue = 'v'; -const absenceValue = 'x'; -const optionalValue = '?'; - -app.command('/attendance', async ({ ack, body, client }) => { +app.command('/presences', async ({ ack, body, client }) => { await ack(); - - const userAttendance = normalizeUserAttendance(body.text); - const userId = body.user_id; - - const userInfo = await client.users.info({ - user: userId, - }); - - const userName = userInfo.user.profile.first_name; - const userLastName = userInfo.user.profile.last_name; - - if (userAttendances[userId]) { - userAttendances[userId] = []; - - if (emojiResponse.length > 0) { - emojiResponse = emojiResponse.filter((message) => !message.startsWith(`${userName} ${userLastName}`)); - } - } - - updateUserAttendance(userId, userAttendance); - - const userResponse = generateEmojiResponse(userAttendance); - - emojiResponse.push(`${userName} ${userLastName} : ${userResponse}`); - - let allResponses = ''; - for (const element of emojiResponse) { - allResponses += `${element}\n`; - } - - const channelHistory = await client.conversations.history({ - channel: 'C062C79CDRN', - }); - - for (const message of channelHistory.messages) { - if (message.ts) { - if (message.text && message.text.includes('[NEWWEEK]')) { - continue; - } - - await client.chat.delete({ - channel: 'C062C79CDRN', - ts: message.ts, - }); - } - } - - const message = `${allResponses}`; - const weekResponseText = generateWeekResponse(); - await client.chat.postMessage({ - channel: 'C062C79CDRN', - text: `${message}\n${weekResponseText}`, - }); + await presences(body, client); }); app.command('/newweek', async ({ ack, client }) => { await ack(); - - const formattedStartDate = formatDate(nextMonday); - const formattedEndDate = formatDate(nextFriday); - // const formattedStartDate = nextMonday.toLocaleDateString('fr-FR', { - // day: 'numeric', - // month: 'numeric', - // year: 'numeric', - // }); - - // const formattedEndDate = nextFriday.toLocaleDateString('fr-FR', { - // day: 'numeric', - // month: 'numeric', - // year: 'numeric', - // }); - - const weekMessage = `Semaine du ${formattedStartDate} au ${formattedEndDate} [NEWWEEK]`; - - await client.chat.postMessage({ - channel: 'C062C79CDRN', - text: weekMessage, - }); + await newWeek(client); }); - -const today = new Date(); -const nextMonday = new Date(today); -nextMonday.setDate(today.getDate() + ((8 - today.getDay()) % 7)); -const nextFriday = new Date(nextMonday); -nextFriday.setDate(nextMonday.getDate() + 4); -function formatDate(date) { - return date.toLocaleDateString('fr-FR', { - day: 'numeric', - month: 'numeric', - year: 'numeric', - }); -} - -for (const day of days) { - weekResponse[day] = 0; -} - -function updateUserAttendance(userId, textAfterCommand) { - if (!userAttendances[userId]) { - userAttendances[userId] = []; - } - - userAttendances[userId].push(textAfterCommand); -} - -function generateEmojiResponse(textAfterCommand) { - const emojiArray = []; - - for (const char of textAfterCommand) { - if (char === presenceValue) { - emojiArray.push('✅'); - } else if (char === absenceValue) { - emojiArray.push('❌'); - } else { - emojiArray.push('❓'); - } - } - return emojiArray.join(''); -} - -function generateWeekResponseString(day, presenceCount, optionalCount) { - let response = `${day}: ${presenceCount}`; - if (optionalCount) { - response += ` (ou `; - if (optionalCount > 1) { - response += ' '; - } - response += `${presenceCount + optionalCount}`; - response += ')'; - } - return response; -} - -function generateWeekResponse() { - for (const day of days) { - weekResponse[day] = { presenceCount: 0, optionalCount: 0 }; - } - - for (const userId of Object.keys(userAttendances)) { - const userAttendance = userAttendances[userId]; - for (const [index, day] of days.entries()) { - for (const attendance of userAttendance) { - switch (attendance[index]) { - case presenceValue: { - weekResponse[day].presenceCount++; - break; - } - case '?': { - weekResponse[day].optionalCount++; - break; - } - } - } - } - } - const weekResponseArray = days.map((day) => { - return generateWeekResponseString(day, weekResponse[day].presenceCount, weekResponse[day].optionalCount); - }); - - return weekResponseArray.join('\n'); -} - -function normalizeUserAttendance(value) { - let result = ''; - for (const dayAttendance of value) { - if (dayAttendance === 'v' || dayAttendance === 'V' || dayAttendance === '1') { - result += presenceValue; - } else if (dayAttendance === 'x' || dayAttendance === 'X' || dayAttendance === '0') { - result += absenceValue; - } else { - result += optionalValue; - } - } - return result; -} diff --git a/bots/attendance/newweek.js b/bots/attendance/newweek.js new file mode 100644 index 00000000..c4af5b64 --- /dev/null +++ b/bots/attendance/newweek.js @@ -0,0 +1,44 @@ +async function newWeek(client) { + const today = new Date(); + const weekDay = today.toLocaleString('en-us', { weekday: 'long' }); + let mondayDate; + + // eslint-disable-next-line unicorn/prefer-switch + if (weekDay === 'Monday') { + mondayDate = today; + } else if (weekDay === 'Tuesday') { + mondayDate = new Date(); + mondayDate.setDate(mondayDate.getDate() - 1); + } else if (weekDay === 'Wednesday') { + mondayDate = new Date(); + mondayDate.setDate(mondayDate.getDate() - 2); + } else if (weekDay === 'Thursday') { + mondayDate = new Date(); + mondayDate.setDate(mondayDate.getDate() - 3); + } else if (weekDay === 'Friday' || weekDay === 'Saturday' || weekDay === 'Sunday') { + mondayDate = new Date(); + mondayDate.setDate(mondayDate.getDate() + ((8 - today.getDay()) % 7)); + } + + const nextFriday = new Date(mondayDate); + nextFriday.setDate(mondayDate.getDate() + 4); + const formattedStartDate = formatDate(mondayDate); + const formattedEndDate = formatDate(nextFriday); + const weekMessage = `Semaine du ${formattedStartDate} au ${formattedEndDate} [NEWWEEK]`; + + await client.chat.postMessage({ + channel: process.env.SLACK_CHANNEL, + text: weekMessage, + }); + return weekMessage; +} + +function formatDate(date) { + return date.toLocaleDateString('fr-FR', { + day: 'numeric', + month: 'numeric', + year: 'numeric', + }); +} + +module.exports = { newWeek }; diff --git a/bots/attendance/newweek.test.js b/bots/attendance/newweek.test.js new file mode 100644 index 00000000..cf3974d9 --- /dev/null +++ b/bots/attendance/newweek.test.js @@ -0,0 +1,60 @@ +const { newWeek } = require('./newweek'); +const MockDate = require('mockdate'); +const fakeClient = { + chat: { + postMessage: () => { + /*nothing to do*/ + }, + }, +}; + +const currentWeekMessage = 'Semaine du 04/12/2023 au 08/12/2023 [NEWWEEK]'; +const nextWeekMessage = 'Semaine du 11/12/2023 au 15/12/2023 [NEWWEEK]'; + +test('Test today is tuesday', async () => { + MockDate.set('2023-12-05'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(currentWeekMessage); +}); + +test('Test today is wednesday', async () => { + MockDate.set('2023-12-06'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(currentWeekMessage); +}); + +test('Test today is monday', async () => { + MockDate.set('2023-12-04'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(currentWeekMessage); +}); + +test('Test today is thursday', async () => { + MockDate.set('2023-12-07'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(currentWeekMessage); +}); + +test('Test today is friday', async () => { + MockDate.set('2023-12-08'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(nextWeekMessage); +}); + +test('Test today is saturday', async () => { + MockDate.set('2023-12-09'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(nextWeekMessage); +}); + +test('Test today is sunday', async () => { + MockDate.set('2023-12-10'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(nextWeekMessage); +}); + +test('Test today is monday', async () => { + MockDate.set('2023-12-11'); + const weekMessage = await newWeek(fakeClient); + expect(weekMessage).toBe(nextWeekMessage); +}); diff --git a/bots/attendance/presences.js b/bots/attendance/presences.js new file mode 100644 index 00000000..98355b7c --- /dev/null +++ b/bots/attendance/presences.js @@ -0,0 +1,162 @@ +const userAttendances = []; +let emojiResponse = []; +const weekResponse = []; +const days = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi']; +const presenceValue = 'v'; +const absenceValue = 'x'; +const optionalValue = '?'; + +async function presences(body, client) { + const userAttendance = normalizeUserAttendance(body.text); + const { userId, userName, userLastName } = await extractUserInfo(body, client); + + updateUserAttendance(userId, userAttendance, userName, userLastName); + + const userResponse = generateEmojiResponse(userAttendance); + + emojiResponse.push(`${userName} ${userLastName} : ${userResponse}`); + + let allResponses = ''; + for (const element of emojiResponse) { + allResponses += `${element}\n`; + } + + await deleteLastAttendanceMessage(client); + + await createUpdatedAttendanceMessage(allResponses, client); +} + +async function createUpdatedAttendanceMessage(allResponses, client) { + const message = `${allResponses}`; + const weekResponseText = generateWeekResponse(); + await client.chat.postMessage({ + slackChannel: process.env.SLACK_CHANNEL, + text: `${message}\n${weekResponseText}`, + }); +} + +async function deleteLastAttendanceMessage(client) { + const channelHistory = await client.conversations.history({ + slackChannel: process.env.SLACK_CHANNEL, + }); + + for (const message of channelHistory.messages) { + if (message.ts) { + if (message.text && message.text.includes('[NEWWEEK]')) { + continue; + } + + await client.chat.delete({ + slackChannel: process.env.SLACK_CHANNEL, + ts: message.ts, + }); + } + } +} + +async function extractUserInfo(body, client) { + const userId = body.user_id; + + const userInfo = await client.users.info({ + user: userId, + }); + + const userName = userInfo.user.profile.first_name; + const userLastName = userInfo.user.profile.last_name; + return { userId, userName, userLastName }; +} + +function updateUserAttendance(userId, textAfterCommand, userName, userLastName) { + if (userAttendances[userId]) { + userAttendances[userId] = []; + + if (emojiResponse.length > 0) { + emojiResponse = emojiResponse.filter((message) => !message.startsWith(`${userName} ${userLastName}`)); + } + } + + if (!userAttendances[userId]) { + userAttendances[userId] = []; + } + + userAttendances[userId].push(textAfterCommand); +} + +function generateEmojiResponse(textAfterCommand) { + const emojiArray = []; + + for (const char of textAfterCommand) { + if (char === presenceValue) { + emojiArray.push('✅'); + } else if (char === absenceValue) { + emojiArray.push('❌'); + } else { + emojiArray.push('❓'); + } + } + return emojiArray.join(''); +} + +function generateWeekResponse() { + for (const day of days) { + weekResponse[day] = { presenceCount: 0, optionalCount: 0 }; + } + + for (const userId of Object.keys(userAttendances)) { + const userAttendance = userAttendances[userId]; + for (const [index, day] of days.entries()) { + for (const attendance of userAttendance) { + switch (attendance[index]) { + case presenceValue: { + weekResponse[day].presenceCount++; + break; + } + case '?': { + weekResponse[day].optionalCount++; + break; + } + } + } + } + } + const weekResponseArray = days.map((day) => { + return generateWeekResponseString(day, weekResponse[day].presenceCount, weekResponse[day].optionalCount); + }); + + return weekResponseArray.join('\n'); +} + +function normalizeUserAttendance(value) { + let result = ''; + for (const dayAttendance of value) { + if (dayAttendance === 'v' || dayAttendance === 'V' || dayAttendance === '1') { + result += presenceValue; + } else if (dayAttendance === '?') { + result += optionalValue; + } else { + result += absenceValue; + } + } + return result; +} + +function generateWeekResponseString(day, presenceCount, optionalCount) { + let response = `${day}: ${presenceCount}`; + if (optionalCount) { + response += ` (ou `; + if (optionalCount > 1) { + response += ' '; + } + response += `${presenceCount + optionalCount}`; + response += ')'; + } + return response; +} +module.exports = { + presences, + generateWeekResponse, + generateWeekResponseString, + normalizeUserAttendance, + generateEmojiResponse, + updateUserAttendance, +}; diff --git a/bots/attendance/presences.test.js b/bots/attendance/presences.test.js new file mode 100644 index 00000000..2e469f31 --- /dev/null +++ b/bots/attendance/presences.test.js @@ -0,0 +1,36 @@ +const { normalizeUserAttendance, generateEmojiResponse } = require('./presences'); + +describe('Attendance', () => { + describe('Normalize user attendance', () => { + test('VVVVV becomes vvvvv', () => { + expect(normalizeUserAttendance('VVVVV')).toBe('vvvvv'); + }); + test('XXXXX becomes xxxxx', () => { + expect(normalizeUserAttendance('XXXXX')).toBe('xxxxx'); + }); + test('11111 becomes vvvvv', () => { + expect(normalizeUserAttendance('11111')).toBe('vvvvv'); + }); + test('????? becomes ?????', () => { + expect(normalizeUserAttendance('?????')).toBe('?????'); + }); + test('00000 becomes xxxxx', () => { + expect(normalizeUserAttendance('00000')).toBe('xxxxx'); + }); + test('vvvvv becomes vvvvv', () => { + expect(normalizeUserAttendance('vvvvv')).toBe('vvvvv'); + }); + test('vV?xX becomes vv?xx', () => { + expect(normalizeUserAttendance('vV?xX')).toBe('vv?xx'); + }); + test('0x1v? becomes xxvv?', () => { + expect(normalizeUserAttendance('0x1v?')).toBe('xxvv?'); + }); + }); + + describe('Generate emoji response', () => { + test('vvxx? = ✅✅❌❌❓ ', () => { + expect(generateEmojiResponse('vvxx?')).toBe('✅✅❌❌❓'); + }); + }); +}); diff --git a/package-lock.json b/package-lock.json index 79ac0f65..9c8e4214 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "@slack/bolt": "^3.14.0", "dotenv": "^16.3.1", - "luxon": "^3.3.0" + "luxon": "^3.3.0", + "mockdate": "^3.0.5" }, "devDependencies": { "@rollup/plugin-replace": "^5.0.2", @@ -7219,6 +7220,11 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mockdate": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz", + "integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index 0e7778a6..7d96a7a5 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dependencies": { "@slack/bolt": "^3.14.0", "dotenv": "^16.3.1", - "luxon": "^3.3.0" + "luxon": "^3.3.0", + "mockdate": "^3.0.5" } }