hackforla / VRMS

Volunteer Relationship Management System: This is an ambitious project to create a system that will help us measure our human capital development, reduce repetitive tasks and processes, and improve outcomes.
GNU Affero General Public License v3.0
40 stars 79 forks source link

Research best practices on creating API call to get all users who are listed as managers on a project #1769

Closed JackHaeg closed 1 month ago

JackHaeg commented 1 month ago

Overview

Currently, there is no way for admins to see a list of users who are project managers and which projects they are associated with on VRMS. Given that, we need to research how to create an API that will return a list of users with each entry corresponding to a project they manage.

Action Items

project: {
managers: [user1, user2]
}

User {
projectsManaged: [project1, project2]
}

Resources/Instructions

ntrehan commented 1 month ago

@trillium @jbubar @bkmorgan3 I have done some research on how we can address this

I observed that the projects collection has a key called "managedByUsers" which contains a list of users that manage that project. We want the inverse of that in the users collection, a list of projects managed by a particular user

There are two ways to go about doing this

Approach 1. Pure Query with no schema change - In this approach I can design a query that will initialize a map, scan through all the projects, and whenever it encounters a project that has users populated in the "managedByUsers" list, it will add the project name to the User's key

var userProjectMap = {};

db.projects.find(
  { managedByUsers: { $exists: true, $ne: [] } },
  { _id: 1, name: 1, managedByUsers: 1 }
).forEach(function(project) {
  project.managedByUsers.forEach(function(user) {
    if (!userProjectMap[user]) {
      userProjectMap[user] = [];
    }
    userProjectMap[user].push(project.name); // Add the project name to the user's list
  });
});

Approach 2 - Storing the data in the DB after a schema change. In this approach we will do the same scan as the first method, but rather than keeping the data in a primary map, we will add a new field in the users section called "managedProjects" and store that data in the user collection This data will be saved permanently and can be fetched quickly and more efficiently

db.projects.find(
  { managedByUsers: { $exists: true, $ne: [] } }
).forEach(function(project) {
  project.managedByUsers.forEach(function(user) {
    // Update the user document to add the project to the 'managedProjects' field
    db.users.updateOne(
      { username: user },
      { $addToSet: { managedProjects: project.name } } 
    );
  });
});

Advantages of Approach 1 - Can be quickly implemented right away without any schema/UI changes. Advantages of Approach 2 - It will be faster in performance if the data grows in the future, and is the more elegant and maintainable solution

Things to consider when doing approach 2 - As we are adding a new key called "managedProjects for a user", we should be adding a field in the users section in the UI of the same name It will list down the projects that the user is managing and the ability for the user to add more projects Another thing to consider is, that when we add a user in the "managedByUser" list in a project, we should also add the project in the "managedProjects" in the user, and vice versa

Please let me know which approach I should go forward with, and then I can proceed with #1771 @JackHaeg

JackHaeg commented 1 month ago

Per Monday's all team discussion, we have elected to go with Approach 1

JackHaeg commented 1 month ago

Closing issue as completed, as research from @ntrehan has been discussed with team and solution has been identified.