Améliore les mails automatiques du BdA Les mails du BdA sont maintenant tous chargés depuis des templates gérés par le système de templates de Django, et plus par de l'interpolation de chaîne de caractères. Ceci permet en particulier d'utiliser (et de configurer) la localisation de Django afin d'afficher les dates de façon uniforme (et sans "hack" à la `date_no_seconds`) dans un format comportant un "à" entre le jour et l'heure. See merge request !113
376 lines
14 KiB
Python
376 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import calendar
|
|
import random
|
|
from datetime import timedelta
|
|
|
|
from django.contrib.sites.models import Site
|
|
from django.db import models
|
|
from django.contrib.auth.models import User
|
|
from django.template import loader
|
|
from django.core import mail
|
|
from django.conf import settings
|
|
from django.utils import timezone, formats
|
|
from django.utils.encoding import python_2_unicode_compatible
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Tirage(models.Model):
|
|
title = models.CharField("Titre", max_length=300)
|
|
ouverture = models.DateTimeField("Date et heure d'ouverture du tirage")
|
|
fermeture = models.DateTimeField("Date et heure de fermerture du tirage")
|
|
tokens = models.TextField("Graine(s) du tirage", blank=True)
|
|
active = models.BooleanField("Tirage actif", default=False)
|
|
enable_do_tirage = models.BooleanField("Le tirage peut être lancé",
|
|
default=False)
|
|
|
|
def __str__(self):
|
|
return "%s - %s" % (self.title, formats.localize(
|
|
timezone.template_localtime(self.fermeture)))
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Salle(models.Model):
|
|
name = models.CharField("Nom", max_length=300)
|
|
address = models.TextField("Adresse")
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class CategorieSpectacle(models.Model):
|
|
name = models.CharField('Nom', max_length=100, unique=True)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Meta:
|
|
verbose_name = "Catégorie"
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Spectacle(models.Model):
|
|
title = models.CharField("Titre", max_length=300)
|
|
category = models.ForeignKey(CategorieSpectacle, blank=True, null=True)
|
|
date = models.DateTimeField("Date & heure")
|
|
location = models.ForeignKey(Salle)
|
|
vips = models.TextField('Personnalités', blank=True)
|
|
description = models.TextField("Description", blank=True)
|
|
slots_description = models.TextField("Description des places", blank=True)
|
|
image = models.ImageField('Image', blank=True, null=True,
|
|
upload_to='imgs/shows/')
|
|
ext_link = models.CharField('Lien vers le site du spectacle', blank=True,
|
|
max_length=500)
|
|
price = models.FloatField("Prix d'une place")
|
|
slots = models.IntegerField("Places")
|
|
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"
|
|
ordering = ("date", "title",)
|
|
|
|
def timestamp(self):
|
|
return "%d" % calendar.timegm(self.date.utctimetuple())
|
|
|
|
def __str__(self):
|
|
return "%s - %s, %s, %.02f€" % (
|
|
self.title,
|
|
formats.localize(timezone.template_localtime(self.date)),
|
|
self.location,
|
|
self.price
|
|
)
|
|
|
|
def send_rappel(self):
|
|
"""
|
|
Envoie un mail de rappel à toutes les personnes qui ont une place pour
|
|
ce spectacle.
|
|
"""
|
|
# 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][1] = 2
|
|
else:
|
|
members[member.id] = [member.first_name, 1, member.email]
|
|
# Pour le BdA
|
|
members[0] = ['BdA', 1, 'bda@ens.fr']
|
|
members[-1] = ['BdA', 2, 'bda@ens.fr']
|
|
# On écrit un mail personnalisé à chaque participant
|
|
mails_to_send = []
|
|
mail_object = str(self)
|
|
for member in members.values():
|
|
mail_body = loader.render_to_string('bda/mails/rappel.txt', {
|
|
'name': member[0],
|
|
'nb_attr': member[1],
|
|
'show': self})
|
|
mail_tot = mail.EmailMessage(
|
|
mail_object, mail_body,
|
|
settings.MAIL_DATA['rappels']['FROM'], [member[2]],
|
|
[], headers={
|
|
'Reply-To': settings.MAIL_DATA['rappels']['REPLYTO']})
|
|
mails_to_send.append(mail_tot)
|
|
# 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()
|
|
|
|
|
|
class Quote(models.Model):
|
|
spectacle = models.ForeignKey(Spectacle)
|
|
text = models.TextField('Citation')
|
|
author = models.CharField('Auteur', max_length=200)
|
|
|
|
|
|
PAYMENT_TYPES = (
|
|
("cash", "Cash"),
|
|
("cb", "CB"),
|
|
("cheque", "Chèque"),
|
|
("autre", "Autre"),
|
|
)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Participant(models.Model):
|
|
user = models.ForeignKey(User)
|
|
choices = models.ManyToManyField(Spectacle,
|
|
through="ChoixSpectacle",
|
|
related_name="chosen_by")
|
|
attributions = models.ManyToManyField(Spectacle,
|
|
through="Attribution",
|
|
related_name="attributed_to")
|
|
paid = models.BooleanField("A payé", default=False)
|
|
paymenttype = models.CharField("Moyen de paiement",
|
|
max_length=6, choices=PAYMENT_TYPES,
|
|
blank=True)
|
|
tirage = models.ForeignKey(Tirage)
|
|
choicesrevente = models.ManyToManyField(Spectacle,
|
|
related_name="subscribed",
|
|
blank=True)
|
|
|
|
def __str__(self):
|
|
return "%s - %s" % (self.user, self.tirage.title)
|
|
|
|
DOUBLE_CHOICES = (
|
|
("1", "1 place"),
|
|
("autoquit", "2 places si possible, 1 sinon"),
|
|
("double", "2 places sinon rien"),
|
|
)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class ChoixSpectacle(models.Model):
|
|
participant = models.ForeignKey(Participant)
|
|
spectacle = models.ForeignKey(Spectacle, related_name="participants")
|
|
priority = models.PositiveIntegerField("Priorité")
|
|
double_choice = models.CharField("Nombre de places",
|
|
default="1", choices=DOUBLE_CHOICES,
|
|
max_length=10)
|
|
|
|
def get_double(self):
|
|
return self.double_choice != "1"
|
|
double = property(get_double)
|
|
|
|
def get_autoquit(self):
|
|
return self.double_choice == "autoquit"
|
|
autoquit = property(get_autoquit)
|
|
|
|
def __str__(self):
|
|
return "Vœux de %s pour %s" % (
|
|
self.participant.user.get_full_name,
|
|
self.spectacle.title)
|
|
|
|
class Meta:
|
|
ordering = ("priority",)
|
|
unique_together = (("participant", "spectacle",),)
|
|
verbose_name = "voeu"
|
|
verbose_name_plural = "voeux"
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Attribution(models.Model):
|
|
participant = models.ForeignKey(Participant)
|
|
spectacle = models.ForeignKey(Spectacle, related_name="attribues")
|
|
given = models.BooleanField("Donnée", default=False)
|
|
|
|
def __str__(self):
|
|
return "%s -- %s, %s" % (self.participant.user, self.spectacle.title,
|
|
self.spectacle.date)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class SpectacleRevente(models.Model):
|
|
attribution = models.OneToOneField(Attribution,
|
|
related_name="revente")
|
|
date = models.DateTimeField("Date de mise en vente",
|
|
default=timezone.now)
|
|
answered_mail = models.ManyToManyField(Participant,
|
|
related_name="wanted",
|
|
blank=True)
|
|
seller = models.ForeignKey(Participant,
|
|
related_name="original_shows",
|
|
verbose_name="Vendeur")
|
|
soldTo = models.ForeignKey(Participant, blank=True, null=True,
|
|
verbose_name="Vendue à")
|
|
|
|
notif_sent = models.BooleanField("Notification envoyée",
|
|
default=False)
|
|
tirage_done = models.BooleanField("Tirage effectué",
|
|
default=False)
|
|
|
|
@property
|
|
def expiration_time(self):
|
|
# L'acheteur doit être connu au plus 12h avant le spectacle
|
|
remaining_time = (self.attribution.spectacle.date
|
|
- self.date - timedelta(hours=13))
|
|
# Au minimum, on attend 2 jours avant le tirage
|
|
delay = min(remaining_time, timedelta(days=2))
|
|
# On a aussi 1h pour changer d'avis
|
|
return self.date + delay + timedelta(hours=1)
|
|
|
|
def expiration_time_str(self):
|
|
return self.expiration_time \
|
|
.astimezone(timezone.get_current_timezone()) \
|
|
.strftime('%d/%m/%y à %H:%M')
|
|
|
|
@property
|
|
def shotgun(self):
|
|
# Soit on a dépassé le délai du tirage, soit il reste peu de
|
|
# temps avant le spectacle
|
|
# On se laisse 5min de marge pour cron
|
|
return (timezone.now() > self.expiration_time + timedelta(minutes=5) or
|
|
(self.attribution.spectacle.date <= timezone.now() +
|
|
timedelta(days=1))) and (timezone.now() >= self.date +
|
|
timedelta(minutes=15))
|
|
|
|
def __str__(self):
|
|
return "%s -- %s" % (self.seller,
|
|
self.attribution.spectacle.title)
|
|
|
|
class Meta:
|
|
verbose_name = "Revente"
|
|
|
|
def send_notif(self):
|
|
inscrits = self.attribution.spectacle.subscribed.select_related('user')
|
|
|
|
mails_to_send = []
|
|
mail_object = "%s" % (self.attribution.spectacle)
|
|
for participant in inscrits:
|
|
mail_body = loader.render_to_string('bda/mails/revente.txt', {
|
|
'user': participant.user,
|
|
'spectacle': self.attribution.spectacle,
|
|
'revente': self,
|
|
'domain': Site.objects.get_current().domain})
|
|
mail_tot = mail.EmailMessage(
|
|
mail_object, mail_body,
|
|
settings.MAIL_DATA['revente']['FROM'],
|
|
[participant.user.email],
|
|
[], headers={
|
|
'Reply-To': settings.MAIL_DATA['revente']['REPLYTO']})
|
|
mails_to_send.append(mail_tot)
|
|
|
|
connection = mail.get_connection()
|
|
connection.send_messages(mails_to_send)
|
|
self.notif_sent = True
|
|
self.save()
|
|
|
|
def mail_shotgun(self):
|
|
"""
|
|
Envoie un mail à toutes les personnes intéréssées par le spectacle pour
|
|
leur indiquer qu'il est désormais disponible au shotgun.
|
|
"""
|
|
inscrits = self.attribution.spectacle.subscribed.select_related('user')
|
|
|
|
mails_to_send = []
|
|
mail_object = "%s" % (self.attribution.spectacle)
|
|
for participant in inscrits:
|
|
mail_body = loader.render_to_string('bda/mails/shotgun.txt', {
|
|
'user': participant.user,
|
|
'spectacle': self.attribution.spectacle,
|
|
'domain': Site.objects.get_current(),
|
|
'mail': self.attribution.participant.user.email})
|
|
mail_tot = mail.EmailMessage(
|
|
mail_object, mail_body,
|
|
settings.MAIL_DATA['revente']['FROM'],
|
|
[participant.user.email],
|
|
[], headers={
|
|
'Reply-To': settings.MAIL_DATA['revente']['REPLYTO']})
|
|
mails_to_send.append(mail_tot)
|
|
|
|
connection = mail.get_connection()
|
|
connection.send_messages(mails_to_send)
|
|
self.notif_sent = True
|
|
self.save()
|
|
|
|
def tirage(self):
|
|
"""
|
|
Lance le tirage au sort associé à la revente. Un gagnant est choisi
|
|
parmis les personnes intéressées par le spectacle. Les personnes sont
|
|
ensuites prévenues par mail du résultat du tirage.
|
|
"""
|
|
inscrits = list(self.answered_mail.all())
|
|
spectacle = self.attribution.spectacle
|
|
seller = self.seller
|
|
|
|
if inscrits:
|
|
mails = []
|
|
mail_subject = "BdA-Revente : {:s}".format(spectacle.title)
|
|
|
|
# Envoie un mail au gagnant et au vendeur
|
|
winner = random.choice(inscrits)
|
|
self.soldTo = winner
|
|
context = {
|
|
'acheteur': winner.user,
|
|
'vendeur': seller.user,
|
|
'spectacle': spectacle,
|
|
}
|
|
mails.append(mail.EmailMessage(
|
|
mail_subject,
|
|
loader.render_to_string('bda/mails/revente-winner.txt',
|
|
context),
|
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
|
to=[winner.user.email],
|
|
reply_to=[seller.user.email],
|
|
))
|
|
mails.append(mail.EmailMessage(
|
|
mail_subject,
|
|
loader.render_to_string('bda/mails/revente-seller.txt',
|
|
context),
|
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
|
to=[seller.user.email],
|
|
reply_to=[winner.user.email],
|
|
))
|
|
|
|
# Envoie un mail aux perdants
|
|
for inscrit in inscrits:
|
|
if inscrit == winner:
|
|
continue
|
|
|
|
mail_body = loader.render_to_string(
|
|
'bda/mails/revente-loser.txt',
|
|
{'acheteur': inscrit.user,
|
|
'vendeur': seller.user,
|
|
'spectacle': spectacle}
|
|
)
|
|
mails.append(mail.EmailMessage(
|
|
mail_subject, mail_body,
|
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
|
to=[inscrit.user.email],
|
|
reply_to=[settings.MAIL_DATA['revente']['REPLYTO']],
|
|
))
|
|
mail.get_connection().send_messages(mails)
|
|
self.tirage_done = True
|
|
self.save()
|