From 2396c163bd50d30985a008787cd1059d04036299 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Fri, 20 Aug 2021 00:45:33 +0200 Subject: [PATCH] =?UTF-8?q?Rajoute=20l'envoi=20des=20mails=20en=20t=C3=A2c?= =?UTF-8?q?he=20de=20fond,=20pour=20ne=20plus=20faire=20planter=20le=20ser?= =?UTF-8?q?veur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0032_auto_20210820_0023.py | 29 +++++++++++++ elections/models.py | 3 +- elections/tasks.py | 12 ++++++ .../templates/elections/upload_voters.html | 33 ++++++++++++++- elections/utils.py | 42 ++++++++++++------- elections/views.py | 14 +++++-- kadenios/settings/common.py | 1 + requirements.txt | 3 +- 8 files changed, 114 insertions(+), 23 deletions(-) create mode 100644 elections/migrations/0032_auto_20210820_0023.py create mode 100644 elections/tasks.py diff --git a/elections/migrations/0032_auto_20210820_0023.py b/elections/migrations/0032_auto_20210820_0023.py new file mode 100644 index 0000000..d539a0b --- /dev/null +++ b/elections/migrations/0032_auto_20210820_0023.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.6 on 2021-08-19 22:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("elections", "0031_alter_election_options"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="has_valid_email", + field=models.BooleanField( + default=None, null=True, verbose_name="email valide" + ), + ), + migrations.AlterField( + model_name="election", + name="sent_mail", + field=models.BooleanField( + default=False, + null=True, + verbose_name="mail avec les identifiants envoyé", + ), + ), + ] diff --git a/elections/models.py b/elections/models.py index f99eda7..12593df 100644 --- a/elections/models.py +++ b/elections/models.py @@ -50,7 +50,7 @@ class Election(models.Model): ) sent_mail = models.BooleanField( - _("mail avec les identifiants envoyé"), default=False + _("mail avec les identifiants envoyé"), null=True, default=False ) created_by = models.ForeignKey( @@ -232,6 +232,7 @@ class User(AbstractUser): on_delete=models.CASCADE, ) full_name = models.CharField(_("Nom et Prénom"), max_length=150, blank=True) + has_valid_email = models.BooleanField(_("email valide"), null=True, default=None) @property def base_username(self): diff --git a/elections/tasks.py b/elections/tasks.py new file mode 100644 index 0000000..87e248f --- /dev/null +++ b/elections/tasks.py @@ -0,0 +1,12 @@ +from background_task import background + +from .models import Election +from .utils import send_mail + + +@background(schedule=5) +def send_election_mail(election_pk, subject, body, reply_to): + election = Election.objects.get(pk=election_pk) + send_mail(election, subject, body, reply_to) + election.sent_mail = True + election.save(update_fields=["sent_mail"]) diff --git a/elections/templates/elections/upload_voters.html b/elections/templates/elections/upload_voters.html index a657596..63a9ef5 100644 --- a/elections/templates/elections/upload_voters.html +++ b/elections/templates/elections/upload_voters.html @@ -29,7 +29,7 @@
- {% if not election.sent_mail %} + {% if election.sent_mail is False %} + {% elif election.sent_mail is None %} +
+ + + + + {% trans "Mail en cours de distribution" %} + +
+ {% else %} +
+ + + + + {% trans "Mail envoyé" %} + +
{% endif %}
@@ -114,7 +132,18 @@ {{ v.base_username }} {{ v.full_name }} - {{ v.email }} + + {{ v.email }} + {% if v.has_valid_email %} + + + + {% elif v.has_valid_email is False %} + + + + {% endif %} + {% endfor %} diff --git a/elections/utils.py b/elections/utils.py index 6e0eb5e..cd4facb 100644 --- a/elections/utils.py +++ b/elections/utils.py @@ -1,5 +1,6 @@ import csv import io +import smtplib import networkx as nx import numpy as np @@ -8,7 +9,7 @@ from networkx.algorithms.dag import ancestors, descendants from django.contrib.auth import get_user_model from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError -from django.core.mail import EmailMessage # , get_connection +from django.core.mail import EmailMessage from django.core.validators import validate_email from django.template.loader import render_to_string from django.urls import reverse @@ -383,13 +384,14 @@ def check_csv(csv_file): return errors -def send_mail(election, mail_form): +def send_mail(election, subject, body, reply_to): """Envoie le mail d'annonce de l'élection avec identifiants et mot de passe aux votant·e·s, le mdp est généré en même temps que le mail est envoyé. """ User = get_user_model() - voters = list(election.registered_voters.all()) + # On n'envoie le mail qu'aux personnes qui n'en n'ont pas déjà reçu un + voters = list(election.registered_voters.exclude(has_valid_email=True)) e_url = reverse("election.view", args=[election.id]) url = f"https://vote.eleves.ens.fr{e_url}" messages = [] @@ -397,18 +399,28 @@ def send_mail(election, mail_form): password = generate_password() v.password = make_password(password) messages.append( - EmailMessage( - subject=mail_form.cleaned_data["objet"], - body=mail_form.cleaned_data["message"].format( - full_name=v.full_name, - election_url=url, - username=v.base_username, - password=password, + ( + EmailMessage( + subject=subject, + body=body.format( + full_name=v.full_name, + election_url=url, + username=v.base_username, + password=password, + ), + to=[v.email], + reply_to=[reply_to], ), - to=[v.email], + v, ) ) - # get_connection(fail_silently=False).send_messages(messages) - for m in messages: - m.send() - User.objects.bulk_update(voters, ["password"]) + + for (m, v) in messages: + try: + m.send() + except smtplib.SMTPException: + v.has_valid_email = False + else: + v.has_valid_email = True + + User.objects.bulk_update(voters, ["password", "has_valid_email"]) diff --git a/elections/views.py b/elections/views.py index 24cf3cb..5e9d748 100644 --- a/elections/views.py +++ b/elections/views.py @@ -41,7 +41,8 @@ from .mixins import ( ) from .models import Election, Option, Question, Vote from .staticdefs import MAIL_VOTE_DELETED, MAIL_VOTERS, QUESTION_TYPES, VOTE_RULES -from .utils import create_users, send_mail +from .tasks import send_election_mail +from .utils import create_users User = get_user_model() @@ -158,7 +159,7 @@ class ElectionUploadVotersView(CreatorOnlyEditMixin, SuccessMessageMixin, FormVi class ElectionMailVotersView(CreatorOnlyEditMixin, SuccessMessageMixin, FormView): model = Election form_class = VoterMailForm - success_message = _("Mail d'annonce envoyé avec succès !") + success_message = _("Mail d'annonce en cours d'envoi !") template_name = "elections/mail_voters.html" def get_queryset(self): @@ -181,9 +182,14 @@ class ElectionMailVotersView(CreatorOnlyEditMixin, SuccessMessageMixin, FormView return super().post(request, *args, **kwargs) def form_valid(self, form): - self.object.sent_mail = True - send_mail(self.object, form) + self.object.sent_mail = None self.object.save() + send_election_mail( + election_pk=self.object.pk, + subject=form.cleaned_data["objet"], + body=form.cleaned_data["message"], + reply_to=self.request.user.email, + ) return super().form_valid(form) diff --git a/kadenios/settings/common.py b/kadenios/settings/common.py index 6143d80..e15dd35 100644 --- a/kadenios/settings/common.py +++ b/kadenios/settings/common.py @@ -53,6 +53,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "kadenios.apps.IgnoreSrcStaticFilesConfig", + "background_task", "shared", "elections", "faqs", diff --git a/requirements.txt b/requirements.txt index bc3ab09..a332359 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ django==3.2.* -django-translated-fields==0.11.1 +django-translated-fields==0.11.* authens>=0.1b2 markdown numpy networkx python-csv +django-background-tasks