Skip to content

Commit

Permalink
Basic waitlist functionality (#139)
Browse files Browse the repository at this point in the history
* added waitlist state to case checking

* updated bucket name

* added waitlist status to settings

* made fields optional

* ran linter
  • Loading branch information
pm3512 authored Dec 26, 2023
1 parent ddbf35e commit 74600e7
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/_enums/StorageBuckets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export enum StorageBucket {
RESUME = "2023-resumes",
PROFILE_PICTURES = "2023-profile-pictures",
RESUME = "2024-resumes",
PROFILE_PICTURES = "2024-profile-pictures",
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/_types/Settings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface ISettings extends Document {
timeConfirm?: string;
enableWhitelist?: number;
whitelistedEmails?: string[];
maxParticipants?: number;
autoWaitlist?: boolean;
waitlistText?: string;
acceptanceText?: string;
confirmationText?: string;
Expand Down
13 changes: 13 additions & 0 deletions src/controllers/SettingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ export const handleGetSettings = async (
res.json(settings.toJSON());
};

/**
* Express handler for getting the waitlist status
*/
export const handleGetWaitlistStatus = async (
req: Request,
res: Response
): Promise<void> => {
const settings = await getSettings();
res.json({
waitlist: settings.autoWaitlist,
});
};

/**
* Express handler for updating settings
*/
Expand Down
5 changes: 4 additions & 1 deletion src/controllers/TestAccount/accountCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export async function createTestAccountWithStatus(
await user.save();

let profile: IProfile | undefined = undefined;
if (doesStatusImply(status, Status.COMPLETED_PROFILE)) {
if (
doesStatusImply(status, Status.COMPLETED_PROFILE) ||
status === Status.WAITLISTED
) {
profile = await createTestProfileForUser(user);

if (doesStatusImply(status, Status.CONFIRMED)) {
Expand Down
28 changes: 24 additions & 4 deletions src/controllers/UsersController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ export const admitUser = async (req: Request, res: Response): Promise<void> => {
if (user == null) {
return bad(res, "User not found");
}
if (!user.hasStatus(Status.COMPLETED_PROFILE)) {
if (
!user.hasStatus(Status.COMPLETED_PROFILE) &&
!user.hasStatus(Status.WAITLISTED)
) {
return bad(res, "User has not completed their profile yet!");
}

Expand Down Expand Up @@ -169,7 +172,14 @@ export const admitAllUsers = async (
const tartanhacks = await getTartanHacks();

const users = await User.find({
status: Status.COMPLETED_PROFILE,
$or: [
{
status: Status.COMPLETED_PROFILE,
},
{
status: Status.WAITLISTED,
},
],
});

const promises = [];
Expand Down Expand Up @@ -200,7 +210,14 @@ export const rejectAllUsers = async (
const tartanhacks = await getTartanHacks();

const users = await User.find({
status: Status.COMPLETED_PROFILE,
$or: [
{
status: Status.COMPLETED_PROFILE,
},
{
status: Status.WAITLISTED,
},
],
});

const promises = [];
Expand Down Expand Up @@ -234,7 +251,10 @@ export const rejectUser = async (
if (user == null) {
return bad(res, "User not found");
}
if (!user.hasStatus(Status.COMPLETED_PROFILE)) {
if (
!user.hasStatus(Status.COMPLETED_PROFILE) &&
!user.hasStatus(Status.WAITLISTED)
) {
return bad(res, "User has not completed their profile yet!");
}
const profile = await Profile.findOne({
Expand Down
2 changes: 2 additions & 0 deletions src/models/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const Settings: Schema<ISettings> = new Schema(
timeConfirm: Date,
enableWhitelist: Boolean,
whitelistedEmails: [String],
maxParticipants: Number,
autoWaitlist: Boolean,
waitlistText: String,
acceptanceText: String,
confirmationText: String,
Expand Down
22 changes: 22 additions & 0 deletions src/routes/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getConfirmTime,
getOpenTime,
handleGetSettings,
handleGetWaitlistStatus,
updateSettings,
} from "../controllers/SettingsController";
import { asyncCatch } from "../util/asyncCatch";
Expand Down Expand Up @@ -149,4 +150,25 @@ router.get("/time/close", asyncCatch(getCloseTime));
*/
router.get("/time/confirm", asyncCatch(getConfirmTime));

/**
* @swagger
* /settings/waitlist:
* get:
* summary: Check whether the waitlist is open
* tags: [Settings Module]
* description: Returns whether newly registered users should be put on a waitlist. Access - Public
* security:
* - apiKeyAuth: []
* responses:
* 200:
* description: Success.
* 400:
* description: Bad request
* 404:
* description: User does not exist.
* 500:
* description: Internal Server Error.
*/
router.get("/waitlist", asyncCatch(handleGetWaitlistStatus));

export default router;
17 changes: 10 additions & 7 deletions src/services/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,29 @@ export const computeAnalytics = async (): Promise<Stats> => {
}

// Count verified
const isVerified = doesStatusImply(status, Status.VERIFIED);
const isVerified =
status == Status.WAITLISTED || doesStatusImply(status, Status.VERIFIED);
stats.verified += isVerified ? 1 : 0;

// Count submitted
const isProfileComplete = doesStatusImply(
status,
Status.COMPLETED_PROFILE
);
const isProfileComplete =
status == Status.WAITLISTED ||
doesStatusImply(status, Status.COMPLETED_PROFILE);
stats.submitted += isProfileComplete ? 1 : 0;

if (isProfileComplete && !isCMU) {
isCMU = profile.school.includes("Carnegie Mellon");
}

// Count accepted
const isAdmitted = doesStatusImply(status, Status.ADMITTED);
const isAdmitted =
status == Status.WAITLISTED || doesStatusImply(status, Status.ADMITTED);
stats.admitted += isAdmitted ? 1 : 0;

// Count confirmed
const isConfirmed = doesStatusImply(status, Status.CONFIRMED);
const isConfirmed =
status == Status.WAITLISTED ||
doesStatusImply(status, Status.CONFIRMED);
stats.confirmed += isConfirmed ? 1 : 0;

// Count declined
Expand Down
8 changes: 8 additions & 0 deletions src/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
"value": [],
"description": "List of enabled email domains. Ignored if enableWhitelist is false."
},
"maxParticipants": {
"value": 450,
"description": "Number of participants that need to confirm before opening the waitlist"
},
"autoWaitlist": {
"value": false,
"description": "Whether or not to automatically place new participants on the waitlist"
},
"waitlistText": {
"value": "You have been placed on the waitlist!",
"description": "Email sent to participants who were placed on the waitlist"
Expand Down

0 comments on commit 74600e7

Please sign in to comment.