Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
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
5 changes: 3 additions & 2 deletions backend/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import teamsRouter from "./judging-algorithm/routes/teams-routes.js"
import cabinsRouter from "./cabin-sorting/routes/sortedHackers-routes.js"

const app = express();
const PORT = process.env.PORT || 4000;

app.use(express.json());

app.use(
cors({
origin: 'http://localhost:4000',
// problem lies here
origin: [`http://localhost:${PORT}`, 'http://localhost:3000'],
credentials: true
})
);
Expand All @@ -24,7 +26,6 @@ app.use('/', roomsRouter);
app.use('/', rotationTimesRouter);
app.use('/', teamsRouter);

const PORT = process.env.PORT || 4000;

app.listen(PORT, () => {
console.log(`Server is running in http://localhost:${PORT}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ const createSortedHacker = async (req: any, res: any) => {
return createResponse;
};

export default {getSortedHackers, createSortedHacker};
const getGroupedHackers = async (_req: any, res: any) => {
const groupedHackers = await sortedHackersService.getGroupedHackers()
res.json(groupedHackers)
return groupedHackers
}

export default {getSortedHackers, createSortedHacker, getGroupedHackers };
129 changes: 128 additions & 1 deletion backend/cabin-sorting/data/json_outputs/sortedHackers.json
Original file line number Diff line number Diff line change
@@ -1 +1,128 @@
[{"question0":"answer1","question1":"answer1","question2":"answer1","question3":"answer1","question4":"answer1","question5":"answer1","question6":"answer1","question7":"answer1","question8":"answer1","question9":"answer1","question10":"answer1","question11":"answer1","id":"64accf3dd36486421bc50e53","email":"test","assignedCabin":"cabin1","secondAssignedCabin":" cabin2"},{"question0":"answer2","question1":"answer2","question2":"answer2","question3":"answer2","question4":"answer2","question5":"answer2","question6":"answer2","question7":"answer2","question8":"answer2","question9":"answer2","question10":"answer2","question11":"answer2","id":"64accf66d36486421bc50e55","email":"test2","assignedCabin":" cabin2","secondAssignedCabin":"cabin1"},{"question0":"answer3","question1":"answer3","question2":"answer3","question3":"answer3","question4":"answer3","question5":"answer3","question6":"answer3","question7":"answer3","question8":"answer3","question9":"answer3","question10":"answer3","question11":"answer3","id":"64accf7fd36486421bc50e57","email":"test3","assignedCabin":" cabin3","secondAssignedCabin":"cabin1"},{"question0":"answer4","question1":"answer4","question2":"answer4","question3":"answer4","question4":"answer4","question5":"answer4","question6":"answer4","question7":"answer4","question8":"answer4","question9":"answer4","question10":"answer4","question11":"answer4","id":"64accf8ed36486421bc50e59","email":"test4","assignedCabin":" cabin4","secondAssignedCabin":"cabin1"}]
[
{
"id": "64accf3dd36486421bc50e53",
"email": "test",
"question0": "answer1",
"question1": "answer1",
"question2": "answer1",
"question3": "answer1",
"question4": "answer1",
"question5": "answer1",
"question6": "answer1",
"question7": "answer1",
"question8": "answer1",
"question9": "answer1",
"question10": "answer1",
"question11": "answer1",
"assignedCabin": "cabin1",
"secondAssignedCabin": "cabin2"
},
{
"id": "64accf66d36486421bc50e55",
"email": "test2",
"question0": "answer2",
"question1": "answer2",
"question2": "answer2",
"question3": "answer2",
"question4": "answer2",
"question5": "answer2",
"question6": "answer2",
"question7": "answer2",
"question8": "answer2",
"question9": "answer2",
"question10": "answer2",
"question11": "answer2",
"assignedCabin": "cabin2",
"secondAssignedCabin": "cabin1"
},
{
"id": "64accf7fd36486421bc50e57",
"email": "test3",
"question0": "answer3",
"question1": "answer3",
"question2": "answer3",
"question3": "answer3",
"question4": "answer3",
"question5": "answer3",
"question6": "answer3",
"question7": "answer3",
"question8": "answer3",
"question9": "answer3",
"question10": "answer3",
"question11": "answer3",
"assignedCabin": "cabin3",
"secondAssignedCabin": "cabin1"
},
{
"id": "64accf8ed36486421bc50e59",
"email": "test4",
"question0": "answer4",
"question1": "answer4",
"question2": "answer4",
"question3": "answer4",
"question4": "answer4",
"question5": "answer4",
"question6": "answer4",
"question7": "answer4",
"question8": "answer4",
"question9": "answer4",
"question10": "answer4",
"question11": "answer4",
"assignedCabin": "cabin4",
"secondAssignedCabin": "cabin1"
},
{
"id": "64ae0242cdcfed39b8b59958",
"email": "test4",
"question0": "answer4",
"question1": "answer4",
"question2": "answer4",
"question3": "answer4",
"question4": "answer4",
"question5": "answer4",
"question6": "answer4",
"question7": "answer4",
"question8": "answer4",
"question9": "answer4",
"question10": "answer4",
"question11": "answer4",
"assignedCabin": "cabin4",
"secondAssignedCabin": "cabin1"
},
{
"id": "64ae024dcdcfed39b8b5995a",
"email": "test4",
"question0": "answer4",
"question1": "answer4",
"question2": "answer4",
"question3": "answer4",
"question4": "answer4",
"question5": "answer4",
"question6": "answer4",
"question7": "answer4",
"question8": "answer4",
"question9": "answer4",
"question10": "answer4",
"question11": "answer4",
"assignedCabin": "cabin4",
"secondAssignedCabin": "cabin1"
},
{
"id": "64b0a5b67c6e7f3511e8509e",
"email": "test4",
"question0": "answer4",
"question1": "answer4",
"question2": "answer4",
"question3": "answer4",
"question4": "answer4",
"question5": "answer4",
"question6": "answer4",
"question7": "answer4",
"question8": "answer4",
"question9": "answer4",
"question10": "answer4",
"question11": "answer4",
"assignedCabin": "cabin4",
"secondAssignedCabin": "cabin1"
}
]
2 changes: 2 additions & 0 deletions backend/cabin-sorting/routes/sortedHackers-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ const router = express.Router();
router.get('/sortedHackers', controller.getSortedHackers);
router.post('/sortedHackers', controller.createSortedHacker);

router.get('/groupedHackers', controller.getGroupedHackers);

export default router;
184 changes: 160 additions & 24 deletions backend/cabin-sorting/service/sortedHackers-service.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,171 @@
import { Types } from "mongoose";
import * as sortedHackersDao from "../dao/sortedHackers-dao.js";
import * as fs from 'fs';
import * as path from 'path';
import { parse } from 'csv-parse/sync';
import { Types } from 'mongoose';
import * as sortedHackersDao from '../dao/sortedHackers-dao.js';

let hackerList: any[];
let answerList: any[];
let cabinList: any[];
let CABIN_SIZE: number;
let QUESTIONS_SIZE: number;

interface HackerDataType {
id: string,
email: string,
[key: string] : string,
id: string;
email: string;
[key: string]: string;
}

const getSortedHackers = async () => {
const rawHackerData = await sortedHackersDao.getSortedHackers();
const formattedHackerData = rawHackerData.map(hackerData => {
const {_id, email, applicationResponses} = hackerData
const initialHackerData : HackerDataType = {
id: _id.toString(),
email: email,
}
const rawHackerData = await sortedHackersDao.getSortedHackers();
const formattedHackerData = rawHackerData.map((hackerData) => {
const { _id, email, applicationResponses } = hackerData;
const initialHackerData: HackerDataType = {
id: _id.toString(),
email: email
};

function formatApplicationResponses(accumulatedResponse : HackerDataType, currentResponseKey : any, currentResponseIndex : number) {
const currentResponseQuestionKey = "question" + currentResponseIndex;
accumulatedResponse[currentResponseQuestionKey] = applicationResponses[currentResponseKey]
return accumulatedResponse
}
function formatApplicationResponses(
accumulatedResponse: HackerDataType,
currentResponseKey: any,
currentResponseIndex: number
) {
const currentResponseQuestionKey = 'question' + currentResponseIndex;
accumulatedResponse[currentResponseQuestionKey] =
applicationResponses[currentResponseKey];
return accumulatedResponse;
}

return Object.keys(applicationResponses).reduce(formatApplicationResponses, initialHackerData)
})
return formattedHackerData;
return Object.keys(applicationResponses).reduce(
formatApplicationResponses,
initialHackerData
);
});
return formattedHackerData;
};

const createSortedHacker = async (hacker : any) => {
const createResponse = await sortedHackersDao.createdSortedHacker(hacker)
const createSortedHacker = async (hacker: any) => {
const createResponse = await sortedHackersDao.createdSortedHacker(hacker);
return createResponse;
};

export default {getSortedHackers, createSortedHacker};

const getGroupedHackers = async () => {
const hackers = await assignHackerCabins();
console.log(hackers)
let cabinEmails: string[][] = [];

hackers.forEach((hacker) => {
console.log(hacker)
})

const groupedHackers = hackers.reduce((accum, currVal) => {
const assignedCabin = currVal.assignedCabin;
const cabinNum = cabinList.indexOf(assignedCabin);
if (cabinNum === -1) {
console.error(
`Cabin assigned to Hacker ${assignedCabin.id} could not be found`
);
return accum;
}
const hackerEmail = currVal.email;
if (!accum[cabinNum]) {
accum[cabinNum] = []
}
accum[cabinNum].push(hackerEmail);

console.log(accum)
return accum;
}, cabinEmails);

return groupedHackers
};

function loadCSV(filepath: string, headers: boolean): any[] {
const csvFileAbsolutePath = path.resolve(
'cabin-sorting',
'data',
'csv_inputs',
filepath);
console.log(csvFileAbsolutePath)

// error handling in case file is missing
let fileContent;
try {
fileContent = fs.readFileSync(csvFileAbsolutePath, {
encoding: 'utf-8'
});
} catch (err) {
console.log(`File cannot be found: "${filepath}"`);
return [];
}

const options = {
delimiter: ',',
columns: headers
};
return parse(fileContent, options);
}

async function assignHackerCabins() {
hackerList = await getSortedHackers();
answerList = loadCSV('answer.csv', true);
cabinList = loadCSV('cabinTypes.csv', false)[0];

CABIN_SIZE = Object.keys(cabinList).length;
QUESTIONS_SIZE = Object.keys(answerList[0]).length;

// ensuring the CSV files exists before continuing
if (
hackerList.length === 0 ||
answerList.length === 0 ||
cabinList.length === 0
) {
console.error(
'Please add the respective csv file(s) to the folder to run the sorting algorithm'
);
return [];
} else {
matchAnswers();
return hackerList;
}
}

function matchAnswers() {
hackerList.forEach((hacker: any) => {
// each element = a different cabin, all initialized to 0
const cabinScore = Array<number>(CABIN_SIZE).fill(0);

hydrateCabinScore(hacker, cabinScore);

// create extra column for hacker that determines the cabin they should
// join (the one with the most points)
const cabinOptions: string[] = Object.values(cabinList);
const maxIndex: number = cabinScore.indexOf(Math.max(...cabinScore));
hacker.assignedCabin = cabinOptions[maxIndex];

// find backup cabin for hacker (in case the first choice fills up)
const counterCopy = cabinScore.slice();
counterCopy[maxIndex] = -1;
hacker.secondAssignedCabin =
cabinOptions[counterCopy.indexOf(Math.max(...counterCopy))];
});
}

function hydrateCabinScore(hacker: any, cabinScore: number[]) {
answerList.forEach((cabin: any, cabinIndex: number) => {
for (
let questionIndex = 0;
questionIndex < QUESTIONS_SIZE;
questionIndex++
) {
if (
cabin['question' + questionIndex.toString()] ===
hacker['question' + questionIndex.toString()]
) {
cabinScore[cabinIndex]++;
}
}
});
}

export default { getSortedHackers, createSortedHacker, getGroupedHackers };
Loading