Files
training-software/notifications/tasks.py
Paperclip CTO 01661bf5a8
Some checks failed
CI / lint (push) Successful in 6s
CI / test (push) Failing after 7s
CI / build-container (push) Has been skipped
fix: restore CI by fixing lint, test collection, and notification syntax
2026-05-18 14:53:29 +02:00

83 lines
2.8 KiB
Python

from celery import shared_task
from django.utils.timezone import now
@shared_task(name="notifications.deliver_notification", bind=True,
autoretry_for=(Exception,), retry_kwargs={"max_retries": 3, "countdown": 60})
def deliver_notification_task(self, delivery_id: str):
"""
Dispatch a single NotificationDelivery via its channel.
Marks status SENT on success or FAILED on final exhaustion.
"""
from .models import DeliveryChannel, DeliveryStatus, NotificationDelivery
try:
delivery = NotificationDelivery.objects.select_related(
"notification__recipient"
).get(pk=delivery_id)
except NotificationDelivery.DoesNotExist:
return {"error": "delivery not found", "id": delivery_id}
try:
if delivery.channel == DeliveryChannel.EMAIL:
_send_email(delivery)
# In-app channel: the Notification row IS the in-app message; nothing more to send.
delivery.status = DeliveryStatus.SENT
delivery.sent_at = now()
delivery.save(update_fields=["status", "sent_at"])
return {"delivery_id": delivery_id, "status": "sent"}
except Exception as exc:
delivery.error_detail = str(exc)
delivery.status = DeliveryStatus.FAILED
delivery.save(update_fields=["status", "error_detail"])
raise # triggers autoretry
def _send_email(delivery):
from django.core.mail import send_mail
from django.conf import settings
n = delivery.notification
recipient_email = n.recipient.email
if not recipient_email:
return
send_mail(
subject=n.title,
message=n.body,
from_email=getattr(settings, "DEFAULT_FROM_EMAIL", "no-reply@example.com"),
recipient_list=[recipient_email],
fail_silently=False,
)
@shared_task(name="notifications.send_course_due_reminders")
def send_course_due_reminders_task():
"""
Periodic task: notify learners of enrollments due within the configured
reminder window. Hook this into Celery Beat.
"""
from tracking.models import Enrollment
from .services import notify
# Enrollments that have a due_at field (future optional extension) or use
# completed_at is None as a proxy for "not yet done". For now we fire
# reminders only for enrollments that have a due_at set on the course.
# Since due_at is not yet on Course, this is a safe no-op stub.
# Replace the queryset below once Course.due_at is added.
qs = Enrollment.objects.none()
count = 0
for enrollment in qs:
notify(
recipient=enrollment.user,
event_type="course_due",
title=f"Course due soon: {enrollment.course.title}",
body="",
object_type="enrollment",
object_id=str(enrollment.pk),
)
count += 1
return {"reminders_sent": count}