DeCal Classes
Berkeleytime has first‑class support for DeCal classes. This feature enriches the standard SIS data with details from the DeCal board so students can see accurate DeCal titles, descriptions, application links, deadlines, and contact information directly in the class catalog and class view.
High‑level flow
- The
decalsdatapuller scrapes the DeCal board API and normalizes each listing into a structuredDeCalCourse. - For each DeCal course, we try to match it to an existing
ClassModelin Mongo using a set of heuristics (title similarity, meeting times, room, and faculty sponsor). - When we find a confident match, we write a
decalsub‑object onto thatClassModel. - Backend GraphQL resolvers expose this data via the
Class.decalfield. - The frontend reads
Class.decaland:- shows a “DeCal” badge in the catalog,
- uses the DeCal title on catalog cards and the class overview header,
- and surfaces application, syllabus, deadline, and contact information in the overview tab.
Scraping and normalization
The DeCal puller lives in apps/datapuller/src/pullers/decals.ts. It talks directly to the DeCal board’s JSON APIs (for example, the approved‑courses endpoint) instead of scraping HTML, then normalizes responses into an internal DeCalCourse shape:
- core metadata:
title,semester,department,category,units - sections: day/time, room, capacity, and section type
- descriptive content:
description,enrollmentInformation,websiteUrl,syllabusUrl - application metadata:
applicationUrl,applicationDueDate - facilitators:
{ name, email }
For development and debugging, the puller can persist the raw DeCal payloads into apps/datapuller/data/decals.json. When the DEBUG flag in the puller is enabled, subsequent runs will re‑use this local file instead of repeatedly calling the remote APIs. This makes it much easier to refine matching heuristics offline.
Matching DeCals to SIS classes
The DeCal board does not expose Berkeleytime class identifiers, so we have to infer the correct ClassModel for each DeCal. The puller uses a multi‑step scoring heuristic:
-
Term bucketing
DeCals are grouped by semester key (for example,"Spring 2026"). For each group we only consider classes from the matching Berkeleytime term, which keeps the candidate set small and avoids cross‑term matches. -
Department narrowing
If the DeCal has a department, we first search only within classes from that department. If no reasonable candidate appears, we fall back to searching across all departments for that term. -
Title similarity (fuzzy search)
We build aFuzzySearchindex of candidate classes using course and class titles. For each DeCal title we:- compute a fuzzy similarity score against each candidate,
- discard clearly unrelated classes below a minimum score,
- and keep only the top‑scoring subset for more expensive checks.
-
Meeting‑time and room heuristics
From the DeCal sections we derive normalized “day + time” strings and room identifiers, handling different display formats. For each candidate class:- we normalize its primary‑section meetings into the same representation,
- award positive score if any meeting time appears in the DeCal’s time set,
- and add extra score if the room or location text matches (for example, same building and room number).
-
Faculty / sponsor similarity
When a DeCal lists a faculty sponsor, we normalize the sponsor name and compare it to instructor names on each candidate class. If the sponsor appears to teach the candidate, we increase that candidate’s score. -
Composite score and decision
Each candidate gets a composite score that combines:- title similarity,
- meeting‑time overlap,
- room overlap,
- and faculty‑name similarity.
The puller selects the single best candidate whose score exceeds a threshold. If there is a tie or no strong candidate, we log the DeCal and skip it rather than guessing.
Persisting DeCal metadata
Once a DeCal is matched to a ClassModel, we populate the decal field on that document:
title: DeCal‑specific course titledescription: extended DeCal descriptionsyllabusUrl: URL to the DeCal syllabusapplicationUrl: URL for the application form or course siteapplicationDueDate: application deadline as a stringinstructors: array of{ name, email }derived from facilitators
This data flows through the stack as follows:
- Backend formatters expose
Class.decalvia the GraphQL schema (packages/gql-typedefs/class.ts). - The canonical catalog query (
packages/shared/queries.ts) includesdecal { title }so catalog cards can show a “DeCal” badge and DeCal title. - The class details queries (
apps/frontend/src/lib/api/classes.ts) fetchdecalso the class overview can:- show a dedicated “DeCal Application” section (application link, syllabus link, and formatted deadline),
- prefer the DeCal description over SIS description when present,
- hide SIS “Class Notes” for DeCal classes,
- and render contact information as
Name: emailpairs.
Overall, the DeCal feature lets Berkeleytime present rich, up‑to‑date information for student‑run courses without requiring any manual data entry in our database.