Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions controllers/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,16 @@ const updateUsernames = async (req, res) => {
}
};

const getUsersWithAbandonedTasks = async (req, res) => {
try {
const data = await userQuery.fetchUsersWithAbandonedTasks();
return res.status(200).json({ message: "Users with abandoned tasks fetched successfully", data });
} catch (error) {
logger.error("Error in getting user who abandoned tasks:", error);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

module.exports = {
verifyUser,
generateChaincode,
Expand Down Expand Up @@ -1061,4 +1071,5 @@ module.exports = {
isDeveloper,
getIdentityStats,
updateUsernames,
getUsersWithAbandonedTasks,
};
34 changes: 34 additions & 0 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const { formatUsername } = require("../utils/username");
const { logType } = require("../constants/logs");
const { addLog } = require("../services/logService");

const tasksModel = firestore.collection("tasks");
const { TASK_STATUS } = require("../constants/tasks");
const { COMPLETED, DONE } = TASK_STATUS;
/**
* Adds or updates the user data
*
Expand Down Expand Up @@ -1030,6 +1033,36 @@ const updateUsersWithNewUsernames = async () => {
}
};

const fetchUsersWithAbandonedTasks = async () => {
try {
const COMPLETED_STATUSES = [DONE, COMPLETED];
const eligibleUsersWithTasks = [];

const userSnapshot = await userModel
.where("roles.archived", "==", true)
.where("roles.in_discord", "==", false)
.get();

for (const userDoc of userSnapshot.docs) {
const user = userDoc.data();

// Check if the user has any tasks with status not in [Done, Complete]
const abandonedTasksQuerySnapshot = await tasksModel
.where("assigneeId", "==", user.id)
.where("status", "not-in", COMPLETED_STATUSES)
.get();

if (!abandonedTasksQuerySnapshot.empty) {
eligibleUsersWithTasks.push(user);
}
}
return eligibleUsersWithTasks;
} catch (error) {
logger.error(`Error in getting users who abandoned tasks: ${error}`);
throw error;
}
};

module.exports = {
addOrUpdate,
fetchPaginatedUsers,
Expand Down Expand Up @@ -1059,4 +1092,5 @@ module.exports = {
fetchUserForKeyValue,
getNonNickNameSyncedUsers,
updateUsersWithNewUsernames,
fetchUsersWithAbandonedTasks,
};
2 changes: 2 additions & 0 deletions routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const ROLES = require("../constants/roles");
const { Services } = require("../constants/bot");
const authenticateProfile = require("../middlewares/authenticateProfile");

router.get("/departed-users", users.getUsersWithAbandonedTasks);
router.post("/", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), users.markUnverified);
router.post("/update-in-discord", authenticate, authorizeRoles([SUPERUSER]), users.setInDiscordScript);
router.post("/verify", authenticate, users.verifyUser);
Expand Down Expand Up @@ -67,6 +68,7 @@ router.patch("/profileURL", authenticate, userValidator.updateProfileURL, users.
router.patch("/rejectDiff", authenticate, authorizeRoles([SUPERUSER]), users.rejectProfileDiff);
router.patch("/:userId", authenticate, authorizeRoles([SUPERUSER]), users.updateUser);
router.get("/suggestedUsers/:skillId", authenticate, authorizeRoles([SUPERUSER]), users.getSuggestedUsers);

module.exports = router;
router.post("/batch-username-update", authenticate, authorizeRoles([SUPERUSER]), users.updateUsernames);
module.exports = router;
84 changes: 84 additions & 0 deletions test/fixtures/tasks/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,89 @@ module.exports = () => {
createdAt: 1644753600,
updatedAt: 1644753600,
},
{
id: "task1_id",
title: "Abandoned Task 1",
type: "feature",
status: "IN_PROGRESS",
priority: "HIGH",
percentCompleted: 50,
createdAt: 1727027666,
updatedAt: 1727027999,
startedOn: 1727027777,
endsOn: 1731542400,
assignee: "archived_user1",
assigneeId: "user1_id",
github: {
issue: {
html_url: "https://github.com/org/repo/issues/1",
url: "https://api.github.com/repos/org/repo/issues/1",
},
},
dependsOn: [],
},
{
id: "task2_id",
title: "Abandoned Task 2",
type: "bug",
status: "BLOCKED",
priority: "MEDIUM",
percentCompleted: 30,
createdAt: 1727027666,
updatedAt: 1727027999,
startedOn: 1727027777,
endsOn: 1731542400,
assignee: "archived_user2",
assigneeId: "user2_id",
github: {
issue: {
html_url: "https://github.com/org/repo/issues/2",
url: "https://api.github.com/repos/org/repo/issues/2",
},
},
dependsOn: [],
},
{
id: "task3_id",
title: "Completed Archived Task",
type: "feature",
status: "DONE",
priority: "LOW",
percentCompleted: 100,
createdAt: 1727027666,
updatedAt: 1727027999,
startedOn: 1727027777,
endsOn: 1731542400,
assignee: "archived_user1",
assigneeId: "user1_id",
github: {
issue: {
html_url: "https://github.com/org/repo/issues/3",
url: "https://api.github.com/repos/org/repo/issues/3",
},
},
dependsOn: [],
},
{
id: "task4_id",
title: "Active User Task",
type: "feature",
status: "IN_PROGRESS",
priority: "HIGH",
percentCompleted: 75,
createdAt: 1727027666,
updatedAt: 1727027999,
startedOn: 1727027777,
endsOn: 1731542400,
assignee: "active_user",
assigneeId: "user3_id",
github: {
issue: {
html_url: "https://github.com/org/repo/issues/4",
url: "https://api.github.com/repos/org/repo/issues/4",
},
},
dependsOn: [],
},
];
};
63 changes: 63 additions & 0 deletions test/fixtures/user/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,5 +442,68 @@ module.exports = () => {
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1667685133/profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar.jpg",
},
},
{
id: "user1_id",
discordId: "123456789",
github_id: "github_user1",
username: "archived_user1",
first_name: "Archived",
last_name: "User One",
linkedin_id: "archived_user1",
github_display_name: "archived-user-1",
phone: "1234567890",
email: "[email protected]",
roles: {
archived: true,
in_discord: false,
},
discordJoinedAt: "2024-01-01T00:00:00.000Z",
picture: {
publicId: "profile/user1",
url: "https://example.com/user1.jpg",
},
},
{
id: "user2_id",
discordId: "987654321",
github_id: "github_user2",
username: "archived_user2",
first_name: "Archived",
last_name: "User Two",
linkedin_id: "archived_user2",
github_display_name: "archived-user-2",
phone: "0987654321",
email: "[email protected]",
roles: {
archived: true,
in_discord: false,
},
discordJoinedAt: "2024-01-02T00:00:00.000Z",
picture: {
publicId: "profile/user2",
url: "https://example.com/user2.jpg",
},
},
{
id: "user3_id",
discordId: "555555555",
github_id: "github_user3",
username: "active_user",
first_name: "Active",
last_name: "User",
linkedin_id: "active_user",
github_display_name: "active-user",
phone: "5555555555",
email: "[email protected]",
roles: {
archived: false,
in_discord: true,
},
discordJoinedAt: "2024-01-03T00:00:00.000Z",
picture: {
publicId: "profile/user3",
url: "https://example.com/user3.jpg",
},
},
];
};
55 changes: 54 additions & 1 deletion test/integration/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ const {
userStatusDataAfterSignup,
userStatusDataAfterFillingJoinSection,
} = require("../fixtures/userStatus/userStatus");
const { addJoinData, addOrUpdate } = require("../../models/users");
const { addJoinData, addOrUpdate, fetchUsersWithAbandonedTasks } = require("../../models/users");
const userStatusModel = require("../../models/userStatus");
const { MAX_USERNAME_LENGTH } = require("../../constants/users.ts");
const tasksData = require("../fixtures/tasks/tasks")();
const tasksModel = firestore.collection("tasks");
const userDBModel = firestore.collection("users");

const userRoleUpdate = userData[4];
const userRoleUnArchived = userData[13];
Expand Down Expand Up @@ -2646,4 +2649,54 @@ describe("Users", function () {
expect(res).to.have.status(401);
});
});

describe("fetchUsersWithAbandonedTasks", function () {
beforeEach(async function () {
// Clean the database
await cleanDb();

// Add test users to the database
const userPromises = userData.map((user) => userDBModel.add(user));
await Promise.all(userPromises);

// Add test tasks to the database
const taskPromises = tasksData.map((task) => tasksModel.add(task));
await Promise.all(taskPromises);
});

afterEach(async function () {
await cleanDb();
});

it("should fetch users with abandoned tasks", async function () {
const usersWithAbandonedTasks = await fetchUsersWithAbandonedTasks();

expect(usersWithAbandonedTasks).to.be.an("array");
expect(usersWithAbandonedTasks).to.have.lengthOf(2); // Two users with abandoned tasks
});

it("should not include user who are present in discord or not archived", async function () {
const usersWithAbandonedTasks = await fetchUsersWithAbandonedTasks();

usersWithAbandonedTasks.forEach((user) => {
expect(user.roles.in_discord).to.not.equal(true);
expect(user.roles.archived).to.not.equal(false);
});
});

it("should return an empty array if there are no users with abandoned tasks", async function () {
await cleanDb();

// Add only active users
const activeUser = userData[11]; // Using the active user from our test data
await userDBModel.add(activeUser);

// Add a task assigned to the active user
const activeTask = tasksData[11]; // Using the active user's task
await tasksModel.add(activeTask);
const usersWithAbandonedTasks = await fetchUsersWithAbandonedTasks();
expect(usersWithAbandonedTasks).to.be.an("array");
expect(usersWithAbandonedTasks).to.have.lengthOf(0);
});
});
});
8 changes: 4 additions & 4 deletions test/unit/models/tasks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,12 @@ describe("tasks", function () {
overdueTask.endsOn = Date.now() / 1000 + 24 * 60 * 60 * 7;
await tasks.updateTask(overdueTask);
const usersWithOverdueTasks = await tasks.getOverdueTasks(days);
expect(usersWithOverdueTasks.length).to.be.equal(5);
expect(usersWithOverdueTasks.length).to.be.equal(8);
});

it("should return all users which have overdue tasks if days is not passed", async function () {
const usersWithOverdueTasks = await tasks.getOverdueTasks();
expect(usersWithOverdueTasks.length).to.be.equal(4);
expect(usersWithOverdueTasks.length).to.be.equal(7);
});
});

Expand All @@ -332,8 +332,8 @@ describe("tasks", function () {

it("Should update task status COMPLETED to DONE", async function () {
const res = await tasks.updateTaskStatus();
expect(res.totalTasks).to.be.equal(8);
expect(res.totalUpdatedStatus).to.be.equal(8);
expect(res.totalTasks).to.be.equal(12);
expect(res.totalUpdatedStatus).to.be.equal(12);
});

it("should throw an error if firebase batch operation fails", async function () {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/services/tasks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe("Tasks services", function () {
const res = await updateTaskStatusToDone(tasks);

expect(res).to.deep.equal({
totalUpdatedStatus: 8,
totalUpdatedStatus: 12,
totalOperationsFailed: 0,
updatedTaskDetails: taskDetails,
failedTaskDetails: [],
Expand All @@ -66,7 +66,7 @@ describe("Tasks services", function () {

expect(res).to.deep.equal({
totalUpdatedStatus: 0,
totalOperationsFailed: 8,
totalOperationsFailed: 12,
updatedTaskDetails: [],
failedTaskDetails: taskDetails,
});
Expand Down
4 changes: 2 additions & 2 deletions test/unit/services/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("Users services", function () {

expect(res).to.deep.equal({
message: "Successfully completed batch updates",
totalUsersArchived: 20,
totalUsersArchived: 23,
totalOperationsFailed: 0,
updatedUserDetails: userDetails,
failedUserDetails: [],
Expand All @@ -76,7 +76,7 @@ describe("Users services", function () {
expect(res).to.deep.equal({
message: "Firebase batch operation failed",
totalUsersArchived: 0,
totalOperationsFailed: 20,
totalOperationsFailed: 23,
updatedUserDetails: [],
failedUserDetails: userDetails,
});
Expand Down
Loading