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