|
| 1 | +from django_email_learning.services.command_models.abstract_command import ( |
| 2 | + AbstractCommand, |
| 3 | +) |
| 4 | +from django_email_learning.models import ContentDelivery, DeliverySchedule |
| 5 | +from django_email_learning.services.email_sender_service import EmailSenderService |
| 6 | +from django_email_learning.services.metrics_service import MetricsService |
| 7 | +from django.core.mail import EmailMultiAlternatives |
| 8 | +from django.template.loader import render_to_string |
| 9 | +from typing import Literal |
| 10 | +from pydantic import ConfigDict |
| 11 | +from django.utils.translation import gettext as _ |
| 12 | +from django.utils import timezone |
| 13 | +from django_email_learning.services.utils import mask_email |
| 14 | + |
| 15 | + |
| 16 | +class AssignmentNotFoundError(Exception): |
| 17 | + pass |
| 18 | + |
| 19 | + |
| 20 | +class SendAssignmentReminderCommand(AbstractCommand): |
| 21 | + command_name: Literal["send_assignment_reminder"] = "send_assignment_reminder" |
| 22 | + delivery_schedule: DeliverySchedule |
| 23 | + |
| 24 | + model_config = ConfigDict(arbitrary_types_allowed=True) |
| 25 | + |
| 26 | + def execute(self) -> None: |
| 27 | + metric_service = MetricsService() |
| 28 | + content = self.delivery_schedule.delivery.course_content |
| 29 | + if not content.assignment: |
| 30 | + raise AssignmentNotFoundError( |
| 31 | + f"CourseContent with ID {content.id} has no associated assignment" |
| 32 | + ) |
| 33 | + email = self.delivery_schedule.delivery.enrollment.learner.email |
| 34 | + self.logger.info( |
| 35 | + f"Sending reminder for assignment with ID {content.assignment.id} to email {mask_email(email)}" |
| 36 | + ) |
| 37 | + |
| 38 | + assignment = content.assignment |
| 39 | + |
| 40 | + subject = _("Reminder: Assignment '{assignment_title}' is due soon").format( |
| 41 | + assignment_title=assignment.title |
| 42 | + ) |
| 43 | + context = { |
| 44 | + "assignment": assignment, |
| 45 | + "link": self.delivery_schedule.link, |
| 46 | + "unsubscribe_link": content.course.generate_unsubscribe_link(email), |
| 47 | + "deadline_time": self.delivery_schedule.delivery.valid_until, |
| 48 | + } |
| 49 | + payload = render_to_string("emails/assignment_reminder.txt", context) |
| 50 | + |
| 51 | + email_service = EmailSenderService() |
| 52 | + email_message = EmailMultiAlternatives( |
| 53 | + subject=subject, |
| 54 | + body=payload, |
| 55 | + from_email=email_service.from_email, |
| 56 | + to=[email], |
| 57 | + ) |
| 58 | + email_message.attach_alternative( |
| 59 | + render_to_string("emails/assignment_reminder.html", context), "text/html" |
| 60 | + ) |
| 61 | + |
| 62 | + try: |
| 63 | + email_service.send(email_message) |
| 64 | + self.delivery_schedule.delivery.remind_at = timezone.now() |
| 65 | + self.delivery_schedule.delivery.reminder_state = ( |
| 66 | + ContentDelivery.ReminderStatus.SENT |
| 67 | + ) |
| 68 | + self.delivery_schedule.delivery.save() |
| 69 | + metric_service.assignment_reminder_sent( |
| 70 | + course_slug=content.course.slug, |
| 71 | + organization_id=content.course.organization.id, |
| 72 | + assignment_id=assignment.id, |
| 73 | + ) |
| 74 | + except Exception as e: |
| 75 | + self.logger.error( |
| 76 | + f"Failed to send assignment reminder for assignment with ID {assignment.id} to email {mask_email(email)}: {str(e)}" |
| 77 | + ) |
| 78 | + raise e |
0 commit comments