Skip to content

Commit

Permalink
create delegation algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-ng committed Apr 2, 2024
1 parent 0c80b2c commit 8c5555a
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
17 changes: 17 additions & 0 deletions backend/typescript/delegation-algorithm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Delegation Algorithm

Run the delegation algorithm from the command line of the running application:

Install ts-node:

```
npm install -g ts-node
```

Run the algorithm:

```
ts-node /delegation-algorithm/src/index.ts
```

The `applicationdashboardtable` should be populated with the review data.
73 changes: 73 additions & 0 deletions backend/typescript/delegation-algorithm/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Sequelize from "sequelize"

import Application from "../../models/application.model";
import ApplicationDashboardModel from "../../models/applicationDashboard.model";
import User from "../../models/user.model";
import { sequelize } from "../../models";

import { roundRobinPairs, assignApplicationsToPairs } from "./roundRobinPairs";

// Mapping of application roles to possible reviewers
const roleMapping = new Map([
["Project Developer", ["VP Engineering", "Project Lead", "Project Developer"]],
["Product Designer", ["VP Design", "VP Product", "Product Manager", "Product Designer"]],
["Project Lead", ["VP Engineering", "Project Lead"]],
["Product Manager", ["VP Product", "Product Manager"]],
["VP Design", ["VP Design"]],
["VP Product", ["VP Product"]],
["VP Engineering", ["VP Engineering"]]
]);


async function runDelegationAlgorithms() {
sequelize.authenticate();

await Promise.all([...roleMapping.keys()].map(async function (role) {
await delegationAlgorithm(role);
}))
}

async function delegationAlgorithm(role: string) {
const applications = await loadApplications(role);
const reviewers = await loadReviewers(roleMapping.get(role) as string[]);

const uniquePairs = roundRobinPairs(reviewers);
const totalPairs = assignApplicationsToPairs(uniquePairs, applications);

await Promise.all(
applications.map(async function (application, i) {
return Promise.all(
totalPairs[i].map(async function (reviewer) {
await ApplicationDashboardModel.create({
applicationId: application.id,
reviewerId: reviewer.id,
reviewerEmail: reviewer.email,
passionFSG: 0,
teamPlayer: 0,
desireToLearn: 0,
skill: 0,
skillCategory: "junior",
reviewerComments: "",
recommendedSecondChoice: "N/A"
});
})
);
})
);
}

async function loadApplications(role: string): Promise<Application[]> {
return await Application.findAll({
attributes: ["id"],
where: { firstChoiceRole: { [Sequelize.Op.like]: `%${role}%` } }
});
}

async function loadReviewers(roles: string[]): Promise<User[]> {
return await User.findAll({
attributes: ["id", "email"],
where: { role: { [Sequelize.Op.in]: roles } }
});
}

runDelegationAlgorithms()
51 changes: 51 additions & 0 deletions backend/typescript/delegation-algorithm/src/roundRobinPairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { isEqual } from "lodash";

import Application from "../../models/application.model";
import User from "../../models/user.model";


// Generate a list of all unique pairs of users — from a given list of users
// Uses Round Robin algorithm for optimized time complexity
export function roundRobinPairs(reviewers: (User | {})[]): [User, User][] {
if (reviewers.length % 2 !== 0) {
reviewers.push({});
}

const fixedReviewer = reviewers[0];
let rotatingReviewers = reviewers.slice(1);
const pairs: [(User | {}), (User | {})][] = [];

for (let i = 0; i < reviewers.length - 1; i++) {
for (let j = 0; j < reviewers.length / 2 - 1; j++) {
if (!isEqual(rotatingReviewers[j], {}) && !isEqual(rotatingReviewers[reviewers.length - j - 2], {})) {
pairs.push([rotatingReviewers[j], rotatingReviewers[reviewers.length - j - 2]]);
}
}
if (!isEqual(fixedReviewer, {}) && !isEqual(rotatingReviewers[Math.floor(reviewers.length / 2) - 1], {})) {
pairs.push([fixedReviewer, rotatingReviewers[Math.floor(reviewers.length / 2) - 1]]);
}
rotatingReviewers = rotatingReviewers.slice(1).concat(rotatingReviewers.slice(0, 1)); //rotate list
}

shuffleArray(pairs);
return pairs as [User, User][];
}

// Multiply pairs to equal the number of applications
export function assignApplicationsToPairs(pairs: [User, User][], applications: Application[]): [User, User][] {
const totalPairsNeeded = applications.length;
while (pairs.length < totalPairsNeeded) {
pairs.push(...pairs.slice(0, totalPairsNeeded - pairs.length));
}

shuffleArray(pairs);
return pairs;
}

// Shuffle a list of items
function shuffleArray<T>(array: T[]): void {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}

0 comments on commit 8c5555a

Please sign in to comment.