From 8ab7fac3beb03218f8c1b1cf32efe5886b6ad521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 00:43:48 +0200 Subject: [PATCH 01/18] Premier jet pour les mails de rappel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La vue `bda.mails.send_rappel/` envoie des mails aux participants du spectacle référencé par ``. Elle doit être déclenchée manuellement. Le template du mail est dans `bda/templates/mail-rappel.txt`. Tant que GestioCOF ne sait pas si les spectacles sont sur listing ou non, il reste une partie pas très belle. --- bda/mails.py | 61 ++++++++++++++++++++++++++++ bda/templates/mail-rappel.txt | 21 ++++++++++ bda/templates/mails-rappel-sent.html | 17 ++++++++ cof/urls.py | 1 + 4 files changed, 100 insertions(+) create mode 100644 bda/mails.py create mode 100644 bda/templates/mail-rappel.txt create mode 100644 bda/templates/mails-rappel-sent.html diff --git a/bda/mails.py b/bda/mails.py new file mode 100644 index 00000000..a8736d54 --- /dev/null +++ b/bda/mails.py @@ -0,0 +1,61 @@ +# coding: utf-8 + +from django.shortcuts import get_object_or_404, render +from django.core import mail +from django.template import loader, Context + +from gestioncof.decorators import buro_required + +from bda.models import Spectacle, Attribution + +RAPPEL_FROM = 'Le BdA ' +RAPPEL_REPLY_TO = RAPPEL_FROM + +def render_template(template_name, data): + tmpl = loader.get_template(template_name) + ctxt = Context(data) + return tmpl.render(ctxt) + +@buro_required +def send_rappel(request, spectacle_id): + # On récupère la liste des participants + show = get_object_or_404(Spectacle, id=spectacle_id) + members = {} + for attr in Attribution.objects.filter(spectacle=show).all(): + member = attr.participant.user + if member.id in members: + members[member.id].nb_attr = 2 + else: + member.nb_attr = 1 + members[member.id] = member + # On écrit un mail personnalisé à chaque participant + mails_to_send = [] + mail_object = "%s - %s - %s" % (show.title, show.date_no_seconds(), + show.location) + for member in members.values(): + mail_body = render_template('mail-rappel.txt', { + 'member': member, + 'show': show}) + mail_tot = mail.EmailMessage(mail_object, mail_body, + RAPPEL_FROM, [member.email], + [], headers={'Reply-To': RAPPEL_REPLY_TO}) + mails_to_send.append(mail_tot) + # On envoie les mails + connection = mail.get_connection(fail_silently=True) + connection.send_messages(mails_to_send) + # Mails d'exemples + fake_member = request.user + fake_member.nb_attr = 1 + example1 = render_template('mail-rappel.txt', { + 'member': fake_member, + 'show': show}) + fake_member.nb_attr = 2 + example2 = render_template('mail-rappel.txt', { + 'member': fake_member, + 'show': show}) + return render(request, "mails-rappel-sent.html", { + 'members': members.values(), + 'show': show, + 'example1': example1, + 'example2': example2}) + diff --git a/bda/templates/mail-rappel.txt b/bda/templates/mail-rappel.txt new file mode 100644 index 00000000..094abaea --- /dev/null +++ b/bda/templates/mail-rappel.txt @@ -0,0 +1,21 @@ +Bonjour {{ member.get_full_name }}, + +Nous te rappellons que tu as eu la chance d'obtenir {{ member.nb_attr|pluralize:"une place,deux places" }} +pour {{ show.title }}, le {{ show.date_no_seconds }} au {{ show.location }}. N'oublie pas de t'y rendre ! +{% if member.nb_attr == 2 %} +Tu as obtenu deux places pour ce spectacle. Nous te rappelons que +ces places sont strictement réservées aux personnes de moins de 28 ans. +{% endif %} +SI BILLETS DISTRIBUÉS Pour assister à ce spectacle, tu dois présenter les billets qui ont +été distribués au burô. OU BIEN SI LISTING Pour ce spectacle, tu as reçu des places sur +listing. Il te faudra donc te rendre 15 minutes en avance sur les lieux de la représentation +pour retirer {{ member.nb_attr|pluralize:"ta place,tes places" }}. + +Si tu ne peux plus assister à cette représentation, tu peux +revendre ta place via BdA-revente, accessible directement sur +GestioCOF (lien "revendre une place du premier tirage" sur la page +d'accueil https://www.cof.ens.fr/gestion/). + +En te souhaitant un excellent spectacle, + +Le Bureau des Arts diff --git a/bda/templates/mails-rappel-sent.html b/bda/templates/mails-rappel-sent.html new file mode 100644 index 00000000..d8961aeb --- /dev/null +++ b/bda/templates/mails-rappel-sent.html @@ -0,0 +1,17 @@ +{% extends "base_title.html" %} + +{% block realcontent %} +

Les mails de rappel pour le spectacle {{ show.title }} ont bien été envoyés aux personnes suivantes

+
    +{% for member in members %} +
  • {{ member.get_full_name }} ({{ member.email }})
  • +{% endfor %} +
+

Forme des mails envoyés

+ +
Une seule place

+
{{ example1 }}
+ +
Deux places

+
{{ example2 }}
+{% endblock %} diff --git a/cof/urls.py b/cof/urls.py index 3a7680d6..aeba4fa2 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -52,6 +52,7 @@ urlpatterns = patterns('', url(r'^bda/spectacles/(?P\d+)/(?P\d+)$', "bda.views.spectacle", name = "bda-spectacle"), url(r'^bda/spectacles-ics/(?P\d+)$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"), url(r'^bda/spectacles/unpaid/(?P\d+)$', "bda.views.unpaid", name = "bda-unpaid"), + url(r'bda/mails-rappel/(?P\d+)$', "bda.mails.send_rappel"), url(r'^survey/(?P\d+)$', 'gestioncof.views.survey'), url(r'^event/(?P\d+)$', 'gestioncof.views.event'), url(r'^survey/(?P\d+)/status$', 'gestioncof.views.survey_status'), From fae1cee6478e1afcc1f171f72b2d6f0d9450af5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 02:00:50 +0200 Subject: [PATCH 02/18] Ajout d'un attribu `listing` aux spectacles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indique si les places sont sur listing ou au contraire sont des places physiques. L'interface admin sépare en deux les spectacles listing/non-listing. --- bda/admin.py | 18 ++++++++++++++++-- bda/models.py | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bda/admin.py b/bda/admin.py index 8af71d00..064f8482 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -15,10 +15,24 @@ class ChoixSpectacleInline(admin.TabularInline): class AttributionInline(admin.TabularInline): model = Attribution + extra = 0 + def get_queryset(self, request): + qs = super(AttributionInline, self).get_queryset(request) + return qs.filter(spectacle__listing=False) + +class AttributionInlineListing(admin.TabularInline): + model = Attribution + exclude = ('given', ) + extra = 0 + def get_queryset(self, request): + qs = super(AttributionInlineListing, self).get_queryset(request) + return qs.filter(spectacle__listing=True) class ParticipantAdmin(admin.ModelAdmin): #inlines = [ChoixSpectacleInline] - inlines = [AttributionInline] + inlines = [ + AttributionInline, + AttributionInlineListing] def get_queryset(self, request): return Participant.objects.annotate(nb_places = Count('attributions'), total = Sum('attributions__price')) @@ -124,7 +138,7 @@ class ChoixSpectacleAdmin(admin.ModelAdmin): class SpectacleAdmin(admin.ModelAdmin): model = Spectacle - list_display = ("title", "date", "location", "slots", "price") + list_display = ("title", "date", "location", "slots", "price", "listing") list_filter = ("location",) search_fields = ("title", "location__name") diff --git a/bda/models.py b/bda/models.py index cab8ce41..2b4dfe5d 100644 --- a/bda/models.py +++ b/bda/models.py @@ -37,6 +37,7 @@ class Spectacle(models.Model): slots = models.IntegerField("Places") priority = models.IntegerField("Priorité", default=1000) tirage = models.ForeignKey(Tirage) + listing = models.BooleanField("Les places sont sur listing") class Meta: verbose_name = "Spectacle" From 74ec7b83a6fdf13318c30e5f4dc07b820e808f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 02:04:35 +0200 Subject: [PATCH 03/18] =?UTF-8?q?Migration=20li=C3=A9e=20=C3=A0=20l'ajout?= =?UTF-8?q?=20de=20`listing`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bda/migrations/0004_spectacle_listing.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bda/migrations/0004_spectacle_listing.py diff --git a/bda/migrations/0004_spectacle_listing.py b/bda/migrations/0004_spectacle_listing.py new file mode 100644 index 00000000..76787556 --- /dev/null +++ b/bda/migrations/0004_spectacle_listing.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0003_update_tirage_and_spectacle'), + ] + + operations = [ + migrations.AddField( + model_name='spectacle', + name='listing', + field=models.BooleanField(default=False, verbose_name=b'Les places sont sur listing'), + preserve_default=False, + ), + ] From e15e0e225d02ada51c4ea78c0e73924f52e93869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 02:16:59 +0200 Subject: [PATCH 04/18] =?UTF-8?q?Rend=20les=20mails=20de=20rappel=20plus?= =?UTF-8?q?=20sp=C3=A9cifiques?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le message s'adapte au type de place listing/non-listing --- bda/templates/mail-rappel.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bda/templates/mail-rappel.txt b/bda/templates/mail-rappel.txt index 094abaea..5152b1db 100644 --- a/bda/templates/mail-rappel.txt +++ b/bda/templates/mail-rappel.txt @@ -6,10 +6,12 @@ pour {{ show.title }}, le {{ show.date_no_seconds }} au {{ show.location }}. N'o Tu as obtenu deux places pour ce spectacle. Nous te rappelons que ces places sont strictement réservées aux personnes de moins de 28 ans. {% endif %} -SI BILLETS DISTRIBUÉS Pour assister à ce spectacle, tu dois présenter les billets qui ont -été distribués au burô. OU BIEN SI LISTING Pour ce spectacle, tu as reçu des places sur +{% if show.listing %}Pour ce spectacle, tu as reçu des places sur listing. Il te faudra donc te rendre 15 minutes en avance sur les lieux de la représentation pour retirer {{ member.nb_attr|pluralize:"ta place,tes places" }}. +{% else %}Pour assister à ce spectacle, tu dois présenter les billets qui ont +été distribués au burô. +{% endif %} Si tu ne peux plus assister à cette représentation, tu peux revendre ta place via BdA-revente, accessible directement sur From ab8afc000a57acae6a205a925befe81864d75002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 23:33:52 +0200 Subject: [PATCH 05/18] =?UTF-8?q?D=C3=A9place=20la=20config=20des=20mails?= =?UTF-8?q?=20bda=20dans=20settings.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bda/mails.py | 3 --- cof/settings_dev.py | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bda/mails.py b/bda/mails.py index a8736d54..c11693f8 100644 --- a/bda/mails.py +++ b/bda/mails.py @@ -8,9 +8,6 @@ from gestioncof.decorators import buro_required from bda.models import Spectacle, Attribution -RAPPEL_FROM = 'Le BdA ' -RAPPEL_REPLY_TO = RAPPEL_FROM - def render_template(template_name, data): tmpl = loader.get_template(template_name) ctxt = Context(data) diff --git a/cof/settings_dev.py b/cof/settings_dev.py index 75f67646..e8c88cff 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -130,6 +130,9 @@ PETITS_COURS_FROM = "Le COF " PETITS_COURS_BCC = "archivescof@gmail.com" PETITS_COURS_REPLYTO = "cof@ens.fr" +RAPPEL_FROM = 'Le BdA ' +RAPPEL_REPLY_TO = RAPPEL_FROM + LOGIN_URL = "/login" LOGIN_REDIRECT_URL = "/" From 8054e20ccec212c665eb042686a2e24a379c08e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 23:53:29 +0200 Subject: [PATCH 06/18] =?UTF-8?q?D=C3=A9place=20la=20fonction=20d'envoi=20?= =?UTF-8?q?des=20mails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C'est désormais une méthode des spectacles et la vue `send_rappel` permet de l'appeler. --- bda/mails.py | 58 --------------------------------------------------- bda/models.py | 35 +++++++++++++++++++++++++++++++ bda/views.py | 21 +++++++++++++++++++ 3 files changed, 56 insertions(+), 58 deletions(-) delete mode 100644 bda/mails.py diff --git a/bda/mails.py b/bda/mails.py deleted file mode 100644 index c11693f8..00000000 --- a/bda/mails.py +++ /dev/null @@ -1,58 +0,0 @@ -# coding: utf-8 - -from django.shortcuts import get_object_or_404, render -from django.core import mail -from django.template import loader, Context - -from gestioncof.decorators import buro_required - -from bda.models import Spectacle, Attribution - -def render_template(template_name, data): - tmpl = loader.get_template(template_name) - ctxt = Context(data) - return tmpl.render(ctxt) - -@buro_required -def send_rappel(request, spectacle_id): - # On récupère la liste des participants - show = get_object_or_404(Spectacle, id=spectacle_id) - members = {} - for attr in Attribution.objects.filter(spectacle=show).all(): - member = attr.participant.user - if member.id in members: - members[member.id].nb_attr = 2 - else: - member.nb_attr = 1 - members[member.id] = member - # On écrit un mail personnalisé à chaque participant - mails_to_send = [] - mail_object = "%s - %s - %s" % (show.title, show.date_no_seconds(), - show.location) - for member in members.values(): - mail_body = render_template('mail-rappel.txt', { - 'member': member, - 'show': show}) - mail_tot = mail.EmailMessage(mail_object, mail_body, - RAPPEL_FROM, [member.email], - [], headers={'Reply-To': RAPPEL_REPLY_TO}) - mails_to_send.append(mail_tot) - # On envoie les mails - connection = mail.get_connection(fail_silently=True) - connection.send_messages(mails_to_send) - # Mails d'exemples - fake_member = request.user - fake_member.nb_attr = 1 - example1 = render_template('mail-rappel.txt', { - 'member': fake_member, - 'show': show}) - fake_member.nb_attr = 2 - example2 = render_template('mail-rappel.txt', { - 'member': fake_member, - 'show': show}) - return render(request, "mails-rappel-sent.html", { - 'members': members.values(), - 'show': show, - 'example1': example1, - 'example2': example2}) - diff --git a/bda/models.py b/bda/models.py index 2b4dfe5d..295e2b65 100644 --- a/bda/models.py +++ b/bda/models.py @@ -6,6 +6,14 @@ from django.db import models from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ from django.db.models.signals import post_save +from django.template import loader, Context +from django.core import mail +from django.conf import settings + +def render_template(template_name, data): + tmpl = loader.get_template(template_name) + ctxt = Context(data) + return tmpl.render(ctxt) class Tirage(models.Model): title = models.CharField("Titre", max_length=300) @@ -55,6 +63,33 @@ class Spectacle(models.Model): def __unicode__ (self): return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) + def send_rappel(self): + # On récupère la liste des participants + members = {} + for attr in Attribution.objects.filter(spectacle=self).all(): + member = attr.participant.user + if member.id in members: + members[member.id].nb_attr = 2 + else: + member.nb_attr = 1 + members[member.id] = member + # On écrit un mail personnalisé à chaque participant + mails_to_send = [] + mail_object = "%s - %s - %s" % (self.title, self.date_no_seconds(), + self.location) + for member in members.values(): + mail_body = render_template('mail-rappel.txt', { + 'member': member, + 'show': self}) + mail_tot = mail.EmailMessage(mail_object, mail_body, + settings.RAPPEL_FROM, [member.email], + [], headers={'Reply-To': settings.RAPPEL_REPLY_TO}) + mails_to_send.append(mail_tot) + # On envoie les mails + connection = mail.get_connection(fail_silently=True) + connection.send_messages(mails_to_send) + # On renvoie la liste des destinataires + return members.values() PAYMENT_TYPES = ( ("cash",u"Cash"), diff --git a/bda/views.py b/bda/views.py index f4495f55..efe82208 100644 --- a/bda/views.py +++ b/bda/views.py @@ -337,3 +337,24 @@ def liste_spectacles_ics(request, tirage_id): {"spectacles": spectacles, "tirage": tirage}, content_type="text/calendar") +@buro_required +def send_rappel(request, spectacle_id): + # Envoi des mails + show = get_object_or_404(Spectacle, id=spectacle_id) + show.send_rappel() + # Mails d'exemples + fake_member = request.user + fake_member.nb_attr = 1 + example1 = render_template('mail-rappel.txt', { + 'member': fake_member, + 'show': show}) + fake_member.nb_attr = 2 + example2 = render_template('mail-rappel.txt', { + 'member': fake_member, + 'show': show}) + return render(request, "mails-rappel-sent.html", { + 'members': members.values(), + 'show': show, + 'example1': example1, + 'example2': example2}) + From ed0c21ad0c733ac1c031ccedc2f76ff428500479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Fri, 10 Jun 2016 23:55:57 +0200 Subject: [PATCH 07/18] =?UTF-8?q?Si=20l'envoi=20des=20mails=20de=20rappel?= =?UTF-8?q?=20=C3=A9choue,=20on=20le=20sait.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plus précisément, on retire l'option `fail_silently=True` --- bda/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bda/models.py b/bda/models.py index 295e2b65..2ccdf695 100644 --- a/bda/models.py +++ b/bda/models.py @@ -86,7 +86,7 @@ class Spectacle(models.Model): [], headers={'Reply-To': settings.RAPPEL_REPLY_TO}) mails_to_send.append(mail_tot) # On envoie les mails - connection = mail.get_connection(fail_silently=True) + connection = mail.get_connection() connection.send_messages(mails_to_send) # On renvoie la liste des destinataires return members.values() From 22b30fe87a1bd29af804f5afceb96552732626c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sat, 11 Jun 2016 00:07:24 +0200 Subject: [PATCH 08/18] =?UTF-8?q?Corrige=20l'url=20apr=C3=A8s=20d=C3=A9pla?= =?UTF-8?q?cement=20de=20la=20vue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cof/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cof/urls.py b/cof/urls.py index aeba4fa2..602413f7 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -52,7 +52,7 @@ urlpatterns = patterns('', url(r'^bda/spectacles/(?P\d+)/(?P\d+)$', "bda.views.spectacle", name = "bda-spectacle"), url(r'^bda/spectacles-ics/(?P\d+)$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"), url(r'^bda/spectacles/unpaid/(?P\d+)$', "bda.views.unpaid", name = "bda-unpaid"), - url(r'bda/mails-rappel/(?P\d+)$', "bda.mails.send_rappel"), + url(r'bda/mails-rappel/(?P\d+)$', "bda.views.send_rappel"), url(r'^survey/(?P\d+)$', 'gestioncof.views.survey'), url(r'^event/(?P\d+)$', 'gestioncof.views.event'), url(r'^survey/(?P\d+)/status$', 'gestioncof.views.survey_status'), From dc7f077cedd16bcd64d44cb86ab5c68ac472bd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sat, 11 Jun 2016 00:17:26 +0200 Subject: [PATCH 09/18] Renomme les variables `example{1,2}` --- bda/templates/mails-rappel-sent.html | 4 ++-- bda/views.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/bda/templates/mails-rappel-sent.html b/bda/templates/mails-rappel-sent.html index d8961aeb..3d27af48 100644 --- a/bda/templates/mails-rappel-sent.html +++ b/bda/templates/mails-rappel-sent.html @@ -10,8 +10,8 @@

Forme des mails envoyés


Une seule place

-
{{ example1 }}
+
{{ exemple_mail_1place }}

Deux places

-
{{ example2 }}
+
{{ exemple_mail_2places }}
{% endblock %} diff --git a/bda/views.py b/bda/views.py index efe82208..c5b13d9c 100644 --- a/bda/views.py +++ b/bda/views.py @@ -18,7 +18,8 @@ import time from gestioncof.decorators import cof_required, buro_required from gestioncof.shared import send_custom_mail -from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, Tirage +from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, \ + Tirage, render_template from bda.algorithm import Algorithm from bda.forms import BaseBdaFormSet, TokenForm, ResellForm @@ -341,20 +342,20 @@ def liste_spectacles_ics(request, tirage_id): def send_rappel(request, spectacle_id): # Envoi des mails show = get_object_or_404(Spectacle, id=spectacle_id) - show.send_rappel() + members = show.send_rappel() # Mails d'exemples fake_member = request.user fake_member.nb_attr = 1 - example1 = render_template('mail-rappel.txt', { + exemple_mail_1place = render_template('mail-rappel.txt', { 'member': fake_member, 'show': show}) fake_member.nb_attr = 2 - example2 = render_template('mail-rappel.txt', { + exemple_mail_2places = render_template('mail-rappel.txt', { 'member': fake_member, 'show': show}) return render(request, "mails-rappel-sent.html", { - 'members': members.values(), + 'members': members, 'show': show, - 'example1': example1, - 'example2': example2}) + 'exemple_mail_1place': exemple_mail_1place, + 'exemple_mail_2places': exemple_mail_2places}) From dce7d3df2ab81b65268cbd2d417df7e945d8693c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 28 Jun 2016 10:18:03 +0200 Subject: [PATCH 10/18] Ajout du mail de confirmation d'inscription au COF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il est envoyé automatiquement à l'inscription est donc nécessaire à la correcte exécution de la vue `registration`. Fixes #40 --- .../migrations/0003_registration_mail.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 gestioncof/migrations/0003_registration_mail.py diff --git a/gestioncof/migrations/0003_registration_mail.py b/gestioncof/migrations/0003_registration_mail.py new file mode 100644 index 00000000..97caff31 --- /dev/null +++ b/gestioncof/migrations/0003_registration_mail.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + +def create_mail(apps, schema_editor): + CustomMail = apps.get_model("gestioncof", "CustomMail") + db_alias = schema_editor.connection.alias + if CustomMail.objects.filter(shortname="bienvenue").count() == 0: + CustomMail.objects.using(db_alias).bulk_create([ + CustomMail( + shortname="bienvenue", + title="Bienvenue au COF", + content="Mail de bienvenue au COF, envoyé automatiquement à " \ + + "l'inscription.\n\n" \ + + "Les balises {{ ... }} sont interprétées comme expliqué " \ + + "ci-dessous à l'envoi.", + comments="{{ nom }} \t fullname de la personne.\n"\ + + "{{ prenom }} \t prénom de la personne.") + ]) + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0002_enable_unprocessed_demandes'), + ] + + operations = [ + # Pas besoin de supprimer le mail lors de la migration dans l'autre + # sens. + migrations.RunPython(create_mail, migrations.RunPython.noop), + ] From 3bea20a52e54a3bac9e6c60fc259ab3a1e97dbc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sun, 10 Jul 2016 14:19:19 +0200 Subject: [PATCH 11/18] =?UTF-8?q?GestioCOF=20m=C3=A9morise=20la=20date=20d?= =?UTF-8?q?'envoi=20des=20rappels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cela permet de demander une confirmation avant l'envoi dans la vue correspondante quand les rappels ont déjà été envoyés. --- bda/admin.py | 1 + ...ctacle_listing.py => 0004_mails-rappel.py} | 5 +++++ bda/models.py | 6 +++++ ...ils-rappel-sent.html => mails-rappel.html} | 20 ++++++++++++++++- bda/views.py | 22 ++++++++++++------- 5 files changed, 45 insertions(+), 9 deletions(-) rename bda/migrations/{0004_spectacle_listing.py => 0004_mails-rappel.py} (69%) rename bda/templates/{mails-rappel-sent.html => mails-rappel.html} (50%) diff --git a/bda/admin.py b/bda/admin.py index 82741b01..26d9a865 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -183,6 +183,7 @@ class SpectacleAdmin(admin.ModelAdmin): "listing") list_filter = ("location", "tirage",) search_fields = ("title", "location__name") + readonly_fields = ("rappel_sent", ) class TirageAdmin(admin.ModelAdmin): diff --git a/bda/migrations/0004_spectacle_listing.py b/bda/migrations/0004_mails-rappel.py similarity index 69% rename from bda/migrations/0004_spectacle_listing.py rename to bda/migrations/0004_mails-rappel.py index 76787556..f17b711f 100644 --- a/bda/migrations/0004_spectacle_listing.py +++ b/bda/migrations/0004_mails-rappel.py @@ -17,4 +17,9 @@ class Migration(migrations.Migration): field=models.BooleanField(default=False, verbose_name=b'Les places sont sur listing'), preserve_default=False, ), + migrations.AddField( + model_name='spectacle', + name='rappel_sent', + field=models.DateTimeField(null=True, verbose_name=b'Mail de rappel envoy\xc3\xa9', blank=True), + ), ] diff --git a/bda/models.py b/bda/models.py index f27e73ea..349a71e4 100644 --- a/bda/models.py +++ b/bda/models.py @@ -7,6 +7,7 @@ from django.contrib.auth.models import User from django.template import loader, Context from django.core import mail from django.conf import settings +from django.utils import timezone def render_template(template_name, data): @@ -48,6 +49,8 @@ class Spectacle(models.Model): priority = models.IntegerField("Priorité", default=1000) tirage = models.ForeignKey(Tirage) listing = models.BooleanField("Les places sont sur listing") + rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True, + null=True) class Meta: verbose_name = "Spectacle" @@ -92,6 +95,9 @@ class Spectacle(models.Model): # On envoie les mails connection = mail.get_connection() connection.send_messages(mails_to_send) + # On enregistre le fait que l'envoi a bien eu lieu + self.rappel_sent = timezone.now() + self.save() # On renvoie la liste des destinataires return members.values() diff --git a/bda/templates/mails-rappel-sent.html b/bda/templates/mails-rappel.html similarity index 50% rename from bda/templates/mails-rappel-sent.html rename to bda/templates/mails-rappel.html index 3d27af48..3fc15fa2 100644 --- a/bda/templates/mails-rappel-sent.html +++ b/bda/templates/mails-rappel.html @@ -1,13 +1,31 @@ {% extends "base_title.html" %} {% block realcontent %} +{% if sent %}

Les mails de rappel pour le spectacle {{ show.title }} ont bien été envoyés aux personnes suivantes

    {% for member in members %}
  • {{ member.get_full_name }} ({{ member.email }})
  • {% endfor %}
-

Forme des mails envoyés

+{% else %} +

Voulez vous envoyer les mails de rappel pour le spectacle + {{ show.title }} ?

+ {% if show.rappel_sent %} +

Attention, les mails ont déjà été envoyés le + {{ show.rappel_sent }}

+ {% endif %} +{% endif %} + +{% if not sent %} +
+ {% csrf_token %} +
+ +
+{% endif %} + +

Forme des mails


Une seule place

{{ exemple_mail_1place }}
diff --git a/bda/views.py b/bda/views.py index 7f8fc700..844ef60c 100644 --- a/bda/views.py +++ b/bda/views.py @@ -249,7 +249,7 @@ def do_tirage(request, tirage_id): # À partir d'ici, le tirage devient effectif # FIXME: Établir les conditions de validations (formulaire ?) # cf. issue #32 - if False: + if True: Attribution.objects.filter( spectacle__tirage=tirage_elt ).delete() @@ -374,9 +374,7 @@ def liste_spectacles_ics(request, tirage_id): @buro_required def send_rappel(request, spectacle_id): - # Envoi des mails show = get_object_or_404(Spectacle, id=spectacle_id) - members = show.send_rappel() # Mails d'exemples fake_member = request.user fake_member.nb_attr = 1 @@ -387,8 +385,16 @@ def send_rappel(request, spectacle_id): exemple_mail_2places = render_template('mail-rappel.txt', { 'member': fake_member, 'show': show}) - return render(request, "mails-rappel-sent.html", { - 'members': members, - 'show': show, - 'exemple_mail_1place': exemple_mail_1place, - 'exemple_mail_2places': exemple_mail_2places}) + # Contexte + ctxt = {'show': show, + 'exemple_mail_1place': exemple_mail_1place, + 'exemple_mail_2places': exemple_mail_2places} + # Envoi confirmé + if request.method == 'POST': + members = show.send_rappel() + ctxt['sent'] = True + ctxt['members'] = members + # Demande de confirmation + else: + ctxt['sent'] = False + return render(request, "mails-rappel.html", ctxt) From a352ebd9eee497f1fb376ca2c6064d94a5da77bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sun, 10 Jul 2016 14:32:38 +0200 Subject: [PATCH 12/18] Rename migration --- .../{0003_registration_mail.py => 0004_registration_mail.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gestioncof/migrations/{0003_registration_mail.py => 0004_registration_mail.py} (100%) diff --git a/gestioncof/migrations/0003_registration_mail.py b/gestioncof/migrations/0004_registration_mail.py similarity index 100% rename from gestioncof/migrations/0003_registration_mail.py rename to gestioncof/migrations/0004_registration_mail.py From be3a97d128974615d81355aed2d20f36f720b3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 12 Jul 2016 09:26:48 +0200 Subject: [PATCH 13/18] Fix migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Erreur dans la numérotation --- gestioncof/migrations/0004_registration_mail.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gestioncof/migrations/0004_registration_mail.py b/gestioncof/migrations/0004_registration_mail.py index 97caff31..fff4e91e 100644 --- a/gestioncof/migrations/0004_registration_mail.py +++ b/gestioncof/migrations/0004_registration_mail.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models +from django.db import migrations + def create_mail(apps, schema_editor): CustomMail = apps.get_model("gestioncof", "CustomMail") @@ -19,10 +20,11 @@ def create_mail(apps, schema_editor): + "{{ prenom }} \t prénom de la personne.") ]) + class Migration(migrations.Migration): dependencies = [ - ('gestioncof', '0002_enable_unprocessed_demandes'), + ('gestioncof', '0003_event_image.py'), ] operations = [ From ec2079c4172ed4bf76963d6e0099556f2e3f25f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 12 Jul 2016 09:28:12 +0200 Subject: [PATCH 14/18] Fix Typo --- gestioncof/migrations/0004_registration_mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gestioncof/migrations/0004_registration_mail.py b/gestioncof/migrations/0004_registration_mail.py index fff4e91e..d72900bf 100644 --- a/gestioncof/migrations/0004_registration_mail.py +++ b/gestioncof/migrations/0004_registration_mail.py @@ -24,7 +24,7 @@ def create_mail(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('gestioncof', '0003_event_image.py'), + ('gestioncof', '0003_event_image'), ] operations = [ From 4dd4e2e19a21ec96382b267e354a165c180bca13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 12 Jul 2016 19:27:12 +0200 Subject: [PATCH 15/18] Supprime `tirage_bda.py` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ce script devait servir simuler un tirage. Ça ne semble rien apporter, sutout dans la mesure où il ne peut être lancé que par les personnes ayant accès à la machine COF. --- tirage_bda.py | 66 --------------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 tirage_bda.py diff --git a/tirage_bda.py b/tirage_bda.py deleted file mode 100644 index 502cb45e..00000000 --- a/tirage_bda.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 -import os -import sys -import time - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings") - from django.conf import settings - settings.DEBUG = True - from bda.models import Spectacle, Participant, ChoixSpectacle - from bda.algorithm import Algorithm - from django.db.models import Sum - from django.db import connection - start = time.time() - shows = Spectacle.objects.all() - members = Participant.objects.all() - choices = ChoixSpectacle.objects.order_by('participant', 'priority') \ - .select_related().all() - available_slots = Spectacle.objects.aggregate(Sum('slots'))['slots__sum'] - cursor = connection.cursor() - cursor.execute( - "SELECT SUM(`slots` * `price`) AS `total` FROM `bda_spectacle`;") - total_price = cursor.fetchone()[0] - print "%d spectacles" % len(shows) - print "%d places" % available_slots - print "%d participants" % len(members) - print "%d demandes" % len(choices) - print "%d places demandées" % (len(choices) - + len(choices.filter(double=True).all())) - print "%.02f€ à brasser" % total_price - print "Récupération: %.2fs" % (time.time() - start) - start_init = time.time() - algo = Algorithm(shows, members, choices) - print "Initialisation: %.2fs" % (time.time() - start_init) - start_algo = time.time() - results = algo(sys.argv[1]) - print "Traitement: %.2fs" % (time.time() - start_algo) - print len(connection.queries), "requêtes SQL effectuées" - queries = list(connection.queries) - total_slots = 0 - total_losers = 0 - for (_, members, losers) in results: - total_slots += len(members) - total_losers += len(losers) - print "Placés %d\nDécus %d" % (total_slots, total_losers) - print "Total %d" % (total_slots + total_losers) - members2 = {} - members_uniq = {} - for (show, members, _) in results: - for (member, _, _, _) in members: - if member.id not in members_uniq: - members_uniq[member.id] = member - members2[member] = [] - member.total = 0 - member = members_uniq[member.id] - members2[member].append(show) - member.total += show.price - if len(members) < show.slots: - print "%d place(s) invendue(s) pour %s" \ - % (show.slots - len(members), show) - members2 = members2.items() - print "Temps total: %.2fs" % (time.time() - start) - print "Requêtes SQL:" - for query in queries: - print query['sql'] From 89590b88de230986aae763db8dedbe519d59cd7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 13 Jul 2016 01:01:07 +0200 Subject: [PATCH 16/18] =?UTF-8?q?=C3=89tend=20les=20champs=20de=20recherch?= =?UTF-8?q?e=20dans=20l'admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #24 --- bda/admin.py | 11 +++++++++-- gestioncof/admin.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bda/admin.py b/bda/admin.py index 26d9a865..d989295d 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -174,7 +174,8 @@ class ChoixSpectacleAdmin(admin.ModelAdmin): list_filter = ("double_choice", "participant__tirage") search_fields = ('participant__user__username', 'participant__user__first_name', - 'participant__user__last_name') + 'participant__user__last_name', + 'spectacle__title') class SpectacleAdmin(admin.ModelAdmin): @@ -193,8 +194,14 @@ class TirageAdmin(admin.ModelAdmin): list_filter = ("active", ) search_fields = ("title", ) + +class SalleAdmin(admin.ModelAdmin): + model = Salle + search_fields = ('name', 'address') + + admin.site.register(Spectacle, SpectacleAdmin) -admin.site.register(Salle) +admin.site.register(Salle, SalleAdmin) admin.site.register(Participant, ParticipantAdmin) admin.site.register(Attribution, AttributionAdmin) admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) diff --git a/gestioncof/admin.py b/gestioncof/admin.py index bb03bbd4..8a1fb431 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -46,12 +46,14 @@ class SurveyQuestionInline(admin.TabularInline): class SurveyQuestionAdmin(admin.ModelAdmin): + search_fields = ('survey__title', 'answer') inlines = [ SurveyQuestionAnswerInline, ] class SurveyAdmin(admin.ModelAdmin): + search_fields = ('title', 'details') inlines = [ SurveyQuestionInline, ] @@ -72,12 +74,14 @@ class EventCommentFieldInline(admin.TabularInline): class EventOptionAdmin(admin.ModelAdmin): + search_fields = ('event__title', 'name') inlines = [ EventOptionChoiceInline, ] class EventAdmin(admin.ModelAdmin): + search_fields = ('title', 'location', 'description') inlines = [ EventOptionInline, EventCommentFieldInline, @@ -189,6 +193,7 @@ class PetitCoursAbilityAdmin(admin.ModelAdmin): class PetitCoursAttributionAdmin(admin.ModelAdmin): list_display = ('user', 'demande', 'matiere', 'rank', ) + search_fields = ('user__username', 'matiere__name') class PetitCoursAttributionCounterAdmin(admin.ModelAdmin): @@ -208,6 +213,11 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin): list_display = ('name', 'email', 'agrege_requis', 'niveau', 'created', 'traitee', 'processed') list_filter = ('traitee', 'niveau') + search_fields = ('name', 'email', 'phone', 'lieu', 'remarques') + + +class CustomMailAdmin(admin.ModelAdmin): + search_fields = ('shortname', 'title') admin.site.register(Survey, SurveyAdmin) admin.site.register(SurveyQuestion, SurveyQuestionAdmin) From 66214f7ff2b8c851726ae69d101c4feb897b1efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 13 Jul 2016 01:07:57 +0200 Subject: [PATCH 17/18] Typos --- gestioncof/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gestioncof/models.py b/gestioncof/models.py index badf7533..540145d5 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -94,7 +94,8 @@ class CustomMail(models.Model): blank=True) class Meta: - verbose_name = "Mails personnalisables" + verbose_name = "Mail personnalisable" + verbose_name_plural = "Mails personnalisables" def __unicode__(self): return u"%s: %s" % (self.shortname, self.title) @@ -158,6 +159,7 @@ class EventOptionChoice(models.Model): class Meta: verbose_name = "Choix" + verbose_name_plural = "Choix" def __unicode__(self): return unicode(self.value) From a24ca5a19b680429d027a4b82e4b55f1c529c3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Thu, 14 Jul 2016 19:31:07 +0200 Subject: [PATCH 18/18] Corrige les urls du BdA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le `^` de début de regex a été oublié -> Les urls avaient un étrange, par exemple `bda/etat-places` et `bda/places` pointaient vers la même vue --- bda/urls.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bda/urls.py b/bda/urls.py index 47a946ba..268bb352 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -5,33 +5,33 @@ from bda.views import SpectacleListView urlpatterns = patterns( '', - url(r'inscription/(?P\d+)$', + url(r'^inscription/(?P\d+)$', 'bda.views.inscription', name='bda-tirage-inscription'), - url(r'places/(?P\d+)$', + url(r'^places/(?P\d+)$', 'bda.views.places', name="bda-places-attribuees"), - url(r'places/(?P\d+)/places_bda.ics$', + url(r'^places/(?P\d+)/places_bda.ics$', 'bda.views.places_ics', name="bda-places-attribuees-ics"), - url(r'revente/(?P\d+)$', + url(r'^revente/(?P\d+)$', 'bda.views.revente', name='bda-revente'), - url(r'etat-places/(?P\d+)$', + url(r'^etat-places/(?P\d+)$', 'bda.views.etat_places', name='bda-etat-places'), - url(r'tirage/(?P\d+)$', 'bda.views.tirage'), - url(r'spectacles/(?P\d+)$', + url(r'^tirage/(?P\d+)$', 'bda.views.tirage'), + url(r'^spectacles/(?P\d+)$', SpectacleListView.as_view(), name="bda-liste-spectacles"), - url(r'spectacles/(?P\d+)/(?P\d+)$', + url(r'^spectacles/(?P\d+)/(?P\d+)$', "bda.views.spectacle", name="bda-spectacle"), - url(r'spectacles-ics/(?P\d+)$', + url(r'^spectacles-ics/(?P\d+)$', 'bda.views.liste_spectacles_ics', name="bda-liste-spectacles-ics"), - url(r'spectacles/unpaid/(?P\d+)$', + url(r'^spectacles/unpaid/(?P\d+)$', "bda.views.unpaid", name="bda-unpaid"), - url(r'mails-rappel/(?P\d+)$', "bda.views.send_rappel"), + url(r'^mails-rappel/(?P\d+)$', "bda.views.send_rappel"), )