init
This commit is contained in:
278
backend/communications/tasks.py
Normal file
278
backend/communications/tasks.py
Normal file
@@ -0,0 +1,278 @@
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
import logging
|
||||
from celery import shared_task
|
||||
from datetime import timedelta
|
||||
|
||||
from events.models import Event, Registration
|
||||
from communications.models import Announcement, NewsletterSubscription
|
||||
from communications.utils import send_announcement_email, send_event_reminder, get_announcement_recipients
|
||||
from communications.push_notifications import push_service
|
||||
|
||||
User = get_user_model()
|
||||
logger = logging.getLogger(__name__)
|
||||
SYSTEM_USER_ID = 1
|
||||
|
||||
|
||||
@shared_task(bind=True, max_retries=3)
|
||||
def send_announcement_notifications(self, announcement_id):
|
||||
"""Send email and push notifications for an announcement"""
|
||||
try:
|
||||
announcement = Announcement.objects.get(id=announcement_id)
|
||||
|
||||
# Send email notifications
|
||||
if announcement.send_email and not announcement.email_sent:
|
||||
recipients = get_announcement_recipients(announcement)
|
||||
if recipients:
|
||||
success = send_announcement_email(announcement, recipients)
|
||||
if success:
|
||||
announcement.email_sent = True
|
||||
announcement.save()
|
||||
logger.info(f"Email notifications sent for announcement {announcement.id}")
|
||||
|
||||
# Send push notifications
|
||||
if announcement.send_push and not announcement.push_sent:
|
||||
sent_count = push_service.send_announcement_notification(announcement)
|
||||
if sent_count > 0:
|
||||
announcement.push_sent = True
|
||||
announcement.save()
|
||||
logger.info(f"Push notifications sent to {sent_count} devices for announcement {announcement.id}")
|
||||
|
||||
return f"Notifications sent for announcement: {announcement.title}"
|
||||
|
||||
except Announcement.DoesNotExist:
|
||||
logger.error(f"Announcement {announcement_id} not found")
|
||||
return f"Announcement {announcement_id} not found"
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to send announcement notifications: {exc}")
|
||||
raise self.retry(exc=exc, countdown=60)
|
||||
|
||||
|
||||
@shared_task(bind=True, max_retries=3)
|
||||
def send_newsletter_confirmation_task(self, subscription_id):
|
||||
"""Send newsletter confirmation email"""
|
||||
try:
|
||||
from .utils import send_newsletter_confirmation
|
||||
|
||||
subscription = NewsletterSubscription.objects.get(id=subscription_id)
|
||||
success = send_newsletter_confirmation(subscription)
|
||||
|
||||
if success:
|
||||
logger.info(f"Newsletter confirmation sent to {subscription.email}")
|
||||
return f"Newsletter confirmation sent to {subscription.email}"
|
||||
else:
|
||||
raise Exception("Failed to send newsletter confirmation")
|
||||
|
||||
except NewsletterSubscription.DoesNotExist:
|
||||
logger.error(f"Newsletter subscription {subscription_id} not found")
|
||||
return f"Newsletter subscription {subscription_id} not found"
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to send newsletter confirmation: {exc}")
|
||||
raise self.retry(exc=exc, countdown=60)
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_event_reminders():
|
||||
"""Send reminders for events starting about 24 hours from now within a 30-minute window."""
|
||||
try:
|
||||
reminder_target = timezone.now() + timedelta(hours=24)
|
||||
window = timedelta(minutes=30)
|
||||
start_range = reminder_target - window
|
||||
end_range = reminder_target + window
|
||||
|
||||
events = Event.objects.filter(
|
||||
start_time__range=(start_range, end_range),
|
||||
status='published',
|
||||
is_deleted=False
|
||||
)
|
||||
|
||||
total_sent = 0
|
||||
|
||||
for event in events:
|
||||
# Get confirmed registrations
|
||||
registrations = Registration.objects.filter(
|
||||
event=event,
|
||||
status='confirmed',
|
||||
is_deleted=False
|
||||
).select_related('user')
|
||||
|
||||
for registration in registrations:
|
||||
try:
|
||||
# Send email reminder
|
||||
send_event_reminder(event, registration.user)
|
||||
|
||||
# Send push notification reminder
|
||||
push_service.send_event_reminder_notification(event, registration.user)
|
||||
|
||||
total_sent += 1
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send reminder to {registration.user.email}: {str(e)}")
|
||||
|
||||
logger.info(f"Event reminders sent to {total_sent} users")
|
||||
return f"Event reminders sent to {total_sent} users"
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to send event reminders: {exc}")
|
||||
raise exc
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_weekly_newsletter():
|
||||
"""Send the weekly newsletter as the system user with recent announcements and upcoming events."""
|
||||
try:
|
||||
# Get active newsletter subscribers
|
||||
subscribers = NewsletterSubscription.objects.filter(
|
||||
is_active=True,
|
||||
confirmed_at__isnull=False,
|
||||
is_deleted=False
|
||||
)
|
||||
|
||||
if not subscribers.exists():
|
||||
logger.info("No active newsletter subscribers found")
|
||||
return "No active newsletter subscribers found"
|
||||
|
||||
# Get recent announcements (last 7 days)
|
||||
week_ago = timezone.now() - timedelta(days=7)
|
||||
recent_announcements = Announcement.objects.filter(
|
||||
is_published=True,
|
||||
publish_date__gte=week_ago,
|
||||
announcement_type__in=['general', 'academic', 'newsletter'],
|
||||
is_deleted=False
|
||||
).order_by('-publish_date')[:5]
|
||||
|
||||
# Get upcoming events (next 14 days)
|
||||
two_weeks_ahead = timezone.now() + timedelta(days=14)
|
||||
upcoming_events = Event.objects.filter(
|
||||
start_time__range=(timezone.now(), two_weeks_ahead),
|
||||
status='published',
|
||||
is_deleted=False
|
||||
).order_by('start_time')[:5]
|
||||
|
||||
newsletter_content = f"""
|
||||
# Weekly Newsletter - {timezone.now().strftime('%B %d, %Y')}
|
||||
|
||||
## Recent Announcements
|
||||
"""
|
||||
|
||||
for announcement in recent_announcements:
|
||||
newsletter_content += f"- **{announcement.title}** ({announcement.publish_date.strftime('%B %d')})\n"
|
||||
|
||||
newsletter_content += "\n## Upcoming Events\n"
|
||||
|
||||
for event in upcoming_events:
|
||||
newsletter_content += f"- **{event.title}** - {event.start_time.strftime('%B %d, %Y at %I:%M %p')}\n"
|
||||
|
||||
if not recent_announcements.exists() and not upcoming_events.exists():
|
||||
newsletter_content += "\nNo recent announcements or upcoming events this week."
|
||||
|
||||
newsletter = Announcement.objects.create(
|
||||
title=f"Weekly Newsletter - {timezone.now().strftime('%B %d, %Y')}",
|
||||
content=newsletter_content,
|
||||
announcement_type='newsletter',
|
||||
priority='normal',
|
||||
author_id=SYSTEM_USER_ID,
|
||||
is_published=True,
|
||||
publish_date=timezone.now(),
|
||||
send_email=True,
|
||||
target_audience='subscribers'
|
||||
)
|
||||
|
||||
# Send to subscribers
|
||||
subscriber_emails = list(subscribers.values_list('email', flat=True))
|
||||
success = send_announcement_email(newsletter, subscriber_emails)
|
||||
|
||||
if success:
|
||||
newsletter.email_sent = True
|
||||
newsletter.save()
|
||||
logger.info(f"Weekly newsletter sent to {len(subscriber_emails)} subscribers")
|
||||
return f"Weekly newsletter sent to {len(subscriber_emails)} subscribers"
|
||||
else:
|
||||
raise Exception("Failed to send weekly newsletter")
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to send weekly newsletter: {exc}")
|
||||
raise exc
|
||||
|
||||
|
||||
@shared_task
|
||||
def cleanup_expired_tokens():
|
||||
"""Clean up expired newsletter confirmation tokens"""
|
||||
try:
|
||||
# Remove unconfirmed subscriptions older than 7 days
|
||||
week_ago = timezone.now() - timedelta(days=7)
|
||||
expired_subscriptions = NewsletterSubscription.objects.filter(
|
||||
confirmed_at__isnull=True,
|
||||
created_at__lt=week_ago
|
||||
)
|
||||
|
||||
count = expired_subscriptions.count()
|
||||
expired_subscriptions.delete()
|
||||
|
||||
logger.info(f"Cleaned up {count} expired newsletter subscriptions")
|
||||
return f"Cleaned up {count} expired newsletter subscriptions"
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to cleanup expired tokens: {exc}")
|
||||
raise exc
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_bulk_announcement(announcement_id, recipient_emails):
|
||||
"""Send announcement to a specific list of recipients"""
|
||||
try:
|
||||
announcement = Announcement.objects.get(id=announcement_id)
|
||||
|
||||
# Split recipients into batches to avoid overwhelming the email server
|
||||
batch_size = 50
|
||||
total_sent = 0
|
||||
|
||||
for i in range(0, len(recipient_emails), batch_size):
|
||||
batch = recipient_emails[i:i + batch_size]
|
||||
success = send_announcement_email(announcement, batch)
|
||||
|
||||
if success:
|
||||
total_sent += len(batch)
|
||||
logger.info(f"Sent announcement to batch of {len(batch)} recipients")
|
||||
|
||||
# Small delay between batches
|
||||
import time
|
||||
time.sleep(1)
|
||||
|
||||
logger.info(f"Bulk announcement sent to {total_sent} recipients")
|
||||
return f"Bulk announcement sent to {total_sent} recipients"
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to send bulk announcement: {exc}")
|
||||
raise exc
|
||||
|
||||
|
||||
@shared_task
|
||||
def process_scheduled_announcements():
|
||||
"""Process announcements scheduled for publication"""
|
||||
try:
|
||||
now = timezone.now()
|
||||
|
||||
# Get announcements scheduled for publication
|
||||
scheduled_announcements = Announcement.objects.filter(
|
||||
is_published=True,
|
||||
publish_date__lte=now,
|
||||
email_sent=False,
|
||||
send_email=True,
|
||||
is_deleted=False
|
||||
)
|
||||
|
||||
processed_count = 0
|
||||
|
||||
for announcement in scheduled_announcements:
|
||||
# Send notifications
|
||||
send_announcement_notifications.delay(announcement.id)
|
||||
processed_count += 1
|
||||
|
||||
logger.info(f"Processed {processed_count} scheduled announcements")
|
||||
return f"Processed {processed_count} scheduled announcements"
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to process scheduled announcements: {exc}")
|
||||
raise exc
|
||||
Reference in New Issue
Block a user