Skip to content

feat(email): add support for scheduled email sending in tickets/talks#3485

Open
MukundC25 wants to merge 3 commits intofossasia:devfrom
MukundC25:feat/email-scheduling-v3
Open

feat(email): add support for scheduled email sending in tickets/talks#3485
MukundC25 wants to merge 3 commits intofossasia:devfrom
MukundC25:feat/email-scheduling-v3

Conversation

@MukundC25
Copy link
Copy Markdown
Member

Fixes #1137

What This PR Does

Implements email scheduling for both the Tickets and Talk/CfP components, allowing organizers to set a future date/time for email delivery instead of sending immediately.

Database Schema & Guards

  • Adds scheduled_at field to QueuedMail (Talk/CfP component)
  • Adds scheduled_at field to [EmailQueue] (Tickets component)
  • Adds validation guards in [send()] methods to raise exceptions when attempting to manually send future-scheduled emails
  • Pre-check in [send_queued_mail] Celery task for correct retry/logging behavior

Scheduler & Background Processing

  • Precise ETA Dispatch: When an email is scheduled, a Celery task is immediately dispatched with eta=scheduled_at, so delivery happens at the exact scheduled time
    • Tickets: dispatches send_queued_mail.apply_async(eta=scheduled_at) per email
    • Talk/Orga: dispatches send_periodic_signal.apply_async(eta=scheduled_at) to trigger [process_scheduled_emails]
  • Safety Net Poller: [process_scheduled_emails] signal receiver runs every 60 seconds via Celery Beat as a fallback for any emails missed (e.g. worker was down)
  • Concurrency Safety: select_for_update(skip_locked=True) + @minimum_interval decorator prevent duplicate sends across workers
  • Exception Handling: Catches SendMailException specifically; re-raises unexpected exceptions to keep errors visible

UI Features

  • Compose UI: Adds "Send Later" datetime picker to email compose forms for both Tickets and Talk components
  • Outbox UI: Status badge showing scheduled time or "Immediate" for each email
  • Outbox Edit: Allows editing scheduled_at for unsent emails
  • Validation: Rejects past timestamps (with 1-minute buffer), prevents empty recipient batches

Infrastructure Requirements

For Local Development: No additional setup beyond docker compose up -d. Celery Beat can be configured via Django admin (PeriodicTask entry for [eventyay.common.tasks.send_periodic_signal].

For Production: Requires Celery Beat to be running with a PeriodicTask entry for [eventyay.common.tasks.send_periodic_signal] at a 60-second interval. This is intentionally not automated via migrations to avoid coupling application migrations to infrastructure configuration.

Testing

  1. Schedule an email via UI with a future timestamp
  2. Verify it appears in outbox with "Scheduled for" badge
  3. Wait for the scheduled time — email should send within seconds
  4. Monitor worker logs: docker compose logs worker -f | grep ScheduledMail
  5. Confirm with: SELECT id, subject, scheduled_at, sent_at FROM sendmail_emailqueue WHERE scheduled_at IS NOT NULL;

Notes

  • scheduled_at stored in UTC, displayed in user's timezone
  • Failed sends are logged but don't block other emails in the batch
  • Emails with no recipients are marked as processed to prevent infinite reprocessing

Email Scheduling (IST)

Screen.Recording.2026-05-07.at.7.54.23.PM.mov

Copilot AI review requested due to automatic review settings May 7, 2026 14:39
@github-project-automation github-project-automation Bot moved this to Backlog in Eventyay Next May 7, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @MukundC25, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “send later” scheduling for outbound emails in both the Tickets (sendmail plugin) and Talk/CfP (orga mail) flows by persisting a scheduled_at timestamp and adding background processing to dispatch due emails.

Changes:

  • Add scheduled_at to both Tickets EmailQueue and Talk/CfP QueuedMail, plus corresponding migrations.
  • Add scheduling fields to compose/edit UIs and show scheduled/immediate status in outbox lists.
  • Add Celery/signal-based processing for scheduled sends, including an eta-trigger path and a periodic poller.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
app/eventyay/plugins/sendmail/views.py Persists scheduled_at when composing and schedules Celery send tasks.
app/eventyay/plugins/sendmail/templates/pretixplugins/sendmail/send_team_form.html Adds “Scheduling” fieldset to team email compose UI.
app/eventyay/plugins/sendmail/templates/pretixplugins/sendmail/send_form.html Adds “Scheduling” fieldset to attendee email compose UI.
app/eventyay/plugins/sendmail/templates/pretixplugins/sendmail/outbox_list.html Adds Status column showing scheduled vs immediate.
app/eventyay/plugins/sendmail/templates/pretixplugins/sendmail/outbox_form.html Allows editing scheduled_at in outbox editor.
app/eventyay/plugins/sendmail/tasks.py Adds scheduled-time pre-check/reschedule behavior in send task.
app/eventyay/plugins/sendmail/models.py Adds scheduled_at field + guard to prevent early sends.
app/eventyay/plugins/sendmail/migrations/0002_emailqueue_scheduled_at.py DB migration for Tickets EmailQueue.scheduled_at.
app/eventyay/plugins/sendmail/forms.py Adds scheduled_at fields + validation and allows editing on outbox items.
app/eventyay/orga/views/mails.py Handles scheduled compose flow and schedules periodic signal dispatch at ETA.
app/eventyay/orga/templates/orga/mails/outbox_list.html Adds Status column showing scheduled vs immediate.
app/eventyay/orga/templates/orga/mails/outbox_form.html Adjusts outbox form rendering (enables scheduled field rendering).
app/eventyay/orga/templates/orga/mails/_mail_editor.html Adds scheduled_at field rendering in shared editor partial.
app/eventyay/orga/forms/mails.py Adds scheduled_at fields/validation and a talk-specific datetime widget.
app/eventyay/event/services.py Hardens periodic event mail checks (null-safety + date_to logic).
app/eventyay/control/forms/init.py Changes timezone awareness handling for SplitDateTimeField.
app/eventyay/common/tasks.py Adds Celery task to emit periodic_task signal.
app/eventyay/common/signals.py Adds scheduled email poller receiver processing due QueuedMail/EmailQueue.
app/eventyay/common/apps.py Ensures common.tasks is imported/registered on app ready.
app/eventyay/base/models/mail.py Adds scheduled_at + guard in Talk/CfP QueuedMail.send().
app/eventyay/base/migrations/0025_queuedmail_scheduled_at.py DB migration for Talk/CfP QueuedMail.scheduled_at.

Comment thread app/eventyay/control/forms/__init__.py Outdated
Comment thread app/eventyay/common/signals.py
Comment thread app/eventyay/plugins/sendmail/tasks.py Outdated
Comment thread app/eventyay/common/signals.py Outdated
Comment thread app/eventyay/orga/forms/mails.py
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52bbcc414d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/eventyay/base/models/mail.py
@MukundC25 MukundC25 force-pushed the feat/email-scheduling-v3 branch from 52bbcc4 to 1fe6bb4 Compare May 7, 2026 14:54
Copilot AI review requested due to automatic review settings May 8, 2026 21:05
@MukundC25 MukundC25 force-pushed the feat/email-scheduling-v3 branch from 002488b to 8d6d19f Compare May 8, 2026 21:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 7 comments.

Comment thread app/eventyay/control/forms/__init__.py Outdated
Comment thread app/eventyay/common/signals.py
Comment thread app/eventyay/common/tasks.py Outdated
Comment thread app/eventyay/plugins/sendmail/models.py Outdated
Comment on lines +174 to +176
if scheduled_at:
send_queued_mail.apply_async(args=[self.request.event.pk, qm.pk], eta=scheduled_at)
self.request.event.log_action(
Comment on lines +441 to +443
for mail in result:
send_scheduled_queuedmail.apply_async(args=[mail.pk], eta=scheduled_at)
messages.success(
Comment thread app/eventyay/common/tasks.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Comment on lines +31 to +33
with transaction.atomic():
qm = (
EmailQueue.objects
Comment on lines +441 to +443
for mail in result:
send_scheduled_queuedmail.apply_async(args=[mail.pk], eta=scheduled_at)
messages.success(
Comment on lines +445 to +446
if self.scheduled_at and self.scheduled_at > now():
raise SendMailException(_('This mail is scheduled for the future and cannot be sent yet.'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Feature: Add Email Scheduling Option (Future Send Time) in Tickets and Talk Components

2 participants