From cca794c17354a8c5de78be60eb35abbee6886766 Mon Sep 17 00:00:00 2001 From: Aron Petkovski Date: Mon, 13 Nov 2023 18:28:50 -0600 Subject: [PATCH] Revert "merged with develop" This reverts commit 3297551fa0dc389e3621ec8c719ebd2ff9e63bee. --- src/server/trpc/router/validator.ts | 260 ++++++++++------------------ 1 file changed, 89 insertions(+), 171 deletions(-) diff --git a/src/server/trpc/router/validator.ts b/src/server/trpc/router/validator.ts index dcf8e8c68..de7541104 100644 --- a/src/server/trpc/router/validator.ts +++ b/src/server/trpc/router/validator.ts @@ -4,7 +4,6 @@ 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'; @@ -49,8 +48,13 @@ export const validatorRouter = router({ throw new TRPCError({ code: 'FORBIDDEN' }); } - const coursesFromApi: JSONCourse[] = courses; - const coursesFromApi: JSONCourse[] = courses; + 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); /* sanitizing data from API db. * TODO: Fix this later somehow */ @@ -64,15 +68,16 @@ 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.id, `${course.subject_prefix} ${course.course_number}`); - courseMapWithIdKey.set(course.id, `${course.subject_prefix} ${course.course_number}`); + courseMapWithIdKey.set( + course.internal_course_number, + `${course.subject_prefix} ${course.course_number}`, + ); } /* Hash to store pre req data. @@ -109,8 +114,7 @@ export const validatorRouter = router({ ): Array<[Array, number]> => { const prereqNotMet: Array<[Array, number]> = []; let count = 0; - if (requirements.options.length === 0) { - if (requirements.options.length === 0) { + if (!requirements || requirements.options.length === 0) { return []; } const temp: [Array, number] = [[], 0]; @@ -158,8 +162,7 @@ export const validatorRouter = router({ ): Array<[Array, number]> => { const coreqNotMet: Array<[Array, number]> = []; let count = 0; - if (requirements.options.length === 0) { - if (requirements.options.length === 0) { + if (!requirements || requirements.options.length === 0) { return []; } const temp: [Array, number] = [[], 0]; @@ -208,8 +211,7 @@ export const validatorRouter = router({ ): Array<[Array, number]> => { const coreqNotMet: Array<[Array, number]> = []; let count = 0; - if (requirements.options.length === 0) { - if (requirements.options.length === 0) { + if (!requirements || requirements.options.length === 0) { return []; } const temp: [Array, number] = [[], 0]; @@ -308,166 +310,86 @@ export const validatorRouter = router({ } }), // Protected route: ensures session user is same as plan owner - 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, - }, + 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, }, - 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, + select: { + name: true, + id: true, + userId: true, + semesters: { + include: { + courses: true, + }, }, + transferCredits: true, }, - transferCredits: true, - }, - }); - - if (!planData) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Plan not found', - }); - } - if (!planData) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Plan not found', }); - } - if (ctx.session.user.id !== planData.userId) { - throw new TRPCError({ code: 'FORBIDDEN' }); - } - if (ctx.session.user.id !== planData.userId) { - throw new TRPCError({ code: 'FORBIDDEN' }); - } + if (!planData) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Plan not found', + }); + } - const { semesters, transferCredits } = planData; - const { semesters, transferCredits } = planData; + if (ctx.session.user.id !== planData.userId) { + throw new TRPCError({ code: 'FORBIDDEN' }); + } - // 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 }; + const { semesters, transferCredits } = planData; + + // Get degree requirements + const degreeRequirements = await ctx.prisma.degreeRequirements.findFirst({ + where: { + plan: { id: planData.id }, + }, }); - }; - const semestersWithCourses = removeInvalidCoursesFromSemesters(); - const semestersWithCourses = removeInvalidCoursesFromSemesters(); + // Get bypasses + const bypasses = degreeRequirements?.bypasses ?? []; - if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') { - return { plan: planData, validation: [], bypasses: [] }; - } - if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') { - return { plan: planData, validation: [], 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 }; + }); + }; - // 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 semestersWithCourses = removeInvalidCoursesFromSemesters(); + + if (!degreeRequirements?.major || degreeRequirements.major === 'undecided') { + return { plan: planData, validation: [], 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); + + const body = { + courses: [...semestersWithCourses.flatMap((s) => s.courses), ...validTransferCredits], + requirements: { + year: startYear, + 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), @@ -487,18 +409,14 @@ export const validatorRouter = router({ console.error('Validator error', err); throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', - cause: err, - message: errorMessage, - cause: err, - message: errorMessage, + message: error.message, + cause: error, }); - }); - }); + } - return { plan: planData, validation: validationData, bypasses }; - }), - return { plan: planData, validation: validationData, bypasses }; - }), + const validationData = await res.json(); + return { plan: planData, validation: validationData, bypasses }; + }), }); type CourseOptions = {