Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/website-bp-be.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ReviewDashboardService from "../../services/implementations/reviewDashboa
import {
ReviewDashboardRowDTO,
ReviewDashboardSidePanelDTO,
ReviewDashboardFilter,
} from "../../types";
import { getErrorMessage } from "../../utilities/errorUtils";

Expand All @@ -11,12 +12,17 @@ const reviewDashboardResolvers = {
Query: {
reviewDashboard: async (
_parent: undefined,
args: { pageNumber: number; resultsPerPage: number },
args: {
pageNumber: number;
resultsPerPage: number;
filter?: ReviewDashboardFilter;
},
): Promise<ReviewDashboardRowDTO[]> => {
try {
return await reviewDashboardService.getReviewDashboard(
args.pageNumber,
args.resultsPerPage,
args.filter,
);
} catch (error) {
throw new Error(getErrorMessage(error));
Expand Down
40 changes: 33 additions & 7 deletions backend/typescript/graphql/types/reviewDashboardType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,38 @@ const reviewDashboardType = gql`
totalScore: Int
}

type Review {
passionFSG: Int
teamPlayer: Int
desireToLearn: Int
skill: Int
skillCategory: String
comments: String
enum DepartmentEnum {
Engineering
Design
Product
Community
}

enum AdditionalFiltersEnum {
IN_REVIEW
REVIEWED
SELECTED
NOT_SELECTED
SENIOR
INTERMEDIATE
JUNIOR
GREATER_THAN_25
BETWEEN_20_AND_25
BETWEEN_15_AND_20
BETWEEN_10_AND_15
LESS_THAN_10
FIRST_YEAR
SECOND_YEAR
THIRD_YEAR
FOURTH_YEAR
FIFTH_YEAR
SIXTH_YEAR
}

input ReviewDashboardFilter {
department: DepartmentEnum
role: [String!]
additionalFilters: [AdditionalFiltersEnum!]
}

type ReviewDetails {
Expand All @@ -47,6 +72,7 @@ const reviewDashboardType = gql`
reviewDashboard(
pageNumber: Int!
resultsPerPage: Int!
filter: ReviewDashboardFilter
): [ReviewDashboardRowDTO!]!

reviewDashboardSidePanel(applicantId: String!): ReviewDashboardSidePanelDTO!
Expand Down
3 changes: 3 additions & 0 deletions backend/typescript/models/applicantRecord.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,7 @@ export default class ApplicantRecord extends Model {

@HasMany(() => ReviewedApplicantRecord, "applicantRecordId")
reviewedApplicantRecords?: NonAttribute<ReviewedApplicantRecord[]>;

@BelongsTo(() => Position, { foreignKey: "position" })
appliedTo?: NonAttribute<Position>;
}
127 changes: 126 additions & 1 deletion backend/typescript/services/implementations/reviewDashboardService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { literal, Op, Sequelize } from "sequelize";
import {
AdditionalFilters,
Department,
PositionTitle,
ReviewDashboardFilter,
ReviewDashboardRowDTO,
ReviewDashboardSidePanelDTO,
ApplicantRole,
} from "../../types";
import IReviewDashboardService from "../interfaces/IReviewDashboardService";
import { getErrorMessage } from "../../utilities/errorUtils";
Expand All @@ -26,6 +31,108 @@ function toDTO(model: ApplicantRecord): ReviewDashboardRowDTO {
};
}

function buildWhereStatement(filter?: ReviewDashboardFilter) {
const exp: any = [];
const ranges: any = [];
const year: any = [];
const skill: any = [];
const status: any = [];
if (filter) {
if (filter.department) {
if (filter.department === Department.Community) {
exp.push({ "$appliedTo.department$": { [Op.eq]: "Community" } });
} else if (filter.department === Department.Design) {
exp.push({ "$appliedTo.department$": { [Op.eq]: "Design" } });
} else if (filter.department === Department.Engineering) {
exp.push({ "$appliedTo.department$": { [Op.eq]: "Engineering" } });
} else if (filter.department === Department.Product) {
exp.push({ "$appliedTo.department$": { [Op.eq]: "Product" } });
}
}

if (Array.isArray(filter.role) && filter.role.length > 0) {
exp.push({
position: {
[Op.in]: filter.role,
},
});
}

if (filter.additionalFilters) {
if (
filter.additionalFilters.includes(AdditionalFilters.GREATER_THAN_25)
) {
ranges.push({ combined_score: { [Op.gt]: 25 } });
}
if (
filter.additionalFilters.includes(AdditionalFilters.BETWEEN_20_AND_25)
) {
ranges.push({ combined_score: { [Op.between]: [20, 25] } });
}
if (
filter.additionalFilters.includes(AdditionalFilters.BETWEEN_15_AND_20)
) {
ranges.push({ combined_score: { [Op.between]: [15, 20] } });
}
if (
filter.additionalFilters.includes(AdditionalFilters.BETWEEN_10_AND_15)
) {
ranges.push({ combined_score: { [Op.between]: [10, 15] } });
}
if (filter.additionalFilters.includes(AdditionalFilters.LESS_THAN_10)) {
ranges.push({ combined_score: { [Op.lt]: 10 } });
}
if (filter.additionalFilters.includes(AdditionalFilters.SENIOR)) {
skill.push({ skillCategory: { [Op.eq]: "Senior" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.JUNIOR)) {
skill.push({ skillCategory: { [Op.eq]: "Junior" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.INTERMEDIATE)) {
skill.push({ skillCategory: { [Op.eq]: "Intermediate" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.FIRST_YEAR)) {
year.push({ "$applicant.academicYear$": { [Op.regexp]: "1(A|B)" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.SECOND_YEAR)) {
year.push({ "$applicant.academicYear$": { [Op.regexp]: "2(A|B)" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.THIRD_YEAR)) {
year.push({ "$applicant.academicYear$": { [Op.regexp]: "3(A|B)" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.FOURTH_YEAR)) {
year.push({ "$applicant.academicYear$": { [Op.regexp]: "4(A|B)" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.FIFTH_YEAR)) {
year.push({ "$applicant.academicYear$": { [Op.regexp]: "5(A|B)" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.SIXTH_YEAR)) {
year.push({
"$applicant.academicYear$": { [Op.iRegexp]: "graduate" },
});
}
if (filter.additionalFilters.includes(AdditionalFilters.IN_REVIEW)) {
status.push({ status: { [Op.eq]: "In Review" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.REVIEWED)) {
status.push({ status: { [Op.eq]: "Reviewed" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.SELECTED)) {
status.push({ status: { [Op.eq]: "Selected for Interview" } });
}
if (filter.additionalFilters.includes(AdditionalFilters.NOT_SELECTED)) {
status.push({ status: { [Op.eq]: "Not Considered" } });
}
}
}

if (ranges.length > 0) exp.push({ [Op.or]: ranges });
if (skill.length > 0) exp.push({ [Op.or]: skill });
if (year.length > 0) exp.push({ [Op.or]: year });
if (status.length > 0) exp.push({ [Op.or]: status });
return exp;
}

function toSidePanelDTO(model: ApplicantRecord): ReviewDashboardSidePanelDTO {
const reviewDetails =
model.reviewedApplicantRecords?.map((reviewRecord) => ({
Expand All @@ -51,6 +158,7 @@ class ReviewDashboardService implements IReviewDashboardService {
async getReviewDashboard(
pageNumber: number,
resultsPerPage: number,
filters?: ReviewDashboardFilter,
): Promise<ReviewDashboardRowDTO[]> {
try {
const perPage = Number.isFinite(Number(resultsPerPage))
Expand All @@ -61,6 +169,8 @@ class ReviewDashboardService implements IReviewDashboardService {
: 1;
const offsetRow = (currentPage - 1) * perPage;

const whereStatement = buildWhereStatement(filters);

// get applicant_record
// JOIN applicant ON applicant_id
// JOIN reviewed_applicant_record ON applicant_record_id
Expand All @@ -70,7 +180,9 @@ class ReviewDashboardService implements IReviewDashboardService {
attributes: { exclude: ["createdAt", "updatedAt"] },
include: [
{
attributes: { exclude: ["createdAt", "updatedAt"] },
attributes: {
exclude: ["createdAt", "updatedAt"],
},
association: "reviewedApplicantRecords",
include: [
{
Expand All @@ -82,8 +194,21 @@ class ReviewDashboardService implements IReviewDashboardService {
{
attributes: { exclude: ["createdAt", "updatedAt"] },
association: "applicant",
required: true,
},
{
attributes: { exclude: ["createdAt", "updatedAt"] },
association: "appliedTo",
required: true,
on: literal(
`"ApplicantRecord"."position"::text = "appliedTo"."title"::text`,
),
},
],
where:
whereStatement.length > 0
? { [Op.and]: whereStatement }
: undefined,
order: [["id", "ASC"]],
limit: perPage,
offset: offsetRow,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ReviewDashboardRowDTO,
ReviewDashboardFilter,
ReviewDashboardSidePanelDTO,
} from "../../types";

Expand All @@ -8,10 +9,12 @@ interface IReviewDashboardService {
* Pagination-supporting viewing of the virtual review dashboard
* @Param page the page the viewer is on
* @Param resultsPerPage the number of results per page
* @param filters the filters for the review dashboard results
*/
getReviewDashboard(
page: number,
resultsPerPage: number,
filters?: ReviewDashboardFilter,
): Promise<ReviewDashboardRowDTO[]>;

/**
Expand Down
49 changes: 49 additions & 0 deletions backend/typescript/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ export type NodemailerConfig = {

export type SignUpMethod = "PASSWORD" | "GOOGLE";

export enum ApplicantRole {
pres = "president", // community tab
int_dir = "internal director",
ext_dir = "external director",
vpe = "vp engineering", // eng tab
vpd = "vp design", // design tab
vpp = "vp product", // prod tab
vpt = "vp talent", // community tab
vp_ext = "vp external", // community tab
vp_int = "vp internal", // community tab
vp_comms = "vp communications", // community tab
vp_scoping = "vp scoping", // community tab
vp_finance = "vp finance & operations", // community tab
pm = "project manager", // prod tab
pl = "project lead", // eng tab
design_mentor = "design mentor", // design tab
graphic_design = "graphic designer", // design tab
product_design = "product designer", // design tab
uxr = "user researcher", // design tab
dev = "project developer", // eng tab
}

export enum Department {
Engineering = "Engineering",
Design = "Design",
Expand Down Expand Up @@ -252,3 +274,30 @@ export type CreateAdminCommentDTO = Pick<
AdminCommentDTO,
"userId" | "applicantRecordId" | "comment"
>;

export enum AdditionalFilters {
IN_REVIEW = "IN_REVIEW",
REVIEWED = "REVIEWED",
SELECTED = "SELECTED",
NOT_SELECTED = "NOT_SELECTED",
SENIOR = "SENIOR",
INTERMEDIATE = "INTERMEDIATE",
JUNIOR = "JUNIOR",
GREATER_THAN_25 = "GREATER_THAN_25",
BETWEEN_20_AND_25 = "BETWEEN_20_AND_25",
BETWEEN_15_AND_20 = "BETWEEN_15_AND_20",
BETWEEN_10_AND_15 = "BETWEEN_10_AND_15",
LESS_THAN_10 = "LESS_THAN_10",
FIRST_YEAR = "FIRST_YEAR",
SECOND_YEAR = "SECOND_YEAR",
THIRD_YEAR = "THIRD_YEAR",
FOURTH_YEAR = "FOURTH_YEAR",
FIFTH_YEAR = "FIFTH_YEAR",
SIXTH_YEAR = "SIXTH_YEAR",
}

export type ReviewDashboardFilter = {
department?: Department;
role?: string[];
additionalFilters?: AdditionalFilters[];
};
Loading