Skip to content

Commit

Permalink
merged with develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Aron Petkovski committed Nov 14, 2023
1 parent d75d13f commit 3297551
Showing 1 changed file with 171 additions and 89 deletions.
260 changes: 171 additions & 89 deletions src/server/trpc/router/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod';

import { env } from '@/env/server.mjs';
import courses, { JSONCourse } from '@data/courses.json';
import courses, { JSONCourse } from '@data/courses.json';

import { courseCache } from './courseCache';
import { DegreeNotFound, DegreeValidationError } from './errors';
Expand Down Expand Up @@ -48,13 +49,8 @@ export const validatorRouter = router({
throw new TRPCError({ code: 'FORBIDDEN' });
}

let year = new Date().getFullYear(); // If plan has no semesters, default to current year.
if (planData.semesters.length > 0) {
// If plan has semesters, default to first semester's year.
year = Math.min(...planData.semesters.map((sem) => sem.year));
}

const coursesFromAPI: PlatformCourse[] = await courseCache.getCourses(year);
const coursesFromApi: JSONCourse[] = courses;
const coursesFromApi: JSONCourse[] = courses;
/* sanitizing data from API db.
* TODO: Fix this later somehow
*/
Expand All @@ -68,16 +64,15 @@ export const validatorRouter = router({
}
>();

for (const course of coursesFromAPI) {
for (const course of coursesFromApi) {
for (const course of coursesFromApi) {
courseMapWithCodeKey.set(`${course.subject_prefix} ${course.course_number}`, {
prereqs: course.prerequisites,
coreqs: course.corequisites,
co_or_pre_requisites: course.co_or_pre_requisites,
});
courseMapWithIdKey.set(
course.internal_course_number,
`${course.subject_prefix} ${course.course_number}`,
);
courseMapWithIdKey.set(course.id, `${course.subject_prefix} ${course.course_number}`);
courseMapWithIdKey.set(course.id, `${course.subject_prefix} ${course.course_number}`);
}

/* Hash to store pre req data.
Expand Down Expand Up @@ -114,7 +109,8 @@ export const validatorRouter = router({
): Array<[Array<string>, number]> => {
const prereqNotMet: Array<[Array<string>, number]> = [];
let count = 0;
if (!requirements || requirements.options.length === 0) {
if (requirements.options.length === 0) {
if (requirements.options.length === 0) {
return [];
}
const temp: [Array<string>, number] = [[], 0];
Expand Down Expand Up @@ -162,7 +158,8 @@ export const validatorRouter = router({
): Array<[Array<string>, number]> => {
const coreqNotMet: Array<[Array<string>, number]> = [];
let count = 0;
if (!requirements || requirements.options.length === 0) {
if (requirements.options.length === 0) {
if (requirements.options.length === 0) {
return [];
}
const temp: [Array<string>, number] = [[], 0];
Expand Down Expand Up @@ -211,7 +208,8 @@ export const validatorRouter = router({
): Array<[Array<string>, number]> => {
const coreqNotMet: Array<[Array<string>, number]> = [];
let count = 0;
if (!requirements || requirements.options.length === 0) {
if (requirements.options.length === 0) {
if (requirements.options.length === 0) {
return [];
}
const temp: [Array<string>, number] = [[], 0];
Expand Down Expand Up @@ -310,86 +308,166 @@ export const validatorRouter = router({
}
}),
// Protected route: ensures session user is same as plan owner
degreeValidator: protectedProcedure
.input(z.object({ planId: z.string().min(1), startYear: z.number() }))
.query(async ({ ctx, input: { planId, startYear } }) => {
// Fetch current plan
const planData = await ctx.prisma.plan.findUnique({
where: {
id: planId,
degreeValidator: protectedProcedure.input(z.string().min(1)).query(async ({ ctx, input }) => {
// Fetch current plan
const planData = await ctx.prisma.plan.findUnique({
where: {
id: input,
},
select: {
name: true,
id: true,
userId: true,
semesters: {
include: {
courses: true,
},
},
select: {
name: true,
id: true,
userId: true,
semesters: {
include: {
courses: true,
},
transferCredits: true,
},
});
degreeValidator: protectedProcedure.input(z.string().min(1)).query(async ({ ctx, input }) => {
// Fetch current plan
const planData = await ctx.prisma.plan.findUnique({
where: {
id: input,
},
select: {
name: true,
id: true,
userId: true,
semesters: {
include: {
courses: true,
},
transferCredits: true,
},
});

if (!planData) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Plan not found',
});
}

if (ctx.session.user.id !== planData.userId) {
throw new TRPCError({ code: 'FORBIDDEN' });
}

const { semesters, transferCredits } = planData;
transferCredits: true,
},
});

// Get degree requirements
const degreeRequirements = await ctx.prisma.degreeRequirements.findFirst({
where: {
plan: { id: planData.id },
},
if (!planData) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Plan not found',
});
}
if (!planData) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Plan not found',
});
}

// Get bypasses
const bypasses = degreeRequirements?.bypasses ?? [];
if (ctx.session.user.id !== planData.userId) {
throw new TRPCError({ code: 'FORBIDDEN' });
}
if (ctx.session.user.id !== planData.userId) {
throw new TRPCError({ code: 'FORBIDDEN' });
}

// Remove invalidCourses
const removeInvalidCoursesFromSemesters = () => {
return semesters.map((sem) => {
const courses = sem.courses
.reduce((acc, curr) => [...acc, curr.code], [] as string[])
.filter((c) => {
const [possiblePrefix, possibleCode] = c.split(' ');
if (Number.isNaN(Number(possibleCode)) || !Number.isNaN(Number(possiblePrefix))) {
return false;
}
return true;
});
return { ...sem, courses };
});
};
const { semesters, transferCredits } = planData;
const { semesters, transferCredits } = planData;

const semestersWithCourses = removeInvalidCoursesFromSemesters();
// Get degree requirements
const degreeRequirements = await ctx.prisma.degreeRequirements.findFirst({
where: {
plan: { id: planData.id },
},
});
// Get degree requirements
const degreeRequirements = await ctx.prisma.degreeRequirements.findFirst({
where: {
plan: { id: planData.id },
},
});

// Get bypasses
const bypasses = degreeRequirements?.bypasses ?? [];
// Get bypasses
const bypasses = degreeRequirements?.bypasses ?? [];

// Remove invalidCourses
const removeInvalidCoursesFromSemesters = () => {
return semesters.map((sem) => {
const courses = sem.courses
.reduce((acc, curr) => [...acc, curr.code], [] as string[])
.filter((c) => {
const [possiblePrefix, possibleCode] = c.split(' ');
if (Number.isNaN(Number(possibleCode)) || !Number.isNaN(Number(possiblePrefix))) {
return false;
}
return true;
});
return { ...sem, courses };
});
};
// Remove invalidCourses
const removeInvalidCoursesFromSemesters = () => {
return semesters.map((sem) => {
const courses = sem.courses
.reduce((acc, curr) => [...acc, curr.code], [] as string[])
.filter((c) => {
const [possiblePrefix, possibleCode] = c.split(' ');
if (Number.isNaN(Number(possibleCode)) || !Number.isNaN(Number(possiblePrefix))) {
return false;
}
return true;
});
return { ...sem, courses };
});
};

if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') {
return { plan: planData, validation: [], bypasses: [] };
}
const semestersWithCourses = removeInvalidCoursesFromSemesters();
const semestersWithCourses = removeInvalidCoursesFromSemesters();

// TODO: will we always ignore odd credits such as 'PSY 1---'?
const regex = /([a-z0-9])* ([a-z0-9]){4}$/gi;
const validTransferCredits = transferCredits.filter((credit) => credit.match(regex) !== null);
if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') {
return { plan: planData, validation: [], bypasses: [] };
}
if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') {
return { plan: planData, validation: [], bypasses: [] };
}

const body = {
courses: [...semestersWithCourses.flatMap((s) => s.courses), ...validTransferCredits],
requirements: {
year: startYear,
majors: [degreeRequirements.major],
minors: [],
},
bypasses,
};
// TODO: will we always ignore odd credits such as 'PSY 1---'?
const regex = /([a-z0-9])* ([a-z0-9]){4}$/gi;
const validTransferCredits = transferCredits.filter((credit) => credit.match(regex) !== null);
// TODO: will we always ignore odd credits such as 'PSY 1---'?
const regex = /([a-z0-9])* ([a-z0-9]){4}$/gi;
const validTransferCredits = transferCredits.filter((credit) => credit.match(regex) !== null);

const body = {
courses: [...semestersWithCourses.flatMap((s) => s.courses), ...validTransferCredits],
requirements: {
majors: [degreeRequirements.major],
minors: [],
},
bypasses,
};
const body = {
courses: [...semestersWithCourses.flatMap((s) => s.courses), ...validTransferCredits],
requirements: {
majors: [degreeRequirements.major],
minors: [],
},
bypasses,
};

const validationData = await fetch(`${env.NEXT_PUBLIC_VALIDATOR}/validate`, {
method: 'POST',
body: JSON.stringify(body),
headers: {
'content-type': 'application/json',
},
})
.then(async (res) => {
if (!res.ok) {
const errorMsg = await res.json();
throw new Error(`validator fetch failed with status ${res.status}: ${errorMsg.error}.`);
}
return res.json();
})
.catch((err) => {
const errorMessage = `Validator error: ${err.message}`;
console.error('Validator error', err);
const validationData = await fetch(`${env.NEXT_PUBLIC_VALIDATOR}/validate`, {
method: 'POST',
body: JSON.stringify(body),
Expand All @@ -409,14 +487,18 @@ export const validatorRouter = router({
console.error('Validator error', err);
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: error.message,
cause: error,
cause: err,
message: errorMessage,
cause: err,
message: errorMessage,
});
}
});
});

const validationData = await res.json();
return { plan: planData, validation: validationData, bypasses };
}),
return { plan: planData, validation: validationData, bypasses };
}),
return { plan: planData, validation: validationData, bypasses };
}),
});

type CourseOptions = {
Expand Down

0 comments on commit 3297551

Please sign in to comment.