Notify members when a new event is planned
New events.tasks.notify_new_event, scheduled from management.views.EventCreateView.form_valid -- the deliberate "a staff member planned one new event" action, not every Event row that happens to get created. A recurring series' rolling-horizon extension (extend_event_series) and bulk fixture imports are NOT wired to this on purpose: either would flood everyone with one push per occurrence instead of the single, deliberate action this is meant to catch. Notifies whoever is still NO_RESPONSE right after creation -- exactly "everyone who needs to respond", using the existing notify_members() -> Notification -> mobile.signals push chain already in place, so this is in-app (visible in the M7 inbox, mark read/mark-all-read) and a push notification both, with no new plumbing needed there. Generalized mobile.views.NotificationsView's News-only "tap to open the source" handling (previously isinstance(source, News)) into _notification_source_link, covering Event sources too -- tapping an "new event" notification now marks it read and opens the event's answer screen, the same way a news notification already opened the article. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
"""Celery task behind the `extend-event-series` beat schedule entry (see
|
||||
rosterchief/settings.CELERY_BEAT_SCHEDULE and features/jobs.py).
|
||||
|
||||
Mirrors `manage.py extend_event_series` exactly -- that command still exists, unchanged, for
|
||||
manual use from a shell (see events/management/commands/extend_event_series.py).
|
||||
"""Celery tasks for events -- the `extend-event-series` beat schedule entry
|
||||
(see rosterchief/settings.CELERY_BEAT_SCHEDULE and features/jobs.py), and
|
||||
notifying members when a new event needs their RSVP.
|
||||
"""
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from events.models import EventSeries
|
||||
from events.models import Attendance, Event, EventSeries
|
||||
from events.services import generate_occurrences, horizon
|
||||
from features.models import Maintenance
|
||||
from members.models import Member
|
||||
from notifications.services import notify_members
|
||||
|
||||
|
||||
@shared_task(name="events.tasks.extend_event_series")
|
||||
@@ -26,3 +28,32 @@ def extend_event_series():
|
||||
total += len(generate_occurrences(series, until))
|
||||
|
||||
return f"Generated {total} occurrence(s) across {EventSeries.objects.count()} series."
|
||||
|
||||
|
||||
@shared_task(name="events.tasks.notify_new_event")
|
||||
def notify_new_event(event_id):
|
||||
"""Scheduled from management.views.EventCreateView.form_valid -- a staff
|
||||
member deliberately planning one new event, not every Event row that
|
||||
happens to get created (a recurring series' rolling-horizon extension
|
||||
via extend_event_series above, or a bulk fixture import, would otherwise
|
||||
flood everyone with one push per occurrence; those are intentionally not
|
||||
wired to this).
|
||||
|
||||
Notifies only whoever is still NO_RESPONSE -- right after creation every
|
||||
invited member starts there (events.services.attendance.sync_event_attendances
|
||||
just ran via the Event post_save signal), so this is exactly "everyone
|
||||
who needs to respond", not the full invited list.
|
||||
"""
|
||||
event = Event.objects.filter(pk=event_id, cancelled=False).select_related("club").first()
|
||||
if event is None:
|
||||
return "Skipped: event no longer exists or was cancelled."
|
||||
|
||||
member_ids = Attendance.objects.filter(event=event, status=Attendance.AttendanceStatus.NO_RESPONSE).values_list("member_id", flat=True)
|
||||
members = Member.objects.filter(id__in=member_ids)
|
||||
if not members:
|
||||
return "Skipped: no one to notify."
|
||||
|
||||
when = timezone.localtime(event.start).strftime("%a %d %b, %H:%M")
|
||||
body = _("New %(kind)s: %(when)s. Let us know if you can make it.") % {"kind": event.get_kind_display(), "when": when}
|
||||
notifications = notify_members(members, club=event.club, title=event.title, body=body, source=event)
|
||||
return f"Notified {len(notifications)} member(s)."
|
||||
|
||||
Reference in New Issue
Block a user