@@ -120,8 +112,8 @@ const CourseSearch = ({ courses, onSelect, searchTerm, updateFilter }) => {
{shouldShowDropdown && (
- {sortedCourses.length > 0 ? (
- sortedCourses.slice(0, 10).map((course, index) => (
+ {displayedCourses.length > 0 ? (
+ displayedCourses.map((course, index) => (
handleSelect(course)}
@@ -134,14 +126,18 @@ const CourseSearch = ({ courses, onSelect, searchTerm, updateFilter }) => {
{course.value.subjectCode} {course.value.courseCode}
-
{course.value.title}
+ {!course.value.terms.includes(CURRENT_SEMESTER) && (
+
+ Not Offered in {CURRENT_SEMESTER}
+
+ )}
{!course.value.terms.includes(CURRENT_SEMESTER) && (
-
+
Not Offered in {CURRENT_SEMESTER}
)}
diff --git a/src/components/schedule/schedule.js b/src/components/schedule/schedule.js
index 00d114b..94dde20 100644
--- a/src/components/schedule/schedule.js
+++ b/src/components/schedule/schedule.js
@@ -8,6 +8,14 @@ import { useToast } from '@chakra-ui/react';
import { getCourseData } from '../calendar';
import ScheduleManager, { processLectureData } from './scheduleManager';
+// Func to get course color index based on detailId
+export const getCourseColorIndex = (detailId, courseColorMap) => {
+ if (!courseColorMap.has(detailId)) {
+ courseColorMap.set(detailId, courseColorMap.size);
+ }
+ return courseColorMap.get(detailId);
+};
+
const ScheduleCalendar = ({ courses = [], setIsLoading, setSelectedCourse, onCourseRemove }) => {
const toast = useToast();
const [hoveredCourse, setHoveredCourse] = useState(null);
@@ -38,14 +46,6 @@ const ScheduleCalendar = ({ courses = [], setIsLoading, setSelectedCourse, onCou
}
}, [selectedLectureIds]);
- // Func to get course color index based on detailId
- const getCourseColorIndex = (detailId) => {
- if (!courseColorMap.has(detailId)) {
- courseColorMap.set(detailId, courseColorMap.size);
- }
- return courseColorMap.get(detailId);
- };
-
useEffect(() => {
const fetchCourseData = async () => {
if (!courses.length) {
@@ -228,7 +228,7 @@ const ScheduleCalendar = ({ courses = [], setIsLoading, setSelectedCourse, onCou
return (
{sortedOverlaps.map((overlappingCourse, index) => {
- const colorIndex = getCourseColorIndex(overlappingCourse.courseDetails.detailId);
+ const colorIndex = getCourseColorIndex(overlappingCourse.courseDetails.detailId, courseColorMap);
const startPos = calculateTimePosition(overlappingCourse.start);
const endPos = calculateTimePosition(overlappingCourse.end);
const top = (startPos - 6) * 2;
@@ -269,6 +269,7 @@ const ScheduleCalendar = ({ courses = [], setIsLoading, setSelectedCourse, onCou
onLectureSelectionChange={handleLectureSelectionChange}
setSelectedCourse={setSelectedCourse}
onCourseRemove={onCourseRemove}
+ courseColorMap={courseColorMap}
/>
diff --git a/src/components/schedule/scheduleManager.js b/src/components/schedule/scheduleManager.js
index 8424486..9a44679 100644
--- a/src/components/schedule/scheduleManager.js
+++ b/src/components/schedule/scheduleManager.js
@@ -57,7 +57,7 @@ export const processLectureData = (courseResults, courses) => {
return coursesArray;
};
-const ScheduleManager = ({ lectures, selectedLectureIds, onLectureSelectionChange, setSelectedCourse, onCourseRemove }) => {
+const ScheduleManager = ({ lectures, selectedLectureIds, onLectureSelectionChange, setSelectedCourse, onCourseRemove, courseColorMap }) => {
const [minCredits, setMinCredits] = useState(0);
const [maxCredits, setMaxCredits] = useState(0);
@@ -161,6 +161,7 @@ const ScheduleManager = ({ lectures, selectedLectureIds, onLectureSelectionChang
onLectureToggle={handleLectureToggle}
setSelectedCourse={setSelectedCourse}
onCourseRemove={onCourseRemove}
+ courseColorMap={courseColorMap}
/>
)) : (
Search for a course by using the search bar, then add a course to show up here!
diff --git a/src/lib/gpaUtils.js b/src/lib/gpaUtils.js
new file mode 100644
index 0000000..1d2e0da
--- /dev/null
+++ b/src/lib/gpaUtils.js
@@ -0,0 +1,230 @@
+import { graphColors } from '@/lib/utils';
+
+// get all professors of a course in an array
+export const collectAllProfessors = (instructors) => {
+ 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 into this format (used for graph and general gpa displays):
+// {
+// grades: [
+// {
+// label: "instr1",
+// data: [
+// 3.11, 3.55, etc etc to 2 decimal places
+// ],
+// backgroundColor: "#ffffff" (based on graphColors)
+// }
+// ],
+// gpa: {
+// "instr1": [3.11, "#ffffff"],
+// "instr2": [3.55, "#ffffff"],
+// },
+// totalSemCount: 2,
+// }
+export const calculateGradesAndGPA = (profs, gpaData) => {
+ const grades = [];
+ const gpa = {};
+ let colorIndex = 0;
+ let totalSemCount = 0;
+
+ for (const instructor of profs) {
+ let avgGPA = 0;
+ let avgGradeDist = Array(13).fill(0);
+ const color = graphColors[colorIndex++ % graphColors.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,
+ });
+
+ totalSemCount += semesterCount;
+ }
+ return { grades, gpa, totalSemCount };
+};
+
+// Average all data for all professors into grade array format like above ^^
+export const averageAllData = (grades) => {
+ const avg = Array(13).fill(0);
+ for (const grade of grades) {
+ grade.data.forEach((val, i) => {
+ avg[i] += val;
+ });
+ }
+
+ return [{
+ label: "Average",
+ data: avg.map((val) => Math.round(val / grades.length * 100) / 100),
+ backgroundColor: graphColors[2],
+ }];
+};
+
+// 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 * perc;
+ 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;
+};
+
+// Processes GPA data into this format, used for gpaModal.js
+// {
+// "instr1": {
+// "Fall 21": {
+// gpa: 3.11,
+// color: "#ffffff" (based on getColor)
+// },
+// "Spring 21": {
+// gpa: 3.55,
+// color: "#ffffff"
+// }
+// }, etc
+// }
+export const processGpaData = (course, recentOnly = false) => {
+ if (!course || Object.keys(course.gpa).length === 0) return {};
+
+ const grades = {};
+ const sems = [];
+
+ for (const instructor in course.gpa) {
+ for (const semester in course.gpa[instructor]) {
+ if (!sems.includes(semester)) {
+ sems.push(semester);
+ }
+ }
+ }
+
+ 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];
+ }
+
+ const seasons = ["Spring", "Summer", "Fall"];
+ return seasons.indexOf(a_split[0]) - seasons.indexOf(b_split[0]);
+ });
+
+ for (const instructor in course.gpa) {
+ grades[instructor] = {};
+
+ const instructorSemesters = sorted_sems;
+ const recentSemesters = recentOnly
+ ? instructorSemesters.slice(-5)
+ : instructorSemesters;
+
+ for (const semester of instructorSemesters) {
+ if (!recentSemesters.includes(semester)) continue;
+
+ 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;
+};
+
+
+// Calculate overall GPA across all professors for a course, outputs
+// {
+// gpa: 3.11,
+// color: "#ffffff" (based on getColor),
+// profCount: 2,
+// totalSemCount: 4,
+// }
+export const calculateOverallGPA = (courseData) => {
+ const allProfs = [];
+ let totalGpa = 0;
+ let profCount = 0;
+
+ try {
+ // Get GPA for each professor
+ for (const prof in courseData.gpa) {
+ if (prof === '') continue;
+ allProfs.push(prof);
+ }
+
+ const { gpa, totalSemCount } = calculateGradesAndGPA(allProfs, courseData.gpa);
+
+ // Calculate average GPA across all professors
+ for (const prof in gpa) {
+ if (gpa[prof][0] !== 0) {
+ totalGpa += gpa[prof][0];
+ profCount++;
+ }
+ }
+
+ const avgGpa = profCount > 0 ? (totalGpa / profCount).toFixed(2) : 0;
+ return {
+ gpa: avgGpa,
+ color: getColor(avgGpa),
+ profCount,
+ totalSemCount,
+ };
+ } catch (e) {
+ console.error("Overall GPA not found: ", e);
+ return {
+ gpa: 0,
+ color: getColor(0),
+ profCount: 0,
+ totalSemCount: 0,
+ };
+ }
+};
\ No newline at end of file
diff --git a/src/lib/utils.js b/src/lib/utils.js
index 6ff0bc4..4fcd47a 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -449,3 +449,11 @@ export const graphColors = [
export const labels = ['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F'];
export const boilerExamsCourses = ["MA15800", "MA16010", "MA16100", "MA16200", "MA26100", "MA26200", "MA26500", "MA26600", "MA30300", "CS15900", "CS17700", "CS25100", "CHM11500", "ECON25200", "ECE20002", "PHYS17200"];
+
+
+// somewhat bandaid fix for missing decsriptions
+export const sanitizeDescription = (data) => {
+ if (data.description && data.description.startsWith("
{
const { grades, gpa } = calculateGradesAndGPA(
allProfs,
courseData.gpa,
- graphColors
);
setGpaGraph({
@@ -272,7 +273,7 @@ const CardDetails = ({ courseData, semData }) => {
{/* Latest Semester Display with conditional styling */}
-
@@ -324,9 +325,10 @@ const CardDetails = ({ courseData, semData }) => {
{/* Other Links Buttons */}
-