From dc75ef0dfcf1b09e3b33e68a4f18baa7a22fe07a Mon Sep 17 00:00:00 2001 From: JAMIE XIAO Date: Mon, 17 Nov 2025 02:39:50 -0500 Subject: [PATCH 1/4] Assign 2 reviewers per unreviewed Hacker submitted before cutoff time. Will need a cleaning for documentation --- constants/routes.constant.js | 10 ++++ controllers/hacker.controller.js | 18 +++++++ middlewares/hacker.middleware.js | 93 ++++++++++++++++++++++++++++++++ routes/api/hacker.js | 31 +++++++++++ scripts/accept_script.py | 4 -- 5 files changed, 152 insertions(+), 4 deletions(-) diff --git a/constants/routes.constant.js b/constants/routes.constant.js index d5aec255..1bcf1da8 100644 --- a/constants/routes.constant.js +++ b/constants/routes.constant.js @@ -213,6 +213,16 @@ const hackerRoutes = { uri: "/api/hacker/reviewerComments2/" + Constants.ROLE_CATEGORIES.SELF, _id: mongoose.Types.ObjectId.createFromTime(179) }, + // patchAnyReviewerNameById: { + // requestType: Constants.REQUEST_TYPES.PATCH, + // uri: "/api/hacker/reviewerName/" + Constants.ROLE_CATEGORIES.ALL, + // _id: mongoose.Types.ObjectId.createFromTime(168) + // }, + // patchSelfReviewerNameById: { + // requestType: Constants.REQUEST_TYPES.PATCH, + // uri: "/api/hacker/reviewerName/" + Constants.ROLE_CATEGORIES.SELF, + // _id: mongoose.Types.ObjectId.createFromTime(169) + // }, patchSelfCheckInById: { requestType: Constants.REQUEST_TYPES.PATCH, uri: "/api/hacker/checkin/" + Constants.ROLE_CATEGORIES.SELF, diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index 25b2a635..410eb808 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -102,9 +102,27 @@ function updatedHackerBatch(req, res) { }); } +/** + * @function assignReviewers + * @param {*} req + * @param {*} res + * @return {JSON} Success or error status + * @description + * Assign reviewers to hackers who've not yet been assigned reviewers and submitted applications before the deadline. + * Returns a 200 status after bulk assignment reviewers. + * The assignments are located in req.body. + */ +function assignedReviewers(req, res) { + return res.status(200).json({ + message: 'Successfully assigned reviewers to hackers', + data: req.body + }); +} + module.exports = { updatedHacker: updatedHacker, updatedHackerBatch: updatedHackerBatch, + assignedReviewers: assignedReviewers, createdHacker: createdHacker, uploadedResume: uploadedResume, downloadedResume: downloadedResume, diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index ba540f99..27b8be20 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -3,6 +3,7 @@ const TAG = `[ HACKER.MIDDLEWARE.js ]`; const mongoose = require("mongoose"); +const ObjectId = mongoose.Types.ObjectId; const { HACKER_REVIEWER_STATUS_NONE } = require("../constants/general.constant"); const { HACKER_REVIEWER_STATUSES } = require("../constants/general.constant"); const { HACKER_REVIEWER_NAMES } = require("../constants/general.constant"); @@ -740,6 +741,97 @@ async function updateBatchHacker(req, res, next) { next(); } + +/** + * Updates a hacker that is specified by req.params.id, and then sets req.email + * to the email of the hacker, found in Account. + * Assigns reviewers to hackers in a batch process. + * @param {*} req + * @param {*} res + * @param {*} next + */ +async function assignReviewers(req, res, next) { + try { + console.log('Starting assignReviewers'); + + const REVIEWER_NAMES = HACKER_REVIEWER_NAMES.filter(name => name !== ''); // get all non-empty reviewer names + console.log('Reviewer names:', REVIEWER_NAMES); + + const cutoff = new Date('2025-11-27T17:23:59.000Z'); // EDIT: set your desired cutoff date here + const cutoffObjectId = new ObjectId(Math.floor(cutoff.getTime() / 1000).toString(16) + "0000000000000000"); + + const hackerModel = require('../models/hacker.model'); + + // find all hackers without assigned reviewers and created before the cutoff date + const hackers = await hackerModel.find({ + reviewerName: '', + _id: { $lte: cutoffObjectId } + }).select('_id'); + + console.log('Found hackers:', hackers.length); + + // get counts + const hackerCount = hackers.length; + const revwiewerCount = REVIEWER_NAMES.length; + + if (hackerCount === 0) { + console.log('No hackers found for reviewer assignment.'); + + req.body = { + success: true, + assigned: 0, + reviewers: revwiewerCount, + assignments: [] + } + + return next(); + } + + console.log(`Found ${hackerCount} assignable reviewers.`); + + let assignments = []; + let hackerIndex = 0; + let updatePromises = []; + + // assign reviewers to hackers + for (const hacker of hackers) { + const assignedReviewer1 = REVIEWER_NAMES[hackerIndex % revwiewerCount]; + const assignedReviewer2 = REVIEWER_NAMES[(hackerIndex + 1) % revwiewerCount]; + + assignments.push({ hackerId: hacker._id, reviewer: assignedReviewer1, reviewer2: assignedReviewer2 }); + + updatePromises.push( + Services.Hacker.updateOne(hacker._id, { reviewerName: assignedReviewer1, reviewerName2: assignedReviewer2 }) + ); + + hackerIndex++; + } + + // exec all updates + await Promise.all(updatePromises); + + console.log(`Completed reviewer assignment at ${new Date().toISOString()}`); + console.log(`Assignments:`, assignments); + + req.body = { + success: true, + assigned: hackerCount, + reviewers: revwiewerCount, + assignments: assignments + } + + return next(); + + } catch (error) { + console.error('Error during reviewer assignment:', error); + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + data: { error: error } + }); + } +} + /** * Sets req.body.status to Accepted for next middleware, and store req.params.id as req.hackerId * @param {{params:{id: string}, body: *}} req @@ -911,6 +1003,7 @@ module.exports = { ), updateHacker: Middleware.Util.asyncMiddleware(updateHacker), updateBatchHacker: Middleware.Util.asyncMiddleware(updateBatchHacker), + assignReviewers: Middleware.Util.asyncMiddleware(assignReviewers), parseAccept: parseAccept, parseAcceptBatch: parseAcceptBatch, parseAcceptEmail: parseAcceptEmail, diff --git a/routes/api/hacker.js b/routes/api/hacker.js index bfc7a5bf..875583c1 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -299,6 +299,37 @@ module.exports = { Controllers.Hacker.updatedHacker ); + /** + * @api {post} /hacker/assignReviewers update a hacker's reviewer status + * @apiName patchAssignReviewers + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) None + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Assigned reviewers to hackers", + * } + * @apiPermission Administrator + */ + hackerRouter.route("/assignReviewers").post( + // console.log("sup"), + (req, res, next) => { + console.log('🔥 HIT THE ROUTE!'); + next(); + }, + // Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + // Middleware.Validator.Hacker.updateReviewerStatusValidator, + // Middleware.parseBody.middleware, + // Middleware.Hacker.parsePatch, + Middleware.Hacker.assignReviewers, + Controllers.Hacker.assignedReviewers + ); + /** * @api {patch} /hacker/reviewerStatus/:id update a hacker's reviewer status * @apiName patchHackerReviewerStatus diff --git a/scripts/accept_script.py b/scripts/accept_script.py index 7fc71334..2e3e47b1 100644 --- a/scripts/accept_script.py +++ b/scripts/accept_script.py @@ -619,10 +619,6 @@ def updateReviewerComments2(): else: _print('could not find {0}'.format( ID), 1, index, len(HACKER_IDs)) - -def assignReviewers(): - HACKER_IDs = getIdList() - numHackers = HACKER_IDs.count # def sendDayOfEmail(): From 80a9369a3a42c144cf2ac8b50b29fbd08eeb50cc Mon Sep 17 00:00:00 2001 From: JAMIE XIAO Date: Mon, 17 Nov 2025 02:44:37 -0500 Subject: [PATCH 2/4] removed to requirement that the reviewer name has to be empty (now it's just filtering if hacker was created before cutoff) --- middlewares/hacker.middleware.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 27b8be20..145c511c 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -762,9 +762,8 @@ async function assignReviewers(req, res, next) { const hackerModel = require('../models/hacker.model'); - // find all hackers without assigned reviewers and created before the cutoff date + // find all hackers created before the cutoff date const hackers = await hackerModel.find({ - reviewerName: '', _id: { $lte: cutoffObjectId } }).select('_id'); From b2b10b15b1998460653576b773649a1116be4932 Mon Sep 17 00:00:00 2001 From: JAMIE XIAO Date: Mon, 17 Nov 2025 02:46:40 -0500 Subject: [PATCH 3/4] clean --- routes/api/hacker.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/routes/api/hacker.js b/routes/api/hacker.js index 875583c1..c69fe2a7 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -315,11 +315,6 @@ module.exports = { * @apiPermission Administrator */ hackerRouter.route("/assignReviewers").post( - // console.log("sup"), - (req, res, next) => { - console.log('🔥 HIT THE ROUTE!'); - next(); - }, // Middleware.Validator.RouteParam.idValidator, // Middleware.Auth.ensureAuthenticated(), // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), From 6bb5af34be6d76636abfbc405289db3dc5afd6d4 Mon Sep 17 00:00:00 2001 From: JAMIE XIAO Date: Wed, 19 Nov 2025 22:42:52 -0500 Subject: [PATCH 4/4] Use string[] parameter instead of hardcoded names --- controllers/hacker.controller.js | 2 +- middlewares/hacker.middleware.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index 410eb808..326c45bd 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -104,7 +104,7 @@ function updatedHackerBatch(req, res) { /** * @function assignReviewers - * @param {*} req + * @param {{body: {names: string[]}}} req * @param {*} res * @return {JSON} Success or error status * @description diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 145c511c..6b357275 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -754,7 +754,8 @@ async function assignReviewers(req, res, next) { try { console.log('Starting assignReviewers'); - const REVIEWER_NAMES = HACKER_REVIEWER_NAMES.filter(name => name !== ''); // get all non-empty reviewer names + // const REVIEWER_NAMES = HACKER_REVIEWER_NAMES.filter(name => name !== ''); // get all non-empty reviewer names + const REVIEWER_NAMES = req.body.names; console.log('Reviewer names:', REVIEWER_NAMES); const cutoff = new Date('2025-11-27T17:23:59.000Z'); // EDIT: set your desired cutoff date here