Skip to content

Commit 3b9b257

Browse files
committed
feat: added script for applicants
1 parent e1ba8c2 commit 3b9b257

8 files changed

+238
-265
lines changed

backend/typescript/migrations/2025.06.21T15.30.15.create-applicant-record.ts

Lines changed: 0 additions & 74 deletions
This file was deleted.

backend/typescript/migrations/2025.06.21T07.02.40.create-applicants.ts renamed to backend/typescript/migrations/2025.07.21T07.02.40.create-applicants.ts

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,78 @@
11
import { DataType } from "sequelize-typescript";
2-
2+
import { v4 as uuidv4 } from "uuid";
33
import { Migration } from "../umzug";
4+
import allApplications from "./applicationlist.json";
5+
import applicants from "./separateJSONs";
46

57
const TABLE_NAME = "applicants";
68

7-
// const SEEDED_DATA = [
8-
// {
9-
// id: "123",
10-
// academicOrCoop: "Academic",
11-
// academicYear: "2024",
12-
// email: "jj2huang@uwaterloo.ca",
13-
// firstName: "Jesse",
14-
// lastName: "Huang",
15-
// heardFrom: "LinkedIn",
16-
// locationPreference: "Waterloo",
17-
// program: "Computer Science",
18-
// pronouns: "he/him",
19-
// resumeUrl:
20-
// "https://www.youtube.com/watch?v=xvFZjo5PgG0&list=RDxvFZjo5PgG0&start_radio=1",
21-
// timesApplied: 1,
22-
// shortAnswerQuestions: ["hi", "bye"],
23-
// term: "S25",
24-
// submittedAt: "2025-06-21T07:02:40.000Z",
25-
// createdAt: new Date(),
26-
// updatedAt: new Date(),
27-
// },
28-
// ];
9+
// Converts timesApplied string to integer
10+
const convertTimesApplied: { [key: string]: number } = {
11+
"This is my first time!": 0,
12+
Once: 1,
13+
Twice: 2,
14+
"3 or more": 3,
15+
};
16+
17+
const MAX_SHORT_ANSWER_LENGTH = 255;
2918

3019
export const up: Migration = async ({ context: sequelize }) => {
3120
await sequelize.getQueryInterface().createTable(TABLE_NAME, {
3221
id: {
33-
type: DataType.STRING,
22+
type: DataType.STRING(4000),
3423
allowNull: false,
3524
primaryKey: true,
3625
},
3726
academicOrCoop: {
38-
type: DataType.STRING,
27+
type: DataType.STRING(4000),
3928
allowNull: false,
4029
},
4130
academicYear: {
42-
type: DataType.STRING,
31+
type: DataType.STRING(4000),
4332
allowNull: false,
4433
},
4534
email: {
46-
type: DataType.STRING,
35+
type: DataType.STRING(4000),
4736
allowNull: false,
4837
},
4938
firstName: {
50-
type: DataType.STRING,
39+
type: DataType.STRING(4000),
5140
allowNull: false,
5241
},
5342
lastName: {
54-
type: DataType.STRING,
43+
type: DataType.STRING(4000),
5544
allowNull: false,
5645
},
5746
heardFrom: {
58-
type: DataType.STRING,
47+
type: DataType.STRING(4000),
5948
allowNull: false,
6049
},
6150
locationPreference: {
62-
type: DataType.STRING,
51+
type: DataType.STRING(4000),
6352
allowNull: false,
6453
},
6554
program: {
66-
type: DataType.STRING,
55+
type: DataType.STRING(4000),
6756
allowNull: false,
6857
},
6958
pronouns: {
70-
type: DataType.STRING,
59+
type: DataType.STRING(4000),
7160
allowNull: false,
7261
},
7362
resumeUrl: {
74-
type: DataType.STRING,
63+
type: DataType.STRING(4000),
7564
allowNull: true,
7665
},
7766
timesApplied: {
7867
type: DataType.INTEGER,
7968
allowNull: false,
8069
},
8170
shortAnswerQuestions: {
82-
type: DataType.ARRAY(DataType.STRING),
71+
type: DataType.ARRAY(DataType.STRING(4000)),
8372
allowNull: true,
8473
},
8574
term: {
86-
type: DataType.STRING,
75+
type: DataType.STRING(4000),
8776
allowNull: false,
8877
},
8978
submittedAt: {
@@ -98,7 +87,12 @@ export const up: Migration = async ({ context: sequelize }) => {
9887
type: DataType.DATE,
9988
allowNull: false,
10089
},
90+
test: {
91+
type: DataType.BOOLEAN,
92+
defaultValue: false,
93+
},
10194
});
95+
await sequelize.getQueryInterface().bulkInsert(TABLE_NAME, applicants);
10296
};
10397

10498
export const down: Migration = async ({ context: sequelize }) => {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { DataType } from "sequelize-typescript";
2+
import { v4 as uuidv4 } from "uuid";
3+
import { Migration } from "../umzug";
4+
5+
import allApplications from "./applicationlist.json";
6+
import applicants from "./separateJSONs";
7+
import {
8+
ApplicationStatus,
9+
SkillCategory,
10+
ApplicantRole,
11+
EngineeringPositionTitles,
12+
DesignPositionTitles,
13+
ProductPositionTitles,
14+
CommunityPositionTitles,
15+
} from "../types";
16+
17+
const ALL_POSITION_TITLES = [
18+
...EngineeringPositionTitles,
19+
...DesignPositionTitles,
20+
...ProductPositionTitles,
21+
...CommunityPositionTitles,
22+
];
23+
24+
const MAX_SHORT_ANSWER_LENGTH = 255;
25+
26+
function normalizeApplicantRole(role: string): string {
27+
if (!role) return "President";
28+
const trimmed = role.trim();
29+
if (trimmed.toLowerCase() === "project developer") return "Developer";
30+
// Capitalize each word for comparison
31+
const formatted = trimmed.replace(/\b\w/g, (c) => c.toUpperCase());
32+
if (ALL_POSITION_TITLES.includes(formatted as any)) return formatted;
33+
return "President";
34+
}
35+
36+
type ApplicantRecord = {
37+
id: string;
38+
applicantId: string;
39+
position: string;
40+
roleSpecificQuestions: string[] | null;
41+
choice: number;
42+
status: ApplicationStatus;
43+
skillCategory: SkillCategory | null;
44+
extraInfo: string | null;
45+
};
46+
47+
const applicantRecords: ApplicantRecord[] = [];
48+
allApplications.forEach((app, idx) => {
49+
const applicantId = applicants[idx].id;
50+
51+
// First choice
52+
if (app.firstChoiceRole) {
53+
const roleSpecificQs =
54+
app.roleSpecificQuestions
55+
?.filter((q) => q.role === app.firstChoiceRole)
56+
?.flatMap(
57+
(q) =>
58+
q.questions?.map((question) =>
59+
JSON.stringify(question).slice(0, MAX_SHORT_ANSWER_LENGTH),
60+
) || [],
61+
) || [];
62+
applicantRecords.push({
63+
id: uuidv4(),
64+
applicantId,
65+
position: normalizeApplicantRole(app.firstChoiceRole),
66+
roleSpecificQuestions: roleSpecificQs.length > 0 ? roleSpecificQs : null,
67+
choice: 1,
68+
status: (app.status as ApplicationStatus) || "Applied",
69+
skillCategory: null,
70+
extraInfo: JSON.stringify({ adminReview: "seeded_admin_review" }),
71+
});
72+
}
73+
74+
// Second choice
75+
if (app.secondChoiceRole) {
76+
const roleSpecificQs =
77+
app.roleSpecificQuestions
78+
?.filter((q) => q.role === app.secondChoiceRole)
79+
?.flatMap(
80+
(q) =>
81+
q.questions?.map((question) =>
82+
JSON.stringify(question).slice(0, MAX_SHORT_ANSWER_LENGTH),
83+
) || [],
84+
) || [];
85+
applicantRecords.push({
86+
id: uuidv4(),
87+
applicantId,
88+
position: normalizeApplicantRole(app.secondChoiceRole),
89+
roleSpecificQuestions: roleSpecificQs.length > 0 ? roleSpecificQs : null,
90+
choice: 2,
91+
status: (app.status as ApplicationStatus) || "Applied",
92+
skillCategory: null,
93+
extraInfo: JSON.stringify({ adminReview: "seeded_admin_review" }),
94+
});
95+
}
96+
});
97+
98+
const TABLE_NAME = "applicant_records";
99+
100+
export const up: Migration = async ({ context: sequelize }) => {
101+
await sequelize.getQueryInterface().createTable(TABLE_NAME, {
102+
id: {
103+
type: DataType.STRING,
104+
allowNull: false,
105+
primaryKey: true,
106+
},
107+
applicantId: {
108+
type: DataType.STRING,
109+
allowNull: false,
110+
references: {
111+
model: "applicants",
112+
key: "id",
113+
},
114+
},
115+
position: {
116+
type: DataType.STRING,
117+
allowNull: true,
118+
references: {
119+
model: "positions",
120+
key: "title",
121+
},
122+
},
123+
roleSpecificQuestions: {
124+
type: DataType.ARRAY(DataType.STRING),
125+
allowNull: true,
126+
},
127+
choice: {
128+
type: DataType.INTEGER,
129+
allowNull: false,
130+
},
131+
status: {
132+
type: DataType.STRING,
133+
allowNull: false,
134+
},
135+
skillCategory: {
136+
type: DataType.STRING,
137+
allowNull: true,
138+
},
139+
extraInfo: {
140+
type: DataType.JSONB,
141+
allowNull: true,
142+
},
143+
});
144+
await sequelize.getQueryInterface().bulkInsert(TABLE_NAME, applicantRecords);
145+
};
146+
147+
export const down: Migration = async ({ context: sequelize }) => {
148+
await sequelize.getQueryInterface().dropTable(TABLE_NAME);
149+
};

backend/typescript/migrations/20250703011602-drop-selected-for-interview-col-from-applicant-records.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)