# -*- 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.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) shotgun = models.BooleanField("Disponible immédiatement", default=False) @property def date_tirage(self): """Renvoie la date du tirage au sort de la revente.""" # 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)) # Le vendeur a aussi 1h pour changer d'avis return self.date + delay + timedelta(hours=1) def date_tirage_str(self): return self.date_tirage \ .astimezone(timezone.get_current_timezone()) \ .strftime('%d/%m/%y à %H:%M') 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.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): 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.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 # Flag inutile, sauf si l'horloge interne merde self.tirage_done = True self.shotgun = True self.save() def tirage(self): 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('mail-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('mail-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('mail-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) # Si personne ne veut de la place, elle part au shotgun else: self.shotgun = True self.tirage_done = True self.save()