From 5def90b7a865c6bbf16534b673fc834734b75d86 Mon Sep 17 00:00:00 2001 From: Jacob Sommer Date: Sun, 10 Nov 2024 18:52:24 -0800 Subject: [PATCH 1/2] fix avg rating --- api/src/controllers/reviews.ts | 7 +++--- .../src/component/SearchPopup/SearchPopup.tsx | 8 +++++-- site/src/pages/SearchPage/CoursePopup.tsx | 12 ++++++---- site/src/pages/SearchPage/ProfessorPopup.tsx | 23 ++++++------------- site/src/types/types.ts | 5 ++-- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/api/src/controllers/reviews.ts b/api/src/controllers/reviews.ts index 58350d9f..1413a74f 100644 --- a/api/src/controllers/reviews.ts +++ b/api/src/controllers/reviews.ts @@ -16,7 +16,7 @@ import { import { TRPCError } from '@trpc/server'; import { db } from '../db'; import { review, user, vote } from '../db/schema'; -import { and, count, desc, eq, sql } from 'drizzle-orm'; +import { and, avg, count, desc, eq, sql } from 'drizzle-orm'; import { datesToStrings } from '../helpers/date'; async function userWroteReview(userId: number | undefined, reviewId: number) { @@ -224,17 +224,16 @@ const reviewsRouter = router({ /** * Get avg ratings for a course's professors or a professor's courses */ - scores: publicProcedure + avgRating: publicProcedure .input(z.object({ type: z.enum(['course', 'professor']), id: z.string() })) .query(async ({ input }) => { const field = input.type === 'course' ? review.courseId : review.professorId; const otherField = input.type === 'course' ? review.professorId : review.courseId; const results = await db - .select({ name: otherField, score: sql`COALESCE(SUM(${vote.vote}), 0)`.mapWith(Number) }) + .select({ name: otherField, avgRating: avg(review.rating).mapWith(Number) }) .from(review) .where(eq(field, input.id)) - .leftJoin(vote, eq(vote.reviewId, review.id)) .groupBy(otherField); return results; diff --git a/site/src/component/SearchPopup/SearchPopup.tsx b/site/src/component/SearchPopup/SearchPopup.tsx index 638c9153..c6e241b0 100644 --- a/site/src/component/SearchPopup/SearchPopup.tsx +++ b/site/src/component/SearchPopup/SearchPopup.tsx @@ -109,11 +109,15 @@ const SearchPopupContent: FC = (props) => {
- {score.score == -1 ? '?' : Number.isInteger(score.score) ? score.score : score.score.toFixed(2)} + {score.avgRating == -1 + ? '?' + : Number.isInteger(score.avgRating) + ? score.avgRating + : score.avgRating.toFixed(2)} / 5.0
- + {score.name}
diff --git a/site/src/pages/SearchPage/CoursePopup.tsx b/site/src/pages/SearchPage/CoursePopup.tsx index 0de7f911..0488c0f5 100644 --- a/site/src/pages/SearchPage/CoursePopup.tsx +++ b/site/src/pages/SearchPage/CoursePopup.tsx @@ -12,24 +12,28 @@ const CoursePopup: FC = () => { useEffect(() => { if (course) { - trpc.reviews.scores.query({ type: 'course', id: course.id }).then((res) => { + trpc.reviews.avgRating.query({ type: 'course', id: course.id }).then((res) => { const scores: ScoreData[] = []; // set of ucinetid professors with scores const scoredProfessors = new Set(res.map((v) => v.name)); // add known scores res.forEach((entry) => { if (course.instructors[entry.name]) { - scores.push({ name: course.instructors[entry.name].shortenedName, score: entry.score, key: entry.name }); + scores.push({ + name: course.instructors[entry.name].shortenedName, + avgRating: entry.avgRating, + id: entry.name, + }); } }); // add unknown score Object.keys(course.instructors).forEach((ucinetid) => { if (!scoredProfessors.has(ucinetid)) { - scores.push({ name: course.instructors[ucinetid].shortenedName, score: -1, key: ucinetid }); + scores.push({ name: course.instructors[ucinetid].shortenedName, avgRating: -1, id: ucinetid }); } }); // sort by highest score - scores.sort((a, b) => b.score - a.score); + scores.sort((a, b) => b.avgRating - a.avgRating); setScores(scores); }); } diff --git a/site/src/pages/SearchPage/ProfessorPopup.tsx b/site/src/pages/SearchPage/ProfessorPopup.tsx index 8f8cdf73..16fa84bf 100644 --- a/site/src/pages/SearchPage/ProfessorPopup.tsx +++ b/site/src/pages/SearchPage/ProfessorPopup.tsx @@ -9,7 +9,7 @@ import { FeaturedReviewData } from '@peterportal/types'; const ProfessorPopup: FC = () => { const professor = useAppSelector(selectProfessor); - const [featured, setFeatured] = useState(null!); + const [featured, setFeatured] = useState(); const [scores, setScores] = useState([]); useEffect(() => { @@ -18,31 +18,22 @@ const ProfessorPopup: FC = () => { type: 'professor', id: professor.ucinetid, } as const; - trpc.reviews.scores.query(reviewParams).then((res: ScoreData[]) => { + trpc.reviews.avgRating.query(reviewParams).then((res) => { + const scores = res.map((course) => ({ name: course.name, avgRating: course.avgRating, id: course.name })); const scoredCourses = new Set(res.map((v) => v.name)); - res.forEach((v) => (v.key = v.name)); Object.keys(professor.courses).forEach((course) => { // remove spaces course = course.replace(/\s+/g, ''); // add unknown score if (!scoredCourses.has(course)) { - res.push({ name: course, score: -1, key: course }); + scores.push({ name: course, avgRating: -1, id: course }); } }); // sort by highest score - res.sort((a, b) => b.score - a.score); - setScores(res); - }); - trpc.reviews.featured.query(reviewParams).then((res) => { - // if has a featured review - if (res) { - setFeatured(res); - } - // no reviews for this professor - else { - setFeatured(null!); - } + res.sort((a, b) => b.avgRating - a.avgRating); + setScores(scores); }); + trpc.reviews.featured.query(reviewParams).then(setFeatured); } }, [professor]); diff --git a/site/src/types/types.ts b/site/src/types/types.ts index 4fa053bb..205470e9 100644 --- a/site/src/types/types.ts +++ b/site/src/types/types.ts @@ -8,8 +8,9 @@ import { export interface ScoreData { name: string; - score: number; - key?: string; + avgRating: number; + /** course id or ucinetid */ + id: string; } export type SearchIndex = 'courses' | 'professors'; From 702bb234a26a0153c30d607097f5fd3171932dba Mon Sep 17 00:00:00 2001 From: Jacob Sommer Date: Sun, 10 Nov 2024 18:53:08 -0800 Subject: [PATCH 2/2] fix featured review route for undefined --- api/src/controllers/reviews.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/api/src/controllers/reviews.ts b/api/src/controllers/reviews.ts index 1413a74f..66b4a54d 100644 --- a/api/src/controllers/reviews.ts +++ b/api/src/controllers/reviews.ts @@ -208,17 +208,15 @@ const reviewsRouter = router({ const featuredReviewCriteria = [desc(review.content), desc(voteSubQuery.score), desc(review.verified)]; const field = input.type === 'course' ? review.courseId : review?.professorId; - const featuredReview = ( - await db - .select() - .from(review) - .where(eq(field, input.id)) - .leftJoin(voteSubQuery, eq(voteSubQuery.reviewId, review.id)) - .orderBy(...featuredReviewCriteria) - .limit(1) - )[0]; + const featuredReview = await db + .select() + .from(review) + .where(eq(field, input.id)) + .leftJoin(voteSubQuery, eq(voteSubQuery.reviewId, review.id)) + .orderBy(...featuredReviewCriteria) + .limit(1); - return datesToStrings(featuredReview.review) as FeaturedReviewData; + return featuredReview.length > 0 ? (datesToStrings(featuredReview[0].review) as FeaturedReviewData) : undefined; }), /**