-
Notifications
You must be signed in to change notification settings - Fork 23
Sync Program Class start_Dt and end_Dt #1016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync Program Class start_Dt and end_Dt #1016
Conversation
jtucholski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@corypride this works pretty well, I noticed one issue:
- I set up a recurring class 7 days per wk on 9/1 through 9/30 so the start_dt was 9/1/25
- I cancelled the first class. The start_dt was updated to 9/2/25.
- I restored the first class. The start_dt was still 9/2/25.
I would have thought the class start_dt goes back to 9/1/25 again.
Also, are you able to include tests that show that the class start_dt and end_dt update when overides are added/removed? We have some existing RRule tests in place so we can probably add it to those?
CK-7vn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just remove/think about those comments, and it works really well as long as we don't hit a class with multiple events, once that's all fixed it'll be great!
jtucholski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments but looks and works great!
CK-7vn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite literally just the same stuff Josh said. Id add that comment so we don't forget to refactor it, add the UTC and make that constant, works great man!
carddev81
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@corypride code looks good, but there are a couple functionality issues when rescheduling the event and restoring that event:
When creating an override event as described in the asana ticket (Nov 1 - 30; override last scheduled event to be December 2). Duplicate events are created. Also when restoring that event, the series is still ending on December 2. See video:
2025-10-15.09-22-57.mp4
a4ea872 to
60bc28a
Compare
CK-7vn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple easy things, it works and tests great though!
backend/src/database/class_events.go
Outdated
|
|
||
| originalEndRange := class.StartDt.AddDate(0, 3, 0) | ||
| if class.EndDt != nil { | ||
| monthsDiff := int(class.EndDt.Sub(class.StartDt).Hours() / (24 * 30)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are assuming that all months have 30 days here, this will cause some issues. Example fix:
startYear, startMonth, _ := class.StartDt.Date()
endYear, endMonth, _ := class.EndDt.Date()
monthsDiff := (endYear-startYear)*12 + int(endMonth-startMonth)
backend/src/database/class_events.go
Outdated
|
|
||
| func (db *DB) calculateOriginalEndDate(trans *gorm.DB, classID uint) (time.Time, error) { | ||
| var events []models.ProgramClassEvent | ||
| if err := trans.Where("class_id = ?", classID).Find(&events).Error; err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should preload Overrides here since we are calling calculateClassBoundariesWithContext(), since both of these methods are called in the same transaction it could cause stale data.
backend/src/database/class_events.go
Outdated
| newOptions.Until = newUntil | ||
|
|
||
| newRule, err := rrule.NewRRule(newOptions) | ||
| if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should bubble up the error from this, because if we ignore it some events might not get their UNTIL updated, and we won't know why. I don't mind the if err == nil logic, but we should probably and an else just in case.
ff3c352 to
de6fc5c
Compare
|
@corypride I'm still experiencing the problem from before of duplicating the data. Also I noticed that the days leading up to the changed end date is also being scheduled rather than just the one event. We can discuss the issue and work on it together, this seems like a weird issue that we may need to work out together. Let me know
|
|
Please let me know when this PR is ready for review, and I will look over it. (waiting because I know you both had a work session today about this). |
de6fc5c to
2af8772
Compare
2af8772 to
8eed054
Compare
carddev81
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just have to fix the javascript logic for determining the next scheduled date
backend/src/database/class_events.go
Outdated
| } | ||
|
|
||
| func (db *DB) syncClassDateBoundaries(trans *gorm.DB, classID uint) error { | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a nit: remove blank line
backend/src/database/class_events.go
Outdated
| updates["end_dt"] = computedEnd | ||
| } | ||
| } else { | ||
| if (!hasOverrides && !ruleUntil.IsZero() && !ruleUntil.Equal(*class.EndDt)) || (!ruleUntil.IsZero() && ruleUntil.After(*class.EndDt)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this up into the else changing it to else if
backend/src/models/class_event.go
Outdated
| type ProgramClassEvent struct { | ||
| DatabaseFields | ||
| ClassID uint `json:"class_id" gorm:"not null" validate:"required"` | ||
| ClassID uint `json:"class_id" gorm:"not null"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious why validate:"required" was removed? Can this be added back?
| ); | ||
|
|
||
| function getNextOccurrenceDateAsStr(): string { | ||
| const now = new Date(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rework to calculate the next scheduled date. You will have to check overrides.
carddev81
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@corypride Looks great!! Tests PASSED!!
calisio
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will approve this PR for now, pending that one comment about a large chunk of tests being commented out but still in the code base. I would be curious to hear from the field if canceling a class would move back or forward a start date. I would assume that even if the first class is cancelled, then the start date would remain on the day of the cancelled first class. I think that the only time the start and end dates should change are if there are events that are NOT CANCELLED taking place before or after the start and end date. I will post in slack about this concern.
| require.True(t, updatedClass.EndDt.After(*createdClass.EndDt), "End date should be later than original November 30") | ||
| } | ||
|
|
||
| // // Test that deleting event overrides properly restores class boundaries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are all of these lines commented out? should we delete them?
73bb5d0 to
43a44b1
Compare

Description
Implemented automatic synchronization of class
start_dtandend_dtfields when events are rescheduled or cancelled beyond the original class date boundaries. This fixes calendar navigation issues where rescheduled events (like moving Thanksgiving classes into December) were inaccessible because the parent class dates didn't reflect the actual schedule.Additionally fixed critical bugs related to date discrepancies between dashboard and calendar views, and issues with restored events not properly reverting dates.
Key Changes Made
1. Dynamic RRULE UNTIL Calculation
File:
backend/src/database/class_events.goFunction:
calculateOriginalEndDate()2. Enhanced Date Boundary Synchronization
Functions Added/Updated:
syncClassDateBoundaries()- Recalculates actual class boundariescalculateClassBoundariesWithContext()- Handles restore scenariosupdateClassDateBoundaries()- Updates only class date fieldsupdateRRuleUntilDates()- Updates RRULE UNTIL parameters3. Multiple Event Series Support
.First()to.Find()and loop through all events4. Improved Timezone Handling
Truncate(24 * time.Hour)for date-only fields5. Enhanced Transaction Integration
Bug Fixes Resolved
1. Date Discrepancy Issue
2. Restore Events Bug
start_dtforward, but restoring didn't move it backrRule.OrigOptions.Dtstartas source of truth3. Multiple Event Series Bug
.First()instead of.Find()Technical Implementation Details
Core Algorithm Flow
start_dt/end_dtto match realityKey Constants
MaxYearsForInfiniteRRule = 5- Prevents infinite pattern issuesClassEndDateBufferMonths = 3- Allows reasonable override extensionsError Handling
Testing Coverage
All integration tests pass, verifying:
Known Limitations
Manual Date Edits: Class
start_dt/end_dtare now computed fields - manual edits through Edit Class form are ignored. To change class dates, admins should reschedule actual events instead.Initial Creation: Sync only triggers on override operations. Initial event creation with mismatched dates won't auto-correct until first override is created (minor UX issue).
Future Enhancements Considered
CreateNewEventfor initial date correction (kept out of scope)This implementation provides a robust, maintainable solution that resolves the core date synchronization issues while maintaining full backward compatibility and comprehensive test coverage.
Screenshot(s)
RRuleDateSync.mp4