Skip to content

Commit 71b9e47

Browse files
committed
feat: Enhance event and notification systems with comprehensive reminder workflows and documentation
1 parent e7939cf commit 71b9e47

File tree

6 files changed

+244
-8
lines changed

6 files changed

+244
-8
lines changed

docs/events_flow.mmd

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,39 @@ flowchart TD
1717
SCOPE -->|>= now| UPCOMING[Upcoming]
1818
SCOPE -->|< now| PAST[Past]
1919

20-
%% Optional Geocoding
20+
%% Optional Geocoding & Location
2121
SAVE --> GEO[Optional: geocoding job]
22+
SAVE --> LOC[LocatableLocation\npolymorphic]
23+
24+
%% Event Notification System
25+
SAVE --> SCHED[EventReminderSchedulerJob]
26+
SCHED --> CALC{Calculate reminder times}
27+
CALC --> R24[Schedule 24h reminder]
28+
CALC --> R1[Schedule 1h reminder]
29+
CALC --> RS[Schedule start reminder]
30+
31+
%% Background Reminder Jobs
32+
R24 --> RJ24[EventReminderJob\n24 hours before]
33+
R1 --> RJ1[EventReminderJob\n1 hour before]
34+
RS --> RJS[EventReminderJob\nat start time]
35+
36+
%% Reminder Processing
37+
RJ24 --> PROC[Process attendees]
38+
RJ1 --> PROC
39+
RJS --> PROC
40+
PROC --> GOING{Filter 'going' status}
41+
GOING --> ERN[EventReminderNotifier]
42+
43+
%% Multi-channel delivery
44+
ERN --> AC[Action Cable\nReal-time notification]
45+
ERN --> EMAIL{Email enabled?}
46+
EMAIL -->|Yes| MAIL[EventMailer\n15min delay]
47+
EMAIL -->|No| SKIP[Skip email]
48+
49+
%% Event Updates
50+
UPDATE[Event updated] --> EUN[EventUpdateNotifier]
51+
EUN --> AC
52+
EUN --> EMAIL
2253

2354
%% Display & Actions
2455
PZ --> SHOW[Show page]
@@ -31,16 +62,23 @@ flowchart TD
3162
AUTH -->|Yes| RSVP[RSVP Actions]
3263
AUTH -->|No| GUEST[Guest view only]
3364
RSVP --> INT[Mark Interested]
34-
RSVP --> GOING[Mark Going]
65+
RSVP --> GOING_ACT[Mark Going]
3566
RSVP --> CANCEL[Cancel RSVP]
3667
INT --> ATT[EventAttendance record]
37-
GOING --> ATT
68+
GOING_ACT --> ATT
3869
CANCEL --> DEL[Delete attendance]
3970

71+
%% Trigger reminders when status changes
72+
ATT --> RESCHED[Reschedule reminders\nif going]
73+
4074
%% ICS Export
4175
SHOW --> ICS[ICS Export]
4276
ICS --> ICSAUTH{Authorization check}
4377
ICSAUTH -->|Public or authorized| EXPORT[Generate .ics file]
4478
ICSAUTH -->|Not authorized| 404[404 Not Found]
4579
EXPORT --> CAL[Calendar application]
4680

81+
%% Notification preferences
82+
ERN -.-> PREFS[Check user preferences:\nevent_reminders, notify_by_email]
83+
EUN -.-> PREFS
84+

docs/events_flow.png

91.7 KB
Loading

docs/events_system.md

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,144 @@
11
# Events & Calendars
22

3-
This## What's Implemented
3+
This document explains the Event model, how events are created and displayed, how visibility works, how calendars fit in, and the comprehensive notification system for event reminders and updates.
4+
5+
## What's Implemented
46
- **RSVPs/Attendees**: `EventAttendance` model with `person_id`, `event_id`, `status` (interested/going/not_going), guarded by privacy/policy.
57
- **ICS Export**: Export endpoint at `/events/:id/ics` that renders VEVENT from name/description/time/location.
8+
- **Event Reminder System**: Comprehensive notification system for upcoming events with multiple delivery channels.
9+
- **Event Update Notifications**: Automatic notifications when event details change.
10+
- **Location Support**: Full location support with polymorphic `LocatableLocation` model.
611

712
## What's Not Implemented Yet
813
- **Recurrence**: No repeat rules; all events are single instances.
914
- **Calendar Entries**: `CalendarEntry` exists but is not used to associate events to calendars.
10-
- **Event Location/Address**: Address association is commented out; no geocoding integration active.
1115
- **Advanced RSVP Features**: No waitlists, capacity limits, or guest allowances.
12-
- **Event Reminders**: No notification system for upcoming events.
13-
- **Event Updates/Changes**: No notification system when event details change.
14-
- **Bulk Operations**: No bulk event creation, editing, or management tools.e explains the Event model, how events are created and displayed, how visibility works, and how calendars fit in. It also notes what is not yet implemented (RSVPs, attendees, recurrence) so maintainers know the current scope.
16+
- **Bulk Operations**: No bulk event creation, editing, or management tools.
1517

1618
## Event Attendance & RSVPs
19+
20+
## Event Reminder & Notification System
21+
22+
### Components Overview
23+
The event notification system consists of several integrated components:
24+
25+
- **EventReminderNotifier**: Noticed event class for sending event reminder notifications
26+
- **EventReminderJob**: Background job for processing reminder notifications for all attendees
27+
- **EventReminderSchedulerJob**: Schedules future reminder notifications at appropriate intervals
28+
- **EventMailer**: Handles email delivery for event reminders and updates
29+
- **EventUpdateNotifier**: Sends notifications when event details change
30+
31+
### Event Reminder Workflow
32+
33+
#### Scheduling Reminders
34+
1. When an event is created or updated, `EventReminderSchedulerJob` is triggered
35+
2. The scheduler calculates appropriate reminder times based on event start time:
36+
- **24 hours before**: For events more than 24 hours away
37+
- **1 hour before**: For events more than 1 hour away
38+
- **At start time**: For immediate notifications
39+
3. Background jobs are scheduled using `perform_at` for each reminder interval
40+
4. Only "going" attendees receive reminder notifications
41+
42+
#### Notification Delivery
43+
1. `EventReminderJob` processes each scheduled reminder:
44+
- Finds all attendees with "going" status
45+
- Creates `EventReminderNotifier` instances for each attendee
46+
- Respects user notification preferences
47+
2. `EventReminderNotifier` handles multi-channel delivery:
48+
- **Action Cable**: Real-time in-app notifications via `NotificationsChannel`
49+
- **Email**: HTML emails with event details (15-minute delay to batch notifications)
50+
3. Email delivery is conditional based on:
51+
- User has email address configured
52+
- User has `notify_by_email` preference enabled
53+
- User has `event_reminders` preference enabled
54+
- Anti-spam: Only one email per unread event notifications
55+
56+
#### Event Update Notifications
57+
1. When event details change, `EventUpdateNotifier` is triggered
58+
2. Sends notifications to all attendees about the changes
59+
3. Includes information about what specific attributes changed
60+
4. Uses the same delivery channels as reminder notifications
61+
62+
### Notification Preferences
63+
Users can control event notifications through their preferences:
64+
- `event_reminders`: Enable/disable event reminder notifications
65+
- `notify_by_email`: Enable/disable email notifications globally
66+
- `show_conversation_details`: Control visibility of conversation details in emails
67+
68+
### Anti-Spam & Batching
69+
- **Email Batching**: 15-minute delay on email delivery to group related notifications
70+
- **Duplicate Prevention**: Only one email per unread notification group per event
71+
- **Preference Respect**: All notifications respect user preferences and can be disabled
72+
73+
### Technical Implementation Details
74+
75+
#### Classes & Responsibilities
76+
- **`BetterTogether::EventReminderNotifier`**: Noticed event class extending `ApplicationNotifier`
77+
- Handles multi-channel delivery (Action Cable + Email)
78+
- Includes anti-spam logic and preference checking
79+
- Generates localized notification content
80+
- **`BetterTogether::EventReminderJob`**: Background job extending `ApplicationJob`
81+
- Processes events and finds "going" attendees
82+
- Creates notifier instances for each attendee
83+
- Handles error cases gracefully (missing events, connection issues)
84+
- Queue: `:notifications` with retry configuration
85+
- **`BetterTogether::EventReminderSchedulerJob`**: Scheduling job
86+
- Calculates appropriate reminder intervals based on event timing
87+
- Schedules future `EventReminderJob` instances
88+
- Prevents scheduling reminders for past events or drafts
89+
- **`BetterTogether::EventMailer`**: Mailer class extending `ApplicationMailer`
90+
- Renders HTML emails with event details
91+
- Uses Rails 7+ parameter pattern (`params[:key]`)
92+
- Includes event location, timing, and registration information
93+
- **`BetterTogether::EventUpdateNotifier`**: Handles event change notifications
94+
- Triggers when event attributes are modified
95+
- Notifies all attendees (not just "going" status)
96+
- Includes information about what changed
97+
98+
#### Notification Timing Strategy
99+
- **24-hour reminders**: For events starting more than 24 hours in the future
100+
- **1-hour reminders**: For events starting more than 1 hour in the future
101+
- **Start-time notifications**: For events starting within the hour
102+
- **Update notifications**: Immediate when event details change
103+
104+
#### Queue & Background Processing
105+
- Uses `:notifications` queue for all event-related jobs
106+
- Retry configuration: Up to 5 attempts with polynomial backoff
107+
- Discard policy: `ActiveRecord::RecordNotFound` errors are discarded
108+
- Error handling: Jobs complete gracefully for missing/invalid events
109+
110+
### Models & Data Flow
111+
- **Event**: Has many `event_attendances` and `attendees` (people)
112+
- **EventAttendance**: Links person to event with status (interested/going/not_going)
113+
- **Noticed::Notification**: Stores notification records with read/unread status
114+
- **Noticed::Event**: Base class for all notifier events
115+
116+
### Testing Coverage
117+
The event reminder system has comprehensive test coverage:
118+
119+
#### EventReminderNotifier Specs
120+
- Tests notification content generation (title, body, identifiers)
121+
- Validates parameter handling and defaults
122+
- Verifies unread count inclusion in messages
123+
- Uses mock objects following established patterns
124+
125+
#### EventReminderJob Specs
126+
- Tests attendee filtering and notification delivery
127+
- Validates error handling for missing/invalid events
128+
- Confirms queue configuration and retry policies
129+
- Verifies reminder type parameter handling
130+
131+
#### EventMailer Specs
132+
- Tests email rendering with event details
133+
- Validates headers, subject lines, and recipient handling
134+
- Tests localization support
135+
- Confirms delivery methods work correctly
136+
137+
#### Integration Testing
138+
- Tests complete notification workflow from event creation to delivery
139+
- Validates preference-based filtering
140+
- Tests anti-spam and batching behavior
141+
- Ensures proper authorization checks
17142
- Model: `BetterTogether::EventAttendance`
18143
- Associations: `belongs_to :event`, `belongs_to :person`
19144
- Status enum: `interested`, `going`, `not_going`

docs/notifications_flow.mmd

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ flowchart TD
1616
MREAD[View conversation] --> MR[Mark read via NotificationReadable]
1717
end
1818

19+
%% Events Flow
20+
subgraph EVT[Events]
21+
E1[Event created/updated] --> ERS[EventReminderSchedulerJob]
22+
ERS --> ERJ[EventReminderJob\nscheduled at intervals]
23+
ERJ --> ERN[EventReminderNotifier]
24+
E2[Event details changed] --> EUN[EventUpdateNotifier]
25+
ERN -->|deliver to going attendees| EV
26+
EUN -->|deliver to all attendees| EV
27+
EM -. gated .-> ERN
28+
EM -. gated .-> EUN
29+
ERN -.-> ERN_NOTE[Email waits 15m; one email per unread event\nRespects: event_reminders, notify_by_email]
30+
EUN -.-> EUN_NOTE[Includes changed attributes info]
31+
EREAD[View event] --> ER[Mark read via NotificationReadable]
32+
end
33+
1934
%% Exchange: Matches
2035
subgraph MAT[Exchange Matches]
2136
X1[Offer/Request created] --> MF[Exchange#notify_matches]

docs/notifications_flow.png

-27.3 KB
Loading

docs/notifications_system.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ This document explains how notifications are produced, delivered, deduplicated,
3838

3939
- PageAuthorshipNotifier (`app/notifiers/better_together/page_authorship_notifier.rb`)
4040
- Trigger: author added/removed on a Page (via `BetterTogether::Authorship`).
41+
- Recipients: all current page authors.
42+
- Delivery: Action Cable immediately; Email deferred (`wait 15.minutes`) and only if `send_email_notification?`.
43+
- Email dedupe grouping: one email per unread page per recipient (on-site notifications may still accumulate).
44+
45+
- EventReminderNotifier (`app/events/better_together/event_reminder_notifier.rb`)
46+
- Trigger: `EventReminderJob` execution 1 hour before event start time (scheduled by `EventReminderSchedulerJob`).
47+
- Recipients: Event creator and interested community members.
48+
- Delivery: Action Cable immediately; Email deferred and only if `send_email_notification?`.
49+
- Email content: Localized event details, start time, location, and community context.
4150
- Delivery: Action Cable immediately; Email deferred (`wait 15.minutes`) and only if `send_email_notification?`.
4251
- Email dedupe grouping: one email per unread page per recipient (on-site notifications may still accumulate).
4352

@@ -50,11 +59,57 @@ This document explains how notifications are produced, delivered, deduplicated,
5059
- Usage:
5160
- Joatu controllers include the concern and mark `MatchNotifier` read on Offer/Request show; mark Agreement-related notifications read on Agreement show.
5261
- ConversationsController uses the concern to mark `NewMessageNotifier` notifications read when viewing a conversation with messages.
62+
- EventsController uses the concern to mark `EventReminderNotifier` notifications read when viewing event details.
5363
- NotificationsController uses the concern for record-based marking.
5464

65+
## Event Notifications
66+
67+
### EventReminderNotifier
68+
- Notifier Class: `BetterTogether::EventReminderNotifier` (Noticed event in `app/events/better_together/event_reminder_notifier.rb`)
69+
- Purpose: Send event reminders to interested participants
70+
- Trigger: `EventReminderJob` executes 1 hour before event start_time for events with notifications enabled
71+
- Recipients: Event creator and any interested members (those who've shown interest in the event)
72+
- Record: BetterTogether::Event being reminded about
73+
- Params: `{ event: <Event instance>, host_community: <Community instance> }`
74+
75+
### Event Email System
76+
- Mailer: EventMailer (`app/mailers/better_together/event_mailer.rb`)
77+
- Template: `event_reminder.html.erb` (I18n localized subject and body)
78+
- Email Logic:
79+
- Check `recipient.send_email_notification?` preference
80+
- Use recipient's preferred locale for rendering
81+
- Subject/body from I18n keys: `better_together.event_mailer.event_reminder.subject`/`.body`
82+
- Include event title, start time (localized), location, and community context
83+
- Handle unread notification count in message
84+
- Delivery: Action Cable immediately; Email deferred and only if email notifications enabled
85+
- Email batching: Multiple events can trigger separate notifications (no artificial grouping)
86+
87+
### Event Reminder Scheduling
88+
- Background Job: `EventReminderSchedulerJob` (runs periodically via cron/scheduler)
89+
- Purpose: Schedule `EventReminderJob` instances for upcoming events
90+
- Logic: Query events with notifications enabled that start in ~1 hour, schedule individual reminder jobs
91+
- Queue: `:notifications` with retry configuration
92+
- Error Handling: Individual event reminder failures don't affect batch processing
93+
94+
### Event Notification Integration
95+
- Triggers:
96+
- Event creation with notifications enabled: schedule future reminder
97+
- Event update: reschedule reminder if start_time changes
98+
- Event deletion: cancel pending reminder jobs
99+
- Action Cable: Real-time notifications via `BetterTogether::NotificationsChannel`
100+
- Read Marking: Event-specific notifications marked read when viewing event details
101+
102+
### Event Anti-Spam Measures
103+
- One reminder per event per recipient (no duplicate scheduling)
104+
- Only events with explicitly enabled notifications trigger reminders
105+
- Respects user email preferences (`send_email_notification?`)
106+
- Failed reminders logged but don't retry indefinitely
107+
- Reminder scheduling only occurs for future events (past events ignored)
108+
55109
## Recipient Preferences & Email
56110

57111
- Email delivery is gated:
112+
- EventReminderNotifier: `recipient.send_email_notification?` must be true for email delivery
58113
- NewMessageNotifier and PageAuthorshipNotifier: `recipient.notify_by_email` must be true, and an internal `should_send_email?` ensures one email per unread conversation/page.
59114
- AgreementNotifier and AgreementStatusNotifier: `recipient.notification_preferences['notify_by_email']` (and presence of email) must be true.
60115
- MatchNotifier: `recipient_has_email?` checks email and preferences.
@@ -68,6 +123,8 @@ This document explains how notifications are produced, delivered, deduplicated,
68123
## Known Behaviors / Considerations
69124

70125
- NewMessage/PageAuthorship email dedupe does not dedupe on-site notifications; MatchNotifier dedupes on-site notifications as well (preventing duplicate unread for the pair).
126+
- EventReminderNotifier sends one notification per event per recipient; scheduling prevents duplicate reminders for the same event.
127+
- Event reminders are only sent for events with notifications explicitly enabled and future start times.
71128
- Unread count included in Action Cable payload is computed per-recipient at send time.
72129
- JSONB params store reference GlobalIDs for Offer/Request; read markers account for both direct string and ActiveJob `_aj_globalid` formats.
73130

@@ -76,4 +133,5 @@ This document explains how notifications are produced, delivered, deduplicated,
76133
- Consider adding on-site dedupe/grouping to NewMessage/PageAuthorship similar to MatchNotifier if desired.
77134
- Aggregate match notifications when many pairs are found at once.
78135
- Add per-notifier throttling windows (e.g., rate limit bursty events via job scheduling).
136+
- Event reminders could be enhanced with configurable timing (currently fixed at 1 hour before).
79137

0 commit comments

Comments
 (0)