Skip to content

Commit cf476d6

Browse files
author
ruiichen
committed
phase1
1 parent e010ca3 commit cf476d6

File tree

7 files changed

+143
-13
lines changed

7 files changed

+143
-13
lines changed

backend/typescript/graphql/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import adminCommentResolvers from "./resolvers/adminCommentsResolvers";
2121
import adminCommentType from "./types/adminCommentsType";
2222
import reviewPageType from "./types/reviewPageType";
2323
import reviewPageResolvers from "./resolvers/reviewPageResolvers";
24+
import applicantRecordType from "./types/applicantRecordType";
25+
import applicantRecordResolvers from "./resolvers/applicantRecordResolvers";
2426

2527
const query = gql`
2628
type Query {
@@ -45,6 +47,7 @@ const executableSchema = makeExecutableSchema({
4547
reviewDashboardType,
4648
adminCommentType,
4749
reviewPageType,
50+
applicantRecordType,
4851
],
4952
resolvers: merge(
5053
authResolvers,
@@ -54,6 +57,7 @@ const executableSchema = makeExecutableSchema({
5457
reviewDashboardResolvers,
5558
adminCommentResolvers,
5659
reviewPageResolvers,
60+
applicantRecordResolvers,
5761
),
5862
});
5963

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import ApplicantRecordService from "../../services/implementations/applicantRecordService";
2+
import { getErrorMessage } from "../../utilities/errorUtils";
3+
4+
const applicantRecordService = new ApplicantRecordService();
5+
6+
const applicantRecordResolvers = {
7+
Mutation: {
8+
populateApplicantRecord: async (
9+
_parent: undefined,
10+
args: { term: string },
11+
): Promise<void> => {
12+
try {
13+
await applicantRecordService.populateApplicantRecord(args.term);
14+
} catch (error) {
15+
throw new Error(getErrorMessage(error));
16+
}
17+
},
18+
},
19+
};
20+
21+
export default applicantRecordResolvers;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { gql } from "apollo-server-express";
2+
3+
const applicantRecordType = gql`
4+
scalar None
5+
6+
extend type Mutation {
7+
populateApplicantRecord(term: String!): None
8+
}
9+
`;
10+
11+
export default applicantRecordType;

backend/typescript/models/applicantRecord.model.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
Model,
1010
Table,
1111
} from "sequelize-typescript";
12-
import { NonAttribute } from "sequelize";
12+
import { NonAttribute, DataTypes } from "sequelize";
1313
import {
1414
ApplicantRecordExtraInfo,
1515
ApplicationStatus,
@@ -22,7 +22,8 @@ import ReviewedApplicantRecord from "./reviewedApplicantRecord.model";
2222
@Table({ tableName: "applicant_records" })
2323
export default class ApplicantRecord extends Model {
2424
@Column({
25-
type: DataType.STRING,
25+
type: DataTypes.UUID,
26+
defaultValue: DataTypes.UUIDV4,
2627
primaryKey: true,
2728
unique: true,
2829
autoIncrement: true,

backend/typescript/server.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,19 @@ sequelize.authenticate();
5050
// databaseURL: "https://uw-blueprint.firebaseio.com",
5151
// });
5252

53-
admin.initializeApp({
54-
credential: admin.credential.cert({
55-
projectId: process.env.FIREBASE_PROJECT_ID,
56-
privateKey: process.env.FIREBASE_SVC_ACCOUNT_PRIVATE_KEY?.replace(
57-
/\\n/g,
58-
"\n",
59-
),
60-
clientEmail: process.env.FIREBASE_SVC_ACCOUNT_CLIENT_EMAIL,
61-
}),
62-
databaseURL: "https://uw-blueprint.firebaseio.com",
63-
});
53+
if (!admin.apps.length) {
54+
admin.initializeApp({
55+
credential: admin.credential.cert({
56+
projectId: process.env.FIREBASE_PROJECT_ID,
57+
privateKey: process.env.FIREBASE_SVC_ACCOUNT_PRIVATE_KEY?.replace(
58+
/\\n/g,
59+
"\n",
60+
),
61+
clientEmail: process.env.FIREBASE_SVC_ACCOUNT_CLIENT_EMAIL,
62+
}),
63+
databaseURL: "https://uw-blueprint.firebaseio.com",
64+
});
65+
}
6466
const db = admin.database();
6567
const ref = db.ref("studentApplications");
6668

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as admin from "firebase-admin";
2+
import IApplicantRecordService from "../interfaces/IApplicantRecordService";
3+
import Applicant from "../../models/applicant.model";
4+
import ApplicantRecord from "../../models/applicantRecord.model";
5+
import { getErrorMessage } from "../../utilities/errorUtils";
6+
import logger from "../../utilities/logger";
7+
8+
const Logger = logger(__filename);
9+
10+
if (!admin.apps.length) {
11+
admin.initializeApp({
12+
credential: admin.credential.cert({
13+
projectId: process.env.FIREBASE_PROJECT_ID,
14+
privateKey: process.env.FIREBASE_SVC_ACCOUNT_PRIVATE_KEY?.replace(
15+
/\\n/g,
16+
"\n",
17+
),
18+
clientEmail: process.env.FIREBASE_SVC_ACCOUNT_CLIENT_EMAIL,
19+
}),
20+
databaseURL: "https://uw-blueprint.firebaseio.com",
21+
});
22+
}
23+
24+
const db = admin.database();
25+
const studentApplicationsRef = db.ref("studentApplications");
26+
27+
class ApplicantRecordService implements IApplicantRecordService {
28+
/* eslint-disable class-methods-use-this */
29+
async populateApplicantRecord(term: string): Promise<void> {
30+
try {
31+
const snapshot = await studentApplicationsRef
32+
.orderByChild("term")
33+
.equalTo(term)
34+
.once("value");
35+
36+
const rawData = snapshot.val() as Record<string, any>;
37+
38+
const applicantsToCreate = Object.values(rawData).map((app: any) => {
39+
const shortAnswers = Array.isArray(app.shortAnswerQuestions)
40+
? app.shortAnswerQuestions.map((q: any) => {
41+
if (typeof q === "string") return q;
42+
return JSON.stringify(q); // fallback
43+
})
44+
: [];
45+
46+
return {
47+
academicOrCoop: app.academicOrCoop ?? "",
48+
academicYear: app.academicYear ?? "",
49+
email: app.email ?? "",
50+
firstName: app.firstName ?? "",
51+
lastName: app.lastName ?? "",
52+
heardFrom: app.heardFrom ?? "",
53+
locationPreference: app.locationPreference ?? "",
54+
program: app.program ?? "",
55+
pronouns: app.pronouns ?? "",
56+
resumeUrl: app.resumeUrl ?? "",
57+
timesApplied:
58+
typeof app.timesApplied === "number" ? app.timesApplied : 0,
59+
shortAnswerQuestions: shortAnswers,
60+
term: app.term ?? term,
61+
submittedAt: app.submittedAt
62+
? new Date(app.submittedAt)
63+
: new Date(app.timestamp ?? Date.now()),
64+
};
65+
});
66+
67+
const createdApplicants = await Applicant.bulkCreate(applicantsToCreate, {
68+
validate: true,
69+
returning: true, // IMPORTANT
70+
});
71+
72+
console.log(createdApplicants);
73+
} catch (error: unknown) {
74+
Logger.error(
75+
`Failed to get dashboard. Reason = ${getErrorMessage(error)}`,
76+
);
77+
throw error;
78+
}
79+
}
80+
}
81+
82+
export default ApplicantRecordService;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
interface IApplicantRecordService {
2+
/**
3+
* Given a term, fetch all application data from Firebase and populate the database accordingly.
4+
* @param term is the term we would like to populate
5+
*/
6+
populateApplicantRecord(term: string): Promise<void>;
7+
}
8+
9+
export default IApplicantRecordService;

0 commit comments

Comments
 (0)