Files
RosterChief/features/jobs.py
Bernard Siebens 17b1c0220a Add a periodic deadline-reminder job for events, including recurring series
New events.tasks.send_deadline_reminders (daily beat schedule, registered
in features.jobs.JOB_REGISTRY for the control panel's Jobs tab), the gap
notify_new_event's own docstring flagged: a recurring series' occurrences
never go through that on-creation path (they're bulk-generated by
extend_event_series, and notifying per-occurrence there would flood
everyone), so they never got any "you need to answer this" nudge at all.

This sweeps every upcoming event once, one week before whichever cutoff
matters -- the event's own answer deadline, or its start when none is
set -- and notifies whoever's still NO_RESPONSE, using the same
notify_members() -> Notification -> push chain notify_new_event already
uses. New Event.deadline_reminder_sent_at makes it idempotent: each
event's window opens once, is marked processed regardless of whether
anyone needed notifying, and a missed run still catches anything whose
window hasn't fully closed by the time the job next runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 17:33:33 +02:00

44 lines
2.0 KiB
Python

"""Registry of the platform jobs Celery Beat runs on a schedule (see
rosterchief/settings.CELERY_BEAT_SCHEDULE).
Keyed on each task's dotted Celery name -- the same string a JobRun row carries in `name`
-- so features/signals.py can tell a tracked platform job apart from any other Celery task
that might get added later without a job to show for it, and so the control panel's Jobs
tab can label a JobRun without importing the task function itself.
"""
from django.utils.translation import gettext_lazy as _
JOB_REGISTRY = {
"events.tasks.extend_event_series": {
"label": _("Extend event series"),
"description": _("Materialises recurring event occurrences up to the rolling horizon, so the calendar never runs dry."),
"schedule": _("Daily at 03:00"),
},
"events.tasks.send_deadline_reminders": {
"label": _("Send deadline reminders"),
"description": _("Nudges whoever still hasn't answered an event, one week before its answer deadline (or its start, when no deadline is set)."),
"schedule": _("Daily at 07:00"),
},
"billing.tasks.renew_subscriptions": {
"label": _("Renew subscriptions"),
"description": _("Opens the next billing period for clubs whose current one is running out."),
"schedule": _("Daily at 04:00"),
},
"billing.tasks.send_billing_reminders": {
"label": _("Send billing reminders"),
"description": _("Emails club admins about outstanding platform fees, once per escalation level."),
"schedule": _("Daily at 05:00"),
},
"billing.tasks.archive_overdue_clubs": {
"label": _("Archive overdue clubs"),
"description": _("Archives clubs unpaid past their grace period."),
"schedule": _("Daily at 06:00"),
},
"club.tasks.generate_seasons": {
"label": _("Generate seasons"),
"description": _("Generates the next two years of season rows for every active club, so signups and rosters never hit a missing season."),
"schedule": _("Monthly, 1st at 05:00"),
},
}