Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IS department and stop easy professor duplicates from being added #135

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
29 changes: 23 additions & 6 deletions packages/backend/src/routers/professor.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { t } from "@backend/trpc";
import { z } from "zod";
import { Professor, ratingBaseParser } from "@backend/types/schema";
import { addRating } from "@backend/types/schemaHelpers";
import { DEPARTMENT_LIST } from "@backend/utils/const";
import { addRating as addRatingToProfessor } from "@backend/types/schemaHelpers";
import { addRating } from "./rating";

export const professorRouter = t.router({
all: t.procedure.query(({ ctx }) => ctx.env.kvDao.getAllProfessors()),
Expand All @@ -29,6 +30,22 @@ export const professorRouter = t.router({
}),
)
.mutation(async ({ ctx, input }) => {
const allProfessors = await ctx.env.kvDao.getAllProfessors();
const existingProfessor = allProfessors.find(
({ firstName, lastName, department }) =>
firstName === input.firstName &&
lastName === input.lastName &&
department === input.department,
);

if (existingProfessor) {
await addRating({ ...input.rating, professor: existingProfessor.id }, ctx);
return (
"Your request for adding a new professor was automatically added under " +
`${existingProfessor.lastName}, ${existingProfessor.firstName}. Please reach out to [email protected] if this is incorrect`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make this a hyperlink?

);
}

const professorId = crypto.randomUUID();
const tags = Object.fromEntries((input.rating.tags ?? []).map((tag) => [tag, 1]));
const professor: Professor = {
Expand All @@ -55,15 +72,15 @@ export const professorRouter = t.router({
};

const existingPendingProfessors = await ctx.env.kvDao.getAllPendingProfessors();
const duplicateProfessor = existingPendingProfessors.find(
const duplicatePendingProfessor = existingPendingProfessors.find(
(prof) =>
prof.firstName === professor.firstName && prof.lastName === professor.lastName,
);

if (duplicateProfessor) {
if (duplicatePendingProfessor) {
const [[courseName, reviews]] = Object.entries(professor.reviews);
addRating(duplicateProfessor, reviews[0], courseName);
await ctx.env.kvDao.putPendingProfessor(duplicateProfessor);
addRatingToProfessor(duplicatePendingProfessor, reviews[0], courseName);
await ctx.env.kvDao.putPendingProfessor(duplicatePendingProfessor);
} else {
await ctx.env.kvDao.putPendingProfessor(professor);

Expand All @@ -76,6 +93,6 @@ export const professorRouter = t.router({
);
}

return "The request for the new professor has been accepted and is pending manual approval";
return "Thank you for adding a professor. It will be reviewed manually and will be available soon";
}),
});
99 changes: 51 additions & 48 deletions packages/backend/src/routers/rating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,67 @@ import { z } from "zod";
import { TRPCError } from "@trpc/server";
import { PendingRating, ratingBaseParser, RatingReport, reportParser } from "@backend/types/schema";
import { DEPARTMENT_LIST } from "@backend/utils/const";
import { Env } from "@backend/env";

export const ratingsRouter = t.router({
add: t.procedure
.input(
ratingBaseParser.merge(
z.object({
professor: z.string().uuid(),
department: z.enum(DEPARTMENT_LIST),
courseNum: z.number().min(100).max(599),
}),
),
)
.mutation(async ({ ctx, input }) => {
// Input is a string subset of PendingRating
const pendingRating: PendingRating = {
id: crypto.randomUUID(),
...input,
postDate: new Date().toString(),
status: "Failed",
error: null,
analyzedScores: null,
anonymousIdentifier: await ctx.env.anonymousIdDao.getIdentifier(),
};
const addRatingParser = ratingBaseParser.merge(
z.object({
professor: z.string().uuid(),
department: z.enum(DEPARTMENT_LIST),
courseNum: z.number().min(100).max(599),
}),
);

const analyzedScores = await ctx.env.ratingAnalyzer.analyzeRaring(pendingRating);
pendingRating.analyzedScores = analyzedScores;
export async function addRating(input: z.infer<typeof addRatingParser>, ctx: { env: Env }) {
// Input is a string subset of PendingRating
const pendingRating: PendingRating = {
id: crypto.randomUUID(),
...input,
postDate: new Date().toString(),
status: "Failed",
error: null,
analyzedScores: null,
anonymousIdentifier: await ctx.env.anonymousIdDao.getIdentifier(),
};

// At least 50% of people would find the text offensive in category
const PERSPECTIVE_THRESHOLD = 0.5;
const analyzedScores = await ctx.env.ratingAnalyzer.analyzeRaring(pendingRating);
pendingRating.analyzedScores = analyzedScores;

const passedAnalysis = Object.values(analyzedScores).reduce((acc, num) => {
if (num === undefined) {
throw new Error("Not all of perspective summery scores were received");
}
return num < PERSPECTIVE_THRESHOLD && acc;
}, true);
// At least 50% of people would find the text offensive in category
const PERSPECTIVE_THRESHOLD = 0.5;

if (!passedAnalysis) {
// Update rating in processing queue
pendingRating.status = "Failed";
await ctx.env.kvDao.addRatingLog(pendingRating);
const passedAnalysis = Object.values(analyzedScores).reduce((acc, num) => {
if (num === undefined) {
throw new Error("Not all of perspective summery scores were received");
}
return num < PERSPECTIVE_THRESHOLD && acc;
}, true);

throw new TRPCError({
code: "PRECONDITION_FAILED",
message:
"Rating failed sentiment analysis, please contact [email protected] for assistance",
});
}
if (!passedAnalysis) {
// Update rating in processing queue
pendingRating.status = "Failed";
await ctx.env.kvDao.addRatingLog(pendingRating);

// Update rating in processing queue
pendingRating.status = "Successful";
throw new TRPCError({
code: "PRECONDITION_FAILED",
message:
"Rating failed sentiment analysis, please contact [email protected] for assistance",
});
}

const updatedProfessor = await ctx.env.kvDao.addRating(pendingRating);
// Update rating in processing queue
pendingRating.status = "Successful";

await ctx.env.kvDao.addRatingLog(pendingRating);
const updatedProfessor = await ctx.env.kvDao.addRating(pendingRating);

return updatedProfessor;
}),
await ctx.env.kvDao.addRatingLog(pendingRating);

return updatedProfessor;
}

export const ratingsRouter = t.router({
add: t.procedure
.input(addRatingParser)
.mutation(async ({ ctx, input }) => addRating(input, ctx)),
report: t.procedure
.input(
reportParser.merge(
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/utils/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const DEPARTMENT_LIST = [
"HNRS",
"IME",
"IP",
"IS",
"ISLA",
"ITAL",
"ITP",
Expand Down
6 changes: 2 additions & 4 deletions packages/frontend/src/components/NewProfessorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ function useNewProfessorForm() {

const onSubmitHandler = async (data: NewProfessorFormInputs) => {
try {
await addNewProfessorMutation({
const successMessage = await addNewProfessorMutation({
firstName: data.professorFirstName,
lastName: data.professorLastName,
department: data.professorDepartment,
Expand All @@ -148,9 +148,7 @@ function useNewProfessorForm() {
tags: data.tags,
},
});
toast.success(
"Thank you for adding a professor. It will be reviewed manually and will be available soon",
);
toast.success(successMessage);
navigate("/");
} catch {
// No need for error will be set by react-query
Expand Down
Loading