Skip to content

Commit 25f3439

Browse files
authored
Merge pull request #1753 from Real-Dev-Squad/feat/missed-progress-updates
2 parents 80f5784 + 3bc300d commit 25f3439

File tree

8 files changed

+395
-5
lines changed

8 files changed

+395
-5
lines changed

config/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
discordUnverifiedRoleId: "<discordUnverifiedRoleId>",
1515
discordDeveloperRoleId: "<discordDeveloperRoleId>",
1616
discordMavenRoleId: "<discordMavenRoleId>",
17+
discordMissedUpdatesRoleId: "discordMissedUpdatesRoleId",
1718
githubApi: {
1819
baseUrl: "https://api.github.com",
1920
org: "Real-Dev-Squad",

constants/tasks.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ const MAPPED_TASK_STATUS = {
3737
UNASSIGNED: "AVAILABLE",
3838
};
3939

40+
const COMPLETED_TASK_STATUS = {
41+
VERIFIED: "VERIFIED",
42+
DONE: "DONE",
43+
COMPLETED: "COMPLETED",
44+
};
4045
const TASK_SIZE = 5;
4146

42-
module.exports = { TASK_TYPE, TASK_STATUS, TASK_STATUS_OLD, MAPPED_TASK_STATUS, TASK_SIZE, DEFAULT_TASK_PRIORITY };
47+
module.exports = {
48+
TASK_TYPE,
49+
TASK_STATUS,
50+
TASK_STATUS_OLD,
51+
MAPPED_TASK_STATUS,
52+
TASK_SIZE,
53+
DEFAULT_TASK_PRIORITY,
54+
COMPLETED_TASK_STATUS,
55+
};

models/discordactions.js

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,18 @@ const dataAccess = require("../services/dataAccessLayer");
1616
const { getDiscordMembers, addRoleToUser, removeRoleFromUser } = require("../services/discordService");
1717
const discordDeveloperRoleId = config.get("discordDeveloperRoleId");
1818
const discordMavenRoleId = config.get("discordMavenRoleId");
19+
const discordMissedUpdatesRoleId = config.get("discordMissedUpdatesRoleId");
20+
1921
const userStatusModel = firestore.collection("usersStatus");
2022
const usersUtils = require("../utils/users");
2123
const { getUsersBasedOnFilter, fetchUser } = require("./users");
24+
const { convertDaysToMilliseconds } = require("../utils/time");
25+
const { chunks } = require("../utils/array");
26+
const tasksModel = firestore.collection("tasks");
27+
const { FIRESTORE_IN_CLAUSE_SIZE } = require("../constants/users");
28+
const discordService = require("../services/discordService");
29+
const { buildTasksQueryForMissedUpdates } = require("../utils/tasks");
30+
const { buildProgressQueryForMissedUpdates } = require("../utils/progresses");
2231

2332
/**
2433
*
@@ -836,6 +845,175 @@ const updateUsersWith31DaysPlusOnboarding = async () => {
836845
}
837846
};
838847

848+
const getMissedProgressUpdatesUsers = async (options = {}) => {
849+
const { cursor, size = 500, excludedDates = [], excludedDays = [0], dateGap = 3 } = options;
850+
const stats = {
851+
tasks: 0,
852+
missedUpdatesTasks: 0,
853+
};
854+
try {
855+
const discordUsersPromise = discordService.getDiscordMembers();
856+
const missedUpdatesRoleId = discordMissedUpdatesRoleId;
857+
858+
let gapWindowStart = Date.now() - convertDaysToMilliseconds(dateGap);
859+
const gapWindowEnd = Date.now();
860+
excludedDates.forEach((timestamp) => {
861+
if (timestamp > gapWindowStart && timestamp < gapWindowEnd) {
862+
gapWindowStart -= convertDaysToMilliseconds(1);
863+
}
864+
});
865+
866+
if (excludedDays.length === 7) {
867+
return { usersToAddRole: [], ...stats };
868+
}
869+
870+
for (let i = gapWindowEnd; i >= gapWindowStart; i -= convertDaysToMilliseconds(1)) {
871+
const day = new Date(i).getDay();
872+
if (excludedDays.includes(day)) {
873+
gapWindowStart -= convertDaysToMilliseconds(1);
874+
}
875+
}
876+
877+
let taskQuery = buildTasksQueryForMissedUpdates(gapWindowStart, size);
878+
879+
if (cursor) {
880+
const data = await tasksModel.doc(cursor).get();
881+
if (!data.data()) {
882+
return {
883+
statusCode: 400,
884+
error: "Bad Request",
885+
message: `Invalid cursor: ${cursor}`,
886+
};
887+
}
888+
taskQuery = taskQuery.startAfter(data);
889+
}
890+
891+
const usersMap = new Map();
892+
const progressCountPromise = [];
893+
const tasksQuerySnapshot = await taskQuery.get();
894+
895+
stats.tasks = tasksQuerySnapshot.size;
896+
tasksQuerySnapshot.forEach((doc) => {
897+
const taskAssignee = doc.data().assignee;
898+
const taskId = doc.id;
899+
900+
if (usersMap.has(taskAssignee)) {
901+
const userData = usersMap.get(taskAssignee);
902+
userData.tasksCount++;
903+
} else {
904+
usersMap.set(taskAssignee, {
905+
tasksCount: 1,
906+
latestProgressCount: dateGap + 1,
907+
isActive: false,
908+
});
909+
}
910+
const updateTasksIdMap = async () => {
911+
const progressQuery = buildProgressQueryForMissedUpdates(taskId, gapWindowStart, gapWindowEnd);
912+
const progressSnapshot = await progressQuery.get();
913+
const userData = usersMap.get(taskAssignee);
914+
userData.latestProgressCount = Math.min(progressSnapshot.data().count, userData.latestProgressCount);
915+
916+
if (userData.latestProgressCount === 0) {
917+
stats.missedUpdatesTasks++;
918+
}
919+
};
920+
progressCountPromise.push(updateTasksIdMap());
921+
});
922+
923+
const userIdChunks = chunks(Array.from(usersMap.keys()), FIRESTORE_IN_CLAUSE_SIZE);
924+
const userStatusSnapshotPromise = userIdChunks.map(
925+
async (userIdList) =>
926+
await userStatusModel
927+
.where("currentStatus.state", "==", userState.ACTIVE)
928+
.where("userId", "in", userIdList)
929+
.get()
930+
);
931+
const userDetailsPromise = userIdChunks.map(
932+
async (userIdList) =>
933+
await userModel
934+
.where("roles.archived", "==", false)
935+
.where(admin.firestore.FieldPath.documentId(), "in", userIdList)
936+
.get()
937+
);
938+
939+
const userStatusChunks = await Promise.all(userStatusSnapshotPromise);
940+
941+
userStatusChunks.forEach((userStatusList) =>
942+
userStatusList.forEach((doc) => {
943+
usersMap.get(doc.data().userId).isActive = true;
944+
})
945+
);
946+
947+
const userDetailsListChunks = await Promise.all(userDetailsPromise);
948+
userDetailsListChunks.forEach((userList) => {
949+
userList.forEach((doc) => {
950+
const userData = usersMap.get(doc.id);
951+
userData.discordId = doc.data().discordId;
952+
});
953+
});
954+
955+
const discordUserList = await Promise.all(discordUsersPromise);
956+
957+
const discordUserMap = new Map();
958+
discordUserList.forEach((discordUser) => {
959+
const discordUserData = { isBot: !!discordUser.user.bot };
960+
discordUser.roles.forEach((roleId) => {
961+
switch (roleId) {
962+
case discordDeveloperRoleId: {
963+
discordUserData.isDeveloper = true;
964+
break;
965+
}
966+
case discordMavenRoleId: {
967+
discordUserData.isMaven = true;
968+
break;
969+
}
970+
case missedUpdatesRoleId: {
971+
discordUserData.hasMissedUpdatesRole = true;
972+
break;
973+
}
974+
}
975+
});
976+
discordUserMap.set(discordUser.user.id, discordUserData);
977+
});
978+
979+
await Promise.all(progressCountPromise);
980+
981+
for (const [userId, userData] of usersMap.entries()) {
982+
const discordUserData = discordUserMap.get(userData.discordId);
983+
const isDiscordMember = !!discordUserData;
984+
const shouldAddRole =
985+
userData.latestProgressCount === 0 &&
986+
userData.isActive &&
987+
isDiscordMember &&
988+
discordUserData.isDeveloper &&
989+
!discordUserData.isMaven &&
990+
!discordUserData.isBot &&
991+
!discordUserData.hasMissedUpdatesRole;
992+
993+
if (!shouldAddRole) {
994+
usersMap.delete(userId);
995+
}
996+
}
997+
998+
const usersToAddRole = [];
999+
for (const userData of usersMap.values()) {
1000+
usersToAddRole.push(userData.discordId);
1001+
}
1002+
const resultDataLength = tasksQuerySnapshot.docs.length;
1003+
const isLast = size && resultDataLength === size;
1004+
const lastVisible = isLast && tasksQuerySnapshot.docs[resultDataLength - 1];
1005+
1006+
if (lastVisible) {
1007+
stats.cursor = lastVisible.id;
1008+
}
1009+
1010+
return { usersToAddRole, ...stats };
1011+
} catch (err) {
1012+
logger.error("Error while running the add missed roles script", err);
1013+
throw err;
1014+
}
1015+
};
1016+
8391017
const addInviteToInviteModel = async (inviteObject) => {
8401018
try {
8411019
const invite = await discordInvitesModel.add(inviteObject);
@@ -878,6 +1056,7 @@ module.exports = {
8781056
updateUsersNicknameStatus,
8791057
updateIdle7dUsersOnDiscord,
8801058
updateUsersWith31DaysPlusOnboarding,
1059+
getMissedProgressUpdatesUsers,
8811060
getUserDiscordInvite,
8821061
addInviteToInviteModel,
8831062
};

test/integration/taskRequests.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,6 @@ describe("Task Requests", function () {
961961
});
962962
});
963963
});
964-
965964
describe("POST /taskRequests", function () {
966965
let fetchIssuesByIdStub;
967966
let fetchTaskStub;

0 commit comments

Comments
 (0)