Merge branch 'Kerl/mail2bda' into 'master'
Copie des mails de rappel pour le BdA * Le BdA est en copie des mails de rappel (création d'un utilisateur générique BdA à cette fin) * Un lien vers la page d'envoi manuel des mails de rappel est ajouté à la page qui récapitule les situations des participants au spectacle * Quelques modification mineures de cette page * Un test très simple vérifie que les pages mentionnées plus haut sont accessibles à un utilisateur COF authentifié See merge request !238
This commit is contained in:
commit
398893b904
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -7,11 +7,20 @@ from custommail.shortcuts import send_mass_custom_mail
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Count
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone, formats
|
from django.utils import timezone, formats
|
||||||
|
|
||||||
|
|
||||||
|
def get_generic_user():
|
||||||
|
generic, _ = User.objects.get_or_create(
|
||||||
|
username="bda_generic",
|
||||||
|
defaults={"email": "bda@ens.fr", "first_name": "Bureau des arts"}
|
||||||
|
)
|
||||||
|
return generic
|
||||||
|
|
||||||
|
|
||||||
class Tirage(models.Model):
|
class Tirage(models.Model):
|
||||||
title = models.CharField("Titre", max_length=300)
|
title = models.CharField("Titre", max_length=300)
|
||||||
ouverture = models.DateTimeField("Date et heure d'ouverture du tirage")
|
ouverture = models.DateTimeField("Date et heure d'ouverture du tirage")
|
||||||
|
@ -96,32 +105,29 @@ class Spectacle(models.Model):
|
||||||
Envoie un mail de rappel à toutes les personnes qui ont une place pour
|
Envoie un mail de rappel à toutes les personnes qui ont une place pour
|
||||||
ce spectacle.
|
ce spectacle.
|
||||||
"""
|
"""
|
||||||
# On récupère la liste des participants
|
# On récupère la liste des participants + le BdA
|
||||||
members = {}
|
members = list(
|
||||||
for attr in Attribution.objects.filter(spectacle=self).all():
|
User.objects
|
||||||
member = attr.participant.user
|
.filter(participant__attributions=self)
|
||||||
if member.id in members:
|
.annotate(nb_attr=Count("id")).order_by()
|
||||||
members[member.id][1] = 2
|
)
|
||||||
else:
|
bda_generic = get_generic_user()
|
||||||
members[member.id] = [member, 1]
|
bda_generic.nb_attr = 1
|
||||||
# FIXME : faire quelque chose de ça, un utilisateur bda_generic ?
|
members.append(bda_generic)
|
||||||
# # Pour le BdA
|
|
||||||
# members[0] = ['BdA', 1, 'bda@ens.fr']
|
|
||||||
# members[-1] = ['BdA', 2, 'bda@ens.fr']
|
|
||||||
# On écrit un mail personnalisé à chaque participant
|
# On écrit un mail personnalisé à chaque participant
|
||||||
datatuple = [(
|
datatuple = [(
|
||||||
'bda-rappel',
|
'bda-rappel',
|
||||||
{'member': member[0], 'nb_attr': member[1], 'show': self},
|
{'member': member, "nb_attr": member.nb_attr, 'show': self},
|
||||||
settings.MAIL_DATA['rappels']['FROM'],
|
settings.MAIL_DATA['rappels']['FROM'],
|
||||||
[member[0].email])
|
[member.email])
|
||||||
for member in members.values()
|
for member in members
|
||||||
]
|
]
|
||||||
send_mass_custom_mail(datatuple)
|
send_mass_custom_mail(datatuple)
|
||||||
# On enregistre le fait que l'envoi a bien eu lieu
|
# On enregistre le fait que l'envoi a bien eu lieu
|
||||||
self.rappel_sent = timezone.now()
|
self.rappel_sent = timezone.now()
|
||||||
self.save()
|
self.save()
|
||||||
# On renvoie la liste des destinataires
|
# On renvoie la liste des destinataires
|
||||||
return members.values()
|
return members
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_past(self):
|
def is_past(self):
|
||||||
|
|
|
@ -10,14 +10,10 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>Voulez vous envoyer les mails de rappel pour le spectacle
|
<h3>Voulez vous envoyer les mails de rappel pour le spectacle {{ show.title }} ?</h3>
|
||||||
{{ show.title }} ?</h3>
|
|
||||||
{% if show.rappel_sent %}
|
|
||||||
<p class="error">Attention, les mails ont déjà été envoyés le
|
|
||||||
{{ show.rappel_sent }}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="empty-form">
|
||||||
{% if not sent %}
|
{% if not sent %}
|
||||||
<form action="" method="post">
|
<form action="" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -26,9 +22,17 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr \>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<em>Note :</em> le template de ce mail peut être modifié à
|
||||||
|
<a href="{% url 'admin:custommail_custommail_change' custommail.pk %}">cette adresse</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr \>
|
||||||
|
|
||||||
<br/>
|
|
||||||
<hr/>
|
|
||||||
<h3>Forme des mails</h3>
|
<h3>Forme des mails</h3>
|
||||||
|
|
||||||
<h4>Une seule place</h4>
|
<h4>Une seule place</h4>
|
||||||
|
@ -40,4 +44,5 @@
|
||||||
{% for part in exemple_mail_2places %}
|
{% for part in exemple_mail_2places %}
|
||||||
<pre>{{ part }}</pre>
|
<pre>{{ part }}</pre>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -36,17 +36,26 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h3><a href="{% url "admin:bda_attribution_add" %}?spectacle={{spectacle.id}}"><span class="glyphicon glyphicon-plus-sign"></span> Ajouter une attribution</a></h3>
|
<h3><a href="{% url "admin:bda_attribution_add" %}?spectacle={{spectacle.id}}"><span class="glyphicon glyphicon-plus-sign"></span> Ajouter une attribution</a></h3>
|
||||||
<br>
|
<div>
|
||||||
|
<div>
|
||||||
<button class="btn btn-default" type="button" onclick="toggle('export-mails')">Afficher/Cacher mails participants</button>
|
<button class="btn btn-default" type="button" onclick="toggle('export-mails')">Afficher/Cacher mails participants</button>
|
||||||
<pre id="export-mails" style="display:none">
|
<pre id="export-mails" style="display:none">{% spaceless %}
|
||||||
{%for participant in participants %}{{participant.email}}, {%endfor%}
|
{% for participant in participants %}{{ participant.email }}, {% endfor %}
|
||||||
</pre>
|
{% endspaceless %}</pre>
|
||||||
<br>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
<button class="btn btn-default" type="button" onclick="toggle('export-salle')">Afficher/Cacher liste noms</button>
|
<button class="btn btn-default" type="button" onclick="toggle('export-salle')">Afficher/Cacher liste noms</button>
|
||||||
<pre id="export-salle" style="display:none">
|
<pre id="export-salle" style="display:none">{% spaceless %}
|
||||||
{% for participant in participants %}{{participant.name}} : {{participant.nb_places}} places
|
{% for participant in participants %}{{participant.name}} : {{participant.nb_places}} places
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</pre>
|
{% endspaceless %}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="{% url 'bda-rappels' spectacle.id %}">Page d'envoi manuel des mails de rappel</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript"
|
<script type="text/javascript"
|
||||||
src="{% static "js/joequery-Stupid-Table-Plugin/stupidtable.js" %}"></script>
|
src="{% static "js/joequery-Stupid-Table-Plugin/stupidtable.js" %}"></script>
|
||||||
<script>
|
<script>
|
28
bda/tests.py
28
bda/tests.py
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -34,11 +35,36 @@ class TestBdAViews(TestCase):
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
self.bda_user = User.objects.create_user(
|
||||||
|
username="bda_user", password="bda4ever"
|
||||||
|
)
|
||||||
|
self.bda_user.profile.is_cof = True
|
||||||
|
self.bda_user.profile.is_buro = True
|
||||||
|
self.bda_user.profile.save()
|
||||||
|
|
||||||
|
def bda_participants(self):
|
||||||
|
"""The BdA participants views can be queried"""
|
||||||
|
client = Client()
|
||||||
|
show = self.tirage.spectacle_set.first()
|
||||||
|
|
||||||
|
client.login(self.bda_user.username, "bda4ever")
|
||||||
|
tirage_resp = client.get("/bda/spectacles/{}".format(self.tirage.id))
|
||||||
|
show_resp = client.get(
|
||||||
|
"/bda/spectacles/{}/{}".format(self.tirage.id, show.id)
|
||||||
|
)
|
||||||
|
reminder_url = "/bda/mails-rappel/{}".format(show.id)
|
||||||
|
reminder_get_resp = client.get(reminder_url)
|
||||||
|
reminder_post_resp = client.post(reminder_url)
|
||||||
|
self.assertEqual(200, tirage_resp.status_code)
|
||||||
|
self.assertEqual(200, show_resp.status_code)
|
||||||
|
self.assertEqual(200, reminder_get_resp.status_code)
|
||||||
|
self.assertEqual(200, reminder_post_resp.status_code)
|
||||||
|
|
||||||
def test_catalogue(self):
|
def test_catalogue(self):
|
||||||
"""Test the catalogue JSON API"""
|
"""Test the catalogue JSON API"""
|
||||||
client = Client()
|
client = Client()
|
||||||
|
|
||||||
# The `list` hooh
|
# The `list` hook
|
||||||
resp = client.get("/bda/catalogue/list")
|
resp = client.get("/bda/catalogue/list")
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
resp.content.decode("utf-8"),
|
resp.content.decode("utf-8"),
|
||||||
|
|
|
@ -44,7 +44,10 @@ urlpatterns = [
|
||||||
url(r'^revente-immediat/(?P<tirage_id>\d+)$',
|
url(r'^revente-immediat/(?P<tirage_id>\d+)$',
|
||||||
views.revente_shotgun,
|
views.revente_shotgun,
|
||||||
name="bda-shotgun"),
|
name="bda-shotgun"),
|
||||||
url(r'^mails-rappel/(?P<spectacle_id>\d+)$', views.send_rappel),
|
url(r'^mails-rappel/(?P<spectacle_id>\d+)$',
|
||||||
|
views.send_rappel,
|
||||||
|
name="bda-rappels"
|
||||||
|
),
|
||||||
url(r'^descriptions/(?P<tirage_id>\d+)$', views.descriptions_spectacles,
|
url(r'^descriptions/(?P<tirage_id>\d+)$', views.descriptions_spectacles,
|
||||||
name='bda-descriptions'),
|
name='bda-descriptions'),
|
||||||
url(r'^catalogue/(?P<request_type>[a-z]+)$', views.catalogue,
|
url(r'^catalogue/(?P<request_type>[a-z]+)$', views.catalogue,
|
||||||
|
|
33
bda/views.py
33
bda/views.py
|
@ -1,15 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from custommail.shortcuts import (
|
from custommail.shortcuts import send_mass_custom_mail, send_custom_mail
|
||||||
send_mass_custom_mail, send_custom_mail, render_custom_mail
|
from custommail.models import CustomMail
|
||||||
)
|
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
@ -27,7 +25,7 @@ from django.views.generic.list import ListView
|
||||||
from gestioncof.decorators import cof_required, buro_required
|
from gestioncof.decorators import cof_required, buro_required
|
||||||
from bda.models import (
|
from bda.models import (
|
||||||
Spectacle, Participant, ChoixSpectacle, Attribution, Tirage,
|
Spectacle, Participant, ChoixSpectacle, Attribution, Tirage,
|
||||||
SpectacleRevente, Salle, Quote, CategorieSpectacle
|
SpectacleRevente, Salle, CategorieSpectacle
|
||||||
)
|
)
|
||||||
from bda.algorithm import Algorithm
|
from bda.algorithm import Algorithm
|
||||||
from bda.forms import (
|
from bda.forms import (
|
||||||
|
@ -305,7 +303,8 @@ def do_tirage(tirage_elt, token):
|
||||||
# On inscrit à BdA-Revente ceux qui n'ont pas eu les places voulues
|
# On inscrit à BdA-Revente ceux qui n'ont pas eu les places voulues
|
||||||
ChoixRevente = Participant.choicesrevente.through
|
ChoixRevente = Participant.choicesrevente.through
|
||||||
|
|
||||||
# Suppression des reventes demandées/enregistrées (si le tirage est relancé)
|
# Suppression des reventes demandées/enregistrées
|
||||||
|
# (si le tirage est relancé)
|
||||||
(
|
(
|
||||||
ChoixRevente.objects
|
ChoixRevente.objects
|
||||||
.filter(spectacle__tirage=tirage_elt)
|
.filter(spectacle__tirage=tirage_elt)
|
||||||
|
@ -612,7 +611,7 @@ def spectacle(request, tirage_id, spectacle_id):
|
||||||
|
|
||||||
participants_info = sorted(participants.values(),
|
participants_info = sorted(participants.values(),
|
||||||
key=lambda part: part['lastname'])
|
key=lambda part: part['lastname'])
|
||||||
return render(request, "bda-participants.html",
|
return render(request, "bda/participants.html",
|
||||||
{"spectacle": spectacle, "participants": participants_info})
|
{"spectacle": spectacle, "participants": participants_info})
|
||||||
|
|
||||||
|
|
||||||
|
@ -651,20 +650,24 @@ def unpaid(request, tirage_id):
|
||||||
def send_rappel(request, spectacle_id):
|
def send_rappel(request, spectacle_id):
|
||||||
show = get_object_or_404(Spectacle, id=spectacle_id)
|
show = get_object_or_404(Spectacle, id=spectacle_id)
|
||||||
# Mails d'exemples
|
# Mails d'exemples
|
||||||
exemple_mail_1place = render_custom_mail('bda-rappel', {
|
custommail = CustomMail.objects.get(shortname="bda-rappel")
|
||||||
|
exemple_mail_1place = custommail.render({
|
||||||
'member': request.user,
|
'member': request.user,
|
||||||
'show': show,
|
'show': show,
|
||||||
'nb_attr': 1
|
'nb_attr': 1
|
||||||
})
|
})
|
||||||
exemple_mail_2places = render_custom_mail('bda-rappel', {
|
exemple_mail_2places = custommail.render({
|
||||||
'member': request.user,
|
'member': request.user,
|
||||||
'show': show,
|
'show': show,
|
||||||
'nb_attr': 2
|
'nb_attr': 2
|
||||||
})
|
})
|
||||||
# Contexte
|
# Contexte
|
||||||
ctxt = {'show': show,
|
ctxt = {
|
||||||
|
'show': show,
|
||||||
'exemple_mail_1place': exemple_mail_1place,
|
'exemple_mail_1place': exemple_mail_1place,
|
||||||
'exemple_mail_2places': exemple_mail_2places}
|
'exemple_mail_2places': exemple_mail_2places,
|
||||||
|
'custommail': custommail,
|
||||||
|
}
|
||||||
# Envoi confirmé
|
# Envoi confirmé
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
members = show.send_rappel()
|
members = show.send_rappel()
|
||||||
|
@ -673,6 +676,14 @@ def send_rappel(request, spectacle_id):
|
||||||
# Demande de confirmation
|
# Demande de confirmation
|
||||||
else:
|
else:
|
||||||
ctxt['sent'] = False
|
ctxt['sent'] = False
|
||||||
|
if show.rappel_sent:
|
||||||
|
messages.warning(
|
||||||
|
request,
|
||||||
|
"Attention, un mail de rappel pour ce spectale a déjà été "
|
||||||
|
"envoyé le {}".format(formats.localize(
|
||||||
|
timezone.template_localtime(show.rappel_sent)
|
||||||
|
))
|
||||||
|
)
|
||||||
return render(request, "bda/mails-rappel.html", ctxt)
|
return render(request, "bda/mails-rappel.html", ctxt)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,9 @@ a {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.empty-form {
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #444;
|
color: #444;
|
||||||
|
@ -341,10 +342,12 @@ fieldset legend {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: large;
|
font-size: large;
|
||||||
color:#DE826B;
|
color:#DE826B;
|
||||||
|
padding-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-content h4 {
|
#main-content h4 {
|
||||||
color:#DE826B;
|
color:#DE826B;
|
||||||
|
padding-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-content h2,
|
#main-content h2,
|
||||||
|
|
Loading…
Reference in a new issue