mirror of
https://github.com/pretix/pretix.git
synced 2026-09-14 16:24:43 +00:00
Refs #1289 -- Download reminders for subevents and download reminder performance
This commit is contained in:
@@ -9,8 +9,8 @@ from celery.exceptions import MaxRetriesExceededError
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Exists, F, Max, OuterRef, Q, Sum
|
from django.db.models import Exists, F, Max, Min, OuterRef, Q, Sum
|
||||||
from django.db.models.functions import Greatest
|
from django.db.models.functions import Coalesce, Greatest
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.timezone import make_aware, now
|
from django.utils.timezone import make_aware, now
|
||||||
@@ -918,54 +918,76 @@ def send_expiry_warnings(sender, **kwargs):
|
|||||||
@scopes_disabled()
|
@scopes_disabled()
|
||||||
def send_download_reminders(sender, **kwargs):
|
def send_download_reminders(sender, **kwargs):
|
||||||
today = now().replace(hour=0, minute=0, second=0, microsecond=0)
|
today = now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
qs = Order.objects.annotate(
|
||||||
|
first_date=Coalesce(
|
||||||
|
Min('all_positions__subevent__date_from'),
|
||||||
|
F('event__date_from')
|
||||||
|
)
|
||||||
|
).filter(
|
||||||
|
status=Order.STATUS_PAID,
|
||||||
|
download_reminder_sent=False,
|
||||||
|
datetime__lte=now() - timedelta(hours=2),
|
||||||
|
first_date__gte=today,
|
||||||
|
).only('pk', 'event_id').order_by('event_id')
|
||||||
|
event_id = None
|
||||||
|
days = None
|
||||||
|
event = None
|
||||||
|
|
||||||
for e in Event.objects.filter(date_from__gte=today):
|
for o in qs:
|
||||||
|
if o.event_id != event_id:
|
||||||
|
days = o.event.settings.get('mail_days_download_reminder', as_type=int)
|
||||||
|
event = o.event
|
||||||
|
event_id = o.event_id
|
||||||
|
|
||||||
days = e.settings.get('mail_days_download_reminder', as_type=int)
|
|
||||||
if days is None:
|
if days is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
reminder_date = (e.date_from - timedelta(days=days)).replace(hour=0, minute=0, second=0, microsecond=0)
|
reminder_date = (o.first_date - timedelta(days=days)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
if now() < reminder_date:
|
if now() < reminder_date:
|
||||||
continue
|
continue
|
||||||
for o in e.orders.filter(status=Order.STATUS_PAID, download_reminder_sent=False, datetime__lte=now() - timedelta(hours=2)).only('pk'):
|
|
||||||
with transaction.atomic():
|
|
||||||
o = Order.objects.select_related('event').select_for_update().get(pk=o.pk)
|
|
||||||
if o.download_reminder_sent:
|
|
||||||
# Race condition
|
|
||||||
continue
|
|
||||||
if not all([r for rr, r in allow_ticket_download.send(e, order=o)]):
|
|
||||||
continue
|
|
||||||
|
|
||||||
with language(o.locale):
|
with transaction.atomic():
|
||||||
o.download_reminder_sent = True
|
o = Order.objects.select_for_update().get(pk=o.pk)
|
||||||
o.save(update_fields=['download_reminder_sent'])
|
if o.download_reminder_sent:
|
||||||
email_template = e.settings.mail_text_download_reminder
|
# Race condition
|
||||||
email_context = get_email_context(event=e, order=o)
|
continue
|
||||||
email_subject = _('Your ticket is ready for download: %(code)s') % {'code': o.code}
|
if not all([r for rr, r in allow_ticket_download.send(event, order=o)]):
|
||||||
try:
|
continue
|
||||||
o.send_mail(
|
|
||||||
email_subject, email_template, email_context,
|
|
||||||
'pretix.event.order.email.download_reminder_sent',
|
|
||||||
attach_tickets=True
|
|
||||||
)
|
|
||||||
except SendMailException:
|
|
||||||
logger.exception('Reminder email could not be sent')
|
|
||||||
|
|
||||||
if e.settings.mail_send_download_reminder_attendee:
|
with language(o.locale):
|
||||||
for p in o.positions.all():
|
o.download_reminder_sent = True
|
||||||
if p.addon_to_id is None and p.attendee_email and p.attendee_email != o.email:
|
o.save(update_fields=['download_reminder_sent'])
|
||||||
email_template = e.settings.mail_text_download_reminder_attendee
|
email_template = event.settings.mail_text_download_reminder
|
||||||
email_context = get_email_context(event=e, order=o, position=p)
|
email_context = get_email_context(event=event, order=o)
|
||||||
try:
|
email_subject = _('Your ticket is ready for download: %(code)s') % {'code': o.code}
|
||||||
o.send_mail(
|
try:
|
||||||
email_subject, email_template, email_context,
|
o.send_mail(
|
||||||
'pretix.event.order.email.download_reminder_sent',
|
email_subject, email_template, email_context,
|
||||||
attach_tickets=True, position=p
|
'pretix.event.order.email.download_reminder_sent',
|
||||||
)
|
attach_tickets=True
|
||||||
except SendMailException:
|
)
|
||||||
logger.exception('Reminder email could not be sent to attendee')
|
except SendMailException:
|
||||||
|
logger.exception('Reminder email could not be sent')
|
||||||
|
|
||||||
|
if event.settings.mail_send_download_reminder_attendee:
|
||||||
|
for p in o.positions.all():
|
||||||
|
if p.subevent_id:
|
||||||
|
reminder_date = (p.subevent.date_from - timedelta(days=days)).replace(
|
||||||
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
if now() < reminder_date:
|
||||||
|
continue
|
||||||
|
if p.addon_to_id is None and p.attendee_email and p.attendee_email != o.email:
|
||||||
|
email_template = event.settings.mail_text_download_reminder_attendee
|
||||||
|
email_context = get_email_context(event=event, order=o, position=p)
|
||||||
|
try:
|
||||||
|
o.send_mail(
|
||||||
|
email_subject, email_template, email_context,
|
||||||
|
'pretix.event.order.email.download_reminder_sent',
|
||||||
|
attach_tickets=True, position=p
|
||||||
|
)
|
||||||
|
except SendMailException:
|
||||||
|
logger.exception('Reminder email could not be sent to attendee')
|
||||||
|
|
||||||
|
|
||||||
def notify_user_changed_order(order, user=None, auth=None):
|
def notify_user_changed_order(order, user=None, auth=None):
|
||||||
|
|||||||
@@ -473,6 +473,33 @@ class DownloadReminderTests(TestCase):
|
|||||||
assert '/ticket/' in djmail.outbox[1].body
|
assert '/ticket/' in djmail.outbox[1].body
|
||||||
assert '/order/' not in djmail.outbox[1].body
|
assert '/order/' not in djmail.outbox[1].body
|
||||||
|
|
||||||
|
@classscope(attr='o')
|
||||||
|
def test_send_to_attendees_subevent_past(self):
|
||||||
|
se1 = self.event.subevents.create(name="Foo", date_from=now() - timedelta(days=2))
|
||||||
|
self.op1.subevent = se1
|
||||||
|
self.op1.attendee_email = 'attendee@dummy.test'
|
||||||
|
self.op1.save()
|
||||||
|
send_download_reminders(sender=self.event)
|
||||||
|
assert len(djmail.outbox) == 0
|
||||||
|
|
||||||
|
@classscope(attr='o')
|
||||||
|
def test_send_to_attendees_subevent_future(self):
|
||||||
|
self.event.settings.mail_send_download_reminder_attendee = True
|
||||||
|
self.event.settings.mail_days_download_reminder = 2
|
||||||
|
se1 = self.event.subevents.create(name="Foo", date_from=now() + timedelta(days=2))
|
||||||
|
se2 = self.event.subevents.create(name="Foo", date_from=now() + timedelta(days=8))
|
||||||
|
self.op1.subevent = se1
|
||||||
|
self.op1.attendee_email = 'attendee@dummy.test'
|
||||||
|
self.op1.save()
|
||||||
|
self.op2 = OrderPosition.objects.create(
|
||||||
|
order=self.order, item=self.ticket, variation=None, subevent=se2, attendee_email="attendee2@dummy.test",
|
||||||
|
price=Decimal("23.00"), attendee_name_parts={"full_name": "Peter"}, positionid=1
|
||||||
|
)
|
||||||
|
send_download_reminders(sender=self.event)
|
||||||
|
assert len(djmail.outbox) == 2
|
||||||
|
assert djmail.outbox[0].to == ['dummy@dummy.test']
|
||||||
|
assert djmail.outbox[1].to == ['attendee@dummy.test']
|
||||||
|
|
||||||
@classscope(attr='o')
|
@classscope(attr='o')
|
||||||
def test_send_not_to_attendees_with_same_address(self):
|
def test_send_not_to_attendees_with_same_address(self):
|
||||||
self.event.settings.mail_send_download_reminder_attendee = True
|
self.event.settings.mail_send_download_reminder_attendee = True
|
||||||
|
|||||||
Reference in New Issue
Block a user