forked from DGNum/gestioCOF
4b1cfbcf2d
Ajoute le BdA dans les destinataires des rappels Modifie légérement la façon dont est généré le mail au passage See merge request !90
347 lines
13 KiB
Python
347 lines
13 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, Context
|
|
from django.core import mail
|
|
from django.conf import settings
|
|
from django.utils import timezone
|
|
from django.utils.encoding import python_2_unicode_compatible
|
|
|
|
|
|
def render_template(template_name, data):
|
|
tmpl = loader.get_template(template_name)
|
|
ctxt = Context(data)
|
|
return tmpl.render(ctxt)
|
|
|
|
|
|
@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 date_no_seconds(self):
|
|
return self.fermeture.astimezone(timezone.get_current_timezone()) \
|
|
.strftime('%d %b %Y %H:%M')
|
|
|
|
def __str__(self):
|
|
return "%s - %s" % (self.title, self.date_no_seconds())
|
|
|
|
|
|
@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 __repr__(self):
|
|
return "[%s]" % self
|
|
|
|
def timestamp(self):
|
|
return "%d" % calendar.timegm(self.date.utctimetuple())
|
|
|
|
def date_no_seconds(self):
|
|
return self.date.astimezone(timezone.get_current_timezone()) \
|
|
.strftime('%d %b %Y %H:%M')
|
|
|
|
def __str__(self):
|
|
return "%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][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 = "%s - %s - %s" % (self.title, self.date_no_seconds(),
|
|
self.location)
|
|
for member in members.values():
|
|
mail_body = render_template('mail-rappel.txt', {
|
|
'name': member[0],
|
|
'nb_attr': member[1],
|
|
'show': self})
|
|
mail_tot = mail.EmailMessage(
|
|
mail_object, mail_body,
|
|
settings.RAPPEL_FROM, [member[2]],
|
|
[], headers={'Reply-To': settings.RAPPEL_REPLY_TO})
|
|
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 à %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 = render_template('mail-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.REVENTE_FROM, [participant.user.email],
|
|
[], headers={'Reply-To': settings.REVENTE_REPLY_TO})
|
|
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):
|
|
inscrits = self.attribution.spectacle.subscribed.select_related('user')
|
|
|
|
mails_to_send = []
|
|
mail_object = "%s" % (self.attribution.spectacle)
|
|
for participant in inscrits:
|
|
mail_body = render_template('mail-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.REVENTE_FROM, [participant.user.email],
|
|
[], headers={'Reply-To': settings.REVENTE_REPLY_TO})
|
|
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):
|
|
inscrits = self.answered_mail
|
|
spectacle = self.attribution.spectacle
|
|
seller = self.seller
|
|
if inscrits.exists():
|
|
winner = random.choice(inscrits.all())
|
|
self.soldTo = winner
|
|
mail_buyer = """Bonjour,
|
|
|
|
Tu as été tiré-e au sort pour racheter une place pour %s le %s (%s) à %0.02f€.
|
|
Tu peux contacter le/la vendeur-se à l'adresse %s.
|
|
|
|
Chaleureusement,
|
|
Le BdA""" % (spectacle.title, spectacle.date_no_seconds(),
|
|
spectacle.location, spectacle.price, seller.email)
|
|
|
|
mail.send_mail("BdA-Revente : %s" % spectacle.title,
|
|
mail_buyer, "bda@ens.fr", [winner.user.email],
|
|
fail_silently=False)
|
|
mail_seller = """Bonjour,
|
|
La personne tirée au sort pour racheter ta place pour %s est %s.
|
|
Tu peux le/la contacter à l'adresse %s.
|
|
|
|
Chaleureusement,
|
|
Le BdA""" % (spectacle.title, winner.user.get_full_name(), winner.user.email)
|
|
|
|
mail.send_mail("BdA-Revente : %s" % spectacle.title,
|
|
mail_seller, "bda@ens.fr", [seller.email],
|
|
fail_silently=False)
|
|
self.tirage_done = True
|
|
self.save()
|