Skip to content

Fix: Refactored backend to fetch course data with schedule#1421

Open
vicksey wants to merge 13 commits intomainfrom
vz/fetch-grades-w-schedules
Open

Fix: Refactored backend to fetch course data with schedule#1421
vicksey wants to merge 13 commits intomainfrom
vz/fetch-grades-w-schedules

Conversation

@vicksey
Copy link
Copy Markdown
Contributor

@vicksey vicksey commented Jan 14, 2026

Summary

Refactored the backend to return a hydrated schedule (schedule + course data in one response) instead of requiring the frontend to make n separate websoc requests per course.

Note: The legacy ScheduleSaveState type is kept for backward compatibility (fallback scenarios, skeleton loading, imports, db saving)

Test Plan

  • Log in to an account with saved schedules
  • Import schedule
  • Load legacy?
  • Make sure custom events load correctly

Issues

Closes #1059

@vicksey vicksey marked this pull request as ready for review January 21, 2026 02:18
@vicksey vicksey requested a review from alexespejo January 21, 2026 02:18
@alexespejo
Copy link
Copy Markdown
Collaborator

Sorry for ghosting this. I'm looking through codebase and stuff to compose my review ✍️

Comment thread apps/antalmanac/src/actions/AppStoreActions.ts Outdated
Comment thread apps/antalmanac/src/backend/lib/websoc-service.ts Outdated
Comment thread apps/antalmanac/src/backend/routers/userData.ts Outdated
Copy link
Copy Markdown
Collaborator

@alexespejo alexespejo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the very delayed review. it took me a while to figure out how stuff works :p

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/backend/lib/websoc-service.ts">

<violation number="1" location="apps/antalmanac/src/backend/lib/websoc-service.ts:69">
P2: Debug `console.log` statements left in production code. These log the full request URL and entire API response body, which will produce excessive output in production. Remove or replace with a proper logger at an appropriate log level.</violation>

<violation number="2" location="apps/antalmanac/src/backend/lib/websoc-service.ts:76">
P1: Missing error handling: `queryWebSoc` doesn't check the HTTP response status or validate `data.data` before passing it to `sortWebsocResponse`. If the API returns an error, this will produce a confusing runtime exception. The sibling `queryWebSocDepartments` correctly validates the response — apply similar validation here.</violation>

<violation number="3" location="apps/antalmanac/src/backend/lib/websoc-service.ts:112">
P1: Bug: `Set` uses reference equality for objects, so this deduplication logic is ineffective. Two course objects from different API responses representing the same course will always be treated as distinct. Consider deduplicating by a unique key (e.g., `courseNumber`) using a `Map` instead.</violation>
</file>

<file name="apps/antalmanac/src/backend/routers/userData.ts">

<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:113">
P2: Courses that fail to hydrate are silently dropped from the schedule. If WebSOC is temporarily unavailable or a course is no longer listed, users will see schedules with missing courses and no indication of what was lost. Consider logging which courses couldn't be hydrated, or returning a partial/fallback representation (e.g., keep the `ShortCourse` data with a flag indicating it's unhydrated) so the frontend can inform the user.</violation>

<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P2: The hydration wrapping logic (null-check → hydrate → reconstruct response) is duplicated identically across `getUserDataHydrated` and `getUserDataWithSession`. Extract a shared helper (e.g., `hydrateUserData(userData)`) to reduce duplication and make future changes less error-prone.</violation>
</file>

<file name="apps/antalmanac/src/stores/AppStore.ts">

<violation number="1" location="apps/antalmanac/src/stores/AppStore.ts:421">
P1: Bug: `JSON.stringify` comparison will always fail due to mismatched property order between `getScheduleAsSaveState()` and `toShortSaveState()`.

`getScheduleAsSaveState()` returns `{ schedules, scheduleIndex }` with courses as `{ color, term, sectionCode }`, but `toShortSaveState()` produces `{ scheduleIndex, schedules }` with courses as `{ sectionCode, term, color }`. Since `JSON.stringify` preserves insertion order, these will never be equal even for identical data. This means `hasDataChanged` is always `false`, so `deleteTempSaveData()` is never reached and `loadTempSaveData()` always runs.

Either align the property order in `toShortSaveState` with `getScheduleAsSaveState`, or use a deep-equality comparison that is order-insensitive.</violation>
</file>

<file name="apps/antalmanac/src/backend/lib/rds.ts">

<violation number="1" location="apps/antalmanac/src/backend/lib/rds.ts:291">
P2: Misleading JSDoc: `schedulesToShort` is a pure data transformation, but the comment says it "Deletes and recreates all of the user's schedules and contents" — this was copy-pasted from the `upsertSchedulesAndContents` method below.</violation>
</file>

<file name="apps/antalmanac/src/actions/AppStoreActions.ts">

<violation number="1" location="apps/antalmanac/src/actions/AppStoreActions.ts:375">
P2: If the `?? userDataResponse` fallback ever activates (i.e., `userData` is nullish), `scheduleSaveState.schedules` will be `undefined`, causing `isEmptySchedule` to throw at runtime. Use optional chaining like `handleScheduleImport` does (`scheduleSaveState?.schedules`), or guard against this case explicitly.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/antalmanac/src/backend/lib/websoc-service.ts
Comment thread apps/antalmanac/src/backend/lib/websoc-service.ts
Comment thread apps/antalmanac/src/stores/AppStore.ts Outdated
Comment thread apps/antalmanac/src/backend/lib/websoc-service.ts
Comment thread apps/antalmanac/src/backend/routers/userData.ts Outdated
Comment thread apps/antalmanac/src/backend/routers/userData.ts
Comment thread apps/antalmanac/src/backend/lib/rds.ts Outdated
Comment thread apps/antalmanac/src/actions/AppStoreActions.ts Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/backend/routers/userData.ts">

<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:82">
P1: All-or-nothing hydration: a single term's WebSOC failure now causes the entire hydration to abort, returning all schedules unhydrated. Previously, only the failed term's courses would be dropped while other terms' courses were still hydrated. Consider restoring per-term graceful degradation (e.g., setting an empty dict for the failed term) so that a partial WebSOC outage doesn't penalize courses from other terms.</violation>

<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P3: The try/catch + hydration + fallback pattern is duplicated between `getUserDataHydrated` and `getUserDataWithSession`. Consider extracting a helper like `tryHydrateUserData(userData)` to reduce duplication and ensure consistent error handling if this pattern evolves.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/antalmanac/src/backend/routers/userData.ts Outdated
Comment thread apps/antalmanac/src/backend/routers/userData.ts
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/backend/routers/userData.ts">

<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:82">
P1: Silent data loss: When WebSOC is unavailable for a term, all courses from that term are silently dropped from the hydrated schedule. Previously, the thrown error caused the caller to fall back to returning the original unhydrated data (which the frontend can handle). Now courses just disappear.

Consider either: (a) preserving the original short course entries when hydration fails for a term so the frontend can handle them, or (b) restoring the throw so the caller's fallback logic can return unhydrated data.</violation>

<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P1: Resilience regression: Removing the try-catch around `hydrateScheduleCourses` means any unexpected error during hydration (e.g., malformed data, runtime errors in the mapping logic) will now crash the endpoint and return an error to the client. Previously, the catch block gracefully fell back to returning unhydrated schedule data, ensuring users always got their data. Consider keeping the try-catch fallback for robustness.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/antalmanac/src/backend/routers/userData.ts Outdated
Comment thread apps/antalmanac/src/backend/routers/userData.ts
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/backend/routers/userData.ts">

<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:181">
P2: The hydration try-catch block is duplicated verbatim between `getUserDataHydrated` and `getUserDataWithSession`. Extract a shared helper (e.g., `hydrateUserData(userData)`) that wraps the try-catch, result construction, and fallback logic to keep it DRY and reduce maintenance burden.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/antalmanac/src/backend/routers/userData.ts
Copy link
Copy Markdown
Collaborator

@alexespejo alexespejo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to use the dev feature in #1565 to test the changes on staging. Maybe we can also benchmark the performance improvements

};

export function isEmptySchedule(schedules: ShortCourseSchedule[]) {
export function isEmptySchedule(schedules: ShortCourseSchedule[] | ScheduleSaveState['schedules']) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: ShortCourseSchedule[] and ScheduleSaveState['schedules'] seem to have the same shape?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ScheduleSaveState['schedules'] has ScheduleCourse which has the course data

export type ScheduleSaveState = {
schedules: Array<{
scheduleName: string;
courses: (ShortCourse | ScheduleCourse)[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still weary about mixing the types just afraid it will create unforeseen complications in prod

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more of a fallback in case websoc resp fails and also allows for the same type to be used to save into db. Like I could technically get rid of it and seperate the two types, bc idt aa will load without websoc. I'm more weary having just a state that must relies on websoc resp but if you think its better, than I can just remove it the mixed type, seperate the two. There's just no way for me to not keep the legacy type bc of our db, skeleton logic, etc.

export type ScheduleSaveState = {
schedules: Array<{
scheduleName: string;
courses: (ShortCourse | ScheduleCourse)[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still weary of mixing the types in our code base. I'm afraid of unforseen complications in prod that can come up

@github-actions github-actions Bot added the Stale label Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fetch grades info with schedule

2 participants