From 4bd80afd7fad7d8a3d4970ceab898ff688860174 Mon Sep 17 00:00:00 2001 From: Eli Campos Date: Fri, 9 Feb 2024 15:57:30 -0500 Subject: [PATCH 1/2] Add Couse Cache System --- app/src/scripts/soc/soc.tsx | 56 ++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/app/src/scripts/soc/soc.tsx b/app/src/scripts/soc/soc.tsx index 30084a7..661036a 100644 --- a/app/src/scripts/soc/soc.tsx +++ b/app/src/scripts/soc/soc.tsx @@ -28,12 +28,16 @@ interface UID { export abstract class SOC_Generic { info: SOCInfo; courses: Course[]; + // Initialize cache for broad search results and individual courses + courseCache: Map; /* CONSTRUCT & INITIALIZE */ protected constructor(info: SOCInfo, courses: Course[]) { this.info = info; this.courses = courses; + // Initialize the cache as an empty Map + this.courseCache = new Map(); } static async initialize(): Promise { @@ -120,27 +124,42 @@ export abstract class SOC_Generic { */ searchCourses(searchBy: SearchBy, phrase: string): Course[] { console.log(`Searching by ${searchBy} for "${phrase}"`); - if (!phrase) + if (!phrase) // Empty search phrase should not return all courses - return []; + return []; const upperPhrase: string = phrase.toUpperCase(); + + // Check cache first for both broad and specific course searches + // Use course code as cache key for specific searches + const cacheKey = searchBy === SearchBy.COURSE_CODE ? upperPhrase : `${searchBy}:${upperPhrase}`; + if (this.courseCache.has(cacheKey)) { + console.log("Returning cached results for:", cacheKey); + // Return cached results + return this.courseCache.get(cacheKey) || []; + } + + let results: Course[] = []; if (searchBy === SearchBy.COURSE_CODE) { - return this.courses.filter((c) => c.code.includes(upperPhrase)); + results = this.courses.filter((c) => c.code.includes(upperPhrase)); + // Cache each individual course from the broad search result + results.forEach(course => this.courseCache.set(course.code.toUpperCase(), [course])); } else if (searchBy === SearchBy.COURSE_TITLE) { - return this.courses.filter((c) => - c.name.toUpperCase().includes(upperPhrase), - ); + results = this.courses.filter((c) => c.name.toUpperCase().includes(upperPhrase)); } else if (searchBy === SearchBy.INSTRUCTOR) { - return this.courses.filter((c) => + results = this.courses.filter((c) => c.sections.some((s) => - s.instructors.some((inst) => - inst.toUpperCase().includes(upperPhrase), - ), + s.instructors.some((inst) => inst.toUpperCase().includes(upperPhrase)), ), ); + } else { + throw new Error("Unhandled SearchBy."); } - throw new Error("Unhandled SearchBy."); + + // Store broad search results in cache + this.courseCache.set(cacheKey, results); + + return results; } /* UTILS */ @@ -217,6 +236,13 @@ export class SOC_API extends SOC_Generic { lcn = 0, ): Promise { if (!phrase) return Promise.resolve(); + // Check cache for individual course code to avoid refetching + const upperPhrase = phrase.toUpperCase(); + if (searchBy === SearchBy.COURSE_CODE && this.courseCache.has(upperPhrase)) { + console.log("Course already fetched and cached:", upperPhrase); + // Exit if the specific course is already cached + return Promise.resolve(); + } const search = JSON.stringify({ by: searchBy, phrase }); if (this.fetchCache.has(search)) { @@ -243,7 +269,7 @@ export class SOC_API extends SOC_Generic { // Add each course, if appropriate COURSES.forEach((courseJson: API_Course) => { const courseID: string = courseJson.courseId, - courseCode: string = courseJson.code, + courseCode: string = courseJson.code.toUpperCase(), courseInd: number = this.courses.length; // Prevent duplicates @@ -266,7 +292,11 @@ export class SOC_API extends SOC_Generic { ); }, ); - this.courses.push(course); // Add the course to the courses array + // Add the course to the courses array + this.courses.push(course); + // Cache the course by its code + this.courseCache.set(courseCode, [course]); + } }); From 3ce551d7075603caff25f8f74c67f3eb688674b9 Mon Sep 17 00:00:00 2001 From: Eli Campos Date: Sun, 11 Feb 2024 00:38:26 -0500 Subject: [PATCH 2/2] Fix formatting --- app/src/scripts/soc/soc.tsx | 43 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/app/src/scripts/soc/soc.tsx b/app/src/scripts/soc/soc.tsx index 661036a..8b0b71a 100644 --- a/app/src/scripts/soc/soc.tsx +++ b/app/src/scripts/soc/soc.tsx @@ -29,7 +29,7 @@ export abstract class SOC_Generic { info: SOCInfo; courses: Course[]; // Initialize cache for broad search results and individual courses - courseCache: Map; + courseCache: Map; /* CONSTRUCT & INITIALIZE */ @@ -37,7 +37,7 @@ export abstract class SOC_Generic { this.info = info; this.courses = courses; // Initialize the cache as an empty Map - this.courseCache = new Map(); + this.courseCache = new Map(); } static async initialize(): Promise { @@ -124,32 +124,41 @@ export abstract class SOC_Generic { */ searchCourses(searchBy: SearchBy, phrase: string): Course[] { console.log(`Searching by ${searchBy} for "${phrase}"`); - if (!phrase) + if (!phrase) // Empty search phrase should not return all courses - return []; + return []; const upperPhrase: string = phrase.toUpperCase(); // Check cache first for both broad and specific course searches - // Use course code as cache key for specific searches - const cacheKey = searchBy === SearchBy.COURSE_CODE ? upperPhrase : `${searchBy}:${upperPhrase}`; + // Use course code as cache key for specific searches + const cacheKey = + searchBy === SearchBy.COURSE_CODE + ? upperPhrase + : `${searchBy}:${upperPhrase}`; if (this.courseCache.has(cacheKey)) { console.log("Returning cached results for:", cacheKey); // Return cached results - return this.courseCache.get(cacheKey) || []; + return this.courseCache.get(cacheKey) || []; } let results: Course[] = []; if (searchBy === SearchBy.COURSE_CODE) { results = this.courses.filter((c) => c.code.includes(upperPhrase)); // Cache each individual course from the broad search result - results.forEach(course => this.courseCache.set(course.code.toUpperCase(), [course])); + results.forEach((course) => + this.courseCache.set(course.code.toUpperCase(), [course]), + ); } else if (searchBy === SearchBy.COURSE_TITLE) { - results = this.courses.filter((c) => c.name.toUpperCase().includes(upperPhrase)); + results = this.courses.filter((c) => + c.name.toUpperCase().includes(upperPhrase), + ); } else if (searchBy === SearchBy.INSTRUCTOR) { results = this.courses.filter((c) => c.sections.some((s) => - s.instructors.some((inst) => inst.toUpperCase().includes(upperPhrase)), + s.instructors.some((inst) => + inst.toUpperCase().includes(upperPhrase), + ), ), ); } else { @@ -236,12 +245,15 @@ export class SOC_API extends SOC_Generic { lcn = 0, ): Promise { if (!phrase) return Promise.resolve(); - // Check cache for individual course code to avoid refetching + // Check cache for individual course code to avoid refetching const upperPhrase = phrase.toUpperCase(); - if (searchBy === SearchBy.COURSE_CODE && this.courseCache.has(upperPhrase)) { + if ( + searchBy === SearchBy.COURSE_CODE && + this.courseCache.has(upperPhrase) + ) { console.log("Course already fetched and cached:", upperPhrase); // Exit if the specific course is already cached - return Promise.resolve(); + return Promise.resolve(); } const search = JSON.stringify({ by: searchBy, phrase }); @@ -293,10 +305,9 @@ export class SOC_API extends SOC_Generic { }, ); // Add the course to the courses array - this.courses.push(course); + this.courses.push(course); // Cache the course by its code - this.courseCache.set(courseCode, [course]); - + this.courseCache.set(courseCode, [course]); } });