From 5a19c990668fbcf9663e5bae289f8ff3cf4c4563 Mon Sep 17 00:00:00 2001 From: COSENTINOLG23 Date: Mon, 20 Apr 2026 14:36:47 -0400 Subject: [PATCH 1/2] added semester filter functionality. button in frontend. made new class that implements filter to stay consistent with other existing filters --- .../main/java/edu/gcc/hallmonitor/Filter.java | 1 + .../java/edu/gcc/hallmonitor/Semester.java | 39 ++++++++++++++++ .../edu/gcc/hallmonitor/SemesterTest.java | 44 +++++++++++++++++++ frontend/src/SearchPage.tsx | 26 ++++++++++- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/edu/gcc/hallmonitor/Semester.java create mode 100644 backend/src/test/java/edu/gcc/hallmonitor/SemesterTest.java diff --git a/backend/src/main/java/edu/gcc/hallmonitor/Filter.java b/backend/src/main/java/edu/gcc/hallmonitor/Filter.java index 5ed1745..f676d67 100644 --- a/backend/src/main/java/edu/gcc/hallmonitor/Filter.java +++ b/backend/src/main/java/edu/gcc/hallmonitor/Filter.java @@ -43,6 +43,7 @@ static void registerFilterType( static void init() { Days.init(); Department.init(); + Semester.init(); NumCredits.init(); ProfName.init(); Time.init(); diff --git a/backend/src/main/java/edu/gcc/hallmonitor/Semester.java b/backend/src/main/java/edu/gcc/hallmonitor/Semester.java new file mode 100644 index 0000000..27f76eb --- /dev/null +++ b/backend/src/main/java/edu/gcc/hallmonitor/Semester.java @@ -0,0 +1,39 @@ +package edu.gcc.hallmonitor; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public record Semester(String semester) implements Filter { + + @Override + public boolean filter(Course course) { + return course.semester().equals(semester); + } + + @Override + public JsonNode toJSON() { + ObjectNode root = Main.MAPPER.createObjectNode(); + root.put("type", "semester"); + root.put("value", semester); + return root; + } + + private static Filter deserialize(JsonNode value) { + return new Semester(value.asText()); + } + + private static Set possibleValues(List courses) { + return courses.stream() + .map(Course::semester) + .collect(Collectors.toSet()); + } + + public static void init() { + Filter.registerFilterType("semester", Semester::deserialize, Semester::possibleValues); + } +} + diff --git a/backend/src/test/java/edu/gcc/hallmonitor/SemesterTest.java b/backend/src/test/java/edu/gcc/hallmonitor/SemesterTest.java new file mode 100644 index 0000000..48da53b --- /dev/null +++ b/backend/src/test/java/edu/gcc/hallmonitor/SemesterTest.java @@ -0,0 +1,44 @@ +package edu.gcc.hallmonitor; + +import java.time.LocalTime; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SemesterTest { + @Test + public void testSemesterFilter() { + Course fall = emptyWithSemester("2023_Fall"); + Course spring = emptyWithSemester("2024_Spring"); + + Semester filter = new Semester("2023_Fall"); + + assertEquals(true, filter.filter(fall)); + assertEquals(false, filter.filter(spring)); + } + + private static Course emptyWithSemester(String semester) { + return new Course( + "", + List.of(""), + "", + 0, + ' ', + "", + 0, + semester, + List.of(new CourseTime( + "M", + LocalTime.of(0, 0), + LocalTime.of(0, 0) + )), + false, + false, + 0, + 0 + ); + } +} + diff --git a/frontend/src/SearchPage.tsx b/frontend/src/SearchPage.tsx index af34eeb..26b12c6 100644 --- a/frontend/src/SearchPage.tsx +++ b/frontend/src/SearchPage.tsx @@ -22,6 +22,8 @@ interface Course { export default function SearchPage() { const courses_per_page = 10; const [query, setQuery] = useState(''); + const [semester, setSemester] = useState('ALL'); + const [semesters, setSemesters] = useState([]); const [department, setDepartment] = useState('ALL'); const [professor, setProfessor] = useState('ALL'); const [courses, setCourses] = useState([]); @@ -70,7 +72,7 @@ export default function SearchPage() { } search(); - }, [department, professor, days, credits, timeStart, timeEnd]); + }, [semester, department, professor, days, credits, timeStart, timeEnd]); //Toggles a course in the user's schedule and syncs with the backend. @@ -137,6 +139,12 @@ export default function SearchPage() { // updating all the filters + const updateSem = async (event: React.ChangeEvent) => { + const updated = event.target.value; + await updateFilter('semester', semester, updated); + setSemester(updated); + }; + const updateDept = async (event: React.ChangeEvent) => { const updated = event.target.value; await updateFilter('department', department, updated); @@ -181,6 +189,11 @@ export default function SearchPage() { const res = await fetch('/courses'); const items: Course[] = await res.json(); + setSemesters( + Array.from(new Set(items.map(c => c.semester))).sort() + ); + + setDepartments( Array.from(new Set(items.map(c => c.subject).filter(d => d && d !== 'ZLOAD'))).sort() ); @@ -229,6 +242,9 @@ export default function SearchPage() { const resp = await fetch('/search/filter'); for (const filter of await resp.json()) { switch (filter.type) { + case "semester": + setSemester(filter.value); + break; case "department": setDepartment(filter.value); break; @@ -290,6 +306,7 @@ export default function SearchPage() { }); setDepartment('ALL'); + setSemester('ALL'); setProfessor('ALL'); setDays(new Set()); setCredits('ALL'); @@ -320,6 +337,13 @@ export default function SearchPage() {

Filters

+

Semester

+ + +

Department & Professor