diff --git a/constants/general.constant.js b/constants/general.constant.js index 5e23d2fe..e1cc8502 100644 --- a/constants/general.constant.js +++ b/constants/general.constant.js @@ -25,6 +25,68 @@ const HACKER_STATUSES = [ HACKER_STATUS_CHECKED_IN, HACKER_STATUS_DECLINED ]; + +const HACKER_REVIEWER_STATUS_NONE = 'None'; +const HACKER_REVIEWER_STATUS_POOR = 'Poor'; +const HACKER_REVIEWER_STATUS_WEAK = 'Weak'; +const HACKER_REVIEWER_STATUS_AVERAGE = 'Average'; +const HACKER_REVIEWER_STATUS_STRONG = 'Strong'; +const HACKER_REVIEWER_STATUS_OUTSTANDING = 'Outstanding'; +const HACKER_REVIEWER_STATUS_WHITELIST = 'Whitelist'; +const HACKER_REVIEWER_STATUSES = [ + HACKER_REVIEWER_STATUS_NONE, + HACKER_REVIEWER_STATUS_POOR, + HACKER_REVIEWER_STATUS_WEAK, + HACKER_REVIEWER_STATUS_AVERAGE, + HACKER_REVIEWER_STATUS_STRONG, + HACKER_REVIEWER_STATUS_OUTSTANDING, + HACKER_REVIEWER_STATUS_WHITELIST, +]; + +const HACKER_REVIEWER_NAMES = [ + '', + 'Amy', + 'Carolyn', + 'Clara', + 'Debo', + 'Deon', + 'Doaa', + 'Emily', + 'Emma', + 'Ethan', + 'Evan', + 'Finnley', + 'Gabriel', + 'Ian', + 'Inaya', + 'Jake', + 'Jamie', + 'Jane J.', + 'Jane K.', + 'Jeffrey', + 'Joshua', + 'Jyothsna', + 'Khyati', + 'Michael', + 'Mika', + 'Mubeen', + 'Mira', + 'Oishika', + 'Olivia', + 'Qi', + 'Rémi', + 'Sebastian', + 'Shirley', + 'Sihan', + 'Siva', + 'Snigdha', + 'Stephanie', + 'Tavi', + 'Tina', + 'Vipul', + 'Yue Qian', +]; + // This date is Jan 6, 2020 00:00:00 GMT -0500 const APPLICATION_CLOSE_TIME = 1578286800000; @@ -185,7 +247,16 @@ module.exports = { HACKER_STATUS_CONFIRMED: HACKER_STATUS_CONFIRMED, HACKER_STATUS_WITHDRAWN: HACKER_STATUS_WITHDRAWN, HACKER_STATUS_CHECKED_IN: HACKER_STATUS_CHECKED_IN, + HACKER_REVIEWER_STATUS_NONE: HACKER_REVIEWER_STATUS_NONE, + HACKER_REVIEWER_STATUS_POOR: HACKER_REVIEWER_STATUS_POOR, + HACKER_REVIEWER_STATUS_WEAK: HACKER_REVIEWER_STATUS_WEAK, + HACKER_REVIEWER_STATUS_AVERAGE: HACKER_REVIEWER_STATUS_AVERAGE, + HACKER_REVIEWER_STATUS_STRONG: HACKER_REVIEWER_STATUS_STRONG, + HACKER_REVIEWER_STATUS_OUTSTANDING: HACKER_REVIEWER_STATUS_OUTSTANDING, + HACKER_REVIEWER_STATUS_WHITELIST: HACKER_REVIEWER_STATUS_WHITELIST, HACKER_STATUSES: HACKER_STATUSES, + HACKER_REVIEWER_STATUSES: HACKER_REVIEWER_STATUSES, + HACKER_REVIEWER_NAMES: HACKER_REVIEWER_NAMES, TRAVEL_STATUS_NONE: TRAVEL_STATUS_NONE, TRAVEL_STATUS_BUS: TRAVEL_STATUS_BUS, TRAVEL_STATUS_POLICY: TRAVEL_STATUS_POLICY, diff --git a/constants/routes.constant.js b/constants/routes.constant.js index e880674d..a4b68b46 100644 --- a/constants/routes.constant.js +++ b/constants/routes.constant.js @@ -153,6 +153,36 @@ const hackerRoutes = { uri: "/api/hacker/status/" + Constants.ROLE_CATEGORIES.SELF, _id: mongoose.Types.ObjectId.createFromTime(125) }, + patchAnyReviewerStatusById: { + requestType: Constants.REQUEST_TYPES.PATCH, + uri: "/api/hacker/reviewerStatus/" + Constants.ROLE_CATEGORIES.ALL, + _id: mongoose.Types.ObjectId.createFromTime(168) + }, + patchSelfReviewerStatusById: { + requestType: Constants.REQUEST_TYPES.PATCH, + uri: "/api/hacker/reviewerStatus/" + Constants.ROLE_CATEGORIES.SELF, + _id: mongoose.Types.ObjectId.createFromTime(169) + }, + patchAnyReviewerStatus2ById: { + requestType: Constants.REQUEST_TYPES.PATCH, + uri: "/api/hacker/reviewerStatus2/" + Constants.ROLE_CATEGORIES.ALL, + _id: mongoose.Types.ObjectId.createFromTime(168) + }, + patchSelfReviewerStatus2ById: { + requestType: Constants.REQUEST_TYPES.PATCH, + uri: "/api/hacker/reviewerStatus2/" + Constants.ROLE_CATEGORIES.SELF, + _id: mongoose.Types.ObjectId.createFromTime(169) + }, + // 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/docs/api/api_data.js b/docs/api/api_data.js index df439b86..c2fa3ded 100644 --- a/docs/api/api_data.js +++ b/docs/api/api_data.js @@ -2117,6 +2117,426 @@ define({ "api": [ } ] }, + { + "type": "patch", + "url": "/hacker/reviewerStatus/:id", + "title": "update a hacker's reviewer status", + "name": "patchHackerReviewerStatus", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerStatus", + "description": "
Reviewer status of the hacker's application ("None"|"Poor"|"Weak"|"Poor"|"Average"|"Strong"|"Outstanding"|"Whitelist")
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"status\": \"Yes\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerStatus/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerStatus/:id", + "title": "update a hacker's reviewer status", + "name": "patchHackerReviewerStatus", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerStatus", + "description": "Reviewer status of the hacker's application ("None"|"Poor"|"Weak"|"Poor"|"Average"|"Strong"|"Outstanding"|"Whitelist")
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerStatus\": \"Poor\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerStatus/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerStatus2/:id", + "title": "update a hacker's reviewer status (2)", + "name": "patchHackerReviewerStatus2", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerStatus2", + "description": "Reviewer status of the hacker's application ("None"|"Poor"|"Weak"|"Poor"|"Average"|"Strong"|"Outstanding"|"Whitelist")
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerStatus2\": \"Outstanding\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerStatus2/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerName/:id", + "title": "update a hacker's reviewer name", + "name": "patchHackerReviewerName", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerName", + "description": "Reviewer's name of the hacker's application
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerName\": \"Jane Doe\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerName/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerName2/:id", + "title": "update a hacker's reviewer name (2)", + "name": "patchHackerReviewerName2", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerName2", + "description": "Reviewer's name (2) of the hacker's application
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerName2\": \"John Doe\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerName2/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerComments/:id", + "title": "update a hacker's reviewer's comments", + "name": "patchHackerReviewerComments", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerComments", + "description": "Reviewer's comments of the hacker's application
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerComments\": \"The application was good and expressed a strong desire and aspiration of joining the team.\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerComments/:id" + } + ] + }, + { + "type": "patch", + "url": "/hacker/reviewerComments2/:id", + "title": "update a hacker's reviewer's comments (2)", + "name": "patchHackerReviewerComments2", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "reviewerComments2", + "description": "Reviewer's comments (2) of the hacker's application
" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "Hacker object
" + } + ] + }, + "examples": [ + { + "title": "Success-Response:", + "content": "{\n \"message\": \"Changed hacker information\",\n \"data\": {\n \"reviewerComments2\": \"The application was good and expressed a strong desire and aspiration of joining the team.\"\n }\n}", + "type": "object" + } + ] + }, + "permission": [ + { + "name": "Administrator" + } + ], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/reviewerComments2/:id" + } + ] + }, { "type": "post", "url": "/hacker/resume/:id", diff --git a/docs/api/api_data.json b/docs/api/api_data.json index 6c9d27c7..c32148c9 100644 --- a/docs/api/api_data.json +++ b/docs/api/api_data.json @@ -1610,7 +1610,7 @@ "examples": [ { "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"status\": \"Applied\",\n \"application\":{\n \"general\":{\n \"school\": \"McGill University\",\n \"degree\": \"Undergraduate\",\n \"fieldOfStudy\": \"Computer Science\",\n \"graduationYear\": \"2021\",\n \"jobInterest\":\"Internship\",\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n },\n \"shortAnswer\": {\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"question1\": \"I love McHacks\",\n \"question2\":\"Pls accept me\",\n \"previousHackathons\": \"5\",\n \"comments\":\"hi!\",\n },\n \"other:\" {\n \"gender\": \"male\",\n \"ethnicity\": \"Asian or Pacific Islander\",\n \"sendEmail\": true,\n \"privacyPolicy\": true,\n \"codeOfConduct\": true,\n }\n \"accommodation\": {\n \"travel\": 0\n },\n \"location:\" {\n \"timeZone\": \"GMT-5\",\n \"country\": \"Canada\",\n \"city\": \"Montreal\",\n }\n }\n }\n }", + "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"status\": \"Applied\",\n \"application\":{\n \"general\":{\n \"school\": \"McGill University\",\n \"degree\": \"Undergraduate\",\n \"fieldOfStudy\": \"Computer Science\",\n \"graduationYear\": \"2021\",\n \"jobInterest\":\"Internship\",\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n },\n \"shortAnswer\": {\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"question1\": \"I love McHacks\",\n \"question2\":\"Pls accept me\",\n \"previousHackathons\": \"5\",\n \"comments\":\"hi!\",\n },\n \"other:\" {\n \"gender\": \"male\",\n \"ethnicity\": \"Asian or Pacific Islander\",\n \"sendEmail\": false,\n \"privacyPolicy\": true,\n \"codeOfConduct\": true,\n }\n \"accommodation\": {\n \"travel\": 0\n },\n \"location:\" {\n \"timeZone\": \"GMT-5\",\n \"country\": \"Canada\",\n \"city\": \"Montreal\",\n }\n }\n }\n }", "type": "object" } ] @@ -2117,6 +2117,7 @@ } ] }, + { "type": "post", "url": "/hacker/resume/:id", diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index f23a346a..145c511c 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -3,6 +3,11 @@ 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"); +const { HACKER_DEFAULT_REVIEWER } = HACKER_REVIEWER_NAMES.find(r => r === ''); // shim to allow us to use Promise.allSettled require("promise.allsettled").shim(); @@ -105,6 +110,12 @@ function parseConfirmation(req, res, next) { /** * @function addDefaultStatus * @param {{body: {hackerDetails: {status: String}}}} req + * @param {{body: {hackerDetails: {reviewerStatus: HACKER_REVIEWER_STATUSES}}}} req + * @param {{body: {hackerDetails: {reviewerStatus2: HACKER_REVIEWER_STATUSES}}}} req + * @param {{body: {hackerDetails: {reviewerName: HACKER_REVIEWER_NAMES}}}} req + * @param {{body: {hackerDetails: {reviewerName2: HACKER_REVIEWER_NAMES}}}} req + * @param {{body: {hackerDetails: {reviewerComments: String}}}} req + * @param {{body: {hackerDetails: {reviewerComments2: String}}}} req * @param {JSON} res * @param {(err?)=>void} next * @return {void} @@ -112,6 +123,12 @@ function parseConfirmation(req, res, next) { */ function addDefaultStatus(req, res, next) { req.body.hackerDetails.status = "Applied"; + req.body.hackerDetails.reviewerStatus = HACKER_REVIEWER_STATUS_NONE; + req.body.hackerDetails.reviewerStatus2 = HACKER_REVIEWER_STATUS_NONE; + req.body.hackerDetails.reviewerName = HACKER_DEFAULT_REVIEWER; + req.body.hackerDetails.reviewerName2 = HACKER_DEFAULT_REVIEWER; + req.body.hackerDetails.reviewerComments = ""; + req.body.hackerDetails.reviewerComments2 = ""; return next(); } @@ -724,6 +741,96 @@ 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 created before the cutoff date + const hackers = await hackerModel.find({ + _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 @@ -895,6 +1002,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/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 05a3b413..fc1283f3 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -306,6 +306,52 @@ module.exports = { false ) ], + updateReviewerStatusValidator: [ + VALIDATOR.enumValidator( + "body", + "reviewerStatus", + Constants.HACKER_REVIEWER_STATUSES, + false + ) + ], + updateReviewerStatus2Validator: [ + VALIDATOR.enumValidator( + "body", + "reviewerStatus2", + Constants.HACKER_REVIEWER_STATUSES, + false + ) + ], + updateReviewerNameValidator: [ + VALIDATOR.stringValidator( + "body", + "reviewerName", + Constants.HACKER_REVIEWER_NAMES, + false + ) + ], + updateReviewerName2Validator: [ + VALIDATOR.stringValidator( + "body", + "reviewerName2", + Constants.HACKER_REVIEWER_NAMES, + false + ) + ], + updateReviewerCommentsValidator: [ + VALIDATOR.stringValidator( + "body", + "reviewerComments", + false + ) + ], + updateReviewerComments2Validator: [ + VALIDATOR.stringValidator( + "body", + "reviewerComments2", + false + ) + ], checkInStatusValidator: [ VALIDATOR.enumValidator( "body", diff --git a/models/hacker.model.js b/models/hacker.model.js index 51171616..38e399f2 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -15,6 +15,40 @@ const HackerSchema = new mongoose.Schema({ required: true, default: "None" }, + reviewerStatus: { + type: String, + enum: Constants.HACKER_REVIEWER_STATUSES, + required: false, + default: Constants.HACKER_REVIEWER_STATUS_NONE + }, + reviewerStatus2: { + type: String, + enum: Constants.HACKER_REVIEWER_STATUSES, + required: false, + default: Constants.HACKER_REVIEWER_STATUS_NONE + }, + reviewerName: { + type: String, + enum: Constants.HACKER_REVIEWER_NAMES, + required: false, + default: Constants.HACKER_REVIEWER_NAMES.find(r => r === '') + }, + reviewerName2: { + type: String, + enum: Constants.HACKER_REVIEWER_NAMES, + required: false, + default: Constants.HACKER_REVIEWER_NAMES.find(r => r === '') + }, + reviewerComments: { + type: String, + required: false, + default: "" + }, + reviewerComments2: { + type: String, + required: false, + default: "" + }, application: { general: { school: { diff --git a/routes/api/hacker.js b/routes/api/hacker.js index 0fe125a6..40bcc2d7 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -299,6 +299,206 @@ 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( + // 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 + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [reviewerStatus] Reviewer status of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "reviewerStatus": "Outstanding" + * } + * } + * @apiPermission Administrator + */ + hackerRouter.route("/reviewerStatus/:id").patch( + 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.updateHacker, + Controllers.Hacker.updatedHacker + ); + + /** + * @api {patch} /hacker/reviewerStatus2/:id update a hacker's reviewer status 2 + * @apiName patchHackerReviewerStatus2 + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [reviewerStatus2] Reviewer status of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "reviewerStatus2": "Outstanding" + * } + * } + * @apiPermission Administrator + */ + hackerRouter.route("/reviewerStatus2/:id").patch( + Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateReviewerStatus2Validator, + // Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Controllers.Hacker.updatedHacker + ); + + /** + * @api {patch} /hacker/reviewerName/:id update a hacker's reviewer name + * @apiName patchHackerReviewerName + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [reviewerName] Reviewer name of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") + * @apiSuccess {string} message Success message + * @apiSuccess {string} data name + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "reviewerName": "John Doe" + * } + * } + * @apiPermission Administrator + */ + hackerRouter.route("/reviewerName/:id").patch( + Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateReviewerNameValidator, + // Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Controllers.Hacker.updatedHacker +); + +/** +* @api {patch} /hacker/reviewerName2/:id update a hacker's reviewer name +* @apiName patchHackerReviewerName2 +* @apiGroup Hacker +* @apiVersion 0.0.9 +* +* @apiParam (body) {string} [reviewerName2] Reviewer name of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") +* @apiSuccess {string} message Success message +* @apiSuccess {string} data name +* @apiSuccessExample {object} Success-Response: +* { +* "message": "Changed hacker information", +* "data": { +* "reviewerName2": "John Doe" +* } +* } +* @apiPermission Administrator +*/ +hackerRouter.route("/reviewerName2/:id").patch( + Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateReviewerName2Validator, + // Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Controllers.Hacker.updatedHacker +); + +/** +* @api {patch} /hacker/reviewerComments/:id update a hacker's reviewer comments +* @apiName patchHackerReviewerComments +* @apiGroup Hacker +* @apiVersion 0.0.9 +* +* @apiParam (body) {string} [reviewerComments] Reviewer comments of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") +* @apiSuccess {string} message Success message +* @apiSuccess {string} data name +* @apiSuccessExample {object} Success-Response: +* { +* "message": "Changed hacker information", +* "data": { +* "reviewerComments": "John Doe" +* } +* } +* @apiPermission Administrator +*/ +hackerRouter.route("/reviewerComments/:id").patch( + Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateReviewerCommentsValidator, + // Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Controllers.Hacker.updatedHacker +); + +/** +* @api {patch} /hacker/reviewerComments/:id update a hacker's reviewer comments +* @apiName patchHackerReviewerComments +* @apiGroup Hacker +* @apiVersion 0.0.9 +* +* @apiParam (body) {string} [reviewerComments] Reviewer comments of the hacker's application ("None"|"Poor"|"Weak"|"Average"|"Strong"|"Outstanding"|"Whitelist") +* @apiSuccess {string} message Success message +* @apiSuccess {string} data name +* @apiSuccessExample {object} Success-Response: +* { +* "message": "Changed hacker information", +* "data": { +* "reviewerComments": "John Doe" +* } +* } +* @apiPermission Administrator +*/ +hackerRouter.route("/reviewerComments2/:id").patch( + Middleware.Validator.RouteParam.idValidator, + // Middleware.Auth.ensureAuthenticated(), + // Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateReviewerComments2Validator, + // Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Controllers.Hacker.updatedHacker +); + /** * @api {patch} /hacker/accept/:id accept a Hacker * @apiName acceptHacker diff --git a/scripts/accept_script.py b/scripts/accept_script.py index f76f445e..2e3e47b1 100644 --- a/scripts/accept_script.py +++ b/scripts/accept_script.py @@ -21,6 +21,59 @@ '6': 'Withdrawn', '7': 'Checked-in' } +VALID_REVIEWER_STATUSES = { + '1': 'None', + '2': 'Poor', + '3': 'Weak', + '4': 'Average', + '5': 'Strong', + '6': 'Outstanding', + '7': 'Whitelist' +} +VALID_REVIEWER_NAME = { + '0': '', + '1': 'Amy', + '2': 'Carolyn', + '3': 'Clara', + '4': 'Debo', + '5': 'Deon', + '6': 'Doaa', + '7': 'Emily', + '8': 'Emma', + '9': 'Ethan', + '10': 'Evan', + '11': 'Finnley', + '12': 'Gabriel', + '13': 'Ian', + '14': 'Inaya', + '15': 'Jake', + '16': 'Jamie', + '17': 'Jane J.', + '18': 'Jane K.', + '19': 'Jeffrey', + '20': 'Joshua', + '21': 'Jyothsna', + '22': 'Khyati', + '23': 'Michael', + '24': 'Mika', + '25': 'Mubeen', + '26': 'Mira', + '27': 'Oishika', + '28': 'Olivia', + '29': 'Qi', + '30': 'Rémi', + '31': 'Sebastian', + '32': 'Shirley', + '33': 'Sihan', + '34': 'Siva', + '35': 'Snigdha', + '36': 'Stephanie', + '37': 'Tavi', + '38': 'Tina', + '39': 'Vipul', + '40': 'Yue Qian', +} + BATCH_ACTIONS = { '1': 'updateStatus', '2': 'dayOf', @@ -28,7 +81,8 @@ '4': 'downloadResume', '5': 'inviteUsers', '6': 'getHackers', - '7': 'acceptHackers' + '7': 'acceptHackers', + '8': 'updateReviewerStatus', } LOG_VERBOSITIES = { '0': 'None', @@ -202,6 +256,71 @@ def status(prefixStr) -> str: ) return initial_status +def reviewerStatus(prefixStr) -> str: + reviewerStatus_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_STATUSES.items()] + initial_reviewerStatus = requestUntilSuccess( + 'Input {0} reviewerStatus:\n{1}'.format(prefixStr, ''.join(reviewerStatus_list)), + 'Invalid {0} reviewerStatus'.format(prefixStr), + lambda x: x in VALID_REVIEWER_STATUSES.keys(), + lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerStatus + +def reviewerStatus2(prefixStr) -> str: + reviewerStatus2_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_STATUSES.items()] + initial_reviewerStatus2 = requestUntilSuccess( + 'Input {0} reviewerStatus2:\n{1}'.format(prefixStr, ''.join(reviewerStatus2_list)), + 'Invalid {0} reviewerStatus2'.format(prefixStr), + lambda x: x in VALID_REVIEWER_STATUSES.keys(), + lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerStatus2 + +def reviewerName(prefixStr) -> str: + reviewerName_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_NAME.items()] + initial_reviewerName = requestUntilSuccess( + 'Input {0} reviewerName:\n{1}'.format(prefixStr, ''.join(reviewerName_list)), + 'Invalid {0} reviewerName'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerName + +def reviewerName2(prefixStr) -> str: + reviewerName2_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_NAME.items()] + initial_reviewerName2 = requestUntilSuccess( + 'Input {0} reviewerName2:\n{1}'.format(prefixStr, ''.join(reviewerName2_list)), + 'Invalid {0} reviewerName2'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerName2 + +def reviewerComments(prefixStr) -> str: + reviewerComments_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerComments = requestUntilSuccess( + 'Input {0} reviewerComments:\n{1}'.format(prefixStr, ''.join(reviewerComments_list)), + 'Invalid {0} reviewerComments'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerComments + +def reviewerComments2(prefixStr) -> str: + reviewerComments2_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerComments2 = requestUntilSuccess( + 'Input {0} reviewerComments2:\n{1}'.format(prefixStr, ''.join(reviewerComments2_list)), + 'Invalid {0} reviewerComments2'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerComments2 def batchAction() -> str: status_list = ['{0}: {1}\n'.format(k, v) for k, v in BATCH_ACTIONS.items()] @@ -230,6 +349,48 @@ def hasValidStatus(status, hackerInfo): return False +def hasValidReviewerStatus(reviewerStatus, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerStatus'] == reviewerStatus: + return True + else: + return False + + +def hasValidReviewerStatus2(reviewerStatus2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerStatus2'] == reviewerStatus2: + return True + else: + return False + + +def hasValidReviewerName(reviewerName, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerName'] == reviewerName: + return True + else: + return False + + +def hasValidReviewerName2(reviewerName2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerName2'] == reviewerName2: + return True + else: + return False + + +def hasValidReviewerComments(reviewerComments, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerComments'] == reviewerComments: + return True + else: + return False + + +def hasValidReviewerComments2(reviewerComments2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerComments2'] == reviewerComments2: + return True + else: + return False + + def search(model: str = 'hacker', query=[], expand: bool = True): q = json.dumps(query) expand = 'true' if expand else 'false' @@ -309,30 +470,180 @@ def updateStatus(): _print('could not find {0}'.format( ID), 1, index, len(HACKER_IDs)) +def updateReviewerStatus(): + INITIAL_REVIEWER_STATUS = reviewerStatus('initial') + NEW_REVIEWER_STATUS = reviewerStatus('new') + HACKER_IDs = getIdList() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerStatus = hasValidReviewerStatus(INITIAL_REVIEWER_STATUS, hacker) + if validReviewerStatus: + r = s.patch('{0}/api/hacker/reviewerStatus/{1}'.format(API_URL, ID), + {"reviewerStatus": NEW_REVIEWER_STATUS}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_STATUS, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) -def sendDayOfEmail(): - INITIAL_STATUS = status('initial') - HACKER_IDs = loadIDs() +def updateReviewerStatus2(): + INITIAL_REVIEWER_STATUS2 = reviewerStatus2('initial') + NEW_REVIEWER_STATUS2 = reviewerStatus2('new') + HACKER_IDs = getIdList() for index, ID in enumerate(HACKER_IDs): # so that we aren't 0-based index index = index + 1 hacker = getHacker(ID) - validStatus = hasValidStatus(INITIAL_STATUS, hacker) - if validStatus: - r = s.post( - '{0}/api/hacker/email/dayOf/{1}'.format(API_URL, ID)) - if r.status_code != 200: - _print('cannot send email to {0}'.format( - ID), 1, index, len(HACKER_IDs)) - else: - _print('Sent email to {0}'.format( - ID), 3, index, len(HACKER_IDs)) + validReviewerStatus2 = hasValidReviewerStatus2(INITIAL_REVIEWER_STATUS2, hacker) + if validReviewerStatus2: + r = s.patch('{0}/api/hacker/reviewerStatus2/{1}'.format(API_URL, ID), + {"reviewerStatus2": NEW_REVIEWER_STATUS2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_STATUS2, ID), 3, index, len(HACKER_IDs)) elif hacker is not None: - _print('Sent invalid status for {0}'.format( + _print('invalid status for {0}'.format( ID), 1, index, len(HACKER_IDs)) else: - _print('Could not find {0}'.format( + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerName(): + INITIAL_REVIEWER_NAME = reviewerName('initial') + NEW_REVIEWER_NAME = reviewerName('new') + HACKER_IDs = getIdList() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerName = hasValidReviewerName(INITIAL_REVIEWER_NAME, hacker) + if validReviewerName: + r = s.patch('{0}/api/hacker/reviewerName/{1}'.format(API_URL, ID), + {"reviewerName": NEW_REVIEWER_NAME}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_NAME, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerName2(): + INITIAL_REVIEWER_NAME2 = reviewerName2('initial') + NEW_REVIEWER_NAME2 = reviewerName2('new') + HACKER_IDs = getIdList() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerName2 = hasValidReviewerName2(INITIAL_REVIEWER_NAME2, hacker) + if validReviewerName2: + r = s.patch('{0}/api/hacker/reviewerName2/{1}'.format(API_URL, ID), + {"reviewerName2": NEW_REVIEWER_NAME2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_NAME2, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerComments(): + INITIAL_REVIEWER_COMMENTS = reviewerComments('initial') + NEW_REVIEWER_COMMENTS = reviewerComments('new') + HACKER_IDs = getIdList() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerComments = hasValidReviewerComments(INITIAL_REVIEWER_COMMENTS, hacker) + if validReviewerComments: + r = s.patch('{0}/api/hacker/reviewerComments/{1}'.format(API_URL, ID), + {"reviewerComments": NEW_REVIEWER_COMMENTS}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_COMMENTS, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerComments2(): + INITIAL_REVIEWER_COMMENTS2 = reviewerComments2('initial') + NEW_REVIEWER_COMMENTS2 = reviewerComments2('new') + HACKER_IDs = getIdList() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerComments2 = hasValidReviewerComments2(INITIAL_REVIEWER_COMMENTS2, hacker) + if validReviewerComments2: + r = s.patch('{0}/api/hacker/reviewerComments/{1}'.format(API_URL, ID), + {"reviewerComments": NEW_REVIEWER_COMMENTS2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_COMMENTS2, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( ID), 1, index, len(HACKER_IDs)) + + +# def sendDayOfEmail(): +# INITIAL_STATUS = status('initial') +# HACKER_IDs = loadIDs() +# for index, ID in enumerate(HACKER_IDs): +# # so that we aren't 0-based index +# index = index + 1 +# hacker = getHacker(ID) +# validStatus = hasValidStatus(INITIAL_STATUS, hacker) +# if validStatus: +# r = s.post( +# '{0}/api/hacker/email/dayOf/{1}'.format(API_URL, ID)) +# if r.status_code != 200: +# _print('cannot send email to {0}'.format( +# ID), 1, index, len(HACKER_IDs)) +# else: +# _print('Sent email to {0}'.format( +# ID), 3, index, len(HACKER_IDs)) +# elif hacker is not None: +# _print('Sent invalid status for {0}'.format( +# ID), 1, index, len(HACKER_IDs)) +# else: +# _print('Could not find {0}'.format( +# ID), 1, index, len(HACKER_IDs)) def sendWeekOfEmail(): @@ -464,6 +775,18 @@ def getHackers(): getHackers() elif BATCH_ACTION == 'acceptHackers': acceptFromEmails() + elif BATCH_ACTION == 'updateReviewerStatus': + updateReviewerStatus() + elif BATCH_ACTION == 'updateReviewerStatus2': + updateReviewerStatus2() + elif BATCH_ACTION == 'updateReviewerName': + updateReviewerName() + elif BATCH_ACTION == 'updateReviewerName2': + updateReviewerName2() + elif BATCH_ACTION == 'updateReviewerComments': + updateReviewerComments() + elif BATCH_ACTION == 'updateReviewerComments2': + updateReviewerComments2() print('Finished {0}'.format(BATCH_ACTION)) except Exception as e: _print('Failed to perform action {0}: {1}'.format( diff --git a/scripts/batch_scripts.py b/scripts/batch_scripts.py index 387aa51b..518e483b 100644 --- a/scripts/batch_scripts.py +++ b/scripts/batch_scripts.py @@ -1,6 +1,6 @@ #!/bin/bash/python3 import base64 -from bson import ObjectId +from json import ObjectId import csv import getpass import json @@ -21,13 +21,23 @@ '6': 'Withdrawn', '7': 'Checked-in' } +VALID_REVIEWER_STATUSES = { + '1': 'None', + '2': 'Poor', + '3': 'Weak', + '4': 'Average', + '5': 'Strong', + '6': 'Outstanding', + '7': 'Whitelist' +} BATCH_ACTIONS = { '1': 'updateStatus', '2': 'dayOf', '3': 'weekOf', '4': 'downloadResume', '5': 'inviteUsers', - '6': 'getHackers' + '6': 'getHackers', + '7': 'updateReviewerStatus' } LOG_VERBOSITIES = { '0': 'None', @@ -212,6 +222,72 @@ def batchAction() -> str: ) return batch_action +def reviewerStatus(prefixStr) -> str: + reviewerStatus_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_STATUSES.items()] + initial_reviewerStatus = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerStatus_list)), + 'Invalid {0} status'.format(prefixStr), + lambda x: x in VALID_REVIEWER_STATUSES.keys(), + lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerStatus + +def reviewerStatus2(prefixStr) -> str: + reviewerStatus2_list = ['{0}: {1}\n'.format(k, v) + for k, v in VALID_REVIEWER_STATUSES.items()] + initial_reviewerStatus2 = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerStatus2_list)), + 'Invalid {0} status'.format(prefixStr), + lambda x: x in VALID_REVIEWER_STATUSES.keys(), + lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerStatus2 + +def reviewerName(prefixStr) -> str: + reviewerName_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerName = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerName_list)), + 'Invalid {0} status'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerName + +def reviewerName2(prefixStr) -> str: + reviewerName2_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerName2 = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerName2_list)), + 'Invalid {0} status'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerName2 + +def reviewerComments(prefixStr) -> str: + reviewerComments_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerComments = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerComments_list)), + 'Invalid {0} status'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerComments + +def reviewerComments2(prefixStr) -> str: + reviewerComments2_list = ['{0}: {1}\n'.format(k, v) + for k, v in str] + initial_reviewerComments2 = requestUntilSuccess( + 'Input {0} status:\n{1}'.format(prefixStr, ''.join(reviewerComments2_list)), + 'Invalid {0} status'.format(prefixStr), + # lambda x: x in VALID_REVIEWER_STATUSES.keys(), + # lambda x: VALID_REVIEWER_STATUSES[x] + ) + return initial_reviewerComments2 + def getHacker(ID): r = s.get('{0}/api/hacker/{1}'.format(API_URL, ID)) @@ -228,6 +304,42 @@ def hasValidStatus(status, hackerInfo): else: return False +def hasValidReviewerStatus(reviewerStatus, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerStatus'] == reviewerStatus: + return True + else: + return False + +def hasValidReviewerStatus2(reviewerStatus2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerStatus2'] == reviewerStatus2: + return True + else: + return False + +def hasValidReviewerName(reviewerName, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerName'] == reviewerName: + return True + else: + return False + +def hasValidReviewerName2(reviewerName2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerName2'] == reviewerName2: + return True + else: + return False + +def hasValidReviewerComments(reviewerComments, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerComments'] == reviewerComments: + return True + else: + return False + +def hasValidReviewerComments2(reviewerComments2, hackerInfo): + if hackerInfo is not None and hackerInfo['reviewerComments2'] == reviewerComments2: + return True + else: + return False + def search(model: str = 'hacker', query=[], expand: bool = True): q = json.dumps(query) @@ -268,6 +380,156 @@ def updateStatus(): _print('could not find {0}'.format( ID), 1, index, len(HACKER_IDs)) +def updateReviewerStatus(): + INITIAL_REVIEWER_STATUS = reviewerStatus('initial') + NEW_REVIEWER_STATUS = reviewerStatus('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerStatus = hasValidReviewerStatus(INITIAL_REVIEWER_STATUS, hacker) + if validReviewerStatus: + r = s.patch('{0}/api/hacker/reviewerStatus/{1}'.format(API_URL, ID), + {"reviewerStatus": NEW_REVIEWER_STATUS}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_STATUS, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerStatus2(): + INITIAL_REVIEWER_STATUS2 = reviewerStatus2('initial') + NEW_REVIEWER_STATUS2 = reviewerStatus2('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerStatus2 = hasValidReviewerStatus2(INITIAL_REVIEWER_STATUS2, hacker) + if validReviewerStatus2: + r = s.patch('{0}/api/hacker/reviewerStatus2/{1}'.format(API_URL, ID), + {"reviewerStatus2": NEW_REVIEWER_STATUS2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_STATUS2, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerName(): + INITIAL_REVIEWER_NAME = reviewerName('initial') + NEW_REVIEWER_NAME = reviewerName('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerName = hasValidReviewerName(INITIAL_REVIEWER_NAME, hacker) + if validReviewerName: + r = s.patch('{0}/api/hacker/reviewerName/{1}'.format(API_URL, ID), + {"reviewerName": NEW_REVIEWER_NAME}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_NAME, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerName2(): + INITIAL_REVIEWER_NAME2 = reviewerName2('initial') + NEW_REVIEWER_NAME2 = reviewerName2('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerName2 = hasValidReviewerName2(INITIAL_REVIEWER_NAME2, hacker) + if validReviewerName2: + r = s.patch('{0}/api/hacker/reviewerName2/{1}'.format(API_URL, ID), + {"reviewerName2": NEW_REVIEWER_NAME2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_NAME2, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerComments(): + INITIAL_REVIEWER_COMMENTS = reviewerComments('initial') + NEW_REVIEWER_COMMENTS = reviewerComments('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerComments = hasValidReviewerComments(INITIAL_REVIEWER_COMMENTS, hacker) + if validReviewerComments: + r = s.patch('{0}/api/hacker/reviewerComments/{1}'.format(API_URL, ID), + {"reviewerComments": NEW_REVIEWER_COMMENTS}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_COMMENTS, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + +def updateReviewerComments2(): + INITIAL_REVIEWER_COMMENTS2 = reviewerComments2('initial') + NEW_REVIEWER_COMMENTS2 = reviewerComments2('new') + HACKER_IDs = loadIDs() + for index, ID in enumerate(HACKER_IDs): + # so that we aren't 0-based index + index = index + 1 + hacker = getHacker(ID) + validReviewerComments2 = hasValidReviewerComments2(INITIAL_REVIEWER_COMMENTS2, hacker) + if validReviewerComments2: + r = s.patch('{0}/api/hacker/reviewerComments2/{1}'.format(API_URL, ID), + {"reviewerComments2": NEW_REVIEWER_COMMENTS2}) + # if r.status_code != 200: + # _print('cannot update status for {0}'.format( + # ID), 1, index, len(HACKER_IDs)) + # else: + _print('{0} {1}'.format( + NEW_REVIEWER_COMMENTS2, ID), 3, index, len(HACKER_IDs)) + elif hacker is not None: + _print('invalid status for {0}'.format( + ID), 1, index, len(HACKER_IDs)) + else: + _print('could not find {0}'.format( + ID), 1, index, len(HACKER_IDs)) + def sendDayOfEmail(): INITIAL_STATUS = status('initial') @@ -421,6 +683,18 @@ def getHackers(): inviteUsers() elif BATCH_ACTION == 'getHackers': getHackers() + elif BATCH_ACTION == 'updateReviewerStatus': + updateReviewerStatus() + elif BATCH_ACTION == 'updateReviewerStatus2': + updateReviewerStatus2() + elif BATCH_ACTION == 'updateReviewerName': + updateReviewerName() + elif BATCH_ACTION == 'updateReviewerName2': + updateReviewerName2() + elif BATCH_ACTION == 'updateReviewerComments': + updateReviewerComments() + elif BATCH_ACTION == 'updateReviewerComments2': + updateReviewerComments2() print('Finished {0}'.format(BATCH_ACTION)) except Exception as e: _print('Failed to perform action {0}: {1}'.format( diff --git a/tests/hacker.test.js b/tests/hacker.test.js index b344e86d..08b59fdb 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -1734,6 +1734,12 @@ describe("GET Hacker stats", function() { res.body.data.should.have.property("stats"); res.body.data.stats.should.have.property("total"); res.body.data.stats.should.have.property("status"); + res.body.data.stats.should.have.property("reviewerStatus"); + res.body.data.stats.should.have.property("reviewerStatus2"); + res.body.data.stats.should.have.property("reviewerName"); + res.body.data.stats.should.have.property("reviewerName2"); + res.body.data.stats.should.have.property("reviewerComments"); + res.body.data.stats.should.have.property("reviewerComments2"); res.body.data.stats.should.have.property("school"); res.body.data.stats.should.have.property("degree"); res.body.data.stats.should.have.property("gender");