Skip to content

Add automated reminder for queue items without ticket (#10175)#11189

Open
nbudin wants to merge 7 commits intomainfrom
feature-10175-queue-without-ticket-reminder
Open

Add automated reminder for queue items without ticket (#10175)#11189
nbudin wants to merge 7 commits intomainfrom
feature-10175-queue-without-ticket-reminder

Conversation

@nbudin
Copy link
Contributor

@nbudin nbudin commented Feb 14, 2026

Summary

Implements #10175 - Adds automated email reminders for users who have items in their signup queue but haven't purchased a ticket yet (only for conventions where tickets are required).

Also refactors the reminder system to be more general by combining RemindDraftEventProposalsJob into a new SendRemindersJob that can handle multiple types of reminders.

Changes

Refactored Reminder System

  • Renamed RemindDraftEventProposalsJob to SendRemindersJob
  • Updated RunNotificationsService to use the new job name
  • The new job calls multiple reminder services:
    • RemindDraftEventProposals (existing functionality)
    • RemindQueueWithoutTicket (new functionality)

New Queue Reminder Feature

  • Service: RemindQueueWithoutTicket - finds users who need reminders
  • Notifier: SignupQueue::NoTicketReminderNotifier - handles email delivery
  • Database: Added queue_no_ticket_reminded_at timestamp to user_con_profiles
  • Configuration: Added signup_queue/no_ticket_reminder notification type
  • Dynamic Destination: Added SignupRankedChoiceUserConProfileEvaluator for targeting

Reminder Criteria

The service finds and reminds users who:

  • ✅ Have pending items in their signup queue
  • ✅ Don't currently have a ticket
  • ✅ Are in a convention where ticket_mode is required_for_signup or ticket_per_event
  • ✅ Haven't been reminded in the last week
  • ✅ Convention hasn't started yet

Email Template

The notification template needs to be configured per-convention in the admin interface at /admin_notifications. The template has access to:

  • user_con_profile - the user's convention profile
  • ticket_name - the convention's word for "ticket"
  • queue_items - array of pending signup ranked choices

Scheduling

Reminders are sent automatically when SendRemindersJob runs, which is triggered daily by RunNotificationsService.

Test Plan

  • Verify the job runs without errors
  • Create a convention with ticket_mode set to required_for_signup
  • Create a user profile with signup queue items but no ticket
  • Configure the notification template in admin
  • Run SendRemindersJob.perform_now and verify email is sent
  • Verify queue_no_ticket_reminded_at is set on the user profile
  • Run the job again immediately and verify no duplicate email is sent
  • Verify badgeless conventions (ticket_mode: disabled) don't trigger reminders

Notes

  • This is a minimal implementation - no convention-level settings to enable/disable
  • No user preference to opt out (can be added later if needed)
  • Email template content must be configured per-convention by admins
  • Reminders are throttled to once per week per user

🤖 Generated with Claude Code

@nbudin nbudin force-pushed the feature-10175-queue-without-ticket-reminder branch 2 times, most recently from 9170425 to 1b3c19d Compare February 14, 2026 19:35
Refactored RemindDraftEventProposalsJob into a general SendRemindersJob
that sends multiple types of reminders. Added new functionality to remind
users who have items in their signup queue but no ticket (only for
conventions where tickets are required).

Changes:
- Renamed RemindDraftEventProposalsJob to SendRemindersJob
- Created RemindQueueWithoutTicket service to find users needing reminders
- Added queue_no_ticket_reminded_at timestamp to user_con_profiles table
- Added signup_queue/no_ticket_reminder notification type to config
- Created SignupQueue::NoTicketReminderNotifier for email delivery
- Added SignupRankedChoiceUserConProfileEvaluator dynamic destination
- Updated RunNotificationsService to use new job name
- Fixed pre-existing rubocop issue in notifier DSL

The service finds users who:
- Have pending items in their signup queue
- Don't have a ticket
- Are in a convention where tickets are required
- Haven't been reminded in the last week

Reminders are sent automatically when the SendRemindersJob runs (daily
via RunNotificationsService).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@nbudin nbudin force-pushed the feature-10175-queue-without-ticket-reminder branch from 1b3c19d to e620c1a Compare February 14, 2026 19:37
nbudin and others added 6 commits February 14, 2026 11:40
The signup_queue/no_ticket_reminder notifier requires a user_con_profile
parameter, which needs to be handled by the preview factory for
integration tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The evaluator now takes a signup_ranked_choice parameter and extracts
the user_con_profile from it, consistent with other evaluators like
SignupUserConProfileEvaluator and OrderUserConProfileEvaluator.

Updated:
- SignupRankedChoiceUserConProfileEvaluator to accept signup_ranked_choice
- SignupQueue::NoTicketReminderNotifier to accept signup_ranked_choice
- RemindQueueWithoutTicket service to pass first pending signup_ranked_choice
- NotifierPreviewFactory to handle signup_ranked_choice parameter

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Instead of finding UserConProfiles and then searching for their
signup_ranked_choices, the service now queries SignupRankedChoices
directly and uses DISTINCT ON to get one per user. This is more
efficient and aligns better with the notifier's interface.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated RemindQueueWithoutTicket to:
- Send reminders only once (no repeat reminders)
- Send exactly one week before the first signup round with automated signups
- Only apply to conventions using ranked_choice signup automation
- Use BETWEEN clause to match signup rounds in the reminder window

Updated tests to:
- Create signup rounds with automation_action and ranked_choice_order
- Verify reminder is not sent when already reminded
- Add test for when signup round is not in reminder window
- Update convention to use signup_automation_mode: 'ranked_choice'

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved the signup_rounds filtering from .joins to .where clauses, which
provides cleaner native parameter binding with ? placeholders. The join
now only establishes the relationship, while all filtering happens in
.where clauses.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Previously, only tests asserting that emails were sent used
perform_enqueued_jobs. Tests asserting no emails were sent didn't
actually process the job queue, so they could accidentally pass even
if emails would have been sent.

Now all tests consistently use perform_enqueued_jobs to ensure the
job queue is processed and assertions are properly validated.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/notifiers/notifier/dynamic_destinations.rb 🟠 56.25% 🟠 56.34% 🟢 0.09%
app/notifiers/notifier_preview_factory.rb 🟢 79.17% 🟢 80% 🟢 0.83%
app/notifiers/signup_queue/no_ticket_reminder_notifier.rb 🔴 0% 🟠 64.29% 🟢 64.29%
app/services/remind_queue_without_ticket.rb 🔴 0% 🟢 100% 🟢 100%
test/services/remind_queue_without_ticket_test.rb 🔴 0% 🟢 100% 🟢 100%
Overall Coverage 🟢 56.5% 🟢 56.65% 🟢 0.15%

Minimum allowed coverage is 0%, this run produced 56.65%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant