diff --git a/services/registration/src/common/adjustScores.ts b/services/registration/src/common/adjustScores.ts index cd7c556..557cae9 100644 --- a/services/registration/src/common/adjustScores.ts +++ b/services/registration/src/common/adjustScores.ts @@ -26,6 +26,11 @@ type CriteriaScores = { score: number; }[]; +// distinguishable referral bonus (points should never be this high for any other reason) +// can we make a separate field for this? yes, however that requires handling a bunch of +// edge cases so surely its okay to be lazy for now +export const REFERRAL_BONUS = 100; + /** The minimum grading score an essay can get. */ const MIN_GRADING_SCORE = 1; /** The maximum grading score an essay can get. */ diff --git a/services/registration/src/routes/grading.ts b/services/registration/src/routes/grading.ts index ad0c376..55a235f 100644 --- a/services/registration/src/routes/grading.ts +++ b/services/registration/src/routes/grading.ts @@ -6,16 +6,31 @@ import { Types } from "mongoose"; import _ from "lodash"; import { ApplicationModel, Essay, StatusType } from "../models/application"; +import { ReferralModel, ReferralStatusType } from "../models/referral"; import { GraderModel } from "../models/grader"; import { Review, ReviewModel } from "../models/review"; import { BranchModel, BranchType, GradingGroupType } from "../models/branch"; import { calibrationQuestionMapping, rubricMapping } from "../config"; -import { getCalibrationMapping } from "../common/adjustScores"; +import { getCalibrationMapping, REFERRAL_BONUS } from "../common/adjustScores"; const MAX_REVIEWS_PER_ESSAY = 2; // NOTE: No. of essays for each application. As such, will need to be updated whenever we add/remove essays. const ESSAY_COUNT = 4; +async function checkForReferralBonus(email?: string, hexathon?: Types.ObjectId) { + if (!email || !hexathon) { + return 0; + } + + const referral = await ReferralModel.exists({ + hexathon, + "status": ReferralStatusType.SUBMITTED, + "referralData.email": new RegExp(`^${_.escapeRegExp(email.trim())}$`, "i"), + }); + + return referral ? REFERRAL_BONUS : 0; +} + type AggregatedEssay = { applicationId: string; applicationBranch: string; @@ -304,7 +319,9 @@ gradingRouter.route("/actions/submit-review").post( if (allEssayReviews.length >= MAX_REVIEWS_PER_ESSAY * ESSAY_COUNT) { application.gradingComplete = true; const sumScores = allEssayReviews.reduce((prev, review) => prev + review.adjustedScore, 0); - application.finalScore = sumScores / allEssayReviews.length; + const baseScore = sumScores / allEssayReviews.length; + const referralBonus = await checkForReferralBonus(application.email, application.hexathon); + application.finalScore = baseScore + referralBonus; await application.save(); } diff --git a/services/registration/src/routes/referrals.ts b/services/registration/src/routes/referrals.ts index 905bae5..365d0c4 100644 --- a/services/registration/src/routes/referrals.ts +++ b/services/registration/src/routes/referrals.ts @@ -6,9 +6,50 @@ import { FilterQuery, isValidObjectId, Types } from "mongoose"; import { validateReferralData } from "../common/util"; import { Referral, ReferralModel, ReferralStatusType } from "../models/referral"; +import { ApplicationModel, StatusType } from "../models/application"; +import { REFERRAL_BONUS } from "../common/adjustScores"; export const referralRouter = express.Router(); +/* + The referral bonus gets applied after the last grader's review is submitted, + this handles the cases where someone is referred after their application is graded +*/ +async function applyReferralBonus( + email?: string, + hexathon?: Types.ObjectId, + referralId?: Types.ObjectId +) { + if (!email || !hexathon || !referralId) { + return; + } + + const emailRegex = new RegExp(`^${_.escapeRegExp(email.trim())}$`, "i"); + + const application = await ApplicationModel.findOne({ + hexathon, + email: emailRegex, + status: { $ne: StatusType.DRAFT }, + }); + + if (!application || !application.gradingComplete) { + return; + } + + const alreadyHasBonus = await ReferralModel.exists({ + hexathon, + "referralData.email": emailRegex, + "status": ReferralStatusType.SUBMITTED, + "_id": { $ne: referralId }, + }); + if (alreadyHasBonus) { + return; + } + + application.finalScore += REFERRAL_BONUS; + await application.save(); +} + referralRouter.route("/actions/create-referral").post( checkAbility("create", "Referral"), asyncHandler(async (req, res) => { @@ -255,6 +296,10 @@ referralRouter.route("/:id/actions/submit-referral").post( throw new BadRequestError("No referral exists with this id or you do not have permission."); } + if (existingReferral.status !== ReferralStatusType.DRAFT) { + throw new BadRequestError("This referral has already been submitted."); + } + let resume; if (existingReferral.referralData.resume) { resume = await apiCall( @@ -281,6 +326,12 @@ referralRouter.route("/:id/actions/submit-referral").post( { new: true, runValidators: true } ); + await applyReferralBonus( + existingReferral.referralData.email, + existingReferral.hexathon, + existingReferral._id + ); + return res.sendStatus(204); }) );