SOC class occurrences are generated on holidays and breaks #38

Open
opened 2026-10-03 19:44:37 +00:00 by jalenluorion · 0 comments
Member

What's wrong
SOC classes become one weekly RRULE from semester start to end, with no exception dates. Nothing in the scraper or in the occurrence expansion knows about university holidays or breaks, so classes show up on them. On prod, about 1,900 Spring 2026 occurrences fall on Spring Break days, and Fall 2026 will show classes during Fall Break and Thanksgiving.

How to reproduce
On prod (read-only), with the break dates from https://www.cmu.edu/hub/calendar/:

select count(*) from event_occurrences o join events e on e.id = o.event_id
where e.event_type = 'ACADEMIC' and e.semester = 'Spring_26'
  and (o.start_datetime at time zone e.event_timezone)::date between '<break start>' and '<break end>';

Expected
No SOC occurrence on a day without classes in CMU's academic calendar.

Where to look

  • api/scraper/helpers/semester.py: SEMESTER_CONFIG only has start and end dates. Add per-semester no-class dates here (checked against the HUB calendar each year).
  • api/scraper/helpers/recurrence.py build_rrule_from_parts() and api/scraper/transforms/soc_events.py: rules are built here.
  • api/scraper/persistence/supabase_recurrence.py: rules are deleted and reinserted every run, and recurrence_exdates rows cascade with them, so write the EXDATEs after the rules on every run.
  • api/app/models/event_occurrence.py _build_occurrence_rows() L321: an EXDATE must equal the occurrence start exactly (same local time in the event's timezone).
  • Line numbers are from main at 2c5f2e9.

Done when

  • a rerun of python -m scraper.scripts.export_soc --semester Fall_26 removes Fall Break and Thanksgiving occurrences
  • test in api/tests/scraper_events/test_no_class_dates.py that expands a MWF class and checks no break day is generated
**What's wrong** SOC classes become one weekly RRULE from semester start to end, with no exception dates. Nothing in the scraper or in the occurrence expansion knows about university holidays or breaks, so classes show up on them. On prod, about 1,900 Spring 2026 occurrences fall on Spring Break days, and Fall 2026 will show classes during Fall Break and Thanksgiving. **How to reproduce** On prod (read-only), with the break dates from https://www.cmu.edu/hub/calendar/: ``` select count(*) from event_occurrences o join events e on e.id = o.event_id where e.event_type = 'ACADEMIC' and e.semester = 'Spring_26' and (o.start_datetime at time zone e.event_timezone)::date between '<break start>' and '<break end>'; ``` **Expected** No SOC occurrence on a day without classes in CMU's academic calendar. **Where to look** - `api/scraper/helpers/semester.py`: `SEMESTER_CONFIG` only has start and end dates. Add per-semester no-class dates here (checked against the HUB calendar each year). - `api/scraper/helpers/recurrence.py` `build_rrule_from_parts()` and `api/scraper/transforms/soc_events.py`: rules are built here. - `api/scraper/persistence/supabase_recurrence.py`: rules are deleted and reinserted every run, and `recurrence_exdates` rows cascade with them, so write the EXDATEs after the rules on every run. - `api/app/models/event_occurrence.py` `_build_occurrence_rows()` L321: an EXDATE must equal the occurrence start exactly (same local time in the event's timezone). - Line numbers are from main at 2c5f2e9. **Done when** - [ ] a rerun of `python -m scraper.scripts.export_soc --semester Fall_26` removes Fall Break and Thanksgiving occurrences - [ ] test in `api/tests/scraper_events/test_no_class_dates.py` that expands a MWF class and checks no break day is generated
jalenluorion added this to the Phase 1 milestone 2026-10-03 19:44:37 +00:00
jalenluorion added this to the CMUCal project 2026-10-03 19:45:09 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ScottyLabs/cal#38
No description provided.