163 lines
6.9 KiB
Python
163 lines
6.9 KiB
Python
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:%M')
|
|
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"<li>"
|
|
f"<strong>{n.title}</strong> ({n.get_notification_type_display()})<br>"
|
|
f"<small style='color:#6c757d;'>Reçue le {created_str} • {sender_str}</small><br>"
|
|
f"<a href='{link}' style='color:#2f3e8b; font-weight:bold;'>Consulter</a>"
|
|
f"</li>"
|
|
)
|
|
|
|
lines_text.append(f"\nAccédez à votre boîte de réception : {domain}/notifications/")
|
|
text_body = "\n".join(lines_text)
|
|
|
|
html_body = f"""
|
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #333;">
|
|
<h2 style="color: #2f3e8b;">Notifications non lues</h2>
|
|
<p>Bonjour {user.get_full_name() or user.username},</p>
|
|
<p>Vous avez <strong>{count}</strong> notification(s) non lue(s) qui attendent votre attention :</p>
|
|
<ul style="line-height: 1.6; padding-left: 20px;">
|
|
{"".join(html_items)}
|
|
</ul>
|
|
<p style="margin-top: 20px;">
|
|
<a href="{domain}/notifications/" style="background-color: #2f3e8b; color: #fff; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">
|
|
Voir mes notifications
|
|
</a>
|
|
</p>
|
|
</div>
|
|
"""
|
|
|
|
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)."))
|