import logging import re from datetime import timedelta from django.conf import settings from django.core.mail import EmailMultiAlternatives from django.core.management.base import BaseCommand from django.utils import timezone from django.utils.translation import gettext as _ from common.models import UserConfig from notifications.models import NotificationRecipient logger = logging.getLogger(__name__) class Command(BaseCommand): help = "Envoie un e-mail de rappel aux utilisateurs ayant des notifications non lues depuis un certain délai." def add_arguments(self, parser): parser.add_argument( '--user', type=str, help="Username spécifique à cibler pour le test.", ) parser.add_argument( '--dry-run', action='store_true', help="Simule l'envoi sans envoyer d'e-mails réels ni mettre à jour la base de données.", ) def handle(self, *args, **options): target_username = options.get('user') dry_run = options.get('dry-run', False) now = timezone.now() configs = UserConfig.objects.filter( notification_reminder_enabled=True, user__is_active=True, ).select_related('user') if target_username: configs = configs.filter(user__username=target_username) sent_count = 0 user_count = 0 for config in configs: user = config.user delay_hours = config.notification_reminder_delay_hours or 24 cutoff_time = now - timedelta(hours=delay_hours) # Recueillir les notifications non lues > délai recipients = ( NotificationRecipient.objects.filter( user=user, read_at__isnull=True, archived_at__isnull=True, reminder_sent_at__isnull=True, notification__created_at__lte=cutoff_time, ) .select_related('notification', 'notification__sender') .order_by('-notification__created_at') ) # Filtrer les notifications expirées valid_recipients = [r for r in recipients if not r.notification.is_expired] if not valid_recipients: continue # Déterminer les adresses e-mail de destination email_override = getattr(settings, 'EMAIL_OVERRIDE', None) if email_override: recipient_emails = [email_override] else: emails = set() raw_emails = config.notification_reminder_emails or user.email or getattr(config, 'generic_email', '') if raw_emails: for email in re.split(r'[;,]', str(raw_emails)): e = email.strip() if e: emails.add(e) recipient_emails = list(emails) if not recipient_emails: logger.warning("Aucune adresse e-mail trouvée pour l'utilisateur %s.", user.username) continue # Construire l'e-mail count = len(valid_recipients) subject = f"[StreetUp] Rappel : {count} notification(s) non lue(s)" # Host URL pour les liens domain = getattr(settings, 'SITE_URL', 'http://localhost:8003') lines_text = [ f"Bonjour {user.get_full_name() or user.username},\n", f"Vous avez {count} notification(s) non lue(s) reçue(s) il y a plus de {delay_hours} heure(s) :\n", ] html_items = [] for r in valid_recipients: n = r.notification created_str = n.created_at.strftime('%d/%m/%Y à %H:%i') sender_str = n.sender.get_full_name() if n.sender else _("Système") link = f"{domain}{n.action_url}" if n.action_url else f"{domain}/notifications/" lines_text.append(f"- [{n.get_notification_type_display()}] {n.title} (reçue le {created_str} par {sender_str})") lines_text.append(f" Lien: {link}\n") html_items.append( f"
  • " f"{n.title} ({n.get_notification_type_display()})
    " f"Reçue le {created_str} • {sender_str}
    " f"Consulter" f"
  • " ) lines_text.append(f"\nAccédez à votre boîte de réception : {domain}/notifications/") text_body = "\n".join(lines_text) html_body = f"""

    Notifications non lues

    Bonjour {user.get_full_name() or user.username},

    Vous avez {count} notification(s) non lue(s) qui attendent votre attention :

    Voir mes notifications

    """ if dry_run: self.stdout.write(f"[DRY-RUN] E-mail simulé pour {user.username} ({recipient_emails}) - {count} notification(s).") else: try: msg = EmailMultiAlternatives( subject=subject, body=text_body, from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@streetup.be'), to=recipient_emails, ) msg.attach_alternative(html_body, "text/html") msg.send() # Marquer le rappel envoyé NotificationRecipient.objects.filter(pk__in=[r.pk for r in valid_recipients]).update( reminder_sent_at=now ) sent_count += 1 user_count += 1 self.stdout.write(self.style.SUCCESS(f"Rappel envoyé avec succès à {user.username} ({recipient_emails}).")) except Exception as exc: logger.exception("Erreur d'envoi du rappel pour %s: %s", user.username, exc) self.stderr.write(self.style.ERROR(f"Erreur d'envoi pour {user.username}: {exc}")) self.stdout.write(self.style.SUCCESS(f"Traitement terminé : {sent_count} e-mail(s) envoyé(s) pour {user_count} utilisateur(s)."))