diff --git a/src/components/OverallGpa.js b/src/components/OverallGpa.js new file mode 100644 index 0000000..a69c714 --- /dev/null +++ b/src/components/OverallGpa.js @@ -0,0 +1,31 @@ +import { calculateOverallGPA } from '@/lib/gpaUtils'; +import { Tooltip } from '@chakra-ui/react'; + +const OverallGpa = ({ courseData, card }) => { + const { gpa, color, profCount, totalSemCount } = calculateOverallGPA(courseData); + + return ( + +
+ {/*
*/} + + GPA: {gpa === 0 ? "N/A" : gpa} + +
+ + ); +}; + +export default OverallGpa; \ No newline at end of file diff --git a/src/components/card.js b/src/components/card.js index 9c29173..d54b205 100644 --- a/src/components/card.js +++ b/src/components/card.js @@ -1,5 +1,7 @@ -import { semesters } from "../lib/utils" -import { CURRENT_SEMESTER } from '@/hooks/useSearchFilters'; // Add this import +import { semesters } from "@/lib/utils" +import { CURRENT_SEMESTER } from '@/hooks/useSearchFilters'; +import OverallGpa from "./OverallGpa"; + import React from 'react'; import Link from 'next/link' @@ -39,17 +41,26 @@ const Card = ({ course, searchTerm }) => {
handleLink()}>

{course.subjectCode} {course.courseCode}: {course.title}

-

- {course.credits[1] > 1 - ? `${course.credits[1]} Credits` - : `${course.credits[1]} Credit`} - {` | `} - {uniqueInstructors[0]} - {uniqueInstructors.length > 1 && ", "} - {uniqueInstructors.length > 1 && - uniqueInstructors[1] - } -

+ +
+
+

+ {course.credits[1] > 1 + ? `${course.credits[1]} Credits` + : `${course.credits[1]} Credit`} + {` | `} +

+ + +
+

+ {uniqueInstructors[0]} + {uniqueInstructors.length > 1 && ", "} + {uniqueInstructors.length > 1 && + uniqueInstructors[1] + } +

+

{course.description.length > 300 @@ -59,16 +70,15 @@ const Card = ({ course, searchTerm }) => {

- {course.sched.includes("Distance Learning") &&

+ {course.sched.includes("Distance Learning") &&

Distance Learning

} {availableSemesters.map((sem, i) => ( (i < 2) && {sem} diff --git a/src/components/fullInstructorModal.js b/src/components/fullInstructorModal.js index 081a661..d9dfcfd 100644 --- a/src/components/fullInstructorModal.js +++ b/src/components/fullInstructorModal.js @@ -1,35 +1,12 @@ import React, { useEffect, useState } from 'react'; import SearchBar from '@/components/SearchBar'; +import { getColor } from '@/lib/gpaUtils'; const FullInstructorModal = ({ course }) => { const [gpa, setGpa] = useState({}); const [searchQuery, setSearchQuery] = useState(''); - // function to get color based on gpa: - const getColor = (gpa) => { - if (gpa === 0) { - return "#18181b"; - } - - // calculate the color based on gpa as a percentage of 4.0 - const perc = gpa / 4.0; - const perc2 = perc * perc * 0.9; - const color1 = [221, 170, 51]; // higher gpa color - const color2 = [79, 0, 56]; // lower gpa color - - const w1 = perc2; - const w2 = 1 - perc2; - - const r = Math.round(color1[0] * w1 + color2[0] * w2 * 1); - const g = Math.round(color1[1] * w1 + color2[1] * w2 * 1); - const b = Math.round(color1[2] * w1 + color2[2] * w2 * 1); - - const hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); - // console.log(hex); - return hex; - }; - useEffect(() => { if (!course) return; @@ -84,44 +61,45 @@ const FullInstructorModal = ({ course }) => { return ( -
-

All Instructors Breakdown

-

- This graphic displays all semesters and professors that have taught this course. Use the search bar below to filter for a specific professor!
- GPA: 1.04.0 -

- -
- {Object.keys(gpa).map((semester, index) => { - const filteredInstructors = Object.keys(gpa[semester]).filter(instructor => - instructor.toLowerCase().includes(searchQuery.toLowerCase()) - ); - - if (filteredInstructors.length === 0) return null; - - return ( -
-

{semester}

-
- {filteredInstructors.map((instructor, index) => ( -
-

{instructor}

- -
-

{gpa[semester][instructor].gpa}

-
+
+

All Instructors Breakdown

+

+ This graphic displays all semesters and professors that have taught this course. Use the search bar below to filter for a specific professor!
+ GPA: 1.04.0 +

+ +
+ {Object.keys(gpa).map((semester, index) => { + const filteredInstructors = Object.keys(gpa[semester]).filter(instructor => + instructor.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + if (filteredInstructors.length === 0) return null; + + return ( +
+

{semester}

+
+ {filteredInstructors.map((instructor, index) => ( +
+

{instructor}

+ +
+ {/*
*/} +

{gpa[semester][instructor].gpa}

- ))} -
+
+ ))}
- ); - })} -
+
+ ); + })}
+
); }; diff --git a/src/components/gpaModal.js b/src/components/gpaModal.js index 5d3e2d6..d3884b6 100644 --- a/src/components/gpaModal.js +++ b/src/components/gpaModal.js @@ -1,11 +1,10 @@ import React, { useEffect, useState } from 'react'; - import SearchBar from '@/components/SearchBar'; - import { CURRENT_SEMESTER } from '@/hooks/useSearchFilters'; +import { processGpaData } from '@/lib/gpaUtils'; const replaceZeroGpaWithDash = (gpaValue) => { - return gpaValue === 0 ? '-' : gpaValue; + return gpaValue === 0 ? '-' : gpaValue; }; const GpaModal = ({ course }) => { @@ -41,8 +40,9 @@ const GpaModal = ({ course }) => { {Object.keys(gpa[instructor]).map((semester, index) => ( // console.log(`bg-[${gpa[instructor][semester].color}]`),
-
-

{replaceZeroGpaWithDash(gpa[instructor][semester].gpa)}

+
+ {/*
*/} +

{replaceZeroGpaWithDash(gpa[instructor][semester].gpa)}

{semester}

{semester.split(" ")[0]}

@@ -104,8 +104,9 @@ export const ScheduleGpaModal = ({ course }) => {
{Object.keys(gpa[instructor]).map((semester, index) => (
-
-

{replaceZeroGpaWithDash(gpa[instructor][semester].gpa)}

+
+ {/*
*/} +

{replaceZeroGpaWithDash(gpa[instructor][semester].gpa)}

))} @@ -120,84 +121,4 @@ export const ScheduleGpaModal = ({ course }) => { )}
); -}; - -// Function to get color based on GPA -export const getColor = (gpa) => { - if (gpa === 0) { - return "#18181b"; - } - - // Calculate the color based on GPA as a percentage of 4.0 - const perc = gpa / 4.0; - const perc2 = perc * perc * 0.9; - const color1 = [221, 170, 51]; // Higher GPA color - const color2 = [79, 0, 56]; // Lower GPA color - - const w1 = perc2; - const w2 = 1 - perc2; - - const r = Math.round(color1[0] * w1 + color2[0] * w2 * 1); - const g = Math.round(color1[1] * w1 + color2[1] * w2 * 1); - const b = Math.round(color1[2] * w1 + color2[2] * w2 * 1); - - const hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); - return hex; -}; - -// Function to process GPA data -const processGpaData = (course, recentOnly = false) => { - if (!course || Object.keys(course.gpa).length === 0) return {}; - - const grades = {}; - const sems = []; - - // Collect all semesters across all instructors - for (const instructor in course.gpa) { - for (const semester in course.gpa[instructor]) { - if (!sems.includes(semester)) { - sems.push(semester); - } - } - } - - // Sort semesters chronologically - const sorted_sems = sems.sort((a, b) => { - const a_split = a.split(" "); - const b_split = b.split(" "); - if (a_split[1] !== b_split[1]) { - return a_split[1] - b_split[1]; // Compare years - } - - const seasons = ["Spring", "Summer", "Fall"]; - return seasons.indexOf(a_split[0]) - seasons.indexOf(b_split[0]); // Compare seasons - }); - - // Process GPA data for each instructor - for (const instructor in course.gpa) { - grades[instructor] = {}; - - // Get recent semesters if flag is true, but keep all semesters with GPA = 0 - const instructorSemesters = sorted_sems; - const recentSemesters = recentOnly - ? instructorSemesters.slice(-5) - : instructorSemesters; - - // Fill in GPA and color for each semester - for (const semester of instructorSemesters) { - if (!recentSemesters.includes(semester)) continue; // Skip if not in the recent subset - - // Fill in GPA data, defaulting to 0 if not present - if (!course.gpa[instructor][semester]) { - grades[instructor][semester] = { gpa: 0, color: getColor(0) }; - } else { - grades[instructor][semester] = { - gpa: course.gpa[instructor][semester][13], - color: getColor(course.gpa[instructor][semester][13]), - }; - } - } - } - - return grades; }; \ No newline at end of file diff --git a/src/components/graph.js b/src/components/graph.js index cfa1b2b..533a117 100644 --- a/src/components/graph.js +++ b/src/components/graph.js @@ -60,7 +60,7 @@ const Graph = ({ data, scheduler = false }) => { } } }} data={data} - // { + // { (example dataset for testing) // { // labels, // datasets: [{ @@ -76,95 +76,4 @@ const Graph = ({ data, scheduler = false }) => { ) } -export default Graph; - - -// util functions for graph and gpa - -// Helper to sanitize description -export const sanitizeDescription = (data) => { - if (data.description && data.description.startsWith(" { - const allProfs = []; - for (const semester in instructors) { - for (const instructor of instructors[semester]) { - if (!allProfs.includes(instructor)) { - allProfs.push(instructor); - } - } - } - return allProfs; -}; - -// Helper to calculate GPA and grade distributions -export const calculateGradesAndGPA = (profs, gpaData, colors) => { - const grades = []; - const gpa = {}; - let colorIndex = 0; - - for (const instructor of profs) { - let avgGPA = 0; - let avgGradeDist = Array(13).fill(0); - const color = colors[colorIndex++ % colors.length]; - - if (!gpaData[instructor]) { - gpa[instructor] = [0, "#ffffff"]; - grades.push({ - label: instructor, - data: avgGradeDist, - backgroundColor: "#ffffff", - }); - continue; - } - - let semesterCount = 0; - for (const sem in gpaData[instructor]) { - avgGPA += gpaData[instructor][sem][13]; - avgGradeDist = avgGradeDist.map( - (val, i) => val + gpaData[instructor][sem][i] - ); - semesterCount++; - } - - avgGradeDist = avgGradeDist.map((val) => - Math.round((val / semesterCount) * 100) / 100 - ); - - gpa[instructor] = [ - Math.round((avgGPA / semesterCount) * 100) / 100, - color, - ]; - grades.push({ - label: instructor, - data: avgGradeDist, - backgroundColor: color, - }); - } - return { grades, gpa }; -}; - - -import { graphColors } from '@/lib/utils'; -export const averageAllData = (grades) => { - - const avg = Array(13).fill(0); - for (const grade of grades) { - grade.data.forEach((val, i) => { - avg[i] += val; - }); - } - - const avgData = [{ - label: "Average", - data: avg.map((val) => Math.round(val / grades.length * 100) / 100), - backgroundColor: graphColors[2], - }]; - - return avgData; - -} \ No newline at end of file +export default Graph; \ No newline at end of file diff --git a/src/components/schedule/CourseGroup.js b/src/components/schedule/CourseGroup.js index 07718ce..048dcf5 100644 --- a/src/components/schedule/CourseGroup.js +++ b/src/components/schedule/CourseGroup.js @@ -1,4 +1,3 @@ - import React, { useState, useEffect } from 'react'; import { Button, @@ -14,19 +13,21 @@ import { } from '@chakra-ui/react'; import { IoMdInformationCircleOutline, IoIosSwap, IoIosArrowForward, IoMdClose } from "react-icons/io"; import { loadRatingsForProfs, getRMPScore } from '@/components/RMP'; -import { calculateGradesAndGPA, collectAllProfessors } from '@/components/graph'; -import { getColor } from '../gpaModal'; -import { graphColors } from '@/lib/utils'; +import { calculateGradesAndGPA, collectAllProfessors, getColor } from '@/lib/gpaUtils'; import { translateType } from '../calendar'; import { sortByTime, sortByFirstDay } from './utils/sortUtils'; +import { getCourseColorIndex } from './schedule'; +import { graphColors } from '@/lib/utils'; // Cache for RMP ratings const rmpScoresCache = new Map(); -const CourseGroup = ({ parentCourse, lectures, selectedLectures, onLectureToggle, setSelectedCourse, onCourseRemove }) => { +const CourseGroup = ({ parentCourse, lectures, selectedLectures, onLectureToggle, setSelectedCourse, onCourseRemove, courseColorMap }) => { const { isOpen, onOpen, onClose } = useDisclosure(); const [rmpScores, setRmpScores] = useState(() => rmpScoresCache.get(parentCourse.detailId) || {}); const [instructorGPAs, setInstructorGPAs] = useState({}); + const colorIndex = getCourseColorIndex(parentCourse.detailId, courseColorMap); + const courseColor = graphColors[colorIndex % graphColors.length]; // Load RMP scores and calculate GPAs when modal opens useEffect(() => { @@ -74,7 +75,6 @@ const CourseGroup = ({ parentCourse, lectures, selectedLectures, onLectureToggle const { gpa } = calculateGradesAndGPA( Array.from(allInstructors), parentCourse.gpa, - graphColors ); setInstructorGPAs(gpa); @@ -188,10 +188,13 @@ const CourseGroup = ({ parentCourse, lectures, selectedLectures, onLectureToggle